ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/bin/kgs-igs
Revision: 1.4
Committed: Sun May 30 02:22:01 2004 UTC (20 years ago) by pcg
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +5 -5 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 pcg 1.1 #!/opt/bin/perl
2    
3     # lots of things have been hardcoded. search for #d# to find the places
4    
5     require 5.008;
6    
7     use KGS::Protocol;
8     use KGS::Listener::Debug;
9    
10     use IO::Socket::INET;
11     use Event;
12    
13     use Getopt::Long;
14     use Carp;
15    
16     our $VERSION = '0.0'; # be more confident....
17    
18     $SIG{QUIT} = sub { Carp::confess "SIGQUIT" };
19    
20     my $conn = new KGS::Protocol;
21     my $kgs;
22     my $igs;
23    
24     my $verbose = 1;
25     my $user;
26     my $pass;
27    
28     $Event::DIED = sub {
29     Event::verbose_exception_handler (@_);
30     Event::unloop_all;
31     };
32    
33     sub rank($) {
34 pcg 1.3 return "NR " if !$_[0]->is_ranked || !$_[0]->rank;
35 pcg 1.1 return $_[0]->rank
36 pcg 1.2 . ($_[0]->is_reliable ? "*" : " ");
37 pcg 1.1 }
38    
39     sub format_user($) {
40     my $format =
41     sprintf "%s|%s|%s",
42     $_[0]{name},
43     $_[0]->flags_string,
44     $_[0]->rank_string;
45    
46     $format =~ y/ //d;
47     $format;
48     }
49    
50     sub coord($$) {
51     qw(A B C D E F G H J K L M N O P Q R S T U V W X Y Z)[$_[0]] . $_[1];
52     }
53    
54     #############################################################################
55    
56     package kgs;
57    
58     use base KGS::Listener;
59    
60     sub new {
61     my $class = shift;
62     my $self = bless { @_ }, $class;
63    
64     print STDERR "$0 version $VERSION connecting...\n" if $verbose;
65    
66     my $sock = new IO::Socket::INET PeerHost => KGS::Protocol::KGSHOST, PeerPort => KGS::Protocol::KGSPORT
67     or die "connect: $!";
68    
69     $sock->blocking (1);
70     $conn->handshake ($sock);
71    
72     $self->listen ($conn, "any");
73    
74     # Listener for kgs data
75     $self->{w} = Event->io (fd => $sock, poll => 'r', cb => sub {
76     my $len = sysread $sock, my $buf, 16384;
77     if ($len) {
78     $conn->feed_data ($buf);
79     } elsif (defined $len || (!$!{EINTR} and !$!{EAGAIN})) {
80     print STDERR "disconnected\n" if $verbose;
81     Event::unloop;
82     }
83     });
84    
85     $conn->login ("kgs-igs $VERSION", $self->{user}, delete $self->{password});
86    
87     $self;
88     }
89    
90     sub inject_login {
91     my ($self, $msg) = @_;
92    
93     print STDERR "login: $msg->{message}\n" if $verbose >= 2;
94    
95     if ($msg->{success}) {
96     $igs->login_ok ($msg->{message});
97     } else {
98     $igs->login_failed ($msg->{message});
99     }
100     }
101    
102     sub inject_msg_room {
103     my ($self, $msg) = @_;
104    
105     #use PApp::Util; warn PApp::Util::dumpval $msg;#d#
106     $igs->send ("21 !$msg->{name}!: $msg->{message}");
107     }
108    
109     sub inject_any {
110     my ($self, $msg) = @_;
111     if ($verbose >= 2) {
112     print STDERR "DEBUG: $msg->{type}#$msg->{channel}";
113     for (sort keys %$msg) {
114     print STDERR" $_<$msg->{$_}>";
115     }
116     print STDERR "\n";
117     }
118     }
119    
120     sub inject_upd_rooms {
121     my ($self, $msg) = @_;
122    
123     for (@{$msg->{rooms}}) {
124     # $gtp->send ("kgs-room-update $_->{channel} $_->{name}");
125     }
126     }
127    
128     sub inject_msg_chat {
129     my ($self, $msg) = @_;
130    
131     return unless (lc $self->{conn}{name}) eq (lc $msg->{name2});
132    
133     # $gtp->send ("kgs-user-chat $msg->{name} $msg->{message}");
134     }
135    
136     sub inject_new_game {
137     my ($self, $msg) = @_;
138    
139     $::lastnew = $msg->{channel};#d#
140     # $gtp->send ("kgs-game-new $msg->{id} $msg->{channel}");
141     }
142    
143     #############################################################################
144    
145     package room;
146    
147     use base KGS::Listener::Room;
148    
149     sub new {
150     my $class = shift;
151     my $self = $class->SUPER::new (conn => $conn, @_);
152    
153     $self->listen ($self->{conn});
154     $self->join;
155    
156     $self;
157     }
158    
159     sub event_join {
160     my $self = shift;
161    
162     $self->SUPER::join (@_);
163    
164     $self->{timer} = Event->timer (after => 0, interval => 60, cb => sub {
165     $self->req_games;
166     });
167     }
168    
169     sub event_update_games {
170     my ($self, $add, $upd, $del) = @_;
171    
172     # for (@$add, @$upd) {
173     # $gtp->send (sprintf "kgs-game-update %d %d %s %s %s %s %d %d %f %d %d %d %d %s",
174     # $self->{channel}, $_->{channel},
175     # $_->type_char,
176 pcg 1.4 # ::format_user $_->{black},
177     # ::format_user $_->{white},
178     # ::format_user $_->{owner},
179 pcg 1.1 # $_->size,
180     # $_->{handicap},
181     # $_->{komi},
182     # $_->moves,
183     # $_->{flags},
184     # $_->{observers},
185     # $_->{saved},
186     # $_->{notes},
187     # );
188     # }
189     #
190     # for (@$del) {
191     # $gtp->send ("kgs-game-delete $self->{channel} $_->{channel}");
192     # }
193     }
194    
195     sub event_update_users {
196     my ($self, $add, $upd, $del) = @_;
197    
198     # for (@$add, @$upd) {
199     # $gtp->send (sprintf "kgs-user-update %s", ::format_user $_);
200     # }
201     #
202     # for (@$del) {
203     # $gtp->send (sprintf "kgs-user-remove %s", ::format_user $_);
204     # }
205     }
206    
207     sub DESTROY {
208     my $self = shift;
209    
210     $self->{timer}->cancel if $self->{timer};
211    
212     $self->SUPER::DESTROY;
213     }
214    
215     #############################################################################
216    
217     package game;
218    
219     use Gtk2::GoBoard::Constants;
220    
221     use base KGS::Listener::Game;
222    
223     sub new {
224     my $class = shift;
225     my $self = $class->SUPER::new (conn => $conn, @_);
226    
227     $self->listen ($self->{conn});
228     $self->join;
229    
230     $self;
231     }
232    
233     sub event_update_users {
234     return;
235    
236     my ($self, $add, $upd, $del) = @_;
237    
238     for (@$add, @$upd) {
239     $gtp->send (sprintf "kgs-user-update %s", ::format_user $_);
240     }
241    
242     for (@$del) {
243     $gtp->send (sprintf "kgs-user-remove %s", ::format_user $_);
244     }
245     }
246    
247     sub inject_resign_game {
248     my ($self, $msg) = @_;
249    
250     $gtp->set_gid ($self->{channel});
251     $gtp->send ("play " . (qw(b w))[$msg->{player}] . " resign");
252     }
253    
254     sub inject_final_result {
255     my ($self, $msg) = @_;
256    
257     $gtp->send (sprintf "kgs-game-score %f %f %f %f %f %f",
258     $_->{whitescore}{territory}, $_->{whitescore}{captures}, $_->{whitescore}{komi},
259     $_->{blackscore}{territory}, $_->{blackscore}{captures}, $_->{blackscore}{komi});
260     }
261    
262     sub inject_set_teacher {
263     my ($self, $msg) = @_;
264     }
265    
266     sub event_update_game {
267     my ($self) = @_;
268    
269     $gtp->set_gid ($self->{channel});
270    
271     # timesettings etc.
272     }
273    
274     sub event_update_tree {
275     my ($self) = @_;
276    
277     $gtp->set_gid ($self->{channel});
278    
279     my $path = $self->get_path;
280     my $prev = $self->{prevpath};
281    
282     $self->{prevpath} = [ @$path ];
283    
284     if (@$prev > 1
285     and @$path > @$prev
286     and (join ":", @$prev) eq (join ":", @$path[0 .. $#$prev])) {
287    
288     splice @$path, @prev, $#path, ();
289    
290     } else {
291     $gtp->send ("boardsize $path->[0]{rules}{size}");
292     $gtp->send ("komi $path->[0]{rules}{komi}");
293     $gtp->send ("clear_board");
294    
295     my $setup = shift @$path;
296     my $handi;
297    
298     while (my ($k, $v) = each %$setup) {
299     if ($k =~ /^(\d+),(\d+)$/) {
300     $handi .= " " . ::coord $1, $2;
301     }
302     }
303    
304     $gtp->send ("set_free_handicap$handi");
305     }
306    
307     for (@$path) {
308     while (my ($k, $v) = each %$_) {
309     if ($k =~ /^(\d+),(\d+)$/) {
310     if ($v->[0] & MARK_MOVE) {
311     if ($v->[0] & MARK_B) {
312     $gtp->send ("play b ". ::coord $1, $2);
313     } else {
314     $gtp->send ("play w ". ::coord $1, $2);
315     }
316     }
317     }
318     }
319     }
320     }
321    
322     sub DESTROY {
323     my $self = shift;
324    
325     $self->SUPER::DESTROY;
326     }
327    
328     #############################################################################
329    
330     package igs;
331    
332     use Gtk2::GoBoard::Constants;
333     use KGS::Constants;
334    
335     use Fcntl;
336    
337     sub new {
338     my $class = shift;
339     my $self = bless { @_ }, $class;
340    
341     my $buf;
342     $self->{w} = Event->io (fd => $self->{fh}, poll => 'r', cb => sub {
343     0 < sysread $self->{fh}, $buf, 4096, length $buf
344     or Event::unloop -1;
345    
346     #$buf =~ y/\x00-\x09\x0b-\x0c\x0e-\x1f\x80-\xff//d;
347     while ($buf =~ s/^([^\015\012]*)\015?\012//) {
348     $self->{feed}->($1);
349     }
350     });
351    
352     syswrite $self->{fh}, "##########################################################\r\n" for 1..1;
353     sleep 1; # required for amny clients :/
354     syswrite $self->{fh}, "Login: ";
355    
356     $self->{feed} = sub {
357     my $user = $_[0];
358     print "login<$user>\n";
359     #$self->send ("\377\373\1");
360     $self->send ("1 1");
361     $self->{feed} = sub {
362     warn "pass <$_[0]>\n";#d#
363     $kgs = new kgs user => $user, password => $_[0] eq "guest" ? "" : $_[0];
364    
365     $self->{feed} = sub { };
366     }
367     };
368    
369     $self;
370     }
371    
372     sub send {
373     my ($self, $cmd) = @_;
374    
375     print "SEND<$cmd>\n";#d#
376     syswrite $self->{fh}, "$cmd\015\012";
377     }
378    
379 pcg 1.3 sub roominfo {
380     my ($self) = @_;
381    
382     (
383     [values %{$self->{room}{users} || {}}],
384     [values %{$self->{room}{games} || {}}],
385     )
386     }
387    
388 pcg 1.1 sub feed {
389     my ($self, $line) = @_;
390    
391     warn "GOT<$line>\n";#d#
392    
393     if ($line =~ /^ga/) { # gamelist
394     $self->send ("7 [##] white name [ rk ] black name [ rk ] (Move size H Komi BY FR) (###)");
395     if ($self->{room}) {
396 pcg 1.3 my (undef, $games) = $self->roominfo;
397     for (@$games) {
398 pcg 1.1 next if $_->{size} <= 0 || $_->{moves} <= 0 || $_->{handicap} < 0 || $_->is_scored;
399    
400     $self->send (sprintf
401 pcg 1.2 "7 [%2d] %11s [%4s] vs. %11s [%4s] (%3d %4d %2d %4.1f %2d %s%s) (%3d)",
402 pcg 1.1 $_->{channel},
403 pcg 1.4 $_->{white}{name}, ::rank $_->{white},
404     $_->{black}{name}, ::rank $_->{black},
405 pcg 1.1 $_->{moves},
406     $_->{size},
407     $_->{handicap},
408     $_->{komi},
409 pcg 1.2 10, "F", "I",
410 pcg 1.1 $_->{observers});
411     }
412     }
413     } elsif ($line =~ /^chan/) { # channels
414     $self->send ("9 #13 Title: English Game Room -- Open");
415    
416     } elsif ($line =~ /^(y\S*|;)\s*/g) { # yell
417     my $channel = $self->{channel};
418     $channel = $1 if $line =~ /\\(\d+)\s*/gc;
419    
420     if ($self->{channel} != $channel) {
421     $self->{channel} = $channel;
422     (delete $self->{room})->part if $self->{room};
423    
424     if ($channel > 0) {
425     $self->{room} = new room channel => $channel;
426     $self->send ("32 Changing into channel $channel.");
427     $self->send ("32 Welcome to cyberspace.");#maybe title##d#
428    
429     }
430     }
431    
432     if ($channel > 0) {
433     # msg_chat to room
434     }
435 pcg 1.2
436     } elsif ($line =~ /^up/) { # uptime
437     $self->send ("9 The current time (GMT) is: Wed May 26 18:21:08 2004");#d#
438     $self->send ("9 The current IGS local time is: Thu May 27 03:21:08 2004");#d#
439     $self->send ("9 Local hour when cron and the ratings, are run: 04:00");
440     $self->send ("9 The server has been up 117 days 10 hours 13 minutes");#d#
441     $self->send ("9 Max moves per game 1000");
442     $self->send ("9 Max dead stones per game 750");
443     $self->send ("9 Max account name length 11");
444     $self->send ("9 Max number of games a player can observe 20");
445     $self->send ("9 Number of aliases allowed 20");
446     $self->send ("9 Number of moves between saves 10");
447     $self->send ("9 Number of allowed connections 50");
448     $self->send ("9 Max channel number 99");
449     $self->send ("9 Locking: Off.");
450     $self->send ("9 Players: 513, Games: 118");#d#
451     $self->send ("9 Max Board Size 19");
452     $self->send ("9 How many days games are stored 180");
453     $self->send ("9 How many days before an inactive player is removed 180");
454    
455     } elsif ($line =~ /^stats\s+(\S+)/) { # stats
456     $self->send ("9 Player: $1");
457     $self->send ("9 Game: go (1)");
458     $self->send ("9 Language: default");
459     $self->send ("9 Rating: 13k 0");
460     $self->send ("9 Rated Games: 0");
461     $self->send ("9 Rank: 13k 16");
462     $self->send ("9 Wins: 0");
463     $self->send ("9 Losses: 0");
464     $self->send ("9 Idle Time: (On server) 0s");
465     $self->send ("9 Address: igs\@schmorp.de");
466     $self->send ("9 Country: Germany");
467     $self->send ("9 Reg date: Mon May 24 08:58:12 2004");
468     $self->send ("9 Info: <None>");
469     $self->send ("9 Defaults (help defs): time 90, size 19, byo-yomi time 10, byo-yomi stones 25");
470     $self->send ("9 Verbose Bell Quiet Shout Automail Open Looking Client Kibitz Chatter");
471     $self->send ("9 Off Off On On Off Off Off On On On");
472    
473 pcg 1.3 } elsif ($line =~ /^t\S*\s+(\S+)(?:\s+(\S+))/) { # toggle
474     my ($setting, $value) = $1;
475     if ($2 =~ /^t/i) {
476     $self->{toggle}{$setting} = 1;
477     } elsif ($2 =~ /^f/i) {
478     $self->{toggle}{$setting} = 0;
479     } else {
480     $self->{toggle}{$setting} = !$self->{toggle}{$setting};
481     }
482    
483     } elsif ($line =~ /^id\s+(.*)/) { # id
484     $self->{id} = $1;
485    
486     } elsif ($line =~ /^wh\S*(?:\s+(.*))?/) { # who
487    
488     $self->send ("27 Info Name Idle Rank | Info Name Idle Rank");
489     my ($users, $games) = $self->roominfo;
490     for (@$users) {
491     # 27 SX -- -- guest8814 4s NR | -- -- guest7528 1m NR
492     }
493     $self->send (sprintf "27 ******** %d Players %d Total Games ********",
494     scalar @$users, scalar @$games);
495    
496     } elsif ($line =~ /^us\S*(?:\s+(.*))?/) { # users
497    
498     $self->send ("42 Name Info Country Rank Won/Lost Obs Pl Idle Flags Language");
499     my ($users, $games) = $self->roominfo;
500     for (@$users) {
501     $self->send (sprintf "42 %-11.11s -- %5s 0/ 0 - - %3s SX default",
502     $_->{name},
503     ::rank ($_),
504     "0s",
505     );
506     }
507     $self->send (sprintf "9 ******** %d Players %d Total Games ********",
508     scalar @$users, scalar @$games);
509    
510 pcg 1.2 # 21 {Game 28: Robot10 vs catty : W 25.5 B 77.0}
511     # 21 {smorim [1k*] has connected.}
512     # 21 {daruma21 [5k*] has connected.}
513     # 21 {Game 102: boni vs z2004 : Black forfeits on time.}
514     # 21 {Game 14: gb vs Mahoba : White resigns.}
515     # 21 {brianlee has disconnected}
516    
517    
518    
519 pcg 1.1 } else {
520     $self->send ("5 Unknown command.");
521     }
522    
523     $self->send ("1 5");
524    
525     }
526    
527     sub login_ok {
528     my ($self, $msg) = @_;
529    
530     $self->{feed} = sub { $self->feed ($_[0]) };
531     #$self->send ("39 IGS entry on 05 - 26 - 2004");
532     #$self->send ("9 File"); # motd
533    
534     $self->send ("9 File");
535     $self->send (" $msg");
536     $self->send ("9 File");
537 pcg 1.3 $self->send ("39 IGS entry on 05 - 27 - 2004");#d#
538 pcg 1.1 $self->send ("1 5");
539    
540     $self->{room} = new room channel => 13; # auto-join english room #d#
541     }
542    
543     sub login_failed {
544     my ($self, $msg) = @_;
545    
546     $self->send ("5 $msg");
547     Event::unloop -1;
548     }
549    
550     package main;
551    
552     sub usage {
553     print STDERR <<EOF;
554     Usage: $0 [options] port
555     -v increase verbosity
556     -q decrease verbosity
557    
558     EOF
559     exit shift;
560     }
561    
562     GetOptions (
563     "v" => sub { $verbose++ },
564     "q" => sub { $verbose-- },
565     "h" => sub { usage(0) },
566     ) or die usage(1);
567    
568     my $port = $ARGV[0] || 6969;
569    
570     my $socket = new IO::Socket::INET LocalPort => $port, Listen => 1, ReuseAddr => 1,
571     or die "cannot create listening socket on port $port: $!";
572    
573     print "Listening on 127.0.0.1:$port, please connect to it using your igs client.\n";
574    
575     while (my $fh = $socket->accept) {
576     if (fork == 0) {
577     $igs = new igs fh => $fh;
578    
579     Event::loop;
580     exit 0;
581     }
582     }
583    
584     1;
585    
586