--- deliantra/Deliantra-Client/DC/Protocol.pm 2007/08/12 09:39:49 1.151 +++ deliantra/Deliantra-Client/DC/Protocol.pm 2007/08/21 00:59:34 1.156 @@ -23,7 +23,7 @@ setup_req => { extmap => 1, excmd => 1, - ywidget => 1,#d# + xwidget1 => 1,#d# %{$arg{setup_req} || {}}, }, ); @@ -61,6 +61,20 @@ #$self->send ("setup sound 0"); # we use a different protocol $self->update_fx_want; + $self->send_exti_req (resource => "exp_table", sub { + my ($exp_table) = @_; + + $self->register_face_handler ($exp_table, sub { + my ($face) = @_; + + $self->{exp_table} = $self->{json_coder}->decode (delete $face->{data}); + + #TODO: update all interested parties + }); + + () + }); + if (my $ts = $cap{tileset}) { if (my ($default) = grep $_->[2] & 1, @$ts) { $self->{tileset} = $default; @@ -106,52 +120,83 @@ my ($ws, $id, $class, $args) = @_; for my $ev (grep /^on_/, keys %$args) { + my $rid = $args->{$ev}; $args->{$ev} = sub { my $id = shift->{s_id}; - $self->send_exti_msg (w_e => $id, $ev, \@_); + $self->send_exti_msg (w_e => $id, $rid, @_); 1 }; } - if (my $widget = eval { + $self->widget_associate ( + $ws, $id => scalar eval { local $SIG{__DIE__}; - "CFPlus::UI::$class"->new ( - %$args, - s_ws => $ws, - s_id => $id, - ) + "CFPlus::UI::$class"->new (%$args) } - ) { - $self->{widget}{$id} - = $self->{widgetset}{$ws}{w}{$id} - = $widget; + ) or warn "server failed creating client-side widget " . (CFPlus::to_json $class) . ": $@\n"; + }); - $widget->connect (on_destroy => sub { - my ($widget) = @_; + # widgetset associate + $self->connect_ext (ws_a => sub { + my (%ass) = @_; + + # everything that has a name, wether conceivably useful or not + my %wkw = ( + root => $CFPlus::UI::ROOT, + tooltip => $CFPlus::UI::TOOLTIP, + + mapwidget => $::MAPWIDGET, + buttonbar => $::BUTTONBAR, + metaserver => $::METASERVER, + buttonbar => $::BUTTONBAR, + login_button => $::LOGIN_BUTTON, + quit_dialog => $::QUIT_DIALOG, + host_entry => $::HOST_ENTRY, + metaserver => $::METASERVER, + server_info => $::SERVER_INFO, + + setup_dialog => $::SETUP_DIALOG, + setup_notebook => $::SETUP_NOTEBOOK, + setup_server => $::SETUP_SERVER, + setup_keyboard => $::SETUP_KEYBOARD, + + pl_notebook => $::PL_NOTEBOOK, + pl_window => $::PL_WINDOW, + inventory_page => $::INVENTORY_PAGE, + stats_page => $::STATS_PAGE, + skill_page => $::SKILL_PAGE, + spell_page => $::SPELL_PAGE, + spell_list => $::SPELL_LIST, + + floorbox => $::FLOORBOX, + help_window => $::HELP_WINDOW, + message_window => $::MESSAGE_WINDOW, + statusbox => $::SDTATUSBOX, + + inv => $::INV, + invr => $::INVR, + invr_hb => $::INVR_HB, - delete $self->{widget}{$widget->{s_id}}; - delete $self->{widgetset}{$widget->{s_ws}}{$widget->{s_id}}; - }); - } else { - warn "server failed creating client-side widget " . (CFPlus::to_json $class) . ": $@\n"; - $self->send_exti_msg (w_e => $id, "destroy"); + ); + + while (my ($id, $name) = each %ass) { + $self->widget_associate (undef, $id => $wkw{$name}) + or warn "server failed to associate non-existent well-known widget $name\n"; } }); # widget call $self->connect_ext (w_c => sub { - my ($id, $rid, $method, $args) = @_; + my ($id, $rid, $method, @args) = @_; my $w = $self->{widget}{$id} or return; - $args ||= []; - if ($rid) { - $self->send_exti_msg (w_r => $rid, [$w->$method (@$args)]); + $self->send_exti_msg (w_r => $rid, $w->$method (@args)); } else { - $w->$method (@$args); + $w->$method (@args); } }); @@ -221,12 +266,41 @@ $self } +sub widget_associate { + my ($self, $ws, $id, $widget) = @_; + + if ($widget) { + $widget->{s_id} = $id; + $self->{widget}{$id} = $widget; + + if ($ws) { + $widget->{s_ws} = $ws; + $self->{widgetset}{$ws}{w}{$id} = $widget; + } + + $widget->connect (on_destroy => sub { + my ($widget) = @_; + + delete $self->{widget}{$widget->{s_id}}; + delete $self->{widgetset}{$widget->{s_ws}}{$widget->{s_id}} + if exists $widget->{s_ws}; + }); + + 1 + } else { + $self->send_exti_msg (w_e => $id, undef); + + 0 + } +} + sub update_fx_want { my ($self) = @_; $self->send_exti_msg (fx_want => { 3 => !!$::CFG->{bgm_enable}, # FT_MUSIC 5 => !!$::CFG->{audio_enable}, # FT_SOUND + 6 => 1, # FT_RSRC }); } @@ -522,7 +596,7 @@ $self->{delay}{$tile} = 1; # we assume the face is in-flight and will eventually come - push @{$self->{face_cb}{$tile}}, sub { + push @{$self->{tile_cb}{$tile}}, sub { delete $self->{delay}{$tile}; $_[0]->upload; }; @@ -554,9 +628,9 @@ my ($hash, $x, $y, $w, $h) = @$map_info; - my $data = $self->{map}->get_rect ($x, $y, $w, $h); - CFPlus::DB::put $self->{mapcache} => $hash => Compress::LZF::compress $data, sub { }; - #warn sprintf "SAVEmap[%s] length %d\n", $hash, length $data;#d# + my $data = Compress::LZF::compress $self->{map}->get_rect ($x, $y, $w, $h); + $self->{map_cache_new}{$hash} = \$data; + CFPlus::DB::put $self->{mapcache} => $hash => $data, sub { }; } sub map_clear { @@ -572,21 +646,21 @@ sub bg_fetch { my ($self) = @_; - my $id; + my $tile; do { - $id = pop @{$self->{bg_fetch}} + $tile = pop @{$self->{bg_fetch}} or return; - } while $self->{texture}[$id]; + } while $self->{texture}[$tile]; - CFPlus::DB::exists tilecache => $id, sub { - my ($exists) = @_; + CFPlus::DB::get tilecache => $tile, sub { + my ($data) = @_; return unless $self->{map}; # stop when destroyed - if ($exists) { - $self->have_tile ($id); - $self->{texture}[$id]->upload; + if (defined $data) { + $self->have_tile ($tile, $data); + $self->{texture}[$tile]->upload; } $self->bg_fetch; @@ -598,20 +672,27 @@ my $gen = $self->{map_change_gen}; - CFPlus::DB::get $self->{mapcache} => $hash, sub { + my $cb = sub { return unless $gen == $self->{map_change_gen}; my ($data) = @_; if (defined $data) { - $data = Compress::LZF::decompress $data; - #warn sprintf "LOADmap[%s,%d,%d] length %d\n", $hash, $x, $y, length $data;#d# + $self->{map_cache_new}{$hash} = \$data; + + my $data = Compress::LZF::decompress $data; my $inprogress = @{ $self->{bg_fetch} || [] }; unshift @{ $self->{bg_fetch} }, $self->{map}->set_rect ($x, $y, $data); $self->bg_fetch unless $inprogress; } }; + + if (my $rdata = $self->{map_cache_old}{$hash}) { + $cb->($$rdata); + } else { + CFPlus::DB::get $self->{mapcache} => $hash, $cb; + } } # hardcode /world/world_xxx_xxx map names, the savings are enourmous, @@ -714,6 +795,7 @@ $self->flush_map; ++$self->{map_change_gen}; + $self->{map_cache_old} = delete $self->{map_cache_new}; my ($ox, $oy) = ($::MAP->ox, $::MAP->oy); @@ -745,56 +827,63 @@ if ($face->{type} == 0) { # FT_FACE my $id = CFPlus::DB::get_tile_id_sync $face->{name}; - $face->{id} = $id; - $self->{faceid}[$facenum] = $id; - + $face->{id} = $id; $self->{map}->set_tileid ($facenum => $id); CFPlus::DB::get tilecache => $id, $cb; } elsif ($face->{type} & 1) { # with metadata CFPlus::DB::get res_meta => $face->{name}, $cb; + + } else { # no metadata + CFPlus::DB::get res_data => $face->{name}, $cb; } } sub face_update { my ($self, $facenum, $face, $changed) = @_; - if ($face->{type} == 0) { # FT_FACE + if ($face->{type} == 0) { + # image, FT_FACE CFPlus::DB::put tilecache => $face->{id} => $face->{data}, sub { } if $changed; - $self->have_tile ($face->{id}); + $self->have_tile ($face->{id}, delete $face->{data}); - } elsif ($face->{type} & 1) { # split metadata + } elsif ($face->{type} & 1) { + # split metadata case, FT_MUSIC, FT_SOUND if ($changed) { # new data - my ($meta_json, $data) = unpack "(w/a*)*", delete $face->{data}; - $face->{meta} = $self->{json_coder}->decode ($meta_json); + my ($meta, $data) = unpack "(w/a*)*", $face->{data}; + $face->{data} = $meta; CFPlus::DB::put res_data => $face->{name} => $data, sub { }; - CFPlus::DB::put res_meta => $face->{name} => $meta_json, sub { }; - } else { - $face->{meta} = $self->{json_coder}->decode (delete $face->{data}); + CFPlus::DB::put res_meta => $face->{name} => $meta, sub { }; } + $face->{data} = $self->{json_coder}->decode ($face->{data}); + ::add_license ($face); if ($face->{type} == 3) { # FT_MUSIC - $self->{music_meta}{$facenum} = $face; - ::message ({ markup => "downloaded song #$facenum" }) if $changed; &::audio_music_push ($facenum); } elsif ($face->{type} == 5) { # FT_SOUND - $self->{sound_meta}{$facenum} = $face; - ::message ({ markup => "downloaded sound #$facenum" }) if $changed; &::audio_sound_push ($facenum); } + } else { + # flat resource case, FT_RSRC + CFPlus::DB::put res_data => $face->{name} => $face->{data}, sub { } + if $changed; + } + + if (my $cbs = $self->{face_cb}{$facenum}) { + $_->($face, $changed) for @$cbs; } } @@ -805,27 +894,48 @@ } sub have_tile { - my ($self, $tile) = @_; + my ($self, $tile, $data) = @_; return unless $self->{map}; my $tex = $self->{texture}[$tile] ||= new CFPlus::Texture - tile => $tile, minify => 1, mipmap => 1, delay => 1; + tile => $tile, + image => $data, delete_image => 1, + minify => 1, mipmap => 1; - $_->($tex) for @{(delete $self->{face_cb}{$tile}) || []}; + if (my $cbs = delete $self->{tile_cb}{$tile}) { + $_->($tex) for @$cbs; + } } -sub connect_face_update { - my ($self, $id, $cb) = @_; +# call in non-void context registers a temporary +# hook with handle, otherwise its permanent +sub on_face_change { + my ($self, $num, $cb) = @_; + + push @{$self->{face_cb}{$num}}, $cb; + + defined wantarray + ? CFPlus::guard { + @{$self->{face_cb}{$num}} + = grep $_ != $cb, + @{$self->{face_cb}{$num}}; + } + : () +} + +# call in non-void context registers a temporary +# hook with handle, otherwise its permanent +sub register_face_handler { + my ($self, $num, $cb) = @_; + + # invoke if available right now + $cb->($self->{face}[$num], 0) + unless exists $self->{face}[$num]{loading}; - push @{$self->{face_cb}{$id}}, $cb; - - CFPlus::guard { - @{$self->{face_cb}{$id}} - = grep $_ != $cb, - @{$self->{face_cb}{$id}}; - } + # future changes + $self->on_face_change ($num => $cb) } sub sound_play { @@ -907,14 +1017,19 @@ $self->logprint ("msg: ", $text); return if $color < 0; # negative color == ignore if not understood - my $fg = $CF_COLOR[$color & 0x1f] || [1, 0, 0]; + my $fg = $CF_COLOR[$color & NDI_COLOR_MASK] || [1, 0, 0]; ## try to create single paragraphs of multiple lines sent by the server # no longer neecssary with TRT servers #$text =~ s/(?<=\S)\n(?=\w)/ /g; - ::message ({ fg => $fg, markup => $_, type => $type, extra => [@extra] }) - for split /\n/, $text; + ::message ({ + fg => $fg, + markup => $_, + type => $type, + extra => [@extra], + color_flags => $color + }) for split /\n/, $text; $self->{statusbox}->add ($text, group => $text, @@ -1026,17 +1141,16 @@ $conn->{open_container} = $tag; update_floorbox; - $::INV_RIGHT_HB->clear (); - $::INV_RIGHT_HB->add (new CFPlus::UI::Label align => 0, expand => 1, text => $name); + $::INVR_HB->clear (); + $::INVR_HB->add (new CFPlus::UI::Label align => 0, expand => 1, text => $name); if ($tag != 0) { # Floor isn't closable, is it? - $::INV_RIGHT_HB->add (new CFPlus::UI::Button + $::INVR_HB->add (new CFPlus::UI::Button text => "Close container", tooltip => "Close the currently open container (if one is open)", on_activate => sub { $::CONN->send ("apply $tag") # $::CONN->{open_container}") if $tag != 0; - #if $CONN->{open_container} != 0; 0 }, );