~armagetronad-dev/armagetronad/tools-php-client-trunk-work

2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
1
<?php
2
function filtercolors($str) {
5 by Armanelgtron
Fix onNetSync, change deprecated stuff to supported stuff (var to public, ereg to preg)
3
	return preg_replace('/0x....../', '', $str);
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
4
}
5
class nMessage {
5 by Armanelgtron
Fix onNetSync, change deprecated stuff to supported stuff (var to public, ereg to preg)
6
	public $descriptor;
7
	public $id;
8
	public $len;
9
	public $buf;
10
	public $bufpos;
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
11
4 by wrtlprnft
Getting the server info should work now even with new clients
12
	static $clientid = 0;
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
13
	static $debug = false;
14
	static $receivedids = array();
15
	static $mid = 1;
16
17
	static function readMsg($sock) {
18
		$msg = new nMessage();
19
		$header = fread($sock, 6);
20
		if($header === false || $header === '') {
21
			return false;
22
		}
23
		while(true) { // read until we get something that might be the
24
		             // start of a packet. No clue where the extra zeroes
25
		            // come from…
26
			$data = unpack('ndescriptor/nid/nlen', $header);
6 by Armanelgtron
Don't loop indefinitely on failure. Also, silence warnings about ob_flush.
27
			if($data === false) return false;
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
28
			$msg->descriptor = $data['descriptor'];
29
			if($msg->descriptor == 0) {
30
				$header = substr($header, 2) . fread($sock, 2);
31
			} else {
32
				break;
33
			}
34
		}
35
		$msg->id = $data['id'];
36
		if(nMessage::$debug) {
37
			print("id<" . $msg->id . '> ');
38
		}
39
		if($msg->id != 0) {
40
			nMessage::ack($sock, $msg->id);
41
		}
42
		$msg->len = $data['len'];
43
		//if($msg->len % 2 == 1) {
44
		//	++$msg->len;
45
		//}
46
		if($msg->len > 0) {
47
			//$msg->buf = fread($sock, $msg->len*2);
48
			$msg->buf = fread($sock, $msg->len*2);
49
		} else {
50
			$msg->buf = '';
51
		}
52
		$msg->bufpos = 0;
53
		if($msg->id != 0) {
54
			//print_r(nMessage::$receivedids);
55
			if(in_array($msg->id, nMessage::$receivedids)) {
56
				if(nMessage::$debug) {
57
					print('Reveived duplicate message ' . $msg->id . "\n");
58
				}
59
				// fetch another message
60
				return nMessage::readMsg($sock);
61
			} else {
62
				nMessage::$receivedids[] = $msg->id;
63
			}
64
		}
65
		//print_r($msg);
66
		return $msg;
67
	}
68
	static function makeMsg($descriptor, $id = false) {
69
		if($id === false) {
70
			$id = nMessage::$mid++;
71
		}
72
		$msg = new nMessage();
73
		$msg->descriptor = $descriptor;
74
		$msg->id = $id;
75
		$msg->len = 0;
76
		$msg->buf = '';
77
		return $msg;
78
	}
79
	static function ack(&$sock, $msgid) {
80
		if(!is_array($msgid)) {
81
			$msgid = array($msgid);
82
		}
83
		$msg = nMessage::makeMsg(1, 0);
84
		foreach($msgid as $id) {
85
			$msg->writeshort($id);
86
		}
87
		$msg->write($sock);
6 by Armanelgtron
Don't loop indefinitely on failure. Also, silence warnings about ob_flush.
88
		@ob_flush();
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
89
		flush();
90
	}
91
92
	function write($sock) {
93
		$this->len = strlen($this->buf) / 2;
94
		$buffer = pack('nnn', $this->descriptor, $this->id, $this->len);
95
		$buffer .= $this->buf;
96
		if(nMessage::$clientid !== false) {
97
			$buffer .= pack('n', nMessage::$clientid);
98
		}
99
		fwrite($sock, $buffer);
100
	}
101
102
	function readshort() {
103
		$data = unpack('nshort', substr($this->buf, $this->bufpos, 2));
104
		$this->bufpos += 2;
105
		return $data['short'];
106
	}
107
108
	function writeshort($short) {
109
		$this->buf .= pack('n', $short);
110
		$this->bufpos += 2;
111
	}
112
113
	function writeint($int) {
114
		$a = $int & 0xFFFF;
115
		$b = ($int - $a) >> 16;
116
		//if($a > 0x8FFF) {
117
		//	++$b;
118
		//}
119
		$this->writeshort($a);
120
		$this->writeshort($b);
121
	}
122
123
	function readint() {
124
		$a = $this->readshort();
125
		$b = $this->readshort();
126
		return ($b << 16)|$a;
127
	}
128
129
	function readfloat() {
130
		$trans = $this->readint();
131
		$mant = $trans & ((1 << 25) - 1);
132
		$negative = $trans & (1 << 25);
133
		$exp = ($trans - $mant - $negative) >> 26;
134
135
		$x = $mant / (1 << 25);
136
		if($negative != 0) {
137
			$x = -$x;
138
		}
139
140
		while($exp >= 6) {
141
			$exp -=6;
142
			$x *= 64.;
143
		}
144
		while($exp > 0) {
145
			--$exp;
146
			$x *= 2.;
147
		}
148
		return $x;
149
	}
150
151
	function readbool() {
152
		return $this->readshort() != 0;
153
	}
154
	
155
	function writebool($bool) {
156
		$this->writeshort($bool ? 1 : 0);
157
	}
158
159
	function readstring() {
160
		$len = $this->readshort();
161
		if($len % 2 == 1) {
162
			++$len;
163
		}
164
		$data = unpack('n*', substr($this->buf, $this->bufpos, $len));
165
		$this->bufpos += $len;
166
		$ret = '';
167
		foreach($data as $i) {
168
			$c1 = $i & 255;
169
			$c2 = ($i >> 8) & 255;
170
			if($c1 > 127) {
171
				++$c2;
172
				$c2 %= 256;
173
			}
174
			if($c1 != 0) {
175
				$ret .= chr($c1);
176
				if($c2 != 0) {
177
					$ret .= chr($c2);
178
				}
179
			}
180
		}
181
		return($ret);
182
	}
183
184
	function writestring($string) {
185
		if($string == '') {
186
			$string = ' ';
187
		}
188
		$l = strlen($string) + 1;
189
		$this->writeshort($l);
190
		for($i = 0; $i + 1 < $l; $i += 2) {
191
			$c1 = ord($string[$i]);
192
			if($l - 1 != $i) {
193
				$c2 = ord($string[$i + 1]);
194
			} else {
195
				$c2 = 0;
196
			}
197
			if($c1 > 127) {
198
				$c2 += 255;
199
				$c2 %= 256;
200
			}
201
			$this->writeshort(($c2 << 8) | $c1);
202
		}
203
		if($i < $l) {
204
			$this->writeshort(0);
205
		}
206
	}
207
208
	function remaining() {
209
		return strlen($this->buf) - $this->bufpos ;
210
	}
211
212
	function end() {
213
		return $this->remaining() <= 0;
214
	}
215
}
216
217
218
class serverinfo {
5 by Armanelgtron
Fix onNetSync, change deprecated stuff to supported stuff (var to public, ereg to preg)
219
	public $host;
220
	public $port;
221
222
	public $port_by_server;
223
	public $host_by_server;
224
225
	public $name;
226
	public $num_players_by_server;
227
	public $version_min;
228
	public $version_max;
229
	public $version;
230
231
	public $max_players;
232
	public $players;
233
	public $global_ids;
234
235
	public $options;
236
	public $uri;
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
237
7 by Armanelgtron
PHP 8 compatibility and fix doChat to only need one argument
238
	function __construct($host, $port) {
239
		$this->serverinfo($host, $port);
240
	}
241
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
242
	function serverinfo($host, $port) {
243
		$this->host = $host;
244
		$this->port = $port;
245
		$sock = fsockopen('udp://' . $host . ':' . $port . '');
246
		stream_set_timeout($sock, 1);
247
		$msg = nMessage::makeMsg(0x35, 0);
248
		$msg->write($sock);
249
		$msg = nMessage::readMsg($sock);
250
		if($msg === false) {
251
			$this->name = false;
252
			return;
253
		}
254
255
		$this->port_by_server = $msg->readint();
256
		$this->host_by_server = $msg->readstring();
257
258
		$this->name = $msg->readstring();
259
		$this->num_players_by_server = $msg->readint();
260
261
		if($msg->end()) return;
262
		$this->version_min = $msg->readint();
263
		$this->version_max = $msg->readint();
264
		$this->version = $msg->readstring();
265
266
		if($msg->end()) return;
267
		$this->max_players = $msg->readint();
268
269
		if($msg->end()) return;
270
		$players = $msg->readString();
271
		$this->players = explode("\n", substr($players, 0, strlen($players) - 1));
272
273
		$this->options = $msg->readString();
274
		$this->uri = $msg->readString();
275
276
		$this->global_ids = array();
277
278
		if($msg->end()) return;
279
280
		$this->global_ids = explode("\n", $msg->readString());
281
282
		fclose($sock);
283
	}
284
}
285
286
function getMasterInfo($host, $port = 4533) {
287
	$sock = fsockopen('udp://' . $host . ':' . $port . '');
288
289
	$msg = nMessage::makeMsg(11, 0x24); //login
290
	$msg->writeshort(0x14); // rate
291
	$msg->writeshort(0); // bb = bigbrother
292
	$msg->writeint(0); // ver.min
293
	$msg->writeint(0xff); // ver.max
294
	$msg->write($sock);
295
	$msg = nMessage::readMsg($sock);
296
	if($msg->descriptor != 5) {
297
		return "rejected!";
298
		//print_r($msg);
299
	}
300
	nMessage::$clientid = $msg->readshort();
301
	$version_min = $msg->readint();
302
	$version_max = $msg->readint();
303
	//print("clientid $clientid; version_min $version_min; version_max $version_max");
304
305
306
	// request small server info
307
	$msg = nMessage::makeMsg(52, 2);
308
	//$msg->writeint(256*256 + 0x75*256 + 0x20);
309
	//$msg->writeint(0);
310
	//print_r($msg);
311
	$msg->write($sock);
312
313
	$ips = array();
314
	$ports = array();
315
316
	$cont = true;
317
318
	while($cont) {
319
		$msg = nMessage::readMsg($sock);
320
		//print_r($msg);
321
		switch($msg->descriptor) {
322
			case 3: // login_deny; you're actually supposed to get this 0.2
323
				   // seconds after the master has finished sending you
324
				  // its information
325
				$cont = false;
326
			break;
327
			case 50: // SmallServerDescriptor
328
				$ports[] = $msg->readint();
329
				$ips[] = $msg->readstring();
330
		}
331
	}
332
	array_multisort($ips, $ports);
333
	$servers = array();
334
	foreach($ips as $i => $ip) {
335
		$servers[] = array('host' => $ip, 'port' => $ports[$i]);
336
	}
337
	return $servers;
338
}
339
class nNetObject {
340
	static $objects = array();
341
5 by Armanelgtron
Fix onNetSync, change deprecated stuff to supported stuff (var to public, ereg to preg)
342
	public $id;
343
	public $owner;
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
344
345
	function readBasics($msg) {
346
		$this->id = $msg->readshort();
347
		$this->owner = $msg->readshort();
348
		nNetObject::$objects[$this->id] = &$this;
349
		//print_r(nNetObject::$objects);
350
	}
351
	function setBasics() {
352
		$this->id = connection::$current->getFreeID();
353
		$this->owner = nMessage::$clientid;
354
		nNetObject::$objects[$this->id] = &$this;
355
	}
356
	function writeBasics($descriptor) {
357
		$msg = nMessage::makeMsg($descriptor);
358
		$msg->writeshort($this->id);
359
		$msg->writeshort($this->owner);
360
		return $msg;
361
	}
362
	//function nNetObject($id, $owner = nMessage::$clientid) {
363
	//	$this->id = $id;
364
	//	$this->owner = $owner;
365
	//	nNetObject::$objects[$this->id] = &$this;
366
	//}
367
	function read($msg) {
368
	}
369
	function write($socket) {
370
	}
371
}
372
$foo = new nNetObject;
373
class ePlayerNetId extends nNetObject {
374
	static $players = array();
375
	static function readMsg($msg) {
376
		$o = new ePlayerNetId;
377
		$o->readBasics($msg);
378
		ePlayerNetId::$players[$o->owner] = &$o;
379
		$o->read($msg);
380
		return $o;
381
	}
382
	static function createPlayer($r, $g, $b, $name) {
383
		$o = new ePlayerNetId();
384
		$o->setBasics();
385
		$o->r = $r;
386
		$o->g = $g;
387
		$o->b = $b;
388
		$o->pingCharity = 100;
389
		$o->name = $name;
390
		$o->ping = 0;
391
		$o->flags = 2; // newSpectate
392
		$o->score = 0;
393
		$o->newdisc = 0;
394
		$o->nextteamid = 0;
395
		$o->currentteamid = 0;
396
		$o->favoriteNumberOfPlayersPerTeam = 42;
397
		$o->nameTeamAfterMe = false;
398
		return $o;
399
	}
400
	function write($sock) {
401
		$msg = $this->writeBasics(201);
402
		$msg->writeshort($this->r);
403
		$msg->writeshort($this->g);
404
		$msg->writeshort($this->b);
405
		$msg->writeshort($this->pingCharity);
406
		$msg->writestring($this->name);
407
		$msg->writeint(0); // TODO: writefloat
408
		$msg->writeshort($this->flags);
409
		$msg->writeint($this->score);
410
		$msg->writeshort($this->newdisc);
411
		//print('Playermsg: ');
412
		//print_r($msg);
413
		$msg->write($sock);
414
		// TODO: the rest
415
	}
416
	function read($msg) {
417
		$this->r = $msg->readshort();
418
		$this->g = $msg->readshort();
419
		$this->b = $msg->readshort();
420
		$this->pingCharity = $msg->readshort();
421
		$this->name = $msg->readstring();
422
		$this->ping = $msg->readfloat();
423
		$this->flags = $msg->readshort();
424
		$this->score = $msg->readint();
425
		$this->newdisc = $msg->readshort();
426
		if(!$msg->end()) {
427
			$this->nextteamid = $msg->readshort();
428
			$this->currentteamid = $msg->readshort();
429
			$this->favoriteNumberOfPlayersPerTeam = $msg->readint();
430
			$this->nameTeamAfterMe = $msg->readbool();
431
		}
432
	}
433
}
434
class eTeam extends nNetObject {
435
	static function readMsg($msg) {
436
		$o = new eTeam;
437
		$o->readBasics($msg);
438
		$o->read($msg);
439
		return $o;
440
	}
441
	function read($msg) {
442
		$this->r = $msg->readshort();
443
		$this->g = $msg->readshort();
444
		$this->b = $msg->readshort();
445
		$this->name = $msg->readstring();
446
		$this->maxPlayers = $msg->readint();
447
		$this->maxImbalance = $msg->readint();
448
		$this->score = $msg->readint();
449
	}
450
}
451
//$info = new serverinfo('81.169.132.36', 4534);
452
//print_r($info);
453
//
454
//print_r(getMasterInfo('master1.armagetronad.net'));
455
456
//$servers = array();
457
//$master = getMasterInfo('master3.armagetronad.net');
458
//foreach($master as $server) {
459
//	print_r(new serverinfo($server['host'], $server['port']));
460
//}
461
//print_r($servers);
462
463
class connection {
464
	static $ID_PREFETCH=50;
465
	static $current=50;
466
	static $debug=false;
467
	
5 by Armanelgtron
Fix onNetSync, change deprecated stuff to supported stuff (var to public, ereg to preg)
468
	public $sock;
469
	public $prefetchedIDs = array();
470
	public $sentMsgs = array();
471
	public $connectiontime;
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
472
473
	function haveFreeID() {
474
		return count($this->prefetchedIDs) > 0;
475
	}
476
	function getFreeID() {
477
		if(count($this->prefetchedIDs) < connection::$ID_PREFETCH / 2) {
478
			// request a couple of new IDs
479
			$msg = nMessage::makeMsg(21); // request a couple of new ids
480
			$msg->writeShort(connection::$ID_PREFETCH / 2);
481
			$this->send($msg);
482
		}
483
		if(count($this->prefetchedIDs) > 0) {
484
			$ret = $this->prefetchedIDs[0];
485
			array_splice($this->prefetchedIDs, 0, 1);
486
			//print('Returning ID ' . $ret);
487
			//debug_print_backtrace();
488
			return $ret;
489
		} else {
490
			return false; //no ID, sorry…
491
		}
492
	}
493
7 by Armanelgtron
PHP 8 compatibility and fix doChat to only need one argument
494
	function __construct($host, $port=4534) {
495
		$this->connection($host, $port);
496
	}
497
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
498
	function connection($host, $port=4534) {
499
		$this->host = $host;
500
		$this->hostip = gethostbyname($host);
501
		$this->port = $port;
502
		
503
		connection::$current = $this;
504
		$this->sock = fsockopen('udp://' . $host . ':' . $port . '');
505
		$this->connectiontime = microtime(true);
506
	}
507
508
	function logout() {
509
		$msg = nMessage::makeMsg(7);
510
		$this->send($msg);
511
	}
512
513
	function send($msg) {
514
		if($msg->id != 0) {
515
			$this->sentMsgs[$msg->id] = &$msg;
516
		}
517
		$msg->write($this->sock);
518
	}
519
520
	function doLogin() {
521
		$msg = nMessage::makeMsg(11, 0x24); //login
522
		$msg->writeshort(0x14); // rate
523
		$msg->writeshort(0); // bb = bigbrother
524
		$msg->writeint(0); // ver.min
525
		$msg->writeint(0xff); // ver.max
526
		$this->send($msg);
527
		$msg = nMessage::readMsg($this->sock);
528
		if($msg->descriptor != 5) {
529
			return false;
530
		}
531
		nMessage::$clientid = $msg->readshort();
532
		$version_min = $msg->readint();
533
		$version_max = $msg->readint();
534
535
		$msg = nMessage::makeMsg(25); // ready to get objects
536
		$this->send($msg);
537
538
		$this->getFreeID(); // make sure we have some IDs ready to use
539
		
540
		$this->mainLoop();
541
	}
542
543
	function loopfunc() {
544
	}
545
546
	function mainLoop() {
547
		$cont = true;
548
		while($cont) {
549
			$this->loopfunc();
550
			$msg = nMessage::readMsg($this->sock);
551
			//print_r($msg);
552
			switch($msg->descriptor) {
553
				case 1:   $cont = $this->handleAck              ($msg); break;
554
				case 3:   $cont = $this->handleLoginDeny        ($msg); break;
555
				case 60:  $cont = $this->handleConfig           ($msg); break;
556
				case 9:   $cont = $this->handleCentreMessage    ($msg); break;
557
				case 10:  $cont = $this->handleVersionControl   ($msg); break;
558
				case 8:   $cont = $this->handleConsoleMessage   ($msg); break;
559
				case 203: $cont = $this->handleChat             ($msg); break;
560
				case 27:  $cont = $this->handleSyncAck          ($msg); break;
561
				case 28:  $cont = $this->handleSync             ($msg); break;
562
				case 202: $cont = $this->handlePlayerReomoval   ($msg); break;
563
				case 312: $cont = $this->handleFullscreenMessage($msg); break;
564
				case 201: $cont = $this->handleNewPlayer        ($msg); break;
565
				case 20:  $cont = $this->handleFreeID           ($msg); break;
566
				case 220: $cont = $this->handleNewTeam          ($msg); break;
567
				case 24:  $cont = $this->handleNetSync          ($msg); break;
568
				case 40:  $cont = $this->handleAuthRequest      ($msg); break;
569
				default: if(connection::$debug) print_r($msg);
570
			}
571
		}
572
		nMessage::$clientid = false;
573
	}
574
	function onAck($ids) {
575
		foreach($ids as $id) {
576
			if(isset($this->sentMsgs[$id])) {
577
				unset($this->sentMsgs[$id]);
578
			} else {
579
			}
580
		}
581
	}
582
	function handleAck              ($msg) {
583
		$ids = array();
584
		while(!$msg->end()) {
585
			$ids[] = $msg->readshort();
586
		}
587
		$this->onAck($ids);
588
		return true;
589
	}
590
	function onLoginDeny($reason) {
591
	}
592
	function handleLoginDeny        ($msg) {
593
		$this->onLoginDeny($msg->readstring());
594
		return false;
595
	}
596
	function onConfig($name, $value) {
597
	}
598
	function handleConfig           ($msg) {
599
		$name = $msg->readstring();
600
		if($msg->remaining() == 2) {
601
			$value = $msg->readbool();
602
		} else if($msg->remaining() == 4) {
603
			$value = $msg->readint() . ' ';
604
			$msg->bufpos-=4;
605
			$value .= $msg->readfloat();
606
		} else if($msg->remaining() > 2) {
607
			$value = $msg->readstring();
608
		} else {
609
			$value = null;
610
		}
611
		$this->onConfig($name, $value);
612
		return true;
613
	}
614
	function onCentreMessage($text) {
615
	}
616
	function handleCentreMessage    ($msg) {
617
		$this->onCentreMessage($msg->readstring());
618
		return true;
619
	}
620
	function onVersionControl($min, $max) {
621
	}
622
	function handleVersionControl   ($msg) {
623
		$min = $msg->readint();
624
		$max = $msg->readint();
625
		$this->onVersionControl($min, $max);
626
		return true;
627
	}
628
	function onConsoleMessage($text) {
629
	}
630
	function handleConsoleMessage   ($msg) {
631
		$this->onConsoleMessage($msg->readstring());
632
		return true;
633
	}
634
	function onChat($playerid, $text) {
635
	}
636
	function handleChat             ($msg) {
637
		$playerid = $msg->readshort();
638
		$text = $msg->readstring();
639
		$this->onChat($playerid, $text);
640
		return true;
641
	}
7 by Armanelgtron
PHP 8 compatibility and fix doChat to only need one argument
642
	function doChat($playerid, $text=null) {
643
		if ($text === null)
2.1.1 by Luke Dashjr
doChat needs a playerid argument, in case its not in the bot use case
644
		{
2.1.2 by Luke Dashjr
we use $text here, not $line
645
			$text = $playerid;
2.1.1 by Luke Dashjr
doChat needs a playerid argument, in case its not in the bot use case
646
			$playerid = $this->playerid;
647
		}
2.1.3 by Luke Dashjr
doChat returns the max chat length if given false as text
648
		// TODO: Determine if we have access level overriding MAX_CHAT?
649
		if ($text === false)
650
			return $this->max_chat;
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
651
		$msg = nMessage::makeMsg(200);
2.1.1 by Luke Dashjr
doChat needs a playerid argument, in case its not in the bot use case
652
		$msg->writeshort($playerid);
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
653
		$msg->writeString($text);
654
		$this->send($msg);
655
		return true;
656
	}
2.1.4 by Luke Dashjr
doPrivMsg function with semantics similar to doChat
657
	function doPrivMsg($playerid, $target, $text) {
658
		if ($text === false)
659
		{
660
			$max_chat = $this->doChat($playerid, false);
661
			return $max_chat - 6 - strlen($target);
662
		}
663
		return
664
		$this->doChat($playerid, "/msg $target $text");
665
	}
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
666
	function onSync($timeout, $sync_sn_netObjects, $c_sync) {
667
		$msg = nMessage::makeMsg(27);
668
		$msg->writeshort($c_sync);
669
		$this->send($msg);
670
	}
671
	function handleSync             ($msg) {
672
		$timeout = $msg->readfloat();
673
		$sync_sn_netObjects = $msg->readshort();
674
		$c_sync = $msg->readshort();
675
		$this->onSync($timeout, $sync_sn_netObjects, $c_sync);
676
		return true;
677
	}
678
	function onSyncAck($c_sync) {
679
	}
680
	function handleSyncAck          ($msg) {
681
		$c_sync = $msg->readshort();
682
		$this->onSyncAck($c_sync);
683
		return true;
684
	}
685
	function onPlayerRemoval($id) {
686
	}
687
	function handlePlayerReomoval   ($msg) {
688
		$this->onPlayerRemoval($msg->readshort());
689
		return true;
690
	}
691
	function onFullscreenMessage($title, $text, $timeout) {
692
	}
693
	function handleFullscreenMessage($msg) {
694
		$title = $msg->readstring();
695
		$text = $msg->readstring();
696
		$timeout = $msg->readfloat();
697
		$this->onFullscreenMessage($title, $text, $timeout);
698
		return true;
699
	}
700
	function onNewPlayer($player) {
701
	}
702
	function handleNewPlayer        ($msg) {
703
		$this->onNewPlayer(ePlayerNetId::readMsg($msg));
704
		return true;
705
	}
706
	function onFreeID($ids) {
707
		foreach($ids as $id) {
708
			$this->prefetchedIDs[] = $id;
709
		}
710
	}
711
	function handleFreeID           ($msg) {
712
		$ids = array();
713
		while(!$msg->end()) {
714
			$id = $msg->readshort();
715
			$count = $msg->end() ? 1 : $msg->readshort();
716
			while($count > 0) {
717
				--$count;
718
				$ids[] = ++$id;
719
			}
720
		}
721
		$this->onFreeID($ids);
722
		return true;
723
	}
724
	function onNewTeam($player) {
725
	}
726
	function handleNewTeam        ($msg) {
727
		$this->onNewTeam(eTeam::readMsg($msg));
728
		return true;
729
	}
730
	function onNetSync(&$obj) {
731
	}
732
	function handleNetSync          ($msg) {
733
		$id = $msg->readshort();
734
		if(!isset(nNetObject::$objects[$id])) {
735
			if(connection::$debug) {
736
				print("Ignoring sync for object ". $id . "\n");
737
			}
738
		} else {
739
			nNetObject::$objects[$id]->read($msg);
740
			if(connection::$debug) {
741
				print("Synced object ". $id . "\n");
742
			}
5 by Armanelgtron
Fix onNetSync, change deprecated stuff to supported stuff (var to public, ereg to preg)
743
			$this->onNetSync(nNetObject::$objects[$id]);
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
744
		}
745
		return true;
746
	}
747
	function handleAuthRequest($msg) {
748
		$scrambled = '';
749
		for ($i = 7; $i >= 0; --$i)
750
		{
751
			$x = $msg->readshort();
752
			$low = chr($x & 255);
753
			$high = chr(($x - $low) >> 8);
754
755
			$scrambled = "$low$high$scrambled";
756
		}
757
		
758
		$user = $msg->readstring();
759
		$tmsg = $msg->readstring();
760
		
761
		/* BROKEN:
762
		$method = "bmd5";
763
		$prefix = "";
764
		$suffix = "";
765
		if (!$msg->end())
766
		{
767
			$method = $msg->readstring();
768
			$prefix = $msg->readstring();
769
			$suffix = $msg->readstring();
770
		}
771
		echo "Method: $method\nPrefix: $prefix\nSuffix: $suffix\n";
772
		*/
773
		
774
		$info = array(
775
			'salt' => $scrambled,
776
			'user' => $user,
777
			'message' => $tmsg,
778
		);
779
		
780
		$this->onAuthRequest($info);
781
		return true;
782
	}
2.1.5 by Luke Dashjr
doAuthInit to call /login
783
	function doAuthInit($playerid, $info) {
784
		return
785
		$this->doChat($playerid, "/login " . $info['authority']);
786
	}
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
787
	function onAuthRequest($info) {
788
	}
789
	function doAuthAnswer(&$info) {
790
		switch ($info['method']) {
791
		case 'bmd5':
792
		case '':
793
			if (!isset($info['passwordhash']))
794
				$info['passwordhash'] = md5($info['password'] . "\0", true);
795
			$info['scrambled'] = md5($info['passwordhash'] . $info['salt'], true);
796
			break;
797
		default:
798
			return false;
799
		}
800
		
801
		$msg = nMessage::makeMsg(41);
802
		for ($i = 7; $i >= 0; --$i)
803
			$msg->writeshort(ord($info['scrambled'][$i << 1]) + (ord($info['scrambled'][($i << 1) + 1]) << 8));
804
		$msg->writeString($info['user']);
805
		$msg->writebool($info['aborted']);
806
		if (!isset($info['automatic']))
807
			$info['automatic'] = true;
808
		$msg->writebool($info['automatic']);
809
		if (!isset($info['server']))
810
			$info['server'] = $this->hostip . ':' . $this->port;
811
		$msg->writeString($info['server']);
812
		$this->send($msg);
813
		return true;
814
	}
2.1.6 by Luke Dashjr
doServerCommand wrapping /admin
815
	function doServerCommand($playerid, $cmd) {
816
		return
817
		$this->doChat($playerid, "/admin $cmd");
818
	}
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
819
}
820
821
class bot extends connection {
7 by Armanelgtron
PHP 8 compatibility and fix doChat to only need one argument
822
	function __construct($host, $port, $nick=null, $message=null) {
823
		$this->bot($host, $port, $nick, $message);
824
	}
825
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
826
	function bot($host, $port, $nick, $message) {
827
		$this->connection($host, $port);
828
		$this->nick = $nick;
829
		$this->message = $message;
830
		$this->doLogin();
831
	}
832
5 by Armanelgtron
Fix onNetSync, change deprecated stuff to supported stuff (var to public, ereg to preg)
833
	public $gotPlayer = false;
834
	public $playerid = 0;
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
835
836
	function loopfunc() {
837
		if($this->haveFreeID() && !$this->gotPlayer) {
838
			$p = ePlayerNetId::createPlayer(15, 0, 0, $this->nick);
839
			$p->write($this->sock);
840
			$this->playerid = $p->id;
841
			$this->gotPlayer = true;
842
843
			$this->doChat($this->message);
844
			
845
			$this->logout();
846
		}
847
	}
848
849
	function onLoginDeny($reason) {
850
		print_r($reason);
851
	}
852
	function onNewPlayer($player) {
853
		//print_r($player);
854
	}
855
	function onConsoleMessage($txt) {
856
		//print($txt . "\n");
857
	}
858
	function onChat($id, $txt) {
859
		//print($txt . "\n");
860
	}
861
	function onConfig($name, $value) {
862
		//print($name . ' => ' . $value . "\n");
863
	}
864
}
865
866
class scores extends connection {
7 by Armanelgtron
PHP 8 compatibility and fix doChat to only need one argument
867
	function __construct($host, $port=4534) {
868
		$this->scores($host, $port);
869
	}
870
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
871
	function scores($host, $port=4534) {
872
		$this->connection($host, $port);
873
		$this->doLogin();
874
		$this->lastsyncrequest = $this->connectiontime;
875
	}
876
5 by Armanelgtron
Fix onNetSync, change deprecated stuff to supported stuff (var to public, ereg to preg)
877
	public $printed = false;
878
	public $objects;
879
	public $lastsyncrequest;
880
	public $syncid = 42;
2 by luke-jr
Copy wrtl's PHP client into its new home; wrtl can delete the private one later
881
882
	function loopfunc() {
883
		if(!$printed && microtime(true) - $this->lastsyncrequest > .1) {
884
			$this->lastsyncrequest = microtime(true);
885
			$msg = nMessage::makeMsg(28);
886
			$msg->writeint(0); // actually a real, but z-man says it will
887
			                  // be ignored anyways
888
			$msg->writeshort(1);
889
			$msg->writeshort($this->syncid++);
890
			//print_r($msg);
891
			$this->send($msg);
892
		}
893
		if(!$printed && microtime(true) - $this->connectiontime > 5) {
894
			// didn't get a syncAck, but we can't wait forever
895
			$this->objects = nNetObject::$objects;
896
			$this->logout();
897
			$this->printed = true;
898
		}
899
	}
900
901
	function onSyncAck($c_sync) {
902
		if(!$printed && $c_sync < $this->syncid && $c_sync >= 42) {
903
			$this->objects = nNetObject::$objects;
904
			$this->logout();
905
			$this->printed = true;
906
		}
907
	}
908
909
	function onLoginDeny($reason) {
910
		if(!$this->printed) {
911
			print($reason);
912
		}
913
	}
914
	function onNewPlayer($player) {
915
		//print_r($player);
916
	}
917
}
918
919
//$obj = new bot('arma', 4539);
920
//print_r(new serverinfo('arma', 4539));
921
//print_r(getMasterInfo('localhost'));
922
?>