1+ const dgram = require ( "dgram" ) ;
2+ const iconv = require ( "iconv-lite" )
3+
4+ const error = ( text ) => {
5+ new Error ( text ) ;
6+ }
7+
8+ const query = async function ( options , callback ) {
9+ let self = this ;
10+ let response = { online : 0 } ;
11+
12+ options . port = options . port || 7777 ;
13+ options . timeout = options . timeout || 1000 ;
14+
15+ if ( ! options . host ) return callback . apply ( options , [ "Invalid \"host\" passed" ] ) ;
16+ if ( ! isFinite ( options . port ) || options . port < 1 || options . port > 65535 ) return callback . apply ( options , [ `Invalid port "${ options . port } ". Port mus"t be larger than 1 and less than 65535` ] ) ;
17+
18+ request . call ( self , options , "i" , async function ( error , information ) {
19+ if ( error ) return callback . apply ( options , [ error ] )
20+
21+ response . address = options . host ;
22+ response . port = options . port ;
23+ response . hostname = information . hostname ;
24+ response . gamemode = information . gamemode ;
25+ response . mapname = information . mapname ;
26+ response . passworded = Boolean ( information . passworded ) ;
27+ response . maxplayers = information . maxplayers ;
28+ response . online = information . players ;
29+
30+ request . call ( self , options , "r" , async function ( error , rules ) {
31+ if ( error ) return callback . apply ( options , [ error ] )
32+
33+ rules . lagcomp = rules . lagcomp === "On" ? true : false ;
34+ rules . weather = parseInt ( rules . weather , 10 ) ;
35+ response . rules = rules ;
36+
37+ if ( response . online > 100 ) {
38+ response . players = [ ]
39+
40+ return callback . apply ( options , [ false , response ] )
41+ }
42+ else {
43+ request . call ( self , options , "d" , function ( error , players ) {
44+ if ( error ) return callback . apply ( options , [ error ] )
45+
46+ response . players = players ;
47+
48+ return callback . apply ( options , [ false , response ] )
49+ } ) ;
50+ }
51+ } ) ;
52+ } ) ;
53+ } ;
54+
55+ const request = function ( options , opcode , callback ) {
56+ let socket = dgram . createSocket ( "udp4" ) ;
57+ let packet = Buffer . alloc ( 11 ) ;
58+
59+ packet . write ( "SAMP" ) ;
60+
61+ for ( let i = 0 ; i < 4 ; ++ i ) packet [ i + 4 ] = options . host . split ( "." ) [ i ] ;
62+
63+ packet [ 8 ] = options . port & 0xff ;
64+ packet [ 9 ] = ( options . port >> 8 ) & 0xff ;
65+ packet [ 10 ] = opcode . charCodeAt ( 0 ) ;
66+
67+ try {
68+ socket . send ( packet , 0 , packet . length , options . port , options . host , function ( error , bytes ) {
69+ if ( error ) return callback . apply ( options , [ error ] ) ;
70+ } ) ;
71+ } catch ( error ) {
72+ return callback . apply ( options , [ error ] ) ;
73+ }
74+
75+ let controller = undefined ;
76+
77+ let onTimeOut = ( ) => {
78+ socket . close ( ) ;
79+ return callback . apply ( options , [ "Socket timed out." ] ) ;
80+ } ;
81+
82+ controller = setTimeout ( onTimeOut , options . timeout ) ;
83+
84+ socket . on ( "message" , function ( message ) {
85+ if ( controller ) clearTimeout ( controller ) ;
86+ if ( message . length < 11 ) return callback . apply ( options , [ "Socket invalid" ] ) ;
87+ else {
88+ socket . close ( ) ;
89+
90+ message = message . slice ( 11 ) ;
91+
92+ let object = { } ;
93+ let array = [ ] ;
94+ let strlen = 0 ;
95+ let offset = 0 ;
96+
97+ try {
98+ if ( opcode == "i" ) {
99+ object . passworded = message . readUInt8 ( offset ) ;
100+ offset += 1 ;
101+
102+ object . players = message . readUInt16LE ( offset ) ;
103+ offset += 2 ;
104+
105+ object . maxplayers = message . readUInt16LE ( offset ) ;
106+ offset += 2 ;
107+
108+ strlen = message . readUInt16LE ( offset ) ;
109+ offset += 4 ;
110+
111+ object . hostname = decode ( message . slice ( offset , ( offset += strlen ) ) ) ;
112+
113+ strlen = message . readUInt16LE ( offset ) ;
114+ offset += 4 ;
115+
116+ object . gamemode = decode ( message . slice ( offset , ( offset += strlen ) ) ) ;
117+
118+ strlen = message . readUInt16LE ( offset ) ;
119+ offset += 4 ;
120+
121+ object . mapname = decode ( message . slice ( offset , ( offset += strlen ) ) ) ;
122+
123+ return callback . apply ( options , [ false , object ] ) ;
124+ }
125+
126+ if ( opcode == "r" ) {
127+ let rulecount = message . readUInt16LE ( offset ) ;
128+ offset += 2 ;
129+
130+ let property ,
131+ value = undefined ;
132+
133+ while ( rulecount ) {
134+ strlen = message . readUInt8 ( offset ) ;
135+ ++ offset ;
136+
137+ property = decode ( message . slice ( offset , ( offset += strlen ) ) ) ;
138+
139+ strlen = message . readUInt8 ( offset ) ;
140+ ++ offset ;
141+
142+ value = decode ( message . slice ( offset , ( offset += strlen ) ) ) ;
143+
144+ object [ property ] = value ;
145+
146+ -- rulecount ;
147+ }
148+
149+ return callback . apply ( options , [ false , object ] ) ;
150+ }
151+
152+ if ( opcode == "d" ) {
153+ let playercount = message . readUInt16LE ( offset ) ;
154+ offset += 2 ;
155+
156+ let player = undefined ;
157+
158+ while ( playercount ) {
159+ player = { } ;
160+
161+ player . id = message . readUInt8 ( offset ) ;
162+ ++ offset ;
163+
164+ strlen = message . readUInt8 ( offset ) ;
165+ ++ offset ;
166+
167+ player . name = decode ( message . slice ( offset , ( offset += strlen ) ) ) ;
168+
169+ player . score = message . readUInt32LE ( offset ) ;
170+ offset += 4 ;
171+
172+ player . ping = message . readUInt16LE ( offset ) ;
173+ offset += 4 ;
174+
175+ array . push ( player ) ;
176+
177+ -- playercount ;
178+ }
179+
180+ return callback . apply ( options , [ false , array ] ) ;
181+ }
182+ } catch ( exception ) {
183+ return callback . apply ( options , [ exception ] ) ;
184+ }
185+ }
186+ } ) ;
187+ } ;
188+
189+ const decode = ( buffer ) => {
190+ return iconv . decode ( buffer , "win1251" ) ;
191+ } ;
192+
193+ module . exports = query ;
0 commit comments