--- deliantra/Deliantra-Client/DC/Main.pm 2011/12/27 07:23:33 1.1
+++ deliantra/Deliantra-Client/DC/Main.pm 2012/01/04 11:23:23 1.8
@@ -19,6 +19,7 @@
use common::sense;
use Carp 'verbose';
+use Cwd ();
use EV;
BEGIN { *time = \&EV::time }
@@ -42,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]";
}
@@ -135,6 +144,7 @@
our $MUSIC_PLAYING_WIDGET;
our $LICENSE_WIDGET;
+our $DOWNLOADS_WIDGET;
our $PICKUP_PAGE;
our $INVENTORY_PAGE;
@@ -517,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
@@ -785,6 +967,8 @@
if ($_[0]) {
DC::lowdelay fileno $CONN->{fh};
+ ota_update_check;
+
status "successfully connected to the server";
} else {
undef $CONN;
@@ -1133,7 +1317,7 @@
tooltip => "You can override the audio driver to use here. Leaving it empty will result "
. "in Deliantra picking one automatically. GNU/Linux users often prefer specific "
. "drivers though, and can experiment with alsa, dsp, esd, pulse, arts, nas "
- . "or other system-specific drivers. Selecting the wrong driver here will simply result"
+ . "or other system-specific drivers. Selecting the wrong driver here will simply result "
. "in no sound.",
on_changed => sub { my ($self, $value) = @_; $CFG->{audio_driver} = $value; 1 }
);
@@ -1775,59 +1959,83 @@
}
sub client_setup {
- my $table = new DC::UI::Table expand => 1, col_expand => [0, 1];
+ my $vbox = new DC::UI::VBox;
- my $row = 0;
+ $vbox->add (my $top = new DC::UI::FancyFrame expand => 1, label => "Client Settings");
+ $vbox->add (my $bot = new DC::UI::FancyFrame expand => 1, label => "Client Info");
- $table->add_at (0, $row, new DC::UI::Label align => 1, text => "Tip of the day");
- $table->add_at (1, $row++, new DC::UI::CheckBox
- c_colspan => 2,
- state => $CFG->{show_tips},
- tooltip => "Show the Tip of the day window at startup?",
- on_changed => sub {
- my ($self, $value) = @_;
- $CFG->{show_tips} = $value;
- 0
- }
- );
+ {
+ $top->add (my $table = new DC::UI::Table expand => 1, col_expand => [0, 1]);
- $table->add_at (0, $row, new DC::UI::Label align => 1, text => "Message Window Size");
- $table->add_at (1, $row++, my $saycmd = new DC::UI::Entry
- c_colspan => 2,
- text => $CFG->{logview_max_par},
- tooltip => "This is maximum number of messages remembered in the Message window. If the server "
- . "sends more messages than this number, older messages get removed to save memory and "
- . "computing time. A value of 0 disables this feature, but that is not recommended.",
- on_changed => sub {
- my ($self, $value) = @_;
- $MESSAGE_DIST->set_max_par ($CFG->{logview_max_par} = $value*1);
- 0
- },
- );
+ my $row = 0;
- $table->add_at (0, $row, new DC::UI::Label align => 1, text => "Config Autosave");
- $table->add_at (1, $row, new DC::UI::CheckBox
- state => $CFG->{config_autosave},
- tooltip => "Normally, configuration settings and the user interface layout "
- . "are saved on client exit. You can disable this behaviour by "
- . "unchecking this checkbox.",
- on_changed => sub {
- my ($self, $value) = @_;
- $CFG->{config_autosave} = $value;
- 0
- }
- );
- $table->add_at (2, $row++, new DC::UI::Button
- text => "Save Now",
- tooltip => "Use this to manually save configuration and UI layout when "
- . "autosave is disabled.",
- on_activate => sub {
- DC::write_cfg;
- 0
- }
- );
+ $table->add_at (0, $row, new DC::UI::Label align => 1, text => "Tip of the day");
+ $table->add_at (1, $row++, new DC::UI::CheckBox
+ c_colspan => 2,
+ state => $CFG->{show_tips},
+ tooltip => "Show the Tip of the day window at startup?",
+ on_changed => sub {
+ my ($self, $value) = @_;
+ $CFG->{show_tips} = $value;
+ 0
+ }
+ );
- $table
+ $table->add_at (0, $row, new DC::UI::Label align => 1, text => "Message Window Size");
+ $table->add_at (1, $row++, my $saycmd = new DC::UI::Entry
+ c_colspan => 2,
+ text => $CFG->{logview_max_par},
+ tooltip => "This is maximum number of messages remembered in the Message window. If the server "
+ . "sends more messages than this number, older messages get removed to save memory and "
+ . "computing time. A value of 0 disables this feature, but that is not recommended.",
+ on_changed => sub {
+ my ($self, $value) = @_;
+ $MESSAGE_DIST->set_max_par ($CFG->{logview_max_par} = $value*1);
+ 0
+ },
+ );
+
+ $table->add_at (0, $row, new DC::UI::Label align => 1, text => "Config Autosave");
+ $table->add_at (1, $row, new DC::UI::CheckBox
+ state => $CFG->{config_autosave},
+ tooltip => "Normally, configuration settings and the user interface layout "
+ . "are saved on client exit. You can disable this behaviour by "
+ . "unchecking this checkbox.",
+ on_changed => sub {
+ my ($self, $value) = @_;
+ $CFG->{config_autosave} = $value;
+ 0
+ }
+ );
+ $table->add_at (2, $row++, new DC::UI::Button
+ text => "Save Now",
+ tooltip => "Use this to manually save configuration and UI layout when "
+ . "autosave is disabled.",
+ on_activate => sub {
+ DC::write_cfg;
+ 0
+ }
+ );
+ }
+
+ {
+ $bot->add (my $table = new DC::UI::Table expand => 1, col_expand => [0, 1]);
+
+ my $row = 0;
+
+ $table->add_at (0, $row , new DC::UI::Label align => 1, text => "Data Directory");
+ $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 => $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
}
sub autopickup_setup {
@@ -2008,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
@@ -2634,7 +2877,7 @@
$NOW = EV::now;
($SDL_CB[$_->{type}] || sub { warn "unhandled event $_->{type}" })->($_)
- for DC::poll_events;
+ for DC::peep_events;
if (%animate_object) {
$_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
@@ -2816,8 +3059,11 @@
}
}
- $ENV{FONTCONFIG_FILE} = DC::find_rcfile "fonts/fonts.conf";
- $ENV{FONTCONFIG_DIR} = DC::find_rcfile "fonts";
+ # 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}; # helps with older versions
{
my @fonts = map DC::find_rcfile "fonts/$_", qw(