crlf -> ln conversion

This commit is contained in:
Michael Morrison 2014-10-29 02:02:03 -05:00
parent 50d3ef20a0
commit f1467c5a04
94 changed files with 11388 additions and 11388 deletions

792
README.md
View file

@ -1,396 +1,396 @@
node-GameDig - Game Server Query Library node-GameDig - Game Server Query Library
--- ---
node-GameDig is a game server query library, capable of querying for the status of 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, nearly any game or voice server. If a server makes its status publically available,
GameDig can fetch it for you. GameDig can fetch it for you.
GameDig is available as a node.js module, as well as a GameDig is available as a node.js module, as well as a
[command line executable](#usage-from-command-line). [command line executable](#usage-from-command-line).
Usage from Node.js Usage from Node.js
--- ---
```shell ```shell
npm install gamedig npm install gamedig
``` ```
```javascript ```javascript
var Gamedig = require('gamedig'); var Gamedig = require('gamedig');
Gamedig.query( Gamedig.query(
{ {
type: 'minecraft', type: 'minecraft',
host: 'mc.example.com' host: 'mc.example.com'
}, },
function(state) { function(state) {
if(state.error) console.log("Server is offline"); if(state.error) console.log("Server is offline");
else console.log(state); else console.log(state);
} }
); );
``` ```
> Is NPM out of date? If you're feeling lucky, you can install the latest code with > Is NPM out of date? If you're feeling lucky, you can install the latest code with
> ```shell > ```shell
> npm install "git+https://github.com/sonicsnes/node-gamedig.git" > npm install "git+https://github.com/sonicsnes/node-gamedig.git"
> ``` > ```
### Input Parameters ### Input Parameters
* **type**: One of the game IDs listed in the game list below * **type**: One of the game IDs listed in the game list below
* **host** * **host**
* **port**: (optional) Uses the protocol default if not set * **port**: (optional) Uses the protocol default if not set
* **notes**: (optional) Passed through to output * **notes**: (optional) Passed through to output
###Callback Function ###Callback Function
The callback function is "guaranteed" to be called exactly once. 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 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. 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: Otherwise, the returned object is guaranteed to contain the following keys:
**Stable, always present:** **Stable, always present:**
* **name** * **name**
* **map** * **map**
* **password**: Boolean * **password**: Boolean
* **maxplayers** * **maxplayers**
* **players**: (array of objects) Each object **may** contain name, ping, score, team, address * **players**: (array of objects) Each object **may** contain name, ping, score, team, address
* **bots**: Same schema as players * **bots**: Same schema as players
* **notes**: Passed through from the input * **notes**: Passed through from the input
**Unstable, not guaranteed:** **Unstable, not guaranteed:**
* **raw**: Contains all information received from the server * **raw**: Contains all information received from the server
* **query**: Details about the query performed * **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. 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. Some servers may return an additional player count number, which may be present in the unstable raw object.
Games List Games List
--- ---
###Supported ###Supported
<!--- BEGIN GENERATED GAMES --> <!--- BEGIN GENERATED GAMES -->
* Age of Chivalry (ageofchivalry) * Age of Chivalry (ageofchivalry)
* Age of Empires 2 (aoe2) [[Separate Query Port](#separate-query-port)] * Age of Empires 2 (aoe2) [[Separate Query Port](#separate-query-port)]
* Alien Arena (alienarena) [[Separate Query Port](#separate-query-port)] * Alien Arena (alienarena) [[Separate Query Port](#separate-query-port)]
* Alien Swarm (alienswarm) * Alien Swarm (alienswarm)
* Aliens vs Predator 2 (avp2) * Aliens vs Predator 2 (avp2)
* Aliens vs Predator 2010 (avp2010) * Aliens vs Predator 2010 (avp2010)
* America's Army (americasarmy) [[Separate Query Port](#separate-query-port)] * America's Army (americasarmy) [[Separate Query Port](#separate-query-port)]
* America's Army 2 (americasarmy2) [[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 3 (americasarmy3) [[Separate Query Port](#separate-query-port)]
* America's Army: Proving Grounds (americasarmypg) [[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 1 (arma)
* ArmA Armed Assault 2 (arma2) [[Separate Query Port](#separate-query-port)] * ArmA Armed Assault 2 (arma2) [[Separate Query Port](#separate-query-port)]
* ArmA Armed Assault 3 (arma3) [[Separate Query Port](#separate-query-port)] * ArmA Armed Assault 3 (arma3) [[Separate Query Port](#separate-query-port)]
* Armagetron (armagetron) * Armagetron (armagetron)
* Baldur's Gate (baldursgate) [[Separate Query Port](#separate-query-port)] * Baldur's Gate (baldursgate) [[Separate Query Port](#separate-query-port)]
* Battlefield 1942 (bf1942) [[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 Vietnam (bfv) [[Separate Query Port](#separate-query-port)]
* Battlefield 2 (bf2) [[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 2142 (bf2142) [[Separate Query Port](#separate-query-port)]
* Battlefield: Bad Company 2 (bfbc2) [[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 3 (bf3) [[Separate Query Port](#separate-query-port)]
* Battlefield 4 (bf4) [[Separate Query Port](#separate-query-port)] * Battlefield 4 (bf4) [[Separate Query Port](#separate-query-port)]
* Breach (breach) * Breach (breach)
* Breed (breed) * Breed (breed)
* Brink (brink) [[Separate Query Port](#separate-query-port)] * Brink (brink) [[Separate Query Port](#separate-query-port)]
* Build and Shoot (buildandshoot) [[Separate Query Port](#separate-query-port)] * Build and Shoot (buildandshoot) [[Separate Query Port](#separate-query-port)]
* Call of Duty (cod) * Call of Duty (cod)
* Call of Duty: United Offensive (coduo) * Call of Duty: United Offensive (coduo)
* Call of Duty 2 (cod2) * Call of Duty 2 (cod2)
* Call of Duty 3 (cod3) * Call of Duty 3 (cod3)
* Call of Duty 4: Modern Warfare (cod4) * Call of Duty 4: Modern Warfare (cod4)
* Call of Duty: World at War (codwaw) * Call of Duty: World at War (codwaw)
* Call of Duty: Modern Warfare 2 (codmw2) * Call of Duty: Modern Warfare 2 (codmw2)
* Call of Duty: Modern Warfare 3 (codmw3) [[Separate Query Port](#separate-query-port)] * Call of Duty: Modern Warfare 3 (codmw3) [[Separate Query Port](#separate-query-port)]
* Call of Juarez (callofjuarez) [[Separate Query Port](#separate-query-port)] * Call of Juarez (callofjuarez) [[Separate Query Port](#separate-query-port)]
* Chaser (chaser) [[Separate Query Port](#separate-query-port)] * Chaser (chaser) [[Separate Query Port](#separate-query-port)]
* Chrome (chrome) [[Separate Query Port](#separate-query-port)] * Chrome (chrome) [[Separate Query Port](#separate-query-port)]
* Codename Eagle (codenameeagle) [[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)] * Commandos 3: Destination Berlin (commandos3) [[Separate Query Port](#separate-query-port)]
* Command and Conquer: Renegade (cacrenegade) [[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)] * Contact J.A.C.K. (contactjack) [[Separate Query Port](#separate-query-port)]
* Counter-Strike 1.6 (cs16) * Counter-Strike 1.6 (cs16)
* Counter-Strike: Condition Zero (cscz) * Counter-Strike: Condition Zero (cscz)
* Counter-Strike: Source (css) * Counter-Strike: Source (css)
* Counter-Strike: Global Offensive (csgo) * Counter-Strike: Global Offensive (csgo)
* Cross Racing Championship (crossracing) [[Separate Query Port](#separate-query-port)] * Cross Racing Championship (crossracing) [[Separate Query Port](#separate-query-port)]
* Crysis (crysis) * Crysis (crysis)
* Crysis Wars (crysiswars) * Crysis Wars (crysiswars)
* Crysis 2 (crysis2) * Crysis 2 (crysis2)
* Daikatana (daikatana) [[Separate Query Port](#separate-query-port)] * Daikatana (daikatana) [[Separate Query Port](#separate-query-port)]
* Dark Messiah of Might and Magic (dmomam) * Dark Messiah of Might and Magic (dmomam)
* Darkest Hour (darkesthour) [[Separate Query Port](#separate-query-port)] * Darkest Hour (darkesthour) [[Separate Query Port](#separate-query-port)]
* DayZ (dayz) [[Separate Query Port](#separate-query-port)] [[Additional Notes](#dayz)] * DayZ (dayz) [[Separate Query Port](#separate-query-port)] [[Additional Notes](#dayz)]
* Deadly Dozen: Pacific Theater (deadlydozenpt) [[Separate Query Port](#separate-query-port)] * Deadly Dozen: Pacific Theater (deadlydozenpt) [[Separate Query Port](#separate-query-port)]
* Deer Hunter 2005 (dh2005) [[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)] * Descent 3 (descent3) [[Separate Query Port](#separate-query-port)]
* Deus Ex (deusex) [[Separate Query Port](#separate-query-port)] * Deus Ex (deusex) [[Separate Query Port](#separate-query-port)]
* Devastation (devastation) [[Separate Query Port](#separate-query-port)] * Devastation (devastation) [[Separate Query Port](#separate-query-port)]
* Dino D-Day (dinodday) * Dino D-Day (dinodday)
* Dirt Track Racing 2 (dirttrackracing2) [[Separate Query Port](#separate-query-port)] * Dirt Track Racing 2 (dirttrackracing2) [[Separate Query Port](#separate-query-port)]
* Day of Defeat (dod) * Day of Defeat (dod)
* Day of Defeat: Source (dods) * Day of Defeat: Source (dods)
* Doom 3 (doom3) * Doom 3 (doom3)
* DOTA 2 (dota2) * DOTA 2 (dota2)
* Drakan (drakan) [[Separate Query Port](#separate-query-port)] * Drakan (drakan) [[Separate Query Port](#separate-query-port)]
* Enemy Territory Quake Wars (etqw) [[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)] * F.E.A.R. (fear) [[Separate Query Port](#separate-query-port)]
* F1 2002 (f12002) [[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)] * F1 Challenge 99-02 (f1c9902) [[Separate Query Port](#separate-query-port)]
* Far Cry (farcry) [[Separate Query Port](#separate-query-port)] * Far Cry (farcry) [[Separate Query Port](#separate-query-port)]
* Far Cry (farcry2) [[Separate Query Port](#separate-query-port)] * Far Cry (farcry2) [[Separate Query Port](#separate-query-port)]
* Fortress Forever (fortressforever) * Fortress Forever (fortressforever)
* Flashpoint (flashpoint) [[Separate Query Port](#separate-query-port)] * Flashpoint (flashpoint) [[Separate Query Port](#separate-query-port)]
* Frontlines: Fuel of War (ffow) [[Separate Query Port](#separate-query-port)] * Frontlines: Fuel of War (ffow) [[Separate Query Port](#separate-query-port)]
* Garry's Mod (garrysmod) * Garry's Mod (garrysmod)
* Ghost Recon: Advanced Warfighter (graw) [[Separate Query Port](#separate-query-port)] * Ghost Recon: Advanced Warfighter (graw) [[Separate Query Port](#separate-query-port)]
* Ghost Recon: Advanced Warfighter 2 (graw2) [[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)] * Giants: Citizen Kabuto (giantscitizenkabuto) [[Separate Query Port](#separate-query-port)]
* Global Operations (globaloperations) [[Separate Query Port](#separate-query-port)] * Global Operations (globaloperations) [[Separate Query Port](#separate-query-port)]
* Gore (gore) [[Separate Query Port](#separate-query-port)] * Gore (gore) [[Separate Query Port](#separate-query-port)]
* Gunman Chronicles (gunmanchronicles) * Gunman Chronicles (gunmanchronicles)
* Half-Life 1 Deathmatch (hldm) * Half-Life 1 Deathmatch (hldm)
* Half-Life 2 Deathmatch (hl2dm) * Half-Life 2 Deathmatch (hl2dm)
* Halo (halo) * Halo (halo)
* Halo 2 (halo2) * Halo 2 (halo2)
* Heretic 2 (heretic2) [[Separate Query Port](#separate-query-port)] * Heretic 2 (heretic2) [[Separate Query Port](#separate-query-port)]
* Hexen World (hexenworld) [[Separate Query Port](#separate-query-port)] * Hexen World (hexenworld) [[Separate Query Port](#separate-query-port)]
* The Hidden: Source (hidden) * The Hidden: Source (hidden)
* Hidden and Dangerous 2 (had2) [[Separate Query Port](#separate-query-port)] * Hidden and Dangerous 2 (had2) [[Separate Query Port](#separate-query-port)]
* Homefront (homefront) * Homefront (homefront)
* Homeworld 2 (homeworld2) [[Separate Query Port](#separate-query-port)] * Homeworld 2 (homeworld2) [[Separate Query Port](#separate-query-port)]
* IGI-2: Covert Strike (igi2) [[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)] * IL-2 Sturmovik (il2) [[Separate Query Port](#separate-query-port)]
* Insurgency (insurgency) * Insurgency (insurgency)
* Iron Storm (ironstorm) [[Separate Query Port](#separate-query-port)] * Iron Storm (ironstorm) [[Separate Query Port](#separate-query-port)]
* James Bond: Nightfire (jamesbondnightfire) [[Separate Query Port](#separate-query-port)] * James Bond: Nightfire (jamesbondnightfire) [[Separate Query Port](#separate-query-port)]
* Just Cause 2 Multiplayer (jc2mp) * Just Cause 2 Multiplayer (jc2mp)
* Killing Floor (killingfloor) [[Separate Query Port](#separate-query-port)] * Killing Floor (killingfloor) [[Separate Query Port](#separate-query-port)]
* Kingpin: Life of Crime (kingpin) [[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)] * KISS Psycho Circus (kisspc) [[Separate Query Port](#separate-query-port)]
* KzMod (kzmod) * KzMod (kzmod)
* Left 4 Dead (left4dead) * Left 4 Dead (left4dead)
* Left 4 Dead 2 (left4dead2) * Left 4 Dead 2 (left4dead2)
* Mafia 2 Multiplayer (m2mp) [[Separate Query Port](#separate-query-port)] * 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: Allied Assault (mohaa) [[Separate Query Port](#separate-query-port)]
* Medal of Honor: Pacific Assault (mohpa) [[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: Airborne (mohab) [[Separate Query Port](#separate-query-port)]
* Medal of Honor: Spearhead (mohsh) [[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: Breakthrough (mohbt) [[Separate Query Port](#separate-query-port)]
* Medal of Honor 2010 (moh2010) [[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)] * Medal of Honor: Warfighter (mohwf) [[Separate Query Port](#separate-query-port)]
* Minecraft (minecraft) [[Additional Notes](#minecraft)] * Minecraft (minecraft) [[Additional Notes](#minecraft)]
* Minecraft (minecraftping) [[Additional Notes](#minecraft)] * Minecraft (minecraftping) [[Additional Notes](#minecraft)]
* Monday Night Combat (mnc) [[Separate Query Port](#separate-query-port)] * 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: Vice City (mtavc) [[Separate Query Port](#separate-query-port)]
* Multi Theft Auto: San Andreas (mtasa) [[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 (mumble) [[Separate Query Port](#separate-query-port)] [[Additional Notes](#mumble)]
* Mumble (mumbleping) [[Additional Notes](#mumble)] * Mumble (mumbleping) [[Additional Notes](#mumble)]
* Mutant Factions (mutantfactions) * Mutant Factions (mutantfactions)
* Nascar Thunder 2004 (nascarthunder2004) [[Separate Query Port](#separate-query-port)] * Nascar Thunder 2004 (nascarthunder2004) [[Separate Query Port](#separate-query-port)]
* netPanzer (netpanzer) * netPanzer (netpanzer)
* No More Room in Hell (nmrih) * No More Room in Hell (nmrih)
* Natural Selection (ns) * Natural Selection (ns)
* Natural Selection 2 (ns2) [[Separate Query Port](#separate-query-port)] * Natural Selection 2 (ns2) [[Separate Query Port](#separate-query-port)]
* Need for Speed: Hot Pursuit 2 (nfshp2) [[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)] * Nerf Arena Blast (nab) [[Separate Query Port](#separate-query-port)]
* Neverwinter Nights (nwn) [[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)] * Neverwinter Nights 2 (nwn2) [[Separate Query Port](#separate-query-port)]
* Nexuiz (nexuiz) [[Separate Query Port](#separate-query-port)] * Nexuiz (nexuiz) [[Separate Query Port](#separate-query-port)]
* Nitro Family (nitrofamily) [[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 (nolf) [[Separate Query Port](#separate-query-port)]
* No One Lives Forever 2 (nolf2) [[Separate Query Port](#separate-query-port)] * No One Lives Forever 2 (nolf2) [[Separate Query Port](#separate-query-port)]
* Nuclear Dawn (nucleardawn) * Nuclear Dawn (nucleardawn)
* OpenArena (openarena) [[Separate Query Port](#separate-query-port)] * OpenArena (openarena) [[Separate Query Port](#separate-query-port)]
* Operation Flashpoint (operationflashpoint) [[Separate Query Port](#separate-query-port)] * Operation Flashpoint (operationflashpoint) [[Separate Query Port](#separate-query-port)]
* Painkiller (painkiller) [[Separate Query Port](#separate-query-port)] * Painkiller (painkiller) [[Separate Query Port](#separate-query-port)]
* Postal 2 (postal2) [[Separate Query Port](#separate-query-port)] * Postal 2 (postal2) [[Separate Query Port](#separate-query-port)]
* Prey (prey) [[Separate Query Port](#separate-query-port)] * Prey (prey) [[Separate Query Port](#separate-query-port)]
* Quake 1: QuakeWorld (quake1) * Quake 1: QuakeWorld (quake1)
* Quake 2 (quake2) * Quake 2 (quake2)
* Quake 3: Arena (quake3) * Quake 3: Arena (quake3)
* Quake 4 (quake4) * Quake 4 (quake4)
* Rag Doll Kung Fu (ragdollkungfu) * Rag Doll Kung Fu (ragdollkungfu)
* Rainbow Six (r6) [[Separate Query Port](#separate-query-port)] * Rainbow Six (r6) [[Separate Query Port](#separate-query-port)]
* Rainbow Six 2: Rogue Spear (r6roguespear) [[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)] * Rainbow Six 3: Raven Shield (r6ravenshield) [[Separate Query Port](#separate-query-port)]
* RalliSport Challenge (rallisportchallenge) [[Separate Query Port](#separate-query-port)] * RalliSport Challenge (rallisportchallenge) [[Separate Query Port](#separate-query-port)]
* Rally Masters (rallymasters) [[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 (redorchestra) [[Separate Query Port](#separate-query-port)]
* Red Orchestra: Ostfront 41-45 (redorchestraost) [[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)] * Red Orchestra 2 (redorchestra2) [[Separate Query Port](#separate-query-port)]
* Redline (redline) [[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)] * Return to Castle Wolfenstein (rtcw) [[Separate Query Port](#separate-query-port)]
* Ricochet (ricochet) * Ricochet (ricochet)
* Rise of Nations (riseofnations) [[Separate Query Port](#separate-query-port)] * Rise of Nations (riseofnations) [[Separate Query Port](#separate-query-port)]
* Rune (rune) [[Separate Query Port](#separate-query-port)] * Rune (rune) [[Separate Query Port](#separate-query-port)]
* Rust (rust) [[Separate Query Port](#separate-query-port)] * Rust (rust) [[Separate Query Port](#separate-query-port)]
* San Andreas Multiplayer (samp) * San Andreas Multiplayer (samp)
* Serious Sam (ss) [[Separate Query Port](#separate-query-port)] * Serious Sam (ss) [[Separate Query Port](#separate-query-port)]
* Serious Sam 2 (ss2) * Serious Sam 2 (ss2)
* Shattered Horizon (shatteredhorizon) * Shattered Horizon (shatteredhorizon)
* The Ship (ship) * The Ship (ship)
* Shogo (shogo) [[Separate Query Port](#separate-query-port)] * Shogo (shogo) [[Separate Query Port](#separate-query-port)]
* Shootmania (shootmania) [[Additional Notes](#nadeo-shootmania--trackmania--etc)] * Shootmania (shootmania) [[Additional Notes](#nadeo-shootmania--trackmania--etc)]
* SiN (sin) [[Separate Query Port](#separate-query-port)] * SiN (sin) [[Separate Query Port](#separate-query-port)]
* SiN Episodes (sinep) * SiN Episodes (sinep)
* Soldat (soldat) [[Separate Query Port](#separate-query-port)] * Soldat (soldat) [[Separate Query Port](#separate-query-port)]
* Soldier of Fortune (sof) [[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)] * 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)] * 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: 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 (stvef) [[Separate Query Port](#separate-query-port)]
* Star Trek: Voyager - Elite Force 2 (stvef2) [[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 (swbf) [[Separate Query Port](#separate-query-port)]
* Star Wars: Battlefront 2 (swbf2) [[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 (swjk) [[Separate Query Port](#separate-query-port)]
* Star Wars: Jedi Knight 2 (swjk2) [[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)] * Star Wars: Republic Commando (swrc) [[Separate Query Port](#separate-query-port)]
* Starbound (starbound) * Starbound (starbound)
* Suicide Survival (suicidesurvival) * Suicide Survival (suicidesurvival)
* SWAT 4 (swat4) [[Separate Query Port](#separate-query-port)] * SWAT 4 (swat4) [[Separate Query Port](#separate-query-port)]
* Sven Coop (svencoop) * Sven Coop (svencoop)
* Synergy (synergy) * Synergy (synergy)
* Tactical Ops (tacticalops) [[Separate Query Port](#separate-query-port)] * Tactical Ops (tacticalops) [[Separate Query Port](#separate-query-port)]
* Team Factor (teamfactor) [[Separate Query Port](#separate-query-port)] * Team Factor (teamfactor) [[Separate Query Port](#separate-query-port)]
* Team Fortress Classic (tfc) * Team Fortress Classic (tfc)
* Team Fortress 2 (tf2) * Team Fortress 2 (tf2)
* Teamspeak 2 (teamspeak2) [[Separate Query Port](#separate-query-port)] * Teamspeak 2 (teamspeak2) [[Separate Query Port](#separate-query-port)]
* Teamspeak 3 (teamspeak3) [[Separate Query Port](#separate-query-port)] * Teamspeak 3 (teamspeak3) [[Separate Query Port](#separate-query-port)]
* Terminus (terminus) [[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)] * 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 3 (thps3) [[Separate Query Port](#separate-query-port)]
* Tony Hawk's Pro Skater 4 (thps4) [[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)] * Tony Hawk's Underground 2 (thu2) [[Separate Query Port](#separate-query-port)]
* Trackmania 2 (trackmania2) [[Additional Notes](#nadeo-shootmania--trackmania--etc)] * Trackmania 2 (trackmania2) [[Additional Notes](#nadeo-shootmania--trackmania--etc)]
* Trackmania Forever (trackmaniaforever) [[Additional Notes](#nadeo-shootmania--trackmania--etc)] * Trackmania Forever (trackmaniaforever) [[Additional Notes](#nadeo-shootmania--trackmania--etc)]
* Tremulous (tremulous) [[Separate Query Port](#separate-query-port)] * Tremulous (tremulous) [[Separate Query Port](#separate-query-port)]
* Tribes: Vengeance (tribesvengeance) [[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)] * Tron 2.0 (tron20) [[Separate Query Port](#separate-query-port)]
* Turok 2 (turok2) [[Separate Query Port](#separate-query-port)] * Turok 2 (turok2) [[Separate Query Port](#separate-query-port)]
* Universal Combat (universalcombat) [[Separate Query Port](#separate-query-port)] * Universal Combat (universalcombat) [[Separate Query Port](#separate-query-port)]
* Unreal (unreal) [[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 (ut) [[Separate Query Port](#separate-query-port)]
* Unreal Tournament 2003 (ut2003) [[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 2004 (ut2004) [[Separate Query Port](#separate-query-port)]
* Unreal Tournament 3 (ut3) [[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)] * Urban Terror (urbanterror) [[Separate Query Port](#separate-query-port)]
* V8 Supercar Challenge (v8supercar) [[Separate Query Port](#separate-query-port)] * V8 Supercar Challenge (v8supercar) [[Separate Query Port](#separate-query-port)]
* Ventrilo (ventrilo) * Ventrilo (ventrilo)
* Vietcong (vietcong) [[Separate Query Port](#separate-query-port)] * Vietcong (vietcong) [[Separate Query Port](#separate-query-port)]
* Vietcong 2 (vietcong2) [[Separate Query Port](#separate-query-port)] * Vietcong 2 (vietcong2) [[Separate Query Port](#separate-query-port)]
* Warsow (warsow) * Warsow (warsow)
* Wheel of Time (wheeloftime) [[Separate Query Port](#separate-query-port)] * Wheel of Time (wheeloftime) [[Separate Query Port](#separate-query-port)]
* Wolfenstein 2009 (wolfenstein2009) [[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)] * Wolfenstein: Enemy Territory (wolfensteinet) [[Separate Query Port](#separate-query-port)]
* Xpand Rally (xpandrally) [[Separate Query Port](#separate-query-port)] * Xpand Rally (xpandrally) [[Separate Query Port](#separate-query-port)]
* Zombie Master (zombiemaster) * Zombie Master (zombiemaster)
* Zombie Panic: Source (zps) * Zombie Panic: Source (zps)
<!--- END GENERATED GAMES --> <!--- END GENERATED GAMES -->
###Not supported (yet) ###Not supported (yet)
* rFactor Engine (rfactor): * rFactor Engine (rfactor):
* rFactor * rFactor
* Arca Sim Racing * Arca Sim Racing
* Cube Engine (cube): * Cube Engine (cube):
* Cube 1 * Cube 1
* Assault Cube * Assault Cube
* Cube 2: Sauerbraten * Cube 2: Sauerbraten
* Blood Frontier * Blood Frontier
* BFRIS * BFRIS
* Call of Duty: Black Ops 1 and 2 (no documentation, may require rcon) * Call of Duty: Black Ops 1 and 2 (no documentation, may require rcon)
* Counter-Strike 2D * Counter-Strike 2D
* Freelancer * Freelancer
* Ghost Recon * Ghost Recon
* GTR2 * GTR2
* Haze * Haze
* Hexen 2 * Hexen 2
* OpenTTD * OpenTTD
* Plain Sight * Plain Sight
* Red Faction * Red Faction
* Savage: Battle for Newerth * Savage: Battle for Newerth
* Savage 2: A Tortured Soul * Savage 2: A Tortured Soul
* Sum of All Fears * Sum of All Fears
* Teeworlds * Teeworlds
* Tribes 1: Starsiege * Tribes 1: Starsiege
* Tribes 2 * Tribes 2
* Vice City Multiplayer * Vice City Multiplayer
* World in Conflict * World in Conflict
> Want support for one of these games? Please open an issue to show your interest! > 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 > __Know how to code?__ Protocols for most of the games above are documented
> in the /reference folder, ready for you to develop into GameDig! > in the /reference folder, ready for you to develop into GameDig!
<!-- --> <!-- -->
> Don't see your game listed here? > Don't see your game listed here?
> >
> First, let us know so we can fix it. Then, you can try using some common query > 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: > protocols directly by using one of these server types:
> * protocol-ase > * protocol-ase
> * protocol-battlefield > * protocol-battlefield
> * protocol-doom3 > * protocol-doom3
> * protocol-gamespy1 > * protocol-gamespy1
> * protocol-gamespy2 > * protocol-gamespy2
> * protocol-gamespy3 > * protocol-gamespy3
> * protocol-nadeo > * protocol-nadeo
> * protocol-quake2 > * protocol-quake2
> * protocol-quake3 > * protocol-quake3
> * protocol-unreal2 > * protocol-unreal2
> * protocol-valve > * protocol-valve
Games with Additional Notes Games with Additional Notes
--- ---
### DayZ ### DayZ
DayZ uses a query port that is separate from its main game port. The query port is usually 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. the game port PLUS 24714 or 24715. You may need to pass this port in as the 'port_query' request option.
### Minecraft ### Minecraft
Some minecraft servers may not respond to a typical status query. If this is the case, try using the 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. 'minecraftping' server type instead, which uses a less accurate but more reliable solution.
### Mumble ### Mumble
For full query results from Mumble, you must be running the For full query results from Mumble, you must be running the
[GTmurmur plugin](http://www.gametracker.com/downloads/gtmurmurplugin.php). [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, 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 you can use the 'mumbleping' server type instead, which uses a less accurate but more reliable solution
### Nadeo (ShootMania / TrackMania / etc) ### Nadeo (ShootMania / TrackMania / etc)
The server must have xmlrpc enabled, and you must pass the xmlrpc port to GameDig, not the connection port. 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. 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 Pass the login into to GameDig with the additional options: login, password
### Terraria ### Terraria
Requires tshock server mod, and a REST user token, which can be passed to GameDig with the Requires tshock server mod, and a REST user token, which can be passed to GameDig with the
additional option: token additional option: token
### Separate Query Port ### Separate Query Port
Games with this note use a query port which is usually not the same as the game's connection 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 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, 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. you may need to pass the 'port_query' option to GameDig as well, indicating the separate query port.
Usage from Command Line Usage from Command Line
--- ---
Want to integrate server queries from a batch script or other programming language? Want to integrate server queries from a batch script or other programming language?
You'll still need npm to install gamedig: You'll still need npm to install gamedig:
```shell ```shell
npm install gamedig -g npm install gamedig -g
``` ```
After installing gamedig globally, you can call gamedig via the command line After installing gamedig globally, you can call gamedig via the command line
using the same parameters mentioned in the API above: using the same parameters mentioned in the API above:
```shell ```shell
gamedig --type minecraft --host mc.example.com --port 11234 gamedig --type minecraft --host mc.example.com --port 11234
``` ```
The output of the command will be in JSON format. The output of the command will be in JSON format.

View file

@ -1,34 +1,34 @@
#!/usr/bin/env node #!/usr/bin/env node
var argv = require('optimist').argv; var argv = require('optimist').argv;
var debug = argv.debug; var debug = argv.debug;
delete argv.debug; delete argv.debug;
var outputFormat = argv.output; var outputFormat = argv.output;
delete argv.output; delete argv.output;
var options = {}; var options = {};
for(var key in argv) { for(var key in argv) {
var value = argv[key]; var value = argv[key];
if( if(
key == '_' key == '_'
|| key.charAt(0) == '$' || key.charAt(0) == '$'
|| (typeof value != 'string' && typeof value != 'number') || (typeof value != 'string' && typeof value != 'number')
) )
continue; continue;
options[key] = value; options[key] = value;
} }
var Gamedig = require('../lib/index'); var Gamedig = require('../lib/index');
if(debug) Gamedig.debug = true; if(debug) Gamedig.debug = true;
Gamedig.isCommandLine = true; Gamedig.isCommandLine = true;
Gamedig.query( Gamedig.query(
options, options,
function(state) { function(state) {
if(outputFormat == 'pretty') { if(outputFormat == 'pretty') {
console.log(JSON.stringify(state,null,' ')); console.log(JSON.stringify(state,null,' '));
} else { } else {
console.log(JSON.stringify(state)); console.log(JSON.stringify(state));
} }
} }
); );

View file

@ -1,19 +1,19 @@
#!/usr/bin/env node #!/usr/bin/env node
var fs = require('fs'); var fs = require('fs');
var TypeResolver = require('../lib/typeresolver'); var TypeResolver = require('../lib/typeresolver');
var generated = TypeResolver.printReadme(); var generated = TypeResolver.printReadme();
var readmeFilename = __dirname+'/../README.md'; var readmeFilename = __dirname+'/../README.md';
var readme = fs.readFileSync(readmeFilename, {encoding:'utf8'}); var readme = fs.readFileSync(readmeFilename, {encoding:'utf8'});
var marker_top = '<!--- BEGIN GENERATED GAMES -->'; var marker_top = '<!--- BEGIN GENERATED GAMES -->';
var marker_bottom = '<!--- END GENERATED GAMES -->'; var marker_bottom = '<!--- END GENERATED GAMES -->';
var start = readme.indexOf(marker_top); var start = readme.indexOf(marker_top);
start += marker_top.length; start += marker_top.length;
var end = readme.indexOf(marker_bottom); var end = readme.indexOf(marker_bottom);
var updated = readme.substr(0,start)+"\n\n"+generated+"\n"+readme.substr(end); var updated = readme.substr(0,start)+"\n\n"+generated+"\n"+readme.substr(end);
fs.writeFileSync(readmeFilename, updated); fs.writeFileSync(readmeFilename, updated);

546
games.txt
View file

@ -1,273 +1,273 @@
# id | pretty | protocol | options | parameters # id | pretty | protocol | options | parameters
#### TODO: #### TODO:
# cube1|Cube 1|cube|port=28786,port_query_offset=1 # cube1|Cube 1|cube|port=28786,port_query_offset=1
# assaultcube|Assault Cube|cube|port_query=28764 # assaultcube|Assault Cube|cube|port_query=28764
# cube2|Cube 2: Sauerbraten|cube|port=28785,port_query_offset=1 # cube2|Cube 2: Sauerbraten|cube|port=28785,port_query_offset=1
# bloodfrontier|Blood Frontier|cube # bloodfrontier|Blood Frontier|cube
# arcasimracing|Arca Sim Racing|rfactor|port=34397,port_query_offset=-100 # arcasimracing|Arca Sim Racing|rfactor|port=34397,port_query_offset=-100
# rfactor|rFactor|rfactor|port=34397,port_query_offset=-100 # rfactor|rFactor|rfactor|port=34397,port_query_offset=-100
# bfris|BFRIS|bfris|port=44001 # bfris|BFRIS|bfris|port=44001
# cs2d|Counter-Strike: 2D|cs2d|port_query=36963 # cs2d|Counter-Strike: 2D|cs2d|port_query=36963
# freelancer|Freelancer|freelancer|port_query=2302 # freelancer|Freelancer|freelancer|port_query=2302
# gr|Ghost Recon|ghostrecon|port=2346,port_query_offset=2 # gr|Ghost Recon|ghostrecon|port=2346,port_query_offset=2
# gtr2|GTR2|gtr2|port=34297,port_query_offset=1 # gtr2|GTR2|gtr2|port=34297,port_query_offset=1
# haze|Haze|haze # haze|Haze|haze
# hexen2|Hexen 2|hexen2|port_query=26900 # hexen2|Hexen 2|hexen2|port_query=26900
# openttd|OpenTTD|openttd|port=3979 # openttd|OpenTTD|openttd|port=3979
# plainsight|Plain Sight|plainsight # plainsight|Plain Sight|plainsight
# redfaction|Red Faction|redfaction|port_query=7755 # redfaction|Red Faction|redfaction|port_query=7755
# savage|Savage|savage|port_query=11235 # savage|Savage|savage|port_query=11235
# savage2|Savage 2|savage2|port_query=11235 # savage2|Savage 2|savage2|port_query=11235
# teeworlds|Teeworlds|teeworlds|port=8303 # teeworlds|Teeworlds|teeworlds|port=8303
# tribes|Tribes 1: Starsiege|tribes|port_query=28001 # tribes|Tribes 1: Starsiege|tribes|port_query=28001
# tribes2|Tribes 2|tribes2|port_query=28000 # tribes2|Tribes 2|tribes2|port_query=28000
# vcmp|Vice City Multiplayer|vcmp # vcmp|Vice City Multiplayer|vcmp
# worldinconflict|World in Conflict|worldinconflict # worldinconflict|World in Conflict|worldinconflict
ageofchivalry|Age of Chivalry|valve ageofchivalry|Age of Chivalry|valve
aoe2|Age of Empires 2|ase|port_query=27224 aoe2|Age of Empires 2|ase|port_query=27224
alienarena|Alien Arena|quake2|port_query=27910 alienarena|Alien Arena|quake2|port_query=27910
alienswarm|Alien Swarm|valve alienswarm|Alien Swarm|valve
avp2|Aliens vs Predator 2|gamespy1|port=27888 avp2|Aliens vs Predator 2|gamespy1|port=27888
# avp2010 doesn't really... have a default port or query port # avp2010 doesn't really... have a default port or query port
# both port and port_query should be specified when used # both port and port_query should be specified when used
avp2010|Aliens vs Predator 2010|valve avp2010|Aliens vs Predator 2010|valve
americasarmy|America's Army|americasarmy|port=1716,port_query_offset=1 americasarmy|America's Army|americasarmy|port=1716,port_query_offset=1
americasarmy2|America's Army 2|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 americasarmy3|America's Army 3|valve|port=8777,port_query=27020
americasarmypg|America's Army: Proving Grounds|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 arma|ArmA Armed Assault 1|gamespy2|port=2302
arma2|ArmA Armed Assault 2|valve|port=2302,port_query_offset=1 arma2|ArmA Armed Assault 2|valve|port=2302,port_query_offset=1
arma3|ArmA Armed Assault 3|valve|port=2302,port_query_offset=1 arma3|ArmA Armed Assault 3|valve|port=2302,port_query_offset=1
armagetron|Armagetron|armagetron|port=4534 armagetron|Armagetron|armagetron|port=4534
baldursgate|Baldur's Gate|gamespy1|port=6073,port_query=1470 baldursgate|Baldur's Gate|gamespy1|port=6073,port_query=1470
bf1942|Battlefield 1942|gamespy1|port=14567,port_query=23000 bf1942|Battlefield 1942|gamespy1|port=14567,port_query=23000
bfv|Battlefield Vietnam|gamespy2|port=15567,port_query=23000 bfv|Battlefield Vietnam|gamespy2|port=15567,port_query=23000
bf2|Battlefield 2|gamespy3|port=16567,port_query=29900|noChallenge bf2|Battlefield 2|gamespy3|port=16567,port_query=29900|noChallenge
bf2142|Battlefield 2142|gamespy3|port=16567,port_query=29900 bf2142|Battlefield 2142|gamespy3|port=16567,port_query=29900
bfbc2|Battlefield: Bad Company 2|battlefield|port=19567,port_query=48888|isBadCompany2 bfbc2|Battlefield: Bad Company 2|battlefield|port=19567,port_query=48888|isBadCompany2
bf3|Battlefield 3|battlefield|port=25200,port_query_offset=22000 bf3|Battlefield 3|battlefield|port=25200,port_query_offset=22000
bf4|Battlefield 4|battlefield|port=25200,port_query_offset=22000 bf4|Battlefield 4|battlefield|port=25200,port_query_offset=22000
breach|Breach|valve|port=27016 breach|Breach|valve|port=27016
breed|Breed|gamespy2|port=7649 breed|Breed|gamespy2|port=7649
brink|Brink|valve|port_query_offset=1 brink|Brink|valve|port_query_offset=1
buildandshoot|Build and Shoot|buildandshoot|port=32887,port_query=32886 buildandshoot|Build and Shoot|buildandshoot|port=32887,port_query=32886
cod|Call of Duty|quake3|port=28960 cod|Call of Duty|quake3|port=28960
coduo|Call of Duty: United Offensive|quake3|port=28960 coduo|Call of Duty: United Offensive|quake3|port=28960
cod2|Call of Duty 2|quake3|port=28960 cod2|Call of Duty 2|quake3|port=28960
cod3|Call of Duty 3|quake3|port=28960 cod3|Call of Duty 3|quake3|port=28960
cod4|Call of Duty 4: Modern Warfare|quake3|port=28960 cod4|Call of Duty 4: Modern Warfare|quake3|port=28960
codwaw|Call of Duty: World at War|quake3|port=28960 codwaw|Call of Duty: World at War|quake3|port=28960
codmw2|Call of Duty: Modern Warfare 2|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 codmw3|Call of Duty: Modern Warfare 3|valve|port_query_offset=2
callofjuarez|Call of Juarez|ase|port_query=26000 callofjuarez|Call of Juarez|ase|port_query=26000
chaser|Chaser|ase|port=3000,port_query_offset=123 chaser|Chaser|ase|port=3000,port_query_offset=123
chrome|Chrome|ase|port=27015,port_query_offset=123 chrome|Chrome|ase|port=27015,port_query_offset=123
codenameeagle|Codename Eagle|gamespy1|port_query=4711 codenameeagle|Codename Eagle|gamespy1|port_query=4711
commandos3|Commandos 3: Destination Berlin|gamespy1|port_query=6500 commandos3|Commandos 3: Destination Berlin|gamespy1|port_query=6500
cacrenegade|Command and Conquer: Renegade|gamespy1|port=4848,port_query=25300 cacrenegade|Command and Conquer: Renegade|gamespy1|port=4848,port_query=25300
contactjack|Contact J.A.C.K.|gamespy1|port_query=27888 contactjack|Contact J.A.C.K.|gamespy1|port_query=27888
cs16|Counter-Strike 1.6|valve cs16|Counter-Strike 1.6|valve
cscz|Counter-Strike: Condition Zero|valve cscz|Counter-Strike: Condition Zero|valve
css|Counter-Strike: Source|valve css|Counter-Strike: Source|valve
csgo|Counter-Strike: Global Offensive|valve||isCsGo csgo|Counter-Strike: Global Offensive|valve||isCsGo
crossracing|Cross Racing Championship|ase|port=12321,port_query_offset=123 crossracing|Cross Racing Championship|ase|port=12321,port_query_offset=123
crysis|Crysis|gamespy3|port=64087 crysis|Crysis|gamespy3|port=64087
crysiswars|Crysis Wars|gamespy3|port=64100 crysiswars|Crysis Wars|gamespy3|port=64100
crysis2|Crysis 2|gamespy3|port=64000 crysis2|Crysis 2|gamespy3|port=64000
daikatana|Daikatana|quake2|port=27982,port_query_offset=10 daikatana|Daikatana|quake2|port=27982,port_query_offset=10
dmomam|Dark Messiah of Might and Magic|valve dmomam|Dark Messiah of Might and Magic|valve
darkesthour|Darkest Hour|unreal2|port=7757,port_query_offset=1 darkesthour|Darkest Hour|unreal2|port=7757,port_query_offset=1
dayz|DayZ|valve|port=2302,port_query_offset=24714|doc_notes=dayz dayz|DayZ|valve|port=2302,port_query_offset=24714|doc_notes=dayz
deadlydozenpt|Deadly Dozen: Pacific Theater|gamespy1|port_query=25300 deadlydozenpt|Deadly Dozen: Pacific Theater|gamespy1|port_query=25300
dh2005|Deer Hunter 2005|gamespy2|port=23459,port_query=34567 dh2005|Deer Hunter 2005|gamespy2|port=23459,port_query=34567
descent3|Descent 3|gamespy1|port=2092,port_query=20142 descent3|Descent 3|gamespy1|port=2092,port_query=20142
deusex|Deus Ex|gamespy2|port=7791,port_query_offset=1 deusex|Deus Ex|gamespy2|port=7791,port_query_offset=1
devastation|Devastation|unreal2|port=7777,port_query_offset=1 devastation|Devastation|unreal2|port=7777,port_query_offset=1
dinodday|Dino D-Day|valve dinodday|Dino D-Day|valve
dirttrackracing2|Dirt Track Racing 2|gamespy1|port=32240,port_query_offset=-100 dirttrackracing2|Dirt Track Racing 2|gamespy1|port=32240,port_query_offset=-100
dod|Day of Defeat|valve dod|Day of Defeat|valve
dods|Day of Defeat: Source|valve dods|Day of Defeat: Source|valve
doom3|Doom 3|doom3|port=27666 doom3|Doom 3|doom3|port=27666
dota2|DOTA 2|valve dota2|DOTA 2|valve
drakan|Drakan|gamespy1|port=27045,port_query_offset=1 drakan|Drakan|gamespy1|port=27045,port_query_offset=1
etqw|Enemy Territory Quake Wars|doom3|port=3074,port_query=27733|isEtqw,hasSpaceBeforeClanTag,hasClanTag,hasTypeFlag etqw|Enemy Territory Quake Wars|doom3|port=3074,port_query=27733|isEtqw,hasSpaceBeforeClanTag,hasClanTag,hasTypeFlag
fear|F.E.A.R.|gamespy2|port_query=27888 fear|F.E.A.R.|gamespy2|port_query=27888
f12002|F1 2002|gamespy1|port_query=3297 f12002|F1 2002|gamespy1|port_query=3297
f1c9902|F1 Challenge 99-02|gamespy1|port_query=34397 f1c9902|F1 Challenge 99-02|gamespy1|port_query=34397
farcry|Far Cry|ase|port=49001,port_query_offset=123 farcry|Far Cry|ase|port=49001,port_query_offset=123
farcry2|Far Cry|ase|port_query=14001 farcry2|Far Cry|ase|port_query=14001
fortressforever|Fortress Forever|valve fortressforever|Fortress Forever|valve
flashpoint|Flashpoint|gamespy1|port=2302,port_query_offset=1 flashpoint|Flashpoint|gamespy1|port=2302,port_query_offset=1
ffow|Frontlines: Fuel of War|ffow|port=5476,port_query_offset=2 ffow|Frontlines: Fuel of War|ffow|port=5476,port_query_offset=2
garrysmod|Garry's Mod|valve garrysmod|Garry's Mod|valve
graw|Ghost Recon: Advanced Warfighter|gamespy2|port_query=15250 graw|Ghost Recon: Advanced Warfighter|gamespy2|port_query=15250
graw2|Ghost Recon: Advanced Warfighter 2|gamespy2|port_query=16250 graw2|Ghost Recon: Advanced Warfighter 2|gamespy2|port_query=16250
giantscitizenkabuto|Giants: Citizen Kabuto|gamespy1|port_query=8911 giantscitizenkabuto|Giants: Citizen Kabuto|gamespy1|port_query=8911
globaloperations|Global Operations|gamespy1|port_query=28672 globaloperations|Global Operations|gamespy1|port_query=28672
gore|Gore|gamespy1|port=27777,port_query_offset=1 gore|Gore|gamespy1|port=27777,port_query_offset=1
gunmanchronicles|Gunman Chronicles|valve gunmanchronicles|Gunman Chronicles|valve
hldm|Half-Life 1 Deathmatch|valve hldm|Half-Life 1 Deathmatch|valve
hl2dm|Half-Life 2 Deathmatch|valve hl2dm|Half-Life 2 Deathmatch|valve
halo|Halo|gamespy2|port=2302 halo|Halo|gamespy2|port=2302
halo2|Halo 2|gamespy2|port=2302 halo2|Halo 2|gamespy2|port=2302
heretic2|Heretic 2|gamespy1|port_query=28910 heretic2|Heretic 2|gamespy1|port_query=28910
hexenworld|Hexen World|hexenworld|port_query=26950 hexenworld|Hexen World|hexenworld|port_query=26950
hidden|The Hidden: Source|valve hidden|The Hidden: Source|valve
had2|Hidden and Dangerous 2|gamespy1|port=11001,port_query_offset=3 had2|Hidden and Dangerous 2|gamespy1|port=11001,port_query_offset=3
homefront|Homefront|valve homefront|Homefront|valve
homeworld2|Homeworld 2|gamespy1|port_query=6500 homeworld2|Homeworld 2|gamespy1|port_query=6500
igi2|IGI-2: Covert Strike|gamespy1|port_query=26001 igi2|IGI-2: Covert Strike|gamespy1|port_query=26001
il2|IL-2 Sturmovik|gamespy1|port_query=21000 il2|IL-2 Sturmovik|gamespy1|port_query=21000
insurgency|Insurgency|valve insurgency|Insurgency|valve
ironstorm|Iron Storm|gamespy1|port_query=3505 ironstorm|Iron Storm|gamespy1|port_query=3505
jamesbondnightfire|James Bond: Nightfire|gamespy1|port_query=6550 jamesbondnightfire|James Bond: Nightfire|gamespy1|port_query=6550
jc2mp|Just Cause 2 Multiplayer|jc2mp|port=7777 jc2mp|Just Cause 2 Multiplayer|jc2mp|port=7777
killingfloor|Killing Floor|killingfloor|port=7707,port_query_offset=1 killingfloor|Killing Floor|killingfloor|port=7707,port_query_offset=1
kingpin|Kingpin: Life of Crime|gamespy1|port=31510,port_query_offset=-10 kingpin|Kingpin: Life of Crime|gamespy1|port=31510,port_query_offset=-10
kisspc|KISS Psycho Circus|gamespy1|port=7777,port_query_offset=1 kisspc|KISS Psycho Circus|gamespy1|port=7777,port_query_offset=1
kzmod|KzMod|valve kzmod|KzMod|valve
left4dead|Left 4 Dead|valve left4dead|Left 4 Dead|valve
left4dead2|Left 4 Dead 2|valve left4dead2|Left 4 Dead 2|valve
m2mp|Mafia 2 Multiplayer|m2mp|port=27016,port_query_offset=1 m2mp|Mafia 2 Multiplayer|m2mp|port=27016,port_query_offset=1
mohaa|Medal of Honor: Allied Assault|gamespy1|port=12203,port_query_offset=97 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 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 mohab|Medal of Honor: Airborne|gamespy1|port=12203,port_query_offset=97
mohsh|Medal of Honor: Spearhead|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 mohbt|Medal of Honor: Breakthrough|gamespy1|port=12203,port_query_offset=97
moh2010|Medal of Honor 2010|battlefield|port=7673,port_query=48888 moh2010|Medal of Honor 2010|battlefield|port=7673,port_query=48888
mohwf|Medal of Honor: Warfighter|battlefield|port=25200,port_query_offset=22000 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 minecraft|Minecraft|gamespy3|port=25565|maxAttempts=2,srvRecord=_minecraft._tcp,doc_notes=minecraft
minecraftping|Minecraft|minecraftping|port=25565|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 mnc|Monday Night Combat|valve|port=7777,port_query=27016
mtavc|Multi Theft Auto: Vice City|ase|port=22003,port_query_offset=123 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 mtasa|Multi Theft Auto: San Andreas|ase|port=22003,port_query_offset=123
mumble|Mumble|mumble|port=64738,port_query=27800|doc_notes=mumble mumble|Mumble|mumble|port=64738,port_query=27800|doc_notes=mumble
mumbleping|Mumble|mumbleping|port=64738|doc_notes=mumble mumbleping|Mumble|mumbleping|port=64738|doc_notes=mumble
mutantfactions|Mutant Factions|mutantfactions|port=11235 mutantfactions|Mutant Factions|mutantfactions|port=11235
nascarthunder2004|Nascar Thunder 2004|gamespy2|port_query=13333 nascarthunder2004|Nascar Thunder 2004|gamespy2|port_query=13333
netpanzer|netPanzer|gamespy1|3030 netpanzer|netPanzer|gamespy1|3030
nmrih|No More Room in Hell|valve nmrih|No More Room in Hell|valve
ns|Natural Selection|valve ns|Natural Selection|valve
ns2|Natural Selection 2|valve|port_query_offset=1 ns2|Natural Selection 2|valve|port_query_offset=1
nfshp2|Need for Speed: Hot Pursuit 2|gamespy1|port_query=61220 nfshp2|Need for Speed: Hot Pursuit 2|gamespy1|port_query=61220
nab|Nerf Arena Blast|gamespy1|port=4444,port_query_offset=1 nab|Nerf Arena Blast|gamespy1|port=4444,port_query_offset=1
nwn|Neverwinter Nights|gamespy2|port_query=5121 nwn|Neverwinter Nights|gamespy2|port_query=5121
nwn2|Neverwinter Nights 2|gamespy2|port=5121,port_query=6500 nwn2|Neverwinter Nights 2|gamespy2|port=5121,port_query=6500
nexuiz|Nexuiz|quake3|port_query=26000 nexuiz|Nexuiz|quake3|port_query=26000
nitrofamily|Nitro Family|gamespy1|port_query=25601 nitrofamily|Nitro Family|gamespy1|port_query=25601
nolf|No One Lives Forever|gamespy1|port_query=27888 nolf|No One Lives Forever|gamespy1|port_query=27888
nolf2|No One Lives Forever 2|gamespy1|port_query=27890 nolf2|No One Lives Forever 2|gamespy1|port_query=27890
nucleardawn|Nuclear Dawn|valve nucleardawn|Nuclear Dawn|valve
openarena|OpenArena|quake3|port_query=27960 openarena|OpenArena|quake3|port_query=27960
operationflashpoint|Operation Flashpoint|gamespy1|port=2234,port_query_offset=1 operationflashpoint|Operation Flashpoint|gamespy1|port=2234,port_query_offset=1
painkiller|Painkiller|ase|port=3455,port_query_offset=123 painkiller|Painkiller|ase|port=3455,port_query_offset=123
postal2|Postal 2|gamespy1|port=7777,port_query_offset=1 postal2|Postal 2|gamespy1|port=7777,port_query_offset=1
prey|Prey|doom3|port_query=27719 prey|Prey|doom3|port_query=27719
quake1|Quake 1: QuakeWorld|quake1|port=27500 quake1|Quake 1: QuakeWorld|quake1|port=27500
quake2|Quake 2|quake2|port=27910 quake2|Quake 2|quake2|port=27910
quake3|Quake 3: Arena|quake3|port=27960 quake3|Quake 3: Arena|quake3|port=27960
quake4|Quake 4|doom3|port=28004|hasClanTag quake4|Quake 4|doom3|port=28004|hasClanTag
ragdollkungfu|Rag Doll Kung Fu|valve ragdollkungfu|Rag Doll Kung Fu|valve
r6|Rainbow Six|gamespy1|port_query=2348 r6|Rainbow Six|gamespy1|port_query=2348
r6roguespear|Rainbow Six 2: Rogue Spear|gamespy1|port_query=2346 r6roguespear|Rainbow Six 2: Rogue Spear|gamespy1|port_query=2346
r6ravenshield|Rainbow Six 3: Raven Shield|gamespy1|port=7777,port_query_offset=1000 r6ravenshield|Rainbow Six 3: Raven Shield|gamespy1|port=7777,port_query_offset=1000
rallisportchallenge|RalliSport Challenge|gamespy1|port_query=17500 rallisportchallenge|RalliSport Challenge|gamespy1|port_query=17500
rallymasters|Rally Masters|gamespy1|port_query=16666 rallymasters|Rally Masters|gamespy1|port_query=16666
redorchestra|Red Orchestra|unreal2|port=7758,port_query_offset=1 redorchestra|Red Orchestra|unreal2|port=7758,port_query_offset=1
redorchestraost|Red Orchestra: Ostfront 41-45|gamespy1|port=7757,port_query_offset=10 redorchestraost|Red Orchestra: Ostfront 41-45|gamespy1|port=7757,port_query_offset=10
redorchestra2|Red Orchestra 2|valve|port=7777,port_query=27015 redorchestra2|Red Orchestra 2|valve|port=7777,port_query=27015
redline|Redline|gamespy1|port_query=25252 redline|Redline|gamespy1|port_query=25252
rtcw|Return to Castle Wolfenstein|quake3|port_query=27960 rtcw|Return to Castle Wolfenstein|quake3|port_query=27960
ricochet|Ricochet|valve ricochet|Ricochet|valve
riseofnations|Rise of Nations|gamespy1|port_query=6501 riseofnations|Rise of Nations|gamespy1|port_query=6501
rune|Rune|gamespy1|port=7777,port_query_offset=1 rune|Rune|gamespy1|port=7777,port_query_offset=1
rust|Rust|valve|port=28015,port_query_offset=1 rust|Rust|valve|port=28015,port_query_offset=1
samp|San Andreas Multiplayer|samp|port=7777 samp|San Andreas Multiplayer|samp|port=7777
ss|Serious Sam|gamespy1|port=25600,port_query_offset=1 ss|Serious Sam|gamespy1|port=25600,port_query_offset=1
ss2|Serious Sam 2|gamespy2|port=25600 ss2|Serious Sam 2|gamespy2|port=25600
shatteredhorizon|Shattered Horizon|valve shatteredhorizon|Shattered Horizon|valve
ship|The Ship|valve ship|The Ship|valve
shogo|Shogo|gamespy1|port_query=27888 shogo|Shogo|gamespy1|port_query=27888
shootmania|Shootmania|nadeo||doc_notes=nadeo-shootmania--trackmania--etc shootmania|Shootmania|nadeo||doc_notes=nadeo-shootmania--trackmania--etc
sin|SiN|gamespy1|port_query=22450 sin|SiN|gamespy1|port_query=22450
sinep|SiN Episodes|valve sinep|SiN Episodes|valve
soldat|Soldat|ase|port=13073,port_query_offset=123 soldat|Soldat|ase|port=13073,port_query_offset=123
sof|Soldier of Fortune|quake1|port_query=28910 sof|Soldier of Fortune|quake1|port_query=28910
sof2|Soldier of Fortune 2|quake3|port_query=20100 sof2|Soldier of Fortune 2|quake3|port_query=20100
stalker|S.T.A.L.K.E.R.|gamespy3|port=5445,port_query_offset=2 stalker|S.T.A.L.K.E.R.|gamespy3|port=5445,port_query_offset=2
stbc|Star Trek: Bridge Commander|gamespy1|port_query=22101 stbc|Star Trek: Bridge Commander|gamespy1|port_query=22101
stvef|Star Trek: Voyager - Elite Force|quake3|port_query=27960 stvef|Star Trek: Voyager - Elite Force|quake3|port_query=27960
stvef2|Star Trek: Voyager - Elite Force 2|quake3|port_query=29253 stvef2|Star Trek: Voyager - Elite Force 2|quake3|port_query=29253
swbf|Star Wars: Battlefront|gamespy2|port_query=3658 swbf|Star Wars: Battlefront|gamespy2|port_query=3658
swbf2|Star Wars: Battlefront 2|gamespy2|port_query=3658 swbf2|Star Wars: Battlefront 2|gamespy2|port_query=3658
swjk|Star Wars: Jedi Knight|quake3|port_query=29070 swjk|Star Wars: Jedi Knight|quake3|port_query=29070
swjk2|Star Wars: Jedi Knight 2|quake3|port_query=28070 swjk2|Star Wars: Jedi Knight 2|quake3|port_query=28070
swrc|Star Wars: Republic Commando|gamespy2|port=7777,port_query=11138 swrc|Star Wars: Republic Commando|gamespy2|port=7777,port_query=11138
starbound|Starbound|valve|port=21025 starbound|Starbound|valve|port=21025
suicidesurvival|Suicide Survival|valve suicidesurvival|Suicide Survival|valve
swat4|SWAT 4|gamespy2|port=10480,port_query_offset=2 swat4|SWAT 4|gamespy2|port=10480,port_query_offset=2
svencoop|Sven Coop|valve svencoop|Sven Coop|valve
synergy|Synergy|valve synergy|Synergy|valve
tacticalops|Tactical Ops|gamespy1|port=7777,port_query_offset=1 tacticalops|Tactical Ops|gamespy1|port=7777,port_query_offset=1
teamfactor|Team Factor|gamespy1|port_query=57778 teamfactor|Team Factor|gamespy1|port_query=57778
tfc|Team Fortress Classic|valve tfc|Team Fortress Classic|valve
tf2|Team Fortress 2|valve tf2|Team Fortress 2|valve
teamspeak2|Teamspeak 2|teamspeak2|port=8767,port_query=51234 teamspeak2|Teamspeak 2|teamspeak2|port=8767,port_query=51234
teamspeak3|Teamspeak 3|teamspeak3|port=9987,port_query=10011 teamspeak3|Teamspeak 3|teamspeak3|port=9987,port_query=10011
terminus|Terminus|gamespy1|port_query=12286 terminus|Terminus|gamespy1|port_query=12286
terraria|Terraria|terraria|port=7777,port_query_offset=101|doc_notes=terraria terraria|Terraria|terraria|port=7777,port_query_offset=101|doc_notes=terraria
thps3|Tony Hawk's Pro Skater 3|gamespy1|port_query=6500 thps3|Tony Hawk's Pro Skater 3|gamespy1|port_query=6500
thps4|Tony Hawk's Pro Skater 4|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 thu2|Tony Hawk's Underground 2|gamespy1|port_query=5153
trackmania2|Trackmania 2|nadeo||doc_notes=nadeo-shootmania--trackmania--etc trackmania2|Trackmania 2|nadeo||doc_notes=nadeo-shootmania--trackmania--etc
trackmaniaforever|Trackmania Forever|nadeo||doc_notes=nadeo-shootmania--trackmania--etc trackmaniaforever|Trackmania Forever|nadeo||doc_notes=nadeo-shootmania--trackmania--etc
tremulous|Tremulous|quake3|port_query=30720 tremulous|Tremulous|quake3|port_query=30720
tribesvengeance|Tribes: Vengeance|gamespy2|port=7777,port_query_offset=1 tribesvengeance|Tribes: Vengeance|gamespy2|port=7777,port_query_offset=1
tron20|Tron 2.0|gamespy2|port_query=27888 tron20|Tron 2.0|gamespy2|port_query=27888
turok2|Turok 2|gamespy1|port_query=12880 turok2|Turok 2|gamespy1|port_query=12880
universalcombat|Universal Combat|ase|port=1135,port_query_offset=123 universalcombat|Universal Combat|ase|port=1135,port_query_offset=123
unreal|Unreal|gamespy1|port=7777,port_query_offset=1 unreal|Unreal|gamespy1|port=7777,port_query_offset=1
ut|Unreal Tournament|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 ut2003|Unreal Tournament 2003|unreal2|port=7757,port_query_offset=1
ut2004|Unreal Tournament 2004|ut2004|port=7777,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 ut3|Unreal Tournament 3|ut3|port=7777,port_query_offset=-1277
urbanterror|Urban Terror|quake3|port_query=27960 urbanterror|Urban Terror|quake3|port_query=27960
v8supercar|V8 Supercar Challenge|gamespy1|port_query=16700 v8supercar|V8 Supercar Challenge|gamespy1|port_query=16700
ventrilo|Ventrilo|ventrilo|port=3784 ventrilo|Ventrilo|ventrilo|port=3784
vietcong|Vietcong|gamespy1|port=5425,port_query=15425 vietcong|Vietcong|gamespy1|port=5425,port_query=15425
vietcong2|Vietcong 2|gamespy2|port=5001,port_query=19967 vietcong2|Vietcong 2|gamespy2|port=5001,port_query=19967
warsow|Warsow|warsow|port=44400 warsow|Warsow|warsow|port=44400
wheeloftime|Wheel of Time|gamespy1|port=7777,port_query_offset=1 wheeloftime|Wheel of Time|gamespy1|port=7777,port_query_offset=1
wolfenstein2009|Wolfenstein 2009|doom3|port_query=27666|hasSpaceBeforeClanTag,hasClanTag,hasTypeFlag wolfenstein2009|Wolfenstein 2009|doom3|port_query=27666|hasSpaceBeforeClanTag,hasClanTag,hasTypeFlag
wolfensteinet|Wolfenstein: Enemy Territory|quake3|port_query=27960 wolfensteinet|Wolfenstein: Enemy Territory|quake3|port_query=27960
xpandrally|Xpand Rally|ase|port=28015,port_query_offset=123 xpandrally|Xpand Rally|ase|port=28015,port_query_offset=123
zombiemaster|Zombie Master|valve zombiemaster|Zombie Master|valve
zps|Zombie Panic: Source|valve zps|Zombie Panic: Source|valve

View file

@ -1,74 +1,74 @@
/* based on Simple JavaScript Inheritance /* based on Simple JavaScript Inheritance
* By John Resig http://ejohn.org/ * By John Resig http://ejohn.org/
* MIT Licensed. * MIT Licensed.
*/ */
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing) // The base Class implementation (does nothing)
var Class = function(){}; var Class = function(){};
// Create a new Class that inherits from this class // Create a new Class that inherits from this class
Class.extend = function() { Class.extend = function() {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
var name = 'Class'; var name = 'Class';
var parent = this; var parent = this;
var prop = {}; var prop = {};
if(typeof args[0] == 'string') name = args.shift(); if(typeof args[0] == 'string') name = args.shift();
if(args.length >= 2) parent = args.shift(); if(args.length >= 2) parent = args.shift();
prop = args.shift(); prop = args.shift();
// Copy prototype from the parent object // Copy prototype from the parent object
var prototype = {}; var prototype = {};
for(var name in parent.prototype) { for(var name in parent.prototype) {
prototype[name] = parent.prototype[name]; prototype[name] = parent.prototype[name];
} }
// Copy the properties over onto the new prototype // Copy the properties over onto the new prototype
for(var name in prop) { for(var name in prop) {
if(typeof prop[name] == "function" && fnTest.test(prop[name])) { if(typeof prop[name] == "function" && fnTest.test(prop[name])) {
// this is a function that references _super, so we have to wrap it // this is a function that references _super, so we have to wrap it
// and provide it with its super function // and provide it with its super function
prototype[name] = (function(name, fn){ prototype[name] = (function(name, fn){
return function() { return function() {
var tmp = this._super; var tmp = this._super;
// Add a new ._super() method that is the same method // Add a new ._super() method that is the same method
// but on the super-class // but on the super-class
if(typeof parent.prototype[name] == 'undefined') { if(typeof parent.prototype[name] == 'undefined') {
if(name == 'init') this._super = parent.prototype.constructor; 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 = function() { throw new Error('Called _super in method without a parent'); }
} else this._super = parent.prototype[name]; } else this._super = parent.prototype[name];
// The method only need to be bound temporarily, so we // The method only need to be bound temporarily, so we
// remove it when we're done executing // remove it when we're done executing
var ret = fn.apply(this, arguments); var ret = fn.apply(this, arguments);
this._super = tmp; this._super = tmp;
return ret; return ret;
}; };
})(name, prop[name]); })(name, prop[name]);
} else { } else {
prototype[name] = prop[name]; prototype[name] = prop[name];
} }
} }
// The dummy class constructor // The dummy class constructor
function Class() { function Class() {
// All construction is actually done in the init method // All construction is actually done in the init method
if(this.init) this.init.apply(this, arguments); if(this.init) this.init.apply(this, arguments);
} }
// Populate our constructed prototype object // Populate our constructed prototype object
Class.prototype = prototype; Class.prototype = prototype;
// Enforce the constructor to be what we expect // Enforce the constructor to be what we expect
Class.prototype.constructor = Class; Class.prototype.constructor = Class;
// And make this class extendable // And make this class extendable
Class.extend = arguments.callee; Class.extend = arguments.callee;
return Class; return Class;
}; };
module.exports = Class; module.exports = Class;

View file

@ -1,80 +1,80 @@
var dgram = require('dgram'), var dgram = require('dgram'),
EventEmitter = require('events').EventEmitter, EventEmitter = require('events').EventEmitter,
util = require('util'), util = require('util'),
dns = require('dns'), dns = require('dns'),
TypeResolver = require('./typeresolver'); TypeResolver = require('./typeresolver');
var activeQueries = []; var activeQueries = [];
var udpSocket = dgram.createSocket('udp4'); var udpSocket = dgram.createSocket('udp4');
udpSocket.unref(); udpSocket.unref();
udpSocket.bind(21943); udpSocket.bind(21943);
udpSocket.on('message', function(buffer, rinfo) { udpSocket.on('message', function(buffer, rinfo) {
if(Gamedig.debug) console.log(rinfo.address+':'+rinfo.port+" <--UDP "+buffer.toString('hex')); if(Gamedig.debug) console.log(rinfo.address+':'+rinfo.port+" <--UDP "+buffer.toString('hex'));
for(var i = 0; i < activeQueries.length; i++) { for(var i = 0; i < activeQueries.length; i++) {
var query = activeQueries[i]; var query = activeQueries[i];
if( if(
query.options.address != rinfo.address query.options.address != rinfo.address
&& query.options.altaddress != rinfo.address && query.options.altaddress != rinfo.address
) continue; ) continue;
if(query.options.port_query != rinfo.port) continue; if(query.options.port_query != rinfo.port) continue;
query._udpResponse(buffer); query._udpResponse(buffer);
break; break;
} }
}); });
udpSocket.on('error', function(e) { udpSocket.on('error', function(e) {
if(Gamedig.debug) console.log("UDP ERROR: "+e); if(Gamedig.debug) console.log("UDP ERROR: "+e);
}); });
Gamedig = { Gamedig = {
query: function(options,callback) { query: function(options,callback) {
if(callback) options.callback = callback; if(callback) options.callback = callback;
var query; var query;
try { try {
query = TypeResolver.lookup(options.type); query = TypeResolver.lookup(options.type);
} catch(e) { } catch(e) {
process.nextTick(function() { process.nextTick(function() {
callback({error:e.message}); callback({error:e.message});
}); });
return; return;
} }
query.debug = Gamedig.debug; query.debug = Gamedig.debug;
query.udpSocket = udpSocket; query.udpSocket = udpSocket;
query.type = options.type; query.type = options.type;
if(!('port' in query.options) && ('port_query' in query.options)) { if(!('port' in query.options) && ('port_query' in query.options)) {
if(Gamedig.isCommandLine) { if(Gamedig.isCommandLine) {
process.stderr.write( process.stderr.write(
"Warning! This game is so old, that we don't know" "Warning! This game is so old, that we don't know"
+" what the server's connection port is. We've guessed that" +" what the server's connection port is. We've guessed that"
+" the query port for "+query.type+" is "+query.options.port_query+"." +" the query port for "+query.type+" is "+query.options.port_query+"."
+" If you know the connection port for this type of server, please let" +" If you know the connection port for this type of server, please let"
+" us know on the GameDig issue tracker, thanks!\n" +" us know on the GameDig issue tracker, thanks!\n"
); );
} }
query.options.port = query.options.port_query; query.options.port = query.options.port_query;
delete query.options.port_query; delete query.options.port_query;
} }
// copy over options // copy over options
for(var i in options) query.options[i] = options[i]; for(var i in options) query.options[i] = options[i];
activeQueries.push(query); activeQueries.push(query);
query.on('finished',function(state) { query.on('finished',function(state) {
var i = activeQueries.indexOf(query); var i = activeQueries.indexOf(query);
if(i >= 0) activeQueries.splice(i, 1); if(i >= 0) activeQueries.splice(i, 1);
}); });
process.nextTick(function() { process.nextTick(function() {
query.start(); query.start();
}); });
return query; return query;
} }
}; };
module.exports = Gamedig; module.exports = Gamedig;

View file

@ -1,124 +1,124 @@
var Iconv = require('iconv-lite'), var Iconv = require('iconv-lite'),
Long = require('long'); Long = require('long');
function readUInt64BE(buffer,offset) { function readUInt64BE(buffer,offset) {
var high = buffer.readUInt32BE(offset); var high = buffer.readUInt32BE(offset);
var low = buffer.readUInt32BE(offset+4); var low = buffer.readUInt32BE(offset+4);
return new Long(low,high,true); return new Long(low,high,true);
} }
function readUInt64LE(buffer,offset) { function readUInt64LE(buffer,offset) {
var low = buffer.readUInt32LE(offset); var low = buffer.readUInt32LE(offset);
var high = buffer.readUInt32LE(offset+4); var high = buffer.readUInt32LE(offset+4);
return new Long(low,high,true); return new Long(low,high,true);
} }
function Reader(query,buffer) { function Reader(query,buffer) {
this.query = query; this.query = query;
this.buffer = buffer; this.buffer = buffer;
this.i = 0; this.i = 0;
} }
Reader.prototype = { Reader.prototype = {
offset: function() { return this.i; }, offset: function() { return this.i; },
skip: function(i) { this.i += i; }, skip: function(i) { this.i += i; },
string: function() { string: function() {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
var options = {}; var options = {};
if(args.length == 0) { if(args.length == 0) {
options = {}; options = {};
} else if(args.length == 1) { } else if(args.length == 1) {
if(typeof args[0] == 'string') options = { delimiter: args[0] }; if(typeof args[0] == 'string') options = { delimiter: args[0] };
else if(typeof args[0] == 'number') options = { length: args[0] }; else if(typeof args[0] == 'number') options = { length: args[0] };
else options = args[0]; else options = args[0];
} }
options.encoding = options.encoding || this.query.encoding; options.encoding = options.encoding || this.query.encoding;
if(options.encoding == 'latin1') options.encoding = 'win1252'; if(options.encoding == 'latin1') options.encoding = 'win1252';
var start = this.i+0; var start = this.i+0;
var end = start; var end = start;
if(!('length' in options)) { if(!('length' in options)) {
// terminated by the delimiter // terminated by the delimiter
var delim = options.delimiter || this.query.delimiter; var delim = options.delimiter || this.query.delimiter;
if(typeof delim == 'string') delim = delim.charCodeAt(0); if(typeof delim == 'string') delim = delim.charCodeAt(0);
while(true) { while(true) {
if(end >= this.buffer.length) { if(end >= this.buffer.length) {
end = this.buffer.length; end = this.buffer.length;
break; break;
} }
if(this.buffer.readUInt8(end) == delim) break; if(this.buffer.readUInt8(end) == delim) break;
end++; end++;
} }
this.i = end+1; this.i = end+1;
} else { } else {
end = start+options.length; end = start+options.length;
if(end >= this.buffer.length) { if(end >= this.buffer.length) {
end = this.buffer.length; end = this.buffer.length;
} }
this.i = end; this.i = end;
} }
var out = this.buffer.slice(start, end); var out = this.buffer.slice(start, end);
var enc = options.encoding; var enc = options.encoding;
if(enc == 'utf8' || enc == 'ucs2' || enc == 'binary') { if(enc == 'utf8' || enc == 'ucs2' || enc == 'binary') {
out = out.toString(enc); out = out.toString(enc);
} else { } else {
out = Iconv.decode(out,enc); out = Iconv.decode(out,enc);
} }
return out; return out;
}, },
int: function(bytes) { int: function(bytes) {
var r = 0; var r = 0;
if(this.remaining() >= bytes) { if(this.remaining() >= bytes) {
if(this.query.byteorder == 'be') { if(this.query.byteorder == 'be') {
if(bytes == 1) r = this.buffer.readInt8(this.i); if(bytes == 1) r = this.buffer.readInt8(this.i);
else if(bytes == 2) r = this.buffer.readInt16BE(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 == 4) r = this.buffer.readInt32BE(this.i);
} else { } else {
if(bytes == 1) r = this.buffer.readInt8(this.i); if(bytes == 1) r = this.buffer.readInt8(this.i);
else if(bytes == 2) r = this.buffer.readInt16LE(this.i); else if(bytes == 2) r = this.buffer.readInt16LE(this.i);
else if(bytes == 4) r = this.buffer.readInt32LE(this.i); else if(bytes == 4) r = this.buffer.readInt32LE(this.i);
} }
} }
this.i += bytes; this.i += bytes;
return r; return r;
}, },
uint: function(bytes) { uint: function(bytes) {
var r = 0; var r = 0;
if(this.remaining() >= bytes) { if(this.remaining() >= bytes) {
if(this.query.byteorder == 'be') { if(this.query.byteorder == 'be') {
if(bytes == 1) r = this.buffer.readUInt8(this.i); if(bytes == 1) r = this.buffer.readUInt8(this.i);
else if(bytes == 2) r = this.buffer.readUInt16BE(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 == 4) r = this.buffer.readUInt32BE(this.i);
else if(bytes == 8) r = readUInt64BE(this.buffer,this.i).toString(); else if(bytes == 8) r = readUInt64BE(this.buffer,this.i).toString();
} else { } else {
if(bytes == 1) r = this.buffer.readUInt8(this.i); if(bytes == 1) r = this.buffer.readUInt8(this.i);
else if(bytes == 2) r = this.buffer.readUInt16LE(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 == 4) r = this.buffer.readUInt32LE(this.i);
else if(bytes == 8) r = readUInt64LE(this.buffer,this.i).toString(); else if(bytes == 8) r = readUInt64LE(this.buffer,this.i).toString();
} }
} }
this.i += bytes; this.i += bytes;
return r; return r;
}, },
float: function() { float: function() {
var r = 0; var r = 0;
if(this.remaining() >= 4) { if(this.remaining() >= 4) {
if(this.query.byteorder == 'be') r = this.buffer.readFloatBE(this.i); if(this.query.byteorder == 'be') r = this.buffer.readFloatBE(this.i);
else r = this.buffer.readFloatLE(this.i); else r = this.buffer.readFloatLE(this.i);
} }
this.i += 4; this.i += 4;
return r; return r;
}, },
remaining: function() { remaining: function() {
return this.buffer.length-this.i; return this.buffer.length-this.i;
}, },
rest: function() { rest: function() {
return this.buffer.slice(this.i); return this.buffer.slice(this.i);
}, },
done: function() { done: function() {
return this.i >= this.buffer.length; return this.i >= this.buffer.length;
} }
}; };
module.exports = Reader; module.exports = Reader;

View file

@ -1,91 +1,91 @@
var Path = require('path'), var Path = require('path'),
fs = require('fs'); fs = require('fs');
var protocolDir = Path.normalize(__dirname+'/../protocols'); var protocolDir = Path.normalize(__dirname+'/../protocols');
var gamesFile = Path.normalize(__dirname+'/../games.txt'); var gamesFile = Path.normalize(__dirname+'/../games.txt');
function parseList(str) { function parseList(str) {
if(!str) return {}; if(!str) return {};
var split = str.split(','); var split = str.split(',');
var out = {}; var out = {};
split.forEach(function(one) { split.forEach(function(one) {
var equals = one.indexOf('='); var equals = one.indexOf('=');
var key = equals == -1 ? one : one.substr(0,equals); var key = equals == -1 ? one : one.substr(0,equals);
var value = equals == -1 ? '' : one.substr(equals+1); var value = equals == -1 ? '' : one.substr(equals+1);
if(value === 'true' || value === '') value = true; if(value === 'true' || value === '') value = true;
else if(value === 'false') value = false; else if(value === 'false') value = false;
else if(!isNaN(value)) value = parseInt(value); else if(!isNaN(value)) value = parseInt(value);
out[key] = value; out[key] = value;
}); });
return out; return out;
} }
function readGames() { function readGames() {
var lines = fs.readFileSync(gamesFile,'utf8').split('\n'); var lines = fs.readFileSync(gamesFile,'utf8').split('\n');
var games = {}; var games = {};
lines.forEach(function(line) { lines.forEach(function(line) {
// strip comments // strip comments
var comment = line.indexOf('#'); var comment = line.indexOf('#');
if(comment != -1) line = line.substr(0,comment); if(comment != -1) line = line.substr(0,comment);
line = line.trim(); line = line.trim();
if(!line) return; if(!line) return;
var split = line.split('|'); var split = line.split('|');
games[split[0].trim()] = { games[split[0].trim()] = {
pretty: split[1].trim(), pretty: split[1].trim(),
protocol: split[2].trim(), protocol: split[2].trim(),
options: parseList(split[3]), options: parseList(split[3]),
params: parseList(split[4]) params: parseList(split[4])
}; };
}); });
return games; return games;
} }
var games = readGames(); var games = readGames();
function createProtocolInstance(type) { function createProtocolInstance(type) {
type = Path.basename(type); type = Path.basename(type);
var path = protocolDir+'/'+type; var path = protocolDir+'/'+type;
if(!fs.existsSync(path+'.js')) throw Error('Protocol definition file missing: '+type); if(!fs.existsSync(path+'.js')) throw Error('Protocol definition file missing: '+type);
var protocol = require(path); var protocol = require(path);
return new protocol(); return new protocol();
} }
module.exports = { module.exports = {
lookup: function(type) { lookup: function(type) {
if(!type) throw Error('No game specified'); if(!type) throw Error('No game specified');
if(type.substr(0,9) == 'protocol-') { if(type.substr(0,9) == 'protocol-') {
return createProtocolInstance(type.substr(9)); return createProtocolInstance(type.substr(9));
} }
var game = games[type]; var game = games[type];
if(!game) throw Error('Invalid game: '+type); if(!game) throw Error('Invalid game: '+type);
var query = createProtocolInstance(game.protocol); var query = createProtocolInstance(game.protocol);
query.pretty = game.pretty; query.pretty = game.pretty;
for(var key in game.options) for(var key in game.options)
query.options[key] = game.options[key]; query.options[key] = game.options[key];
for(var key in game.params) for(var key in game.params)
query[key] = game.params[key]; query[key] = game.params[key];
return query; return query;
}, },
printReadme: function() { printReadme: function() {
var out = ''; var out = '';
for(var key in games) { for(var key in games) {
var game = games[key]; var game = games[key];
out += "* "+game.pretty+" ("+key+")"; out += "* "+game.pretty+" ("+key+")";
if(game.options.port_query_offset || game.options.port_query) if(game.options.port_query_offset || game.options.port_query)
out += " [[Separate Query Port](#separate-query-port)]"; out += " [[Separate Query Port](#separate-query-port)]";
if(game.params.doc_notes) if(game.params.doc_notes)
out += " [[Additional Notes](#"+game.params.doc_notes+")]" out += " [[Additional Notes](#"+game.params.doc_notes+")]"
out += "\n"; out += "\n";
} }
return out; return out;
} }
}; };

View file

@ -1,41 +1,41 @@
{ {
"name": "gamedig", "name": "gamedig",
"description": "Query for the status of any game server in Node.JS", "description": "Query for the status of any game server in Node.JS",
"tags": [ "tags": [
"srcds", "srcds",
"query", "query",
"game", "game",
"utility", "utility",
"util", "util",
"server" "server"
], ],
"main": "lib/index.js", "main": "lib/index.js",
"author": "Michael Morrison", "author": "Michael Morrison",
"version": "0.2.10", "version": "0.2.10",
"repository" : { "repository" : {
"type" : "git", "type" : "git",
"url" : "https://github.com/sonicsnes/node-gamedig.git" "url" : "https://github.com/sonicsnes/node-gamedig.git"
}, },
"bugs" : { "bugs" : {
"url" : "https://github.com/sonicsnes/node-gamedig/issues" "url" : "https://github.com/sonicsnes/node-gamedig/issues"
}, },
"licenses" : [ "licenses" : [
{ {
"type" : "MIT", "type" : "MIT",
"url" : "https://raw.github.com/sonicsnes/node-gamedig/master/LICENSE" "url" : "https://raw.github.com/sonicsnes/node-gamedig/master/LICENSE"
} }
], ],
"dependencies": { "dependencies": {
"iconv-lite": "~0.2.11", "iconv-lite": "~0.2.11",
"long": "~1.1.2", "long": "~1.1.2",
"async": "~0.2.10", "async": "~0.2.10",
"compressjs": "~1.0.1", "compressjs": "~1.0.1",
"gbxremote": "~0.1.4", "gbxremote": "~0.1.4",
"request": "~2.33.0", "request": "~2.33.0",
"optimist": "~0.6.0", "optimist": "~0.6.0",
"varint": "~1.0.0" "varint": "~1.0.0"
}, },
"bin": { "bin": {
"gamedig": "bin/gamedig.js" "gamedig": "bin/gamedig.js"
} }
} }

View file

@ -1,20 +1,20 @@
module.exports = require('./gamespy2').extend({ module.exports = require('./gamespy2').extend({
finalizeState: function(state) { finalizeState: function(state) {
this._super(state); this._super(state);
state.name = this.stripColor(state.name); state.name = this.stripColor(state.name);
state.map = this.stripColor(state.map); state.map = this.stripColor(state.map);
for(var i in state.raw) { for(var i in state.raw) {
if(!(typeof state.raw[i] == 'string')) continue; if(!(typeof state.raw[i] == 'string')) continue;
state.raw[i] = this.stripColor(state.raw[i]); state.raw[i] = this.stripColor(state.raw[i]);
} }
for(var i = 0; i < state.players.length; i++) { for(var i = 0; i < state.players.length; i++) {
var player = state.players[i]; var player = state.players[i];
if(!('name' in player)) continue; if(!('name' in player)) continue;
player.name = this.stripColor(player.name); player.name = this.stripColor(player.name);
} }
}, },
stripColor: function(str) { stripColor: function(str) {
// uses unreal 2 color codes // uses unreal 2 color codes
return str.replace(/\x1b...|[\x00-\x1a]/g,''); return str.replace(/\x1b...|[\x00-\x1a]/g,'');
} }
}); });

View file

@ -1,64 +1,64 @@
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.encoding = 'latin1'; this.encoding = 'latin1';
this.byteorder = 'be'; this.byteorder = 'be';
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
var b = new Buffer([0,0x35,0,0,0,0,0,0x11]); var b = new Buffer([0,0x35,0,0,0,0,0,0x11]);
this.udpSend(b,function(buffer) { this.udpSend(b,function(buffer) {
var reader = self.reader(buffer); var reader = self.reader(buffer);
reader.skip(6); reader.skip(6);
state.raw.port = self.readUInt(reader); state.raw.port = self.readUInt(reader);
state.raw.hostname = self.readString(reader,buffer); state.raw.hostname = self.readString(reader,buffer);
state.name = self.stripColorCodes(self.readString(reader,buffer)); state.name = self.stripColorCodes(self.readString(reader,buffer));
state.raw.numplayers = self.readUInt(reader); state.raw.numplayers = self.readUInt(reader);
state.raw.versionmin = self.readUInt(reader); state.raw.versionmin = self.readUInt(reader);
state.raw.versionmax = self.readUInt(reader); state.raw.versionmax = self.readUInt(reader);
state.raw.version = self.readString(reader,buffer); state.raw.version = self.readString(reader,buffer);
state.maxplayers = self.readUInt(reader); state.maxplayers = self.readUInt(reader);
var players = self.readString(reader,buffer); var players = self.readString(reader,buffer);
var list = players.split('\n'); var list = players.split('\n');
for(var i = 0; i < list.length; i++) { for(var i = 0; i < list.length; i++) {
if(!list[i]) continue; if(!list[i]) continue;
state.players.push({ state.players.push({
name:self.stripColorCodes(list[i]) name:self.stripColorCodes(list[i])
}); });
} }
state.raw.options = self.stripColorCodes(self.readString(reader,buffer)); state.raw.options = self.stripColorCodes(self.readString(reader,buffer));
state.raw.uri = self.readString(reader,buffer); state.raw.uri = self.readString(reader,buffer);
state.raw.globalids = self.readString(reader,buffer); state.raw.globalids = self.readString(reader,buffer);
self.finish(state); self.finish(state);
return true; return true;
}); });
}, },
readUInt: function(reader) { readUInt: function(reader) {
var a = reader.uint(2); var a = reader.uint(2);
var b = reader.uint(2); var b = reader.uint(2);
return (b<<16) + a; return (b<<16) + a;
}, },
readString: function(reader,b) { readString: function(reader,b) {
var len = reader.uint(2); var len = reader.uint(2);
if(!len) return ''; if(!len) return '';
var out = ''; var out = '';
for(var i = 0; i < len; i+=2) { for(var i = 0; i < len; i+=2) {
var hi = reader.uint(1); var hi = reader.uint(1);
var lo = reader.uint(1); var lo = reader.uint(1);
if(i+1<len) out += String.fromCharCode(lo); if(i+1<len) out += String.fromCharCode(lo);
if(i+2<len) out += String.fromCharCode(hi); if(i+2<len) out += String.fromCharCode(hi);
} }
return out; return out;
}, },
stripColorCodes: function(str) { stripColorCodes: function(str) {
return str.replace(/0x[0-9a-f]{6}/g,''); return str.replace(/0x[0-9a-f]{6}/g,'');
} }
}); });

View file

@ -1,47 +1,47 @@
module.exports = require('./core').extend({ module.exports = require('./core').extend({
run: function(state) { run: function(state) {
var self = this; var self = this;
self.udpSend('s',function(buffer) { self.udpSend('s',function(buffer) {
var reader = self.reader(buffer); var reader = self.reader(buffer);
var header = reader.string({length:4}); var header = reader.string({length:4});
if(header != 'EYE1') return; if(header != 'EYE1') return;
state.raw.gamename = self.readString(reader); state.raw.gamename = self.readString(reader);
state.raw.port = parseInt(self.readString(reader)); state.raw.port = parseInt(self.readString(reader));
state.name = self.readString(reader); state.name = self.readString(reader);
state.raw.gametype = self.readString(reader); state.raw.gametype = self.readString(reader);
state.map = self.readString(reader); state.map = self.readString(reader);
state.raw.version = self.readString(reader); state.raw.version = self.readString(reader);
state.password = self.readString(reader) == '1'; state.password = self.readString(reader) == '1';
state.raw.numplayers = parseInt(self.readString(reader)); state.raw.numplayers = parseInt(self.readString(reader));
state.maxplayers = parseInt(self.readString(reader)); state.maxplayers = parseInt(self.readString(reader));
while(!reader.done()) { while(!reader.done()) {
var key = self.readString(reader); var key = self.readString(reader);
if(!key) break; if(!key) break;
var value = self.readString(reader); var value = self.readString(reader);
state.raw[key] = value; state.raw[key] = value;
} }
console.log(reader.rest()); console.log(reader.rest());
while(!reader.done()) { while(!reader.done()) {
var flags = reader.uint(1); var flags = reader.uint(1);
var player = {}; var player = {};
if(flags & 1) player.name = self.readString(reader); if(flags & 1) player.name = self.readString(reader);
if(flags & 2) player.team = self.readString(reader); if(flags & 2) player.team = self.readString(reader);
if(flags & 4) player.skin = self.readString(reader); if(flags & 4) player.skin = self.readString(reader);
if(flags & 8) player.score = parseInt(self.readString(reader)); if(flags & 8) player.score = parseInt(self.readString(reader));
if(flags & 16) player.ping = parseInt(self.readString(reader)); if(flags & 16) player.ping = parseInt(self.readString(reader));
if(flags & 32) player.time = parseInt(self.readString(reader)); if(flags & 32) player.time = parseInt(self.readString(reader));
state.players.push(player); state.players.push(player);
} }
self.finish(state); self.finish(state);
}); });
}, },
readString: function(reader) { readString: function(reader) {
var len = reader.uint(1); var len = reader.uint(1);
return reader.string({length:len-1}); return reader.string({length:len-1});
} }
}); });

View file

@ -1,163 +1,163 @@
var async = require('async'); var async = require('async');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.encoding = 'latin1'; this.encoding = 'latin1';
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
var decoded; var decoded;
async.series([ async.series([
function(c) { function(c) {
self.query(['serverInfo'], function(data) { self.query(['serverInfo'], function(data) {
if(self.debug) console.log(data); if(self.debug) console.log(data);
if(data.shift() != 'OK') return self.fatal('Missing OK'); if(data.shift() != 'OK') return self.fatal('Missing OK');
state.raw.name = data.shift(); state.raw.name = data.shift();
state.raw.numplayers = parseInt(data.shift()); state.raw.numplayers = parseInt(data.shift());
state.maxplayers = parseInt(data.shift()); state.maxplayers = parseInt(data.shift());
state.raw.gametype = data.shift(); state.raw.gametype = data.shift();
state.map = data.shift(); state.map = data.shift();
state.raw.roundsplayed = parseInt(data.shift()); state.raw.roundsplayed = parseInt(data.shift());
state.raw.roundstotal = parseInt(data.shift()); state.raw.roundstotal = parseInt(data.shift());
var teamCount = data.shift(); var teamCount = data.shift();
state.raw.teams = []; state.raw.teams = [];
for(var i = 0; i < teamCount; i++) { for(var i = 0; i < teamCount; i++) {
var tickets = parseFloat(data.shift()); var tickets = parseFloat(data.shift());
state.raw.teams.push({ state.raw.teams.push({
tickets:tickets tickets:tickets
}); });
} }
state.raw.targetscore = parseInt(data.shift()); state.raw.targetscore = parseInt(data.shift());
data.shift(); data.shift();
state.raw.ranked = (data.shift() == 'true'); state.raw.ranked = (data.shift() == 'true');
state.raw.punkbuster = (data.shift() == 'true'); state.raw.punkbuster = (data.shift() == 'true');
state.password = (data.shift() == 'true'); state.password = (data.shift() == 'true');
state.raw.uptime = parseInt(data.shift()); state.raw.uptime = parseInt(data.shift());
state.raw.roundtime = parseInt(data.shift()); state.raw.roundtime = parseInt(data.shift());
if(self.isBadCompany2) { if(self.isBadCompany2) {
data.shift(); data.shift();
data.shift(); data.shift();
} }
state.raw.ip = data.shift(); state.raw.ip = data.shift();
state.raw.punkbusterversion = data.shift(); state.raw.punkbusterversion = data.shift();
state.raw.joinqueue = (data.shift() == 'true'); state.raw.joinqueue = (data.shift() == 'true');
state.raw.region = data.shift(); state.raw.region = data.shift();
if(!self.isBadCompany2) { if(!self.isBadCompany2) {
state.raw.pingsite = data.shift(); state.raw.pingsite = data.shift();
state.raw.country = data.shift(); state.raw.country = data.shift();
state.raw.quickmatch = (data.shift() == 'true'); state.raw.quickmatch = (data.shift() == 'true');
} }
c(); c();
}); });
}, },
function(c) { function(c) {
self.query(['version'], function(data) { self.query(['version'], function(data) {
if(self.debug) console.log(data); if(self.debug) console.log(data);
if(data[0] != 'OK') return self.fatal('Missing OK'); if(data[0] != 'OK') return self.fatal('Missing OK');
state.raw.version = data[2]; state.raw.version = data[2];
c(); c();
}); });
}, },
function(c) { function(c) {
self.query(['listPlayers','all'], function(data) { self.query(['listPlayers','all'], function(data) {
if(self.debug) console.log(data); if(self.debug) console.log(data);
if(data.shift() != 'OK') return self.fatal('Missing OK'); if(data.shift() != 'OK') return self.fatal('Missing OK');
var fieldCount = parseInt(data.shift()); var fieldCount = parseInt(data.shift());
var fields = []; var fields = [];
for(var i = 0; i < fieldCount; i++) { for(var i = 0; i < fieldCount; i++) {
fields.push(data.shift()); fields.push(data.shift());
} }
var numplayers = data.shift(); var numplayers = data.shift();
for(var i = 0; i < numplayers; i++) { for(var i = 0; i < numplayers; i++) {
var player = {}; var player = {};
fields.forEach(function(key) { fields.forEach(function(key) {
var value = data.shift(); var value = data.shift();
if(key == 'teamId') key = 'team'; if(key == 'teamId') key = 'team';
else if(key == 'squadId') key = 'squad'; else if(key == 'squadId') key = 'squad';
if( if(
key == 'kills' key == 'kills'
|| key == 'deaths' || key == 'deaths'
|| key == 'score' || key == 'score'
|| key == 'rank' || key == 'rank'
|| key == 'team' || key == 'team'
|| key == 'squad' || key == 'squad'
|| key == 'ping' || key == 'ping'
|| key == 'type' || key == 'type'
) { ) {
value = parseInt(value); value = parseInt(value);
} }
player[key] = value; player[key] = value;
}); });
state.players.push(player); state.players.push(player);
} }
self.finish(state); self.finish(state);
}); });
} }
]); ]);
}, },
query: function(params,c) { query: function(params,c) {
var self = this; var self = this;
this.tcpSend(buildPacket(params), function(data) { this.tcpSend(buildPacket(params), function(data) {
var decoded = self.decodePacket(data); var decoded = self.decodePacket(data);
if(!decoded) return false; if(!decoded) return false;
c(decoded); c(decoded);
return true; return true;
}); });
}, },
decodePacket: function(buffer) { decodePacket: function(buffer) {
if(buffer.length < 8) return false; if(buffer.length < 8) return false;
var reader = this.reader(buffer); var reader = this.reader(buffer);
var header = reader.uint(4); var header = reader.uint(4);
var totalLength = reader.uint(4); var totalLength = reader.uint(4);
if(buffer.length < totalLength) return false; if(buffer.length < totalLength) return false;
var paramCount = reader.uint(4); var paramCount = reader.uint(4);
var params = []; var params = [];
for(var i = 0; i < paramCount; i++) { for(var i = 0; i < paramCount; i++) {
var len = reader.uint(4); var len = reader.uint(4);
params.push(reader.string({length:len})); params.push(reader.string({length:len}));
var strNull = reader.uint(1); var strNull = reader.uint(1);
} }
return params; return params;
} }
}); });
function buildPacket(params) { function buildPacket(params) {
var self = this; var self = this;
var paramBuffers = []; var paramBuffers = [];
params.forEach(function(param) { params.forEach(function(param) {
paramBuffers.push(new Buffer(param,'utf8')); paramBuffers.push(new Buffer(param,'utf8'));
}); });
var totalLength = 12; var totalLength = 12;
paramBuffers.forEach(function(paramBuffer) { paramBuffers.forEach(function(paramBuffer) {
totalLength += paramBuffer.length+1+4; totalLength += paramBuffer.length+1+4;
}); });
var b = new Buffer(totalLength); var b = new Buffer(totalLength);
b.writeUInt32LE(0,0); b.writeUInt32LE(0,0);
b.writeUInt32LE(totalLength,4); b.writeUInt32LE(totalLength,4);
b.writeUInt32LE(params.length,8); b.writeUInt32LE(params.length,8);
var offset = 12; var offset = 12;
paramBuffers.forEach(function(paramBuffer) { paramBuffers.forEach(function(paramBuffer) {
b.writeUInt32LE(paramBuffer.length, offset); offset += 4; b.writeUInt32LE(paramBuffer.length, offset); offset += 4;
paramBuffer.copy(b, offset); offset += paramBuffer.length; paramBuffer.copy(b, offset); offset += paramBuffer.length;
b.writeUInt8(0, offset); offset += 1; b.writeUInt8(0, offset); offset += 1;
}); });
return b; return b;
} }

View file

@ -1,55 +1,55 @@
var request = require('request'); var request = require('request');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
run: function(state) { run: function(state) {
var self = this; var self = this;
request({ request({
uri: 'http://'+this.options.address+':'+this.options.port_query+'/', uri: 'http://'+this.options.address+':'+this.options.port_query+'/',
timeout: 3000, timeout: 3000,
}, function(e,r,body) { }, function(e,r,body) {
if(e) return self.fatal('HTTP error'); if(e) return self.fatal('HTTP error');
var m = body.match(/status server for (.*?)\r|\n/); var m = body.match(/status server for (.*?)\r|\n/);
if(m) state.name = m[1]; if(m) state.name = m[1];
var m = body.match(/Current uptime: (\d+)/); var m = body.match(/Current uptime: (\d+)/);
if(m) state.raw.uptime = m[1]; if(m) state.raw.uptime = m[1];
var m = body.match(/currently running (.*?) by /); var m = body.match(/currently running (.*?) by /);
if(m) state.map = m[1]; if(m) state.map = m[1];
var m = body.match(/Current players: (\d+)\/(\d+)/); var m = body.match(/Current players: (\d+)\/(\d+)/);
if(m) { if(m) {
state.raw.numplayers = m[1]; state.raw.numplayers = m[1];
state.maxplayers = m[2]; state.maxplayers = m[2];
} }
var m = body.match(/class="playerlist"([^]+?)\/table/); var m = body.match(/class="playerlist"([^]+?)\/table/);
if(m) { if(m) {
var table = m[1]; var table = m[1];
var pre = /<tr>[^]*<td>([^]*)<\/td>[^]*<td>([^]*)<\/td>[^]*<td>([^]*)<\/td>[^]*<td>([^]*)<\/td>/g; var pre = /<tr>[^]*<td>([^]*)<\/td>[^]*<td>([^]*)<\/td>[^]*<td>([^]*)<\/td>[^]*<td>([^]*)<\/td>/g;
while(pm = pre.exec(table)) { while(pm = pre.exec(table)) {
if(pm[2] == 'Ping') continue; if(pm[2] == 'Ping') continue;
state.players.push({ state.players.push({
name: pm[1], name: pm[1],
ping: pm[2], ping: pm[2],
team: pm[3], team: pm[3],
score: pm[4] score: pm[4]
}); });
} }
} }
/* /*
var m = this.options.address.match(/(\d+)\.(\d+)\.(\d+)\.(\d+)/); var m = this.options.address.match(/(\d+)\.(\d+)\.(\d+)\.(\d+)/);
if(m) { if(m) {
var o1 = parseInt(m[1]); var o1 = parseInt(m[1]);
var o2 = parseInt(m[2]); var o2 = parseInt(m[2]);
var o3 = parseInt(m[3]); var o3 = parseInt(m[3]);
var o4 = parseInt(m[4]); var o4 = parseInt(m[4]);
var addr = o1+(o2<<8)+(o3<<16)+(o4<<24); var addr = o1+(o2<<8)+(o3<<16)+(o4<<24);
state.raw.url = 'aos://'+addr; state.raw.url = 'aos://'+addr;
} }
*/ */
self.finish(state); self.finish(state);
}); });
} }
}); });

View file

@ -1,312 +1,312 @@
var EventEmitter = require('events').EventEmitter, var EventEmitter = require('events').EventEmitter,
dns = require('dns'), dns = require('dns'),
net = require('net'), net = require('net'),
async = require('async'), async = require('async'),
Class = require('../lib/Class'), Class = require('../lib/Class'),
Reader = require('../lib/reader'); Reader = require('../lib/reader');
module.exports = Class.extend(EventEmitter,{ module.exports = Class.extend(EventEmitter,{
init: function() { init: function() {
this._super(); this._super();
this.options = { this.options = {
tcpTimeout: 1000, tcpTimeout: 1000,
udpTimeout: 1000 udpTimeout: 1000
}; };
this.maxAttempts = 1; this.maxAttempts = 1;
this.attempt = 1; this.attempt = 1;
this.finished = false; this.finished = false;
this.encoding = 'utf8'; this.encoding = 'utf8';
this.byteorder = 'le'; this.byteorder = 'le';
this.delimiter = '\0'; this.delimiter = '\0';
var self = this; var self = this;
this.globalTimeoutTimer = setTimeout(function() { this.globalTimeoutTimer = setTimeout(function() {
self.fatal('timeout'); self.fatal('timeout');
},10000); },10000);
}, },
fatal: function(err,noretry) { fatal: function(err,noretry) {
if(!noretry && this.attempt < this.maxAttempts) { if(!noretry && this.attempt < this.maxAttempts) {
this.attempt++; this.attempt++;
this.start(); this.start();
return; return;
} }
this.done({error: err.toString()}); this.done({error: err.toString()});
}, },
initState: function() { initState: function() {
return { return {
name: '', name: '',
map: '', map: '',
password: false, password: false,
raw: {}, raw: {},
maxplayers: 0, maxplayers: 0,
players: [], players: [],
bots: [] bots: []
}; };
}, },
finalizeState: function(state) {}, finalizeState: function(state) {},
finish: function(state) { finish: function(state) {
this.finalizeState(state); this.finalizeState(state);
this.done(state); this.done(state);
}, },
done: function(state) { done: function(state) {
if(this.finished) return; if(this.finished) return;
clearTimeout(this.globalTimeoutTimer); clearTimeout(this.globalTimeoutTimer);
if(this.options.notes) if(this.options.notes)
state.notes = this.options.notes; state.notes = this.options.notes;
state.query = {}; state.query = {};
if('host' in this.options) state.query.host = this.options.host; if('host' in this.options) state.query.host = this.options.host;
if('address' in this.options) state.query.address = this.options.address; if('address' in this.options) state.query.address = this.options.address;
if('port' in this.options) state.query.port = this.options.port; 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; if('port_query' in this.options) state.query.port_query = this.options.port_query;
state.query.type = this.type; state.query.type = this.type;
if('pretty' in this) state.query.pretty = this.pretty; if('pretty' in this) state.query.pretty = this.pretty;
this.reset(); this.reset();
this.finished = true; this.finished = true;
this.emit('finished',state); this.emit('finished',state);
if(this.options.callback) this.options.callback(state); if(this.options.callback) this.options.callback(state);
}, },
reset: function() { reset: function() {
if(this.timers) { if(this.timers) {
this.timers.forEach(function(timer) { this.timers.forEach(function(timer) {
clearTimeout(timer); clearTimeout(timer);
}); });
} }
this.timers = []; this.timers = [];
if(this.tcpSocket) { if(this.tcpSocket) {
this.tcpSocket.destroy(); this.tcpSocket.destroy();
delete this.tcpSocket; delete this.tcpSocket;
} }
this.udpTimeoutTimer = false; this.udpTimeoutTimer = false;
this.udpCallback = false; this.udpCallback = false;
}, },
start: function() { start: function() {
var self = this; var self = this;
var options = self.options; var options = self.options;
this.reset(); this.reset();
async.series([ async.series([
function(c) { function(c) {
// resolve host names // resolve host names
if(!('host' in options)) return c(); if(!('host' in options)) return c();
if(options.host.match(/\d+\.\d+\.\d+\.\d+/)) { if(options.host.match(/\d+\.\d+\.\d+\.\d+/)) {
options.address = options.host; options.address = options.host;
c(); c();
} else { } else {
self.parseDns(options.host,c); self.parseDns(options.host,c);
} }
}, },
function(c) { function(c) {
// calculate query port if needed // calculate query port if needed
if(!('port_query' in options) && 'port' in options) { if(!('port_query' in options) && 'port' in options) {
var offset = options.port_query_offset || 0; var offset = options.port_query_offset || 0;
options.port_query = options.port + offset; options.port_query = options.port + offset;
} }
c(); c();
}, },
function(c) { function(c) {
// run // run
self.run(self.initState()); self.run(self.initState());
} }
]); ]);
}, },
parseDns: function(host,c) { parseDns: function(host,c) {
var self = this; var self = this;
function resolveStandard(host,c) { function resolveStandard(host,c) {
dns.lookup(host, function(err,address,family) { dns.lookup(host, function(err,address,family) {
if(err) return self.fatal(err); if(err) return self.fatal(err);
self.options.address = address; self.options.address = address;
c(); c();
}); });
} }
function resolveSrv(srv,host,c) { function resolveSrv(srv,host,c) {
dns.resolve(srv+'.'+host, 'SRV', function(err,addresses) { dns.resolve(srv+'.'+host, 'SRV', function(err,addresses) {
if(err) return resolveStandard(host,c); if(err) return resolveStandard(host,c);
if(addresses.length >= 1) { if(addresses.length >= 1) {
var line = addresses[0]; var line = addresses[0];
self.options.port = line.port; self.options.port = line.port;
var srvhost = line.name; var srvhost = line.name;
if(srvhost.match(/\d+\.\d+\.\d+\.\d+/)) { if(srvhost.match(/\d+\.\d+\.\d+\.\d+/)) {
self.options.address = srvhost; self.options.address = srvhost;
c(); c();
} else { } else {
// resolve yet again // resolve yet again
resolveStandard(srvhost,c); resolveStandard(srvhost,c);
} }
return; return;
} }
return resolveStandard(host,c); return resolveStandard(host,c);
}); });
} }
if(this.srvRecord) resolveSrv(this.srvRecord,host,c); if(this.srvRecord) resolveSrv(this.srvRecord,host,c);
else resolveStandard(host,c); else resolveStandard(host,c);
}, },
// utils // utils
reader: function(buffer) { reader: function(buffer) {
return new Reader(this,buffer); return new Reader(this,buffer);
}, },
translate: function(obj,trans) { translate: function(obj,trans) {
for(var from in trans) { for(var from in trans) {
var to = trans[from]; var to = trans[from];
if(from in obj) { if(from in obj) {
if(to) obj[to] = obj[from]; if(to) obj[to] = obj[from];
delete obj[from]; delete obj[from];
} }
} }
}, },
setTimeout: function(c,t) { setTimeout: function(c,t) {
if(this.finished) return 0; if(this.finished) return 0;
var id = setTimeout(c,t); var id = setTimeout(c,t);
this.timers.push(id); this.timers.push(id);
return id; return id;
}, },
trueTest: function(str) { trueTest: function(str) {
if(typeof str == 'boolean') return str; if(typeof str == 'boolean') return str;
if(typeof str == 'number') return str != 0; if(typeof str == 'number') return str != 0;
if(typeof str == 'string') { if(typeof str == 'string') {
if(str.toLowerCase() == 'true') return true; if(str.toLowerCase() == 'true') return true;
if(str == 'yes') return true; if(str == 'yes') return true;
if(str == '1') return true; if(str == '1') return true;
} }
return false; return false;
}, },
debugBuffer: function(buffer) { debugBuffer: function(buffer) {
var out = ''; var out = '';
var out2 = ''; var out2 = '';
for(var i = 0; i < buffer.length; i++) { for(var i = 0; i < buffer.length; i++) {
var sliced = buffer.slice(i,i+1); var sliced = buffer.slice(i,i+1);
out += sliced.toString('hex')+' '; out += sliced.toString('hex')+' ';
var chr = sliced.toString(); var chr = sliced.toString();
if(chr < ' ' || chr > '~') chr = ' '; if(chr < ' ' || chr > '~') chr = ' ';
out2 += chr+' '; out2 += chr+' ';
if(out.length > 60) { if(out.length > 60) {
console.log(out); console.log(out);
console.log(out2); console.log(out2);
out = out2 = ''; out = out2 = '';
} }
} }
console.log(out); console.log(out);
console.log(out2); console.log(out2);
}, },
_tcpConnect: function(c) { _tcpConnect: function(c) {
var self = this; var self = this;
if(this.tcpSocket) return c(this.tcpSocket); if(this.tcpSocket) return c(this.tcpSocket);
var connected = false; var connected = false;
var received = new Buffer(0); var received = new Buffer(0);
var address = this.options.address; var address = this.options.address;
var port = this.options.port_query; var port = this.options.port_query;
var socket = this.tcpSocket = net.connect(port,address,function() { var socket = this.tcpSocket = net.connect(port,address,function() {
if(self.debug) console.log(address+':'+port+" TCPCONNECTED"); if(self.debug) console.log(address+':'+port+" TCPCONNECTED");
connected = true; connected = true;
c(socket); c(socket);
}); });
socket.setTimeout(10000); socket.setTimeout(10000);
socket.setNoDelay(true); socket.setNoDelay(true);
if(this.debug) console.log(address+':'+port+" TCPCONNECT"); if(this.debug) console.log(address+':'+port+" TCPCONNECT");
var writeHook = socket.write; var writeHook = socket.write;
socket.write = function(data) { socket.write = function(data) {
if(self.debug) console.log(address+':'+port+" TCP--> "+data.toString('hex')); if(self.debug) console.log(address+':'+port+" TCP--> "+data.toString('hex'));
writeHook.apply(this,arguments); writeHook.apply(this,arguments);
} }
socket.on('error', function() {}); socket.on('error', function() {});
socket.on('close', function() { socket.on('close', function() {
if(!self.tcpCallback) return; if(!self.tcpCallback) return;
if(connected) return self.fatal('Socket closed while waiting on TCP'); if(connected) return self.fatal('Socket closed while waiting on TCP');
else return self.fatal('TCP Connection Refused'); else return self.fatal('TCP Connection Refused');
}); });
socket.on('data', function(data) { socket.on('data', function(data) {
if(!self.tcpCallback) return; if(!self.tcpCallback) return;
if(self.debug) console.log(address+':'+port+" <--TCP "+data.toString('hex')); if(self.debug) console.log(address+':'+port+" <--TCP "+data.toString('hex'));
received = Buffer.concat([received,data]); received = Buffer.concat([received,data]);
if(self.tcpCallback(received)) { if(self.tcpCallback(received)) {
clearTimeout(self.tcpTimeoutTimer); clearTimeout(self.tcpTimeoutTimer);
self.tcpCallback = false; self.tcpCallback = false;
received = new Buffer(0); received = new Buffer(0);
} }
}); });
}, },
tcpSend: function(buffer,ondata) { tcpSend: function(buffer,ondata) {
var self = this; var self = this;
process.nextTick(function() { process.nextTick(function() {
if(self.tcpCallback) return self.fatal('Attempted to send TCP packet while still waiting on a managed response'); if(self.tcpCallback) return self.fatal('Attempted to send TCP packet while still waiting on a managed response');
self._tcpConnect(function(socket) { self._tcpConnect(function(socket) {
socket.write(buffer); socket.write(buffer);
}); });
if(!ondata) return; if(!ondata) return;
self.tcpTimeoutTimer = self.setTimeout(function() { self.tcpTimeoutTimer = self.setTimeout(function() {
self.tcpCallback = false; self.tcpCallback = false;
self.fatal('TCP Watchdog Timeout'); self.fatal('TCP Watchdog Timeout');
},self.options.tcpTimeout); },self.options.tcpTimeout);
self.tcpCallback = ondata; self.tcpCallback = ondata;
}); });
}, },
udpSend: function(buffer,onpacket,ontimeout) { udpSend: function(buffer,onpacket,ontimeout) {
var self = this; var self = this;
process.nextTick(function() { process.nextTick(function() {
if(self.udpCallback) return self.fatal('Attempted to send UDP packet while still waiting on a managed response'); if(self.udpCallback) return self.fatal('Attempted to send UDP packet while still waiting on a managed response');
self._udpSendNow(buffer); self._udpSendNow(buffer);
if(!onpacket) return; if(!onpacket) return;
self.udpTimeoutTimer = self.setTimeout(function() { self.udpTimeoutTimer = self.setTimeout(function() {
self.udpCallback = false; self.udpCallback = false;
var timeout = false; var timeout = false;
if(!ontimeout || ontimeout() !== true) timeout = true; if(!ontimeout || ontimeout() !== true) timeout = true;
if(timeout) self.fatal('UDP Watchdog Timeout'); if(timeout) self.fatal('UDP Watchdog Timeout');
},self.options.udpTimeout); },self.options.udpTimeout);
self.udpCallback = onpacket; self.udpCallback = onpacket;
}); });
}, },
_udpSendNow: function(buffer) { _udpSendNow: function(buffer) {
if(!('port_query' in this.options)) return this.fatal('Attempted to send without setting a port'); 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(!('address' in this.options)) return this.fatal('Attempted to send without setting an address');
if(typeof buffer == 'string') buffer = new Buffer(buffer,'binary'); 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')); 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); this.udpSocket.send(buffer,0,buffer.length,this.options.port_query,this.options.address);
}, },
_udpResponse: function(buffer) { _udpResponse: function(buffer) {
if(this.udpCallback) { if(this.udpCallback) {
var result = this.udpCallback(buffer); var result = this.udpCallback(buffer);
if(result === true) { if(result === true) {
// we're done with this udp session // we're done with this udp session
clearTimeout(this.udpTimeoutTimer); clearTimeout(this.udpTimeoutTimer);
this.udpCallback = false; this.udpCallback = false;
} }
} else { } else {
this.udpResponse(buffer); this.udpResponse(buffer);
} }
}, },
udpResponse: function() {} udpResponse: function() {}
}); });

View file

@ -1,95 +1,95 @@
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.pretty = 'Doom 3'; this.pretty = 'Doom 3';
this.encoding = 'latin1'; this.encoding = 'latin1';
this.isEtqw = false; this.isEtqw = false;
this.hasSpaceBeforeClanTag = false; this.hasSpaceBeforeClanTag = false;
this.hasClanTag = false; this.hasClanTag = false;
this.hasTypeFlag = false; this.hasTypeFlag = false;
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
this.udpSend('\xff\xffgetInfo\x00PiNGPoNG\x00',function(buffer) { this.udpSend('\xff\xffgetInfo\x00PiNGPoNG\x00',function(buffer) {
var reader = self.reader(buffer); var reader = self.reader(buffer);
var header = reader.uint(2); var header = reader.uint(2);
if(header != 0xffff) return; if(header != 0xffff) return;
var header2 = reader.string(); var header2 = reader.string();
if(header2 != 'infoResponse') return; if(header2 != 'infoResponse') return;
var tailSize = 5; var tailSize = 5;
if(self.isEtqw) { if(self.isEtqw) {
var taskId = reader.uint(4); var taskId = reader.uint(4);
} }
var challenge = reader.uint(4); var challenge = reader.uint(4);
var protoVersion = reader.uint(4); var protoVersion = reader.uint(4);
state.raw.protocolVersion = (protoVersion>>16)+'.'+(protoVersion&0xffff); state.raw.protocolVersion = (protoVersion>>16)+'.'+(protoVersion&0xffff);
if(self.isEtqw) { if(self.isEtqw) {
var size = reader.uint(4); var size = reader.uint(4);
} }
while(!reader.done()) { while(!reader.done()) {
var key = reader.string(); var key = reader.string();
var value = self.stripColors(reader.string()); var value = self.stripColors(reader.string());
if(key == 'si_map') { if(key == 'si_map') {
value = value.replace('maps/',''); value = value.replace('maps/','');
value = value.replace('.entities',''); value = value.replace('.entities','');
} }
if(!key) break; if(!key) break;
state.raw[key] = value; state.raw[key] = value;
} }
var i = 0; var i = 0;
while(!reader.done()) { while(!reader.done()) {
i++; i++;
var player = {}; var player = {};
player.id = reader.uint(1); player.id = reader.uint(1);
if(player.id == 32) break; if(player.id == 32) break;
player.ping = reader.uint(2); player.ping = reader.uint(2);
if(!self.isEtqw) player.rate = reader.uint(4); if(!self.isEtqw) player.rate = reader.uint(4);
player.name = self.stripColors(reader.string()); player.name = self.stripColors(reader.string());
if(self.hasClanTag) { if(self.hasClanTag) {
if(self.hasSpaceBeforeClanTag) reader.uint(1); if(self.hasSpaceBeforeClanTag) reader.uint(1);
player.clantag = self.stripColors(reader.string()); player.clantag = self.stripColors(reader.string());
} }
if(self.hasTypeFlag) player.typeflag = reader.uint(1); if(self.hasTypeFlag) player.typeflag = reader.uint(1);
if(!player.ping || player.typeflag) if(!player.ping || player.typeflag)
state.bots.push(player); state.bots.push(player);
else else
state.players.push(player); state.players.push(player);
} }
state.raw.osmask = reader.uint(4); state.raw.osmask = reader.uint(4);
if(self.isEtqw) { if(self.isEtqw) {
state.raw.ranked = reader.uint(1); state.raw.ranked = reader.uint(1);
state.raw.timeleft = reader.uint(4); state.raw.timeleft = reader.uint(4);
state.raw.gamestate = reader.uint(1); state.raw.gamestate = reader.uint(1);
state.raw.servertype = reader.uint(1); state.raw.servertype = reader.uint(1);
// 0 = regular, 1 = tv // 0 = regular, 1 = tv
if(state.raw.servertype == 0) { if(state.raw.servertype == 0) {
state.raw.interestedClients = reader.uint(1); state.raw.interestedClients = reader.uint(1);
} else if(state.raw.servertype == 1) { } else if(state.raw.servertype == 1) {
state.raw.connectedClients = reader.uint(4); state.raw.connectedClients = reader.uint(4);
state.raw.maxClients = reader.uint(4); state.raw.maxClients = reader.uint(4);
} }
} }
if(state.raw.si_name) state.name = state.raw.si_name; 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_map) state.map = state.raw.si_map;
if(state.raw.si_maxplayers) state.maxplayers = parseInt(state.raw.si_maxplayers); if(state.raw.si_maxplayers) state.maxplayers = parseInt(state.raw.si_maxplayers);
if(state.raw.si_usepass == '1') state.password = true; if(state.raw.si_usepass == '1') state.password = true;
self.finish(state); self.finish(state);
return true; return true;
}); });
}, },
stripColors: function(str) { stripColors: function(str) {
// uses quake 3 color codes // uses quake 3 color codes
return str.replace(/\^(X.{6}|.)/g,''); return str.replace(/\^(X.{6}|.)/g,'');
} }
}); });

View file

@ -1,33 +1,33 @@
module.exports = require('./valve').extend({ module.exports = require('./valve').extend({
init: function() { init: function() {
this._super(); this._super();
this.byteorder = 'be'; this.byteorder = 'be';
this.legacyChallenge = true; this.legacyChallenge = true;
}, },
queryInfo: function(state,c) { queryInfo: function(state,c) {
var self = this; var self = this;
self.sendPacket(0x46,false,'LSQ',0x49,function(b) { self.sendPacket(0x46,false,'LSQ',0x49,function(b) {
var reader = self.reader(b); var reader = self.reader(b);
state.raw.protocol = reader.uint(1); state.raw.protocol = reader.uint(1);
state.name = reader.string(); state.name = reader.string();
state.map = reader.string(); state.map = reader.string();
state.raw.mod = reader.string(); state.raw.mod = reader.string();
state.raw.gamemode = reader.string(); state.raw.gamemode = reader.string();
state.raw.description = reader.string(); state.raw.description = reader.string();
state.raw.version = reader.string(); state.raw.version = reader.string();
state.raw.port = reader.uint(2); state.raw.port = reader.uint(2);
state.raw.numplayers = reader.uint(1); state.raw.numplayers = reader.uint(1);
state.maxplayers = reader.uint(1); state.maxplayers = reader.uint(1);
state.raw.listentype = String.fromCharCode(reader.uint(1)); state.raw.listentype = String.fromCharCode(reader.uint(1));
state.raw.environment = String.fromCharCode(reader.uint(1)); state.raw.environment = String.fromCharCode(reader.uint(1));
state.password = !!reader.uint(1); state.password = !!reader.uint(1);
state.raw.secure = reader.uint(1); state.raw.secure = reader.uint(1);
state.raw.averagefps = reader.uint(1); state.raw.averagefps = reader.uint(1);
state.raw.round = reader.uint(1); state.raw.round = reader.uint(1);
state.raw.maxrounds = reader.uint(1); state.raw.maxrounds = reader.uint(1);
state.raw.timeleft = reader.uint(2); state.raw.timeleft = reader.uint(2);
c(); c();
}); });
} }
}); });

View file

@ -1,85 +1,85 @@
var async = require('async'); var async = require('async');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.sessionId = 1; this.sessionId = 1;
this.encoding = 'latin1'; this.encoding = 'latin1';
this.byteorder = 'be'; this.byteorder = 'be';
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
async.series([ async.series([
function(c) { function(c) {
self.sendPacket('info', function(data) { self.sendPacket('info', function(data) {
state.raw = data; state.raw = data;
if('hostname' in state.raw) state.name = state.raw.hostname; if('hostname' in state.raw) state.name = state.raw.hostname;
if('mapname' in state.raw) state.map = state.raw.mapname; if('mapname' in state.raw) state.map = state.raw.mapname;
if(self.trueTest(state.raw.password)) state.password = true; if(self.trueTest(state.raw.password)) state.password = true;
if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers); if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers);
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendPacket('rules', function(data) { self.sendPacket('rules', function(data) {
state.raw.rules = data; state.raw.rules = data;
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendPacket('players', function(data) { self.sendPacket('players', function(data) {
var players = {}; var players = {};
var teams = {}; var teams = {};
for(var ident in data) { for(var ident in data) {
var split = ident.split('_'); var split = ident.split('_');
var key = split[0]; var key = split[0];
var id = split[1]; var id = split[1];
var value = data[ident]; var value = data[ident];
if(key == 'teamname') { if(key == 'teamname') {
teams[id] = value; teams[id] = value;
} else { } else {
if(!(id in players)) players[id] = {}; if(!(id in players)) players[id] = {};
if(key == 'playername') key = 'name'; if(key == 'playername') key = 'name';
else if(key == 'team') value = parseInt(value); else if(key == 'team') value = parseInt(value);
else if(key == 'score' || key == 'ping' || key == 'deaths') value = parseInt(value); else if(key == 'score' || key == 'ping' || key == 'deaths') value = parseInt(value);
players[id][key] = value; players[id][key] = value;
} }
} }
state.raw.teams = teams; state.raw.teams = teams;
for(var i in players) state.players.push(players[i]); for(var i in players) state.players.push(players[i]);
self.finish(state); self.finish(state);
}); });
} }
]); ]);
}, },
sendPacket: function(type,callback) { sendPacket: function(type,callback) {
var self = this; var self = this;
var queryId = ''; var queryId = '';
var output = {}; var output = {};
this.udpSend('\\'+type+'\\',function(buffer) { this.udpSend('\\'+type+'\\',function(buffer) {
var reader = self.reader(buffer); var reader = self.reader(buffer);
var str = reader.string({length:buffer.length}); var str = reader.string({length:buffer.length});
var split = str.split('\\'); var split = str.split('\\');
split.shift(); split.shift();
var data = {}; var data = {};
while(split.length) { while(split.length) {
var key = split.shift(); var key = split.shift();
var value = split.shift() || ''; var value = split.shift() || '';
data[key] = value; data[key] = value;
} }
if(!('queryid' in data)) return; if(!('queryid' in data)) return;
if(queryId && data.queryid != queryId) return; if(queryId && data.queryid != queryId) return;
for(var i in data) output[i] = data[i]; for(var i in data) output[i] = data[i];
if('final' in output) { if('final' in output) {
delete output.final; delete output.final;
delete output.queryid; delete output.queryid;
callback(output); callback(output);
return true; return true;
} }
}); });
} }
}); });

View file

@ -1,96 +1,96 @@
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.sessionId = 1; this.sessionId = 1;
this.encoding = 'latin1'; this.encoding = 'latin1';
this.byteorder = 'be'; this.byteorder = 'be';
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
var request = new Buffer([0xfe,0xfd,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff]); var request = new Buffer([0xfe,0xfd,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0xff]);
var packets = []; var packets = [];
this.udpSend(request, this.udpSend(request,
function(buffer) { function(buffer) {
if(packets.length && buffer.readUInt8(0) == 0) if(packets.length && buffer.readUInt8(0) == 0)
buffer = buffer.slice(1); buffer = buffer.slice(1);
packets.push(buffer); packets.push(buffer);
}, },
function() { function() {
var buffer = Buffer.concat(packets); var buffer = Buffer.concat(packets);
var reader = self.reader(buffer); var reader = self.reader(buffer);
var header = reader.uint(1); var header = reader.uint(1);
if(header != 0) return; if(header != 0) return;
var pingId = reader.uint(4); var pingId = reader.uint(4);
if(pingId != 1) return; if(pingId != 1) return;
while(!reader.done()) { while(!reader.done()) {
var key = reader.string(); var key = reader.string();
var value = reader.string(); var value = reader.string();
if(!key) break; if(!key) break;
state.raw[key] = value; state.raw[key] = value;
} }
if('hostname' in state.raw) state.name = state.raw.hostname; if('hostname' in state.raw) state.name = state.raw.hostname;
if('mapname' in state.raw) state.map = state.raw.mapname; if('mapname' in state.raw) state.map = state.raw.mapname;
if(self.trueTest(state.raw.password)) state.password = true; if(self.trueTest(state.raw.password)) state.password = true;
if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers); if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers);
state.players = self.readFieldData(reader); state.players = self.readFieldData(reader);
state.raw.teams = self.readFieldData(reader); state.raw.teams = self.readFieldData(reader);
self.finish(state); self.finish(state);
return true; return true;
} }
); );
}, },
readFieldData: function(reader) { readFieldData: function(reader) {
var count = reader.uint(1); var count = reader.uint(1);
// count is unreliable (often it's wrong), so we don't use it. // count is unreliable (often it's wrong), so we don't use it.
// read until we hit an empty first field string // read until we hit an empty first field string
if(this.debug) console.log("Reading fields, starting at: "+reader.rest()); if(this.debug) console.log("Reading fields, starting at: "+reader.rest());
var fields = []; var fields = [];
while(!reader.done()) { while(!reader.done()) {
var field = reader.string(); var field = reader.string();
if(!field) break; if(!field) break;
if(field.charCodeAt(0) <= 2) field = field.substring(1); if(field.charCodeAt(0) <= 2) field = field.substring(1);
fields.push(field); fields.push(field);
if(this.debug) console.log("field:"+field); if(this.debug) console.log("field:"+field);
} }
var units = []; var units = [];
outer: while(!reader.done()) { outer: while(!reader.done()) {
var unit = {}; var unit = {};
for(var iField = 0; iField < fields.length; iField++) { for(var iField = 0; iField < fields.length; iField++) {
var key = fields[iField]; var key = fields[iField];
var value = reader.string(); var value = reader.string();
if(!value && iField == 0) break outer; if(!value && iField == 0) break outer;
if(this.debug) console.log("value:"+value); if(this.debug) console.log("value:"+value);
if(key == 'player_') key = 'name'; if(key == 'player_') key = 'name';
else if(key == 'score_') key = 'score'; else if(key == 'score_') key = 'score';
else if(key == 'deaths_') key = 'deaths'; else if(key == 'deaths_') key = 'deaths';
else if(key == 'ping_') key = 'ping'; else if(key == 'ping_') key = 'ping';
else if(key == 'team_') key = 'team'; else if(key == 'team_') key = 'team';
else if(key == 'kills_') key = 'kills'; else if(key == 'kills_') key = 'kills';
else if(key == 'team_t') key = 'name'; else if(key == 'team_t') key = 'name';
else if(key == 'tickets_t') key = 'tickets'; else if(key == 'tickets_t') key = 'tickets';
if( if(
key == 'score' || key == 'deaths' key == 'score' || key == 'deaths'
|| key == 'ping' || key == 'team' || key == 'ping' || key == 'team'
|| key == 'kills' || key == 'tickets' || key == 'kills' || key == 'tickets'
) { ) {
if(value === '') continue; if(value === '') continue;
value = parseInt(value); value = parseInt(value);
} }
unit[key] = value; unit[key] = value;
} }
units.push(unit); units.push(unit);
} }
return units; return units;
} }
}); });

View file

@ -1,168 +1,168 @@
var async = require('async'); var async = require('async');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.sessionId = 1; this.sessionId = 1;
this.encoding = 'latin1'; this.encoding = 'latin1';
this.byteorder = 'be'; this.byteorder = 'be';
this.noChallenge = false; this.noChallenge = false;
this.useOnlySingleSplit = false; this.useOnlySingleSplit = false;
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
var challenge,packets; var challenge,packets;
async.series([ async.series([
function(c) { function(c) {
if(self.noChallenge) return c(); if(self.noChallenge) return c();
self.sendPacket(9,false,false,false,function(buffer) { self.sendPacket(9,false,false,false,function(buffer) {
var reader = self.reader(buffer); var reader = self.reader(buffer);
challenge = parseInt(reader.string()); challenge = parseInt(reader.string());
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendPacket(0,challenge,new Buffer([0xff,0xff,0xff,0x01]),true,function(b) { self.sendPacket(0,challenge,new Buffer([0xff,0xff,0xff,0x01]),true,function(b) {
packets = b; packets = b;
c(); c();
}); });
}, },
function(c) { function(c) {
// iterate over the received packets // iterate over the received packets
// the first packet will start off with k/v pairs, followed with data fields // the first packet will start off with k/v pairs, followed with data fields
// the following packets will only have data fields // the following packets will only have data fields
var data = {}; var data = {};
for(var iPacket = 0; iPacket < packets.length; iPacket++) { for(var iPacket = 0; iPacket < packets.length; iPacket++) {
var packet = packets[iPacket]; var packet = packets[iPacket];
var reader = self.reader(packet); var reader = self.reader(packet);
if(self.debug) { if(self.debug) {
console.log("+++"+packet.toString('hex')); console.log("+++"+packet.toString('hex'));
console.log(":::"+packet.toString('ascii')); console.log(":::"+packet.toString('ascii'));
} }
if(iPacket == 0) { if(iPacket == 0) {
while(!reader.done()) { while(!reader.done()) {
var key = reader.string(); var key = reader.string();
if(!key) break; if(!key) break;
var value = reader.string(); var value = reader.string();
// reread the next line if we hit the weird ut3 bug // reread the next line if we hit the weird ut3 bug
if(value == 'p1073741829') value = reader.string(); if(value == 'p1073741829') value = reader.string();
state.raw[key] = value; state.raw[key] = value;
} }
} }
var firstMode = true; var firstMode = true;
while(!reader.done()) { while(!reader.done()) {
var mode = reader.string(); var mode = reader.string();
if(mode.charCodeAt(0) <= 2) mode = mode.substring(1); if(mode.charCodeAt(0) <= 2) mode = mode.substring(1);
if(!mode) continue; if(!mode) continue;
var offset = 0; var offset = 0;
if(iPacket != 0 && firstMode) offset = reader.uint(1); if(iPacket != 0 && firstMode) offset = reader.uint(1);
reader.skip(1); reader.skip(1);
firstMode = false; firstMode = false;
while(!reader.done()) { while(!reader.done()) {
var item = reader.string(); var item = reader.string();
if(!item) break; if(!item) break;
if( if(
mode == 'player_' mode == 'player_'
|| mode == 'score_' || mode == 'score_'
|| mode == 'ping_' || mode == 'ping_'
|| mode == 'team_' || mode == 'team_'
|| mode == 'deaths_' || mode == 'deaths_'
|| mode == 'pid_' || mode == 'pid_'
) { ) {
if(state.players.length <= offset) if(state.players.length <= offset)
state.players.push({}); state.players.push({});
} }
if(mode == 'player_') state.players[offset].name = item; if(mode == 'player_') state.players[offset].name = item;
if(mode == 'score_') state.players[offset].score = parseInt(item); if(mode == 'score_') state.players[offset].score = parseInt(item);
if(mode == 'ping_') state.players[offset].ping = parseInt(item); if(mode == 'ping_') state.players[offset].ping = parseInt(item);
if(mode == 'team_') state.players[offset].team = parseInt(item); if(mode == 'team_') state.players[offset].team = parseInt(item);
if(mode == 'deaths_') state.players[offset].deaths = parseInt(item); if(mode == 'deaths_') state.players[offset].deaths = parseInt(item);
if(mode == 'pid_') state.players[offset].pid = item; if(mode == 'pid_') state.players[offset].pid = item;
offset++; offset++;
} }
} }
} }
if('hostname' in state.raw) state.name = state.raw.hostname; if('hostname' in state.raw) state.name = state.raw.hostname;
else if('servername' in state.raw) state.name = state.raw.servername; else if('servername' in state.raw) state.name = state.raw.servername;
if('mapname' in state.raw) state.map = state.raw.mapname; if('mapname' in state.raw) state.map = state.raw.mapname;
if(state.raw.password == '1') state.password = true; if(state.raw.password == '1') state.password = true;
if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers); if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers);
self.finish(state); self.finish(state);
} }
]); ]);
}, },
sendPacket: function(type,challenge,payload,assemble,c) { sendPacket: function(type,challenge,payload,assemble,c) {
var self = this; var self = this;
var challengeLength = (this.noChallenge || challenge === false) ? 0 : 4; var challengeLength = (this.noChallenge || challenge === false) ? 0 : 4;
var payloadLength = payload ? payload.length : 0; var payloadLength = payload ? payload.length : 0;
var b = new Buffer(7 + challengeLength + payloadLength); var b = new Buffer(7 + challengeLength + payloadLength);
b.writeUInt8(0xFE, 0); b.writeUInt8(0xFE, 0);
b.writeUInt8(0xFD, 1); b.writeUInt8(0xFD, 1);
b.writeUInt8(type, 2); b.writeUInt8(type, 2);
b.writeUInt32BE(this.sessionId, 3); b.writeUInt32BE(this.sessionId, 3);
if(challengeLength) b.writeInt32BE(challenge, 7); if(challengeLength) b.writeInt32BE(challenge, 7);
if(payloadLength) payload.copy(b, 7+challengeLength); if(payloadLength) payload.copy(b, 7+challengeLength);
var numPackets = 0; var numPackets = 0;
var packets = {}; var packets = {};
this.udpSend(b,function(buffer) { this.udpSend(b,function(buffer) {
var reader = self.reader(buffer); var reader = self.reader(buffer);
var iType = reader.uint(1); var iType = reader.uint(1);
if(iType != type) return; if(iType != type) return;
var iSessionId = reader.uint(4); var iSessionId = reader.uint(4);
if(iSessionId != self.sessionId) return; if(iSessionId != self.sessionId) return;
if(!assemble) { if(!assemble) {
c(reader.rest()); c(reader.rest());
return true; return true;
} }
if(self.useOnlySingleSplit) { if(self.useOnlySingleSplit) {
// has split headers, but they are worthless and only one packet is used // has split headers, but they are worthless and only one packet is used
reader.skip(11); reader.skip(11);
c([reader.rest()]); c([reader.rest()]);
return true; return true;
} }
reader.skip(9); // filler data -- usually set to 'splitnum\0' reader.skip(9); // filler data -- usually set to 'splitnum\0'
var id = reader.uint(1); var id = reader.uint(1);
var last = (id & 0x80); var last = (id & 0x80);
id = id & 0x7f; id = id & 0x7f;
if(last) numPackets = id+1; if(last) numPackets = id+1;
reader.skip(1); // "another 'packet number' byte, but isn't understood." reader.skip(1); // "another 'packet number' byte, but isn't understood."
packets[id] = reader.rest(); packets[id] = reader.rest();
if(self.debug) { if(self.debug) {
console.log("Received packet #"+id); console.log("Received packet #"+id);
if(last) console.log("(last)"); if(last) console.log("(last)");
} }
if(!numPackets || Object.keys(packets).length != numPackets) return; if(!numPackets || Object.keys(packets).length != numPackets) return;
// assemble the parts // assemble the parts
var list = []; var list = [];
for(var i = 0; i < numPackets; i++) { for(var i = 0; i < numPackets; i++) {
if(!(i in packets)) { if(!(i in packets)) {
self.fatal('Missing packet #'+i); self.fatal('Missing packet #'+i);
return true; return true;
} }
list.push(packets[i]); list.push(packets[i]);
} }
c(list); c(list);
return true; return true;
}); });
} }
}); });

View file

@ -1,6 +1,6 @@
module.exports = require('./quake2').extend({ module.exports = require('./quake2').extend({
init: function() { init: function() {
this._super(); this._super();
this.sendHeader = '\xFFstatus\x0a'; this.sendHeader = '\xFFstatus\x0a';
} }
}); });

View file

@ -1,24 +1,24 @@
/* /*
module.exports = require('./valve').extend({ module.exports = require('./valve').extend({
init: function() { init: function() {
this._super(); this._super();
} }
}); });
*/ */
// supposedly, gamespy3 is the "official" query protocol for jcmp, // supposedly, gamespy3 is the "official" query protocol for jcmp,
// but it's broken (requires useOnlySingleSplit), and doesn't include player names // but it's broken (requires useOnlySingleSplit), and doesn't include player names
module.exports = require('./gamespy3').extend({ module.exports = require('./gamespy3').extend({
init: function() { init: function() {
this._super(); this._super();
this.useOnlySingleSplit = true; this.useOnlySingleSplit = true;
}, },
finalizeState: function(state) { finalizeState: function(state) {
this._super(state); this._super(state);
if(!state.players.length && parseInt(state.raw.numplayers)) { if(!state.players.length && parseInt(state.raw.numplayers)) {
for(var i = 0; i < parseInt(state.raw.numplayers); i++) { for(var i = 0; i < parseInt(state.raw.numplayers); i++) {
state.players.push({}); state.players.push({});
} }
} }
} }
}); });

View file

@ -1,6 +1,6 @@
module.exports = require('./unreal2').extend({ module.exports = require('./unreal2').extend({
readExtraInfo: function(reader,state) { readExtraInfo: function(reader,state) {
state.raw.wavecurrent = reader.uint(4); state.raw.wavecurrent = reader.uint(4);
state.raw.wavetotal = reader.uint(4); state.raw.wavetotal = reader.uint(4);
} }
}); });

View file

@ -1,37 +1,37 @@
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.encoding = 'latin1'; this.encoding = 'latin1';
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
this.udpSend('M2MP',function(buffer) { this.udpSend('M2MP',function(buffer) {
var reader = self.reader(buffer); var reader = self.reader(buffer);
var header = reader.string({length:4}); var header = reader.string({length:4});
if(header != 'M2MP') return; if(header != 'M2MP') return;
state.name = self.readString(reader); state.name = self.readString(reader);
state.raw.numplayers = self.readString(reader); state.raw.numplayers = self.readString(reader);
state.maxplayers = self.readString(reader); state.maxplayers = self.readString(reader);
state.raw.gamemode = self.readString(reader); state.raw.gamemode = self.readString(reader);
state.password = !!reader.uint(1); state.password = !!reader.uint(1);
while(!reader.done()) { while(!reader.done()) {
var name = self.readString(reader); var name = self.readString(reader);
if(!name) break; if(!name) break;
state.players.push({ state.players.push({
name:name name:name
}); });
} }
self.finish(state); self.finish(state);
return true; return true;
}); });
}, },
readString: function(reader) { readString: function(reader) {
var length = reader.uint(1); var length = reader.uint(1);
return reader.string({length:length-1}); return reader.string({length:length-1});
}, },
}); });

View file

@ -1,93 +1,93 @@
var varint = require('varint'), var varint = require('varint'),
async = require('async'); async = require('async');
function varIntBuffer(num) { function varIntBuffer(num) {
return new Buffer(varint.encode(num)); return new Buffer(varint.encode(num));
} }
function buildPacket(id,data) { function buildPacket(id,data) {
if(!data) data = new Buffer(0); if(!data) data = new Buffer(0);
var idBuffer = varIntBuffer(id); var idBuffer = varIntBuffer(id);
return Buffer.concat([ return Buffer.concat([
varIntBuffer(data.length+idBuffer.length), varIntBuffer(data.length+idBuffer.length),
idBuffer, idBuffer,
data data
]); ]);
} }
module.exports = require('./core').extend({ module.exports = require('./core').extend({
run: function(state) { run: function(state) {
var self = this; var self = this;
var receivedData; var receivedData;
async.series([ async.series([
function(c) { function(c) {
// build and send handshake and status TCP packet // build and send handshake and status TCP packet
var portBuf = new Buffer(2); var portBuf = new Buffer(2);
portBuf.writeUInt16BE(self.options.port_query,0); portBuf.writeUInt16BE(self.options.port_query,0);
var addressBuf = new Buffer(self.options.address,'utf8'); var addressBuf = new Buffer(self.options.address,'utf8');
var bufs = [ var bufs = [
varIntBuffer(4), varIntBuffer(4),
varIntBuffer(addressBuf.length), varIntBuffer(addressBuf.length),
addressBuf, addressBuf,
portBuf, portBuf,
varIntBuffer(1) varIntBuffer(1)
]; ];
var outBuffer = Buffer.concat([ var outBuffer = Buffer.concat([
buildPacket(0,Buffer.concat(bufs)), buildPacket(0,Buffer.concat(bufs)),
buildPacket(0) buildPacket(0)
]); ]);
self.tcpSend(outBuffer, function(data) { self.tcpSend(outBuffer, function(data) {
if(data.length < 10) return false; if(data.length < 10) return false;
var expected = varint.decode(data); var expected = varint.decode(data);
data = data.slice(varint.decode.bytesRead); data = data.slice(varint.decode.bytesRead);
if(data.length < expected) return false; if(data.length < expected) return false;
receivedData = data; receivedData = data;
c(); c();
return true; return true;
}); });
}, },
function(c) { function(c) {
// parse response // parse response
var data = receivedData; var data = receivedData;
var packetId = varint.decode(data); var packetId = varint.decode(data);
data = data.slice(varint.decode.bytesRead); data = data.slice(varint.decode.bytesRead);
var strLen = varint.decode(data); var strLen = varint.decode(data);
data = data.slice(varint.decode.bytesRead); data = data.slice(varint.decode.bytesRead);
var str = data.toString('utf8'); var str = data.toString('utf8');
var json; var json;
try { try {
json = JSON.parse(str); json = JSON.parse(str);
delete json.favicon; delete json.favicon;
if(self.debug) console.log(json); if(self.debug) console.log(json);
} catch(e) { } catch(e) {
return self.fatal('Invalid JSON'); return self.fatal('Invalid JSON');
} }
state.raw.version = json.version.name; state.raw.version = json.version.name;
state.maxplayers = json.players.max; state.maxplayers = json.players.max;
state.raw.description = json.description.text; state.raw.description = json.description.text;
if(json.players.sample) { if(json.players.sample) {
for(var i = 0; i < json.players.sample.length; i++) { for(var i = 0; i < json.players.sample.length; i++) {
state.players.push({ state.players.push({
id: json.players.sample[i].id, id: json.players.sample[i].id,
name: json.players.sample[i].name name: json.players.sample[i].name
}); });
} }
} }
while(state.players.length < json.players.online) { while(state.players.length < json.players.online) {
state.players.push({}); state.players.push({});
} }
self.finish(state); self.finish(state);
} }
]); ]);
} }
}); });

View file

@ -1,44 +1,44 @@
var async = require('async'); var async = require('async');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.options.tcpTimeout = 5000; this.options.tcpTimeout = 5000;
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
this.tcpSend('json', function(buffer) { this.tcpSend('json', function(buffer) {
if(buffer.length < 10) return; if(buffer.length < 10) return;
var str = buffer.toString(); var str = buffer.toString();
var json; var json;
try { try {
json = JSON.parse(str); json = JSON.parse(str);
} catch(e) { } catch(e) {
// probably not all here yet // probably not all here yet
return; return;
} }
state.raw = json; state.raw = json;
state.name = json.name; state.name = json.name;
var channelStack = [state.raw.root]; var channelStack = [state.raw.root];
while(channelStack.length) { while(channelStack.length) {
var channel = channelStack.shift(); var channel = channelStack.shift();
channel.description = self.cleanComment(channel.description); channel.description = self.cleanComment(channel.description);
channelStack = channelStack.concat(channel.channels); channelStack = channelStack.concat(channel.channels);
for(var i = 0; i < channel.users.length; i++) { for(var i = 0; i < channel.users.length; i++) {
var user = channel.users[i]; var user = channel.users[i];
user.comment = self.cleanComment(user.comment); user.comment = self.cleanComment(user.comment);
state.players.push(user); state.players.push(user);
} }
} }
self.finish(state); self.finish(state);
return true; return true;
}); });
}, },
cleanComment: function(str) { cleanComment: function(str) {
return str.replace(/<.*>/g,''); return str.replace(/<.*>/g,'');
} }
}); });

View file

@ -1,29 +1,29 @@
var async = require('async'); var async = require('async');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.byteorder = 'be'; this.byteorder = 'be';
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
this.udpSend('\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08', function(buffer) { this.udpSend('\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08', function(buffer) {
if(buffer.length < 24) return; if(buffer.length < 24) return;
var reader = self.reader(buffer); var reader = self.reader(buffer);
reader.skip(1); reader.skip(1);
state.raw.versionMajor = reader.uint(1); state.raw.versionMajor = reader.uint(1);
state.raw.versionMinor = reader.uint(1); state.raw.versionMinor = reader.uint(1);
state.raw.versionPatch = reader.uint(1); state.raw.versionPatch = reader.uint(1);
reader.skip(8); reader.skip(8);
state.raw.numplayers = reader.uint(4); state.raw.numplayers = reader.uint(4);
state.maxplayers = reader.uint(4); state.maxplayers = reader.uint(4);
state.raw.allowedbandwidth = reader.uint(4); state.raw.allowedbandwidth = reader.uint(4);
for(var i = 0; i < state.raw.numplayers; i++) { for(var i = 0; i < state.raw.numplayers; i++) {
state.players.push({}); state.players.push({});
} }
self.finish(state); self.finish(state);
return true; return true;
}); });
} }
}); });

View file

@ -1,54 +1,54 @@
var request = require('request'); var request = require('request');
module.exports = require('./protocols/core').extend({ module.exports = require('./protocols/core').extend({
run: function(state) { run: function(state) {
var self = this; var self = this;
request({ request({
uri: 'http://mutantfactions.net/game/receiveLobby.php', uri: 'http://mutantfactions.net/game/receiveLobby.php',
timeout: 3000, timeout: 3000,
}, function(e,r,body) { }, function(e,r,body) {
if(e) return self.fatal('Lobby request error'); if(e) return self.fatal('Lobby request error');
var split = body.split('<br/>'); var split = body.split('<br/>');
var found = false; var found = false;
for(var i = 0; i < split.length; i++) { for(var i = 0; i < split.length; i++) {
var line = split[i]; var line = split[i];
var fields = line.split('::'); var fields = line.split('::');
var ip = fields[2]; var ip = fields[2];
var port = fields[3]; var port = fields[3];
if(ip == self.options.address && port == self.options.port) { if(ip == self.options.address && port == self.options.port) {
found = fields; found = fields;
break; break;
} }
} }
if(!found) return self.fatal('Server not found in list'); if(!found) return self.fatal('Server not found in list');
state.raw.countrycode = fields[0]; state.raw.countrycode = fields[0];
state.raw.country = fields[1]; state.raw.country = fields[1];
state.name = fields[4]; state.name = fields[4];
state.map = fields[5]; state.map = fields[5];
state.raw.numplayers = fields[6]; state.raw.numplayers = fields[6];
state.maxplayers = fields[7]; state.maxplayers = fields[7];
// fields[8] is unknown? // fields[8] is unknown?
state.raw.rules = fields[9]; state.raw.rules = fields[9];
state.raw.gamemode = fields[10]; state.raw.gamemode = fields[10];
state.raw.gangsters = fields[11]; state.raw.gangsters = fields[11];
state.raw.cashrate = fields[12]; state.raw.cashrate = fields[12];
state.raw.missions = fields[13]; state.raw.missions = fields[13];
state.raw.vehicles = fields[14]; state.raw.vehicles = fields[14];
state.raw.customweapons = fields[15]; state.raw.customweapons = fields[15];
state.raw.friendlyfire = fields[16]; state.raw.friendlyfire = fields[16];
state.raw.mercs = fields[17]; state.raw.mercs = fields[17];
// fields[18] is unknown? listen server? // fields[18] is unknown? listen server?
state.raw.version = fields[19]; state.raw.version = fields[19];
for(var i = 0; i < state.raw.numplayers; i++) { for(var i = 0; i < state.raw.numplayers; i++) {
state.players.push({}); state.players.push({});
} }
self.finish(state); self.finish(state);
}); });
} }
}); });

View file

@ -1,75 +1,75 @@
var gbxremote = require('gbxremote'), var gbxremote = require('gbxremote'),
async = require('async'); async = require('async');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.options.port = 2350; this.options.port = 2350;
this.options.port_query = 5000; this.options.port_query = 5000;
this.gbxclient = false; this.gbxclient = false;
}, },
reset: function() { reset: function() {
this._super(); this._super();
if(this.gbxclient) { if(this.gbxclient) {
this.gbxclient.terminate(); this.gbxclient.terminate();
this.gbxclient = false; this.gbxclient = false;
} }
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
var cmds = [ var cmds = [
['Connect'], ['Connect'],
['Authenticate', this.options.login,this.options.password], ['Authenticate', this.options.login,this.options.password],
['GetStatus'], ['GetStatus'],
['GetPlayerList',500,0], ['GetPlayerList',500,0],
['GetServerOptions'], ['GetServerOptions'],
['GetCurrentChallengeInfo'], ['GetCurrentChallengeInfo'],
['GetCurrentGameInfo'] ['GetCurrentGameInfo']
]; ];
var results = []; var results = [];
async.eachSeries(cmds, function(cmdset,c) { async.eachSeries(cmds, function(cmdset,c) {
var cmd = cmdset[0]; var cmd = cmdset[0];
var params = cmdset.slice(1); var params = cmdset.slice(1);
if(cmd == 'Connect') { if(cmd == 'Connect') {
var client = self.gbxclient = gbxremote.createClient(self.options.port_query,self.options.host, function(err) { 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)); if(err) return self.fatal('GBX error '+JSON.stringify(err));
c(); c();
}); });
client.on('error',function(){}); client.on('error',function(){});
} else { } else {
self.gbxclient.methodCall(cmd, params, function(err, value) { self.gbxclient.methodCall(cmd, params, function(err, value) {
if(err) return self.fatal('XMLRPC error '+JSON.stringify(err)); if(err) return self.fatal('XMLRPC error '+JSON.stringify(err));
results.push(value); results.push(value);
c(); c();
}); });
} }
}, function() { }, function() {
var gamemode = ''; var gamemode = '';
var igm = results[5].GameMode; var igm = results[5].GameMode;
if(igm == 0) gamemode="Rounds"; if(igm == 0) gamemode="Rounds";
if(igm == 1) gamemode="Time Attack"; if(igm == 1) gamemode="Time Attack";
if(igm == 2) gamemode="Team"; if(igm == 2) gamemode="Team";
if(igm == 3) gamemode="Laps"; if(igm == 3) gamemode="Laps";
if(igm == 4) gamemode="Stunts"; if(igm == 4) gamemode="Stunts";
if(igm == 5) gamemode="Cup"; if(igm == 5) gamemode="Cup";
state.name = self.stripColors(results[3].Name); state.name = self.stripColors(results[3].Name);
state.password = (results[3].Password != 'No password'); state.password = (results[3].Password != 'No password');
state.maxplayers = results[3].CurrentMaxPlayers; state.maxplayers = results[3].CurrentMaxPlayers;
state.map = self.stripColors(results[4].Name); state.map = self.stripColors(results[4].Name);
state.raw.gametype = gamemode; state.raw.gametype = gamemode;
results[2].forEach(function(player) { results[2].forEach(function(player) {
state.players.push({name:self.stripColors(player.Name)}); state.players.push({name:self.stripColors(player.Name)});
}); });
self.finish(state); self.finish(state);
}); });
}, },
stripColors: function(str) { stripColors: function(str) {
return str.replace(/\$([0-9a-f][^\$]?[^\$]?|[^\$]?)/g,''); return str.replace(/\$([0-9a-f][^\$]?[^\$]?|[^\$]?)/g,'');
} }
}); });

View file

@ -1,7 +1,7 @@
module.exports = require('./quake2').extend({ module.exports = require('./quake2').extend({
init: function() { init: function() {
this._super(); this._super();
this.responseHeader = 'n'; this.responseHeader = 'n';
this.isQuake1 = true; this.isQuake1 = true;
} }
}); });

View file

@ -1,85 +1,85 @@
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.encoding = 'latin1'; this.encoding = 'latin1';
this.delimiter = '\n'; this.delimiter = '\n';
this.sendHeader = 'status'; this.sendHeader = 'status';
this.responseHeader = 'print'; this.responseHeader = 'print';
this.isQuake1 = false; this.isQuake1 = false;
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
this.udpSend('\xff\xff\xff\xff'+this.sendHeader+'\x00',function(buffer) { this.udpSend('\xff\xff\xff\xff'+this.sendHeader+'\x00',function(buffer) {
var reader = self.reader(buffer); var reader = self.reader(buffer);
var header = reader.string({length:4}); var header = reader.string({length:4});
if(header != '\xff\xff\xff\xff') return; if(header != '\xff\xff\xff\xff') return;
var response; var response;
if(this.isQuake1) { if(this.isQuake1) {
response = reader.string({length:1}); response = reader.string({length:1});
} else { } else {
response = reader.string(); response = reader.string();
} }
if(response != this.responseHeader) return; if(response != this.responseHeader) return;
var info = reader.string().split('\\'); var info = reader.string().split('\\');
if(info[0] == '') info.shift(); if(info[0] == '') info.shift();
while(true) { while(true) {
var key = info.shift(); var key = info.shift();
var value = info.shift(); var value = info.shift();
if(typeof value == 'undefined') break; if(typeof value == 'undefined') break;
state.raw[key] = value; state.raw[key] = value;
} }
while(!reader.done()) { while(!reader.done()) {
var line = reader.string(); var line = reader.string();
if(!line || line.charAt(0) == '\0') break; if(!line || line.charAt(0) == '\0') break;
var args = []; var args = [];
var split = line.split('"'); var split = line.split('"');
var inQuote = false; var inQuote = false;
split.forEach(function(part,i) { split.forEach(function(part,i) {
var inQuote = (i%2 == 1); var inQuote = (i%2 == 1);
if(inQuote) { if(inQuote) {
args.push(part); args.push(part);
} else { } else {
var splitSpace = part.split(' '); var splitSpace = part.split(' ');
splitSpace.forEach(function(subpart) { splitSpace.forEach(function(subpart) {
if(subpart) args.push(subpart); if(subpart) args.push(subpart);
}); });
} }
}); });
var player = {}; var player = {};
if(self.isQuake1) { if(self.isQuake1) {
player.id = parseInt(args.shift()); player.id = parseInt(args.shift());
player.score = parseInt(args.shift()); player.score = parseInt(args.shift());
player.time = parseInt(args.shift()); player.time = parseInt(args.shift());
player.ping = parseInt(args.shift()); player.ping = parseInt(args.shift());
player.name = args.shift(); player.name = args.shift();
player.skin = args.shift(); player.skin = args.shift();
player.color1 = parseInt(args.shift()); player.color1 = parseInt(args.shift());
player.color2 = parseInt(args.shift()); player.color2 = parseInt(args.shift());
} else { } else {
player.frags = parseInt(args.shift()); player.frags = parseInt(args.shift());
player.ping = parseInt(args.shift()); player.ping = parseInt(args.shift());
player.name = args.shift() || ''; player.name = args.shift() || '';
player.address = args.shift() || ''; player.address = args.shift() || '';
} }
(player.ping ? state.players : state.bots).push(player); (player.ping ? state.players : state.bots).push(player);
} }
if('g_needpass' in state.raw) state.password = state.raw.g_needpass; if('g_needpass' in state.raw) state.password = state.raw.g_needpass;
if('mapname' in state.raw) state.map = state.raw.mapname; if('mapname' in state.raw) state.map = state.raw.mapname;
if('sv_maxclients' in state.raw) state.maxplayers = state.raw.sv_maxclients; if('sv_maxclients' in state.raw) state.maxplayers = state.raw.sv_maxclients;
if('sv_hostname' in state.raw) state.name = state.raw.sv_hostname; if('sv_hostname' in state.raw) state.name = state.raw.sv_hostname;
self.finish(state); self.finish(state);
return true; return true;
}); });
} }
}); });

View file

@ -1,19 +1,19 @@
module.exports = require('./quake2').extend({ module.exports = require('./quake2').extend({
init: function() { init: function() {
this._super(); this._super();
this.sendHeader = 'getstatus'; this.sendHeader = 'getstatus';
this.responseHeader = 'statusResponse'; this.responseHeader = 'statusResponse';
}, },
finalizeState: function(state) { finalizeState: function(state) {
state.name = this.stripColors(state.name); state.name = this.stripColors(state.name);
for(var i in state.raw) { for(var i in state.raw) {
state.raw[i] = this.stripColors(state.raw[i]); state.raw[i] = this.stripColors(state.raw[i]);
} }
for(var i = 0; i < state.players.length; i++) { for(var i = 0; i < state.players.length; i++) {
state.players[i].name = this.stripColors(state.players[i].name); state.players[i].name = this.stripColors(state.players[i].name);
} }
}, },
stripColors: function(str) { stripColors: function(str) {
return str.replace(/\^(X.{6}|.)/g,''); return str.replace(/\^(X.{6}|.)/g,'');
} }
}); });

View file

@ -1,90 +1,90 @@
var async = require('async'); var async = require('async');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
run: function(state) { run: function(state) {
var self = this; var self = this;
var len; var len;
async.series([ async.series([
function(c) { function(c) {
self.sendPacket('i',function(reader) { self.sendPacket('i',function(reader) {
state.password = !!reader.uint(1); state.password = !!reader.uint(1);
state.raw.numplayers = reader.uint(2); state.raw.numplayers = reader.uint(2);
state.maxplayers = reader.uint(2); state.maxplayers = reader.uint(2);
state.name = self.readString(reader,4); state.name = self.readString(reader,4);
state.raw.gamemode = self.readString(reader,4); state.raw.gamemode = self.readString(reader,4);
self.map = self.readString(reader,4); self.map = self.readString(reader,4);
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendPacket('r',function(reader) { self.sendPacket('r',function(reader) {
var ruleCount = reader.uint(2); var ruleCount = reader.uint(2);
state.raw.rules = {}; state.raw.rules = {};
for(var i = 0; i < ruleCount; i++) { for(var i = 0; i < ruleCount; i++) {
var key = self.readString(reader,1); var key = self.readString(reader,1);
var value = self.readString(reader,1); var value = self.readString(reader,1);
state.raw.rules[key] = value; state.raw.rules[key] = value;
} }
if('mapname' in state.raw.rules) if('mapname' in state.raw.rules)
state.map = state.raw.rules.mapname; state.map = state.raw.rules.mapname;
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendPacket('d',function(reader) { self.sendPacket('d',function(reader) {
var playerCount = reader.uint(2); var playerCount = reader.uint(2);
for(var i = 0; i < playerCount; i++) { for(var i = 0; i < playerCount; i++) {
var player = {}; var player = {};
player.id = reader.uint(1); player.id = reader.uint(1);
player.name = self.readString(reader,1); player.name = self.readString(reader,1);
player.score = reader.int(4); player.score = reader.int(4);
player.ping = reader.uint(4); player.ping = reader.uint(4);
state.players.push(player); state.players.push(player);
} }
c(); c();
},function() { },function() {
for(var i = 0; i < state.raw.numplayers; i++) { for(var i = 0; i < state.raw.numplayers; i++) {
state.players.push({}); state.players.push({});
} }
}); });
}, },
function(c) { function(c) {
self.finish(state); self.finish(state);
} }
]); ]);
}, },
readString: function(reader,lenBytes) { readString: function(reader,lenBytes) {
var length = reader.uint(lenBytes); var length = reader.uint(lenBytes);
if(!length) return ''; if(!length) return '';
var string = reader.string({length:length}); var string = reader.string({length:length});
return string; return string;
}, },
sendPacket: function(type,onresponse,ontimeout) { sendPacket: function(type,onresponse,ontimeout) {
var self = this; var self = this;
var outbuffer = new Buffer(11); var outbuffer = new Buffer(11);
outbuffer.writeUInt32BE(0x53414D50,0); outbuffer.writeUInt32BE(0x53414D50,0);
var ipSplit = self.options.address.split('.'); var ipSplit = self.options.address.split('.');
outbuffer.writeUInt8(parseInt(ipSplit[0]),4); outbuffer.writeUInt8(parseInt(ipSplit[0]),4);
outbuffer.writeUInt8(parseInt(ipSplit[1]),5); outbuffer.writeUInt8(parseInt(ipSplit[1]),5);
outbuffer.writeUInt8(parseInt(ipSplit[2]),6); outbuffer.writeUInt8(parseInt(ipSplit[2]),6);
outbuffer.writeUInt8(parseInt(ipSplit[3]),7); outbuffer.writeUInt8(parseInt(ipSplit[3]),7);
outbuffer.writeUInt16LE(self.options.port,8); outbuffer.writeUInt16LE(self.options.port,8);
outbuffer.writeUInt8(type.charCodeAt(0),10); outbuffer.writeUInt8(type.charCodeAt(0),10);
this.udpSend(outbuffer,function(buffer) { this.udpSend(outbuffer,function(buffer) {
var reader = self.reader(buffer); var reader = self.reader(buffer);
for(var i = 0; i < outbuffer.length; i++) { for(var i = 0; i < outbuffer.length; i++) {
if(outbuffer.readUInt8(i) !== reader.uint(1)) return; if(outbuffer.readUInt8(i) !== reader.uint(1)) return;
} }
onresponse(reader); onresponse(reader);
return true; return true;
},function() { },function() {
if(ontimeout) { if(ontimeout) {
ontimeout(); ontimeout();
return true; return true;
} }
}); });
} }
}); });

View file

@ -1,77 +1,77 @@
var async = require('async'); var async = require('async');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
run: function(state) { run: function(state) {
var self = this; var self = this;
async.series([ async.series([
function(c) { function(c) {
self.sendCommand('sel '+self.options.port, function(data) { self.sendCommand('sel '+self.options.port, function(data) {
if(data != '[TS]') self.fatal('Invalid header'); if(data != '[TS]') self.fatal('Invalid header');
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendCommand('si', function(data) { self.sendCommand('si', function(data) {
var split = data.split('\r\n'); var split = data.split('\r\n');
split.forEach(function(line) { split.forEach(function(line) {
var equals = line.indexOf('='); var equals = line.indexOf('=');
var key = equals == -1 ? line : line.substr(0,equals); var key = equals == -1 ? line : line.substr(0,equals);
var value = equals == -1 ? '' : line.substr(equals+1); var value = equals == -1 ? '' : line.substr(equals+1);
state.raw[key] = value; state.raw[key] = value;
}); });
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendCommand('pl', function(data) { self.sendCommand('pl', function(data) {
var split = data.split('\r\n'); var split = data.split('\r\n');
var fields = split.shift().split('\t'); var fields = split.shift().split('\t');
split.forEach(function(line) { split.forEach(function(line) {
var split2 = line.split('\t'); var split2 = line.split('\t');
var player = {}; var player = {};
split2.forEach(function(value,i) { split2.forEach(function(value,i) {
var key = fields[i]; var key = fields[i];
if(!key) return; if(!key) return;
if(key == 'nick') key = 'name'; if(key == 'nick') key = 'name';
if(m = value.match(/^"(.*)"$/)) value = m[1]; if(m = value.match(/^"(.*)"$/)) value = m[1];
player[key] = value; player[key] = value;
}); });
state.players.push(player); state.players.push(player);
}); });
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendCommand('cl', function(data) { self.sendCommand('cl', function(data) {
var split = data.split('\r\n'); var split = data.split('\r\n');
var fields = split.shift().split('\t'); var fields = split.shift().split('\t');
state.raw.channels = []; state.raw.channels = [];
split.forEach(function(line) { split.forEach(function(line) {
var split2 = line.split('\t'); var split2 = line.split('\t');
var channel = {}; var channel = {};
split2.forEach(function(value,i) { split2.forEach(function(value,i) {
var key = fields[i]; var key = fields[i];
if(!key) return; if(!key) return;
if(m = value.match(/^"(.*)"$/)) value = m[1]; if(m = value.match(/^"(.*)"$/)) value = m[1];
channel[key] = value; channel[key] = value;
}); });
state.raw.channels.push(channel); state.raw.channels.push(channel);
}); });
c(); c();
}); });
}, },
function(c) { function(c) {
self.finish(state); self.finish(state);
} }
]); ]);
}, },
sendCommand: function(cmd,c) { sendCommand: function(cmd,c) {
this.tcpSend(cmd+'\x0A', function(buffer) { this.tcpSend(cmd+'\x0A', function(buffer) {
if(buffer.length < 6) return; if(buffer.length < 6) return;
if(buffer.slice(-6).toString() != '\r\nOK\r\n') return; if(buffer.slice(-6).toString() != '\r\nOK\r\n') return;
c(buffer.slice(0,-6).toString()); c(buffer.slice(0,-6).toString());
return true; return true;
}); });
} }
}); });

View file

@ -1,74 +1,74 @@
var async = require('async'); var async = require('async');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
run: function(state) { run: function(state) {
var self = this; var self = this;
async.series([ async.series([
function(c) { function(c) {
self.sendCommand('use port='+self.options.port, function(data) { self.sendCommand('use port='+self.options.port, function(data) {
var split = data.split('\n\r'); var split = data.split('\n\r');
if(split[0] != 'TS3') self.fatal('Invalid header'); if(split[0] != 'TS3') self.fatal('Invalid header');
c(); c();
}, true); }, true);
}, },
function(c) { function(c) {
self.sendCommand('serverinfo', function(data) { self.sendCommand('serverinfo', function(data) {
state.raw = data[0]; state.raw = data[0];
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendCommand('clientlist', function(data) { self.sendCommand('clientlist', function(data) {
for(var i = 0; i < data.length; i++) { for(var i = 0; i < data.length; i++) {
data[i].name = data[i].client_nickname; data[i].name = data[i].client_nickname;
delete data[i].client_nickname; delete data[i].client_nickname;
state.players.push(data[i]); state.players.push(data[i]);
} }
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendCommand('channellist -topic', function(data) { self.sendCommand('channellist -topic', function(data) {
state.raw.channels = data; state.raw.channels = data;
c(); c();
}); });
}, },
function(c) { function(c) {
self.finish(state); self.finish(state);
} }
]); ]);
}, },
sendCommand: function(cmd,c,raw) { sendCommand: function(cmd,c,raw) {
this.tcpSend(cmd+'\x0A', function(buffer) { this.tcpSend(cmd+'\x0A', function(buffer) {
if(buffer.length < 21) return; if(buffer.length < 21) return;
if(buffer.slice(-21).toString() != '\n\rerror id=0 msg=ok\n\r') return; if(buffer.slice(-21).toString() != '\n\rerror id=0 msg=ok\n\r') return;
var body = buffer.slice(0,-21).toString(); var body = buffer.slice(0,-21).toString();
var out; var out;
if(raw) { if(raw) {
out = body; out = body;
} else { } else {
var segments = body.split('|'); var segments = body.split('|');
out = []; out = [];
segments.forEach(function(line) { segments.forEach(function(line) {
var split = line.split(' '); var split = line.split(' ');
var unit = {}; var unit = {};
split.forEach(function(field) { split.forEach(function(field) {
var equals = field.indexOf('='); var equals = field.indexOf('=');
var key = equals == -1 ? field : field.substr(0,equals); var key = equals == -1 ? field : field.substr(0,equals);
var value = equals == -1 ? '' : field.substr(equals+1) var value = equals == -1 ? '' : field.substr(equals+1)
.replace(/\\s/g,' ').replace(/\\\//g,'/'); .replace(/\\s/g,' ').replace(/\\\//g,'/');
unit[key] = value; unit[key] = value;
}); });
out.push(unit); out.push(unit);
}); });
} }
c(out); c(out);
return true; return true;
}); });
} }
}); });

View file

@ -1,35 +1,35 @@
var request = require('request'); var request = require('request');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
run: function(state) { run: function(state) {
var self = this; var self = this;
request({ request({
uri: 'http://'+this.options.address+':'+this.options.port_query+'/v2/server/status', uri: 'http://'+this.options.address+':'+this.options.port_query+'/v2/server/status',
timeout: 3000, timeout: 3000,
qs: { qs: {
players: 'true', players: 'true',
token: this.options.token token: this.options.token
} }
}, function(e,r,body) { }, function(e,r,body) {
if(e) return self.fatal('HTTP error'); if(e) return self.fatal('HTTP error');
var json; var json;
try { try {
json = JSON.parse(body); json = JSON.parse(body);
} catch(e) { } catch(e) {
return self.fatal('Invalid JSON'); return self.fatal('Invalid JSON');
} }
if(json.status != 200) return self.fatal('Invalid status'); if(json.status != 200) return self.fatal('Invalid status');
json.players.forEach(function(one) { json.players.forEach(function(one) {
state.players.push({name:one.nickname,team:one.team}); state.players.push({name:one.nickname,team:one.team});
}); });
state.name = json.name; state.name = json.name;
state.raw.port = json.port; state.raw.port = json.port;
state.raw.numplayers = json.playercount; state.raw.numplayers = json.playercount;
self.finish(state); self.finish(state);
}); });
} }
}); });

View file

@ -1,142 +1,142 @@
var async = require('async'); var async = require('async');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.encoding = 'latin1'; this.encoding = 'latin1';
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
async.series([ async.series([
function(c) { function(c) {
self.sendPacket(0,true,function(b) { self.sendPacket(0,true,function(b) {
var reader = self.reader(b); var reader = self.reader(b);
state.raw.serverid = reader.uint(4); state.raw.serverid = reader.uint(4);
state.raw.ip = self.readUnrealString(reader); state.raw.ip = self.readUnrealString(reader);
state.raw.port = reader.uint(4); state.raw.port = reader.uint(4);
state.raw.queryport = reader.uint(4); state.raw.queryport = reader.uint(4);
state.name = self.readUnrealString(reader,true); state.name = self.readUnrealString(reader,true);
state.map = self.readUnrealString(reader,true); state.map = self.readUnrealString(reader,true);
state.raw.gametype = self.readUnrealString(reader,true); state.raw.gametype = self.readUnrealString(reader,true);
state.raw.numplayers = reader.uint(4); state.raw.numplayers = reader.uint(4);
state.maxplayers = reader.uint(4); state.maxplayers = reader.uint(4);
self.readExtraInfo(reader,state); self.readExtraInfo(reader,state);
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendPacket(1,true,function(b) { self.sendPacket(1,true,function(b) {
var reader = self.reader(b); var reader = self.reader(b);
state.raw.mutators = []; state.raw.mutators = [];
state.raw.rules = {}; state.raw.rules = {};
while(!reader.done()) { while(!reader.done()) {
var key = self.readUnrealString(reader,true); var key = self.readUnrealString(reader,true);
var value = self.readUnrealString(reader,true); var value = self.readUnrealString(reader,true);
if(key == 'Mutator') state.raw.mutators.push(value); if(key == 'Mutator') state.raw.mutators.push(value);
else state.raw.rules[key] = value; else state.raw.rules[key] = value;
} }
if('GamePassword' in state.raw.rules) if('GamePassword' in state.raw.rules)
state.password = state.raw.rules.GamePassword != 'True'; state.password = state.raw.rules.GamePassword != 'True';
c(); c();
}); });
}, },
function(c) { function(c) {
self.sendPacket(2,false,function(b) { self.sendPacket(2,false,function(b) {
var reader = self.reader(b); var reader = self.reader(b);
while(!reader.done()) { while(!reader.done()) {
var player = {}; var player = {};
player.id = reader.uint(4); player.id = reader.uint(4);
if(!player.id) break; if(!player.id) break;
if(player.id == 0) { if(player.id == 0) {
// Unreal2XMP Player (ID is always 0) // Unreal2XMP Player (ID is always 0)
reader.skip(4); reader.skip(4);
} }
player.name = self.readUnrealString(reader,true); player.name = self.readUnrealString(reader,true);
player.ping = reader.uint(4); player.ping = reader.uint(4);
player.score = reader.int(4); player.score = reader.int(4);
reader.skip(4); // stats ID reader.skip(4); // stats ID
// Extra data for Unreal2XMP players // Extra data for Unreal2XMP players
if(player.id == 0) { if(player.id == 0) {
var count = reader.uint(1); var count = reader.uint(1);
for(var iField = 0; iField < count; iField++) { for(var iField = 0; iField < count; iField++) {
var key = self.readUnrealString(reader,true); var key = self.readUnrealString(reader,true);
var value = self.readUnrealString(reader,true); var value = self.readUnrealString(reader,true);
player[key] = value; player[key] = value;
} }
} }
if(player.id == 0 && player.name == 'Player') { if(player.id == 0 && player.name == 'Player') {
// these show up in ut2004 queries, but aren't real // these show up in ut2004 queries, but aren't real
// not even really sure why they're there // not even really sure why they're there
continue; continue;
} }
(player.ping ? state.players : state.bots).push(player); (player.ping ? state.players : state.bots).push(player);
} }
c(); c();
}); });
}, },
function(c) { function(c) {
self.finish(state); self.finish(state);
} }
]); ]);
}, },
readExtraInfo: function(reader,state) { readExtraInfo: function(reader,state) {
if(this.debug) { if(this.debug) {
console.log("UNREAL2 EXTRA INFO:"); 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.uint(4)); console.log(reader.uint(4));
console.log(reader.uint(4)); console.log(reader.uint(4));
console.log(reader.buffer.slice(reader.i)); console.log(reader.buffer.slice(reader.i));
} }
}, },
readUnrealString: function(reader, stripColor) { readUnrealString: function(reader, stripColor) {
var length = reader.uint(1); var length = reader.uint(1);
var out; var out;
if(length < 0x80) { if(length < 0x80) {
//out = reader.string({length:length}); //out = reader.string({length:length});
out = ''; out = '';
if(length > 0) out = reader.string(); if(length > 0) out = reader.string();
} else { } else {
length = (length&0x7f)*2; length = (length&0x7f)*2;
if(this.debug) { if(this.debug) {
console.log("UCS2 STRING"); console.log("UCS2 STRING");
console.log(length,reader.buffer.slice(reader.i,reader.i+length)); console.log(length,reader.buffer.slice(reader.i,reader.i+length));
} }
out = reader.string({encoding:'ucs2',length:length}); out = reader.string({encoding:'ucs2',length:length});
} }
if(out.charCodeAt(out.length-1) == 0) if(out.charCodeAt(out.length-1) == 0)
out = out.substring(0,out.length-1); out = out.substring(0,out.length-1);
if(stripColor) if(stripColor)
out = out.replace(/\x1b...|[\x00-\x1a]/g,''); out = out.replace(/\x1b...|[\x00-\x1a]/g,'');
return out; return out;
}, },
sendPacket: function(type,required,callback) { sendPacket: function(type,required,callback) {
var self = this; var self = this;
var outbuffer = new Buffer([0x79,0,0,0,type]); var outbuffer = new Buffer([0x79,0,0,0,type]);
var packets = []; var packets = [];
this.udpSend(outbuffer,function(buffer) { this.udpSend(outbuffer,function(buffer) {
var reader = self.reader(buffer); var reader = self.reader(buffer);
var header = reader.uint(4); var header = reader.uint(4);
var iType = reader.uint(1); var iType = reader.uint(1);
if(iType != type) return; if(iType != type) return;
packets.push(reader.rest()); packets.push(reader.rest());
},function() { },function() {
if(!packets.length && required) return; if(!packets.length && required) return;
callback(Buffer.concat(packets)); callback(Buffer.concat(packets));
return true; return true;
}); });
} }
}); });

View file

@ -1,7 +1,7 @@
module.exports = require('./unreal2').extend({ module.exports = require('./unreal2').extend({
readExtraInfo: function(reader,state) { readExtraInfo: function(reader,state) {
state.raw.ping = reader.uint(4); state.raw.ping = reader.uint(4);
state.raw.flags = reader.uint(4); state.raw.flags = reader.uint(4);
state.raw.skill = reader.uint(2); state.raw.skill = reader.uint(2);
} }
}); });

View file

@ -1,43 +1,43 @@
module.exports = require('./gamespy3').extend({ module.exports = require('./gamespy3').extend({
finalizeState: function(state) { finalizeState: function(state) {
this._super(state); this._super(state);
this.translate(state.raw,{ this.translate(state.raw,{
'mapname': false, 'mapname': false,
'p1073741825': 'map', 'p1073741825': 'map',
'p1073741826': 'gametype', 'p1073741826': 'gametype',
'p1073741827': 'servername', 'p1073741827': 'servername',
'p1073741828': 'custom_mutators', 'p1073741828': 'custom_mutators',
'gamemode': 'joininprogress', 'gamemode': 'joininprogress',
's32779': 'gamemode', 's32779': 'gamemode',
's0': 'bot_skill', 's0': 'bot_skill',
's6': 'pure_server', 's6': 'pure_server',
's7': 'password', 's7': 'password',
's8': 'vs_bots', 's8': 'vs_bots',
's10': 'force_respawn', 's10': 'force_respawn',
'p268435704': 'frag_limit', 'p268435704': 'frag_limit',
'p268435705': 'time_limit', 'p268435705': 'time_limit',
'p268435703': 'numbots', 'p268435703': 'numbots',
'p268435717': 'stock_mutators', 'p268435717': 'stock_mutators',
'p1073741829': 'stock_mutators', 'p1073741829': 'stock_mutators',
's1': false, 's1': false,
's9': false, 's9': false,
's11': false, 's11': false,
's12': false, 's12': false,
's13': false, 's13': false,
's14': false, 's14': false,
'p268435706': false, 'p268435706': false,
'p268435968': false, 'p268435968': false,
'p268435969': false 'p268435969': false
}); });
function split(a) { function split(a) {
var s = a.split('\x1c'); var s = a.split('\x1c');
s = s.filter(function(e) { return e }); s = s.filter(function(e) { return e });
return s; return s;
} }
if('custom_mutators' in state.raw) state.raw['custom_mutators'] = split(state.raw['custom_mutators']); 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('stock_mutators' in state.raw) state.raw['stock_mutators'] = split(state.raw['stock_mutators']);
if('map' in state.raw) state.map = state.raw.map; if('map' in state.raw) state.map = state.raw.map;
} }
}); });

View file

@ -1,325 +1,325 @@
var async = require('async'), var async = require('async'),
Bzip2 = require('compressjs').Bzip2; Bzip2 = require('compressjs').Bzip2;
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.options.port = 27015; this.options.port = 27015;
// legacy goldsrc info response -- basically not used by ANYTHING now, // legacy goldsrc info response -- basically not used by ANYTHING now,
// as most (all?) goldsrc servers respond with the source info reponse // as most (all?) goldsrc servers respond with the source info reponse
// delete in a few years if nothing ends up using it anymore // delete in a few years if nothing ends up using it anymore
this.goldsrcInfo = false; this.goldsrcInfo = false;
// unfortunately, the split format from goldsrc is still around, but we // unfortunately, the split format from goldsrc is still around, but we
// can detect that during the query // can detect that during the query
this.goldsrcSplits = false; this.goldsrcSplits = false;
// some mods require a challenge, but don't provide them in the new format // some mods require a challenge, but don't provide them in the new format
// at all, use the old dedicated challenge query if needed // at all, use the old dedicated challenge query if needed
this.legacyChallenge = false; this.legacyChallenge = false;
// cs:go provides an annoying additional bot that looks exactly like a player, // cs:go provides an annoying additional bot that looks exactly like a player,
// but is always named "Max Players" // but is always named "Max Players"
this.isCsGo = false; this.isCsGo = false;
// 2006 engines don't pass packet switching size in split packet header // 2006 engines don't pass packet switching size in split packet header
// while all others do, this need is detected automatically // while all others do, this need is detected automatically
this._skipSizeInSplitHeader = false; this._skipSizeInSplitHeader = false;
this._challenge = ''; this._challenge = '';
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
async.series([ async.series([
function(c) { self.queryInfo(state,c); }, function(c) { self.queryInfo(state,c); },
function(c) { self.queryChallenge(state,c); }, function(c) { self.queryChallenge(state,c); },
function(c) { self.queryPlayers(state,c); }, function(c) { self.queryPlayers(state,c); },
function(c) { self.queryRules(state,c); }, function(c) { self.queryRules(state,c); },
function(c) { self.finish(state); } function(c) { self.finish(state); }
]); ]);
}, },
queryInfo: function(state,c) { queryInfo: function(state,c) {
var self = this; var self = this;
self.sendPacket( self.sendPacket(
0x54,false,'Source Engine Query\0', 0x54,false,'Source Engine Query\0',
self.goldsrcInfo ? 0x6D : 0x49, self.goldsrcInfo ? 0x6D : 0x49,
function(b) { function(b) {
var reader = self.reader(b); var reader = self.reader(b);
if(self.goldsrcInfo) state.raw.address = reader.string(); if(self.goldsrcInfo) state.raw.address = reader.string();
else state.raw.protocol = reader.uint(1); else state.raw.protocol = reader.uint(1);
state.name = reader.string(); state.name = reader.string();
state.map = reader.string(); state.map = reader.string();
state.raw.folder = reader.string(); state.raw.folder = reader.string();
state.raw.game = reader.string(); state.raw.game = reader.string();
state.raw.steamappid = reader.uint(2); state.raw.steamappid = reader.uint(2);
state.raw.numplayers = reader.uint(1); state.raw.numplayers = reader.uint(1);
state.maxplayers = reader.uint(1); state.maxplayers = reader.uint(1);
if(self.goldsrcInfo) state.raw.protocol = reader.uint(1); if(self.goldsrcInfo) state.raw.protocol = reader.uint(1);
else state.raw.numbots = reader.uint(1); else state.raw.numbots = reader.uint(1);
state.raw.listentype = reader.uint(1); state.raw.listentype = reader.uint(1);
state.raw.environment = reader.uint(1); state.raw.environment = reader.uint(1);
if(!self.goldsrcInfo) { if(!self.goldsrcInfo) {
state.raw.listentype = String.fromCharCode(state.raw.listentype); state.raw.listentype = String.fromCharCode(state.raw.listentype);
state.raw.environment = String.fromCharCode(state.raw.environment); state.raw.environment = String.fromCharCode(state.raw.environment);
} }
state.password = !!reader.uint(1); state.password = !!reader.uint(1);
if(self.goldsrcInfo) { if(self.goldsrcInfo) {
state.raw.ismod = reader.uint(1); state.raw.ismod = reader.uint(1);
if(state.raw.ismod) { if(state.raw.ismod) {
state.raw.modlink = reader.string(); state.raw.modlink = reader.string();
state.raw.moddownload = reader.string(); state.raw.moddownload = reader.string();
reader.skip(1); reader.skip(1);
state.raw.modversion = reader.uint(4); state.raw.modversion = reader.uint(4);
state.raw.modsize = reader.uint(4); state.raw.modsize = reader.uint(4);
state.raw.modtype = reader.uint(1); state.raw.modtype = reader.uint(1);
state.raw.moddll = reader.uint(1); state.raw.moddll = reader.uint(1);
} }
} }
state.raw.secure = reader.uint(1); state.raw.secure = reader.uint(1);
if(self.goldsrcInfo) { if(self.goldsrcInfo) {
state.raw.numbots = reader.uint(1); state.raw.numbots = reader.uint(1);
} else { } else {
if(state.raw.folder == 'ship') { if(state.raw.folder == 'ship') {
state.raw.shipmode = reader.uint(1); state.raw.shipmode = reader.uint(1);
state.raw.shipwitnesses = reader.uint(1); state.raw.shipwitnesses = reader.uint(1);
state.raw.shipduration = reader.uint(1); state.raw.shipduration = reader.uint(1);
} }
state.raw.version = reader.string(); state.raw.version = reader.string();
var extraFlag = reader.uint(1); var extraFlag = reader.uint(1);
if(extraFlag & 0x80) state.raw.port = reader.uint(2); if(extraFlag & 0x80) state.raw.port = reader.uint(2);
if(extraFlag & 0x10) state.raw.steamid = reader.uint(8); if(extraFlag & 0x10) state.raw.steamid = reader.uint(8);
if(extraFlag & 0x40) { if(extraFlag & 0x40) {
state.raw.sourcetvport = reader.uint(2); state.raw.sourcetvport = reader.uint(2);
state.raw.sourcetvname = reader.string(); state.raw.sourcetvname = reader.string();
} }
if(extraFlag & 0x20) state.raw.tags = reader.string(); if(extraFlag & 0x20) state.raw.tags = reader.string();
if(extraFlag & 0x01) state.raw.gameid = reader.uint(8); if(extraFlag & 0x01) state.raw.gameid = reader.uint(8);
} }
// from https://developer.valvesoftware.com/wiki/Server_queries // from https://developer.valvesoftware.com/wiki/Server_queries
if( if(
state.raw.protocol == 7 && ( state.raw.protocol == 7 && (
state.raw.steamappid == 215 state.raw.steamappid == 215
|| state.raw.steamappid == 17550 || state.raw.steamappid == 17550
|| state.raw.steamappid == 17700 || state.raw.steamappid == 17700
|| state.raw.steamappid == 240 || state.raw.steamappid == 240
) )
) { ) {
self._skipSizeInSplitHeader = true; self._skipSizeInSplitHeader = true;
} }
if(self.debug) { if(self.debug) {
console.log("STEAM APPID: "+state.raw.steamappid); console.log("STEAM APPID: "+state.raw.steamappid);
console.log("PROTOCOL: "+state.raw.protocol); console.log("PROTOCOL: "+state.raw.protocol);
} }
if(state.raw.protocol == 48) { if(state.raw.protocol == 48) {
if(self.debug) console.log("GOLDSRC DETECTED - USING MODIFIED SPLIT FORMAT"); if(self.debug) console.log("GOLDSRC DETECTED - USING MODIFIED SPLIT FORMAT");
self.goldsrcSplits = true; self.goldsrcSplits = true;
} }
c(); c();
} }
); );
}, },
queryChallenge: function(state,c) { queryChallenge: function(state,c) {
var self = this; var self = this;
if(this.legacyChallenge) { if(this.legacyChallenge) {
self.sendPacket(0x57,false,false,0x41,function(b) { self.sendPacket(0x57,false,false,0x41,function(b) {
// sendPacket will catch the response packet and // sendPacket will catch the response packet and
// save the challenge for us // save the challenge for us
c(); c();
}); });
} else { } else {
c(); c();
} }
}, },
queryPlayers: function(state,c) { queryPlayers: function(state,c) {
var self = this; var self = this;
self.sendPacket(0x55,true,false,0x44,function(b) { self.sendPacket(0x55,true,false,0x44,function(b) {
var reader = self.reader(b); var reader = self.reader(b);
var num = reader.uint(1); var num = reader.uint(1);
var csgoHiddenPlayers = false; var csgoHiddenPlayers = false;
for(var i = 0; i < num; i++) { for(var i = 0; i < num; i++) {
reader.skip(1); reader.skip(1);
var name = reader.string(); var name = reader.string();
var score = reader.int(4); var score = reader.int(4);
var time = reader.float(); var time = reader.float();
if(self.debug) console.log("Found player: "+name+" "+score+" "+time); if(self.debug) console.log("Found player: "+name+" "+score+" "+time);
// connecting players don't count as players. // connecting players don't count as players.
if(!name) continue; if(!name) continue;
(time == -1 ? state.bots : state.players).push({ (time == -1 ? state.bots : state.players).push({
name:name, score:score, time:time name:name, score:score, time:time
}); });
} }
if(self.isCsGo && state.players.length == 1 && state.players[0].name == 'Max Players') { if(self.isCsGo && state.players.length == 1 && state.players[0].name == 'Max Players') {
if(self.debug) console.log("CSGO server using limited player details"); if(self.debug) console.log("CSGO server using limited player details");
state.players = []; state.players = [];
for(var i = 0; i < state.raw.numplayers; i++) { state.players.push({}); } for(var i = 0; i < state.raw.numplayers; i++) { state.players.push({}); }
} }
// if we didn't find the bots, iterate // if we didn't find the bots, iterate
// through and guess which ones they are // through and guess which ones they are
if(!state.bots.length && state.raw.numbots) { if(!state.bots.length && state.raw.numbots) {
var maxTime = 0; var maxTime = 0;
state.players.forEach(function(player) { state.players.forEach(function(player) {
maxTime = Math.max(player.time,maxTime); maxTime = Math.max(player.time,maxTime);
}); });
for(var i = 0; i < state.players.length; i++) { for(var i = 0; i < state.players.length; i++) {
var player = state.players[i]; var player = state.players[i];
if(state.bots.length >= state.raw.numbots) continue; if(state.bots.length >= state.raw.numbots) continue;
if(player.time != maxTime) continue; if(player.time != maxTime) continue;
state.bots.push(player); state.bots.push(player);
state.players.splice(i, 1); state.players.splice(i, 1);
i--; i--;
} }
} }
c(); c();
}); });
}, },
queryRules: function(state,c) { queryRules: function(state,c) {
var self = this; var self = this;
self.sendPacket(0x56,true,false,0x45,function(b) { self.sendPacket(0x56,true,false,0x45,function(b) {
var reader = self.reader(b); var reader = self.reader(b);
var num = reader.uint(2); var num = reader.uint(2);
state.raw.rules = {}; state.raw.rules = {};
for(var i = 0; i < num; i++) { for(var i = 0; i < num; i++) {
var key = reader.string(); var key = reader.string();
var value = reader.string(); var value = reader.string();
state.raw.rules[key] = value; state.raw.rules[key] = value;
} }
c(); c();
}, function() { }, function() {
// no rules were returned after timeout -- // no rules were returned after timeout --
// the server probably has them disabled // the server probably has them disabled
// ignore the timeout // ignore the timeout
c(); c();
return true; return true;
}); });
}, },
sendPacket: function(type,sendChallenge,payload,expect,callback,ontimeout) { sendPacket: function(type,sendChallenge,payload,expect,callback,ontimeout) {
var self = this; var self = this;
var packetStorage = {}; var packetStorage = {};
send(); send();
function send(c) { function send(c) {
if(typeof payload == 'string') payload = new Buffer(payload,'binary'); if(typeof payload == 'string') payload = new Buffer(payload,'binary');
var challengeLength = sendChallenge ? 4 : 0; var challengeLength = sendChallenge ? 4 : 0;
var payloadLength = payload ? payload.length : 0; var payloadLength = payload ? payload.length : 0;
var b = new Buffer(5 + challengeLength + payloadLength); var b = new Buffer(5 + challengeLength + payloadLength);
b.writeInt32LE(-1, 0); b.writeInt32LE(-1, 0);
b.writeUInt8(type, 4); b.writeUInt8(type, 4);
if(sendChallenge) { if(sendChallenge) {
var challenge = self._challenge; var challenge = self._challenge;
if(!challenge) challenge = 0xffffffff; if(!challenge) challenge = 0xffffffff;
if(self.byteorder == 'le') b.writeUInt32LE(challenge, 5); if(self.byteorder == 'le') b.writeUInt32LE(challenge, 5);
else b.writeUInt32BE(challenge, 5); else b.writeUInt32BE(challenge, 5);
} }
if(payloadLength) payload.copy(b, 5+challengeLength); if(payloadLength) payload.copy(b, 5+challengeLength);
self.udpSend(b,receivedOne,ontimeout); self.udpSend(b,receivedOne,ontimeout);
} }
function receivedOne(buffer) { function receivedOne(buffer) {
var reader = self.reader(buffer); var reader = self.reader(buffer);
var header = reader.int(4); var header = reader.int(4);
if(header == -1) { if(header == -1) {
// full package // full package
if(self.debug) console.log("Received full packet"); if(self.debug) console.log("Received full packet");
return receivedFull(reader); return receivedFull(reader);
} }
if(header == -2) { if(header == -2) {
// partial package // partial package
var uid = reader.uint(4); var uid = reader.uint(4);
if(!(uid in packetStorage)) packetStorage[uid] = {}; if(!(uid in packetStorage)) packetStorage[uid] = {};
var packets = packetStorage[uid]; var packets = packetStorage[uid];
var bzip = false; var bzip = false;
if(!self.goldsrcSplits && uid & 0x80000000) bzip = true; if(!self.goldsrcSplits && uid & 0x80000000) bzip = true;
var packetNum,payload,numPackets; var packetNum,payload,numPackets;
if(self.goldsrcSplits) { if(self.goldsrcSplits) {
packetNum = reader.uint(1); packetNum = reader.uint(1);
numPackets = packetNum & 0x0f; numPackets = packetNum & 0x0f;
packetNum = (packetNum & 0xf0) >> 4; packetNum = (packetNum & 0xf0) >> 4;
payload = reader.rest(); payload = reader.rest();
} else { } else {
numPackets = reader.uint(1); numPackets = reader.uint(1);
packetNum = reader.uint(1); packetNum = reader.uint(1);
if(!self._skipSizeInSplitHeader) reader.skip(2); if(!self._skipSizeInSplitHeader) reader.skip(2);
if(packetNum == 0 && bzip) reader.skip(8); if(packetNum == 0 && bzip) reader.skip(8);
payload = reader.rest(); payload = reader.rest();
} }
packets[packetNum] = payload; packets[packetNum] = payload;
if(self.debug) { if(self.debug) {
console.log("Received partial packet uid:"+uid+" num:"+packetNum); console.log("Received partial packet uid:"+uid+" num:"+packetNum);
console.log("Received "+Object.keys(packets).length+'/'+numPackets+" packets for this UID"); console.log("Received "+Object.keys(packets).length+'/'+numPackets+" packets for this UID");
} }
if(Object.keys(packets).length != numPackets) return; if(Object.keys(packets).length != numPackets) return;
// assemble the parts // assemble the parts
var list = []; var list = [];
for(var i = 0; i < numPackets; i++) { for(var i = 0; i < numPackets; i++) {
if(!(i in packets)) { if(!(i in packets)) {
self.fatal('Missing packet #'+i); self.fatal('Missing packet #'+i);
return true; return true;
} }
list.push(packets[i]); list.push(packets[i]);
} }
var assembled = Buffer.concat(list); var assembled = Buffer.concat(list);
if(bzip) { if(bzip) {
if(self.debug) console.log("BZIP DETECTED - Extracing packet..."); if(self.debug) console.log("BZIP DETECTED - Extracing packet...");
try { try {
assembled = new Buffer(Bzip2.decompressFile(assembled)); assembled = new Buffer(Bzip2.decompressFile(assembled));
} catch(e) { } catch(e) {
self.fatal('Invalid bzip packet'); self.fatal('Invalid bzip packet');
return true; return true;
} }
} }
var assembledReader = self.reader(assembled); var assembledReader = self.reader(assembled);
assembledReader.skip(4); // header assembledReader.skip(4); // header
return receivedFull(assembledReader); return receivedFull(assembledReader);
} }
} }
function receivedFull(reader) { function receivedFull(reader) {
var type = reader.uint(1); var type = reader.uint(1);
if(type == 0x41) { if(type == 0x41) {
if(self.debug) console.log('Received challenge key'); if(self.debug) console.log('Received challenge key');
if(self._challenge) return self.fatal('Received more than one challenge key'); if(self._challenge) return self.fatal('Received more than one challenge key');
self._challenge = reader.uint(4); self._challenge = reader.uint(4);
if(sendChallenge) { if(sendChallenge) {
if(self.debug) console.log('Restarting query'); if(self.debug) console.log('Restarting query');
send(); send();
return true; return true;
} }
} }
if(self.debug) console.log("Received "+type.toString(16)+" expected "+expect.toString(16)); if(self.debug) console.log("Received "+type.toString(16)+" expected "+expect.toString(16));
if(type != expect) return; if(type != expect) return;
callback(reader.rest()); callback(reader.rest());
return true; return true;
} }
} }
}); });

View file

@ -1,240 +1,240 @@
var async = require('async'); var async = require('async');
module.exports = require('./core').extend({ module.exports = require('./core').extend({
init: function() { init: function() {
this._super(); this._super();
this.byteorder = 'be'; this.byteorder = 'be';
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
this.sendCommand(2,'',function(data) { this.sendCommand(2,'',function(data) {
state.raw = splitFields(data.toString()); state.raw = splitFields(data.toString());
state.raw.CLIENTS.forEach(function(client) { state.raw.CLIENTS.forEach(function(client) {
client.name = client.NAME; client.name = client.NAME;
delete client.NAME; delete client.NAME;
client.ping = parseInt(client.PING); client.ping = parseInt(client.PING);
delete client.PING; delete client.PING;
state.players.push(client); state.players.push(client);
}); });
delete state.raw.CLIENTS; delete state.raw.CLIENTS;
if('NAME' in state.raw) state.name = state.raw.NAME; if('NAME' in state.raw) state.name = state.raw.NAME;
if('MAXCLIENTS' in state.raw) state.maxplayers = state.raw.MAXCLIENTS; if('MAXCLIENTS' in state.raw) state.maxplayers = state.raw.MAXCLIENTS;
if(self.trueTest(state.raw.AUTH)) state.password = true; if(self.trueTest(state.raw.AUTH)) state.password = true;
self.finish(state); self.finish(state);
}); });
}, },
sendCommand: function(cmd,password,c) { sendCommand: function(cmd,password,c) {
var self = this; var self = this;
var body = new Buffer(16); var body = new Buffer(16);
body.write(password,0,15,'utf8'); body.write(password,0,15,'utf8');
var encrypted = encrypt(cmd,body); var encrypted = encrypt(cmd,body);
var packets = {}; var packets = {};
this.udpSend(encrypted, function(buffer) { this.udpSend(encrypted, function(buffer) {
if(buffer.length < 20) return; if(buffer.length < 20) return;
var data = decrypt(buffer); var data = decrypt(buffer);
if(data.zero != 0) return; if(data.zero != 0) return;
packets[data.packetNum] = data.body; packets[data.packetNum] = data.body;
if(Object.keys(packets).length != data.packetTotal) return; if(Object.keys(packets).length != data.packetTotal) return;
var out = []; var out = [];
for(var i = 0; i < data.packetTotal; i++) { for(var i = 0; i < data.packetTotal; i++) {
if(!(i in packets)) return self.fatal('Missing packet #'+i); if(!(i in packets)) return self.fatal('Missing packet #'+i);
out.push(packets[i]); out.push(packets[i]);
} }
c(Buffer.concat(out)); c(Buffer.concat(out));
return true; return true;
}); });
} }
}); });
function splitFields(str,subMode) { function splitFields(str,subMode) {
var splitter,delim; var splitter,delim;
if(subMode) { if(subMode) {
splitter = '='; splitter = '=';
delim = ','; delim = ',';
} else { } else {
splitter = ': '; splitter = ': ';
delim = '\n'; delim = '\n';
} }
var split = str.split(delim); var split = str.split(delim);
var out = {}; var out = {};
if(!subMode) { if(!subMode) {
out.CHANNELS = []; out.CHANNELS = [];
out.CLIENTS = []; out.CLIENTS = [];
} }
split.forEach(function(one) { split.forEach(function(one) {
var equal = one.indexOf(splitter); var equal = one.indexOf(splitter);
var key = equal == -1 ? one : one.substr(0,equal); var key = equal == -1 ? one : one.substr(0,equal);
if(!key || key == '\0') return; if(!key || key == '\0') return;
var value = equal == -1 ? '' : one.substr(equal+splitter.length); var value = equal == -1 ? '' : one.substr(equal+splitter.length);
if(!subMode && key == 'CHANNEL') out.CHANNELS.push(splitFields(value,true)); if(!subMode && key == 'CHANNEL') out.CHANNELS.push(splitFields(value,true));
else if(!subMode && key == 'CLIENT') out.CLIENTS.push(splitFields(value,true)); else if(!subMode && key == 'CLIENT') out.CLIENTS.push(splitFields(value,true));
else out[key] = value; else out[key] = value;
}); });
return out; return out;
} }
function randInt(min,max) { function randInt(min,max) {
return Math.floor(Math.random()*(max-min+1)+min) return Math.floor(Math.random()*(max-min+1)+min)
} }
function crc(body) { function crc(body) {
var crc = 0; var crc = 0;
for(var i = 0; i < body.length; i++) { for(var i = 0; i < body.length; i++) {
crc = crc_table[crc>>8] ^ body.readUInt8(i) ^ (crc<<8); crc = crc_table[crc>>8] ^ body.readUInt8(i) ^ (crc<<8);
crc &= 0xffff; crc &= 0xffff;
} }
return crc; return crc;
} }
function encrypt(cmd,body) { function encrypt(cmd,body) {
var headerKeyStart = randInt(0,0xff); var headerKeyStart = randInt(0,0xff);
var headerKeyAdd = randInt(1,0xff); var headerKeyAdd = randInt(1,0xff);
var bodyKeyStart = randInt(0,0xff); var bodyKeyStart = randInt(0,0xff);
var bodyKeyAdd = randInt(1,0xff); var bodyKeyAdd = randInt(1,0xff);
var header = new Buffer(20); var header = new Buffer(20);
header.writeUInt8(headerKeyStart,0); header.writeUInt8(headerKeyStart,0);
header.writeUInt8(headerKeyAdd,1); header.writeUInt8(headerKeyAdd,1);
header.writeUInt16BE(cmd,4); header.writeUInt16BE(cmd,4);
header.writeUInt16BE(body.length,8); header.writeUInt16BE(body.length,8);
header.writeUInt16BE(body.length,10); header.writeUInt16BE(body.length,10);
header.writeUInt16BE(1,12); header.writeUInt16BE(1,12);
header.writeUInt16BE(0,14); header.writeUInt16BE(0,14);
header.writeUInt8(bodyKeyStart,16); header.writeUInt8(bodyKeyStart,16);
header.writeUInt8(bodyKeyAdd,17); header.writeUInt8(bodyKeyAdd,17);
header.writeUInt16BE(crc(body),18); header.writeUInt16BE(crc(body),18);
var offset = headerKeyStart; var offset = headerKeyStart;
for(var i = 2; i < header.length; i++) { for(var i = 2; i < header.length; i++) {
var val = header.readUInt8(i); var val = header.readUInt8(i);
val += code_head.charCodeAt(offset) + ((i-2) % 5); val += code_head.charCodeAt(offset) + ((i-2) % 5);
val = val & 0xff; val = val & 0xff;
header.writeUInt8(val,i); header.writeUInt8(val,i);
offset = (offset+headerKeyAdd) & 0xff; offset = (offset+headerKeyAdd) & 0xff;
} }
offset = bodyKeyStart; offset = bodyKeyStart;
for(var i = 0; i < body.length; i++) { for(var i = 0; i < body.length; i++) {
var val = body.readUInt8(i); var val = body.readUInt8(i);
val += code_body.charCodeAt(offset) + (i % 72); val += code_body.charCodeAt(offset) + (i % 72);
val = val & 0xff; val = val & 0xff;
body.writeUInt8(val,i); body.writeUInt8(val,i);
offset = (offset+bodyKeyAdd) & 0xff; offset = (offset+bodyKeyAdd) & 0xff;
} }
return Buffer.concat([header,body]); return Buffer.concat([header,body]);
} }
function decrypt(data) { function decrypt(data) {
var header = data.slice(0,20); var header = data.slice(0,20);
var body = data.slice(20); var body = data.slice(20);
var headerKeyStart = header.readUInt8(0); var headerKeyStart = header.readUInt8(0);
var headerKeyAdd = header.readUInt8(1); var headerKeyAdd = header.readUInt8(1);
var offset = headerKeyStart; var offset = headerKeyStart;
for(var i = 2; i < header.length; i++) { for(var i = 2; i < header.length; i++) {
var val = header.readUInt8(i); var val = header.readUInt8(i);
val -= code_head.charCodeAt(offset) + ((i-2) % 5); val -= code_head.charCodeAt(offset) + ((i-2) % 5);
val = val & 0xff; val = val & 0xff;
header.writeUInt8(val,i); header.writeUInt8(val,i);
offset = (offset+headerKeyAdd) & 0xff; offset = (offset+headerKeyAdd) & 0xff;
} }
var bodyKeyStart = header.readUInt8(16); var bodyKeyStart = header.readUInt8(16);
var bodyKeyAdd = header.readUInt8(17); var bodyKeyAdd = header.readUInt8(17);
offset = bodyKeyStart; offset = bodyKeyStart;
for(var i = 0; i < body.length; i++) { for(var i = 0; i < body.length; i++) {
var val = body.readUInt8(i); var val = body.readUInt8(i);
val -= code_body.charCodeAt(offset) + (i % 72); val -= code_body.charCodeAt(offset) + (i % 72);
val = val & 0xff; val = val & 0xff;
body.writeUInt8(val,i); body.writeUInt8(val,i);
offset = (offset+bodyKeyAdd) & 0xff; offset = (offset+bodyKeyAdd) & 0xff;
} }
// header format: // header format:
// key, zero, cmd, echo, totallength, thislength // key, zero, cmd, echo, totallength, thislength
// totalpacket, packetnum, body key, crc // totalpacket, packetnum, body key, crc
return { return {
zero: header.readUInt16BE(2), zero: header.readUInt16BE(2),
cmd: header.readUInt16BE(4), cmd: header.readUInt16BE(4),
packetTotal: header.readUInt16BE(12), packetTotal: header.readUInt16BE(12),
packetNum: header.readUInt16BE(14), packetNum: header.readUInt16BE(14),
body: body body: body
}; };
} }
var code_head = var code_head =
'\x80\xe5\x0e\x38\xba\x63\x4c\x99\x88\x63\x4c\xd6\x54\xb8\x65\x7e'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'; '\xff\x95\x6d\xc7\x04\xa2\x3b\xc4\x1b\x72\xc7\x6c\x82\x60\xd1\x0d';
var code_body = var code_body =
'\x82\x8b\x7f\x68\x90\xe0\x44\x09\x19\x3b\x8e\x5f\xc2\x82\x38\x23'+ '\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'+ '\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'+ '\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\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'+ '\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'; '\xdb\x77\xdd\xea\x38\x59\x89\x32\xbc\x00\x3a\x6d\x61\x4e\xdb\x29';
var crc_table = [ var crc_table = [
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
]; ];

View file

@ -1,12 +1,12 @@
module.exports = require('./quake3').extend({ module.exports = require('./quake3').extend({
finalizeState: function(state) { finalizeState: function(state) {
this._super(state); this._super(state);
if(state.players) { if(state.players) {
for(var i = 0; i < state.players.length; i++) { for(var i = 0; i < state.players.length; i++) {
var player = state.players[i]; var player = state.players[i];
player.team = player.address; player.team = player.address;
delete player.address; delete player.address;
} }
} }
} }
}); });

View file

@ -1,5 +1,5 @@
The files in this folder are INDIVIDUALLY LICENSED. The files in this folder are INDIVIDUALLY LICENSED.
The applicable license is located in the header of each individual file. The applicable license is located in the header of each individual file.
These files are not covered by the node-GameDig project license. These files are not covered by the node-GameDig project license.
The source of node-GameDig does not use or execute the content of The source of node-GameDig does not use or execute the content of
any file of this folder, and maintains a separate, unrelated license. any file of this folder, and maintains a separate, unrelated license.

View file

@ -1,2 +1,2 @@
gamedig note: gamedig note:
connect over TCP, and the data just starts coming, no packet needs sent? connect over TCP, and the data just starts coming, no packet needs sent?

View file

@ -1,276 +1,276 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski
*/ */
{ {
/* BFRIS */ /* BFRIS */
BFRIS_SERVER, /* id */ BFRIS_SERVER, /* id */
"BFS", /* type_prefix */ "BFS", /* type_prefix */
"bfs", /* type_string */ "bfs", /* type_string */
"-bfs", /* type_option */ "-bfs", /* type_option */
"BFRIS", /* game_name */ "BFRIS", /* game_name */
0, /* master */ 0, /* master */
BFRIS_DEFAULT_PORT, /* default_port */ BFRIS_DEFAULT_PORT, /* default_port */
0, /* port_offset */ 0, /* port_offset */
TF_TCP_CONNECT, /* flags */ TF_TCP_CONNECT, /* flags */
"Rules", /* game_rule */ "Rules", /* game_rule */
"BFRIS", /* template_var */ "BFRIS", /* template_var */
NULL, /* status_packet */ NULL, /* status_packet */
0, /* status_len */ 0, /* status_len */
NULL, /* player_packet */ NULL, /* player_packet */
0, /* player_len */ 0, /* player_len */
NULL, /* rule_packet */ NULL, /* rule_packet */
0, /* rule_len */ 0, /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
display_bfris_player_info, /* display_player_func */ display_bfris_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
raw_display_bfris_player_info,/* display_raw_player_func */ raw_display_bfris_player_info,/* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_bfris_player_info, /* display_xml_player_func */ xml_display_bfris_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_bfris_request_packet, /* status_query_func */ send_bfris_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_bfris_packet, /* packet_func */ deal_with_bfris_packet, /* packet_func */
}, },
/* postions of map name, player name (in player substring), zero-based */ /* postions of map name, player name (in player substring), zero-based */
#define BFRIS_MAP_POS 18 #define BFRIS_MAP_POS 18
#define BFRIS_PNAME_POS 11 #define BFRIS_PNAME_POS 11
query_status_t deal_with_bfris_packet(struct qserver *server, char *rawpkt, int pktlen) query_status_t deal_with_bfris_packet(struct qserver *server, char *rawpkt, int pktlen)
{ {
int i, player_data_pos, nplayers; int i, player_data_pos, nplayers;
SavedData *sdata; SavedData *sdata;
unsigned char *saved_data; unsigned char *saved_data;
int saved_data_size; int saved_data_size;
debug( 2, "deal_with_bfris_packet %p, %d", server, pktlen ); debug( 2, "deal_with_bfris_packet %p, %d", server, pktlen );
server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
/* add to the data previously saved */ /* add to the data previously saved */
sdata = &server->saved_data; sdata = &server->saved_data;
if (!sdata->data) if (!sdata->data)
{ {
sdata->data = (char*)malloc(pktlen); sdata->data = (char*)malloc(pktlen);
} }
else else
{ {
sdata->data = (char*)realloc(sdata->data, sdata->datalen + pktlen); sdata->data = (char*)realloc(sdata->data, sdata->datalen + pktlen);
} }
memcpy(sdata->data + sdata->datalen, rawpkt, pktlen); memcpy(sdata->data + sdata->datalen, rawpkt, pktlen);
sdata->datalen += pktlen; sdata->datalen += pktlen;
saved_data = (unsigned char*)sdata->data; saved_data = (unsigned char*)sdata->data;
saved_data_size = sdata->datalen; saved_data_size = sdata->datalen;
/* after we get the server portion of the data, server->game != NULL */ /* after we get the server portion of the data, server->game != NULL */
if (!server->game) if (!server->game)
{ {
/* server data goes up to map name */ /* server data goes up to map name */
if (sdata->datalen <= BFRIS_MAP_POS) if (sdata->datalen <= BFRIS_MAP_POS)
{ {
return INPROGRESS; return INPROGRESS;
} }
/* see if map name is complete */ /* see if map name is complete */
player_data_pos = 0; player_data_pos = 0;
for (i = BFRIS_MAP_POS; i < saved_data_size; i++) for (i = BFRIS_MAP_POS; i < saved_data_size; i++)
{ {
if (saved_data[i] == '\0') if (saved_data[i] == '\0')
{ {
player_data_pos = i + 1; player_data_pos = i + 1;
/* data must extend beyond map name */ /* data must extend beyond map name */
if (saved_data_size <= player_data_pos) if (saved_data_size <= player_data_pos)
{ {
return INPROGRESS; return INPROGRESS;
} }
break; break;
} }
} }
/* did we find beginning of player data? */ /* did we find beginning of player data? */
if (!player_data_pos) if (!player_data_pos)
{ {
return INPROGRESS; return INPROGRESS;
} }
/* now we can go ahead and fill in server data */ /* now we can go ahead and fill in server data */
server->map_name = strdup((char*)saved_data + BFRIS_MAP_POS); server->map_name = strdup((char*)saved_data + BFRIS_MAP_POS);
server->max_players = saved_data[12]; server->max_players = saved_data[12];
server->protocol_version = saved_data[11]; server->protocol_version = saved_data[11];
/* save game type */ /* save game type */
switch (saved_data[13] &15) switch (saved_data[13] &15)
{ {
case 0: case 0:
server->game = "FFA"; server->game = "FFA";
break; break;
case 5: case 5:
server->game = "Rover"; server->game = "Rover";
break; break;
case 6: case 6:
server->game = "Occupation"; server->game = "Occupation";
break; break;
case 7: case 7:
server->game = "SPAAL"; server->game = "SPAAL";
break; break;
case 8: case 8:
server->game = "CTF"; server->game = "CTF";
break; break;
default: default:
server->game = "unknown"; server->game = "unknown";
break; break;
} }
server->flags |= FLAG_DO_NOT_FREE_GAME; server->flags |= FLAG_DO_NOT_FREE_GAME;
add_rule(server, server->type->game_rule, server->game, NO_FLAGS); add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
if (get_server_rules) if (get_server_rules)
{ {
char buf[24]; char buf[24];
/* server revision */ /* server revision */
sprintf(buf, "%d", (unsigned int)saved_data[11]); sprintf(buf, "%d", (unsigned int)saved_data[11]);
add_rule(server, "Revision", buf, NO_FLAGS); add_rule(server, "Revision", buf, NO_FLAGS);
/* latency */ /* latency */
sprintf(buf, "%d", (unsigned int)saved_data[10]); sprintf(buf, "%d", (unsigned int)saved_data[10]);
add_rule(server, "Latency", buf, NO_FLAGS); add_rule(server, "Latency", buf, NO_FLAGS);
/* player allocation */ /* player allocation */
add_rule(server, "Allocation", saved_data[13] &16 ? "Automatic" : "Manual", NO_FLAGS); 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 /* 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. the player information, and that the server data is taken care of.
*/ */
/* start of player data */ /* start of player data */
player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1; player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1;
/* ensure all player data have arrived */ /* ensure all player data have arrived */
nplayers = 0; nplayers = 0;
while (saved_data[player_data_pos] != '\0') while (saved_data[player_data_pos] != '\0')
{ {
player_data_pos += BFRIS_PNAME_POS; player_data_pos += BFRIS_PNAME_POS;
/* does player data extend to player name? */ /* does player data extend to player name? */
if (saved_data_size <= player_data_pos + 1) if (saved_data_size <= player_data_pos + 1)
{ {
return INPROGRESS; return INPROGRESS;
} }
/* does player data extend to end of player name? */ /* does player data extend to end of player name? */
for (i = 0; player_data_pos + i < saved_data_size; i++) for (i = 0; player_data_pos + i < saved_data_size; i++)
{ {
if (saved_data_size == player_data_pos + i + 1) if (saved_data_size == player_data_pos + i + 1)
{ {
return INPROGRESS; return INPROGRESS;
} }
if (saved_data[player_data_pos + i] == '\0') if (saved_data[player_data_pos + i] == '\0')
{ {
player_data_pos += i + 1; player_data_pos += i + 1;
nplayers++; nplayers++;
break; break;
} }
} }
} }
/* all player data are complete */ /* all player data are complete */
server->num_players = nplayers; server->num_players = nplayers;
if (get_player_info) if (get_player_info)
{ {
/* start of player data */ /* start of player data */
player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1; player_data_pos = BFRIS_MAP_POS + strlen((char*)saved_data + BFRIS_MAP_POS) + 1;
for (i = 0; i < nplayers; i++) for (i = 0; i < nplayers; i++)
{ {
struct player *player; struct player *player;
player = add_player(server, saved_data[player_data_pos]); player = add_player(server, saved_data[player_data_pos]);
player->ship = saved_data[player_data_pos + 1]; player->ship = saved_data[player_data_pos + 1];
player->ping = saved_data[player_data_pos + 2]; player->ping = saved_data[player_data_pos + 2];
player->frags = saved_data[player_data_pos + 3]; player->frags = saved_data[player_data_pos + 3];
player->team = saved_data[player_data_pos + 4]; player->team = saved_data[player_data_pos + 4];
switch (player->team) switch (player->team)
{ {
case 0: case 0:
player->team_name = "silver"; player->team_name = "silver";
break; break;
case 1: case 1:
player->team_name = "red"; player->team_name = "red";
break; break;
case 2: case 2:
player->team_name = "blue"; player->team_name = "blue";
break; break;
case 3: case 3:
player->team_name = "green"; player->team_name = "green";
break; break;
case 4: case 4:
player->team_name = "purple"; player->team_name = "purple";
break; break;
case 5: case 5:
player->team_name = "yellow"; player->team_name = "yellow";
break; break;
case 6: case 6:
player->team_name = "cyan"; player->team_name = "cyan";
break; break;
default: default:
player->team_name = "unknown"; player->team_name = "unknown";
break; break;
} }
player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
player->room = saved_data[player_data_pos + 5]; player->room = saved_data[player_data_pos + 5];
/* score is little-endian integer */ /* score is little-endian integer */
player->score = saved_data[player_data_pos + 7] + player->score = saved_data[player_data_pos + 7] +
(saved_data[player_data_pos + 8] << 8) + (saved_data[player_data_pos + 8] << 8) +
(saved_data[player_data_pos + 9] << 16) + (saved_data[player_data_pos + 9] << 16) +
(saved_data[player_data_pos + 10] << 24); (saved_data[player_data_pos + 10] << 24);
/* for archs with > 4-byte int */ /* for archs with > 4-byte int */
if (player->score &0x80000000) if (player->score &0x80000000)
{ {
player->score = - (~(player->score)) - 1; player->score = - (~(player->score)) - 1;
} }
player_data_pos += BFRIS_PNAME_POS; player_data_pos += BFRIS_PNAME_POS;
player->name = strdup((char*)saved_data + player_data_pos); player->name = strdup((char*)saved_data + player_data_pos);
player_data_pos += strlen(player->name) + 1; player_data_pos += strlen(player->name) + 1;
} }
} }
server->server_name = BFRIS_SERVER_NAME; server->server_name = BFRIS_SERVER_NAME;
return DONE_FORCE; return DONE_FORCE;
} }

View file

@ -1,2 +1,2 @@
I was under the impression all the crysis games used gamespy? I was under the impression all the crysis games used gamespy?
If anyone notices a problem, this is reference for some old cryengine protocol. If anyone notices a problem, this is reference for some old cryengine protocol.

View file

@ -1,100 +1,100 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: cry.php,v 1.2 2008/04/22 18:52:27 tombuskens Exp $ * $Id: cry.php,v 1.2 2008/04/22 18:52:27 tombuskens Exp $
*/ */
[cry] [cry]
rules = "\x7f\xff\xff\xffrules" rules = "\x7f\xff\xff\xffrules"
status = "\x7f\xff\xff\xffstatus" status = "\x7f\xff\xff\xffstatus"
players = "\x7f\xff\xff\xffplayers" players = "\x7f\xff\xff\xffplayers"
[farcry2] [farcry2]
status = "\x06\x01\x00\x00\x2b\xbf\x53\x51\xdc\x80\x19\xb8\xb0\x57\xa3\x75" status = "\x06\x01\x00\x00\x2b\xbf\x53\x51\xdc\x80\x19\xb8\xb0\x57\xa3\x75"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* CryEngine protocol * CryEngine protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.2 $ * @version $Revision: 1.2 $
*/ */
class GameQ_Protocol_cry extends GameQ_Protocol class GameQ_Protocol_cry extends GameQ_Protocol
{ {
public function rules() public function rules()
{ {
// Header // Header
$this->header(); $this->header();
// Rules // Rules
while ($this->p->getLength()) { while ($this->p->getLength()) {
$this->r->add($this->p->readString(), $this->p->readString()); $this->r->add($this->p->readString(), $this->p->readString());
} }
} }
public function status() public function status()
{ {
// Header // Header
$this->header(); $this->header();
// Unknown // Unknown
$this->p->read(15); $this->p->read(15);
$this->r->add('hostname', $this->p->readString()); $this->r->add('hostname', $this->p->readString());
$this->r->add('mod', $this->p->readString()); $this->r->add('mod', $this->p->readString());
$this->r->add('gametype', $this->p->readString()); $this->r->add('gametype', $this->p->readString());
$this->r->add('map', $this->p->readString()); $this->r->add('map', $this->p->readString());
$this->r->add('num_players', $this->p->readInt8()); $this->r->add('num_players', $this->p->readInt8());
$this->r->add('max_players', $this->p->readInt8()); $this->r->add('max_players', $this->p->readInt8());
$this->r->add('password', $this->p->readInt8()); $this->r->add('password', $this->p->readInt8());
$this->p->read(2); $this->p->read(2);
$this->r->add('punkbuster', $this->p->readInt8()); $this->r->add('punkbuster', $this->p->readInt8());
} }
public function players() public function players()
{ {
$this->header(); $this->header();
$this->p->skip(2); $this->p->skip(2);
while ($this->p->getLength()) { while ($this->p->getLength()) {
$this->r->addPlayer('name', $this->p->readString()); $this->r->addPlayer('name', $this->p->readString());
$this->r->addPlayer('team', $this->p->readString()); $this->r->addPlayer('team', $this->p->readString());
$this->p->skip(1); $this->p->skip(1);
$this->r->addPlayer('score', $this->p->readInt8()); $this->r->addPlayer('score', $this->p->readInt8());
$this->p->skip(3); $this->p->skip(3);
$this->r->addPlayer('ping', $this->p->readInt8()); $this->r->addPlayer('ping', $this->p->readInt8());
$this->p->skip(7); $this->p->skip(7);
} }
} }
private function header() private function header()
{ {
if ($this->p->read(4) !== "\x7f\xff\xff\xff") { if ($this->p->read(4) !== "\x7f\xff\xff\xff") {
throw new GameQ_ParsingException($this->p); throw new GameQ_ParsingException($this->p);
} }
$this->p->skip(2); $this->p->skip(2);
} }
} }
?> ?>

View file

@ -1,326 +1,326 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski
*/ */
{ {
/* CRYSIS PROTOCOL */ /* CRYSIS PROTOCOL */
CRYSIS_PROTOCOL_SERVER, /* id */ CRYSIS_PROTOCOL_SERVER, /* id */
"CRYSIS", /* type_prefix */ "CRYSIS", /* type_prefix */
"crysis", /* type_string */ "crysis", /* type_string */
"-crysis", /* type_option */ "-crysis", /* type_option */
"Crysis", /* game_name */ "Crysis", /* game_name */
0, /* master */ 0, /* master */
0, /* default_port */ 0, /* default_port */
0, /* port_offset */ 0, /* port_offset */
TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG, /* flags */ TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG, /* flags */
"gamerules", /* game_rule */ "gamerules", /* game_rule */
"CRYSISPROTOCOL", /* template_var */ "CRYSISPROTOCOL", /* template_var */
NULL, /* status_packet */ NULL, /* status_packet */
0, /* status_len */ 0, /* status_len */
NULL, /* player_packet */ NULL, /* player_packet */
0, /* player_len */ 0, /* player_len */
NULL, /* rule_packet */ NULL, /* rule_packet */
0, /* rule_len */ 0, /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
NULL, /* display_player_func */ NULL, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
NULL, /* display_raw_player_func */ NULL, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_player_info, /* display_xml_player_func */ xml_display_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_crysis_request_packet, /* status_query_func */ send_crysis_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_crysis_packet, /* packet_func */ deal_with_crysis_packet, /* packet_func */
}, },
/* /*
* qstat 2.8 * qstat 2.8
* by Steve Jankowski * by Steve Jankowski
* *
* Crysis query protocol * Crysis query protocol
* Copyright 2012 Steven Hartland * Copyright 2012 Steven Hartland
* *
* Licensed under the Artistic License, see LICENSE.txt for license terms * Licensed under the Artistic License, see LICENSE.txt for license terms
* *
*/ */
#include <sys/types.h> #include <sys/types.h>
#ifndef _WIN32 #ifndef _WIN32
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#else #else
#include <winsock.h> #include <winsock.h>
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
#include "debug.h" #include "debug.h"
#include "utils.h" #include "utils.h"
#include "qstat.h" #include "qstat.h"
#include "md5.h" #include "md5.h"
#include "packet_manip.h" #include "packet_manip.h"
char *decode_crysis_val( char *val ) char *decode_crysis_val( char *val )
{ {
// Very basic html conversion // Very basic html conversion
val = str_replace( val, "&quot;", "\"" ); val = str_replace( val, "&quot;", "\"" );
return str_replace( val, "&amp;", "&" ); return str_replace( val, "&amp;", "&" );
} }
query_status_t send_crysis_request_packet( struct qserver *server ) query_status_t send_crysis_request_packet( struct qserver *server )
{ {
char cmd[256], buf[1024], *password, *md5; char cmd[256], buf[1024], *password, *md5;
debug( 2, "challenge: %ld", server->challenge ); debug( 2, "challenge: %ld", server->challenge );
switch ( server->challenge ) switch ( server->challenge )
{ {
case 0: case 0:
// Not seen a challenge yet, request it // Not seen a challenge yet, request it
server->challenge++; server->challenge++;
sprintf( cmd, "challenge" ); sprintf( cmd, "challenge" );
break; break;
case 1: case 1:
server->challenge++; server->challenge++;
password = get_param_value( server, "password", "" ); password = get_param_value( server, "password", "" );
sprintf( cmd, "%s:%s", server->challenge_string, password ); sprintf( cmd, "%s:%s", server->challenge_string, password );
md5 = md5_hex( cmd, strlen( cmd ) ); md5 = md5_hex( cmd, strlen( cmd ) );
sprintf( cmd, "authenticate %s", md5 ); sprintf( cmd, "authenticate %s", md5 );
free( md5 ); free( md5 );
break; break;
case 2: case 2:
// NOTE: we currently don't support player info // NOTE: we currently don't support player info
server->challenge++; server->challenge++;
server->flags |= TF_STATUS_QUERY; server->flags |= TF_STATUS_QUERY;
server->n_servers = 3; server->n_servers = 3;
sprintf( cmd, "status" ); sprintf( cmd, "status" );
break; break;
case 3: case 3:
return DONE_FORCE; return DONE_FORCE;
} }
server->saved_data.pkt_max = -1; 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<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>%s</methodName><params /></methodCall>", VERSION, (int)(98 + strlen(cmd)), cmd); 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<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>%s</methodName><params /></methodCall>", VERSION, (int)(98 + strlen(cmd)), cmd);
return send_packet( server, buf, strlen( buf ) ); return send_packet( server, buf, strlen( buf ) );
} }
query_status_t valid_crysis_response( struct qserver *server, char *rawpkt, int pktlen ) query_status_t valid_crysis_response( struct qserver *server, char *rawpkt, int pktlen )
{ {
char *s; char *s;
int len; int len;
int cnt = packet_count( server ); int cnt = packet_count( server );
if ( 0 == cnt && 0 != strncmp( "HTTP/1.1 200 OK", rawpkt, 15 ) ) if ( 0 == cnt && 0 != strncmp( "HTTP/1.1 200 OK", rawpkt, 15 ) )
{ {
// not valid response // not valid response
return REQ_ERROR; return REQ_ERROR;
} }
s = strnstr(rawpkt, "Content-Length: ", pktlen ); s = strnstr(rawpkt, "Content-Length: ", pktlen );
if ( NULL == s ) if ( NULL == s )
{ {
// not valid response // not valid response
return INPROGRESS; return INPROGRESS;
} }
s += 16; s += 16;
if ( 1 != sscanf( s, "%d", &len ) ) if ( 1 != sscanf( s, "%d", &len ) )
{ {
return INPROGRESS; return INPROGRESS;
} }
s = strnstr(rawpkt, "\015\012\015\012", pktlen ); s = strnstr(rawpkt, "\015\012\015\012", pktlen );
if ( NULL == s ) if ( NULL == s )
{ {
return INPROGRESS; return INPROGRESS;
} }
s += 4; s += 4;
if ( pktlen != ( s - rawpkt + len ) ) if ( pktlen != ( s - rawpkt + len ) )
{ {
return INPROGRESS; return INPROGRESS;
} }
return DONE_FORCE; return DONE_FORCE;
} }
char* crysis_response( struct qserver *server, char *rawpkt, int pktlen ) char* crysis_response( struct qserver *server, char *rawpkt, int pktlen )
{ {
char *s, *e; char *s, *e;
int len = pktlen; int len = pktlen;
s = strnstr(rawpkt, "<methodResponse><params><param><value><string>", len ); s = strnstr(rawpkt, "<methodResponse><params><param><value><string>", len );
if ( NULL == s ) if ( NULL == s )
{ {
// not valid response // not valid response
return NULL; return NULL;
} }
s += 46; s += 46;
len += rawpkt - s; len += rawpkt - s;
e = strnstr(s, "</string></value>", len ); e = strnstr(s, "</string></value>", len );
if ( NULL == e ) if ( NULL == e )
{ {
// not valid response // not valid response
return NULL; return NULL;
} }
*e = '\0'; *e = '\0';
return strdup( s ); return strdup( s );
} }
query_status_t deal_with_crysis_packet( struct qserver *server, char *rawpkt, int pktlen ) query_status_t deal_with_crysis_packet( struct qserver *server, char *rawpkt, int pktlen )
{ {
char *s, *val, *line; char *s, *val, *line;
query_status_t state = INPROGRESS; query_status_t state = INPROGRESS;
debug( 2, "processing..." ); debug( 2, "processing..." );
if ( ! server->combined ) if ( ! server->combined )
{ {
state = valid_crysis_response( server, rawpkt, pktlen ); state = valid_crysis_response( server, rawpkt, pktlen );
server->retry1 = n_retries; server->retry1 = n_retries;
if ( 0 == server->n_requests ) if ( 0 == server->n_requests )
{ {
server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 ); server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 );
server->n_requests++; server->n_requests++;
} }
switch ( state ) switch ( state )
{ {
case INPROGRESS: case INPROGRESS:
{ {
// response fragment recieved // response fragment recieved
int pkt_id; int pkt_id;
int pkt_max; int pkt_max;
// We're expecting more to come // We're expecting more to come
debug( 5, "fragment recieved..." ); debug( 5, "fragment recieved..." );
pkt_id = packet_count( server ); pkt_id = packet_count( server );
pkt_max = pkt_id++; pkt_max = pkt_id++;
if ( ! add_packet( server, 0, pkt_id, pkt_max, pktlen, rawpkt, 1 ) ) if ( ! add_packet( server, 0, pkt_id, pkt_max, pktlen, rawpkt, 1 ) )
{ {
// fatal error e.g. out of memory // fatal error e.g. out of memory
return MEM_ERROR; return MEM_ERROR;
} }
// combine_packets will call us recursively // combine_packets will call us recursively
return combine_packets( server ); return combine_packets( server );
} }
case DONE_FORCE: case DONE_FORCE:
break; // single packet response fall through break; // single packet response fall through
default: default:
return state; return state;
} }
} }
if ( DONE_FORCE != state ) if ( DONE_FORCE != state )
{ {
state = valid_crysis_response( server, rawpkt, pktlen ); state = valid_crysis_response( server, rawpkt, pktlen );
switch ( state ) switch ( state )
{ {
case DONE_FORCE: case DONE_FORCE:
break; // actually process break; // actually process
default: default:
return state; return state;
} }
} }
debug( 3, "packet: challenge = %ld", server->challenge ); debug( 3, "packet: challenge = %ld", server->challenge );
switch ( server->challenge ) switch ( server->challenge )
{ {
case 1: case 1:
s = crysis_response( server, rawpkt, pktlen ); s = crysis_response( server, rawpkt, pktlen );
if ( NULL != s ) if ( NULL != s )
{ {
server->challenge_string = s; server->challenge_string = s;
return send_crysis_request_packet( server ); return send_crysis_request_packet( server );
} }
return REQ_ERROR; return REQ_ERROR;
case 2: case 2:
s = crysis_response( server, rawpkt, pktlen ); s = crysis_response( server, rawpkt, pktlen );
if ( NULL == s ) if ( NULL == s )
{ {
return REQ_ERROR; return REQ_ERROR;
} }
if ( 0 != strncmp( s, "authorized", 10 ) ) if ( 0 != strncmp( s, "authorized", 10 ) )
{ {
free( s ); free( s );
return REQ_ERROR; return REQ_ERROR;
} }
free( s ); free( s );
return send_crysis_request_packet( server ); return send_crysis_request_packet( server );
case 3: case 3:
s = crysis_response( server, rawpkt, pktlen ); s = crysis_response( server, rawpkt, pktlen );
if ( NULL == s ) if ( NULL == s )
{ {
return REQ_ERROR; return REQ_ERROR;
} }
} }
// Correct ping // Correct ping
// Not quite right but gives a good estimate // Not quite right but gives a good estimate
server->ping_total = ( server->ping_total * server->n_requests ) / 2; server->ping_total = ( server->ping_total * server->n_requests ) / 2;
debug( 3, "processing response..." ); debug( 3, "processing response..." );
s = decode_crysis_val( s ); s = decode_crysis_val( s );
line = strtok( s, "\012" ); line = strtok( s, "\012" );
// NOTE: id=XXX and msg=XXX will be processed by the mod following the one they where the response of // NOTE: id=XXX and msg=XXX will be processed by the mod following the one they where the response of
while ( NULL != line ) while ( NULL != line )
{ {
debug( 4, "LINE: %s\n", line ); debug( 4, "LINE: %s\n", line );
val = strstr( line, ":" ); val = strstr( line, ":" );
if ( NULL != val ) if ( NULL != val )
{ {
*val = '\0'; *val = '\0';
val+=2; val+=2;
debug( 4, "var: %s, val: %s", line, val ); debug( 4, "var: %s, val: %s", line, val );
if ( 0 == strcmp( "name", line ) ) if ( 0 == strcmp( "name", line ) )
{ {
server->server_name = strdup( val ); server->server_name = strdup( val );
} }
else if ( 0 == strcmp( "level", line ) ) else if ( 0 == strcmp( "level", line ) )
{ {
server->map_name = strdup( val ); server->map_name = strdup( val );
} }
else if ( 0 == strcmp( "players", line ) ) else if ( 0 == strcmp( "players", line ) )
{ {
if ( 2 == sscanf( val, "%d/%d", &server->num_players, &server->max_players) ) if ( 2 == sscanf( val, "%d/%d", &server->num_players, &server->max_players) )
{ {
} }
} }
else if ( else if (
0 == strcmp( "version", line ) || 0 == strcmp( "version", line ) ||
0 == strcmp( "gamerules", line ) || 0 == strcmp( "gamerules", line ) ||
0 == strcmp( "time remaining", line ) 0 == strcmp( "time remaining", line )
) )
{ {
add_rule( server, line, val, NO_FLAGS ); add_rule( server, line, val, NO_FLAGS );
} }
} }
line = strtok( NULL, "\012" ); line = strtok( NULL, "\012" );
} }
gettimeofday( &server->packet_time1, NULL ); gettimeofday( &server->packet_time1, NULL );
return DONE_FORCE; return DONE_FORCE;
} }

View file

@ -1,56 +1,56 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: cs2d.php,v 1.1 2008/04/14 18:04:50 tombuskens Exp $ * $Id: cs2d.php,v 1.1 2008/04/14 18:04:50 tombuskens Exp $
*/ */
[cs2d] [cs2d]
status = "\xfa\x00" status = "\xfa\x00"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Counterstrike 2d Protocol * Counterstrike 2d Protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.1 $ * @version $Revision: 1.1 $
*/ */
class GameQ_Protocol_cs2d extends GameQ_Protocol class GameQ_Protocol_cs2d extends GameQ_Protocol
{ {
public function status() public function status()
{ {
$this->p->skip(2); $this->p->skip(2);
$this->r->add('hostname', $this->readString()); $this->r->add('hostname', $this->readString());
$this->r->add('password', $this->p->readInt8()); $this->r->add('password', $this->p->readInt8());
$this->r->add('mapname', $this->readString()); $this->r->add('mapname', $this->readString());
$this->r->add('num_players', $this->p->readInt8()); $this->r->add('num_players', $this->p->readInt8());
$this->r->add('max_players', $this->p->readInt8()); $this->r->add('max_players', $this->p->readInt8());
$this->r->add('fog_of_war', $this->p->readInt8()); $this->r->add('fog_of_war', $this->p->readInt8());
$this->r->add('war_mode', $this->p->readInt8()); $this->r->add('war_mode', $this->p->readInt8());
$this->r->add('version', $this->readString()); $this->r->add('version', $this->readString());
} }
private function readString() private function readString()
{ {
$str = $this->p->readString("\x0D"); $str = $this->p->readString("\x0D");
$this->p->skip(1); $this->p->skip(1);
return $str; return $str;
} }
} }
?> ?>

View file

@ -1,70 +1,70 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_29(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
// REFERENCE: http://www.cs2d.com/servers.php // REFERENCE: http://www.cs2d.com/servers.php
if ($lgsl_need['s'] || $lgsl_need['e']) if ($lgsl_need['s'] || $lgsl_need['e'])
{ {
$lgsl_need['s'] = FALSE; $lgsl_need['s'] = FALSE;
$lgsl_need['e'] = FALSE; $lgsl_need['e'] = FALSE;
fwrite($lgsl_fp, "\x01\x00\x03\x10\x21\xFB\x01\x75\x00"); fwrite($lgsl_fp, "\x01\x00\x03\x10\x21\xFB\x01\x75\x00");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
$buffer = substr($buffer, 4); // REMOVE HEADER $buffer = substr($buffer, 4); // REMOVE HEADER
$server['e']['bit_flags'] = ord(lgsl_cut_byte($buffer, 1)) - 48; $server['e']['bit_flags'] = ord(lgsl_cut_byte($buffer, 1)) - 48;
$server['s']['name'] = lgsl_cut_pascal($buffer); $server['s']['name'] = lgsl_cut_pascal($buffer);
$server['s']['map'] = lgsl_cut_pascal($buffer); $server['s']['map'] = lgsl_cut_pascal($buffer);
$server['s']['players'] = 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['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['gamemode'] = 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['e']['bots'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['password'] = ($server['e']['bit_flags'] & 1) ? "1" : "0"; $server['s']['password'] = ($server['e']['bit_flags'] & 1) ? "1" : "0";
$server['e']['registered_only'] = ($server['e']['bit_flags'] & 2) ? "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']['fog_of_war'] = ($server['e']['bit_flags'] & 4) ? "1" : "0";
$server['e']['friendlyfire'] = ($server['e']['bit_flags'] & 8) ? "1" : "0"; $server['e']['friendlyfire'] = ($server['e']['bit_flags'] & 8) ? "1" : "0";
} }
if ($lgsl_need['p']) if ($lgsl_need['p'])
{ {
$lgsl_need['p'] = FALSE; $lgsl_need['p'] = FALSE;
fwrite($lgsl_fp, "\x01\x00\xFB\x05"); fwrite($lgsl_fp, "\x01\x00\xFB\x05");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
$buffer = substr($buffer, 4); // REMOVE HEADER $buffer = substr($buffer, 4); // REMOVE HEADER
$player_total = ord(lgsl_cut_byte($buffer, 1)); $player_total = ord(lgsl_cut_byte($buffer, 1));
for ($i=0; $i<$player_total; $i++) for ($i=0; $i<$player_total; $i++)
{ {
$server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1)); $server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1));
$server['p'][$i]['name'] = lgsl_cut_pascal($buffer); $server['p'][$i]['name'] = lgsl_cut_pascal($buffer);
$server['p'][$i]['team'] = ord(lgsl_cut_byte($buffer, 1)); $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]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l");
$server['p'][$i]['deaths'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l"); $server['p'][$i]['deaths'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l");
} }
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,58 +1,58 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: cube.php,v 1.1 2007/07/04 09:08:36 tombuskens Exp $ * $Id: cube.php,v 1.1 2007/07/04 09:08:36 tombuskens Exp $
*/ */
[cube] [cube]
status = "\x00" status = "\x00"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Cube Engine protocol * Cube Engine protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.1 $ * @version $Revision: 1.1 $
*/ */
class GameQ_Protocol_cube extends GameQ_Protocol class GameQ_Protocol_cube extends GameQ_Protocol
{ {
/* /*
* status packet * status packet
*/ */
public function status() public function status()
{ {
// Header // Header
if (!$this->p->read() == "\x00") { if (!$this->p->read() == "\x00") {
throw new GameQ_ParsingException($this->p); throw new GameQ_ParsingException($this->p);
} }
$this->p->skip(2); $this->p->skip(2);
// Vars // Vars
$this->r->add('protocol', $this->p->readInt8()); $this->r->add('protocol', $this->p->readInt8());
$this->r->add('mode', $this->p->readInt8()); $this->r->add('mode', $this->p->readInt8());
$this->r->add('num_players', $this->p->readInt8()); $this->r->add('num_players', $this->p->readInt8());
$this->r->add('time_remaining', $this->p->readInt8()); $this->r->add('time_remaining', $this->p->readInt8());
$this->r->add('map', $this->p->readString()); $this->r->add('map', $this->p->readString());
$this->r->add('servername', $this->p->readString()); $this->r->add('servername', $this->p->readString());
$this->r->add('max_players', $this->p->readInt8()); $this->r->add('max_players', $this->p->readInt8());
} }
} }
?> ?>

View file

@ -1,79 +1,79 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: sauerbraten.php,v 1.2 2008/06/25 13:50:47 tombuskens Exp $ * $Id: sauerbraten.php,v 1.2 2008/06/25 13:50:47 tombuskens Exp $
*/ */
[sauerbraten] [sauerbraten]
status = "\xFF" status = "\xFF"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Sauerbraten / Cube 2 Engine protocol * Sauerbraten / Cube 2 Engine protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.2 $ * @version $Revision: 1.2 $
*/ */
class GameQ_Protocol_sauerbraten extends GameQ_Protocol class GameQ_Protocol_sauerbraten extends GameQ_Protocol
{ {
private function getint() private function getint()
{ {
$i = $this->p->readInt8(); $i = $this->p->readInt8();
if ($i == 0x80) if ($i == 0x80)
{ {
$i = $this->p->readInt8(); $i = $this->p->readInt8();
$i |= $this->p->readInt8() << 8; $i |= $this->p->readInt8() << 8;
} }
else if ($i == 0x81) else if ($i == 0x81)
{ {
$i = $this->p->readInt8(); $i = $this->p->readInt8();
$i |= $this->p->readInt8() << 8; $i |= $this->p->readInt8() << 8;
$i |= $this->p->readInt8() << 16; $i |= $this->p->readInt8() << 16;
$i |= $this->p->readInt8() << 24; $i |= $this->p->readInt8() << 24;
} }
return $i; return $i;
} }
/* /*
* status packet * status packet
*/ */
public function status() public function status()
{ {
// Header // Header
if (!$this->p->read() == "\x00") { if (!$this->p->read() == "\x00") {
throw new GameQ_ParsingException($this->p); throw new GameQ_ParsingException($this->p);
} }
// Vars // Vars
$this->r->add('num_players', $this->getint()); $this->r->add('num_players', $this->getint());
$this->r->add('num_attributes', $this->getint()); $this->r->add('num_attributes', $this->getint());
$this->r->add('protocol', $this->getint()); $this->r->add('protocol', $this->getint());
$this->r->add('servermode', $this->getint()); $this->r->add('servermode', $this->getint());
$this->r->add('time_remaining', $this->getint()); $this->r->add('time_remaining', $this->getint());
$this->r->add('max_players', $this->getint()); $this->r->add('max_players', $this->getint());
$this->r->add('locked', $this->getint()); $this->r->add('locked', $this->getint());
$this->r->add('map', $this->p->readString()); $this->r->add('map', $this->p->readString());
$this->r->add('servername', $this->p->readString()); $this->r->add('servername', $this->p->readString());
} }
} }
?> ?>

View file

@ -1,168 +1,168 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_24(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
// REFERENCE: http://cubelister.sourceforge.net // REFERENCE: http://cubelister.sourceforge.net
fwrite($lgsl_fp, "\x21\x21"); fwrite($lgsl_fp, "\x21\x21");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
$buffer = substr($buffer, 2); // REMOVE HEADER $buffer = substr($buffer, 2); // REMOVE HEADER
//---------------------------------------------------------+ //---------------------------------------------------------+
if ($buffer[0] == "\x1b") // CUBE 1 if ($buffer[0] == "\x1b") // CUBE 1
{ {
// RESPONSE IS XOR ENCODED FOR SOME STRANGE REASON // RESPONSE IS XOR ENCODED FOR SOME STRANGE REASON
for ($i=0; $i<strlen($buffer); $i++) { $buffer[$i] = chr(ord($buffer[$i]) ^ 0x61); } for ($i=0; $i<strlen($buffer); $i++) { $buffer[$i] = chr(ord($buffer[$i]) ^ 0x61); }
$server['s']['game'] = "Cube"; $server['s']['game'] = "Cube";
$server['e']['netcode'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['netcode'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['timeleft'] = lgsl_time(ord(lgsl_cut_byte($buffer, 1)) * 60); $server['e']['timeleft'] = lgsl_time(ord(lgsl_cut_byte($buffer, 1)) * 60);
$server['s']['map'] = lgsl_cut_string($buffer); $server['s']['map'] = lgsl_cut_string($buffer);
$server['s']['name'] = lgsl_cut_string($buffer); $server['s']['name'] = lgsl_cut_string($buffer);
$server['s']['playersmax'] = "0"; // NOT PROVIDED $server['s']['playersmax'] = "0"; // NOT PROVIDED
// DOES NOT RETURN PLAYER INFORMATION // DOES NOT RETURN PLAYER INFORMATION
return TRUE; return TRUE;
} }
elseif ($buffer[0] == "\x80") // ASSAULT CUBE elseif ($buffer[0] == "\x80") // ASSAULT CUBE
{ {
$server['s']['game'] = "AssaultCube"; $server['s']['game'] = "AssaultCube";
$server['e']['netcode'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['netcode'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['version'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); $server['e']['version'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['timeleft'] = lgsl_time(ord(lgsl_cut_byte($buffer, 1)) * 60); $server['e']['timeleft'] = lgsl_time(ord(lgsl_cut_byte($buffer, 1)) * 60);
$server['s']['map'] = lgsl_cut_string($buffer); $server['s']['map'] = lgsl_cut_string($buffer);
$server['s']['name'] = lgsl_cut_string($buffer); $server['s']['name'] = lgsl_cut_string($buffer);
$server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1));
} }
elseif ($buffer[1] == "\x05") // CUBE 2 - SAUERBRATEN elseif ($buffer[1] == "\x05") // CUBE 2 - SAUERBRATEN
{ {
$server['s']['game'] = "Sauerbraten"; $server['s']['game'] = "Sauerbraten";
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1));
$info_returned = ord(lgsl_cut_byte($buffer, 1)); // CODED FOR 5 $info_returned = ord(lgsl_cut_byte($buffer, 1)); // CODED FOR 5
$server['e']['netcode'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['netcode'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['version'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); $server['e']['version'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['timeleft'] = lgsl_time(ord(lgsl_cut_byte($buffer, 1)) * 60); $server['e']['timeleft'] = lgsl_time(ord(lgsl_cut_byte($buffer, 1)) * 60);
$server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); // BIT FIELD $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); // BIT FIELD
$server['s']['password'] = $server['s']['password'] & 4 ? "1" : "0"; $server['s']['password'] = $server['s']['password'] & 4 ? "1" : "0";
$server['s']['map'] = lgsl_cut_string($buffer); $server['s']['map'] = lgsl_cut_string($buffer);
$server['s']['name'] = lgsl_cut_string($buffer); $server['s']['name'] = lgsl_cut_string($buffer);
} }
elseif ($buffer[1] == "\x06") // BLOODFRONTIER elseif ($buffer[1] == "\x06") // BLOODFRONTIER
{ {
$server['s']['game'] = "Blood Frontier"; $server['s']['game'] = "Blood Frontier";
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1));
$info_returned = ord(lgsl_cut_byte($buffer, 1)); // CODED FOR 6 $info_returned = ord(lgsl_cut_byte($buffer, 1)); // CODED FOR 6
$server['e']['netcode'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['netcode'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['version'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); $server['e']['version'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['mutators'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['mutators'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['timeleft'] = lgsl_time(ord(lgsl_cut_byte($buffer, 1)) * 60); $server['e']['timeleft'] = lgsl_time(ord(lgsl_cut_byte($buffer, 1)) * 60);
$server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1)); $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); // BIT FIELD $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); // BIT FIELD
$server['s']['password'] = $server['s']['password'] & 4 ? "1" : "0"; $server['s']['password'] = $server['s']['password'] & 4 ? "1" : "0";
$server['s']['map'] = lgsl_cut_string($buffer); $server['s']['map'] = lgsl_cut_string($buffer);
$server['s']['name'] = lgsl_cut_string($buffer); $server['s']['name'] = lgsl_cut_string($buffer);
} }
else // UNKNOWN else // UNKNOWN
{ {
return FALSE; return FALSE;
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
// CRAZY PROTOCOL - REQUESTS MUST BE MADE FOR EACH PLAYER // CRAZY PROTOCOL - REQUESTS MUST BE MADE FOR EACH PLAYER
// BOTS ARE RETURNED BUT NOT INCLUDED IN THE PLAYER TOTAL // BOTS ARE RETURNED BUT NOT INCLUDED IN THE PLAYER TOTAL
// AND THERE CAN BE ID GAPS BETWEEN THE PLAYERS RETURNED // AND THERE CAN BE ID GAPS BETWEEN THE PLAYERS RETURNED
if ($lgsl_need['p'] && $server['s']['players']) if ($lgsl_need['p'] && $server['s']['players'])
{ {
$player_key = 0; $player_key = 0;
for ($player_id=0; $player_id<32; $player_id++) for ($player_id=0; $player_id<32; $player_id++)
{ {
fwrite($lgsl_fp, "\x00\x01".chr($player_id)); fwrite($lgsl_fp, "\x00\x01".chr($player_id));
// READ PACKET // READ PACKET
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { break; } if (!$buffer) { break; }
// CHECK IF PLAYER ID IS ACTIVE // CHECK IF PLAYER ID IS ACTIVE
if ($buffer[5] != "\x00") if ($buffer[5] != "\x00")
{ {
if ($player_key < $server['s']['players']) { continue; } if ($player_key < $server['s']['players']) { continue; }
break; break;
} }
// IF PREVIEW PACKET GET THE FULL PACKET THAT FOLLOWS // IF PREVIEW PACKET GET THE FULL PACKET THAT FOLLOWS
if (strlen($buffer) < 15) if (strlen($buffer) < 15)
{ {
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { break; } if (!$buffer) { break; }
} }
// REMOVE HEADER // REMOVE HEADER
$buffer = substr($buffer, 7); $buffer = substr($buffer, 7);
// WE CAN NOW GET THE PLAYER DETAILS // WE CAN NOW GET THE PLAYER DETAILS
if ($server['s']['game'] == "Blood Frontier") if ($server['s']['game'] == "Blood Frontier")
{ {
$server['p'][$player_key]['pid'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['pid'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
$server['p'][$player_key]['ping'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['ping'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
$server['p'][$player_key]['ping'] = $server['p'][$player_key]['ping'] == 128 ? lgsl_unpack(lgsl_cut_byte($buffer, 2), "S") : $server['p'][$player_key]['ping']; $server['p'][$player_key]['ping'] = $server['p'][$player_key]['ping'] == 128 ? lgsl_unpack(lgsl_cut_byte($buffer, 2), "S") : $server['p'][$player_key]['ping'];
$server['p'][$player_key]['name'] = lgsl_cut_string($buffer); $server['p'][$player_key]['name'] = lgsl_cut_string($buffer);
$server['p'][$player_key]['team'] = lgsl_cut_string($buffer); $server['p'][$player_key]['team'] = lgsl_cut_string($buffer);
$server['p'][$player_key]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "c"); $server['p'][$player_key]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "c");
$server['p'][$player_key]['damage'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['damage'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
$server['p'][$player_key]['deaths'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['deaths'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
$server['p'][$player_key]['teamkills'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['teamkills'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
$server['p'][$player_key]['accuracy'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C")."%"; $server['p'][$player_key]['accuracy'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C")."%";
$server['p'][$player_key]['health'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "c"); $server['p'][$player_key]['health'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "c");
$server['p'][$player_key]['spree'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['spree'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
$server['p'][$player_key]['weapon'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['weapon'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
} }
else else
{ {
$server['p'][$player_key]['pid'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['pid'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
$server['p'][$player_key]['name'] = lgsl_cut_string($buffer); $server['p'][$player_key]['name'] = lgsl_cut_string($buffer);
$server['p'][$player_key]['team'] = lgsl_cut_string($buffer); $server['p'][$player_key]['team'] = lgsl_cut_string($buffer);
$server['p'][$player_key]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "c"); $server['p'][$player_key]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "c");
$server['p'][$player_key]['deaths'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['deaths'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
$server['p'][$player_key]['teamkills'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['teamkills'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
$server['p'][$player_key]['accuracy'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C")."%"; $server['p'][$player_key]['accuracy'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C")."%";
$server['p'][$player_key]['health'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "c"); $server['p'][$player_key]['health'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "c");
$server['p'][$player_key]['armour'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['armour'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
$server['p'][$player_key]['weapon'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C"); $server['p'][$player_key]['weapon'] = lgsl_unpack(lgsl_cut_byte($buffer, 1), "C");
} }
$player_key++; $player_key++;
} }
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,284 +1,284 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski
*/ */
char cube2_serverstatus[3] = {'\x80', '\x10', '\x27'}; char cube2_serverstatus[3] = {'\x80', '\x10', '\x27'};
{ {
/* Cube 2/Sauerbraten/Blood Frontier */ /* Cube 2/Sauerbraten/Blood Frontier */
CUBE2_SERVER, /* id */ CUBE2_SERVER, /* id */
"CUBE2", /* type_prefix */ "CUBE2", /* type_prefix */
"cube2", /* type_string */ "cube2", /* type_string */
"-cubes", /* type_option */ "-cubes", /* type_option */
"Sauerbraten", /* game_name */ "Sauerbraten", /* game_name */
0, /* master */ 0, /* master */
CUBE2_DEFAULT_PORT, /* default_port */ CUBE2_DEFAULT_PORT, /* default_port */
1, /* port_offset */ 1, /* port_offset */
0, /* flags */ 0, /* flags */
"", /* game_rule */ "", /* game_rule */
"CUBE2", /* template_var */ "CUBE2", /* template_var */
cube2_serverstatus, /* status_packet */ cube2_serverstatus, /* status_packet */
sizeof(cube2_serverstatus), /* status_len */ sizeof(cube2_serverstatus), /* status_len */
NULL, /* player_packet */ NULL, /* player_packet */
0, /* player_len */ 0, /* player_len */
NULL, /* rule_packet */ NULL, /* rule_packet */
0, /* rule_len */ 0, /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
NULL, /* display_player_func */ NULL, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
NULL, /* display_raw_player_func */ NULL, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
NULL, /* display_xml_player_func */ NULL, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_cube2_request_packet, /* status_query_func */ send_cube2_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_cube2_packet, /* packet_func */ deal_with_cube2_packet, /* packet_func */
}, },
/* /*
* qstat 2.12 * qstat 2.12
* by Steve Jankowski * by Steve Jankowski
* *
* Cube 2 / Sauerbraten protocol * Cube 2 / Sauerbraten protocol
* Copyright 2011 NoisyB * Copyright 2011 NoisyB
* *
* Licensed under the Artistic License, see LICENSE.txt for license terms * Licensed under the Artistic License, see LICENSE.txt for license terms
*/ */
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include "debug.h" #include "debug.h"
#include "qstat.h" #include "qstat.h"
#include "packet_manip.h" #include "packet_manip.h"
struct offset struct offset
{ {
unsigned char *d; unsigned char *d;
int pos; int pos;
int len; int len;
}; };
//#define SB_MASTER_SERVER "http://sauerbraten.org/masterserver/retrieve.do?item=list" //#define SB_MASTER_SERVER "http://sauerbraten.org/masterserver/retrieve.do?item=list"
#define SB_PROTOCOL 258 #define SB_PROTOCOL 258
#define MIN(a,b) ((a)<(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX_ATTR 255 #define MAX_ATTR 255
#define MAX_STRING 1024 #define MAX_STRING 1024
static int static int
getint (struct offset * d) getint (struct offset * d)
{ {
int val = 0; int val = 0;
if ( d->pos >= d->len ) if ( d->pos >= d->len )
{ {
return 0; return 0;
} }
val = d->d[d->pos++] & 0xff; // 8 bit value val = d->d[d->pos++] & 0xff; // 8 bit value
// except... // except...
if ( val == 0x80 && d->pos < d->len - 2 ) // 16 bit value if ( val == 0x80 && d->pos < d->len - 2 ) // 16 bit value
{ {
val = (d->d[d->pos++] & 0xff); val = (d->d[d->pos++] & 0xff);
val |= (d->d[d->pos++] & 0xff) << 8; val |= (d->d[d->pos++] & 0xff) << 8;
} }
else if ( val == 0x81 && d->pos < d->len - 4 ) // 32 bit value else if ( val == 0x81 && d->pos < d->len - 4 ) // 32 bit value
{ {
val = (d->d[d->pos++] & 0xff); val = (d->d[d->pos++] & 0xff);
val |= (d->d[d->pos++] & 0xff) << 8; val |= (d->d[d->pos++] & 0xff) << 8;
val |= (d->d[d->pos++] & 0xff) << 16; val |= (d->d[d->pos++] & 0xff) << 16;
val |= (d->d[d->pos++] & 0xff) << 24; val |= (d->d[d->pos++] & 0xff) << 24;
} }
return val; return val;
} }
static char * getstr( char *dest, int dest_len, struct offset *d ) static char * getstr( char *dest, int dest_len, struct offset *d )
{ {
int len = 0; int len = 0;
if (d->pos >= d->len) if (d->pos >= d->len)
{ {
return NULL; return NULL;
} }
len = MIN( dest_len, d->len - d->pos ); len = MIN( dest_len, d->len - d->pos );
strncpy( dest, (const char *) d->d + d->pos, len )[len - 1] = 0; strncpy( dest, (const char *) d->d + d->pos, len )[len - 1] = 0;
d->pos += strlen (dest) + 1; d->pos += strlen (dest) + 1;
return dest; return dest;
} }
static char* sb_getversion_s (int n) static char* sb_getversion_s (int n)
{ {
static char *version_s[] = static char *version_s[] =
{ {
"Justice", "Justice",
"CTF", "CTF",
"Assassin", "Assassin",
"Summer", "Summer",
"Spring", "Spring",
"Gui", "Gui",
"Water", "Water",
"Normalmap", "Normalmap",
"Sp", "Sp",
"Occlusion", "Occlusion",
"Shader", "Shader",
"Physics", "Physics",
"Mp", "Mp",
"", "",
"Agc", "Agc",
"Quakecon", "Quakecon",
"Independence" "Independence"
}; };
n = SB_PROTOCOL - n; n = SB_PROTOCOL - n;
if (n >= 0 && (size_t) n < sizeof(version_s) / sizeof(version_s[0])) if (n >= 0 && (size_t) n < sizeof(version_s) / sizeof(version_s[0]))
{ {
return version_s[n]; return version_s[n];
} }
return "unknown"; return "unknown";
} }
static char* sb_getmode_s(int n) static char* sb_getmode_s(int n)
{ {
static char *mode_s[] = static char *mode_s[] =
{ {
"slowmo SP", "slowmo SP",
"slowmo DMSP", "slowmo DMSP",
"demo", "demo",
"SP", "SP",
"DMSP", "DMSP",
"ffa/default", "ffa/default",
"coopedit", "coopedit",
"ffa/duel", "ffa/duel",
"teamplay", "teamplay",
"instagib", "instagib",
"instagib team", "instagib team",
"efficiency", "efficiency",
"efficiency team", "efficiency team",
"insta arena", "insta arena",
"insta clan arena", "insta clan arena",
"tactics arena", "tactics arena",
"tactics clan arena", "tactics clan arena",
"capture", "capture",
"insta capture", "insta capture",
"regen capture", "regen capture",
"assassin", "assassin",
"insta assassin", "insta assassin",
"ctf", "ctf",
"insta ctf" "insta ctf"
}; };
n += 6; n += 6;
if (n >= 0 && (size_t) n < sizeof(mode_s) / sizeof(mode_s[0])) if (n >= 0 && (size_t) n < sizeof(mode_s) / sizeof(mode_s[0]))
{ {
return mode_s[n]; return mode_s[n];
} }
return "unknown"; return "unknown";
} }
query_status_t send_cube2_request_packet( struct qserver *server ) query_status_t send_cube2_request_packet( struct qserver *server )
{ {
return send_packet( server, server->type->status_packet, server->type->status_len ); 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 ) query_status_t deal_with_cube2_packet( struct qserver *server, char *rawpkt, int pktlen )
{ {
// skip unimplemented ack, crc, etc // skip unimplemented ack, crc, etc
int i; int i;
int numattr; int numattr;
int attr[MAX_ATTR]; int attr[MAX_ATTR];
char buf[MAX_STRING]; char buf[MAX_STRING];
enum { enum {
MM_OPEN = 0, MM_OPEN = 0,
MM_VETO, MM_VETO,
MM_LOCKED, MM_LOCKED,
MM_PRIVATE MM_PRIVATE
}; };
struct offset d; struct offset d;
d.d = (unsigned char *) rawpkt; d.d = (unsigned char *) rawpkt;
d.pos = 0; d.pos = 0;
d.len = pktlen; d.len = pktlen;
server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 ); server->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
getint( &d ); // we have the ping already getint( &d ); // we have the ping already
server->num_players = getint( &d ); server->num_players = getint( &d );
numattr = getint( &d ); numattr = getint( &d );
for ( i = 0; i < numattr && i < MAX_ATTR; i++ ) for ( i = 0; i < numattr && i < MAX_ATTR; i++ )
{ {
attr[i] = getint (&d); attr[i] = getint (&d);
} }
server->protocol_version = attr[0]; server->protocol_version = attr[0];
sprintf( buf, "%d %s", attr[0], sb_getversion_s (attr[0]) ); sprintf( buf, "%d %s", attr[0], sb_getversion_s (attr[0]) );
add_rule( server, "version", buf, NO_FLAGS ); add_rule( server, "version", buf, NO_FLAGS );
sprintf( buf, "%d %s", attr[1], sb_getmode_s (attr[1]) ); sprintf( buf, "%d %s", attr[1], sb_getmode_s (attr[1]) );
add_rule( server, "mode", buf, NO_FLAGS ); add_rule( server, "mode", buf, NO_FLAGS );
sprintf( buf, "%d", attr[2] ); sprintf( buf, "%d", attr[2] );
add_rule( server, "seconds_left", buf, NO_FLAGS ); add_rule( server, "seconds_left", buf, NO_FLAGS );
server->max_players = attr[3]; server->max_players = attr[3];
switch ( attr[5] ) switch ( attr[5] )
{ {
case MM_OPEN: case MM_OPEN:
sprintf( buf, "%d open", attr[5] ); sprintf( buf, "%d open", attr[5] );
break; break;
case MM_VETO: case MM_VETO:
sprintf( buf, "%d veto", attr[5] ); sprintf( buf, "%d veto", attr[5] );
break; break;
case MM_LOCKED: case MM_LOCKED:
sprintf( buf, "%d locked", attr[5] ); sprintf( buf, "%d locked", attr[5] );
break; break;
case MM_PRIVATE: case MM_PRIVATE:
sprintf( buf, "%d private", attr[5] ); sprintf( buf, "%d private", attr[5] );
break; break;
default: default:
sprintf( buf, "%d unknown", attr[5] ); sprintf( buf, "%d unknown", attr[5] );
} }
add_rule( server, "mm", buf, NO_FLAGS); add_rule( server, "mm", buf, NO_FLAGS);
for ( i = 0; i < numattr && i < MAX_ATTR; i++ ) for ( i = 0; i < numattr && i < MAX_ATTR; i++ )
{ {
char buf2[MAX_STRING]; char buf2[MAX_STRING];
sprintf( buf, "attr%d", i ); sprintf( buf, "attr%d", i );
sprintf( buf2, "%d", attr[i] ); sprintf( buf2, "%d", attr[i] );
add_rule( server, buf, buf2, NO_FLAGS ); add_rule( server, buf, buf2, NO_FLAGS );
} }
getstr( buf, MAX_STRING, &d ); getstr( buf, MAX_STRING, &d );
server->map_name = strdup(buf); server->map_name = strdup(buf);
getstr( buf, MAX_STRING, &d ); getstr( buf, MAX_STRING, &d );
server->server_name = strdup(buf); server->server_name = strdup(buf);
return DONE_FORCE; return DONE_FORCE;
} }

View file

@ -1,2 +1,2 @@
This is the doc for some descent 3 protocol that ISNT the gamespy This is the doc for some descent 3 protocol that ISNT the gamespy
protocol. Not really sure what it's for. protocol. Not really sure what it's for.

View file

@ -1,223 +1,223 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * 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. */ /* for some reason Descent3 uses a different request for pxo/non-pxo games. blah. */
unsigned char descent3_pxoinfoquery[] = { unsigned char descent3_pxoinfoquery[] = {
0x01, /* "internal descent3 routing" */ 0x01, /* "internal descent3 routing" */
0x29, /* request server info? (pxo listed servers) */ 0x29, /* request server info? (pxo listed servers) */
0x0b, 0x00, /* packet length (- routing byte) */ 0x0b, 0x00, /* packet length (- routing byte) */
0x1b, 0x2f, 0xf4, 0x41, 0x09, 0x00, 0x00, 0x00 /* unknown */ 0x1b, 0x2f, 0xf4, 0x41, 0x09, 0x00, 0x00, 0x00 /* unknown */
}; };
unsigned char descent3_tcpipinfoquery[] = { unsigned char descent3_tcpipinfoquery[] = {
0x01, /* "internal descent3 routing" */ 0x01, /* "internal descent3 routing" */
0x1e, /* request server info? (tcpip only servers) */ 0x1e, /* request server info? (tcpip only servers) */
0x0b, 0x00, /* packet length (- routing byte) */ 0x0b, 0x00, /* packet length (- routing byte) */
0x1b, 0x2f, 0xf4, 0x41, 0x09, 0x00, 0x00, 0x00 /* unknown */ 0x1b, 0x2f, 0xf4, 0x41, 0x09, 0x00, 0x00, 0x00 /* unknown */
}; };
/* http://ml.warpcore.org/d3dl/200101/msg00001.html /* http://ml.warpcore.org/d3dl/200101/msg00001.html
* http://ml.warpcore.org/d3dl/200101/msg00004.html */ * http://ml.warpcore.org/d3dl/200101/msg00004.html */
unsigned char descent3_playerquery[] = { unsigned char descent3_playerquery[] = {
0x01, /* "internal descent3 routing" */ 0x01, /* "internal descent3 routing" */
0x72, /* MP_REQUEST_PLAYERLIST */ 0x72, /* MP_REQUEST_PLAYERLIST */
0x03, 0x00 /* packet length (- routing byte) */ 0x03, 0x00 /* packet length (- routing byte) */
}; };
{ {
/* DESCENT3 PROTOCOL */ /* DESCENT3 PROTOCOL */
DESCENT3_SERVER, /* id */ DESCENT3_SERVER, /* id */
"D3S", /* type_prefix */ "D3S", /* type_prefix */
"d3s", /* type_string */ "d3s", /* type_string */
"-d3s", /* type_option */ "-d3s", /* type_option */
"Descent3", /* game_name */ "Descent3", /* game_name */
0, /* master */ 0, /* master */
DESCENT3_DEFAULT_PORT, /* default_port */ DESCENT3_DEFAULT_PORT, /* default_port */
0, /* port_offset */ 0, /* port_offset */
0, /* flags */ 0, /* flags */
"gametype", /* game_rule */ "gametype", /* game_rule */
"DESCENT3", /* template_var */ "DESCENT3", /* template_var */
(char*) &descent3_tcpipinfoquery, /* status_packet */ (char*) &descent3_tcpipinfoquery, /* status_packet */
sizeof( descent3_tcpipinfoquery), /* status_len */ sizeof( descent3_tcpipinfoquery), /* status_len */
(char*) &descent3_playerquery, /* player_packet */ (char*) &descent3_playerquery, /* player_packet */
sizeof( descent3_playerquery), /* player_len */ sizeof( descent3_playerquery), /* player_len */
NULL, /* rule_packet */ NULL, /* rule_packet */
0, /* rule_len */ 0, /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
display_descent3_player_info, /* display_player_func */ display_descent3_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
raw_display_descent3_player_info, /* display_raw_player_func */ raw_display_descent3_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_descent3_player_info, /* display_xml_player_func */ xml_display_descent3_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_gps_request_packet, /* status_query_func */ send_gps_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_descent3_packet, /* packet_func */ deal_with_descent3_packet, /* packet_func */
}, },
{ {
/* DESCENT3 PROTOCOL */ /* DESCENT3 PROTOCOL */
DESCENT3_PXO_SERVER, /* id */ DESCENT3_PXO_SERVER, /* id */
"D3P", /* type_prefix */ "D3P", /* type_prefix */
"d3p", /* type_string */ "d3p", /* type_string */
"-d3p", /* type_option */ "-d3p", /* type_option */
"Descent3 PXO protocol", /* game_name */ "Descent3 PXO protocol", /* game_name */
0, /* master */ 0, /* master */
DESCENT3_DEFAULT_PORT, /* default_port */ DESCENT3_DEFAULT_PORT, /* default_port */
0, /* port_offset */ 0, /* port_offset */
0, /* flags */ 0, /* flags */
"gametype", /* game_rule */ "gametype", /* game_rule */
"DESCENT3", /* template_var */ "DESCENT3", /* template_var */
(char*) &descent3_pxoinfoquery, /* status_packet */ (char*) &descent3_pxoinfoquery, /* status_packet */
sizeof( descent3_pxoinfoquery), /* status_len */ sizeof( descent3_pxoinfoquery), /* status_len */
(char*) &descent3_playerquery, /* player_packet */ (char*) &descent3_playerquery, /* player_packet */
sizeof( descent3_playerquery), /* player_len */ sizeof( descent3_playerquery), /* player_len */
NULL, /* rule_packet */ NULL, /* rule_packet */
0, /* rule_len */ 0, /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
display_descent3_player_info, /* display_player_func */ display_descent3_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
raw_display_descent3_player_info, /* display_raw_player_func */ raw_display_descent3_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_descent3_player_info, /* display_xml_player_func */ xml_display_descent3_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_gps_request_packet, /* status_query_func */ send_gps_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_descent3_packet, /* packet_func */ deal_with_descent3_packet, /* packet_func */
}, },
query_status_t deal_with_descent3_packet(struct qserver *server, char *rawpkt, int pktlen) query_status_t deal_with_descent3_packet(struct qserver *server, char *rawpkt, int pktlen)
{ {
char *pkt; char *pkt;
char buf[24]; char buf[24];
debug( 2, "deal_with_descent3_packet %p, %d", server, pktlen ); debug( 2, "deal_with_descent3_packet %p, %d", server, pktlen );
if (server->server_name == NULL) if (server->server_name == NULL)
{ {
server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
} }
if (pktlen < 4) if (pktlen < 4)
{ {
fprintf(stderr, "short descent3 packet\n"); fprintf(stderr, "short descent3 packet\n");
print_packet(server, rawpkt, pktlen); print_packet(server, rawpkt, pktlen);
return PKT_ERROR; return PKT_ERROR;
} }
/* 'info' response */ /* 'info' response */
if (rawpkt[1] == 0x1f) if (rawpkt[1] == 0x1f)
{ {
if (server->server_name != NULL) if (server->server_name != NULL)
{ {
return PKT_ERROR; return PKT_ERROR;
} }
pkt = &rawpkt[0x15]; pkt = &rawpkt[0x15];
server->server_name = strdup(pkt); server->server_name = strdup(pkt);
pkt += strlen(pkt) + 2; pkt += strlen(pkt) + 2;
server->map_name = strdup(pkt); /* mission name (blah.mn3) */ server->map_name = strdup(pkt); /* mission name (blah.mn3) */
pkt += strlen(pkt) + 2; pkt += strlen(pkt) + 2;
add_rule(server, "level_name", pkt, NO_FLAGS); add_rule(server, "level_name", pkt, NO_FLAGS);
pkt += strlen(pkt) + 2; pkt += strlen(pkt) + 2;
add_rule(server, "gametype", pkt, NO_FLAGS); add_rule(server, "gametype", pkt, NO_FLAGS);
pkt += strlen(pkt) + 1; pkt += strlen(pkt) + 1;
sprintf(buf, "%hu", swap_short_from_little(pkt)); sprintf(buf, "%hu", swap_short_from_little(pkt));
add_rule(server, "level_num", buf, NO_FLAGS); add_rule(server, "level_num", buf, NO_FLAGS);
pkt += 2; pkt += 2;
server->num_players = swap_short_from_little(pkt); server->num_players = swap_short_from_little(pkt);
pkt += 2; pkt += 2;
server->max_players = swap_short_from_little(pkt); server->max_players = swap_short_from_little(pkt);
pkt += 2; pkt += 2;
/* unknown/undecoded fields.. stuff like permissible, banned items/ships, etc */ /* unknown/undecoded fields.. stuff like permissible, banned items/ships, etc */
add_uchar_rule(server, "u0", pkt[0]); add_uchar_rule(server, "u0", pkt[0]);
add_uchar_rule(server, "u1", pkt[1]); add_uchar_rule(server, "u1", pkt[1]);
add_uchar_rule(server, "u2", pkt[2]); add_uchar_rule(server, "u2", pkt[2]);
add_uchar_rule(server, "u3", pkt[3]); add_uchar_rule(server, "u3", pkt[3]);
add_uchar_rule(server, "u4", pkt[4]); add_uchar_rule(server, "u4", pkt[4]);
add_uchar_rule(server, "u5", pkt[5]); add_uchar_rule(server, "u5", pkt[5]);
add_uchar_rule(server, "u6", pkt[6]); add_uchar_rule(server, "u6", pkt[6]);
add_uchar_rule(server, "u7", pkt[7]); add_uchar_rule(server, "u7", pkt[7]);
add_uchar_rule(server, "u8", pkt[8]); add_uchar_rule(server, "u8", pkt[8]);
add_uchar_rule(server, "randpowerup", (unsigned char)!(pkt[4] &1)); /* add_uchar_rule(server, "randpowerup", (unsigned char)!(pkt[4] &1)); /*
randomize powerup spawn */ randomize powerup spawn */
add_uchar_rule(server, "acccollisions", (unsigned char)((pkt[5] &4) > 0)); add_uchar_rule(server, "acccollisions", (unsigned char)((pkt[5] &4) > 0));
/* accurate collision detection */ /* accurate collision detection */
add_uchar_rule(server, "brightships", (unsigned char)((pkt[5] &16) > 0)); add_uchar_rule(server, "brightships", (unsigned char)((pkt[5] &16) > 0));
/* bright player ships */ /* bright player ships */
add_uchar_rule(server, "mouselook", (unsigned char)((pkt[6] &1) > 0)); /* add_uchar_rule(server, "mouselook", (unsigned char)((pkt[6] &1) > 0)); /*
mouselook enabled */ mouselook enabled */
sprintf(buf, "%s%s", (pkt[4] &16) ? "PP" : "CS", (pkt[6] &1) ? "-ML" : ""); sprintf(buf, "%s%s", (pkt[4] &16) ? "PP" : "CS", (pkt[6] &1) ? "-ML" : "");
add_rule(server, "servertype", buf, NO_FLAGS); add_rule(server, "servertype", buf, NO_FLAGS);
sprintf(buf, "%hhu", pkt[9]); sprintf(buf, "%hhu", pkt[9]);
add_rule(server, "difficulty", buf, NO_FLAGS); add_rule(server, "difficulty", buf, NO_FLAGS);
/* unknown/undecoded fields after known flags removed */ /* unknown/undecoded fields after known flags removed */
add_uchar_rule(server, "x4", (unsigned char)(pkt[4] &~(1+16))); 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, "x5", (unsigned char)(pkt[5] &~(4+16)));
add_uchar_rule(server, "x6", (unsigned char)(pkt[6] &~1)); add_uchar_rule(server, "x6", (unsigned char)(pkt[6] &~1));
if (get_player_info && server->num_players) if (get_player_info && server->num_players)
{ {
server->next_player_info = 0; server->next_player_info = 0;
send_player_request_packet(server); send_player_request_packet(server);
return INPROGRESS; return INPROGRESS;
} }
} }
/* MP_PLAYERLIST_DATA */ /* MP_PLAYERLIST_DATA */
else if (rawpkt[1] == 0x73) else if (rawpkt[1] == 0x73)
{ {
struct player *player; struct player *player;
struct player **last_player = &server->players; struct player **last_player = &server->players;
if (server->players != NULL) if (server->players != NULL)
{ {
return PKT_ERROR; return PKT_ERROR;
} }
pkt = &rawpkt[0x4]; pkt = &rawpkt[0x4];
while (*pkt) while (*pkt)
{ {
player = (struct player*)calloc(1, sizeof(struct player)); player = (struct player*)calloc(1, sizeof(struct player));
player->name = strdup(pkt); player->name = strdup(pkt);
pkt += strlen(pkt) + 1; pkt += strlen(pkt) + 1;
*last_player = player; *last_player = player;
last_player = &player->next; last_player = &player->next;
} }
server->next_player_info = NO_PLAYER_INFO; server->next_player_info = NO_PLAYER_INFO;
} }
else else
{ {
fprintf(stderr, "unknown d3 packet\n"); fprintf(stderr, "unknown d3 packet\n");
print_packet(server, rawpkt, pktlen); print_packet(server, rawpkt, pktlen);
} }
return DONE_FORCE; return DONE_FORCE;
} }

View file

@ -1,2 +1,2 @@
I was under the impression farcry uses ASE. I was under the impression farcry uses ASE.
If anyone ever has issues, this is the doc for some old farcry query protocol. If anyone ever has issues, this is the doc for some old farcry query protocol.

View file

@ -1,261 +1,261 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski
*/ */
unsigned char farcry_serverquery[] = { unsigned char farcry_serverquery[] = {
0x08,0x80 0x08,0x80
}; };
{ {
/* FARCRY PROTOCOL */ /* FARCRY PROTOCOL */
FARCRY_SERVER, /* id */ FARCRY_SERVER, /* id */
"FCS", /* type_prefix */ "FCS", /* type_prefix */
"fcs", /* type_string */ "fcs", /* type_string */
"-fcs", /* type_option */ "-fcs", /* type_option */
"FarCry", /* game_name */ "FarCry", /* game_name */
0, /* master */ 0, /* master */
FARCRY_DEFAULT_PORT, /* default_port */ FARCRY_DEFAULT_PORT, /* default_port */
0, /* port_offset */ 0, /* port_offset */
TF_QUERY_ARG, /* flags */ TF_QUERY_ARG, /* flags */
"gametype", /* game_rule */ "gametype", /* game_rule */
"FARCRY", /* template_var */ "FARCRY", /* template_var */
(char*)farcry_serverquery, /* status_packet */ (char*)farcry_serverquery, /* status_packet */
sizeof( savage_serverquery ) - 1, /* status_len */ sizeof( savage_serverquery ) - 1, /* status_len */
NULL, /* player_packet */ NULL, /* player_packet */
0, /* player_len */ 0, /* player_len */
NULL, /* rule_packet */ NULL, /* rule_packet */
0, /* rule_len */ 0, /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
display_farcry_player_info, /* display_player_func */ display_farcry_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
raw_display_farcry_player_info, /* display_raw_player_func */ raw_display_farcry_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_farcry_player_info, /* display_xml_player_func */ xml_display_farcry_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_farcry_request_packet, /* status_query_func */ send_farcry_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_farcry_packet, /* packet_func */ deal_with_farcry_packet, /* packet_func */
}, },
query_status_t send_farcry_request_packet(struct qserver *server) query_status_t send_farcry_request_packet(struct qserver *server)
{ {
int len; int len;
char *pkt; char *pkt;
if (get_player_info) if (get_player_info)
{ {
pkt = server->type->player_packet; pkt = server->type->player_packet;
len = server->type->player_len; len = server->type->player_len;
} }
else else
{ {
pkt = server->type->status_packet; pkt = server->type->status_packet;
len = server->type->status_len; len = server->type->status_len;
} }
return send_packet(server, pkt, len); return send_packet(server, pkt, len);
} }
query_status_t deal_with_farcry_packet(struct qserver *server, char *rawpkt, int pktlen) query_status_t deal_with_farcry_packet(struct qserver *server, char *rawpkt, int pktlen)
{ {
char *s, *key, *value, *end; char *s, *key, *value, *end;
debug( 2, "deal_with_farcry_packet %p, %d", server, pktlen ); debug( 2, "deal_with_farcry_packet %p, %d", server, pktlen );
server->n_servers++; server->n_servers++;
if (NULL == server->server_name) if (NULL == server->server_name)
{ {
server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
} }
else else
{ {
gettimeofday(&server->packet_time1, NULL); gettimeofday(&server->packet_time1, NULL);
} }
rawpkt[pktlen] = '\0'; rawpkt[pktlen] = '\0';
end = s = rawpkt; end = s = rawpkt;
end += pktlen; end += pktlen;
while (*s) while (*s)
{ {
// Find the seperator // Find the seperator
while (s <= end && *s != '\xFF') while (s <= end && *s != '\xFF')
{ {
s++; s++;
} }
if (s >= end) if (s >= end)
{ {
// Hit the end no more // Hit the end no more
break; break;
} }
// key start // key start
key = ++s; key = ++s;
while (s < end && *s != '\xFE') while (s < end && *s != '\xFE')
{ {
s++; s++;
} }
if (*s != '\xFE') if (*s != '\xFE')
{ {
// malformed // malformed
break; break;
} }
*s++ = '\0'; *s++ = '\0';
// key end // key end
// value start // value start
value = s; value = s;
while (s < end && *s != '\xFF') while (s < end && *s != '\xFF')
{ {
s++; s++;
} }
if (*s == '\xFF') if (*s == '\xFF')
{ {
*s = '\0'; *s = '\0';
} }
//fprintf( stderr, "'%s' = '%s'\n", key, value ); //fprintf( stderr, "'%s' = '%s'\n", key, value );
// Decode current key par // Decode current key par
if (0 == strcmp("cmax", key)) if (0 == strcmp("cmax", key))
{ {
// Max players // Max players
server->max_players = atoi(value); server->max_players = atoi(value);
} }
else if (0 == strcmp("cnum", key)) else if (0 == strcmp("cnum", key))
{ {
// Current players // Current players
server->num_players = atoi(value); server->num_players = atoi(value);
} }
else if (0 == strcmp("bal", key)) else if (0 == strcmp("bal", key))
{ {
// Balance // Balance
add_rule(server, "Balance", value, NO_FLAGS); add_rule(server, "Balance", value, NO_FLAGS);
} }
else if (0 == strcmp("world", key)) else if (0 == strcmp("world", key))
{ {
// Current map // Current map
server->map_name = strdup(value); server->map_name = strdup(value);
} }
else if (0 == strcmp("gametype", key)) else if (0 == strcmp("gametype", key))
{ {
// Game type // Game type
server->game = find_savage_game(value); server->game = find_savage_game(value);
add_rule(server, server->type->game_rule, server->game, NO_FLAGS); add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
} }
else if (0 == strcmp("pure", key)) else if (0 == strcmp("pure", key))
{ {
// Pure // Pure
add_rule(server, "Pure", value, NO_FLAGS); add_rule(server, "Pure", value, NO_FLAGS);
} }
else if (0 == strcmp("time", key)) else if (0 == strcmp("time", key))
{ {
// Current game time // Current game time
add_rule(server, "Time", value, NO_FLAGS); add_rule(server, "Time", value, NO_FLAGS);
} }
else if (0 == strcmp("notes", key)) else if (0 == strcmp("notes", key))
{ {
// Notes // Notes
add_rule(server, "Notes", value, NO_FLAGS); add_rule(server, "Notes", value, NO_FLAGS);
} }
else if (0 == strcmp("needcmdr", key)) else if (0 == strcmp("needcmdr", key))
{ {
// Need Commander // Need Commander
add_rule(server, "Need Commander", value, NO_FLAGS); add_rule(server, "Need Commander", value, NO_FLAGS);
} }
else if (0 == strcmp("name", key)) else if (0 == strcmp("name", key))
{ {
// Server name // Server name
server->server_name = strdup(value); server->server_name = strdup(value);
} }
else if (0 == strcmp("fw", key)) else if (0 == strcmp("fw", key))
{ {
// Firewalled // Firewalled
add_rule(server, "Firewalled", value, NO_FLAGS); add_rule(server, "Firewalled", value, NO_FLAGS);
} }
else if (0 == strcmp("players", key)) else if (0 == strcmp("players", key))
{ {
// Players names // Players names
int player_number = 0; int player_number = 0;
int team_number = 1; int team_number = 1;
char *team_name, *player_name, *n; char *team_name, *player_name, *n;
n = team_name = value; n = team_name = value;
// team name // team name
n++; n++;
while (*n && *n != '\x0a') while (*n && *n != '\x0a')
{ {
n++; n++;
} }
if (*n != '\x0a') if (*n != '\x0a')
{ {
// Broken data // Broken data
break; break;
} }
*n = '\0'; *n = '\0';
player_name = ++n; player_name = ++n;
while (*n) while (*n)
{ {
while (*n && *n != '\x0a') while (*n && *n != '\x0a')
{ {
n++; n++;
} }
if (*n != '\x0a') if (*n != '\x0a')
{ {
// Broken data // Broken data
break; break;
} }
*n = '\0'; *n = '\0';
n++; n++;
if (0 == strncmp("Team ", player_name, 5)) if (0 == strncmp("Team ", player_name, 5))
{ {
team_name = player_name; team_name = player_name;
team_number++; team_number++;
} }
else else
{ {
if (0 != strlen(player_name)) if (0 != strlen(player_name))
{ {
struct player *player = add_player(server, player_number); struct player *player = add_player(server, player_number);
if (NULL != player) if (NULL != player)
{ {
player->name = strdup(player_name); player->name = strdup(player_name);
player->team = team_number; player->team = team_number;
player->team_name = strdup(team_name); player->team_name = strdup(team_name);
} player_number++; } player_number++;
} }
} }
player_name = n; player_name = n;
} }
} }
*s = '\xFF'; *s = '\xFF';
} }
return DONE_FORCE; return DONE_FORCE;
} }

View file

@ -1,57 +1,57 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: freelancer.php,v 1.2 2008/02/22 13:33:40 tombuskens Exp $ * $Id: freelancer.php,v 1.2 2008/02/22 13:33:40 tombuskens Exp $
*/ */
[freelancer] [freelancer]
status = "\x00\x02\xf1\x26\x01\x26\xf0\x90\xa6\xf0\x26\x57\x4e\xac\xa0\xec\xf8\x68\xe4\x8d\x21" 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'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Freelancer protocol * Freelancer protocol
* UNTESTED * UNTESTED
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.2 $ * @version $Revision: 1.2 $
*/ */
class GameQ_Protocol_freelancer extends GameQ_Protocol class GameQ_Protocol_freelancer extends GameQ_Protocol
{ {
/* /*
* status packet * status packet
*/ */
public function status() public function status()
{ {
// Server name length @ 3 // Server name length @ 3
$this->p->skip(3); $this->p->skip(3);
$name_length = $this->p->readInt8() - 90; $name_length = $this->p->readInt8() - 90;
// Max players @ 20 // Max players @ 20
$this->p->skip(17); $this->p->skip(17);
$this->r->add('max_players', $this->p->readInt8() - 1); $this->r->add('max_players', $this->p->readInt8() - 1);
// Num players @ 24 // Num players @ 24
$this->p->skip(3); $this->p->skip(3);
$this->r->add('num_players', $this->p->readInt8() - 1); $this->r->add('num_players', $this->p->readInt8() - 1);
// Servername @ 91 // Servername @ 91
$this->p->skip(66); $this->p->skip(66);
$this->r->add('servername', $this->p->read($name_length)); $this->r->add('servername', $this->p->read($name_length));
} }
} }
?> ?>

View file

@ -1,58 +1,58 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_14(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
// REFERENCE: http://flstat.cryosphere.co.uk/global-list.php // 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"); 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); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
//---------------------------------------------------------+ //---------------------------------------------------------+
$buffer = substr($buffer, 4); // HEADER ( 00 03 F1 26 ) $buffer = substr($buffer, 4); // HEADER ( 00 03 F1 26 )
$buffer = substr($buffer, 4); // NOT USED ( 87 + NAME LENGTH ) $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); // NOT USED ( NAME END TO BUFFER END LENGTH )
$buffer = substr($buffer, 4); // UNKNOWN ( 80 ) $buffer = substr($buffer, 4); // UNKNOWN ( 80 )
$server['s']['map'] = "freelancer"; $server['s']['map'] = "freelancer";
$server['s']['password'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1 ? 1 : 0; $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']['playersmax'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1;
$server['s']['players'] = 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 ) $buffer = substr($buffer, 4); // UNKNOWN ( 88 )
$name_length = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l"); $name_length = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l");
$buffer = substr($buffer, 56); // UNKNOWN $buffer = substr($buffer, 56); // UNKNOWN
$server['s']['name'] = lgsl_cut_byte($buffer, $name_length); $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, ":"); 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 // WHATS LEFT IS THE MOTD
$server['e']['motd'] = substr($buffer, 0, -1); $server['e']['motd'] = substr($buffer, 0, -1);
// REMOVE UTF-8 ENCODING NULLS // REMOVE UTF-8 ENCODING NULLS
$server['s']['name'] = str_replace("\x00", "", $server['s']['name']); $server['s']['name'] = str_replace("\x00", "", $server['s']['name']);
$server['e']['motd'] = str_replace("\x00", "", $server['e']['motd']); $server['e']['motd'] = str_replace("\x00", "", $server['e']['motd']);
// DOES NOT RETURN PLAYER INFORMATION // DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,64 +1,64 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: ghostrecon.php,v 1.1 2007/07/02 10:14:32 tombuskens Exp $ * $Id: ghostrecon.php,v 1.1 2007/07/02 10:14:32 tombuskens Exp $
*/ */
[ghostrecon] [ghostrecon]
status = "\xc0\xde\xf1\x11\x42\x06\x00\xf5\x03\x00\x78\x30\x63" status = "\xc0\xde\xf1\x11\x42\x06\x00\xf5\x03\x00\x78\x30\x63"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Ghost Recon protocol * Ghost Recon protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.1 $ * @version $Revision: 1.1 $
*/ */
class GameQ_Protocol_ghostrecon extends GameQ_Protocol class GameQ_Protocol_ghostrecon extends GameQ_Protocol
{ {
/* /*
* Status * Status
*/ */
public function status() public function status()
{ {
// Unknown // Unknown
$this->p->skip(25); $this->p->skip(25);
$this->r->add('servername', $this->readGhostString()); $this->r->add('servername', $this->readGhostString());
$this->r->add('map', $this->readGhostString()); $this->r->add('map', $this->readGhostString());
$this->r->add('mission', $this->readGhostString()); $this->r->add('mission', $this->readGhostString());
$this->r->add('gametype', $this->readGhostString()); $this->r->add('gametype', $this->readGhostString());
} }
/** /**
* Read a Ghost Recon string * Read a Ghost Recon string
* *
* @return string The string * @return string The string
*/ */
private function readGhostString() private function readGhostString()
{ {
if ($this->p->getLength() < 4) return ''; if ($this->p->getLength() < 4) return '';
$this->p->skip(4); $this->p->skip(4);
return $this->p->readString(); return $this->p->readString();
} }
} }
?> ?>

View file

@ -1,133 +1,133 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) 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"); fwrite($lgsl_fp, "\xC0\xDE\xF1\x11\x42\x06\x00\xF5\x03\x21\x21\x21\x21");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
//---------------------------------------------------------+ //---------------------------------------------------------+
$buffer = substr($buffer, 25); // REMOVE HEADER $buffer = substr($buffer, 25); // REMOVE HEADER
$server['s']['name'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); $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['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']['nextmap'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['gametype'] = 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); $buffer = substr($buffer, 1);
$server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 4)); $server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 4));
$server['s']['players'] = 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++) 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)); $server['p'][$player_key]['name'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
$buffer = substr($buffer, 17); $buffer = substr($buffer, 17);
$server['e']['version'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); $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']['mods'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['time'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); $server['e']['time'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['status'] = ord(lgsl_cut_byte($buffer, 4)); $server['e']['status'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['gamemode'] = 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']['motd'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['respawns'] = ord(lgsl_cut_byte($buffer, 4)); $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']['time_limit'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['voting'] = ord(lgsl_cut_byte($buffer, 4)); $server['e']['voting'] = ord(lgsl_cut_byte($buffer, 4));
$buffer = substr($buffer, 2); $buffer = substr($buffer, 2);
//---------------------------------------------------------+ //---------------------------------------------------------+
for ($player_key=0; $player_key<$server['s']['players']; $player_key++) for ($player_key=0; $player_key<$server['s']['players']; $player_key++)
{ {
$server['p'][$player_key]['team'] = ord(lgsl_cut_byte($buffer, 4)); $server['p'][$player_key]['team'] = ord(lgsl_cut_byte($buffer, 4));
$unknown = ord(lgsl_cut_byte($buffer, 1)); $unknown = ord(lgsl_cut_byte($buffer, 1));
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
$buffer = substr($buffer, 7); $buffer = substr($buffer, 7);
$server['e']['platoon_1_color'] = ord(lgsl_cut_byte($buffer, 8)); $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_2_color'] = ord(lgsl_cut_byte($buffer, 8));
$server['e']['platoon_3_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']['platoon_4_color'] = ord(lgsl_cut_byte($buffer, 8));
$server['e']['timer_on'] = ord(lgsl_cut_byte($buffer, 1)); $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']['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_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_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_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']['time_respawn_safe'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['difficulty'] = ord(lgsl_cut_byte($buffer, 4)); $server['e']['difficulty'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['respawn_total'] = 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']['random_insertions'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['spectators'] = 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']['arcademode'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['ai_backup'] = 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']['random_teams'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['time_starting'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f")); $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_friends'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['identify_threats'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['identify_threats'] = ord(lgsl_cut_byte($buffer, 1));
$buffer = substr($buffer, 5); $buffer = substr($buffer, 5);
$server['e']['restrictions'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3)); $server['e']['restrictions'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
//---------------------------------------------------------+ //---------------------------------------------------------+
switch ($server['e']['status']) switch ($server['e']['status'])
{ {
case 3: $server['e']['status'] = "Joining"; break; case 3: $server['e']['status'] = "Joining"; break;
case 4: $server['e']['status'] = "Joining"; break; case 4: $server['e']['status'] = "Joining"; break;
case 5: $server['e']['status'] = "Joining"; break; case 5: $server['e']['status'] = "Joining"; break;
} }
switch ($server['e']['gamemode']) switch ($server['e']['gamemode'])
{ {
case 2: $server['e']['gamemode'] = "Co-Op"; break; case 2: $server['e']['gamemode'] = "Co-Op"; break;
case 3: $server['e']['gamemode'] = "Solo"; break; case 3: $server['e']['gamemode'] = "Solo"; break;
case 4: $server['e']['gamemode'] = "Team"; break; case 4: $server['e']['gamemode'] = "Team"; break;
} }
switch ($server['e']['respawns']) switch ($server['e']['respawns'])
{ {
case 0: $server['e']['respawns'] = "None"; break; case 0: $server['e']['respawns'] = "None"; break;
case 1: $server['e']['respawns'] = "Individual"; break; case 1: $server['e']['respawns'] = "Individual"; break;
case 2: $server['e']['respawns'] = "Team"; break; case 2: $server['e']['respawns'] = "Team"; break;
case 3: $server['e']['respawns'] = "Infinite"; break; case 3: $server['e']['respawns'] = "Infinite"; break;
} }
switch ($server['e']['difficulty']) switch ($server['e']['difficulty'])
{ {
case 0: $server['e']['difficulty'] = "Recruit"; break; case 0: $server['e']['difficulty'] = "Recruit"; break;
case 1: $server['e']['difficulty'] = "Veteran"; break; case 1: $server['e']['difficulty'] = "Veteran"; break;
case 2: $server['e']['difficulty'] = "Elite"; break; case 2: $server['e']['difficulty'] = "Elite"; break;
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

File diff suppressed because it is too large Load diff

View file

@ -1,229 +1,229 @@
/********* /*********
QStat - Real-time game server stats QStat - Real-time game server stats
http://sourceforge.net/p/qstat/ http://sourceforge.net/p/qstat/
License: The Artistic License 2.0 License: The Artistic License 2.0
*********/ *********/
Ghost Recon - QStat notes Ghost Recon - QStat notes
------------------------- -------------------------
The following Server Stats are pulled from the Ghost Recon Server - NOTE 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. many other stats continue to work as normal due to the base qstat program.
$SERVERNAME $SERVERNAME
The name of the GR Server. The name of the GR Server.
$PLAYERS $PLAYERS
The number of Players that are playing, oberving or in the Lobby The number of Players that are playing, oberving or in the Lobby
(note the ignoreserverplayer Argument above) (note the ignoreserverplayer Argument above)
$MAXPLAYERS $MAXPLAYERS
The maximum players that the server will allow playing, oberving The maximum players that the server will allow playing, oberving
or in the Lobby (note the ignoreserverplayer Argument above) or in the Lobby (note the ignoreserverplayer Argument above)
$MAP $MAP
The Name of the MAP that is being used (NOTE not the Mission) The Name of the MAP that is being used (NOTE not the Mission)
$GAME $GAME
The Mods that the server is running. Ex: mp1; is the Desert The Mods that the server is running. Ex: mp1; is the Desert
Seige Mod Seige Mod
$(RULE:error) $(RULE:error)
If an error occured there may be some detail here. IF the problm If an error occured there may be some detail here. IF the problm
occurred very early in the interpretation then $SERVERNAME will occurred very early in the interpretation then $SERVERNAME will
hold the details. hold the details.
$(RULE:mission) $(RULE:mission)
The name of the Mission that the server is running. The name of the Mission that the server is running.
$(RULE:gamemode) $(RULE:gamemode)
What is the Game Mode that the server is in. Known values are What is the Game Mode that the server is in. Known values are
COOP, TEAM and SOLO COOP, TEAM and SOLO
$(RULE:missiontype) $(RULE:missiontype)
What is the Mission Type. Known Values are: Mission, Firefight, What is the Mission Type. Known Values are: Mission, Firefight,
Recon, Hamburger Hill, Last Man Standing, Sharpshooter, Search Recon, Hamburger Hill, Last Man Standing, Sharpshooter, Search
And Rescue, Domination, and Seige. And Rescue, Domination, and Seige.
$(RULE:dedicated) $(RULE:dedicated)
Is this server Dedicated; Yes or No. Is this server Dedicated; Yes or No.
$(RULE:status) $(RULE:status)
What is the Playing Status of the Server, values are Playing, What is the Playing Status of the Server, values are Playing,
Joining or Debrief. Joining or Debrief.
$(RULE:gametime) $(RULE:gametime)
What is the Time limit for the Game. Values are 00:00, 05:00, 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 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, is for an unlimited game. The format of this uses the -ts,
-tc and -tsw command line options. -tc and -tsw command line options.
$(RULE:timeplayed) $(RULE:timeplayed)
How long has this game been playing. The format of this uses How long has this game been playing. The format of this uses
the -ts, -tc and -tsw command line options. the -ts, -tc and -tsw command line options.
$(RULE:remainingtime) $(RULE:remainingtime)
How much time is left in this game. The format of this uses How much time is left in this game. The format of this uses
the -ts, -tc and -tsw command line options. the -ts, -tc and -tsw command line options.
$(RULE:version) $(RULE:version)
What is the Version number reported by the server. Patch 1.2 = What is the Version number reported by the server. Patch 1.2 =
10.1010A, Patch 1.3 = 11.101A 10.1010A, Patch 1.3 = 11.101A
$(RULE:spawntype) $(RULE:spawntype)
What type of spawn is in use. Known Values are None, Infinite, What type of spawn is in use. Known Values are None, Infinite,
Individual and Team. Individual and Team.
$(RULE:spawncount) $(RULE:spawncount)
How many spawns are allowed. Enhancment possibility to add How many spawns are allowed. Enhancment possibility to add
$(IF:SPAWN) to filter out when spawntype is none. $(IF:SPAWN) to filter out when spawntype is none.
$(RULE:restrict) $(RULE:restrict)
What Weapon restrictions are in force for the server. What Weapon restrictions are in force for the server.
$(RULE:password) $(RULE:password)
Does the Server have a join password defined Yes or No. Does the Server have a join password defined Yes or No.
$(RULE:ti) $(RULE:ti)
Is the server using the Threat Indicator. Is the server using the Threat Indicator.
$(RULE:motd) $(RULE:motd)
What is the Message Of The Day - Note these can be quite big. What is the Message Of The Day - Note these can be quite big.
$(RULE:patch) $(RULE:patch)
What is the patch level of the GR Server. What is the patch level of the GR Server.
$(RULE:usestarttime) $(RULE:usestarttime)
Is the server configured to start a game after "starttimeset" Is the server configured to start a game after "starttimeset"
(Yes) OR does everyone need to click on ready (no). (Yes) OR does everyone need to click on ready (no).
$(RULE:starttimeset) $(RULE:starttimeset)
What time is configured to automatically start the next round. What time is configured to automatically start the next round.
$(RULE:debrieftime) $(RULE:debrieftime)
How long does the server wait at the Debrief screen after How long does the server wait at the Debrief screen after
a mission. a mission.
$(RULE:respawnmin) $(RULE:respawnmin)
How long must a dead player wait before he can repawn. How long must a dead player wait before he can repawn.
$(RULE:respawnmax) $(RULE:respawnmax)
What is the longest time that a user has to respawn. What is the longest time that a user has to respawn.
$(RULE:respawnsafe) $(RULE:respawnsafe)
How long after respawn is a player invulnerable/cannot damage How long after respawn is a player invulnerable/cannot damage
others. others.
$(RULE:allowobservers) $(RULE:allowobservers)
Does the server allow observers? Yes or No Does the server allow observers? Yes or No
$(RULE:startwait) $(RULE:startwait)
How long untill the automatic start timer forces the next game How long untill the automatic start timer forces the next game
to start. to start.
$(RULE:iff) $(RULE:iff)
What Identification - Friend or Foe is configured. None, What Identification - Friend or Foe is configured. None,
Reticule or Names Reticule or Names
$PLAYERNAME $PLAYERNAME
What is the Players Name. What is the Players Name.
$TEAMNUM $TEAMNUM
What Team Number is the Player On. Known Values are 1,2,3,4,5. 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 1 is Team BLUE, 2 is Team Read, 3 is Team Yellow, 4 is
Team Green, 5 is Unassigned (observer or in lobby) Team Green, 5 is Unassigned (observer or in lobby)
$TEAMNAME $TEAMNAME
What is the Name of the Team, see above. What is the Name of the Team, see above.
$DEATHS $DEATHS
What is the health of this player. 0 Alive, 1 Dead. Note if the 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. player has spawns remaining this can change from 1 back to 0.
Enhancement possibility to add $HEALTH or $(RULE:health). Enhancement possibility to add $HEALTH or $(RULE:health).
Hopefully RSE/UBI will add the Deaths, Frags, and Ping to the Hopefully RSE/UBI will add the Deaths, Frags, and Ping to the
availible information. If this happens then it would be better availible information. If this happens then it would be better
to have a $HEALTH to have a $HEALTH
$(IF:DEATHS) and $(IFNOT:DEATHS) $(IF:DEATHS) and $(IFNOT:DEATHS)
A Test to see if the player is dead. Usefull in this constuct: A Test to see if the player is dead. Usefull in this constuct:
$(IF:DEATHS)Dead$(ENDIF)$(IFNOT:DEATHS)Alive$(ENDIF) $(IF:DEATHS)Dead$(ENDIF)$(IFNOT:DEATHS)Alive$(ENDIF)
Ghost Recon communicates on two UDP ports and one TCP stream. Normally TCP 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 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 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 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 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 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 is the lack of many meaningful detail player stats (Deaths, frags, hit
percentage, ping etc.). I imagines that some of these are availible in 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 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 The Ghost Recon packets are variable structures with a lot of string
lengths. This requires fairly defensive programming as Red Storm lengths. This requires fairly defensive programming as Red Storm
Entertainment is not forthcoming with any details. Entertainment is not forthcoming with any details.
This release adds support for the GhostRecon game. Number one note This release adds support for the GhostRecon game. Number one note
is that Red Storm and UBI do not provide the information that many is that Red Storm and UBI do not provide the information that many
Quake based users expect. Specifically they do not make Frags, Deaths 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. 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 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 server administrator to make the status of his or her server available
to the public via the web. to the public via the web.
This change uses all undocumented interfaces to the Ghost Recon server 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 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 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 work against the 1.2, 1.3, and 1.4 patches and Island Thunder add-on to
Ghost Recon. Ghost Recon.
The Ghost Recon game type is GRS. For command-line queries, use -grs The Ghost Recon game type is GRS. For command-line queries, use -grs
There is one query argument to this server, ignoreserverplayer. There is one query argument to this server, ignoreserverplayer.
This option controls whether the first player is ignored. Ghost Recon This option controls whether the first player is ignored. Ghost Recon
requires that the dedicated server program take up one of the player slots requires that the dedicated server program take up one of the player slots
(always the first slot). The ignoreserverplayer option defaults to 'yes', (always the first slot). The ignoreserverplayer option defaults to 'yes',
so the "server player" will normally not be seen. If you are running so the "server player" will normally not be seen. If you are running
a non-dedicated server, then set ignoreserverplayer to 'no' like this: a non-dedicated server, then set ignoreserverplayer to 'no' like this:
-grs,ignoreserverplayer=no -grs,ignoreserverplayer=no
Otherwise you would not be able to display your own stats. Otherwise you would not be able to display your own stats.
Ghost Recon support provided by Bob Marriott. Ghost Recon support provided by Bob Marriott.

View file

@ -1,40 +1,40 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_15(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
fwrite($lgsl_fp, "GTR2_Direct_IP_Search\x00"); fwrite($lgsl_fp, "GTR2_Direct_IP_Search\x00");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
//---------------------------------------------------------+ //---------------------------------------------------------+
$buffer = str_replace("\xFE", "\xFF", $buffer); $buffer = str_replace("\xFE", "\xFF", $buffer);
$buffer = explode("\xFF", $buffer); $buffer = explode("\xFF", $buffer);
$server['s']['name'] = $buffer[3]; $server['s']['name'] = $buffer[3];
$server['s']['game'] = $buffer[7]; $server['s']['game'] = $buffer[7];
$server['e']['version'] = $buffer[11]; $server['e']['version'] = $buffer[11];
$server['e']['hostport'] = $buffer[15]; $server['e']['hostport'] = $buffer[15];
$server['s']['map'] = $buffer[19]; $server['s']['map'] = $buffer[19];
$server['s']['players'] = $buffer[25]; $server['s']['players'] = $buffer[25];
$server['s']['playersmax'] = $buffer[27]; $server['s']['playersmax'] = $buffer[27];
$server['e']['gamemode'] = $buffer[31]; $server['e']['gamemode'] = $buffer[31];
// DOES NOT RETURN PLAYER INFORMATION // DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

File diff suppressed because it is too large Load diff

View file

@ -1,52 +1,52 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: hexen2.php,v 1.1 2007/07/11 09:12:31 tombuskens Exp $ * $Id: hexen2.php,v 1.1 2007/07/11 09:12:31 tombuskens Exp $
*/ */
[hexen2] [hexen2]
status = "\x80\x00\x00\x0e\x02HEXENII\x00\x05" status = "\x80\x00\x00\x0e\x02HEXENII\x00\x05"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Hexen 2 protocol * Hexen 2 protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.1 $ * @version $Revision: 1.1 $
*/ */
class GameQ_Protocol_hexen2 extends GameQ_Protocol class GameQ_Protocol_hexen2 extends GameQ_Protocol
{ {
/* /*
* status packet * status packet
*/ */
public function status() public function status()
{ {
// Header? // Header?
$this->p->skip(5); $this->p->skip(5);
$this->r->add('address', $this->p->readString()); $this->r->add('address', $this->p->readString());
$this->r->add('servername', $this->p->readString()); $this->r->add('servername', $this->p->readString());
$this->r->add('map', $this->p->readString()); $this->r->add('map', $this->p->readString());
$this->r->add('num_players', $this->p->readInt8()); $this->r->add('num_players', $this->p->readInt8());
$this->r->add('max_players', $this->p->readInt8()); $this->r->add('max_players', $this->p->readInt8());
$this->p->skip(); // unknown $this->p->skip(); // unknown
} }
} }
?> ?>

View file

@ -1,77 +1,77 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: openttd.php,v 1.1 2009/10/24 18:45:16 evilpie Exp $ * $Id: openttd.php,v 1.1 2009/10/24 18:45:16 evilpie Exp $
*/ */
[openttd] [openttd]
status = "\x03\x00\x00" status = "\x03\x00\x00"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* OpenTTD Protocol, direct port from udp.cpp source from the game * OpenTTD Protocol, direct port from udp.cpp source from the game
* *
* @author Tom Schuster <evilpie@users.sf.net> * @author Tom Schuster <evilpie@users.sf.net>
* @version $Revision: 1.1 $ * @version $Revision: 1.1 $
*/ */
class GameQ_Protocol_openttd extends GameQ_Protocol class GameQ_Protocol_openttd extends GameQ_Protocol
{ {
public function status () public function status ()
{ {
$this->p->readInt16(); # packet size $this->p->readInt16(); # packet size
$this->p->readInt8(); # packet type $this->p->readInt8(); # packet type
$protocol_version = $this->p->readInt8(); $protocol_version = $this->p->readInt8();
$this->r->add('protocol_version', $protocol_version); $this->r->add('protocol_version', $protocol_version);
switch ($protocol_version) switch ($protocol_version)
{ {
case 4: case 4:
$num_grfs = $this->p->readInt8(); #number of grfs $num_grfs = $this->p->readInt8(); #number of grfs
$this->r->add('num_grfs', $num_grfs); $this->r->add('num_grfs', $num_grfs);
$this->p->skip ($num_grfs * 20); #skip id and md5 hash $this->p->skip ($num_grfs * 20); #skip id and md5 hash
case 3: case 3:
$this->r->add('game_date', $this->p->readInt32()); $this->r->add('game_date', $this->p->readInt32());
$this->r->add('start_date', $this->p->readInt32()); $this->r->add('start_date', $this->p->readInt32());
case 2: case 2:
$this->r->add('companies_max', $this->p->readInt8()); $this->r->add('companies_max', $this->p->readInt8());
$this->r->add('companies_on', $this->p->readInt8()); $this->r->add('companies_on', $this->p->readInt8());
$this->r->add('spectators_max', $this->p->readInt8()); $this->r->add('spectators_max', $this->p->readInt8());
case 1: case 1:
$this->r->add('hostname', $this->p->readString()); $this->r->add('hostname', $this->p->readString());
$this->r->add('version', $this->p->readString()); $this->r->add('version', $this->p->readString());
$this->r->add('language', $this->p->readInt8()); $this->r->add('language', $this->p->readInt8());
$this->r->add('password', $this->p->readInt8()); $this->r->add('password', $this->p->readInt8());
$this->r->add('max_clients', $this->p->readInt8()); $this->r->add('max_clients', $this->p->readInt8());
$this->r->add('clients', $this->p->readInt8()); $this->r->add('clients', $this->p->readInt8());
$this->r->add('spectators', $this->p->readInt8()); $this->r->add('spectators', $this->p->readInt8());
if ($protocol_version < 3) if ($protocol_version < 3)
{ {
$days = ( 365 * 1920 + 1920 / 4 - 1920 / 100 + 1920 / 400 ); $days = ( 365 * 1920 + 1920 / 4 - 1920 / 100 + 1920 / 400 );
$this->r->add('game_date', $this->p->readInt16() + $days); $this->r->add('game_date', $this->p->readInt16() + $days);
$this->r->add('start_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', $this->p->readString());
$this->r->add('map_width', $this->p->readInt16()); $this->r->add('map_width', $this->p->readInt16());
$this->r->add('map_height', $this->p->readInt16()); $this->r->add('map_height', $this->p->readInt16());
$this->r->add('map_type', $this->p->readInt8()); $this->r->add('map_type', $this->p->readInt8());
$this->r->add('dedicated', $this->p->readInt8()); $this->r->add('dedicated', $this->p->readInt8());
} }
} }
} }

View file

@ -1,64 +1,64 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_22(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
fwrite($lgsl_fp,"\x03\x00\x00"); fwrite($lgsl_fp,"\x03\x00\x00");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
$buffer = substr($buffer, 3); // REMOVE HEADER $buffer = substr($buffer, 3); // REMOVE HEADER
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
$response_type = ord(lgsl_cut_byte($buffer, 1)); // TYPE SHOULD BE 4 $response_type = ord(lgsl_cut_byte($buffer, 1)); // TYPE SHOULD BE 4
//---------------------------------------------------------+ //---------------------------------------------------------+
$grf_count = ord(lgsl_cut_byte($buffer, 1)); $grf_count = ord(lgsl_cut_byte($buffer, 1));
for ($a=0; $a<$grf_count; $a++) for ($a=0; $a<$grf_count; $a++)
{ {
$server['e']['grf_'.$a.'_id'] = strtoupper(dechex(lgsl_unpack(lgsl_cut_byte($buffer, 4), "N"))); $server['e']['grf_'.$a.'_id'] = strtoupper(dechex(lgsl_unpack(lgsl_cut_byte($buffer, 4), "N")));
for ($b=0; $b<16; $b++) for ($b=0; $b<16; $b++)
{ {
$server['e']['grf_'.$a.'_md5'] .= strtoupper(dechex(ord(lgsl_cut_byte($buffer, 1)))); $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_current'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "L");
$server['e']['date_start'] = 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_max'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['companies'] = 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['e']['spectators_max'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['name'] = lgsl_cut_string($buffer); $server['s']['name'] = lgsl_cut_string($buffer);
$server['e']['version'] = lgsl_cut_string($buffer); $server['e']['version'] = lgsl_cut_string($buffer);
$server['e']['language'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['language'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['password'] = 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']['playersmax'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['players'] = 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['e']['spectators'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['map'] = lgsl_cut_string($buffer); $server['s']['map'] = lgsl_cut_string($buffer);
$server['e']['map_width'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); $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_height'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['e']['map_set'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['map_set'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1));
// DOES NOT RETURN PLAYER INFORMATION // DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,404 +1,404 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski
*/ */
char ottd_serverinfo[] = { char ottd_serverinfo[] = {
0x03, 0x00, // packet length 0x03, 0x00, // packet length
0x00, // packet type 0x00, // packet type
}; };
char ottd_serverdetails[] = { char ottd_serverdetails[] = {
0x03, 0x00, // packet length 0x03, 0x00, // packet length
0x02, // packet type 0x02, // packet type
}; };
{ {
/* openTTD */ /* openTTD */
OTTD_SERVER, /* id */ OTTD_SERVER, /* id */
"OTTDS", /* type_prefix */ "OTTDS", /* type_prefix */
"ottds", /* type_string */ "ottds", /* type_string */
"-ottds", /* type_option */ "-ottds", /* type_option */
"OpenTTD", /* game_name */ "OpenTTD", /* game_name */
0, /* master */ 0, /* master */
3979, /* default_port */ 3979, /* default_port */
0, /* port_offset */ 0, /* port_offset */
0, /* flags */ 0, /* flags */
"", /* game_rule */ "", /* game_rule */
"OPENTTD", /* template_var */ "OPENTTD", /* template_var */
(char*) &ottd_serverinfo, /* status_packet */ (char*) &ottd_serverinfo, /* status_packet */
sizeof( ottd_serverinfo), /* status_len */ sizeof( ottd_serverinfo), /* status_len */
NULL, /* player_packet */ NULL, /* player_packet */
0, /* player_len */ 0, /* player_len */
(char*) &ottd_serverdetails,/* rule_packet */ (char*) &ottd_serverdetails,/* rule_packet */
sizeof( ottd_serverdetails), /* rule_len */ sizeof( ottd_serverdetails), /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
display_q2_player_info, /* display_player_func */ display_q2_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
raw_display_q2_player_info, /* display_raw_player_func */ raw_display_q2_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_player_info,/* display_xml_player_func */ xml_display_player_info,/* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_ottd_request_packet, /* status_query_func */ send_ottd_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_ottd_packet, /* packet_func */ deal_with_ottd_packet, /* packet_func */
}, },
/* /*
* qstat 2.11 * qstat 2.11
* *
* opentTTD protocol * opentTTD protocol
* Copyright 2007 Ludwig Nussel * Copyright 2007 Ludwig Nussel
* *
* Licensed under the Artistic License, see LICENSE.txt for license terms * Licensed under the Artistic License, see LICENSE.txt for license terms
*/ */
#include <sys/types.h> #include <sys/types.h>
#ifndef _WIN32 #ifndef _WIN32
#include <sys/socket.h> #include <sys/socket.h>
#endif #endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "qstat.h" #include "qstat.h"
#include "qserver.h" #include "qserver.h"
#include "debug.h" #include "debug.h"
enum { MAX_VEHICLE_TYPES = 5, MAX_STATION_TYPES = 5 }; enum { MAX_VEHICLE_TYPES = 5, MAX_STATION_TYPES = 5 };
static const char* vehicle_types[] = { static const char* vehicle_types[] = {
"num_trains", "num_trains",
"num_trucks", "num_trucks",
"num_busses", "num_busses",
"num_aircrafts", "num_aircrafts",
"num_ships", "num_ships",
}; };
static const char* station_types[] = { static const char* station_types[] = {
"num_stations", "num_stations",
"num_truckbays", "num_truckbays",
"num_busstations", "num_busstations",
"num_airports", "num_airports",
"num_docks", "num_docks",
}; };
query_status_t deal_with_ottdmaster_packet(struct qserver *server, char *rawpkt, int pktlen) query_status_t deal_with_ottdmaster_packet(struct qserver *server, char *rawpkt, int pktlen)
{ {
unsigned num; unsigned num;
server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
server->server_name = MASTER; server->server_name = MASTER;
if(swap_short_from_little(rawpkt) != pktlen) if(swap_short_from_little(rawpkt) != pktlen)
{ {
malformed_packet( server, "invalid packet length" ); malformed_packet( server, "invalid packet length" );
return PKT_ERROR; return PKT_ERROR;
} }
if(rawpkt[2] != 7) if(rawpkt[2] != 7)
{ {
malformed_packet( server, "invalid packet type" ); malformed_packet( server, "invalid packet type" );
return PKT_ERROR; return PKT_ERROR;
} }
if(rawpkt[3] != 1) if(rawpkt[3] != 1)
{ {
malformed_packet( server, "invalid packet version" ); malformed_packet( server, "invalid packet version" );
return PKT_ERROR; return PKT_ERROR;
} }
num = swap_short_from_little(&rawpkt[4]); num = swap_short_from_little(&rawpkt[4]);
rawpkt += 6; rawpkt += 6;
pktlen -= 6; pktlen -= 6;
if( num && num*6 <= pktlen ) if( num && num*6 <= pktlen )
{ {
unsigned i; unsigned i;
server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen ); server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen );
memset(server->master_pkt + server->master_pkt_len, 0, pktlen ); memset(server->master_pkt + server->master_pkt_len, 0, pktlen );
server->master_pkt_len += pktlen; server->master_pkt_len += pktlen;
for( i = 0; i < num * 6; i += 6 ) for( i = 0; i < num * 6; i += 6 )
{ {
memcpy(&server->master_pkt[i], &rawpkt[i], 4); memcpy(&server->master_pkt[i], &rawpkt[i], 4);
server->master_pkt[i+4] = rawpkt[i+5]; server->master_pkt[i+4] = rawpkt[i+5];
server->master_pkt[i+5] = rawpkt[i+4]; server->master_pkt[i+5] = rawpkt[i+4];
} }
server->n_servers += num; server->n_servers += num;
} }
else else
{ {
malformed_packet( server, "invalid packet" ); malformed_packet( server, "invalid packet" );
return PKT_ERROR; return PKT_ERROR;
} }
bind_sockets(); bind_sockets();
return DONE_AUTO; return DONE_AUTO;
} }
#define xstr(s) str(s) #define xstr(s) str(s)
#define str(s) #s #define str(s) #s
#define GET_STRING do { \ #define GET_STRING do { \
str = (char*)ptr; \ str = (char*)ptr; \
ptr = memchr(ptr, '\0', end-ptr); \ ptr = memchr(ptr, '\0', end-ptr); \
if ( !ptr ) \ if ( !ptr ) \
{ \ { \
malformed_packet( server, "%s:%s invalid packet", __FILE__, xstr(__LINE__) ); \ malformed_packet( server, "%s:%s invalid packet", __FILE__, xstr(__LINE__) ); \
return PKT_ERROR; \ return PKT_ERROR; \
} \ } \
++ptr; \ ++ptr; \
} while(0) } while(0)
#define FAIL_IF(cond, msg) \ #define FAIL_IF(cond, msg) \
if((cond)) { \ if((cond)) { \
malformed_packet( server, "%s:%s %s", __FILE__, xstr(__LINE__), msg ); \ malformed_packet( server, "%s:%s %s", __FILE__, xstr(__LINE__), msg ); \
return PKT_ERROR; \ return PKT_ERROR; \
} }
#define INVALID_IF(cond) \ #define INVALID_IF(cond) \
FAIL_IF(cond, "invalid packet") FAIL_IF(cond, "invalid packet")
query_status_t deal_with_ottd_packet(struct qserver *server, char *rawpkt, int pktlen) query_status_t deal_with_ottd_packet(struct qserver *server, char *rawpkt, int pktlen)
{ {
unsigned char *ptr = (unsigned char*)rawpkt; unsigned char *ptr = (unsigned char*)rawpkt;
unsigned char *end = (unsigned char*)(rawpkt + pktlen); unsigned char *end = (unsigned char*)(rawpkt + pktlen);
unsigned char type; unsigned char type;
char* str; char* str;
char buf[32]; char buf[32];
unsigned ver; unsigned ver;
server->n_servers++; server->n_servers++;
if ( server->server_name == NULL) if ( server->server_name == NULL)
{ {
server->ping_total += time_delta( &packet_recv_time, &server->packet_time1); server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
server->n_requests++; server->n_requests++;
} }
else else
{ {
gettimeofday( &server->packet_time1, NULL); gettimeofday( &server->packet_time1, NULL);
} }
FAIL_IF(pktlen < 4 || swap_short_from_little(rawpkt) > pktlen, "invalid packet"); FAIL_IF(pktlen < 4 || swap_short_from_little(rawpkt) > pktlen, "invalid packet");
type = ptr[2]; type = ptr[2];
ver = ptr[3]; ver = ptr[3];
ptr += 4; ptr += 4;
debug(3, "len %hu type %hhu ver %hhu", swap_short_from_little(rawpkt), type, ver); 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"); FAIL_IF(ver != 4 && ver != 5, "only version 4 and 5 servers are supported");
if(type == 1) // info packet if(type == 1) // info packet
{ {
unsigned numgrf = *ptr; unsigned numgrf = *ptr;
FAIL_IF(ptr + numgrf * 20 + 1 > end, "invalid newgrf number"); FAIL_IF(ptr + numgrf * 20 + 1 > end, "invalid newgrf number");
ptr += numgrf * 20 + 1; ptr += numgrf * 20 + 1;
snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr)); snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
add_rule(server, "date_days", buf, NO_FLAGS); add_rule(server, "date_days", buf, NO_FLAGS);
ptr += 4; ptr += 4;
snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr)); snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
add_rule(server, "startdate_days", buf, NO_FLAGS); add_rule(server, "startdate_days", buf, NO_FLAGS);
ptr += 4; ptr += 4;
FAIL_IF(ptr + 3 > end, "invalid packet"); FAIL_IF(ptr + 3 > end, "invalid packet");
snprintf(buf, sizeof(buf), "%hhu", ptr[0]); snprintf(buf, sizeof(buf), "%hhu", ptr[0]);
add_rule(server, "maxcompanies", buf, NO_FLAGS); add_rule(server, "maxcompanies", buf, NO_FLAGS);
snprintf(buf, sizeof(buf), "%hhu", ptr[1]); snprintf(buf, sizeof(buf), "%hhu", ptr[1]);
add_rule(server, "numcompanies", buf, NO_FLAGS); add_rule(server, "numcompanies", buf, NO_FLAGS);
server->max_spectators = ptr[2]; server->max_spectators = ptr[2];
ptr += 3; ptr += 3;
GET_STRING; GET_STRING;
server->server_name = strdup(str); server->server_name = strdup(str);
GET_STRING; GET_STRING;
add_rule(server, "version", str, NO_FLAGS); add_rule(server, "version", str, NO_FLAGS);
FAIL_IF(ptr + 7 > end, "invalid packet"); FAIL_IF(ptr + 7 > end, "invalid packet");
{ {
static const char* langs[] = { static const char* langs[] = {
"any", "any",
"English", "English",
"German", "German",
"French" "French"
}; };
unsigned i = *ptr++; unsigned i = *ptr++;
if(i > 3) i = 0; if(i > 3) i = 0;
add_rule(server, "language", (char*)langs[i], NO_FLAGS); add_rule(server, "language", (char*)langs[i], NO_FLAGS);
} }
add_rule(server, "password", *ptr++ ? "1" : "0", NO_FLAGS); add_rule(server, "password", *ptr++ ? "1" : "0", NO_FLAGS);
server->max_players = *ptr++; server->max_players = *ptr++;
server->num_players = *ptr++; server->num_players = *ptr++;
server->num_spectators = *ptr++; server->num_spectators = *ptr++;
GET_STRING; GET_STRING;
server->map_name = strdup(str); server->map_name = strdup(str);
snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
add_rule(server, "map_width", buf, NO_FLAGS); add_rule(server, "map_width", buf, NO_FLAGS);
snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
add_rule(server, "map_height", buf, NO_FLAGS); add_rule(server, "map_height", buf, NO_FLAGS);
{ {
static const char* sets[] = { static const char* sets[] = {
"temperate", "temperate",
"arctic", "arctic",
"desert", "desert",
"toyland" "toyland"
}; };
unsigned i = *ptr++; unsigned i = *ptr++;
if(i > 3) i = 0; if(i > 3) i = 0;
add_rule(server, "map_set", (char*)sets[i], NO_FLAGS); add_rule(server, "map_set", (char*)sets[i], NO_FLAGS);
} }
add_rule(server, "dedicated", *ptr++ ? "1" : "0", NO_FLAGS); add_rule(server, "dedicated", *ptr++ ? "1" : "0", NO_FLAGS);
} }
else if(type == 3) // player packet else if(type == 3) // player packet
{ {
unsigned i, j; unsigned i, j;
INVALID_IF(ptr + 2 > end); INVALID_IF(ptr + 2 > end);
server->num_players = *ptr++; server->num_players = *ptr++;
for(i = 0; i < server->num_players; ++i) for(i = 0; i < server->num_players; ++i)
{ {
unsigned long long lli; unsigned long long lli;
struct player* player; struct player* player;
unsigned char nr; unsigned char nr;
nr = *ptr++; nr = *ptr++;
debug(3, "player number %d", nr); debug(3, "player number %d", nr);
player = add_player(server, i); player = add_player(server, i);
FAIL_IF(!player, "can't allocate player"); FAIL_IF(!player, "can't allocate player");
GET_STRING; GET_STRING;
player->name = strdup(str); player->name = strdup(str);
debug(3, "name %s", str); debug(3, "name %s", str);
player->frags = 0; player->frags = 0;
INVALID_IF(ptr + 4 + 3*8 + 2 + 1 + 2*MAX_VEHICLE_TYPES + 2*MAX_STATION_TYPES > end); 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)); snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
player_add_info(player, "startdate", buf, 0); player_add_info(player, "startdate", buf, 0);
ptr += 4; ptr += 4;
lli = swap_long_from_little(ptr+4); lli = swap_long_from_little(ptr+4);
lli <<= 32; lli <<= 32;
lli += swap_long_from_little(ptr); lli += swap_long_from_little(ptr);
snprintf(buf, sizeof(buf), "%lld", lli); snprintf(buf, sizeof(buf), "%lld", lli);
player_add_info(player, "value", buf, 0); player_add_info(player, "value", buf, 0);
ptr += 8; ptr += 8;
lli = swap_long_from_little(ptr+4); lli = swap_long_from_little(ptr+4);
lli <<= 32; lli <<= 32;
lli = swap_long_from_little(ptr); lli = swap_long_from_little(ptr);
snprintf(buf, sizeof(buf), "%lld", lli); snprintf(buf, sizeof(buf), "%lld", lli);
player_add_info(player, "money", buf, 0); player_add_info(player, "money", buf, 0);
ptr += 8; ptr += 8;
lli = swap_long_from_little(ptr+4); lli = swap_long_from_little(ptr+4);
lli <<= 32; lli <<= 32;
lli += swap_long_from_little(ptr); lli += swap_long_from_little(ptr);
snprintf(buf, sizeof(buf), "%lld", lli); snprintf(buf, sizeof(buf), "%lld", lli);
player_add_info(player, "income", buf, 0); player_add_info(player, "income", buf, 0);
ptr += 8; ptr += 8;
snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
player_add_info(player, "performance", buf, 0); player_add_info(player, "performance", buf, 0);
ptr += 2; ptr += 2;
player_add_info(player, "password", *ptr?"1":"0", 0); player_add_info(player, "password", *ptr?"1":"0", 0);
++ptr; ++ptr;
for (j = 0; j < MAX_VEHICLE_TYPES; ++j) for (j = 0; j < MAX_VEHICLE_TYPES; ++j)
{ {
snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
player_add_info(player, (char*)vehicle_types[j], buf, 0); player_add_info(player, (char*)vehicle_types[j], buf, 0);
ptr += 2; ptr += 2;
} }
for (j = 0; j < MAX_STATION_TYPES; ++j) for (j = 0; j < MAX_STATION_TYPES; ++j)
{ {
snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
player_add_info(player, (char*)station_types[j], buf, 0); player_add_info(player, (char*)station_types[j], buf, 0);
ptr += 2; ptr += 2;
} }
if (ver != 5) if (ver != 5)
{ {
// connections // connections
while(ptr + 1 < end && *ptr) while(ptr + 1 < end && *ptr)
{ {
++ptr; ++ptr;
GET_STRING; // client name GET_STRING; // client name
debug(3, "%s played by %s", str, player->name); debug(3, "%s played by %s", str, player->name);
GET_STRING; // id GET_STRING; // id
INVALID_IF(ptr + 4 > end); INVALID_IF(ptr + 4 > end);
ptr += 4; ptr += 4;
} }
++ptr; // record terminated by zero byte ++ptr; // record terminated by zero byte
} }
} }
// spectators // spectators
while(ptr + 1 < end && *ptr) while(ptr + 1 < end && *ptr)
{ {
++ptr; ++ptr;
GET_STRING; // client name GET_STRING; // client name
debug(3, "spectator %s", str); debug(3, "spectator %s", str);
GET_STRING; // id GET_STRING; // id
INVALID_IF(ptr + 4 > end); INVALID_IF(ptr + 4 > end);
ptr += 4; ptr += 4;
} }
++ptr; // record terminated by zero byte ++ptr; // record terminated by zero byte
server->next_rule = NO_SERVER_RULES; // we're done server->next_rule = NO_SERVER_RULES; // we're done
server->next_player_info = server->num_players; // we're done server->next_player_info = server->num_players; // we're done
} }
else else
{ {
malformed_packet( server, "invalid type" ); malformed_packet( server, "invalid type" );
return PKT_ERROR; return PKT_ERROR;
} }
server->retry1 = n_retries; // we're done with this packet, reset retry counter server->retry1 = n_retries; // we're done with this packet, reset retry counter
return DONE_AUTO; return DONE_AUTO;
} }
query_status_t send_ottdmaster_request_packet(struct qserver *server) query_status_t send_ottdmaster_request_packet(struct qserver *server)
{ {
return qserver_send_initial(server, server->type->master_packet, server->type->master_len); return qserver_send_initial(server, server->type->master_packet, server->type->master_len);
} }
query_status_t send_ottd_request_packet(struct qserver *server) query_status_t send_ottd_request_packet(struct qserver *server)
{ {
qserver_send_initial(server, server->type->status_packet, server->type->status_len); qserver_send_initial(server, server->type->status_packet, server->type->status_len);
if(get_server_rules || get_player_info) if(get_server_rules || get_player_info)
{ {
server->next_rule = ""; // trigger calling send_a2s_rule_request_packet server->next_rule = ""; // trigger calling send_a2s_rule_request_packet
} }
return INPROGRESS; return INPROGRESS;
} }

View file

@ -1,33 +1,33 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_32(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
fwrite($lgsl_fp, "\x05\x00\x00\x01\x0A"); fwrite($lgsl_fp, "\x05\x00\x00\x01\x0A");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
$buffer = substr($buffer, 5); // REMOVE HEADER $buffer = substr($buffer, 5); // REMOVE HEADER
$server['s']['name'] = lgsl_cut_pascal($buffer); $server['s']['name'] = lgsl_cut_pascal($buffer);
$server['s']['map'] = lgsl_cut_pascal($buffer); $server['s']['map'] = lgsl_cut_pascal($buffer);
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 1)); $server['s']['players'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['playersmax'] = 0; // HELD ON MASTER $server['s']['playersmax'] = 0; // HELD ON MASTER
// DOES NOT RETURN PLAYER INFORMATION // DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,3 +1,3 @@
Most sources say raven shield uses gamespy1, but these docs are for Most sources say raven shield uses gamespy1, but these docs are for
some other raven shield protocol. If anyone has an issue, this may some other raven shield protocol. If anyone has an issue, this may
be the actual protocol. be the actual protocol.

View file

@ -1,446 +1,446 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski
*/ */
char ravenshield_serverquery[] = "REPORT"; char ravenshield_serverquery[] = "REPORT";
{ {
/* RAVENSHIELD PROTOCOL */ /* RAVENSHIELD PROTOCOL */
RAVENSHIELD_SERVER, /* id */ RAVENSHIELD_SERVER, /* id */
"RSS", /* type_prefix */ "RSS", /* type_prefix */
"rss", /* type_string */ "rss", /* type_string */
"-rss", /* type_option */ "-rss", /* type_option */
"Ravenshield", /* game_name */ "Ravenshield", /* game_name */
0, /* master */ 0, /* master */
RAVENSHIELD_DEFAULT_PORT, /* default_port */ RAVENSHIELD_DEFAULT_PORT, /* default_port */
1000, /* port_offset */ 1000, /* port_offset */
TF_QUERY_ARG, /* flags */ TF_QUERY_ARG, /* flags */
"gametype", /* game_rule */ "gametype", /* game_rule */
"RAVENSHIELD", /* template_var */ "RAVENSHIELD", /* template_var */
(char*)ravenshield_serverquery, /* status_packet */ (char*)ravenshield_serverquery, /* status_packet */
sizeof( ravenshield_serverquery ) - 1, /* status_len */ sizeof( ravenshield_serverquery ) - 1, /* status_len */
NULL, /* player_packet */ NULL, /* player_packet */
0, /* player_len */ 0, /* player_len */
NULL, /* rule_packet */ NULL, /* rule_packet */
0, /* rule_len */ 0, /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
display_ravenshield_player_info, /* display_player_func */ display_ravenshield_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
raw_display_ravenshield_player_info, /* display_raw_player_func */ raw_display_ravenshield_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_ravenshield_player_info, /* display_xml_player_func */ xml_display_ravenshield_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_qserver_request_packet, /* status_query_func */ send_qserver_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_ravenshield_packet, /* packet_func */ deal_with_ravenshield_packet, /* packet_func */
}, },
query_status_t deal_with_ravenshield_packet(struct qserver *server, char *rawpkt, int pktlen) query_status_t deal_with_ravenshield_packet(struct qserver *server, char *rawpkt, int pktlen)
{ {
char *s, *key, *value; char *s, *key, *value;
debug( 2, "deal_with_ravenshield_packet %p, %d", server, pktlen ); debug( 2, "deal_with_ravenshield_packet %p, %d", server, pktlen );
server->n_servers++; server->n_servers++;
if (NULL == server->server_name) if (NULL == server->server_name)
{ {
server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
} }
else else
{ {
gettimeofday(&server->packet_time1, NULL); gettimeofday(&server->packet_time1, NULL);
} }
rawpkt[pktlen] = '\0'; rawpkt[pktlen] = '\0';
s = rawpkt; s = rawpkt;
while (*s) while (*s)
{ {
// Find the seperator // Find the seperator
while (*s && *s != '\xB6') while (*s && *s != '\xB6')
{ {
s++; s++;
} }
if (! *s) if (! *s)
{ {
// Hit the end no more // Hit the end no more
break; break;
} }
// key start // key start
key = ++s; key = ++s;
while (*s && *s != ' ') while (*s && *s != ' ')
{ {
s++; s++;
} }
if (*s != ' ') if (*s != ' ')
{ {
// malformed // malformed
break; break;
} }
*s++ = '\0'; *s++ = '\0';
// key end // key end
// value start // value start
value = s; value = s;
while (*s && *s != '\xB6') while (*s && *s != '\xB6')
{ {
s++; s++;
} }
if (*s == '\xB6') if (*s == '\xB6')
{ {
*(s - 1) = '\0'; *(s - 1) = '\0';
} }
// Decode current key par // Decode current key par
if (0 == strcmp("A1", key)) if (0 == strcmp("A1", key))
{ {
// Max players // Max players
server->max_players = atoi(value); server->max_players = atoi(value);
} }
else if (0 == strcmp("A2", key)) else if (0 == strcmp("A2", key))
{ {
// TeamKillerPenalty // TeamKillerPenalty
add_rule(server, "TeamKillerPenalty", value, NO_FLAGS); add_rule(server, "TeamKillerPenalty", value, NO_FLAGS);
} }
else if (0 == strcmp("B1", key)) else if (0 == strcmp("B1", key))
{ {
// Current players // Current players
server->num_players = atoi(value); server->num_players = atoi(value);
} }
else if (0 == strcmp("B2", key)) else if (0 == strcmp("B2", key))
{ {
// AllowRadar // AllowRadar
add_rule(server, "AllowRadar", value, NO_FLAGS); add_rule(server, "AllowRadar", value, NO_FLAGS);
} }
else if (0 == strcmp("D2", key)) else if (0 == strcmp("D2", key))
{ {
// Version info // Version info
add_rule(server, "Version", value, NO_FLAGS); add_rule(server, "Version", value, NO_FLAGS);
} }
else if (0 == strcmp("E1", key)) else if (0 == strcmp("E1", key))
{ {
// Current map // Current map
server->map_name = strdup(value); server->map_name = strdup(value);
} }
else if (0 == strcmp("E2", key)) else if (0 == strcmp("E2", key))
{ {
// Unknown // Unknown
} }
else if (0 == strcmp("F1", key)) else if (0 == strcmp("F1", key))
{ {
// Game type // Game type
server->game = find_ravenshield_game(value); server->game = find_ravenshield_game(value);
add_rule(server, server->type->game_rule, server->game, NO_FLAGS); add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
} }
else if (0 == strcmp("F2", key)) else if (0 == strcmp("F2", key))
{ {
// Unknown // Unknown
} }
else if (0 == strcmp("G1", key)) else if (0 == strcmp("G1", key))
{ {
// Password // Password
add_rule(server, "Password", value, NO_FLAGS); add_rule(server, "Password", value, NO_FLAGS);
} }
else if (0 == strcmp("G2", key)) else if (0 == strcmp("G2", key))
{ {
// Query port // Query port
} }
else if (0 == strcmp("H1", key)) else if (0 == strcmp("H1", key))
{ {
// Unknown // Unknown
} }
else if (0 == strcmp("H2", key)) else if (0 == strcmp("H2", key))
{ {
// Number of Terrorists // Number of Terrorists
add_rule(server, "nbTerro", value, NO_FLAGS); add_rule(server, "nbTerro", value, NO_FLAGS);
} }
else if (0 == strcmp("I1", key)) else if (0 == strcmp("I1", key))
{ {
// Server name // Server name
server->server_name = strdup(value); server->server_name = strdup(value);
} }
else if (0 == strcmp("I2", key)) else if (0 == strcmp("I2", key))
{ {
// Unknown // Unknown
} }
else if (0 == strcmp("J1", key)) else if (0 == strcmp("J1", key))
{ {
// Game Type Order // Game Type Order
// Not pretty ignore for now // Not pretty ignore for now
//add_rule( server, "Game Type Order", value, NO_FLAGS ); //add_rule( server, "Game Type Order", value, NO_FLAGS );
} }
else if (0 == strcmp("J2", key)) else if (0 == strcmp("J2", key))
{ {
// RotateMap // RotateMap
add_rule(server, "RotateMap", value, NO_FLAGS); add_rule(server, "RotateMap", value, NO_FLAGS);
} }
else if (0 == strcmp("K1", key)) else if (0 == strcmp("K1", key))
{ {
// Map Cycle // Map Cycle
// Not pretty ignore for now // Not pretty ignore for now
//add_rule( server, "Map Cycle", value, NO_FLAGS ); //add_rule( server, "Map Cycle", value, NO_FLAGS );
} }
else if (0 == strcmp("K2", key)) else if (0 == strcmp("K2", key))
{ {
// Force First Person Weapon // Force First Person Weapon
add_rule(server, "ForceFPersonWeapon", value, NO_FLAGS); add_rule(server, "ForceFPersonWeapon", value, NO_FLAGS);
} }
else if (0 == strcmp("L1", key)) else if (0 == strcmp("L1", key))
{ {
// Players names // Players names
int player_number = 0; int player_number = 0;
char *n = value; char *n = value;
if (*n == '/') if (*n == '/')
{ {
// atleast 1 player // atleast 1 player
n++; n++;
while (*n && *n != '\xB6') while (*n && *n != '\xB6')
{ {
char *player_name = n; char *player_name = n;
while (*n && *n != '/' && *n != '\xB6') while (*n && *n != '/' && *n != '\xB6')
{ {
n++; n++;
} }
if (*n == '/') if (*n == '/')
{ {
*n++ = '\0'; *n++ = '\0';
} }
else if (*n == '\xB6') else if (*n == '\xB6')
{ {
*(n - 1) = '\0'; *(n - 1) = '\0';
} }
if (0 != strlen(player_name)) if (0 != strlen(player_name))
{ {
struct player *player = add_player(server, player_number); struct player *player = add_player(server, player_number);
if (NULL != player) if (NULL != player)
{ {
player->name = strdup(player_name); player->name = strdup(player_name);
} }
player_number++; player_number++;
} }
} }
} }
} }
else if (0 == strcmp("L3", key)) else if (0 == strcmp("L3", key))
{ {
// PunkBuster state // PunkBuster state
add_rule(server, "PunkBuster", value, NO_FLAGS); add_rule(server, "PunkBuster", value, NO_FLAGS);
} }
else if (0 == strcmp("M1", key)) else if (0 == strcmp("M1", key))
{ {
// Players times // Players times
int player_number = 0; int player_number = 0;
char *n = value; char *n = value;
if (*n == '/') if (*n == '/')
{ {
// atleast 1 player // atleast 1 player
n++; n++;
while (*n && *n != '\xB6') while (*n && *n != '\xB6')
{ {
char *time = n; char *time = n;
while (*n && *n != '/' && *n != '\xB6') while (*n && *n != '/' && *n != '\xB6')
{ {
n++; n++;
} }
if (*n == '/') if (*n == '/')
{ {
*n++ = '\0'; *n++ = '\0';
} }
else if (*n == '\xB6') else if (*n == '\xB6')
{ {
*(n - 1) = '\0'; *(n - 1) = '\0';
} }
if (0 != strlen(time)) if (0 != strlen(time))
{ {
int mins, seconds; int mins, seconds;
if (2 == sscanf(time, "%d:%d", &mins, &seconds)) if (2 == sscanf(time, "%d:%d", &mins, &seconds))
{ {
struct player *player = get_player_by_number(server, player_number); struct player *player = get_player_by_number(server, player_number);
if (NULL != player) if (NULL != player)
{ {
player->connect_time = mins * 60+seconds; player->connect_time = mins * 60+seconds;
} }
} }
player_number++; player_number++;
} }
} }
} }
} }
else if (0 == strcmp("N1", key)) else if (0 == strcmp("N1", key))
{ {
// Players ping // Players ping
int player_number = 0; int player_number = 0;
char *n = value; char *n = value;
if (*n == '/') if (*n == '/')
{ {
// atleast 1 player // atleast 1 player
n++; n++;
while (*n && *n != '\xB6') while (*n && *n != '\xB6')
{ {
char *ping = n; char *ping = n;
while (*n && *n != '/' && *n != '\xB6') while (*n && *n != '/' && *n != '\xB6')
{ {
n++; n++;
} }
if (*n == '/') if (*n == '/')
{ {
*n++ = '\0'; *n++ = '\0';
} }
else if (*n == '\xB6') else if (*n == '\xB6')
{ {
*(n - 1) = '\0'; *(n - 1) = '\0';
} }
if (0 != strlen(ping)) if (0 != strlen(ping))
{ {
struct player *player = get_player_by_number(server, player_number); struct player *player = get_player_by_number(server, player_number);
if (NULL != player) if (NULL != player)
{ {
player->ping = atoi(ping); player->ping = atoi(ping);
} player_number++; } player_number++;
} }
} }
} }
} }
else if (0 == strcmp("O1", key)) else if (0 == strcmp("O1", key))
{ {
// Players fags // Players fags
int player_number = 0; int player_number = 0;
char *n = value; char *n = value;
if (*n == '/') if (*n == '/')
{ {
// atleast 1 player // atleast 1 player
n++; n++;
while (*n && *n != '\xB6') while (*n && *n != '\xB6')
{ {
char *frags = n; char *frags = n;
while (*n && *n != '/' && *n != '\xB6') while (*n && *n != '/' && *n != '\xB6')
{ {
n++; n++;
} }
if (*n == '/') if (*n == '/')
{ {
*n++ = '\0'; *n++ = '\0';
} }
else if (*n == '\xB6') else if (*n == '\xB6')
{ {
*(n - 1) = '\0'; *(n - 1) = '\0';
} }
if (0 != strlen(frags)) if (0 != strlen(frags))
{ {
struct player *player = get_player_by_number(server, player_number); struct player *player = get_player_by_number(server, player_number);
if (NULL != player) if (NULL != player)
{ {
player->frags = atoi(frags); player->frags = atoi(frags);
} player_number++; } player_number++;
} }
} }
} }
} }
else if (0 == strcmp("P1", key)) else if (0 == strcmp("P1", key))
{ {
// Game port // Game port
// Not pretty ignore for now // Not pretty ignore for now
/* /*
change_server_port( server, atoi( value ), 0 ); change_server_port( server, atoi( value ), 0 );
*/ */
} }
else if (0 == strcmp("Q1", key)) else if (0 == strcmp("Q1", key))
{ {
// RoundsPerMatch // RoundsPerMatch
add_rule(server, "RoundsPerMatch", value, NO_FLAGS); add_rule(server, "RoundsPerMatch", value, NO_FLAGS);
} }
else if (0 == strcmp("R1", key)) else if (0 == strcmp("R1", key))
{ {
// RoundTime // RoundTime
add_rule(server, "RoundTime", value, NO_FLAGS); add_rule(server, "RoundTime", value, NO_FLAGS);
} }
else if (0 == strcmp("S1", key)) else if (0 == strcmp("S1", key))
{ {
// BetweenRoundTime // BetweenRoundTime
add_rule(server, "BetweenRoundTime", value, NO_FLAGS); add_rule(server, "BetweenRoundTime", value, NO_FLAGS);
} }
else if (0 == strcmp("T1", key)) else if (0 == strcmp("T1", key))
{ {
// BombTime // BombTime
add_rule(server, "BombTime", value, NO_FLAGS); add_rule(server, "BombTime", value, NO_FLAGS);
} }
else if (0 == strcmp("W1", key)) else if (0 == strcmp("W1", key))
{ {
// ShowNames // ShowNames
add_rule(server, "ShowNames", value, NO_FLAGS); add_rule(server, "ShowNames", value, NO_FLAGS);
} }
else if (0 == strcmp("X1", key)) else if (0 == strcmp("X1", key))
{ {
// InternetServer // InternetServer
add_rule(server, "InternetServer", value, NO_FLAGS); add_rule(server, "InternetServer", value, NO_FLAGS);
} }
else if (0 == strcmp("Y1", key)) else if (0 == strcmp("Y1", key))
{ {
// FriendlyFire // FriendlyFire
add_rule(server, "FriendlyFire", value, NO_FLAGS); add_rule(server, "FriendlyFire", value, NO_FLAGS);
} }
else if (0 == strcmp("Z1", key)) else if (0 == strcmp("Z1", key))
{ {
// Autobalance // Autobalance
add_rule(server, "Autobalance", value, NO_FLAGS); add_rule(server, "Autobalance", value, NO_FLAGS);
} }
} }
return DONE_FORCE; return DONE_FORCE;
} }
char *find_ravenshield_game(char *gameno) char *find_ravenshield_game(char *gameno)
{ {
switch (atoi(gameno)) switch (atoi(gameno))
{ {
case 8: case 8:
return strdup("Team Deathmatch"); return strdup("Team Deathmatch");
break; break;
case 13: case 13:
return strdup("Deathmatch"); return strdup("Deathmatch");
break; break;
case 14: case 14:
return strdup("Team Deathmatch"); return strdup("Team Deathmatch");
break; break;
case 15: case 15:
return strdup("Bomb"); return strdup("Bomb");
break; break;
case 16: case 16:
return strdup("Escort Pilot"); return strdup("Escort Pilot");
break; break;
default: default:
// 1.50 and above actually uses a string so // 1.50 and above actually uses a string so
// return that // return that
return strdup(gameno); return strdup(gameno);
break; break;
} }
} }

View file

@ -1,59 +1,59 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: redfaction.php,v 1.1 2007/06/30 12:43:43 tombuskens Exp $ * $Id: redfaction.php,v 1.1 2007/06/30 12:43:43 tombuskens Exp $
*/ */
[redfaction] [redfaction]
status = "\x00\x00\x00\x00" status = "\x00\x00\x00\x00"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Red Faction Protocol * Red Faction Protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.1 $ * @version $Revision: 1.1 $
*/ */
class GameQ_Protocol_redfaction extends GameQ_Protocol class GameQ_Protocol_redfaction extends GameQ_Protocol
{ {
/* /*
* getstatus packet * getstatus packet
*/ */
public function status() public function status()
{ {
// Header, we're being carefull here // Header, we're being carefull here
if ($this->p->read() !== "\x00") { if ($this->p->read() !== "\x00") {
throw new GameQ_ParsingException($this->p); throw new GameQ_ParsingException($this->p);
} }
// Dunno // Dunno
while ($this->p->read() !== "\x00") {} while ($this->p->read() !== "\x00") {}
$this->p->read(); $this->p->read();
// Data // Data
$this->r->add('servername', $this->p->readString()); $this->r->add('servername', $this->p->readString());
$this->r->add('gametype', $this->p->readInt8()); $this->r->add('gametype', $this->p->readInt8());
$this->r->add('num_players', $this->p->readInt8()); $this->r->add('num_players', $this->p->readInt8());
$this->r->add('max_players', $this->p->readInt8()); $this->r->add('max_players', $this->p->readInt8());
$this->r->add('map', $this->p->readString()); $this->r->add('map', $this->p->readString());
$this->p->read(); $this->p->read();
$this->r->add('dedicated', $this->p->readInt8()); $this->r->add('dedicated', $this->p->readInt8());
} }
} }

View file

@ -1,62 +1,62 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: rfactor.php,v 1.2 2009/08/13 20:46:40 evilpie Exp $ * $Id: rfactor.php,v 1.2 2009/08/13 20:46:40 evilpie Exp $
*/ */
[rfactor] [rfactor]
status = "rF_S" status = "rF_S"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* rFactor Protocol * rFactor Protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.2 $ * @version $Revision: 1.2 $
*/ */
class GameQ_Protocol_rfactor extends GameQ_Protocol class GameQ_Protocol_rfactor extends GameQ_Protocol
{ {
public function status() public function status()
{ {
// Header // Header
$this->p->jumpto(17); $this->p->jumpto(17);
$this->r->add('version', $this->p->readInt16()); $this->r->add('version', $this->p->readInt16());
$this->p->jumpto(25); $this->p->jumpto(25);
$this->r->add('series', $this->p->readString()); $this->r->add('series', $this->p->readString());
$this->p->jumpto(45); $this->p->jumpto(45);
$this->r->add('servername', $this->p->readString()); $this->r->add('servername', $this->p->readString());
$this->p->jumpto(73); $this->p->jumpto(73);
$this->r->add('map', $this->p->readString()); $this->r->add('map', $this->p->readString());
$this->p->jumpto(105); $this->p->jumpto(105);
$this->r->add('motd', $this->p->readString()); $this->r->add('motd', $this->p->readString());
$this->p->jumpto(206); $this->p->jumpto(206);
$this->r->add('rate', $this->p->readInt8()); $this->r->add('rate', $this->p->readInt8());
$this->r->add('numplayers', $this->p->readInt8()); $this->r->add('numplayers', $this->p->readInt8());
$this->r->add('maxplayers', $this->p->readInt8()); $this->r->add('maxplayers', $this->p->readInt8());
$this->r->add('numbots', $this->p->readInt8()); $this->r->add('numbots', $this->p->readInt8());
$this->r->add('session', $this->p->readInt8() >> 5); $this->r->add('session', $this->p->readInt8() >> 5);
$this->r->add('damage', $this->p->readInt8()); $this->r->add('damage', $this->p->readInt8());
$this->p->jumpto(217); $this->p->jumpto(217);
$this->r->add('time', $this->p->readInt16()); $this->r->add('time', $this->p->readInt16());
$this->r->add('laps', $this->p->readInt16() / 16); $this->r->add('laps', $this->p->readInt16() / 16);
} }
} }
?> ?>

View file

@ -1,94 +1,94 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_16(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
// REFERENCE: // REFERENCE:
// http://www.planetpointy.co.uk/software/rfactorsspy.shtml // http://www.planetpointy.co.uk/software/rfactorsspy.shtml
// http://users.pandora.be/viperius/mUtil/ // http://users.pandora.be/viperius/mUtil/
// USES FIXED DATA POSITIONS WITH RANDOM CHARACTERS FILLING THE GAPS // USES FIXED DATA POSITIONS WITH RANDOM CHARACTERS FILLING THE GAPS
fwrite($lgsl_fp, "rF_S"); fwrite($lgsl_fp, "rF_S");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
//---------------------------------------------------------+ //---------------------------------------------------------+
// $server['e']['gamename'] = lgsl_get_string($buffer); // $server['e']['gamename'] = lgsl_get_string($buffer);
$buffer = substr($buffer, 8); $buffer = substr($buffer, 8);
// $server['e']['fullupdate'] = lgsl_unpack($buffer[0], "C"); // $server['e']['fullupdate'] = lgsl_unpack($buffer[0], "C");
$server['e']['region'] = lgsl_unpack($buffer[1] .$buffer[2], "S"); $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']['ip'] = ($buffer[3] .$buffer[4].$buffer[5].$buffer[6]); // UNSIGNED LONG
// $server['e']['size'] = lgsl_unpack($buffer[7] .$buffer[8], "S"); // $server['e']['size'] = lgsl_unpack($buffer[7] .$buffer[8], "S");
$server['e']['version'] = lgsl_unpack($buffer[9] .$buffer[10], "S"); $server['e']['version'] = lgsl_unpack($buffer[9] .$buffer[10], "S");
// $server['e']['version_racecast'] = lgsl_unpack($buffer[11].$buffer[12], "S"); // $server['e']['version_racecast'] = lgsl_unpack($buffer[11].$buffer[12], "S");
$server['e']['hostport'] = lgsl_unpack($buffer[13].$buffer[14], "S"); $server['e']['hostport'] = lgsl_unpack($buffer[13].$buffer[14], "S");
// $server['e']['queryport'] = lgsl_unpack($buffer[15].$buffer[16], "S"); // $server['e']['queryport'] = lgsl_unpack($buffer[15].$buffer[16], "S");
$buffer = substr($buffer, 17); $buffer = substr($buffer, 17);
$server['s']['game'] = lgsl_get_string($buffer); $server['s']['game'] = lgsl_get_string($buffer);
$buffer = substr($buffer, 20); $buffer = substr($buffer, 20);
$server['s']['name'] = lgsl_get_string($buffer); $server['s']['name'] = lgsl_get_string($buffer);
$buffer = substr($buffer, 28); $buffer = substr($buffer, 28);
$server['s']['map'] = lgsl_get_string($buffer); $server['s']['map'] = lgsl_get_string($buffer);
$buffer = substr($buffer, 32); $buffer = substr($buffer, 32);
$server['e']['motd'] = lgsl_get_string($buffer); $server['e']['motd'] = lgsl_get_string($buffer);
$buffer = substr($buffer, 96); $buffer = substr($buffer, 96);
$server['e']['packed_aids'] = lgsl_unpack($buffer[0].$buffer[1], "S"); $server['e']['packed_aids'] = lgsl_unpack($buffer[0].$buffer[1], "S");
// $server['e']['ping'] = lgsl_unpack($buffer[2].$buffer[3], "S"); // $server['e']['ping'] = lgsl_unpack($buffer[2].$buffer[3], "S");
$server['e']['packed_flags'] = lgsl_unpack($buffer[4], "C"); $server['e']['packed_flags'] = lgsl_unpack($buffer[4], "C");
$server['e']['rate'] = lgsl_unpack($buffer[5], "C"); $server['e']['rate'] = lgsl_unpack($buffer[5], "C");
$server['s']['players'] = lgsl_unpack($buffer[6], "C"); $server['s']['players'] = lgsl_unpack($buffer[6], "C");
$server['s']['playersmax'] = lgsl_unpack($buffer[7], "C"); $server['s']['playersmax'] = lgsl_unpack($buffer[7], "C");
$server['e']['bots'] = lgsl_unpack($buffer[8], "C"); $server['e']['bots'] = lgsl_unpack($buffer[8], "C");
$server['e']['packed_special'] = lgsl_unpack($buffer[9], "C"); $server['e']['packed_special'] = lgsl_unpack($buffer[9], "C");
$server['e']['damage'] = lgsl_unpack($buffer[10], "C"); $server['e']['damage'] = lgsl_unpack($buffer[10], "C");
$server['e']['packed_rules'] = lgsl_unpack($buffer[11].$buffer[12], "S"); $server['e']['packed_rules'] = lgsl_unpack($buffer[11].$buffer[12], "S");
$server['e']['credits1'] = lgsl_unpack($buffer[13], "C"); $server['e']['credits1'] = lgsl_unpack($buffer[13], "C");
$server['e']['credits2'] = lgsl_unpack($buffer[14].$buffer[15], "S"); $server['e']['credits2'] = lgsl_unpack($buffer[14].$buffer[15], "S");
$server['e']['time'] = lgsl_time(lgsl_unpack($buffer[16].$buffer[17], "S")); $server['e']['time'] = lgsl_time(lgsl_unpack($buffer[16].$buffer[17], "S"));
$server['e']['laps'] = lgsl_unpack($buffer[18].$buffer[19], "s") / 16; $server['e']['laps'] = lgsl_unpack($buffer[18].$buffer[19], "s") / 16;
$buffer = substr($buffer, 23); $buffer = substr($buffer, 23);
$server['e']['vehicles'] = lgsl_get_string($buffer); $server['e']['vehicles'] = lgsl_get_string($buffer);
// DOES NOT RETURN PLAYER INFORMATION // DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+ //---------------------------------------------------------+
$server['s']['password'] = ($server['e']['packed_special'] & 2) ? 1 : 0; $server['s']['password'] = ($server['e']['packed_special'] & 2) ? 1 : 0;
$server['e']['racecast'] = ($server['e']['packed_special'] & 4) ? 1 : 0; $server['e']['racecast'] = ($server['e']['packed_special'] & 4) ? 1 : 0;
$server['e']['fixedsetups'] = ($server['e']['packed_special'] & 16) ? 1 : 0; $server['e']['fixedsetups'] = ($server['e']['packed_special'] & 16) ? 1 : 0;
$server['e']['aids'] = ""; $server['e']['aids'] = "";
if ($server['e']['packed_aids'] & 1) { $server['e']['aids'] .= " TractionControl"; } 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'] & 2) { $server['e']['aids'] .= " AntiLockBraking"; }
if ($server['e']['packed_aids'] & 4) { $server['e']['aids'] .= " StabilityControl"; } 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'] & 8) { $server['e']['aids'] .= " AutoShifting"; }
if ($server['e']['packed_aids'] & 16) { $server['e']['aids'] .= " AutoClutch"; } 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'] & 32) { $server['e']['aids'] .= " Invulnerability"; }
if ($server['e']['packed_aids'] & 64) { $server['e']['aids'] .= " OppositeLock"; } 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'] & 128) { $server['e']['aids'] .= " SteeringHelp"; }
if ($server['e']['packed_aids'] & 256) { $server['e']['aids'] .= " BrakingHelp"; } 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'] & 512) { $server['e']['aids'] .= " SpinRecovery"; }
if ($server['e']['packed_aids'] & 1024) { $server['e']['aids'] .= " AutoPitstop"; } if ($server['e']['packed_aids'] & 1024) { $server['e']['aids'] .= " AutoPitstop"; }
$server['e']['aids'] = str_replace(" ", " / ", trim($server['e']['aids'])); $server['e']['aids'] = str_replace(" ", " / ", trim($server['e']['aids']));
$server['e']['vehicles'] = str_replace("|", " / ", trim($server['e']['vehicles'])); $server['e']['vehicles'] = str_replace("|", " / ", trim($server['e']['vehicles']));
unset($server['e']['packed_aids']); unset($server['e']['packed_aids']);
unset($server['e']['packed_flags']); unset($server['e']['packed_flags']);
unset($server['e']['packed_special']); unset($server['e']['packed_special']);
unset($server['e']['packed_rules']); unset($server['e']['packed_rules']);
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,92 +1,92 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_12(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
// REFERENCE: // REFERENCE:
// VICE CITY CURRENTLY ONLY SUPPORTS THE 'i' CHALLENGE // VICE CITY CURRENTLY ONLY SUPPORTS THE 'i' CHALLENGE
if ($server['b']['type'] == "samp") { $challenge_packet = "SAMP\x21\x21\x21\x21\x00\x00"; } 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; } 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"; } if ($lgsl_need['s']) { $challenge_packet .= "i"; }
elseif ($lgsl_need['e']) { $challenge_packet .= "r"; } elseif ($lgsl_need['e']) { $challenge_packet .= "r"; }
elseif ($lgsl_need['p']) { $challenge_packet .= "d"; } elseif ($lgsl_need['p']) { $challenge_packet .= "d"; }
fwrite($lgsl_fp, $challenge_packet); fwrite($lgsl_fp, $challenge_packet);
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
//---------------------------------------------------------+ //---------------------------------------------------------+
$buffer = substr($buffer, 10); // REMOVE HEADER $buffer = substr($buffer, 10); // REMOVE HEADER
$response_type = lgsl_cut_byte($buffer, 1); $response_type = lgsl_cut_byte($buffer, 1);
//---------------------------------------------------------+ //---------------------------------------------------------+
if ($response_type == "i") if ($response_type == "i")
{ {
$lgsl_need['s'] = FALSE; $lgsl_need['s'] = FALSE;
$server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); $server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['s']['playersmax'] = 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['s']['name'] = lgsl_cut_pascal($buffer, 4);
$server['e']['gamemode'] = lgsl_cut_pascal($buffer, 4); $server['e']['gamemode'] = lgsl_cut_pascal($buffer, 4);
$server['s']['map'] = lgsl_cut_pascal($buffer, 4); $server['s']['map'] = lgsl_cut_pascal($buffer, 4);
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
elseif ($response_type == "r") elseif ($response_type == "r")
{ {
$lgsl_need['e'] = FALSE; $lgsl_need['e'] = FALSE;
$item_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); $item_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
for ($i=0; $i<$item_total; $i++) for ($i=0; $i<$item_total; $i++)
{ {
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
$data_key = strtolower(lgsl_cut_pascal($buffer)); $data_key = strtolower(lgsl_cut_pascal($buffer));
$data_value = lgsl_cut_pascal($buffer); $data_value = lgsl_cut_pascal($buffer);
$server['e'][$data_key] = $data_value; $server['e'][$data_key] = $data_value;
} }
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
elseif ($response_type == "d") elseif ($response_type == "d")
{ {
$lgsl_need['p'] = FALSE; $lgsl_need['p'] = FALSE;
$player_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); $player_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
for ($i=0; $i<$player_total; $i++) for ($i=0; $i<$player_total; $i++)
{ {
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
$server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1)); $server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1));
$server['p'][$i]['name'] = lgsl_cut_pascal($buffer); $server['p'][$i]['name'] = lgsl_cut_pascal($buffer);
$server['p'][$i]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); $server['p'][$i]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S");
$server['p'][$i]['ping'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); $server['p'][$i]['ping'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S");
} }
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,92 +1,92 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: silverback.php,v 1.1 2007/07/11 09:12:31 tombuskens Exp $ * $Id: silverback.php,v 1.1 2007/07/11 09:12:31 tombuskens Exp $
*/ */
[silverback] [silverback]
status = "\x9e\x4c\x23\x00\x00\xcePiNG" status = "\x9e\x4c\x23\x00\x00\xcePiNG"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Silverback Engine Protocol * Silverback Engine Protocol
* (Savage) * (Savage)
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.1 $ * @version $Revision: 1.1 $
*/ */
class GameQ_Protocol_silverback extends GameQ_Protocol class GameQ_Protocol_silverback extends GameQ_Protocol
{ {
/* /*
* status packet * status packet
*/ */
public function status() public function status()
{ {
while ($this->p->getLength()) { while ($this->p->getLength()) {
$var = $this->p->readString("\xFE"); $var = $this->p->readString("\xFE");
if ($var == 'players') break; if ($var == 'players') break;
$this->r->add($var, $this->p->readString("\xFF")); $this->r->add($var, $this->p->readString("\xFF"));
} }
$this->players(); $this->players();
} }
/* /*
* player / team data * player / team data
*/ */
public function players() public function players()
{ {
$team = ''; $team = '';
$players = 0; $players = 0;
while ($this->p->getLength()) { while ($this->p->getLength()) {
if ($this->p->lookAhead() == "\x20") { if ($this->p->lookAhead() == "\x20") {
$this->p->skip(); $this->p->skip();
$this->r->addPlayer('name', $this->p->readString("\x0a")); $this->r->addPlayer('name', $this->p->readString("\x0a"));
$this->r->addPlayer('team', $team); $this->r->addPlayer('team', $team);
++$players; ++$players;
} }
else { else {
$team = $this->p->readString("\x0a"); $team = $this->p->readString("\x0a");
if ($team != '--empty--') { if ($team != '--empty--') {
$this->r->addTeam('name', $team); $this->r->addTeam('name', $team);
} }
} }
} }
} }
/* /*
* Merge packets * Merge packets
*/ */
public function preprocess($packets) public function preprocess($packets)
{ {
// Cut off headers and join packets // Cut off headers and join packets
$return = ''; $return = '';
foreach ($packets as $packet) { foreach ($packets as $packet) {
$return .= substr($packet, 12, strlen($packet) - 13); $return .= substr($packet, 12, strlen($packet) - 13);
} }
return $return; return $return;
} }
} }
?> ?>

View file

@ -1,66 +1,66 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_17(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
// REFERENCE: http://masterserver.savage.s2games.com // REFERENCE: http://masterserver.savage.s2games.com
fwrite($lgsl_fp, "\x9e\x4c\x23\x00\x00\xce\x21\x21\x21\x21"); fwrite($lgsl_fp, "\x9e\x4c\x23\x00\x00\xce\x21\x21\x21\x21");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
//---------------------------------------------------------+ //---------------------------------------------------------+
$buffer = substr($buffer, 12); // REMOVE HEADER $buffer = substr($buffer, 12); // REMOVE HEADER
while ($key = strtolower(lgsl_cut_string($buffer, 0, "\xFE"))) while ($key = strtolower(lgsl_cut_string($buffer, 0, "\xFE")))
{ {
if ($key == "players") { break; } if ($key == "players") { break; }
$value = lgsl_cut_string($buffer, 0, "\xFF"); $value = lgsl_cut_string($buffer, 0, "\xFF");
$value = str_replace("\x00", "", $value); $value = str_replace("\x00", "", $value);
$value = lgsl_parse_color($value, $server['b']['type']); $value = lgsl_parse_color($value, $server['b']['type']);
$server['e'][$key] = $value; $server['e'][$key] = $value;
} }
$server['s']['name'] = $server['e']['name']; unset($server['e']['name']); $server['s']['name'] = $server['e']['name']; unset($server['e']['name']);
$server['s']['map'] = $server['e']['world']; unset($server['e']['world']); $server['s']['map'] = $server['e']['world']; unset($server['e']['world']);
$server['s']['players'] = $server['e']['cnum']; unset($server['e']['cnum']); $server['s']['players'] = $server['e']['cnum']; unset($server['e']['cnum']);
$server['s']['playersmax'] = $server['e']['cmax']; 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['s']['password'] = $server['e']['pass']; unset($server['e']['cnum']);
//---------------------------------------------------------+ //---------------------------------------------------------+
$server['t'][0]['name'] = $server['e']['race1']; $server['t'][0]['name'] = $server['e']['race1'];
$server['t'][1]['name'] = $server['e']['race2']; $server['t'][1]['name'] = $server['e']['race2'];
$server['t'][2]['name'] = "spectator"; $server['t'][2]['name'] = "spectator";
$team_key = -1; $team_key = -1;
$player_key = 0; $player_key = 0;
while ($value = lgsl_cut_string($buffer, 0, "\x0a")) while ($value = lgsl_cut_string($buffer, 0, "\x0a"))
{ {
if ($value[0] == "\x00") { break; } if ($value[0] == "\x00") { break; }
if ($value[0] != "\x20") { $team_key++; continue; } 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]['name'] = lgsl_parse_color(substr($value, 1), $server['b']['type']);
$server['p'][$player_key]['team'] = $server['t'][$team_key]['name']; $server['p'][$player_key]['team'] = $server['t'][$team_key]['name'];
$player_key++; $player_key++;
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,278 +1,278 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski
*/ */
unsigned char savage_serverquery[] = { unsigned char savage_serverquery[] = {
0x9e,0x4c,0x23,0x00,0x00,0xc8,0x01,0x21,0x00,0x00 0x9e,0x4c,0x23,0x00,0x00,0xc8,0x01,0x21,0x00,0x00
}; };
unsigned char savage_playerquery[] = { unsigned char savage_playerquery[] = {
0x9e,0x4c,0x23,0x00,0x00,0xce,0x76,0x46,0x00,0x00 0x9e,0x4c,0x23,0x00,0x00,0xce,0x76,0x46,0x00,0x00
}; };
{ {
/* SAVAGE PROTOCOL */ /* SAVAGE PROTOCOL */
SAVAGE_SERVER, /* id */ SAVAGE_SERVER, /* id */
"SAS", /* type_prefix */ "SAS", /* type_prefix */
"sas", /* type_string */ "sas", /* type_string */
"-sas", /* type_option */ "-sas", /* type_option */
"Savage", /* game_name */ "Savage", /* game_name */
0, /* master */ 0, /* master */
SAVAGE_DEFAULT_PORT, /* default_port */ SAVAGE_DEFAULT_PORT, /* default_port */
0, /* port_offset */ 0, /* port_offset */
TF_QUERY_ARG, /* flags */ TF_QUERY_ARG, /* flags */
"gametype", /* game_rule */ "gametype", /* game_rule */
"SAVAGE", /* template_var */ "SAVAGE", /* template_var */
(char*)savage_serverquery, /* status_packet */ (char*)savage_serverquery, /* status_packet */
sizeof( savage_serverquery ) - 1, /* status_len */ sizeof( savage_serverquery ) - 1, /* status_len */
(char*)savage_playerquery, /* player_packet */ (char*)savage_playerquery, /* player_packet */
sizeof( savage_playerquery ) - 1, /* player_len */ sizeof( savage_playerquery ) - 1, /* player_len */
NULL, /* rule_packet */ NULL, /* rule_packet */
0, /* rule_len */ 0, /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
display_savage_player_info, /* display_player_func */ display_savage_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
raw_display_savage_player_info, /* display_raw_player_func */ raw_display_savage_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_savage_player_info, /* display_xml_player_func */ xml_display_savage_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_savage_request_packet, /* status_query_func */ send_savage_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_savage_packet, /* packet_func */ deal_with_savage_packet, /* packet_func */
}, },
query_status_t send_savage_request_packet(struct qserver *server) query_status_t send_savage_request_packet(struct qserver *server)
{ {
int len; int len;
char *pkt; char *pkt;
if (get_player_info) if (get_player_info)
{ {
pkt = server->type->player_packet; pkt = server->type->player_packet;
len = server->type->player_len; len = server->type->player_len;
} }
else else
{ {
pkt = server->type->status_packet; pkt = server->type->status_packet;
len = server->type->status_len; len = server->type->status_len;
} }
return send_packet(server, pkt, len); return send_packet(server, pkt, len);
} }
query_status_t deal_with_savage_packet(struct qserver *server, char *rawpkt, int pktlen) query_status_t deal_with_savage_packet(struct qserver *server, char *rawpkt, int pktlen)
{ {
char *s, *key, *value, *end; char *s, *key, *value, *end;
debug( 2, "deal_with_savage_packet %p, %d", server, pktlen ); debug( 2, "deal_with_savage_packet %p, %d", server, pktlen );
server->n_servers++; server->n_servers++;
if (NULL == server->server_name) if (NULL == server->server_name)
{ {
server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
} }
else else
{ {
gettimeofday(&server->packet_time1, NULL); gettimeofday(&server->packet_time1, NULL);
} }
rawpkt[pktlen] = '\0'; rawpkt[pktlen] = '\0';
end = s = rawpkt; end = s = rawpkt;
end += pktlen; end += pktlen;
while (*s) while (*s)
{ {
// Find the seperator // Find the seperator
while (s <= end && *s != '\xFF') while (s <= end && *s != '\xFF')
{ {
s++; s++;
} }
if (s >= end) if (s >= end)
{ {
// Hit the end no more // Hit the end no more
break; break;
} }
// key start // key start
key = ++s; key = ++s;
while (s < end && *s != '\xFE') while (s < end && *s != '\xFE')
{ {
s++; s++;
} }
if (*s != '\xFE') if (*s != '\xFE')
{ {
// malformed // malformed
break; break;
} }
*s++ = '\0'; *s++ = '\0';
// key end // key end
// value start // value start
value = s; value = s;
while (s < end && *s != '\xFF') while (s < end && *s != '\xFF')
{ {
s++; s++;
} }
if (*s == '\xFF') if (*s == '\xFF')
{ {
*s = '\0'; *s = '\0';
} }
//fprintf( stderr, "'%s' = '%s'\n", key, value ); //fprintf( stderr, "'%s' = '%s'\n", key, value );
// Decode current key par // Decode current key par
if (0 == strcmp("cmax", key)) if (0 == strcmp("cmax", key))
{ {
// Max players // Max players
server->max_players = atoi(value); server->max_players = atoi(value);
} }
else if (0 == strcmp("cnum", key)) else if (0 == strcmp("cnum", key))
{ {
// Current players // Current players
server->num_players = atoi(value); server->num_players = atoi(value);
} }
else if (0 == strcmp("bal", key)) else if (0 == strcmp("bal", key))
{ {
// Balance // Balance
add_rule(server, "Balance", value, NO_FLAGS); add_rule(server, "Balance", value, NO_FLAGS);
} }
else if (0 == strcmp("world", key)) else if (0 == strcmp("world", key))
{ {
// Current map // Current map
server->map_name = strdup(value); server->map_name = strdup(value);
} }
else if (0 == strcmp("gametype", key)) else if (0 == strcmp("gametype", key))
{ {
// Game type // Game type
server->game = find_savage_game(value); server->game = find_savage_game(value);
add_rule(server, server->type->game_rule, server->game, NO_FLAGS); add_rule(server, server->type->game_rule, server->game, NO_FLAGS);
} }
else if (0 == strcmp("pure", key)) else if (0 == strcmp("pure", key))
{ {
// Pure // Pure
add_rule(server, "Pure", value, NO_FLAGS); add_rule(server, "Pure", value, NO_FLAGS);
} }
else if (0 == strcmp("time", key)) else if (0 == strcmp("time", key))
{ {
// Current game time // Current game time
add_rule(server, "Time", value, NO_FLAGS); add_rule(server, "Time", value, NO_FLAGS);
} }
else if (0 == strcmp("notes", key)) else if (0 == strcmp("notes", key))
{ {
// Notes // Notes
add_rule(server, "Notes", value, NO_FLAGS); add_rule(server, "Notes", value, NO_FLAGS);
} }
else if (0 == strcmp("needcmdr", key)) else if (0 == strcmp("needcmdr", key))
{ {
// Need Commander // Need Commander
add_rule(server, "Need Commander", value, NO_FLAGS); add_rule(server, "Need Commander", value, NO_FLAGS);
} }
else if (0 == strcmp("name", key)) else if (0 == strcmp("name", key))
{ {
// Server name // Server name
server->server_name = strdup(value); server->server_name = strdup(value);
} }
else if (0 == strcmp("fw", key)) else if (0 == strcmp("fw", key))
{ {
// Firewalled // Firewalled
add_rule(server, "Firewalled", value, NO_FLAGS); add_rule(server, "Firewalled", value, NO_FLAGS);
} }
else if (0 == strcmp("players", key)) else if (0 == strcmp("players", key))
{ {
// Players names // Players names
int player_number = 0; int player_number = 0;
int team_number = 1; int team_number = 1;
char *team_name, *player_name, *n; char *team_name, *player_name, *n;
n = team_name = value; n = team_name = value;
// team name // team name
n++; n++;
while (*n && *n != '\x0a') while (*n && *n != '\x0a')
{ {
n++; n++;
} }
if (*n != '\x0a') if (*n != '\x0a')
{ {
// Broken data // Broken data
break; break;
} }
*n = '\0'; *n = '\0';
player_name = ++n; player_name = ++n;
while (*n) while (*n)
{ {
while (*n && *n != '\x0a') while (*n && *n != '\x0a')
{ {
n++; n++;
} }
if (*n != '\x0a') if (*n != '\x0a')
{ {
// Broken data // Broken data
break; break;
} }
*n = '\0'; *n = '\0';
n++; n++;
if (0 == strncmp("Team ", player_name, 5)) if (0 == strncmp("Team ", player_name, 5))
{ {
team_name = player_name; team_name = player_name;
team_number++; team_number++;
} }
else else
{ {
if (0 != strlen(player_name)) if (0 != strlen(player_name))
{ {
struct player *player = add_player(server, player_number); struct player *player = add_player(server, player_number);
if (NULL != player) if (NULL != player)
{ {
player->name = strdup(player_name); player->name = strdup(player_name);
player->team = team_number; player->team = team_number;
player->team_name = strdup(team_name); player->team_name = strdup(team_name);
} player_number++; } player_number++;
} }
} }
player_name = n; player_name = n;
} }
} }
*s = '\xFF'; *s = '\xFF';
} }
return DONE_FORCE; return DONE_FORCE;
} }
char *find_savage_game(char *gametype) char *find_savage_game(char *gametype)
{ {
if (0 == strcmp("RTSS", gametype)) if (0 == strcmp("RTSS", gametype))
{ {
return strdup("RTSS"); return strdup("RTSS");
} }
else else
{ {
return strdup("Unknown"); return strdup("Unknown");
} }
} }

View file

@ -1,55 +1,55 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: savage2.php,v 1.1 2008/05/22 14:16:11 tombuskens Exp $ * $Id: savage2.php,v 1.1 2008/05/22 14:16:11 tombuskens Exp $
*/ */
[savage2] [savage2]
status = "\x01" status = "\x01"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Savage 2 Protocol * Savage 2 Protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.1 $ * @version $Revision: 1.1 $
*/ */
class GameQ_Protocol_savage2 extends GameQ_Protocol class GameQ_Protocol_savage2 extends GameQ_Protocol
{ {
/* /*
* status packet * status packet
*/ */
public function status() public function status()
{ {
$this->p->skip(12); $this->p->skip(12);
$this->r->add('hostname', $this->p->readString()); $this->r->add('hostname', $this->p->readString());
$this->r->add('num_players', $this->p->readInt8()); $this->r->add('num_players', $this->p->readInt8());
$this->r->add('max_players', $this->p->readInt8()); $this->r->add('max_players', $this->p->readInt8());
$this->r->add('time', $this->p->readString()); $this->r->add('time', $this->p->readString());
$this->r->add('map', $this->p->readString()); $this->r->add('map', $this->p->readString());
$this->r->add('nextmap', $this->p->readString()); $this->r->add('nextmap', $this->p->readString());
$this->r->add('location', $this->p->readString()); $this->r->add('location', $this->p->readString());
$this->r->add('min_players', $this->p->readInt8()); $this->r->add('min_players', $this->p->readInt8());
$this->r->add('gametype', $this->p->readString()); $this->r->add('gametype', $this->p->readString());
$this->r->add('version', $this->p->readString()); $this->r->add('version', $this->p->readString());
$this->r->add('min_level', $this->p->readInt8()); $this->r->add('min_level', $this->p->readInt8());
} }
} }
?> ?>

View file

@ -1,46 +1,46 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_18(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
// REFERENCE: http://masterserver.savage2.s2games.com // REFERENCE: http://masterserver.savage2.s2games.com
fwrite($lgsl_fp, "\x01"); fwrite($lgsl_fp, "\x01");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
//---------------------------------------------------------+ //---------------------------------------------------------+
$buffer = substr($buffer, 12); // REMOVE HEADER $buffer = substr($buffer, 12); // REMOVE HEADER
$server['s']['name'] = lgsl_cut_string($buffer); $server['s']['name'] = lgsl_cut_string($buffer);
$server['s']['players'] = 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['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['time'] = lgsl_cut_string($buffer); $server['e']['time'] = lgsl_cut_string($buffer);
$server['s']['map'] = lgsl_cut_string($buffer); $server['s']['map'] = lgsl_cut_string($buffer);
$server['e']['nextmap'] = lgsl_cut_string($buffer); $server['e']['nextmap'] = lgsl_cut_string($buffer);
$server['e']['location'] = lgsl_cut_string($buffer); $server['e']['location'] = lgsl_cut_string($buffer);
$server['e']['minimum_players'] = ord(lgsl_cut_string($buffer)); $server['e']['minimum_players'] = ord(lgsl_cut_string($buffer));
$server['e']['gamemode'] = lgsl_cut_string($buffer); $server['e']['gamemode'] = lgsl_cut_string($buffer);
$server['e']['version'] = lgsl_cut_string($buffer); $server['e']['version'] = lgsl_cut_string($buffer);
$server['e']['minimum_level'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['minimum_level'] = ord(lgsl_cut_byte($buffer, 1));
// DOES NOT RETURN PLAYER INFORMATION // DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,65 +1,65 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: teeworlds.php,v 1.2 2009/03/09 13:36:32 tombuskens Exp $ * $Id: teeworlds.php,v 1.2 2009/03/09 13:36:32 tombuskens Exp $
*/ */
[teeworlds] [teeworlds]
status = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFgief" status = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFgief"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Teeworlds protocol * Teeworlds protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.2 $ * @version $Revision: 1.2 $
*/ */
class GameQ_Protocol_teeworlds extends GameQ_Protocol class GameQ_Protocol_teeworlds extends GameQ_Protocol
{ {
/* /*
* status packet * status packet
*/ */
public function status() public function status()
{ {
$this->p->skip(14); $this->p->skip(14);
$this->r->add('version', $this->p->readString()); $this->r->add('version', $this->p->readString());
$this->r->add('hostname', $this->p->readString()); $this->r->add('hostname', $this->p->readString());
$this->r->add('map', $this->p->readString()); $this->r->add('map', $this->p->readString());
$this->r->add('gametype', $this->p->readString()); $this->r->add('gametype', $this->p->readString());
$this->r->add('password', $this->p->readString()); $this->r->add('password', $this->p->readString());
$this->r->add('ping', $this->p->readString()); $this->r->add('ping', $this->p->readString());
$this->r->add('num_players', $this->p->readString()); $this->r->add('num_players', $this->p->readString());
$this->r->add('max_players', $this->p->readString()); $this->r->add('max_players', $this->p->readString());
$this->players(); $this->players();
} }
private function players() private function players()
{ {
while ($name = $this->p->readString()) { while ($name = $this->p->readString()) {
$this->r->addPlayer('name', $name); $this->r->addPlayer('name', $name);
$this->r->addPlayer('score', $this->p->readString()); $this->r->addPlayer('score', $this->p->readString());
} }
} }
} }
?> ?>

View file

@ -1,109 +1,109 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* Teeworlds Protocol * Teeworlds Protocol
* *
* @author Marcel Bößendörfer <m.boessendoerfer@marbis.net> * @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
*/ */
class GameQ_Protocols_Teeworlds extends GameQ_Protocols { class GameQ_Protocols_Teeworlds extends GameQ_Protocols {
/** /**
* Array of packets we want to look up. * Array of packets we want to look up.
* Each key should correspond to a defined method in this or a parent class * Each key should correspond to a defined method in this or a parent class
* *
* @var array * @var array
*/ */
protected $packets = array( protected $packets = array(
self::PACKET_ALL => "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x67\x69\x65\x33\x05", 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") // 0.5 Packet (not compatible, maybe some wants to implement "Teeworldsold")
//self::PACKET_STATUS => "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFgief", //self::PACKET_STATUS => "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFgief",
); );
/** /**
* Methods to be run when processing the response(s) * Methods to be run when processing the response(s)
* *
* @var array * @var array
*/ */
protected $process_methods = array( protected $process_methods = array(
"process_all" "process_all"
); );
/** /**
* Default port for this server type * Default port for this server type
* *
* @var int * @var int
*/ */
protected $port = 8303; // Default port, used if not set when instanced protected $port = 8303; // Default port, used if not set when instanced
/** /**
* The protocol being used * The protocol being used
* *
* @var string * @var string
*/ */
protected $protocol = 'teeworlds'; protected $protocol = 'teeworlds';
/** /**
* String name of this protocol class * String name of this protocol class
* *
* @var string * @var string
*/ */
protected $name = 'teeworlds'; protected $name = 'teeworlds';
/** /**
* Longer string name of this protocol class * Longer string name of this protocol class
* *
* @var string * @var string
*/ */
protected $name_long = "Teeworlds"; protected $name_long = "Teeworlds";
/* /*
* Internal methods * Internal methods
*/ */
protected function process_all() { protected function process_all() {
if(!$this->hasValidResponse(self::PACKET_ALL)) if(!$this->hasValidResponse(self::PACKET_ALL))
{ {
return array(); return array();
} }
$data = $this->packets_response[self::PACKET_ALL][0]; $data = $this->packets_response[self::PACKET_ALL][0];
$buf = new GameQ_Buffer($data); $buf = new GameQ_Buffer($data);
$result = new GameQ_Result(); $result = new GameQ_Result();
$buf->readString(); $buf->readString();
$result->add('version', $buf->readString()); $result->add('version', $buf->readString());
$result->add('hostname', $buf->readString()); $result->add('hostname', $buf->readString());
$result->add('map', $buf->readString()); $result->add('map', $buf->readString());
$result->add('game_descr', $buf->readString()); $result->add('game_descr', $buf->readString());
$result->add('flags', $buf->readString()); // not use about that $result->add('flags', $buf->readString()); // not use about that
$result->add('num_players', $buf->readString()); $result->add('num_players', $buf->readString());
$result->add('maxplayers', $buf->readString()); $result->add('maxplayers', $buf->readString());
$result->add('num_players_total', $buf->readString()); $result->add('num_players_total', $buf->readString());
$result->add('maxplayers_total', $buf->readString()); $result->add('maxplayers_total', $buf->readString());
// Players // Players
while ($buf->getLength()) { while ($buf->getLength()) {
$result->addPlayer('name', $buf->readString()); $result->addPlayer('name', $buf->readString());
$result->addPlayer('clan', $buf->readString()); $result->addPlayer('clan', $buf->readString());
$result->addPlayer('flag', $buf->readString()); $result->addPlayer('flag', $buf->readString());
$result->addPlayer('score', $buf->readString()); $result->addPlayer('score', $buf->readString());
$result->addPlayer('team', $buf->readString()); $result->addPlayer('team', $buf->readString());
} }
return $result->fetch(); return $result->fetch();
} }
} }

View file

@ -1,54 +1,54 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_21(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
fwrite($lgsl_fp,"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief"); fwrite($lgsl_fp,"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
$buffer = substr($buffer, 20); // REMOVE HEADER $buffer = substr($buffer, 20); // REMOVE HEADER
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
//---------------------------------------------------------+ //---------------------------------------------------------+
$server['s']['name'] = lgsl_cut_string($buffer); $server['s']['name'] = lgsl_cut_string($buffer);
$server['s']['map'] = lgsl_cut_string($buffer); $server['s']['map'] = lgsl_cut_string($buffer);
$server['e']['gamemode'] = lgsl_cut_string($buffer); $server['e']['gamemode'] = lgsl_cut_string($buffer);
$server['s']['password'] = lgsl_cut_string($buffer); $server['s']['password'] = lgsl_cut_string($buffer);
$server['e']['progress'] = lgsl_cut_string($buffer)."%"; $server['e']['progress'] = lgsl_cut_string($buffer)."%";
$server['s']['players'] = lgsl_cut_string($buffer); $server['s']['players'] = lgsl_cut_string($buffer);
$server['s']['playersmax'] = lgsl_cut_string($buffer); $server['s']['playersmax'] = lgsl_cut_string($buffer);
switch ($server['e']['gamemode']) switch ($server['e']['gamemode'])
{ {
case 0: $server['e']['gamemode'] = "Deathmatch"; break; case 0: $server['e']['gamemode'] = "Deathmatch"; break;
case 1: $server['e']['gamemode'] = "Team Deathmatch"; break; case 1: $server['e']['gamemode'] = "Team Deathmatch"; break;
case 2: $server['e']['gamemode'] = "Capture The Flag"; break; case 2: $server['e']['gamemode'] = "Capture The Flag"; break;
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
$player_key = 0; $player_key = 0;
while ($buffer) while ($buffer)
{ {
$server['p'][$player_key]['name'] = lgsl_cut_string($buffer); $server['p'][$player_key]['name'] = lgsl_cut_string($buffer);
$server['p'][$player_key]['score'] = lgsl_cut_string($buffer); $server['p'][$player_key]['score'] = lgsl_cut_string($buffer);
$player_key ++; $player_key ++;
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,144 +1,144 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * 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' }; char tee_serverstatus[14] = { '\x20', '\0', '\0', '\0', '\0', '\0', '\xFF', '\xFF', '\xFF', '\xFF', 'g', 'i', 'e', 'f' };
{ {
/* Teeworlds */ /* Teeworlds */
TEE_SERVER, /* id */ TEE_SERVER, /* id */
"TEE", /* type_prefix */ "TEE", /* type_prefix */
"tee", /* type_string */ "tee", /* type_string */
"-tee", /* type_option */ "-tee", /* type_option */
"Teeworlds", /* game_name */ "Teeworlds", /* game_name */
0, /* master */ 0, /* master */
35515, /* default_port */ 35515, /* default_port */
0, /* port_offset */ 0, /* port_offset */
0, /* flags */ 0, /* flags */
"gametype", /* game_rule */ "gametype", /* game_rule */
"TEE", /* template_var */ "TEE", /* template_var */
tee_serverstatus, /* status_packet */ tee_serverstatus, /* status_packet */
sizeof(tee_serverstatus), /* status_len */ sizeof(tee_serverstatus), /* status_len */
NULL, /* player_packet */ NULL, /* player_packet */
0, /* player_len */ 0, /* player_len */
NULL, /* rule_packet */ NULL, /* rule_packet */
0, /* rule_len */ 0, /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
display_tee_player_info, /* display_player_func */ display_tee_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
raw_display_tee_player_info, /* display_raw_player_func */ raw_display_tee_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_tee_player_info, /* display_xml_player_func */ xml_display_tee_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_tee_request_packet, /* status_query_func */ send_tee_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_tee_packet, /* packet_func */ deal_with_tee_packet, /* packet_func */
}, },
/* /*
* qstat 2.11 * qstat 2.11
* by Steve Jankowski * by Steve Jankowski
* *
* Teeworlds protocol * Teeworlds protocol
* Copyright 2008 ? Emiliano Leporati * Copyright 2008 ? Emiliano Leporati
* *
* Licensed under the Artistic License, see LICENSE.txt for license terms * Licensed under the Artistic License, see LICENSE.txt for license terms
* *
*/ */
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include "debug.h" #include "debug.h"
#include "qstat.h" #include "qstat.h"
#include "packet_manip.h" #include "packet_manip.h"
char tee_serverinfo[8] = { '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', 'o' }; char tee_serverinfo[8] = { '\xFF', '\xFF', '\xFF', '\xFF', 'i', 'n', 'f', 'o' };
query_status_t send_tee_request_packet( struct qserver *server ) query_status_t send_tee_request_packet( struct qserver *server )
{ {
return send_packet( server, server->type->status_packet, server->type->status_len ); 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 ) query_status_t deal_with_tee_packet( struct qserver *server, char *rawpkt, int pktlen )
{ {
// skip unimplemented ack, crc, etc // skip unimplemented ack, crc, etc
char *pkt = rawpkt + 6; char *pkt = rawpkt + 6;
char *tok = NULL, *version = NULL; char *tok = NULL, *version = NULL;
int i; int i;
struct player* player; struct player* player;
server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
if (0 == memcmp( pkt, tee_serverinfo, 8)) if (0 == memcmp( pkt, tee_serverinfo, 8))
{ {
pkt += 8; pkt += 8;
// version // version
version = strdup(pkt); pkt += strlen(pkt) + 1; version = strdup(pkt); pkt += strlen(pkt) + 1;
// server name // server name
server->server_name = strdup(pkt); pkt += strlen(pkt) + 1; server->server_name = strdup(pkt); pkt += strlen(pkt) + 1;
// map name // map name
server->map_name = strdup(pkt); pkt += strlen(pkt) + 1; server->map_name = strdup(pkt); pkt += strlen(pkt) + 1;
// game type // game type
switch(atoi(pkt)) { switch(atoi(pkt)) {
case 0: case 0:
add_rule( server, server->type->game_rule, "dm", NO_FLAGS); add_rule( server, server->type->game_rule, "dm", NO_FLAGS);
break; break;
case 1: case 1:
add_rule( server, server->type->game_rule, "tdm", NO_FLAGS); add_rule( server, server->type->game_rule, "tdm", NO_FLAGS);
break; break;
case 2: case 2:
add_rule( server, server->type->game_rule, "ctf", NO_FLAGS); add_rule( server, server->type->game_rule, "ctf", NO_FLAGS);
break; break;
default: default:
add_rule( server, server->type->game_rule, "unknown", NO_FLAGS); add_rule( server, server->type->game_rule, "unknown", NO_FLAGS);
break; break;
} }
pkt += strlen(pkt) + 1; pkt += strlen(pkt) + 1;
pkt += strlen(pkt) + 1; pkt += strlen(pkt) + 1;
pkt += strlen(pkt) + 1; pkt += strlen(pkt) + 1;
// num players // num players
server->num_players = atoi(pkt); pkt += strlen(pkt) + 1; server->num_players = atoi(pkt); pkt += strlen(pkt) + 1;
// max players // max players
server->max_players = atoi(pkt); pkt += strlen(pkt) + 1; server->max_players = atoi(pkt); pkt += strlen(pkt) + 1;
// players // players
for(i = 0; i < server->num_players; i++) for(i = 0; i < server->num_players; i++)
{ {
player = add_player( server, i ); player = add_player( server, i );
player->name = strdup(pkt); pkt += strlen(pkt) + 1; player->name = strdup(pkt); pkt += strlen(pkt) + 1;
player->score = atoi(pkt); pkt += strlen(pkt) + 1; player->score = atoi(pkt); pkt += strlen(pkt) + 1;
} }
// version reprise // version reprise
server->protocol_version = 0; server->protocol_version = 0;
if (NULL == (tok = strtok(version, "."))) return -1; if (NULL == (tok = strtok(version, "."))) return -1;
server->protocol_version |= (atoi(tok) & 0x000F) << 12; server->protocol_version |= (atoi(tok) & 0x000F) << 12;
if (NULL == (tok = strtok(NULL, "."))) return -1; if (NULL == (tok = strtok(NULL, "."))) return -1;
server->protocol_version |= (atoi(tok) & 0x000F) << 8; server->protocol_version |= (atoi(tok) & 0x000F) << 8;
if (NULL == (tok = strtok(NULL, "."))) return -1; if (NULL == (tok = strtok(NULL, "."))) return -1;
server->protocol_version |= (atoi(tok) & 0x00FF); server->protocol_version |= (atoi(tok) & 0x00FF);
free(version); free(version);
return DONE_FORCE; return DONE_FORCE;
} }
// unknown packet type // unknown packet type
return PKT_ERROR; return PKT_ERROR;
} }

View file

@ -1,63 +1,63 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: tribes.php,v 1.1 2007/07/07 14:20:21 tombuskens Exp $ * $Id: tribes.php,v 1.1 2007/07/07 14:20:21 tombuskens Exp $
*/ */
[tribes] [tribes]
status = "b++" status = "b++"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Tribes protocol * Tribes protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.1 $ * @version $Revision: 1.1 $
*/ */
class GameQ_Protocol_tribes extends GameQ_Protocol class GameQ_Protocol_tribes extends GameQ_Protocol
{ {
public function status() public function status()
{ {
// Header // Header
if ($this->p->read(4) != 'c++b') { if ($this->p->read(4) != 'c++b') {
throw new GameQ_ParsingException($this->p); throw new GameQ_ParsingException($this->p);
} }
// Variables // Variables
$this->r->add('game', $this->p->readPascalString()); $this->r->add('game', $this->p->readPascalString());
$this->r->add('version', $this->p->readPascalString()); $this->r->add('version', $this->p->readPascalString());
$this->r->add('hostname', $this->p->readPascalString()); $this->r->add('hostname', $this->p->readPascalString());
$this->r->add('dedicated', $this->p->readInt8()); $this->r->add('dedicated', $this->p->readInt8());
$this->r->add('password', $this->p->readInt8()); $this->r->add('password', $this->p->readInt8());
$this->r->add('num_players', $this->p->readInt8()); $this->r->add('num_players', $this->p->readInt8());
$this->r->add('max_players', $this->p->readInt8()); $this->r->add('max_players', $this->p->readInt8());
$this->r->add('cpu_lsb', $this->p->readInt8()); $this->r->add('cpu_lsb', $this->p->readInt8());
$this->r->add('cpu_msb', $this->p->readInt8()); $this->r->add('cpu_msb', $this->p->readInt8());
$this->r->add('mod', $this->p->readPascalString()); $this->r->add('mod', $this->p->readPascalString());
$this->r->add('gametype', $this->p->readPascalString()); $this->r->add('gametype', $this->p->readPascalString());
$this->r->add('map', $this->p->readPascalString()); $this->r->add('map', $this->p->readPascalString());
$this->r->add('motd', $this->p->readPascalString()); $this->r->add('motd', $this->p->readPascalString());
$this->r->add('teamcount', $this->p->readInt8()); // Not sure $this->r->add('teamcount', $this->p->readInt8()); // Not sure
// TODO: player listing // TODO: player listing
} }
} }
?> ?>

View file

@ -1,125 +1,125 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_23(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
// REFERENCE: // REFERENCE:
// http://siteinthe.us // http://siteinthe.us
// http://www.tribesmasterserver.com // http://www.tribesmasterserver.com
fwrite($lgsl_fp, "b++"); fwrite($lgsl_fp, "b++");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
$buffer = substr($buffer, 4); // REMOVE HEADER $buffer = substr($buffer, 4); // REMOVE HEADER
//---------------------------------------------------------+ //---------------------------------------------------------+
$server['s']['game'] = lgsl_cut_pascal($buffer); $server['s']['game'] = lgsl_cut_pascal($buffer);
$server['e']['version'] = lgsl_cut_pascal($buffer); $server['e']['version'] = lgsl_cut_pascal($buffer);
$server['s']['name'] = lgsl_cut_pascal($buffer); $server['s']['name'] = lgsl_cut_pascal($buffer);
$server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['password'] = 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']['players'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['playersmax'] = 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']['cpu'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['e']['mod'] = lgsl_cut_pascal($buffer); $server['e']['mod'] = lgsl_cut_pascal($buffer);
$server['e']['type'] = lgsl_cut_pascal($buffer); $server['e']['type'] = lgsl_cut_pascal($buffer);
$server['s']['map'] = lgsl_cut_pascal($buffer); $server['s']['map'] = lgsl_cut_pascal($buffer);
$server['e']['motd'] = lgsl_cut_pascal($buffer); $server['e']['motd'] = lgsl_cut_pascal($buffer);
$server['e']['teams'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['teams'] = ord(lgsl_cut_byte($buffer, 1));
//---------------------------------------------------------+ //---------------------------------------------------------+
$team_field = "?".lgsl_cut_pascal($buffer); $team_field = "?".lgsl_cut_pascal($buffer);
$team_field = split("\t", $team_field); $team_field = split("\t", $team_field);
foreach ($team_field as $key => $value) foreach ($team_field as $key => $value)
{ {
$value = substr($value, 1); $value = substr($value, 1);
$value = strtolower($value); $value = strtolower($value);
$team_field[$key] = $value; $team_field[$key] = $value;
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
$player_field = "?".lgsl_cut_pascal($buffer); $player_field = "?".lgsl_cut_pascal($buffer);
$player_field = split("\t", $player_field); $player_field = split("\t", $player_field);
foreach ($player_field as $key => $value) foreach ($player_field as $key => $value)
{ {
$value = substr($value, 1); $value = substr($value, 1);
$value = strtolower($value); $value = strtolower($value);
if ($value == "player name") { $value = "name"; } if ($value == "player name") { $value = "name"; }
$player_field[$key] = $value; $player_field[$key] = $value;
} }
$player_field[] = "unknown_1"; $player_field[] = "unknown_1";
$player_field[] = "unknown_2"; $player_field[] = "unknown_2";
//---------------------------------------------------------+ //---------------------------------------------------------+
for ($i=0; $i<$server['e']['teams']; $i++) for ($i=0; $i<$server['e']['teams']; $i++)
{ {
$team_name = lgsl_cut_pascal($buffer); $team_name = lgsl_cut_pascal($buffer);
$team_info = lgsl_cut_pascal($buffer); $team_info = lgsl_cut_pascal($buffer);
if (!$team_info) { continue; } if (!$team_info) { continue; }
$team_info = str_replace("%t", $team_name, $team_info); $team_info = str_replace("%t", $team_name, $team_info);
$team_info = split("\t", $team_info); $team_info = split("\t", $team_info);
foreach ($team_info as $key => $value) foreach ($team_info as $key => $value)
{ {
$field = $team_field[$key]; $field = $team_field[$key];
$value = trim($value); $value = trim($value);
if ($field == "team name") { $field = "name"; } if ($field == "team name") { $field = "name"; }
$server['t'][$i][$field] = $value; $server['t'][$i][$field] = $value;
} }
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
for ($i=0; $i<$server['s']['players']; $i++) for ($i=0; $i<$server['s']['players']; $i++)
{ {
$player_bits = array(); $player_bits = array();
$player_bits[] = ord(lgsl_cut_byte($buffer, 1)) * 4; // %p = PING $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)); // %l = PACKET LOSS
$player_bits[] = ord(lgsl_cut_byte($buffer, 1)); // %t = TEAM $player_bits[] = ord(lgsl_cut_byte($buffer, 1)); // %t = TEAM
$player_bits[] = lgsl_cut_pascal($buffer); // %n = PLAYER NAME $player_bits[] = lgsl_cut_pascal($buffer); // %n = PLAYER NAME
$player_info = lgsl_cut_pascal($buffer); $player_info = lgsl_cut_pascal($buffer);
if (!$player_info) { continue; } if (!$player_info) { continue; }
$player_info = str_replace(array("%p","%l","%t","%n"), $player_bits, $player_info); $player_info = str_replace(array("%p","%l","%t","%n"), $player_bits, $player_info);
$player_info = split("\t", $player_info); $player_info = split("\t", $player_info);
foreach ($player_info as $key => $value) foreach ($player_info as $key => $value)
{ {
$field = $player_field[$key]; $field = $player_field[$key];
$value = trim($value); $value = trim($value);
if ($field == "team") { $value = $server['t'][$value]['name']; } if ($field == "team") { $value = $server['t'][$value]['name']; }
$server['p'][$i][$field] = $value; $server['p'][$i][$field] = $value;
} }
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,247 +1,247 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski
*/ */
/* TRIBES */ /* TRIBES */
char tribes_info[] = { '`', '*', '*' }; char tribes_info[] = { '`', '*', '*' };
char tribes_players[] = { 'b', '*', '*' }; char tribes_players[] = { 'b', '*', '*' };
/* This is what the game sends to get minimal status /* This is what the game sends to get minimal status
{ '\020', '\03', '\377', 0, (unsigned char)0xc5, 6 }; { '\020', '\03', '\377', 0, (unsigned char)0xc5, 6 };
*/ */
char tribes_info_reponse[] = { 'a', '*', '*', 'b' }; char tribes_info_reponse[] = { 'a', '*', '*', 'b' };
char tribes_players_reponse[] = { 'c', '*', '*', 'b' }; char tribes_players_reponse[] = { 'c', '*', '*', 'b' };
char tribes_masterquery[] = { 0x10, 0x3, '\377', 0, 0x2 }; char tribes_masterquery[] = { 0x10, 0x3, '\377', 0, 0x2 };
char tribes_master_response[] = { 0x10, 0x6 }; char tribes_master_response[] = { 0x10, 0x6 };
{ {
/* TRIBES */ /* TRIBES */
TRIBES_SERVER, /* id */ TRIBES_SERVER, /* id */
"TBS", /* type_prefix */ "TBS", /* type_prefix */
"tbs", /* type_string */ "tbs", /* type_string */
"-tbs", /* type_option */ "-tbs", /* type_option */
"Tribes", /* game_name */ "Tribes", /* game_name */
0, /* master */ 0, /* master */
TRIBES_DEFAULT_PORT, /* default_port */ TRIBES_DEFAULT_PORT, /* default_port */
0, /* port_offset */ 0, /* port_offset */
TF_SINGLE_QUERY, /* flags */ TF_SINGLE_QUERY, /* flags */
"game", /* game_rule */ "game", /* game_rule */
"TRIBES", /* template_var */ "TRIBES", /* template_var */
(char*) &tribes_info, /* status_packet */ (char*) &tribes_info, /* status_packet */
sizeof( tribes_info), /* status_len */ sizeof( tribes_info), /* status_len */
(char*) &tribes_players, /* player_packet */ (char*) &tribes_players, /* player_packet */
sizeof( tribes_players), /* player_len */ sizeof( tribes_players), /* player_len */
(char*) &tribes_players, /* rule_packet */ (char*) &tribes_players, /* rule_packet */
sizeof( tribes_players), /* rule_len */ sizeof( tribes_players), /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
display_tribes_player_info, /* display_player_func */ display_tribes_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
raw_display_tribes_player_info, /* display_raw_player_func */ raw_display_tribes_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_tribes_player_info, /* display_xml_player_func */ xml_display_tribes_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_tribes_request_packet, /* status_query_func */ send_tribes_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_tribes_packet, /* packet_func */ deal_with_tribes_packet, /* packet_func */
}, },
query_status_t deal_with_tribes_packet(struct qserver *server, char *rawpkt, int pktlen) query_status_t deal_with_tribes_packet(struct qserver *server, char *rawpkt, int pktlen)
{ {
unsigned char *pkt, *end; unsigned char *pkt, *end;
int len, pnum, ping, packet_loss, n_teams, t; int len, pnum, ping, packet_loss, n_teams, t;
struct player *player; struct player *player;
struct player **teams = NULL; struct player **teams = NULL;
struct player **last_player = &server->players; struct player **last_player = &server->players;
char buf[24]; char buf[24];
debug( 2, "deal_with_tribes_packet %p, %d", server, pktlen ); debug( 2, "deal_with_tribes_packet %p, %d", server, pktlen );
if (server->server_name == NULL) if (server->server_name == NULL)
{ {
server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
} }
else else
{ {
gettimeofday(&server->packet_time1, NULL); gettimeofday(&server->packet_time1, NULL);
} }
if (pktlen < sizeof(tribes_info_reponse)) if (pktlen < sizeof(tribes_info_reponse))
{ {
return PKT_ERROR; return PKT_ERROR;
} }
if (strncmp(rawpkt, tribes_players_reponse, sizeof(tribes_players_reponse)) != 0) if (strncmp(rawpkt, tribes_players_reponse, sizeof(tribes_players_reponse)) != 0)
{ {
return PKT_ERROR; return PKT_ERROR;
} }
pkt = (unsigned char*) &rawpkt[sizeof(tribes_info_reponse)]; pkt = (unsigned char*) &rawpkt[sizeof(tribes_info_reponse)];
len = *pkt; /* game name: "Tribes" */ len = *pkt; /* game name: "Tribes" */
add_nrule(server, "gamename", (char*)pkt + 1, len); add_nrule(server, "gamename", (char*)pkt + 1, len);
pkt += len + 1; pkt += len + 1;
len = *pkt; /* version */ len = *pkt; /* version */
add_nrule(server, "version", (char*)pkt + 1, len); add_nrule(server, "version", (char*)pkt + 1, len);
pkt += len + 1; pkt += len + 1;
len = *pkt; /* server name */ len = *pkt; /* server name */
server->server_name = strndup((char*)pkt + 1, len); server->server_name = strndup((char*)pkt + 1, len);
pkt += len + 1; pkt += len + 1;
add_rule(server, "dedicated", *pkt ? "1" : "0", NO_FLAGS); add_rule(server, "dedicated", *pkt ? "1" : "0", NO_FLAGS);
pkt++; /* flag: dedicated server */ pkt++; /* flag: dedicated server */
add_rule(server, "needpass", *pkt ? "1" : "0", NO_FLAGS); add_rule(server, "needpass", *pkt ? "1" : "0", NO_FLAGS);
pkt++; /* flag: password on server */ pkt++; /* flag: password on server */
server->num_players = *pkt++; server->num_players = *pkt++;
server->max_players = *pkt++; server->max_players = *pkt++;
sprintf(buf, "%u", (unsigned int)pkt[0] + (unsigned int)pkt[1] *256); sprintf(buf, "%u", (unsigned int)pkt[0] + (unsigned int)pkt[1] *256);
add_rule(server, "cpu", buf, NO_FLAGS); add_rule(server, "cpu", buf, NO_FLAGS);
pkt++; /* cpu speed, lsb */ pkt++; /* cpu speed, lsb */
pkt++; /* cpu speed, msb */ pkt++; /* cpu speed, msb */
len = *pkt; /* Mod (game) */ len = *pkt; /* Mod (game) */
add_nrule(server, "mods", (char*)pkt + 1, len); add_nrule(server, "mods", (char*)pkt + 1, len);
pkt += len + 1; pkt += len + 1;
len = *pkt; /* game (mission): "C&H" */ len = *pkt; /* game (mission): "C&H" */
add_nrule(server, "game", (char*)pkt + 1, len); add_nrule(server, "game", (char*)pkt + 1, len);
pkt += len + 1; pkt += len + 1;
len = *pkt; /* Mission (map) */ len = *pkt; /* Mission (map) */
server->map_name = strndup((char*)pkt + 1, len); server->map_name = strndup((char*)pkt + 1, len);
pkt += len + 1; pkt += len + 1;
len = *pkt; /* description (contains Admin: and Email: ) */ len = *pkt; /* description (contains Admin: and Email: ) */
debug( 2, "%.*s\n", len, pkt + 1); debug( 2, "%.*s\n", len, pkt + 1);
pkt += len + 1; pkt += len + 1;
n_teams = *pkt++; /* number of teams */ n_teams = *pkt++; /* number of teams */
if (n_teams == 255) if (n_teams == 255)
{ {
return PKT_ERROR; return PKT_ERROR;
} }
sprintf(buf, "%d", n_teams); sprintf(buf, "%d", n_teams);
add_rule(server, "numteams", buf, NO_FLAGS); add_rule(server, "numteams", buf, NO_FLAGS);
len = *pkt; /* first title */ len = *pkt; /* first title */
debug( 2, "%.*s\n", len, pkt + 1); debug( 2, "%.*s\n", len, pkt + 1);
pkt += len + 1; pkt += len + 1;
len = *pkt; /* second title */ len = *pkt; /* second title */
debug( 2, "%.*s\n", len, pkt + 1); debug( 2, "%.*s\n", len, pkt + 1);
pkt += len + 1; pkt += len + 1;
if (n_teams > 1) if (n_teams > 1)
{ {
teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams); teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams);
for (t = 0; t < n_teams; t++) for (t = 0; t < n_teams; t++)
{ {
teams[t] = (struct player*)calloc(1, sizeof(struct player)); teams[t] = (struct player*)calloc(1, sizeof(struct player));
teams[t]->number = TRIBES_TEAM; teams[t]->number = TRIBES_TEAM;
teams[t]->team = t; teams[t]->team = t;
len = *pkt; /* team name */ len = *pkt; /* team name */
teams[t]->name = strndup((char*)pkt + 1, len); teams[t]->name = strndup((char*)pkt + 1, len);
debug( 2, "team#0 <%.*s>\n", len, pkt + 1); debug( 2, "team#0 <%.*s>\n", len, pkt + 1);
pkt += len + 1; pkt += len + 1;
len = *pkt; /* team score */ len = *pkt; /* team score */
if (len > 2) if (len > 2)
{ {
strncpy(buf, (char*)pkt + 1+3, len - 3); strncpy(buf, (char*)pkt + 1+3, len - 3);
buf[len - 3] = '\0'; buf[len - 3] = '\0';
} }
else else
{ {
debug( 2, "%s score len %d\n", server->arg, len); debug( 2, "%s score len %d\n", server->arg, len);
buf[0] = '\0'; buf[0] = '\0';
} }
teams[t]->frags = atoi(buf); teams[t]->frags = atoi(buf);
debug( 2, "team#0 <%.*s>\n", len - 3, pkt + 1+3); debug( 2, "team#0 <%.*s>\n", len - 3, pkt + 1+3);
pkt += len + 1; pkt += len + 1;
} }
} }
else else
{ {
len = *pkt; /* DM team? */ len = *pkt; /* DM team? */
debug( 2, "%.*s\n", len, pkt + 1); debug( 2, "%.*s\n", len, pkt + 1);
pkt += len + 1; pkt += len + 1;
pkt++; pkt++;
n_teams = 0; n_teams = 0;
} }
pnum = 0; pnum = 0;
while ((char*)pkt < (rawpkt + pktlen)) while ((char*)pkt < (rawpkt + pktlen))
{ {
ping = (unsigned int) *pkt << 2; ping = (unsigned int) *pkt << 2;
pkt++; pkt++;
packet_loss = *pkt; packet_loss = *pkt;
pkt++; pkt++;
debug( 2, "player#%d, team #%d\n", pnum, (int) *pkt); debug( 2, "player#%d, team #%d\n", pnum, (int) *pkt);
pkt++; pkt++;
len = *pkt; len = *pkt;
if ((char*)pkt + len > (rawpkt + pktlen)) if ((char*)pkt + len > (rawpkt + pktlen))
{ {
break; break;
} }
player = (struct player*)calloc(1, sizeof(struct player)); player = (struct player*)calloc(1, sizeof(struct player));
player->team = pkt[ - 1]; player->team = pkt[ - 1];
if (n_teams && player->team < n_teams) if (n_teams && player->team < n_teams)
{ {
player->team_name = teams[player->team]->name; player->team_name = teams[player->team]->name;
} }
else if (player->team == 255 && n_teams) else if (player->team == 255 && n_teams)
{ {
player->team_name = "Unknown"; player->team_name = "Unknown";
} }
player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
player->ping = ping; player->ping = ping;
player->packet_loss = packet_loss; player->packet_loss = packet_loss;
player->name = strndup((char*)pkt + 1, len); player->name = strndup((char*)pkt + 1, len);
debug( 2, "player#%d, name %.*s\n", pnum, len, pkt + 1); debug( 2, "player#%d, name %.*s\n", pnum, len, pkt + 1);
pkt += len + 1; pkt += len + 1;
len = *pkt; len = *pkt;
debug( 2, "player#%d, info <%.*s>\n", pnum, len, pkt + 1); debug( 2, "player#%d, info <%.*s>\n", pnum, len, pkt + 1);
end = (unsigned char*)strchr((char*)pkt + 9, 0x9); end = (unsigned char*)strchr((char*)pkt + 9, 0x9);
if (end) if (end)
{ {
strncpy(buf, (char*)pkt + 9, end - (pkt + 9)); strncpy(buf, (char*)pkt + 9, end - (pkt + 9));
buf[end - (pkt + 9)] = '\0'; buf[end - (pkt + 9)] = '\0';
player->frags = atoi(buf); player->frags = atoi(buf);
debug( 2, "player#%d, score <%.*s>\n", pnum, (unsigned)(end - (pkt + 9)), pkt + 9); debug( 2, "player#%d, score <%.*s>\n", pnum, (unsigned)(end - (pkt + 9)), pkt + 9);
} }
*last_player = player; *last_player = player;
last_player = &player->next; last_player = &player->next;
pkt += len + 1; pkt += len + 1;
pnum++; pnum++;
} }
for (t = n_teams; t;) for (t = n_teams; t;)
{ {
t--; t--;
teams[t]->next = server->players; teams[t]->next = server->players;
server->players = teams[t]; server->players = teams[t];
} }
free(teams); free(teams);
return DONE_AUTO; return DONE_AUTO;
} }

View file

@ -1,104 +1,104 @@
<?php <?php
/** /**
* This file is part of GameQ. * This file is part of GameQ.
* *
* GameQ is free software; you can redistribute it and/or modify * GameQ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* GameQ is distributed in the hope that it will be useful, * GameQ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* $Id: tribes2.php,v 1.1 2007/07/07 14:52:01 tombuskens Exp $ * $Id: tribes2.php,v 1.1 2007/07/07 14:52:01 tombuskens Exp $
*/ */
[tribes2] [tribes2]
info = "\x0E\x02\x01\x02\x03\x04" info = "\x0E\x02\x01\x02\x03\x04"
status = "\x12\x02\x01\x02\x03\x04" status = "\x12\x02\x01\x02\x03\x04"
require_once GAMEQ_BASE . 'Protocol.php'; require_once GAMEQ_BASE . 'Protocol.php';
/** /**
* Tribes 2 protocol * Tribes 2 protocol
* *
* @author Tom Buskens <t.buskens@deviation.nl> * @author Tom Buskens <t.buskens@deviation.nl>
* @version $Revision: 1.1 $ * @version $Revision: 1.1 $
*/ */
class GameQ_Protocol_tribes2 extends GameQ_Protocol class GameQ_Protocol_tribes2 extends GameQ_Protocol
{ {
public function info() public function info()
{ {
// Header // Header
$this->p->skip(6); $this->p->skip(6);
$this->r->add('version', $this->p->readPascalString()); $this->r->add('version', $this->p->readPascalString());
// TODO: Protocol and build numbers // TODO: Protocol and build numbers
$this->p->skip(12); $this->p->skip(12);
$this->r->add('hostname', $this->p->readPascalString()); $this->r->add('hostname', $this->p->readPascalString());
} }
public function status() public function status()
{ {
// Header // Header
$this->p->skip(6); $this->p->skip(6);
// Vars // Vars
$this->r->add('mod', $this->p->readPascalString()); $this->r->add('mod', $this->p->readPascalString());
$this->r->add('gametype', $this->p->readPascalString()); $this->r->add('gametype', $this->p->readPascalString());
$this->r->add('map', $this->p->readPascalString()); $this->r->add('map', $this->p->readPascalString());
$this->readBitflag($this->p->read()); $this->readBitflag($this->p->read());
$this->r->add('num_players', $this->p->readInt8()); $this->r->add('num_players', $this->p->readInt8());
$this->r->add('max_players', $this->p->readInt8()); $this->r->add('max_players', $this->p->readInt8());
$this->r->add('num_bots', $this->p->readInt8()); $this->r->add('num_bots', $this->p->readInt8());
$this->r->add('cpu', $this->p->readInt16()); $this->r->add('cpu', $this->p->readInt16());
$this->r->add('info', $this->p->readPascalString()); $this->r->add('info', $this->p->readPascalString());
$this->p->skip(2); $this->p->skip(2);
$this->teams(); $this->teams();
$this->players(); $this->players();
} }
private function teams() private function teams()
{ {
$num_teams = $this->p->read(); $num_teams = $this->p->read();
$this->r->add('num_teams', $num_teams); $this->r->add('num_teams', $num_teams);
$this->p->skip(); $this->p->skip();
for ($i = 0; $i < $num_teams; $i++) { for ($i = 0; $i < $num_teams; $i++) {
$this->r->addTeam('name', $this->p->readString("\x09")); $this->r->addTeam('name', $this->p->readString("\x09"));
$this->r->addTeam('score', $this->p->readString("\x0a")); $this->r->addTeam('score', $this->p->readString("\x0a"));
} }
} }
private function players() private function players()
{ {
// TODO // TODO
} }
private function readBitflag($flag) private function readBitflag($flag)
{ {
$vars = array('dedicated', 'password', 'linux', $vars = array('dedicated', 'password', 'linux',
'tournament', 'no_alias'); 'tournament', 'no_alias');
$bit = 1; $bit = 1;
foreach ($vars as $var) { foreach ($vars as $var) {
$value = ($flag & $bit) ? 1 : 0; $value = ($flag & $bit) ? 1 : 0;
$this->r->add($var, $value); $this->r->add($var, $value);
$bit *= 2; $bit *= 2;
} }
} }
} }
?> ?>

View file

@ -1,72 +1,72 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_25(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
// REFERENCE: http://www.tribesnext.com // REFERENCE: http://www.tribesnext.com
fwrite($lgsl_fp,"\x12\x02\x21\x21\x21\x21"); fwrite($lgsl_fp,"\x12\x02\x21\x21\x21\x21");
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
$buffer = substr($buffer, 6); // REMOVE HEADER $buffer = substr($buffer, 6); // REMOVE HEADER
//---------------------------------------------------------+ //---------------------------------------------------------+
$server['s']['game'] = lgsl_cut_pascal($buffer); $server['s']['game'] = lgsl_cut_pascal($buffer);
$server['e']['gamemode'] = lgsl_cut_pascal($buffer); $server['e']['gamemode'] = lgsl_cut_pascal($buffer);
$server['s']['map'] = lgsl_cut_pascal($buffer); $server['s']['map'] = lgsl_cut_pascal($buffer);
$server['e']['bit_flags'] = ord(lgsl_cut_byte($buffer, 1)); $server['e']['bit_flags'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['players'] = 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['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['bots'] = 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']['cpu'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['e']['motd'] = lgsl_cut_pascal($buffer); $server['e']['motd'] = lgsl_cut_pascal($buffer);
$server['e']['unknown'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); $server['e']['unknown'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['e']['dedicated'] = ($server['e']['bit_flags'] & 1) ? "1" : "0"; $server['e']['dedicated'] = ($server['e']['bit_flags'] & 1) ? "1" : "0";
$server['s']['password'] = ($server['e']['bit_flags'] & 2) ? "1" : "0"; $server['s']['password'] = ($server['e']['bit_flags'] & 2) ? "1" : "0";
$server['e']['os'] = ($server['e']['bit_flags'] & 4) ? "L" : "W"; $server['e']['os'] = ($server['e']['bit_flags'] & 4) ? "L" : "W";
$server['e']['tournament'] = ($server['e']['bit_flags'] & 8) ? "1" : "0"; $server['e']['tournament'] = ($server['e']['bit_flags'] & 8) ? "1" : "0";
$server['e']['no_alias'] = ($server['e']['bit_flags'] & 16) ? "1" : "0"; $server['e']['no_alias'] = ($server['e']['bit_flags'] & 16) ? "1" : "0";
unset($server['e']['bit_flags']); unset($server['e']['bit_flags']);
//---------------------------------------------------------+ //---------------------------------------------------------+
$team_total = lgsl_cut_string($buffer, 0, "\x0A"); $team_total = lgsl_cut_string($buffer, 0, "\x0A");
for ($i=0; $i<$team_total; $i++) for ($i=0; $i<$team_total; $i++)
{ {
$server['t'][$i]['name'] = lgsl_cut_string($buffer, 0, "\x09"); $server['t'][$i]['name'] = lgsl_cut_string($buffer, 0, "\x09");
$server['t'][$i]['score'] = lgsl_cut_string($buffer, 0, "\x0A"); $server['t'][$i]['score'] = lgsl_cut_string($buffer, 0, "\x0A");
} }
$player_total = lgsl_cut_string($buffer, 0, "\x0A"); $player_total = lgsl_cut_string($buffer, 0, "\x0A");
for ($i=0; $i<$player_total; $i++) for ($i=0; $i<$player_total; $i++)
{ {
lgsl_cut_byte($buffer, 1); // ? 16 lgsl_cut_byte($buffer, 1); // ? 16
lgsl_cut_byte($buffer, 1); // ? 8 or 14 = BOT / 12 = ALIAS / 11 = NORMAL 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 if (ord($buffer[0]) < 32) { lgsl_cut_byte($buffer, 1); } // ? 8 PREFIXES SOME NAMES
$server['p'][$i]['name'] = lgsl_cut_string($buffer, 0, "\x11"); $server['p'][$i]['name'] = lgsl_cut_string($buffer, 0, "\x11");
lgsl_cut_string($buffer, 0, "\x09"); // ALWAYS BLANK lgsl_cut_string($buffer, 0, "\x09"); // ALWAYS BLANK
$server['p'][$i]['team'] = lgsl_cut_string($buffer, 0, "\x09"); $server['p'][$i]['team'] = lgsl_cut_string($buffer, 0, "\x09");
$server['p'][$i]['score'] = lgsl_cut_string($buffer, 0, "\x0A"); $server['p'][$i]['score'] = lgsl_cut_string($buffer, 0, "\x0A");
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,455 +1,455 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski
*/ */
/* TRIBES 2 */ /* TRIBES 2 */
#define TRIBES2_QUERY_GAME_TYPES 2 #define TRIBES2_QUERY_GAME_TYPES 2
#define TRIBES2_QUERY_MASTER 6 #define TRIBES2_QUERY_MASTER 6
#define TRIBES2_QUERY_PING 14 #define TRIBES2_QUERY_PING 14
#define TRIBES2_QUERY_INFO 18 #define TRIBES2_QUERY_INFO 18
#define TRIBES2_RESPONSE_GAME_TYPES 4 #define TRIBES2_RESPONSE_GAME_TYPES 4
#define TRIBES2_RESPONSE_MASTER 8 #define TRIBES2_RESPONSE_MASTER 8
#define TRIBES2_RESPONSE_PING 16 #define TRIBES2_RESPONSE_PING 16
#define TRIBES2_RESPONSE_INFO 20 #define TRIBES2_RESPONSE_INFO 20
#define TRIBES2_NO_COMPRESS 2 #define TRIBES2_NO_COMPRESS 2
#define TRIBES2_DEFAULT_PACKET_INDEX 255 #define TRIBES2_DEFAULT_PACKET_INDEX 255
#define TRIBES2_STATUS_DEDICATED (1<<0) #define TRIBES2_STATUS_DEDICATED (1<<0)
#define TRIBES2_STATUS_PASSWORD (1<<1) #define TRIBES2_STATUS_PASSWORD (1<<1)
#define TRIBES2_STATUS_LINUX (1<<2) #define TRIBES2_STATUS_LINUX (1<<2)
#define TRIBES2_STATUS_TOURNAMENT (1<<3) #define TRIBES2_STATUS_TOURNAMENT (1<<3)
#define TRIBES2_STATUS_NOALIAS (1<<4) #define TRIBES2_STATUS_NOALIAS (1<<4)
#define TRIBES2_STATUS_TEAMDAMAGE (1<<5) #define TRIBES2_STATUS_TEAMDAMAGE (1<<5)
#define TRIBES2_STATUS_TOURNAMENT_VER3 (1<<6) #define TRIBES2_STATUS_TOURNAMENT_VER3 (1<<6)
#define TRIBES2_STATUS_NOALIAS_VER3 (1<<7) #define TRIBES2_STATUS_NOALIAS_VER3 (1<<7)
char tribes2_game_types_request[] = { TRIBES2_QUERY_GAME_TYPES, 0, 1,2,3,4 }; 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_ping[] = { TRIBES2_QUERY_PING, TRIBES2_NO_COMPRESS, 1,2,3,4 };
char tribes2_info[] = { TRIBES2_QUERY_INFO, TRIBES2_NO_COMPRESS, 1,2,3,4 }; char tribes2_info[] = { TRIBES2_QUERY_INFO, TRIBES2_NO_COMPRESS, 1,2,3,4 };
unsigned char tribes2_masterquery[] = { unsigned char tribes2_masterquery[] = {
TRIBES2_QUERY_MASTER, 128, /* <= build 22228, this was 0 */ TRIBES2_QUERY_MASTER, 128, /* <= build 22228, this was 0 */
11,12,13,14, 11,12,13,14,
255, 255,
3, 'a', 'n', 'y', 3, 'a', 'n', 'y',
3, 'a', 'n', 'y', 3, 'a', 'n', 'y',
0, 255, /* min/max players */ 0, 255, /* min/max players */
0xff, 0xff, 0xff, 0xff, /* region mask */ 0xff, 0xff, 0xff, 0xff, /* region mask */
0, 0, 0, 0, /* build version */ 0, 0, 0, 0, /* build version */
0, /* status */ 0, /* status */
255, /* max bots */ 255, /* max bots */
0, 0, /* min cpu */ 0, 0, /* min cpu */
0 /* # buddies */ 0 /* # buddies */
}; };
#define TRIBES2_ID_OFFSET 2 #define TRIBES2_ID_OFFSET 2
{ {
/* TRIBES 2 */ /* TRIBES 2 */
TRIBES2_SERVER, /* id */ TRIBES2_SERVER, /* id */
"T2S", /* type_prefix */ "T2S", /* type_prefix */
"t2s", /* type_string */ "t2s", /* type_string */
"-t2s", /* type_option */ "-t2s", /* type_option */
"Tribes 2", /* game_name */ "Tribes 2", /* game_name */
0, /* master */ 0, /* master */
TRIBES2_DEFAULT_PORT, /* default_port */ TRIBES2_DEFAULT_PORT, /* default_port */
0, /* port_offset */ 0, /* port_offset */
0, /* flags */ 0, /* flags */
"game", /* game_rule */ "game", /* game_rule */
"TRIBES2", /* template_var */ "TRIBES2", /* template_var */
(char*) &tribes2_ping, /* status_packet */ (char*) &tribes2_ping, /* status_packet */
sizeof( tribes2_ping), /* status_len */ sizeof( tribes2_ping), /* status_len */
(char*) &tribes2_info, /* player_packet */ (char*) &tribes2_info, /* player_packet */
sizeof( tribes2_info), /* player_len */ sizeof( tribes2_info), /* player_len */
(char*) NULL, /* rule_packet */ (char*) NULL, /* rule_packet */
0, /* rule_len */ 0, /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
display_tribes2_player_info, /* display_player_func */ display_tribes2_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
raw_display_tribes2_player_info, /* display_raw_player_func */ raw_display_tribes2_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_tribes2_player_info, /* display_xml_player_func */ xml_display_tribes2_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_tribes2_request_packet, /* status_query_func */ send_tribes2_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_tribes2_packet, /* packet_func */ deal_with_tribes2_packet, /* packet_func */
}, },
query_status_t send_tribes2_request_packet(struct qserver *server) query_status_t send_tribes2_request_packet(struct qserver *server)
{ {
int rc; int rc;
if (server->flags &FLAG_BROADCAST && server->server_name == NULL) if (server->flags &FLAG_BROADCAST && server->server_name == NULL)
{ {
rc = send_broadcast(server, server->type->status_packet, server->type->status_len); rc = send_broadcast(server, server->type->status_packet, server->type->status_len);
} }
else if (server->server_name == NULL) else if (server->server_name == NULL)
{ {
rc = send(server->fd, server->type->status_packet, server->type->status_len, 0); rc = send(server->fd, server->type->status_packet, server->type->status_len, 0);
} }
else else
{ {
rc = send(server->fd, server->type->player_packet, server->type->player_len, 0); rc = send(server->fd, server->type->player_packet, server->type->player_len, 0);
} }
if (rc == SOCKET_ERROR) if (rc == SOCKET_ERROR)
{ {
return send_error( server, rc ); return send_error( server, rc );
} }
register_send(server); register_send(server);
return rc; return rc;
} }
void get_tribes2_player_type(struct player *player) void get_tribes2_player_type(struct player *player)
{ {
char *name = player->name; char *name = player->name;
for (; *name; name++) for (; *name; name++)
{ {
switch (*name) switch (*name)
{ {
case 0x8: case 0x8:
player->type_flag = PLAYER_TYPE_NORMAL; player->type_flag = PLAYER_TYPE_NORMAL;
continue; continue;
case 0xc: case 0xc:
player->type_flag = PLAYER_TYPE_ALIAS; player->type_flag = PLAYER_TYPE_ALIAS;
continue; continue;
case 0xe: case 0xe:
player->type_flag = PLAYER_TYPE_BOT; player->type_flag = PLAYER_TYPE_BOT;
continue; continue;
case 0xb: case 0xb:
break; break;
default: default:
continue; continue;
} }
name++; name++;
if (isprint(*name)) if (isprint(*name))
{ {
char *n = name; char *n = name;
for (; isprint(*n); n++) for (; isprint(*n); n++)
; ;
player->tribe_tag = strndup(name, n - name); player->tribe_tag = strndup(name, n - name);
name = n; name = n;
} }
if (! *name) if (! *name)
{ {
break; break;
} }
} }
} }
query_status_t deal_with_tribes2_packet(struct qserver *server, char *pkt, int pktlen) query_status_t deal_with_tribes2_packet(struct qserver *server, char *pkt, int pktlen)
{ {
char str[256], *pktstart = pkt, *term, *start; char str[256], *pktstart = pkt, *term, *start;
unsigned int minimum_net_protocol, build_version, i, t, len, s, status; unsigned int minimum_net_protocol, build_version, i, t, len, s, status;
unsigned int net_protocol; unsigned int net_protocol;
unsigned short cpu_speed; unsigned short cpu_speed;
int n_teams = 0, n_players; int n_teams = 0, n_players;
struct player **teams = NULL, *player; struct player **teams = NULL, *player;
struct player **last_player = &server->players; struct player **last_player = &server->players;
int query_version; int query_version;
debug( 2, "deal_with_tribes2_packet %p, %d", server, pktlen ); debug( 2, "deal_with_tribes2_packet %p, %d", server, pktlen );
pkt[pktlen] = '\0'; pkt[pktlen] = '\0';
if (server->server_name == NULL) if (server->server_name == NULL)
{ {
server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
} }
/* /*
else else
gettimeofday( &server->packet_time1, NULL); gettimeofday( &server->packet_time1, NULL);
*/ */
if (pkt[0] == TRIBES2_RESPONSE_PING) if (pkt[0] == TRIBES2_RESPONSE_PING)
{ {
if (pkt[6] < 4 || pkt[6] > 12 || strncmp(pkt + 7, "VER", 3) != 0) if (pkt[6] < 4 || pkt[6] > 12 || strncmp(pkt + 7, "VER", 3) != 0)
{ {
return PKT_ERROR; return PKT_ERROR;
} }
strncpy(str, pkt + 10, pkt[6] - 3); strncpy(str, pkt + 10, pkt[6] - 3);
str[pkt[6] - 3] = '\0'; str[pkt[6] - 3] = '\0';
query_version = atoi(str); query_version = atoi(str);
add_nrule(server, "queryversion", pkt + 7, pkt[6]); add_nrule(server, "queryversion", pkt + 7, pkt[6]);
pkt += 7+pkt[6]; pkt += 7+pkt[6];
server->protocol_version = query_version; server->protocol_version = query_version;
if (query_version != 3 && query_version != 5) if (query_version != 3 && query_version != 5)
{ {
server->server_name = strdup("Unknown query version"); server->server_name = strdup("Unknown query version");
return PKT_ERROR; return PKT_ERROR;
} }
if (query_version == 5) if (query_version == 5)
{ {
net_protocol = swap_long_from_little(pkt); net_protocol = swap_long_from_little(pkt);
sprintf(str, "%u", net_protocol); sprintf(str, "%u", net_protocol);
add_rule(server, "net_protocol", str, NO_FLAGS); add_rule(server, "net_protocol", str, NO_FLAGS);
pkt += 4; pkt += 4;
} }
minimum_net_protocol = swap_long_from_little(pkt); minimum_net_protocol = swap_long_from_little(pkt);
sprintf(str, "%u", minimum_net_protocol); sprintf(str, "%u", minimum_net_protocol);
add_rule(server, "minimum_net_protocol", str, NO_FLAGS); add_rule(server, "minimum_net_protocol", str, NO_FLAGS);
pkt += 4; pkt += 4;
build_version = swap_long_from_little(pkt); build_version = swap_long_from_little(pkt);
sprintf(str, "%u", build_version); sprintf(str, "%u", build_version);
add_rule(server, "build_version", str, NO_FLAGS); add_rule(server, "build_version", str, NO_FLAGS);
pkt += 4; pkt += 4;
server->server_name = strndup(pkt + 1, *(unsigned char*)(pkt)); server->server_name = strndup(pkt + 1, *(unsigned char*)(pkt));
/* Always send the player request because the ping packet /* Always send the player request because the ping packet
* contains very little information */ * contains very little information */
send_player_request_packet(server); send_player_request_packet(server);
return 0; return 0;
} }
else if (pkt[0] != TRIBES2_RESPONSE_INFO) else if (pkt[0] != TRIBES2_RESPONSE_INFO)
{ {
return PKT_ERROR; return PKT_ERROR;
} }
pkt += 6; pkt += 6;
for (i = 0; i < *(unsigned char*)pkt; i++) for (i = 0; i < *(unsigned char*)pkt; i++)
if (!isprint(pkt[i + 1])) if (!isprint(pkt[i + 1]))
{ {
return PKT_ERROR; return PKT_ERROR;
} }
add_nrule(server, server->type->game_rule, pkt + 1, *(unsigned char*)pkt); add_nrule(server, server->type->game_rule, pkt + 1, *(unsigned char*)pkt);
server->game = strndup(pkt + 1, *(unsigned char*)pkt); server->game = strndup(pkt + 1, *(unsigned char*)pkt);
pkt += *pkt + 1; pkt += *pkt + 1;
add_nrule(server, "mission", pkt + 1, *(unsigned char*)pkt); add_nrule(server, "mission", pkt + 1, *(unsigned char*)pkt);
pkt += *pkt + 1; pkt += *pkt + 1;
server->map_name = strndup(pkt + 1, *(unsigned char*)pkt); server->map_name = strndup(pkt + 1, *(unsigned char*)pkt);
pkt += *pkt + 1; pkt += *pkt + 1;
status = *(unsigned char*)pkt; status = *(unsigned char*)pkt;
sprintf(str, "%u", status); sprintf(str, "%u", status);
add_rule(server, "status", str, NO_FLAGS); add_rule(server, "status", str, NO_FLAGS);
if (status &TRIBES2_STATUS_DEDICATED) if (status &TRIBES2_STATUS_DEDICATED)
{ {
add_rule(server, "dedicated", "1", NO_FLAGS); add_rule(server, "dedicated", "1", NO_FLAGS);
} }
if (status &TRIBES2_STATUS_PASSWORD) if (status &TRIBES2_STATUS_PASSWORD)
{ {
add_rule(server, "password", "1", NO_FLAGS); add_rule(server, "password", "1", NO_FLAGS);
} }
if (status &TRIBES2_STATUS_LINUX) if (status &TRIBES2_STATUS_LINUX)
{ {
add_rule(server, "linux", "1", NO_FLAGS); add_rule(server, "linux", "1", NO_FLAGS);
} }
if (status &TRIBES2_STATUS_TEAMDAMAGE) if (status &TRIBES2_STATUS_TEAMDAMAGE)
{ {
add_rule(server, "teamdamage", "1", NO_FLAGS); add_rule(server, "teamdamage", "1", NO_FLAGS);
} }
if (server->protocol_version == 3) if (server->protocol_version == 3)
{ {
if (status &TRIBES2_STATUS_TOURNAMENT_VER3) if (status &TRIBES2_STATUS_TOURNAMENT_VER3)
{ {
add_rule(server, "tournament", "1", NO_FLAGS); add_rule(server, "tournament", "1", NO_FLAGS);
} }
if (status &TRIBES2_STATUS_NOALIAS_VER3) if (status &TRIBES2_STATUS_NOALIAS_VER3)
{ {
add_rule(server, "no_aliases", "1", NO_FLAGS); add_rule(server, "no_aliases", "1", NO_FLAGS);
} }
} }
else else
{ {
if (status &TRIBES2_STATUS_TOURNAMENT) if (status &TRIBES2_STATUS_TOURNAMENT)
{ {
add_rule(server, "tournament", "1", NO_FLAGS); add_rule(server, "tournament", "1", NO_FLAGS);
} }
if (status &TRIBES2_STATUS_NOALIAS) if (status &TRIBES2_STATUS_NOALIAS)
{ {
add_rule(server, "no_aliases", "1", NO_FLAGS); add_rule(server, "no_aliases", "1", NO_FLAGS);
} }
} }
pkt++; pkt++;
server->num_players = *(unsigned char*)pkt; server->num_players = *(unsigned char*)pkt;
pkt++; pkt++;
server->max_players = *(unsigned char*)pkt; server->max_players = *(unsigned char*)pkt;
pkt++; pkt++;
sprintf(str, "%u", *(unsigned char*)pkt); sprintf(str, "%u", *(unsigned char*)pkt);
add_rule(server, "bot_count", str, NO_FLAGS); add_rule(server, "bot_count", str, NO_FLAGS);
pkt++; pkt++;
cpu_speed = swap_short_from_little(pkt); cpu_speed = swap_short_from_little(pkt);
sprintf(str, "%hu", cpu_speed); sprintf(str, "%hu", cpu_speed);
add_rule(server, "cpu_speed", str, NO_FLAGS); add_rule(server, "cpu_speed", str, NO_FLAGS);
pkt += 2; pkt += 2;
if (strcmp(server->server_name, "VER3") == 0) if (strcmp(server->server_name, "VER3") == 0)
{ {
free(server->server_name); free(server->server_name);
server->server_name = strndup(pkt + 1, *(unsigned char*)pkt); server->server_name = strndup(pkt + 1, *(unsigned char*)pkt);
} }
else else
{ {
add_nrule(server, "info", pkt + 1, *(unsigned char*)pkt); add_nrule(server, "info", pkt + 1, *(unsigned char*)pkt);
} }
pkt += *(unsigned char*)pkt + 1; pkt += *(unsigned char*)pkt + 1;
len = swap_short_from_little(pkt); len = swap_short_from_little(pkt);
pkt += 2; pkt += 2;
start = pkt; start = pkt;
if (len + (pkt - pktstart) > pktlen) if (len + (pkt - pktstart) > pktlen)
{ {
len -= (len + (pkt - pktstart)) - pktlen; len -= (len + (pkt - pktstart)) - pktlen;
} }
if (len == 0 || pkt - pktstart >= pktlen) if (len == 0 || pkt - pktstart >= pktlen)
{ {
goto info_done; goto info_done;
} }
term = strchr(pkt, 0xa); term = strchr(pkt, 0xa);
if (!term) if (!term)
{ {
goto info_done; goto info_done;
} }
*term = '\0'; *term = '\0';
n_teams = atoi(pkt); n_teams = atoi(pkt);
sprintf(str, "%d", n_teams); sprintf(str, "%d", n_teams);
add_rule(server, "numteams", str, NO_FLAGS); add_rule(server, "numteams", str, NO_FLAGS);
pkt = term + 1; pkt = term + 1;
if (pkt - pktstart >= pktlen) if (pkt - pktstart >= pktlen)
{ {
goto info_done; goto info_done;
} }
teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams); teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams);
for (t = 0; t < n_teams; t++) for (t = 0; t < n_teams; t++)
{ {
teams[t] = (struct player*)calloc(1, sizeof(struct player)); teams[t] = (struct player*)calloc(1, sizeof(struct player));
teams[t]->number = TRIBES_TEAM; teams[t]->number = TRIBES_TEAM;
teams[t]->team = t; teams[t]->team = t;
/* team name */ /* team name */
term = strchr(pkt, 0x9); term = strchr(pkt, 0x9);
if (!term) if (!term)
{ {
n_teams = t; n_teams = t;
goto info_done; goto info_done;
} teams[t]->name = strndup(pkt, term - pkt); } teams[t]->name = strndup(pkt, term - pkt);
pkt = term + 1; pkt = term + 1;
term = strchr(pkt, 0xa); term = strchr(pkt, 0xa);
if (!term) if (!term)
{ {
n_teams = t; n_teams = t;
goto info_done; goto info_done;
} }
*term = '\0'; *term = '\0';
teams[t]->frags = atoi(pkt); teams[t]->frags = atoi(pkt);
pkt = term + 1; pkt = term + 1;
if (pkt - pktstart >= pktlen) if (pkt - pktstart >= pktlen)
{ {
goto info_done; goto info_done;
} }
} }
term = strchr(pkt, 0xa); term = strchr(pkt, 0xa);
if (!term || term - start >= len) if (!term || term - start >= len)
{ {
goto info_done; goto info_done;
} }
*term = '\0'; *term = '\0';
n_players = atoi(pkt); n_players = atoi(pkt);
pkt = term + 1; pkt = term + 1;
for (i = 0; i < n_players && pkt - start < len; i++) for (i = 0; i < n_players && pkt - start < len; i++)
{ {
pkt++; /* skip first byte (0x10) */ pkt++; /* skip first byte (0x10) */
if (pkt - start >= len) if (pkt - start >= len)
{ {
break; break;
} }
player = (struct player*)calloc(1, sizeof(struct player)); player = (struct player*)calloc(1, sizeof(struct player));
term = strchr(pkt, 0x11); term = strchr(pkt, 0x11);
if (!term || term - start >= len) if (!term || term - start >= len)
{ {
free(player); free(player);
break; break;
} player->name = strndup(pkt, term - pkt); } player->name = strndup(pkt, term - pkt);
get_tribes2_player_type(player); get_tribes2_player_type(player);
pkt = term + 1; pkt = term + 1;
pkt++; /* skip 0x9 */ pkt++; /* skip 0x9 */
if (pkt - start >= len) if (pkt - start >= len)
{ {
break; break;
} }
term = strchr(pkt, 0x9); term = strchr(pkt, 0x9);
if (!term || term - start >= len) if (!term || term - start >= len)
{ {
free(player->name); free(player->name);
free(player); free(player);
break; break;
} }
for (t = 0; t < n_teams; t++) for (t = 0; t < n_teams; t++)
{ {
if (term - pkt == strlen(teams[t]->name) && strncmp(pkt, teams[t]->name, term - pkt) == 0) if (term - pkt == strlen(teams[t]->name) && strncmp(pkt, teams[t]->name, term - pkt) == 0)
{ {
break; break;
} }
} }
if (t == n_teams) if (t == n_teams)
{ {
player->team = - 1; player->team = - 1;
player->team_name = "Unassigned"; player->team_name = "Unassigned";
} }
else else
{ {
player->team = t; player->team = t;
player->team_name = teams[t]->name; player->team_name = teams[t]->name;
} }
player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
pkt = term + 1; pkt = term + 1;
for (s = 0; *pkt != 0xa && pkt - start < len; pkt++) for (s = 0; *pkt != 0xa && pkt - start < len; pkt++)
{ {
str[s++] = *pkt; str[s++] = *pkt;
} }
str[s] = '\0'; str[s] = '\0';
player->frags = atoi(str); player->frags = atoi(str);
if (*pkt == 0xa) if (*pkt == 0xa)
{ {
pkt++; pkt++;
} }
*last_player = player; *last_player = player;
last_player = &player->next; last_player = &player->next;
} }
info_done: info_done:
for (t = n_teams; t;) for (t = n_teams; t;)
{ {
t--; t--;
teams[t]->next = server->players; teams[t]->next = server->players;
server->players = teams[t]; server->players = teams[t];
} }
if (teams) if (teams)
{ {
free(teams); free(teams);
} }
return DONE_FORCE; return DONE_FORCE;
} }

View file

@ -1,90 +1,90 @@
/*----------------------------------------------------------------------------------------------------------\ /*----------------------------------------------------------------------------------------------------------\
| | | |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] | | [ 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) | | 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) function lgsl_query_12(&$server, &$lgsl_need, &$lgsl_fp)
{ {
//---------------------------------------------------------+ //---------------------------------------------------------+
// REFERENCE: // REFERENCE:
// VICE CITY CURRENTLY ONLY SUPPORTS THE 'i' CHALLENGE // VICE CITY CURRENTLY ONLY SUPPORTS THE 'i' CHALLENGE
if ($server['b']['type'] == "samp") { $challenge_packet = "SAMP\x21\x21\x21\x21\x00\x00"; } 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; } 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"; } if ($lgsl_need['s']) { $challenge_packet .= "i"; }
elseif ($lgsl_need['e']) { $challenge_packet .= "r"; } elseif ($lgsl_need['e']) { $challenge_packet .= "r"; }
elseif ($lgsl_need['p']) { $challenge_packet .= "d"; } elseif ($lgsl_need['p']) { $challenge_packet .= "d"; }
fwrite($lgsl_fp, $challenge_packet); fwrite($lgsl_fp, $challenge_packet);
$buffer = fread($lgsl_fp, 4096); $buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
//---------------------------------------------------------+ //---------------------------------------------------------+
$buffer = substr($buffer, 10); // REMOVE HEADER $buffer = substr($buffer, 10); // REMOVE HEADER
$response_type = lgsl_cut_byte($buffer, 1); $response_type = lgsl_cut_byte($buffer, 1);
//---------------------------------------------------------+ //---------------------------------------------------------+
if ($response_type == "i") if ($response_type == "i")
{ {
$lgsl_need['s'] = FALSE; $lgsl_need['s'] = FALSE;
$server['s']['password'] = ord(lgsl_cut_byte($buffer, 1)); $server['s']['password'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); $server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['s']['playersmax'] = 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['s']['name'] = lgsl_cut_pascal($buffer, 4);
$server['e']['gamemode'] = lgsl_cut_pascal($buffer, 4); $server['e']['gamemode'] = lgsl_cut_pascal($buffer, 4);
$server['s']['map'] = lgsl_cut_pascal($buffer, 4); $server['s']['map'] = lgsl_cut_pascal($buffer, 4);
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
elseif ($response_type == "r") elseif ($response_type == "r")
{ {
$lgsl_need['e'] = FALSE; $lgsl_need['e'] = FALSE;
$item_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); $item_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
for ($i=0; $i<$item_total; $i++) for ($i=0; $i<$item_total; $i++)
{ {
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
$data_key = strtolower(lgsl_cut_pascal($buffer)); $data_key = strtolower(lgsl_cut_pascal($buffer));
$data_value = lgsl_cut_pascal($buffer); $data_value = lgsl_cut_pascal($buffer);
$server['e'][$data_key] = $data_value; $server['e'][$data_key] = $data_value;
} }
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
elseif ($response_type == "d") elseif ($response_type == "d")
{ {
$lgsl_need['p'] = FALSE; $lgsl_need['p'] = FALSE;
$player_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S"); $player_total = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
for ($i=0; $i<$player_total; $i++) for ($i=0; $i<$player_total; $i++)
{ {
if (!$buffer) { return FALSE; } if (!$buffer) { return FALSE; }
$server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1)); $server['p'][$i]['pid'] = ord(lgsl_cut_byte($buffer, 1));
$server['p'][$i]['name'] = lgsl_cut_pascal($buffer); $server['p'][$i]['name'] = lgsl_cut_pascal($buffer);
$server['p'][$i]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); $server['p'][$i]['score'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S");
$server['p'][$i]['ping'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S"); $server['p'][$i]['ping'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "S");
} }
} }
//---------------------------------------------------------+ //---------------------------------------------------------+
return TRUE; return TRUE;
} }

View file

@ -1,241 +1,241 @@
LICENSE: The Artistic License 2.0 LICENSE: The Artistic License 2.0
/* /*
* qstat.h * qstat.h
* by Steve Jankowski * by Steve Jankowski
* steve@qstat.org * steve@qstat.org
* http://www.qstat.org * http://www.qstat.org
* *
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski
*/ */
{ {
/* World in Confict PROTOCOL */ /* World in Confict PROTOCOL */
WIC_PROTOCOL_SERVER, /* id */ WIC_PROTOCOL_SERVER, /* id */
"WICS", /* type_prefix */ "WICS", /* type_prefix */
"wics", /* type_string */ "wics", /* type_string */
"-wics", /* type_option */ "-wics", /* type_option */
"World in Conflict", /* game_name */ "World in Conflict", /* game_name */
0, /* master */ 0, /* master */
0, /* default_port */ 0, /* default_port */
0, /* port_offset */ 0, /* port_offset */
TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG, /* flags */ TF_TCP_CONNECT|TF_QUERY_ARG_REQUIRED|TF_QUERY_ARG, /* flags */
"N/A", /* game_rule */ "N/A", /* game_rule */
"WICPROTOCOL", /* template_var */ "WICPROTOCOL", /* template_var */
NULL, /* status_packet */ NULL, /* status_packet */
0, /* status_len */ 0, /* status_len */
NULL, /* player_packet */ NULL, /* player_packet */
0, /* player_len */ 0, /* player_len */
NULL, /* rule_packet */ NULL, /* rule_packet */
0, /* rule_len */ 0, /* rule_len */
NULL, /* master_packet */ NULL, /* master_packet */
0, /* master_len */ 0, /* master_len */
NULL, /* master_protocol */ NULL, /* master_protocol */
NULL, /* master_query */ NULL, /* master_query */
display_wic_player_info, /* display_player_func */ display_wic_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */ display_server_rules, /* display_rule_func */
raw_display_wic_player_info, /* display_raw_player_func */ raw_display_wic_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */ raw_display_server_rules, /* display_raw_rule_func */
xml_display_wic_player_info, /* display_xml_player_func */ xml_display_wic_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */ xml_display_server_rules, /* display_xml_rule_func */
send_wic_request_packet, /* status_query_func */ send_wic_request_packet, /* status_query_func */
NULL, /* rule_query_func */ NULL, /* rule_query_func */
NULL, /* player_query_func */ NULL, /* player_query_func */
deal_with_wic_packet, /* packet_func */ deal_with_wic_packet, /* packet_func */
}, },
/* /*
* qstat 2.8 * qstat 2.8
* by Steve Jankowski * by Steve Jankowski
* *
* World in Conflict Protocol * World in Conflict Protocol
* Copyright 2007 Steven Hartland * Copyright 2007 Steven Hartland
* *
* Licensed under the Artistic License, see LICENSE.txt for license terms * Licensed under the Artistic License, see LICENSE.txt for license terms
* *
*/ */
#include <sys/types.h> #include <sys/types.h>
#ifndef _WIN32 #ifndef _WIN32
#include <sys/socket.h> #include <sys/socket.h>
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
#include "debug.h" #include "debug.h"
#include "qstat.h" #include "qstat.h"
#include "packet_manip.h" #include "packet_manip.h"
query_status_t send_wic_request_packet( struct qserver *server ) query_status_t send_wic_request_packet( struct qserver *server )
{ {
char buf[256]; char buf[256];
int serverport = get_param_i_value( server, "port", 0 ); int serverport = get_param_i_value( server, "port", 0 );
char *password = get_param_value( server, "password", "N/A" ); char *password = get_param_value( server, "password", "N/A" );
change_server_port( server, serverport, 1 ); change_server_port( server, serverport, 1 );
if ( get_player_info ) if ( get_player_info )
{ {
server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY; server->flags |= TF_PLAYER_QUERY|TF_RULES_QUERY;
sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/listplayers\x0d\x0a/exit\x0d\x0a", password ); sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/listplayers\x0d\x0a/exit\x0d\x0a", password );
server->saved_data.pkt_index = 2; server->saved_data.pkt_index = 2;
} }
else else
{ {
server->flags |= TF_STATUS_QUERY; server->flags |= TF_STATUS_QUERY;
sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/exit\x0d\x0a", password ); sprintf( buf, "%s\x0d\x0a/listsettings\x0d\x0a/exit\x0d\x0a", password );
server->saved_data.pkt_index = 1; server->saved_data.pkt_index = 1;
} }
return send_packet( server, buf, strlen( buf ) ); return send_packet( server, buf, strlen( buf ) );
} }
query_status_t deal_with_wic_packet( struct qserver *server, char *rawpkt, int pktlen ) query_status_t deal_with_wic_packet( struct qserver *server, char *rawpkt, int pktlen )
{ {
char *s, *end, *team = NULL; char *s, *end, *team = NULL;
int mode = server->n_servers, slot, score; int mode = server->n_servers, slot, score;
char name[256], role[256]; 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 ) ); 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->ping_total += time_delta( &packet_recv_time, &server->packet_time1 );
server->n_requests++; server->n_requests++;
if ( 0 == pktlen ) if ( 0 == pktlen )
{ {
// Invalid password // Invalid password
return REQ_ERROR; return REQ_ERROR;
} }
gettimeofday( &server->packet_time1, NULL); gettimeofday( &server->packet_time1, NULL);
rawpkt[pktlen]= '\0'; rawpkt[pktlen]= '\0';
end = &rawpkt[pktlen]; end = &rawpkt[pktlen];
s = rawpkt; s = rawpkt;
while ( NULL != s ) while ( NULL != s )
{ {
int len = strlen( s ); int len = strlen( s );
*(s + len - 2) = '\0'; // strip off \x0D\x0A *(s + len - 2) = '\0'; // strip off \x0D\x0A
debug( 2, "Line[%d]: %s", mode, s ); debug( 2, "Line[%d]: %s", mode, s );
if ( 0 == mode ) if ( 0 == mode )
{ {
// Settings // Settings
// TODO: make parse safe // TODO: make parse safe
if ( 0 == strncmp( s, "Settings: ", 9 ) ) if ( 0 == strncmp( s, "Settings: ", 9 ) )
{ {
// Server Rule // Server Rule
char *key = s + 10; char *key = s + 10;
char *value = strchr( key, ' ' ); char *value = strchr( key, ' ' );
*value = '\0'; *value = '\0';
value++; value++;
debug( 2, "key: '%s' = '%s'", key, value ); debug( 2, "key: '%s' = '%s'", key, value );
if ( 0 == strcmp( "myGameName", key ) ) if ( 0 == strcmp( "myGameName", key ) )
{ {
server->server_name = strdup( value ); server->server_name = strdup( value );
} }
else if ( 0 == strcmp( "myMapFilename", key ) ) else if ( 0 == strcmp( "myMapFilename", key ) )
{ {
server->map_name = strdup( value ); server->map_name = strdup( value );
} }
else if ( 0 == strcmp( "myMaxPlayers", key ) ) else if ( 0 == strcmp( "myMaxPlayers", key ) )
{ {
server->max_players = atoi( value ); server->max_players = atoi( value );
} }
else if ( 0 == strcmp( "myCurrentNumberOfPlayers", key ) ) else if ( 0 == strcmp( "myCurrentNumberOfPlayers", key ) )
{ {
server->num_players = atoi( value); server->num_players = atoi( value);
} }
else else
{ {
add_rule( server, key, value, NO_FLAGS ); add_rule( server, key, value, NO_FLAGS );
} }
} }
else if ( 0 == strcmp( "Listing players", s ) ) else if ( 0 == strcmp( "Listing players", s ) )
{ {
// end of rules request // end of rules request
server->saved_data.pkt_index--; server->saved_data.pkt_index--;
mode++; mode++;
} }
else if ( 0 == strcmp( "Exit confirmed.", s ) ) else if ( 0 == strcmp( "Exit confirmed.", s ) )
{ {
server->n_servers = mode; server->n_servers = mode;
return DONE_FORCE; return DONE_FORCE;
} }
} }
else if ( 1 == mode ) else if ( 1 == mode )
{ {
// Player info // Player info
if ( 0 == strncmp( s, "Team: ", 6 ) ) if ( 0 == strncmp( s, "Team: ", 6 ) )
{ {
team = s + 6; team = s + 6;
debug( 2, "Team: %s", team ); debug( 2, "Team: %s", team );
} }
else if ( 4 == sscanf( s, "Slot: %d Role: %s Score: %d Name: %255[^\x0d\x0a]", &slot, role, &score, name ) ) else if ( 4 == sscanf( s, "Slot: %d Role: %s Score: %d Name: %255[^\x0d\x0a]", &slot, role, &score, name ) )
{ {
// Player info // Player info
struct player *player = add_player( server, server->n_player_info ); struct player *player = add_player( server, server->n_player_info );
if ( NULL != player ) if ( NULL != player )
{ {
player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
player->name = strdup( name ); player->name = strdup( name );
player->score = score; player->score = score;
player->team_name = team; player->team_name = team;
player->tribe_tag = strdup( role ); player->tribe_tag = strdup( role );
// Indicate if its a bot // Indicate if its a bot
player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0; player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0;
} }
debug( 2, "player %d, role %s, score %d, name %s", slot, role, score, name ); 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 ) ) else if ( 3 == sscanf( s, "Slot: %d Role: Score: %d Name: %255[^\x0d\x0a]", &slot, &score, name ) )
{ {
// Player info // Player info
struct player *player = add_player( server, server->n_player_info ); struct player *player = add_player( server, server->n_player_info );
if ( NULL != player ) if ( NULL != player )
{ {
player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM; player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
player->name = strdup( name ); player->name = strdup( name );
player->score = score; player->score = score;
player->team_name = team; player->team_name = team;
// Indicate if its a bot // Indicate if its a bot
player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0; player->type_flag = ( 0 == strcmp( name, "Computer: Balanced" ) ) ? 1 : 0;
} }
debug( 2, "player %d, score %d, name %s", slot, score, name ); debug( 2, "player %d, score %d, name %s", slot, score, name );
} }
else if ( 0 == strcmp( "Exit confirmed.", s ) ) else if ( 0 == strcmp( "Exit confirmed.", s ) )
{ {
server->n_servers = mode; server->n_servers = mode;
return DONE_FORCE; return DONE_FORCE;
} }
} }
s += len; s += len;
if ( s + 1 < end ) if ( s + 1 < end )
{ {
s++; // next line s++; // next line
} }
else else
{ {
s = NULL; s = NULL;
} }
} }
server->n_servers = mode; server->n_servers = mode;
if ( 0 == server->saved_data.pkt_index ) if ( 0 == server->saved_data.pkt_index )
{ {
server->map_name = strdup( "N/A" ); server->map_name = strdup( "N/A" );
return DONE_FORCE; return DONE_FORCE;
} }
return INPROGRESS; return INPROGRESS;
} }