File Coverage

blib/lib/Game/Tibia/Packet/Charlist.pm
Criterion Covered Total %
statement 69 71 97.1
branch 12 26 46.1
condition 10 21 47.6
subroutine 12 12 100.0
pod 2 2 100.0
total 105 132 79.5


line stmt bran cond sub pod time code
1 2     2   126796 use strict;
  2         19  
  2         48  
2 2     2   7 use warnings;
  2         3  
  2         47  
3 2     2   22 use v5.16.0;
  2         4  
4             package Game::Tibia::Packet::Charlist;
5              
6             # ABSTRACT: Character list packet support for the MMORPG Tibia
7             our $VERSION = '0.007'; # VERSION
8              
9 2     2   10 use Carp;
  2         4  
  2         104  
10 2     2   1077 use Game::Tibia::Packet;
  2         4  
  2         6  
11              
12             =pod
13              
14             =encoding utf8
15              
16             =head1 NAME
17              
18             Game::Tibia::Packet::Charlist - Character list packet support for the MMORPG Tibia
19              
20              
21             =head1 SYNOPSIS
22              
23             use Game::Tibia::Packet::Charlist;
24              
25             my $p = Game::Tibia::Packet::Charlist->new(
26             packet => $packet,
27             xtea => $xtea,
28             version => 830
29             );
30              
31             $p->{premium_days} = 0xff;
32             $sock->send($p->finalize);
33              
34              
35             =head1 DESCRIPTION
36              
37             Decodes Tibia Login packets into hashes and vice versa.
38              
39             =cut
40              
41             our %params;
42             sub import {
43 2     2   16 (undef, %params) = (shift, %params, @_);
44 2 50 66     1309 die "Malformed Tibia version\n" if exists $params{tibia} && $params{tibia} !~ /^\d+$/;
45             }
46              
47 2     2   12 use constant DLG_MOTD => 0x14;
  2         2  
  2         88  
48 2     2   8 use constant DLG_INFO => 0x15;
  2         5  
  2         87  
49 2     2   9 use constant DLG_ERROR => 0x0a;
  2         3  
  2         67  
50 2     2   8 use constant DLG_CHARLIST => 0x64;
  2         3  
  2         1164  
51              
52             =head1 METHODS AND ARGUMENTS
53              
54             =over 4
55              
56             =item new([packet => $packet, version => $version, xtea => $xtea])
57              
58             Constructs a new Game::Tibia::Packet::Charlist instance of version C<$version>. When C and C are specified, the supplied packet is decrypted and is then retrievable with the C subroutine.
59              
60             =cut
61              
62             sub new {
63 4     4 1 668 my $class = shift;
64              
65 4         20 my $self = {
66             packet => undef,
67             xtea => undef,
68              
69             @_
70             };
71              
72 4   66     17 $self->{version} //= $params{tibia};
73 4 50 33     33 croak " 761 <= protocol version < 980 isn't satisfied" if !defined $self->{version} || ! (761 <= $self->{version} && $self->{version} < 980);
      33        
74 4 50 66     19 croak "Packet was specified without XTEA key" if defined $self->{packet} && !defined $self->{xtea};
75 4 50       29 $self->{versions}{client} = Game::Tibia::Packet::version $self->{version} unless ref $self->{version};
76              
77 4 100       19 if (defined $self->{packet}) {
78             my $packet = Game::Tibia::Packet->new(
79             packet => $self->{packet},
80             xtea => $self->{xtea},
81             version => $self->{version},
82 3         17 );
83              
84 3         9 my $payload = $packet->payload;
85 3         17 (my $type, $payload) = unpack 'Ca*', $payload;
86 3 50       12 if ($type eq DLG_MOTD) {
    0          
    0          
87 3         34 ($self->{motd}, $payload) = unpack '(S/a)< a*', $payload;
88             } elsif ($type eq DLG_INFO) {
89 0         0 ($self->{info}, $payload) = unpack '(S/a)< a*', $payload;
90             } elsif ($type eq DLG_ERROR) {
91 0         0 ($self->{error}, $payload) = unpack '(S/a)< a*', $payload;
92             }
93 3         12 ($type, $payload) = unpack 'Ca*', $payload;
94 3 50       18 if ($type eq DLG_CHARLIST) {
95 3         10 (my $count, $payload) = unpack 'Ca*', $payload;
96 3         7 $self->{characters} = undef;
97 3         5 my @chars;
98 3         18 while ($count--) {
99 6         9 my $char;
100 6         37 ($char->{name}, $char->{world}{name}, $char->{world}{ip}, $char->{world}{port}, $payload)
101             = unpack '(S/a S/a a4 S)< a*', $payload;
102 6         27 $char->{world}{ip} = join '.', unpack('C4', $char->{world}{ip});
103 6         22 push @chars, $char;
104             }
105 3         8 $self->{characters} = \@chars;
106             }
107 3         32 $self->{premium_days} = unpack 'S<', $payload;
108             }
109              
110 4         11 bless $self, $class;
111 4         14 return $self;
112             }
113              
114             =item finalize([$xtea]])
115              
116             Finalizes the packet. encrypts with XTEA and prepends header
117              
118             =cut
119              
120              
121             sub finalize {
122 2     2 1 8 my $self = shift;
123 2   33     16 my $xtea = shift // $self->{xtea};
124              
125 2         6 my $packet = Game::Tibia::Packet->new(version => $self->{version});
126 2 50       7 $packet->payload .= pack '(C S/a)<', DLG_MOTD, $self->{motd} if defined $self->{motd};
127 2 50       7 $packet->payload .= pack '(C S/a)<', DLG_INFO, $self->{info} if defined $self->{info};
128 2 50       4 $packet->payload .= pack '(C S/a)<', DLG_ERROR, $self->{error} if defined $self->{error};
129 2 50 33     5 if (defined $self->{characters} && @{$self->{characters}} > 0) {
  2         6  
130 2         5 $packet->payload .= pack 'C C', DLG_CHARLIST, scalar @{$self->{characters}};
  2         4  
131 2         3 foreach my $char (@{$self->{characters}}) {
  2         5  
132             $packet->payload .= pack '(S/a S/a a4 S)<',
133             $char->{name}, $char->{world}{name},
134 4         8 pack("C4", split('\.', $char->{world}{ip})), $char->{world}{port};
135             }
136             }
137 2         5 $packet->payload .= pack('S<', $self->{premium_days}); # pacc days
138 2         5 return $packet->finalize($xtea);
139             }
140              
141              
142             1;
143             __END__