--- deliantra/server/utils/cfutil.in 2009/10/26 11:31:39 1.73 +++ deliantra/server/utils/cfutil.in 2009/11/15 18:04:00 1.79 @@ -1,5 +1,27 @@ #!@PERL@ +# +# This file is part of Deliantra, the Roguelike Realtime MMORPG. +# +# Copyright (©) 2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team +# +# Deliantra is free software: you can redistribute it and/or modify it under +# the terms of the Affero GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the Affero GNU General Public License +# and the GNU General Public License along with this program. If not, see +# . +# +# The authors can be reached via e-mail to +# + use strict; my $prefix = "@prefix@"; @@ -23,6 +45,7 @@ use YAML::XS (); use JSON::XS (); use IO::AIO (); +use Digest::MD5 (); use Coro 5.12; use Coro::AIO; @@ -75,6 +98,28 @@ my $s_INT = EV::signal INT => sub { exit 1 }; my $s_TERM = EV::signal TERM => sub { exit 1 }; +our %hash; + +# here we could try to avoid collisions and reduce chksum size further +sub make_hash($\$\$;$) { + my ($id, $dataref, $hashref, $clen) = @_; + + my $hash = substr +(Digest::MD5::md5 $$dataref), 0, $clen || 4; + + if (exists $hash{$hash}) { + # hahs collinion, but some files are simply identical + if (${$hash{$hash}[1]} ne $$dataref) { + warn "hash collision $hash{$hash}[0] vs. $id\n"; + exit 1; + } else { + print "$hash{$hash}[0] and $id are identical (which is fine).\n" if $VERBOSE >= 3; + } + } + $hash{$hash} = [$id, $dataref, $hashref]; + + $$hashref = $hash; +} + mkdir $TMPDIR, 0700 or die "$TMPDIR: $!"; @@ -385,7 +430,7 @@ $ARC{$m->{_name}} = $m; } - $o->{editor_folder} = $dir; + $o->{editor_folder} ||= "\x00$dir"; # horrible kludge my $visibility = delete $o->{visibility}; my $magicmap = delete $o->{magicmap}; @@ -609,20 +654,21 @@ sub generate_plurals { # use Lingua::EN::Inflect (); # Lingua::EN::Inflect::classical; -# Lingua::EN::Inflect::def_noun 'talisman' => 'talismans'; +# Lingua::EN::Inflect::def_noun '(.*)staff' => '$1staves'; # policy # Lingua::EN::Inflect::def_noun '(.*)boots' => '$1boots'; # hack # # for my $a (@ARC) { # my $name = $a->{name} || $a->{_name}; # # next unless $a->{name_pl}; -# +# next if $a->{invisible}; +# next if $a->{is_floor}; +# next if $a->{no_pick}; # # my $test = Lingua::EN::Inflect::PL_N_eq $name, Lingua::EN::Inflect::PL $name; # my $pl = $test =~ /^(?:eq|p:.)$/ # ? $name # : Lingua::EN::Inflect::PL $name; -# die "$test $pl" if $pl =~ /bootss/;#d# # # if ($pl ne $a->{name_pl}) { # warn "$a->{_name}: plural differs, $pl vs $a->{name_pl}\n"; @@ -678,8 +724,9 @@ print "scanning done, processing results...\n" if $VERBOSE; { # remove path prefix from editor_folder - substr $_->{editor_folder}, 0, 1 + length $path, "" - for values %ARC; + $_->{editor_folder} =~ /^\x00/ + and substr $_->{editor_folder}, 0, 2 + length $path, "" + for values %ARC; print "resolving inheritance tree...\n" if $VERBOSE; # resolve inherit @@ -720,14 +767,14 @@ print "generating plurals...\n" if $VERBOSE; generate_plurals; - print "writing archetypes...\n" if $VERBOSE; + printf "writing %d archetypes...\n", scalar @ARC if $VERBOSE; open my $fh, ">:utf8", "$DATADIR/archetypes~" or die "$DATADIR/archetypes~: $!"; print $fh Deliantra::archlist_to_string [sort { $a->{_name} cmp $b->{_name} } @ARC]; } { - print "writing treasures...\n" if $VERBOSE; + printf "writing treasures (%d octets)...\n", length $TRS if $VERBOSE; open my $fh, ">:utf8", "$DATADIR/treasures~" or die "$DATADIR/treasures~: $!"; print $fh $TRS; @@ -739,6 +786,9 @@ length $v->{data32} or warn "$k: face has no png32. this will not work (shoddy gcfclient will crash of course).\n"; length $v->{data64} or warn "$k: face has no png64. this will not work very well.\n"; + make_hash $k, $v->{data32}, $v->{hash32}; + make_hash $k, $v->{data64}, $v->{hash64}; + #length $v->{data32} <= 10000 or warn "$k: face32 larger than 10000 bytes, will not work with crossfire client.\n"; #length $v->{data64} <= 10000 or warn "$k: face64 larger than 10000 bytes.\n"; @@ -750,7 +800,30 @@ delete $v->{arc}; } - print "writing facedata...\n" if $VERBOSE; + print "processing resources...\n" if $VERBOSE; + my $enc = JSON::XS->new->utf8->canonical->relaxed; + while (my ($k, $v) = each %RESOURCE) { + + if ($v->{type} & 1) { + # prepend meta info + + my $meta = $enc->encode ({ + name => $k, + %{ $v->{meta} || {} }, + }); + + $v->{data} = pack "(w/a*)*", $meta, $v->{data}; + } + + make_hash $k, $v->{data}, $v->{hash}, 6; # 6 for the benefit of existing clients + } + + printf "writing facedata (%d faces, %d anims, %d resources)...\n", + scalar keys %FACEINFO, + scalar keys %ANIMINFO, + scalar keys %RESOURCE + if $VERBOSE; + open my $fh, ">:perlio", "$DATADIR/facedata~" or die "$DATADIR/facedata~: $!"; @@ -760,6 +833,7 @@ animinfo => \%ANIMINFO, resource => \%RESOURCE, }; + } print "committing files...\n" if $VERBOSE;