--- deliantra/Deliantra-Client/DC/Main.pm 2011/12/29 07:13:44 1.3 +++ deliantra/Deliantra-Client/DC/Main.pm 2012/01/04 11:23:23 1.8 @@ -43,7 +43,15 @@ BEGIN { $SIG{__DIE__} = sub { - return if $^S; + return if $^S; # quick reject + + # return if there are any eval contexts in the csall stack + for my $i (0..999) { + my ($sub, $is_require) = (caller $i)[3, 7] + or last; + return if $sub eq "(eval)" && !$is_require; + } + crash "CRASH/DIE: $_[0]" => 1; DC::fatal Carp::longmess "$_[0]"; } @@ -136,6 +144,7 @@ our $MUSIC_PLAYING_WIDGET; our $LICENSE_WIDGET; +our $DOWNLOADS_WIDGET; our $PICKUP_PAGE; our $INVENTORY_PAGE; @@ -518,6 +527,178 @@ } ############################################################################# +# Over-the-air updates + +sub ota_update_finish { + $MESSAGE_DIST->message ({ type => "ota_update", markup => $_[0] }); +} + +sub ota_update_status { +} + +sub ota_update { + my ($face, $size) = @_; + + my $coro = Coro::async_pool { + my $override = "$Urlader::EXE_DIR/override"; + + $MESSAGE_DIST->add_channel ({ + id => "ota_update", + title => "Update", + tooltip => "Software Update Log", + }); + + $MESSAGE_DIST->message ({ type => "ota_update", markup => "preparing override..." }); + + my $fh = Coro::AIO::aio_open "$override.tmp", IO::AIO::O_WRONLY | IO::AIO::O_CREAT | IO::AIO::O_TRUNC, 0777; + + unless ($fh) { + ota_update_finish "unable to write software update:\n$Urlader::EXE_DIR/override.tmp:\n$!"; + return; + } + + $MESSAGE_DIST->message ({ type => "ota_update", markup => "downloading $size bytes..." }); + + my $cv = AE::cv; + my $error; + + $cv->begin (Coro::rouse_cb); + + $CONN->ask_face ( + $face, + -1000, + sub { + $STATUSBOX->add ( + (sprintf "update download: %d/%d", $size - $_[1], $size), + pri => -9, group => "ota_update", timeout => 60, fg => [1, 1, 0, 1] + ); + + $cv->begin; + my $len = length $_[2]; + IO::AIO::aio_write $fh, $_[1], $len, $_[2], undef, sub { + $error ||= $_[0] != $len; + $cv->end; + }; + }, + sub { + $cv->end; + }, + ); + + Coro::rouse_wait; + + $STATUSBOX->clr_group ("ota_update"); + + $error ||= Coro::AIO::aio_fsync $fh; + $error ||= Coro::AIO::aio_close $fh; + + if ($error) { + $MESSAGE_DIST->message ({ type => "ota_update", markup => "file write error, update aborted." }); + Coro::AIO::aio_unlink "$override.tmp"; + return; + } + + $MESSAGE_DIST->message ({ type => "ota_update", markup => "replacing override file..." }); + + if (Coro::AIO::aio_rename "$override.tmp", $override) { + $MESSAGE_DIST->message ({ type => "ota_update", markup => "unable to replace override file, update aborted." }); + Coro::AIO::aio_unlink "$override.tmp"; + } + + $MESSAGE_DIST->message ({ type => "ota_update", markup => "update successfull, changes become active at next start." }); + }; + + $CONN->{ota_update} = Guard::guard { + $coro->cancel; + }; +} + +sub ota_update_ask { + my ($ok, $face, $ver, $size, $changes) = @_; + + $CONN->{w}{ota_dialog} = my $dialog = new DC::UI::Toplevel + x => "center", + y => "center", + max_w => $::WIDTH * 0.7, + max_h => $::WIDTH * 0.7, + title => "Software update available", + child => my $vbox = new DC::UI::VBox, + ; + + $vbox->add (new DC::UI::Label + ellipsise => 0, + text => "The server offers a software update, " + . "do you want to start downloading this update in the background?", + ); + + $vbox->add (new DC::UI::FancyFrame + expand => 1, + label => "Changes", + child => (new DC::UI::TextScroller + expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4, + par => [{ + markup => "Old revision: $Urlader::EXE_VER\n" + . "New revision: $ver\n" + . "Download size: $size bytes\n\n" + . DC::asxml $changes + }], + ), + ); + + $vbox->add (my $hbox = new DC::UI::HBox); + + $hbox->add (new DC::UI::Button + expand => 1, + text => "Not now", + on_activate => sub { + $dialog->destroy; + 0 + } + ); + $hbox->add (new DC::UI::Button + expand => 1, + text => "Yes, start downloading", + on_activate => sub { + $dialog->destroy; + ota_update $face, $size; + 0 + }, + ); + + $dialog->show; +} + +sub ota_update_check { + return unless defined $Urlader::EXE_ID; + + ::message { markup => "Checking for software update..." }; + + $CONN->send_exti_req (ota_update => $Urlader::URLADER_VERSION, $Urlader::EXE_ID, $Urlader::EXE_VER, sub { + my ($ok, $face, $ver, $size, $changes) = @_; + + if ($ok) { + if (defined $ver) { + ::message { markup => "Server offers version $ver (we are version $Urlader::EXE_VER)." }; + &ota_update_ask; + } else { + ::message { markup => "Server has no newer version." }; + } + } else { + ::message { markup => "Server does not support software update." }; + } + +# $self->register_face_handler ($exp_table, sub { +# my ($face) = @_; + +# $self->{exp_table} = $self->{json_coder}->decode (delete $face->{data}); +# $_->() for values %{ $self->{on_exp_update} || {} }; +# }); + + () + }); +} + +############################################################################# sub destroy_query_dialog { (delete $_[0]{query_dialog})->destroy @@ -786,6 +967,8 @@ if ($_[0]) { DC::lowdelay fileno $CONN->{fh}; + ota_update_check; + status "successfully connected to the server"; } else { undef $CONN; @@ -1844,12 +2027,12 @@ $table->add_at (1, $row++, new DC::UI::Label align => 0, text => $Deliantra::VARDIR, tooltip => ""); $table->add_at (0, $row , new DC::UI::Label align => 1, text => "Database Directory"); $table->add_at (1, $row++, new DC::UI::Label align => 0, text => $DC::DB::DBDIR, tooltip => ""); + $table->add_at (0, $row , new DC::UI::Label align => 1, text => "Urlader (Prebuilt)"); + $table->add_at (1, $row++, new DC::UI::Label align => 0, text => $ENV{URLADER_VERSION}, tooltip => ""); $table->add_at (0, $row , new DC::UI::Label align => 1, text => "Branch (Prebuilt)"); - $table->add_at (1, $row++, new DC::UI::Label align => 0, text => $::EXE_ID, tooltip => ""); - $table->add_at (0, $row , new DC::UI::Label align => 1, text => "Version (Prebuilt)"); - $table->add_at (1, $row++, new DC::UI::Label align => 0, text => $::EXE_VER, tooltip => ""); - $table->add_at (0, $row , new DC::UI::Label align => 1, text => "Update (Prebuilt)"); - $table->add_at (1, $row++, new DC::UI::Label align => 0, text => $::UPDPAR, tooltip => ""); + $table->add_at (1, $row++, new DC::UI::Label align => 0, text => $ENV{URLADER_EXE_ID}, tooltip => ""); + $table->add_at (0, $row , new DC::UI::Label align => 1, text => "Revision (Prebuilt)"); + $table->add_at (1, $row++, new DC::UI::Label align => 0, text => $ENV{URLADER_EXE_VER}, tooltip => ""); } $vbox @@ -2033,16 +2216,51 @@ my $vb = new DC::UI::VBox; $vb->add (new DC::UI::FancyFrame - label => "Currently playing music", + label => "Current background music", child => new DC::UI::ScrolledWindow scroll_x => 1, scroll_y => 0, child => ($MUSIC_PLAYING_WIDGET = new DC::UI::Label ellipsise => 0, fontsize => 0.8), ); $vb->add (new DC::UI::FancyFrame + label => "Current downloads", + child => ($DOWNLOADS_WIDGET = new DC::UI::Table + expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4), + ); + + $DOWNLOADS_WIDGET->connect (visibility_change => sub { + my ($self) = @_; + + delete $self->{updater}; + return unless $_[1]; + + $self->{updater} = AE::timer 0, 0.7, sub { + $self->clear; + + return unless $CONN; + + my @nums = sort { $b <=> $a } keys %{ $CONN->{ix_recv_buf} }; + return unless @nums; + + $self->add_at (0, 0, new DC::UI::Label align => 1, text => "Face"); + $self->add_at (1, 0, new DC::UI::Label align => 0, text => "Octets/Total"); + + for my $row (0 .. $#nums) { + my $num = $nums[$row]; + + my $total = length $CONN->{ix_recv_buf}{$num}; + my $got = $total - $CONN->{ix_recv_ofs}{$num}; + + $self->add_at (0, $row + 1, new DC::UI::Label align => 1, text => $num, tooltip => ""); + $self->add_at (1, $row + 1, new DC::UI::Label align => 0, text => "$got/$total", tooltip => ""); + } + }; + }); + + $vb->add (new DC::UI::FancyFrame label => "Other media used in this session", expand => 1, child => ($LICENSE_WIDGET = new DC::UI::TextScroller - expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4), + expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4), ); $vb @@ -2842,9 +3060,10 @@ } # fontconfig doesn't support relative paths anymore, so use abs_path and keep fingers crossed + # these are ignored under windows, for some reason, and thus set in the loader $ENV{FONTCONFIG_FILE} = "fonts.conf"; $ENV{FONTCONFIG_PATH} = Cwd::abs_path DC::find_rcfile "fonts"; - $ENV{FONTCONFIG_DIR} = $ENV{FONTCONFIG_PATH}; + $ENV{FONTCONFIG_DIR} = $ENV{FONTCONFIG_PATH}; # helps with older versions { my @fonts = map DC::find_rcfile "fonts/$_", qw(