ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/bin/kgs-igs
Revision: 1.1
Committed: Wed May 26 03:28:36 2004 UTC (20 years ago) by pcg
Branch: MAIN
Log Message:
*** empty log message ***

File Contents

# Content
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 return "NR" if !$_[0]->is_ranked || !$_[0]->rank;
35 return $_[0]->rank
36 . ($_[0]->is_reliable ? "*" : "");
37 }
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 # ::format_user $_->{user1},
177 # ::format_user $_->{user2},
178 # ::format_user $_->{user3},
179 # $_->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 sub feed {
380 my ($self, $line) = @_;
381
382 warn "GOT<$line>\n";#d#
383
384 if ($line =~ /^ga/) { # gamelist
385 $self->send ("7 [##] white name [ rk ] black name [ rk ] (Move size H Komi BY FR) (###)");
386 if ($self->{room}) {
387 for (values %{$self->{room}{games} || {}}) {
388 next if $_->{size} <= 0 || $_->{moves} <= 0 || $_->{handicap} < 0 || $_->is_scored;
389
390 $self->send (sprintf
391 "7 [%d] %11s [%4s] vs. %11s [%4s] (%3d %4d %2d %3.1f %2d %s%s) (%3d)",
392 $_->{channel},
393 $_->{user2}{name}, ::rank $_->{user2},
394 $_->{user1}{name}, ::rank $_->{user1},
395 $_->{moves},
396 $_->{size},
397 $_->{handicap},
398 $_->{komi},
399 0, "F", "I",
400 $_->{observers});
401 }
402 }
403 } elsif ($line =~ /^chan/) { # channels
404 $self->send ("9 #13 Title: English Game Room -- Open");
405
406 } elsif ($line =~ /^(y\S*|;)\s*/g) { # yell
407 my $channel = $self->{channel};
408 $channel = $1 if $line =~ /\\(\d+)\s*/gc;
409
410 if ($self->{channel} != $channel) {
411 $self->{channel} = $channel;
412 (delete $self->{room})->part if $self->{room};
413
414 if ($channel > 0) {
415 $self->{room} = new room channel => $channel;
416 $self->send ("32 Changing into channel $channel.");
417 $self->send ("32 Welcome to cyberspace.");#maybe title##d#
418
419 }
420 }
421
422 if ($channel > 0) {
423 # msg_chat to room
424 }
425 } else {
426 $self->send ("5 Unknown command.");
427 }
428
429 $self->send ("1 5");
430
431 }
432
433 sub login_ok {
434 my ($self, $msg) = @_;
435
436 $self->{feed} = sub { $self->feed ($_[0]) };
437 #$self->send ("39 IGS entry on 05 - 26 - 2004");
438 #$self->send ("9 File"); # motd
439
440 $self->send ("9 File");
441 $self->send (" $msg");
442 $self->send ("9 File");
443 $self->send ("1 5");
444
445 $self->{room} = new room channel => 13; # auto-join english room #d#
446 }
447
448 sub login_failed {
449 my ($self, $msg) = @_;
450
451 $self->send ("5 $msg");
452 Event::unloop -1;
453 }
454
455 package main;
456
457 sub usage {
458 print STDERR <<EOF;
459 Usage: $0 [options] port
460 -v increase verbosity
461 -q decrease verbosity
462
463 EOF
464 exit shift;
465 }
466
467 GetOptions (
468 "v" => sub { $verbose++ },
469 "q" => sub { $verbose-- },
470 "h" => sub { usage(0) },
471 ) or die usage(1);
472
473 my $port = $ARGV[0] || 6969;
474
475 my $socket = new IO::Socket::INET LocalPort => $port, Listen => 1, ReuseAddr => 1,
476 or die "cannot create listening socket on port $port: $!";
477
478 print "Listening on 127.0.0.1:$port, please connect to it using your igs client.\n";
479
480 while (my $fh = $socket->accept) {
481 if (fork == 0) {
482 $igs = new igs fh => $fh;
483
484 Event::loop;
485 exit 0;
486 }
487 }
488
489 1;
490
491