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

View File

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

View File

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

546
games.txt
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,240 +1,240 @@
var async = require('async');
module.exports = require('./core').extend({
init: function() {
this._super();
this.byteorder = 'be';
},
run: function(state) {
var self = this;
this.sendCommand(2,'',function(data) {
state.raw = splitFields(data.toString());
state.raw.CLIENTS.forEach(function(client) {
client.name = client.NAME;
delete client.NAME;
client.ping = parseInt(client.PING);
delete client.PING;
state.players.push(client);
});
delete state.raw.CLIENTS;
if('NAME' in state.raw) state.name = state.raw.NAME;
if('MAXCLIENTS' in state.raw) state.maxplayers = state.raw.MAXCLIENTS;
if(self.trueTest(state.raw.AUTH)) state.password = true;
self.finish(state);
});
},
sendCommand: function(cmd,password,c) {
var self = this;
var body = new Buffer(16);
body.write(password,0,15,'utf8');
var encrypted = encrypt(cmd,body);
var packets = {};
this.udpSend(encrypted, function(buffer) {
if(buffer.length < 20) return;
var data = decrypt(buffer);
if(data.zero != 0) return;
packets[data.packetNum] = data.body;
if(Object.keys(packets).length != data.packetTotal) return;
var out = [];
for(var i = 0; i < data.packetTotal; i++) {
if(!(i in packets)) return self.fatal('Missing packet #'+i);
out.push(packets[i]);
}
c(Buffer.concat(out));
return true;
});
}
});
function splitFields(str,subMode) {
var splitter,delim;
if(subMode) {
splitter = '=';
delim = ',';
} else {
splitter = ': ';
delim = '\n';
}
var split = str.split(delim);
var out = {};
if(!subMode) {
out.CHANNELS = [];
out.CLIENTS = [];
}
split.forEach(function(one) {
var equal = one.indexOf(splitter);
var key = equal == -1 ? one : one.substr(0,equal);
if(!key || key == '\0') return;
var value = equal == -1 ? '' : one.substr(equal+splitter.length);
if(!subMode && key == 'CHANNEL') out.CHANNELS.push(splitFields(value,true));
else if(!subMode && key == 'CLIENT') out.CLIENTS.push(splitFields(value,true));
else out[key] = value;
});
return out;
}
function randInt(min,max) {
return Math.floor(Math.random()*(max-min+1)+min)
}
function crc(body) {
var crc = 0;
for(var i = 0; i < body.length; i++) {
crc = crc_table[crc>>8] ^ body.readUInt8(i) ^ (crc<<8);
crc &= 0xffff;
}
return crc;
}
function encrypt(cmd,body) {
var headerKeyStart = randInt(0,0xff);
var headerKeyAdd = randInt(1,0xff);
var bodyKeyStart = randInt(0,0xff);
var bodyKeyAdd = randInt(1,0xff);
var header = new Buffer(20);
header.writeUInt8(headerKeyStart,0);
header.writeUInt8(headerKeyAdd,1);
header.writeUInt16BE(cmd,4);
header.writeUInt16BE(body.length,8);
header.writeUInt16BE(body.length,10);
header.writeUInt16BE(1,12);
header.writeUInt16BE(0,14);
header.writeUInt8(bodyKeyStart,16);
header.writeUInt8(bodyKeyAdd,17);
header.writeUInt16BE(crc(body),18);
var offset = headerKeyStart;
for(var i = 2; i < header.length; i++) {
var val = header.readUInt8(i);
val += code_head.charCodeAt(offset) + ((i-2) % 5);
val = val & 0xff;
header.writeUInt8(val,i);
offset = (offset+headerKeyAdd) & 0xff;
}
offset = bodyKeyStart;
for(var i = 0; i < body.length; i++) {
var val = body.readUInt8(i);
val += code_body.charCodeAt(offset) + (i % 72);
val = val & 0xff;
body.writeUInt8(val,i);
offset = (offset+bodyKeyAdd) & 0xff;
}
return Buffer.concat([header,body]);
}
function decrypt(data) {
var header = data.slice(0,20);
var body = data.slice(20);
var headerKeyStart = header.readUInt8(0);
var headerKeyAdd = header.readUInt8(1);
var offset = headerKeyStart;
for(var i = 2; i < header.length; i++) {
var val = header.readUInt8(i);
val -= code_head.charCodeAt(offset) + ((i-2) % 5);
val = val & 0xff;
header.writeUInt8(val,i);
offset = (offset+headerKeyAdd) & 0xff;
}
var bodyKeyStart = header.readUInt8(16);
var bodyKeyAdd = header.readUInt8(17);
offset = bodyKeyStart;
for(var i = 0; i < body.length; i++) {
var val = body.readUInt8(i);
val -= code_body.charCodeAt(offset) + (i % 72);
val = val & 0xff;
body.writeUInt8(val,i);
offset = (offset+bodyKeyAdd) & 0xff;
}
// header format:
// key, zero, cmd, echo, totallength, thislength
// totalpacket, packetnum, body key, crc
return {
zero: header.readUInt16BE(2),
cmd: header.readUInt16BE(4),
packetTotal: header.readUInt16BE(12),
packetNum: header.readUInt16BE(14),
body: body
};
}
var code_head =
'\x80\xe5\x0e\x38\xba\x63\x4c\x99\x88\x63\x4c\xd6\x54\xb8\x65\x7e'+
'\xbf\x8a\xf0\x17\x8a\xaa\x4d\x0f\xb7\x23\x27\xf6\xeb\x12\xf8\xea'+
'\x17\xb7\xcf\x52\x57\xcb\x51\xcf\x1b\x14\xfd\x6f\x84\x38\xb5\x24'+
'\x11\xcf\x7a\x75\x7a\xbb\x78\x74\xdc\xbc\x42\xf0\x17\x3f\x5e\xeb'+
'\x74\x77\x04\x4e\x8c\xaf\x23\xdc\x65\xdf\xa5\x65\xdd\x7d\xf4\x3c'+
'\x4c\x95\xbd\xeb\x65\x1c\xf4\x24\x5d\x82\x18\xfb\x50\x86\xb8\x53'+
'\xe0\x4e\x36\x96\x1f\xb7\xcb\xaa\xaf\xea\xcb\x20\x27\x30\x2a\xae'+
'\xb9\x07\x40\xdf\x12\x75\xc9\x09\x82\x9c\x30\x80\x5d\x8f\x0d\x09'+
'\xa1\x64\xec\x91\xd8\x8a\x50\x1f\x40\x5d\xf7\x08\x2a\xf8\x60\x62'+
'\xa0\x4a\x8b\xba\x4a\x6d\x00\x0a\x93\x32\x12\xe5\x07\x01\x65\xf5'+
'\xff\xe0\xae\xa7\x81\xd1\xba\x25\x62\x61\xb2\x85\xad\x7e\x9d\x3f'+
'\x49\x89\x26\xe5\xd5\xac\x9f\x0e\xd7\x6e\x47\x94\x16\x84\xc8\xff'+
'\x44\xea\x04\x40\xe0\x33\x11\xa3\x5b\x1e\x82\xff\x7a\x69\xe9\x2f'+
'\xfb\xea\x9a\xc6\x7b\xdb\xb1\xff\x97\x76\x56\xf3\x52\xc2\x3f\x0f'+
'\xb6\xac\x77\xc4\xbf\x59\x5e\x80\x74\xbb\xf2\xde\x57\x62\x4c\x1a'+
'\xff\x95\x6d\xc7\x04\xa2\x3b\xc4\x1b\x72\xc7\x6c\x82\x60\xd1\x0d';
var code_body =
'\x82\x8b\x7f\x68\x90\xe0\x44\x09\x19\x3b\x8e\x5f\xc2\x82\x38\x23'+
'\x6d\xdb\x62\x49\x52\x6e\x21\xdf\x51\x6c\x76\x37\x86\x50\x7d\x48'+
'\x1f\x65\xe7\x52\x6a\x88\xaa\xc1\x32\x2f\xf7\x54\x4c\xaa\x6d\x7e'+
'\x6d\xa9\x8c\x0d\x3f\xff\x6c\x09\xb3\xa5\xaf\xdf\x98\x02\xb4\xbe'+
'\x6d\x69\x0d\x42\x73\xe4\x34\x50\x07\x30\x79\x41\x2f\x08\x3f\x42'+
'\x73\xa7\x68\xfa\xee\x88\x0e\x6e\xa4\x70\x74\x22\x16\xae\x3c\x81'+
'\x14\xa1\xda\x7f\xd3\x7c\x48\x7d\x3f\x46\xfb\x6d\x92\x25\x17\x36'+
'\x26\xdb\xdf\x5a\x87\x91\x6f\xd6\xcd\xd4\xad\x4a\x29\xdd\x7d\x59'+
'\xbd\x15\x34\x53\xb1\xd8\x50\x11\x83\x79\x66\x21\x9e\x87\x5b\x24'+
'\x2f\x4f\xd7\x73\x34\xa2\xf7\x09\xd5\xd9\x42\x9d\xf8\x15\xdf\x0e'+
'\x10\xcc\x05\x04\x35\x81\xb2\xd5\x7a\xd2\xa0\xa5\x7b\xb8\x75\xd2'+
'\x35\x0b\x39\x8f\x1b\x44\x0e\xce\x66\x87\x1b\x64\xac\xe1\xca\x67'+
'\xb4\xce\x33\xdb\x89\xfe\xd8\x8e\xcd\x58\x92\x41\x50\x40\xcb\x08'+
'\xe1\x15\xee\xf4\x64\xfe\x1c\xee\x25\xe7\x21\xe6\x6c\xc6\xa6\x2e'+
'\x52\x23\xa7\x20\xd2\xd7\x28\x07\x23\x14\x24\x3d\x45\xa5\xc7\x90'+
'\xdb\x77\xdd\xea\x38\x59\x89\x32\xbc\x00\x3a\x6d\x61\x4e\xdb\x29';
var crc_table = [
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
];
var async = require('async');
module.exports = require('./core').extend({
init: function() {
this._super();
this.byteorder = 'be';
},
run: function(state) {
var self = this;
this.sendCommand(2,'',function(data) {
state.raw = splitFields(data.toString());
state.raw.CLIENTS.forEach(function(client) {
client.name = client.NAME;
delete client.NAME;
client.ping = parseInt(client.PING);
delete client.PING;
state.players.push(client);
});
delete state.raw.CLIENTS;
if('NAME' in state.raw) state.name = state.raw.NAME;
if('MAXCLIENTS' in state.raw) state.maxplayers = state.raw.MAXCLIENTS;
if(self.trueTest(state.raw.AUTH)) state.password = true;
self.finish(state);
});
},
sendCommand: function(cmd,password,c) {
var self = this;
var body = new Buffer(16);
body.write(password,0,15,'utf8');
var encrypted = encrypt(cmd,body);
var packets = {};
this.udpSend(encrypted, function(buffer) {
if(buffer.length < 20) return;
var data = decrypt(buffer);
if(data.zero != 0) return;
packets[data.packetNum] = data.body;
if(Object.keys(packets).length != data.packetTotal) return;
var out = [];
for(var i = 0; i < data.packetTotal; i++) {
if(!(i in packets)) return self.fatal('Missing packet #'+i);
out.push(packets[i]);
}
c(Buffer.concat(out));
return true;
});
}
});
function splitFields(str,subMode) {
var splitter,delim;
if(subMode) {
splitter = '=';
delim = ',';
} else {
splitter = ': ';
delim = '\n';
}
var split = str.split(delim);
var out = {};
if(!subMode) {
out.CHANNELS = [];
out.CLIENTS = [];
}
split.forEach(function(one) {
var equal = one.indexOf(splitter);
var key = equal == -1 ? one : one.substr(0,equal);
if(!key || key == '\0') return;
var value = equal == -1 ? '' : one.substr(equal+splitter.length);
if(!subMode && key == 'CHANNEL') out.CHANNELS.push(splitFields(value,true));
else if(!subMode && key == 'CLIENT') out.CLIENTS.push(splitFields(value,true));
else out[key] = value;
});
return out;
}
function randInt(min,max) {
return Math.floor(Math.random()*(max-min+1)+min)
}
function crc(body) {
var crc = 0;
for(var i = 0; i < body.length; i++) {
crc = crc_table[crc>>8] ^ body.readUInt8(i) ^ (crc<<8);
crc &= 0xffff;
}
return crc;
}
function encrypt(cmd,body) {
var headerKeyStart = randInt(0,0xff);
var headerKeyAdd = randInt(1,0xff);
var bodyKeyStart = randInt(0,0xff);
var bodyKeyAdd = randInt(1,0xff);
var header = new Buffer(20);
header.writeUInt8(headerKeyStart,0);
header.writeUInt8(headerKeyAdd,1);
header.writeUInt16BE(cmd,4);
header.writeUInt16BE(body.length,8);
header.writeUInt16BE(body.length,10);
header.writeUInt16BE(1,12);
header.writeUInt16BE(0,14);
header.writeUInt8(bodyKeyStart,16);
header.writeUInt8(bodyKeyAdd,17);
header.writeUInt16BE(crc(body),18);
var offset = headerKeyStart;
for(var i = 2; i < header.length; i++) {
var val = header.readUInt8(i);
val += code_head.charCodeAt(offset) + ((i-2) % 5);
val = val & 0xff;
header.writeUInt8(val,i);
offset = (offset+headerKeyAdd) & 0xff;
}
offset = bodyKeyStart;
for(var i = 0; i < body.length; i++) {
var val = body.readUInt8(i);
val += code_body.charCodeAt(offset) + (i % 72);
val = val & 0xff;
body.writeUInt8(val,i);
offset = (offset+bodyKeyAdd) & 0xff;
}
return Buffer.concat([header,body]);
}
function decrypt(data) {
var header = data.slice(0,20);
var body = data.slice(20);
var headerKeyStart = header.readUInt8(0);
var headerKeyAdd = header.readUInt8(1);
var offset = headerKeyStart;
for(var i = 2; i < header.length; i++) {
var val = header.readUInt8(i);
val -= code_head.charCodeAt(offset) + ((i-2) % 5);
val = val & 0xff;
header.writeUInt8(val,i);
offset = (offset+headerKeyAdd) & 0xff;
}
var bodyKeyStart = header.readUInt8(16);
var bodyKeyAdd = header.readUInt8(17);
offset = bodyKeyStart;
for(var i = 0; i < body.length; i++) {
var val = body.readUInt8(i);
val -= code_body.charCodeAt(offset) + (i % 72);
val = val & 0xff;
body.writeUInt8(val,i);
offset = (offset+bodyKeyAdd) & 0xff;
}
// header format:
// key, zero, cmd, echo, totallength, thislength
// totalpacket, packetnum, body key, crc
return {
zero: header.readUInt16BE(2),
cmd: header.readUInt16BE(4),
packetTotal: header.readUInt16BE(12),
packetNum: header.readUInt16BE(14),
body: body
};
}
var code_head =
'\x80\xe5\x0e\x38\xba\x63\x4c\x99\x88\x63\x4c\xd6\x54\xb8\x65\x7e'+
'\xbf\x8a\xf0\x17\x8a\xaa\x4d\x0f\xb7\x23\x27\xf6\xeb\x12\xf8\xea'+
'\x17\xb7\xcf\x52\x57\xcb\x51\xcf\x1b\x14\xfd\x6f\x84\x38\xb5\x24'+
'\x11\xcf\x7a\x75\x7a\xbb\x78\x74\xdc\xbc\x42\xf0\x17\x3f\x5e\xeb'+
'\x74\x77\x04\x4e\x8c\xaf\x23\xdc\x65\xdf\xa5\x65\xdd\x7d\xf4\x3c'+
'\x4c\x95\xbd\xeb\x65\x1c\xf4\x24\x5d\x82\x18\xfb\x50\x86\xb8\x53'+
'\xe0\x4e\x36\x96\x1f\xb7\xcb\xaa\xaf\xea\xcb\x20\x27\x30\x2a\xae'+
'\xb9\x07\x40\xdf\x12\x75\xc9\x09\x82\x9c\x30\x80\x5d\x8f\x0d\x09'+
'\xa1\x64\xec\x91\xd8\x8a\x50\x1f\x40\x5d\xf7\x08\x2a\xf8\x60\x62'+
'\xa0\x4a\x8b\xba\x4a\x6d\x00\x0a\x93\x32\x12\xe5\x07\x01\x65\xf5'+
'\xff\xe0\xae\xa7\x81\xd1\xba\x25\x62\x61\xb2\x85\xad\x7e\x9d\x3f'+
'\x49\x89\x26\xe5\xd5\xac\x9f\x0e\xd7\x6e\x47\x94\x16\x84\xc8\xff'+
'\x44\xea\x04\x40\xe0\x33\x11\xa3\x5b\x1e\x82\xff\x7a\x69\xe9\x2f'+
'\xfb\xea\x9a\xc6\x7b\xdb\xb1\xff\x97\x76\x56\xf3\x52\xc2\x3f\x0f'+
'\xb6\xac\x77\xc4\xbf\x59\x5e\x80\x74\xbb\xf2\xde\x57\x62\x4c\x1a'+
'\xff\x95\x6d\xc7\x04\xa2\x3b\xc4\x1b\x72\xc7\x6c\x82\x60\xd1\x0d';
var code_body =
'\x82\x8b\x7f\x68\x90\xe0\x44\x09\x19\x3b\x8e\x5f\xc2\x82\x38\x23'+
'\x6d\xdb\x62\x49\x52\x6e\x21\xdf\x51\x6c\x76\x37\x86\x50\x7d\x48'+
'\x1f\x65\xe7\x52\x6a\x88\xaa\xc1\x32\x2f\xf7\x54\x4c\xaa\x6d\x7e'+
'\x6d\xa9\x8c\x0d\x3f\xff\x6c\x09\xb3\xa5\xaf\xdf\x98\x02\xb4\xbe'+
'\x6d\x69\x0d\x42\x73\xe4\x34\x50\x07\x30\x79\x41\x2f\x08\x3f\x42'+
'\x73\xa7\x68\xfa\xee\x88\x0e\x6e\xa4\x70\x74\x22\x16\xae\x3c\x81'+
'\x14\xa1\xda\x7f\xd3\x7c\x48\x7d\x3f\x46\xfb\x6d\x92\x25\x17\x36'+
'\x26\xdb\xdf\x5a\x87\x91\x6f\xd6\xcd\xd4\xad\x4a\x29\xdd\x7d\x59'+
'\xbd\x15\x34\x53\xb1\xd8\x50\x11\x83\x79\x66\x21\x9e\x87\x5b\x24'+
'\x2f\x4f\xd7\x73\x34\xa2\xf7\x09\xd5\xd9\x42\x9d\xf8\x15\xdf\x0e'+
'\x10\xcc\x05\x04\x35\x81\xb2\xd5\x7a\xd2\xa0\xa5\x7b\xb8\x75\xd2'+
'\x35\x0b\x39\x8f\x1b\x44\x0e\xce\x66\x87\x1b\x64\xac\xe1\xca\x67'+
'\xb4\xce\x33\xdb\x89\xfe\xd8\x8e\xcd\x58\x92\x41\x50\x40\xcb\x08'+
'\xe1\x15\xee\xf4\x64\xfe\x1c\xee\x25\xe7\x21\xe6\x6c\xc6\xa6\x2e'+
'\x52\x23\xa7\x20\xd2\xd7\x28\x07\x23\x14\x24\x3d\x45\xa5\xc7\x90'+
'\xdb\x77\xdd\xea\x38\x59\x89\x32\xbc\x00\x3a\x6d\x61\x4e\xdb\x29';
var crc_table = [
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,58 +1,58 @@
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
//------------------------------------------------------------------------------------------------------------+
//------------------------------------------------------------------------------------------------------------+
function lgsl_query_14(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
// REFERENCE: http://flstat.cryosphere.co.uk/global-list.php
fwrite($lgsl_fp, "\x00\x02\xf1\x26\x01\x26\xf0\x90\xa6\xf0\x26\x57\x4e\xac\xa0\xec\xf8\x68\xe4\x8d\x21");
$buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; }
//---------------------------------------------------------+
$buffer = substr($buffer, 4); // HEADER ( 00 03 F1 26 )
$buffer = substr($buffer, 4); // NOT USED ( 87 + NAME LENGTH )
$buffer = substr($buffer, 4); // NOT USED ( NAME END TO BUFFER END LENGTH )
$buffer = substr($buffer, 4); // UNKNOWN ( 80 )
$server['s']['map'] = "freelancer";
$server['s']['password'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1 ? 1 : 0;
$server['s']['playersmax'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1;
$server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1;
$buffer = substr($buffer, 4); // UNKNOWN ( 88 )
$name_length = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l");
$buffer = substr($buffer, 56); // UNKNOWN
$server['s']['name'] = lgsl_cut_byte($buffer, $name_length);
lgsl_cut_string($buffer, 0, ":");
lgsl_cut_string($buffer, 0, ":");
lgsl_cut_string($buffer, 0, ":");
lgsl_cut_string($buffer, 0, ":");
lgsl_cut_string($buffer, 0, ":");
// WHATS LEFT IS THE MOTD
$server['e']['motd'] = substr($buffer, 0, -1);
// REMOVE UTF-8 ENCODING NULLS
$server['s']['name'] = str_replace("\x00", "", $server['s']['name']);
$server['e']['motd'] = str_replace("\x00", "", $server['e']['motd']);
// DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+
return TRUE;
}
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
//------------------------------------------------------------------------------------------------------------+
//------------------------------------------------------------------------------------------------------------+
function lgsl_query_14(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
// REFERENCE: http://flstat.cryosphere.co.uk/global-list.php
fwrite($lgsl_fp, "\x00\x02\xf1\x26\x01\x26\xf0\x90\xa6\xf0\x26\x57\x4e\xac\xa0\xec\xf8\x68\xe4\x8d\x21");
$buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; }
//---------------------------------------------------------+
$buffer = substr($buffer, 4); // HEADER ( 00 03 F1 26 )
$buffer = substr($buffer, 4); // NOT USED ( 87 + NAME LENGTH )
$buffer = substr($buffer, 4); // NOT USED ( NAME END TO BUFFER END LENGTH )
$buffer = substr($buffer, 4); // UNKNOWN ( 80 )
$server['s']['map'] = "freelancer";
$server['s']['password'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1 ? 1 : 0;
$server['s']['playersmax'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1;
$server['s']['players'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l") - 1;
$buffer = substr($buffer, 4); // UNKNOWN ( 88 )
$name_length = lgsl_unpack(lgsl_cut_byte($buffer, 4), "l");
$buffer = substr($buffer, 56); // UNKNOWN
$server['s']['name'] = lgsl_cut_byte($buffer, $name_length);
lgsl_cut_string($buffer, 0, ":");
lgsl_cut_string($buffer, 0, ":");
lgsl_cut_string($buffer, 0, ":");
lgsl_cut_string($buffer, 0, ":");
lgsl_cut_string($buffer, 0, ":");
// WHATS LEFT IS THE MOTD
$server['e']['motd'] = substr($buffer, 0, -1);
// REMOVE UTF-8 ENCODING NULLS
$server['s']['name'] = str_replace("\x00", "", $server['s']['name']);
$server['e']['motd'] = str_replace("\x00", "", $server['e']['motd']);
// DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+
return TRUE;
}

View File

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

View File

@ -1,133 +1,133 @@
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
function lgsl_query_19(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
fwrite($lgsl_fp, "\xC0\xDE\xF1\x11\x42\x06\x00\xF5\x03\x21\x21\x21\x21");
$buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; }
//---------------------------------------------------------+
$buffer = substr($buffer, 25); // REMOVE HEADER
$server['s']['name'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['s']['map'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['nextmap'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['gametype'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$buffer = substr($buffer, 1);
$server['s']['password'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 4));
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 4));
//---------------------------------------------------------+
for ($player_key=0; $player_key<$server['s']['players']; $player_key++)
{
$server['p'][$player_key]['name'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
}
//---------------------------------------------------------+
$buffer = substr($buffer, 17);
$server['e']['version'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['mods'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['time'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['status'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['motd'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['respawns'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['time_limit'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['voting'] = ord(lgsl_cut_byte($buffer, 4));
$buffer = substr($buffer, 2);
//---------------------------------------------------------+
for ($player_key=0; $player_key<$server['s']['players']; $player_key++)
{
$server['p'][$player_key]['team'] = ord(lgsl_cut_byte($buffer, 4));
$unknown = ord(lgsl_cut_byte($buffer, 1));
}
//---------------------------------------------------------+
$buffer = substr($buffer, 7);
$server['e']['platoon_1_color'] = ord(lgsl_cut_byte($buffer, 8));
$server['e']['platoon_2_color'] = ord(lgsl_cut_byte($buffer, 8));
$server['e']['platoon_3_color'] = ord(lgsl_cut_byte($buffer, 8));
$server['e']['platoon_4_color'] = ord(lgsl_cut_byte($buffer, 8));
$server['e']['timer_on'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['timer_time'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['time_debriefing'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['time_respawn_min'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['time_respawn_max'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['time_respawn_safe'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['difficulty'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['respawn_total'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['random_insertions'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['spectators'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['arcademode'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['ai_backup'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['random_teams'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['time_starting'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['identify_friends'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['identify_threats'] = ord(lgsl_cut_byte($buffer, 1));
$buffer = substr($buffer, 5);
$server['e']['restrictions'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
//---------------------------------------------------------+
switch ($server['e']['status'])
{
case 3: $server['e']['status'] = "Joining"; break;
case 4: $server['e']['status'] = "Joining"; break;
case 5: $server['e']['status'] = "Joining"; break;
}
switch ($server['e']['gamemode'])
{
case 2: $server['e']['gamemode'] = "Co-Op"; break;
case 3: $server['e']['gamemode'] = "Solo"; break;
case 4: $server['e']['gamemode'] = "Team"; break;
}
switch ($server['e']['respawns'])
{
case 0: $server['e']['respawns'] = "None"; break;
case 1: $server['e']['respawns'] = "Individual"; break;
case 2: $server['e']['respawns'] = "Team"; break;
case 3: $server['e']['respawns'] = "Infinite"; break;
}
switch ($server['e']['difficulty'])
{
case 0: $server['e']['difficulty'] = "Recruit"; break;
case 1: $server['e']['difficulty'] = "Veteran"; break;
case 2: $server['e']['difficulty'] = "Elite"; break;
}
//---------------------------------------------------------+
return TRUE;
}
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
function lgsl_query_19(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
fwrite($lgsl_fp, "\xC0\xDE\xF1\x11\x42\x06\x00\xF5\x03\x21\x21\x21\x21");
$buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; }
//---------------------------------------------------------+
$buffer = substr($buffer, 25); // REMOVE HEADER
$server['s']['name'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['s']['map'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['nextmap'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['gametype'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$buffer = substr($buffer, 1);
$server['s']['password'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 4));
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 4));
//---------------------------------------------------------+
for ($player_key=0; $player_key<$server['s']['players']; $player_key++)
{
$server['p'][$player_key]['name'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
}
//---------------------------------------------------------+
$buffer = substr($buffer, 17);
$server['e']['version'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['mods'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['time'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['status'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['gamemode'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['motd'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
$server['e']['respawns'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['time_limit'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['voting'] = ord(lgsl_cut_byte($buffer, 4));
$buffer = substr($buffer, 2);
//---------------------------------------------------------+
for ($player_key=0; $player_key<$server['s']['players']; $player_key++)
{
$server['p'][$player_key]['team'] = ord(lgsl_cut_byte($buffer, 4));
$unknown = ord(lgsl_cut_byte($buffer, 1));
}
//---------------------------------------------------------+
$buffer = substr($buffer, 7);
$server['e']['platoon_1_color'] = ord(lgsl_cut_byte($buffer, 8));
$server['e']['platoon_2_color'] = ord(lgsl_cut_byte($buffer, 8));
$server['e']['platoon_3_color'] = ord(lgsl_cut_byte($buffer, 8));
$server['e']['platoon_4_color'] = ord(lgsl_cut_byte($buffer, 8));
$server['e']['timer_on'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['timer_time'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['time_debriefing'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['time_respawn_min'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['time_respawn_max'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['time_respawn_safe'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['difficulty'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['respawn_total'] = ord(lgsl_cut_byte($buffer, 4));
$server['e']['random_insertions'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['spectators'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['arcademode'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['ai_backup'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['random_teams'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['time_starting'] = lgsl_time(lgsl_unpack(lgsl_cut_byte($buffer, 4), "f"));
$server['e']['identify_friends'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['identify_threats'] = ord(lgsl_cut_byte($buffer, 1));
$buffer = substr($buffer, 5);
$server['e']['restrictions'] = lgsl_get_string(lgsl_cut_pascal($buffer, 4, 3, -3));
//---------------------------------------------------------+
switch ($server['e']['status'])
{
case 3: $server['e']['status'] = "Joining"; break;
case 4: $server['e']['status'] = "Joining"; break;
case 5: $server['e']['status'] = "Joining"; break;
}
switch ($server['e']['gamemode'])
{
case 2: $server['e']['gamemode'] = "Co-Op"; break;
case 3: $server['e']['gamemode'] = "Solo"; break;
case 4: $server['e']['gamemode'] = "Team"; break;
}
switch ($server['e']['respawns'])
{
case 0: $server['e']['respawns'] = "None"; break;
case 1: $server['e']['respawns'] = "Individual"; break;
case 2: $server['e']['respawns'] = "Team"; break;
case 3: $server['e']['respawns'] = "Infinite"; break;
}
switch ($server['e']['difficulty'])
{
case 0: $server['e']['difficulty'] = "Recruit"; break;
case 1: $server['e']['difficulty'] = "Veteran"; break;
case 2: $server['e']['difficulty'] = "Elite"; break;
}
//---------------------------------------------------------+
return TRUE;
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,40 +1,40 @@
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
function lgsl_query_15(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
fwrite($lgsl_fp, "GTR2_Direct_IP_Search\x00");
$buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; }
//---------------------------------------------------------+
$buffer = str_replace("\xFE", "\xFF", $buffer);
$buffer = explode("\xFF", $buffer);
$server['s']['name'] = $buffer[3];
$server['s']['game'] = $buffer[7];
$server['e']['version'] = $buffer[11];
$server['e']['hostport'] = $buffer[15];
$server['s']['map'] = $buffer[19];
$server['s']['players'] = $buffer[25];
$server['s']['playersmax'] = $buffer[27];
$server['e']['gamemode'] = $buffer[31];
// DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+
return TRUE;
}
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
function lgsl_query_15(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
fwrite($lgsl_fp, "GTR2_Direct_IP_Search\x00");
$buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; }
//---------------------------------------------------------+
$buffer = str_replace("\xFE", "\xFF", $buffer);
$buffer = explode("\xFF", $buffer);
$server['s']['name'] = $buffer[3];
$server['s']['game'] = $buffer[7];
$server['e']['version'] = $buffer[11];
$server['e']['hostport'] = $buffer[15];
$server['s']['map'] = $buffer[19];
$server['s']['players'] = $buffer[25];
$server['s']['playersmax'] = $buffer[27];
$server['e']['gamemode'] = $buffer[31];
// DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+
return TRUE;
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -1,64 +1,64 @@
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------
function lgsl_query_22(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
fwrite($lgsl_fp,"\x03\x00\x00");
$buffer = fread($lgsl_fp, 4096);
$buffer = substr($buffer, 3); // REMOVE HEADER
if (!$buffer) { return FALSE; }
$response_type = ord(lgsl_cut_byte($buffer, 1)); // TYPE SHOULD BE 4
//---------------------------------------------------------+
$grf_count = ord(lgsl_cut_byte($buffer, 1));
for ($a=0; $a<$grf_count; $a++)
{
$server['e']['grf_'.$a.'_id'] = strtoupper(dechex(lgsl_unpack(lgsl_cut_byte($buffer, 4), "N")));
for ($b=0; $b<16; $b++)
{
$server['e']['grf_'.$a.'_md5'] .= strtoupper(dechex(ord(lgsl_cut_byte($buffer, 1))));
}
}
//---------------------------------------------------------+
$server['e']['date_current'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "L");
$server['e']['date_start'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "L");
$server['e']['companies_max'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['companies'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['spectators_max'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['name'] = lgsl_cut_string($buffer);
$server['e']['version'] = lgsl_cut_string($buffer);
$server['e']['language'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['password'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['spectators'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['map'] = lgsl_cut_string($buffer);
$server['e']['map_width'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['e']['map_height'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['e']['map_set'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1));
// DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+
return TRUE;
}
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------
function lgsl_query_22(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
fwrite($lgsl_fp,"\x03\x00\x00");
$buffer = fread($lgsl_fp, 4096);
$buffer = substr($buffer, 3); // REMOVE HEADER
if (!$buffer) { return FALSE; }
$response_type = ord(lgsl_cut_byte($buffer, 1)); // TYPE SHOULD BE 4
//---------------------------------------------------------+
$grf_count = ord(lgsl_cut_byte($buffer, 1));
for ($a=0; $a<$grf_count; $a++)
{
$server['e']['grf_'.$a.'_id'] = strtoupper(dechex(lgsl_unpack(lgsl_cut_byte($buffer, 4), "N")));
for ($b=0; $b<16; $b++)
{
$server['e']['grf_'.$a.'_md5'] .= strtoupper(dechex(ord(lgsl_cut_byte($buffer, 1))));
}
}
//---------------------------------------------------------+
$server['e']['date_current'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "L");
$server['e']['date_start'] = lgsl_unpack(lgsl_cut_byte($buffer, 4), "L");
$server['e']['companies_max'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['companies'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['spectators_max'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['name'] = lgsl_cut_string($buffer);
$server['e']['version'] = lgsl_cut_string($buffer);
$server['e']['language'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['password'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['spectators'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['map'] = lgsl_cut_string($buffer);
$server['e']['map_width'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['e']['map_height'] = lgsl_unpack(lgsl_cut_byte($buffer, 2), "S");
$server['e']['map_set'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['dedicated'] = ord(lgsl_cut_byte($buffer, 1));
// DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+
return TRUE;
}

View File

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

View File

@ -1,33 +1,33 @@
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
function lgsl_query_32(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
fwrite($lgsl_fp, "\x05\x00\x00\x01\x0A");
$buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; }
$buffer = substr($buffer, 5); // REMOVE HEADER
$server['s']['name'] = lgsl_cut_pascal($buffer);
$server['s']['map'] = lgsl_cut_pascal($buffer);
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['playersmax'] = 0; // HELD ON MASTER
// DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+
return TRUE;
}
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
function lgsl_query_32(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
fwrite($lgsl_fp, "\x05\x00\x00\x01\x0A");
$buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; }
$buffer = substr($buffer, 5); // REMOVE HEADER
$server['s']['name'] = lgsl_cut_pascal($buffer);
$server['s']['map'] = lgsl_cut_pascal($buffer);
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['playersmax'] = 0; // HELD ON MASTER
// DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+
return TRUE;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,46 +1,46 @@
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
//------------------------------------------------------------------------------------------------------------+
//------------------------------------------------------------------------------------------------------------+
function lgsl_query_18(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
// REFERENCE: http://masterserver.savage2.s2games.com
fwrite($lgsl_fp, "\x01");
$buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; }
//---------------------------------------------------------+
$buffer = substr($buffer, 12); // REMOVE HEADER
$server['s']['name'] = lgsl_cut_string($buffer);
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['time'] = lgsl_cut_string($buffer);
$server['s']['map'] = lgsl_cut_string($buffer);
$server['e']['nextmap'] = lgsl_cut_string($buffer);
$server['e']['location'] = lgsl_cut_string($buffer);
$server['e']['minimum_players'] = ord(lgsl_cut_string($buffer));
$server['e']['gamemode'] = lgsl_cut_string($buffer);
$server['e']['version'] = lgsl_cut_string($buffer);
$server['e']['minimum_level'] = ord(lgsl_cut_byte($buffer, 1));
// DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+
return TRUE;
}
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
//------------------------------------------------------------------------------------------------------------+
//------------------------------------------------------------------------------------------------------------+
function lgsl_query_18(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
// REFERENCE: http://masterserver.savage2.s2games.com
fwrite($lgsl_fp, "\x01");
$buffer = fread($lgsl_fp, 4096);
if (!$buffer) { return FALSE; }
//---------------------------------------------------------+
$buffer = substr($buffer, 12); // REMOVE HEADER
$server['s']['name'] = lgsl_cut_string($buffer);
$server['s']['players'] = ord(lgsl_cut_byte($buffer, 1));
$server['s']['playersmax'] = ord(lgsl_cut_byte($buffer, 1));
$server['e']['time'] = lgsl_cut_string($buffer);
$server['s']['map'] = lgsl_cut_string($buffer);
$server['e']['nextmap'] = lgsl_cut_string($buffer);
$server['e']['location'] = lgsl_cut_string($buffer);
$server['e']['minimum_players'] = ord(lgsl_cut_string($buffer));
$server['e']['gamemode'] = lgsl_cut_string($buffer);
$server['e']['version'] = lgsl_cut_string($buffer);
$server['e']['minimum_level'] = ord(lgsl_cut_byte($buffer, 1));
// DOES NOT RETURN PLAYER INFORMATION
//---------------------------------------------------------+
return TRUE;
}

View File

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

View File

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

View File

@ -1,54 +1,54 @@
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
function lgsl_query_21(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
fwrite($lgsl_fp,"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief");
$buffer = fread($lgsl_fp, 4096);
$buffer = substr($buffer, 20); // REMOVE HEADER
if (!$buffer) { return FALSE; }
//---------------------------------------------------------+
$server['s']['name'] = lgsl_cut_string($buffer);
$server['s']['map'] = lgsl_cut_string($buffer);
$server['e']['gamemode'] = lgsl_cut_string($buffer);
$server['s']['password'] = lgsl_cut_string($buffer);
$server['e']['progress'] = lgsl_cut_string($buffer)."%";
$server['s']['players'] = lgsl_cut_string($buffer);
$server['s']['playersmax'] = lgsl_cut_string($buffer);
switch ($server['e']['gamemode'])
{
case 0: $server['e']['gamemode'] = "Deathmatch"; break;
case 1: $server['e']['gamemode'] = "Team Deathmatch"; break;
case 2: $server['e']['gamemode'] = "Capture The Flag"; break;
}
//---------------------------------------------------------+
$player_key = 0;
while ($buffer)
{
$server['p'][$player_key]['name'] = lgsl_cut_string($buffer);
$server['p'][$player_key]['score'] = lgsl_cut_string($buffer);
$player_key ++;
}
//---------------------------------------------------------+
return TRUE;
}
/*----------------------------------------------------------------------------------------------------------\
| |
| [ LIVE GAME SERVER LIST ] [ © RICHARD PERRY FROM GREYCUBE.COM ] |
| |
| Released under the terms and conditions of the GNU General Public License Version 3 (http://gnu.org) |
| |
\-----------------------------------------------------------------------------------------------------------*/
function lgsl_query_21(&$server, &$lgsl_need, &$lgsl_fp)
{
//---------------------------------------------------------+
fwrite($lgsl_fp,"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief");
$buffer = fread($lgsl_fp, 4096);
$buffer = substr($buffer, 20); // REMOVE HEADER
if (!$buffer) { return FALSE; }
//---------------------------------------------------------+
$server['s']['name'] = lgsl_cut_string($buffer);
$server['s']['map'] = lgsl_cut_string($buffer);
$server['e']['gamemode'] = lgsl_cut_string($buffer);
$server['s']['password'] = lgsl_cut_string($buffer);
$server['e']['progress'] = lgsl_cut_string($buffer)."%";
$server['s']['players'] = lgsl_cut_string($buffer);
$server['s']['playersmax'] = lgsl_cut_string($buffer);
switch ($server['e']['gamemode'])
{
case 0: $server['e']['gamemode'] = "Deathmatch"; break;
case 1: $server['e']['gamemode'] = "Team Deathmatch"; break;
case 2: $server['e']['gamemode'] = "Capture The Flag"; break;
}
//---------------------------------------------------------+
$player_key = 0;
while ($buffer)
{
$server['p'][$player_key]['name'] = lgsl_cut_string($buffer);
$server['p'][$player_key]['score'] = lgsl_cut_string($buffer);
$player_key ++;
}
//---------------------------------------------------------+
return TRUE;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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