#!/usr/bin/perl use IO::Socket; $|++; $host = "server.themanaworld.org"; $msg = eAnet->new(); $msg->connect("$host:6901"); auth("user", "pass"); sub auth { my ($user, $pass) = @_; $msg->writeInt16(0x0064); $msg->writeInt32(0); $msg->writeStr($user, 24); $msg->writeStr($pass, 24); $msg->writeInt8(1); while($msg->getdata()) { $id = $msg->readInt16(); if ($id == 0x63) { my $len = $msg->readInt16() - 4; print "len: $len\n"; $updatehost = $msg->readStr($len); print("update host: $updatehost\n"); } if ($id == 0x69) { $msg->skip(2); $session_ID1 = $msg->readInt32(); $account_ID = $msg->readInt32(); $session_ID2 = $msg->readInt32(); $msg->skip(30); $sex = $msg->readInt8(); $server_address = $msg->readInt32(); $server_port = $msg->readInt16(); $server_name = $msg->readStr(20); $msg->skip(2); print("s1: $session_ID1 s2: $session_ID2 a: $account_ID sex: $sex port: $server_port name: $server_name\n"); $msg->disconnect(); # We're done with login server } } # Char login seq. $msg->connect("$host:$server_port"); $msg->writeInt16(0x0065); $msg->writeInt32($account_ID); $msg->writeInt32($session_ID1); $msg->writeInt32($session_ID2); $msg->writeInt16(1); # TMW_CLIENT_PROTOCOL_VERSION $msg->writeInt8($sex); $msg->skip(4); while ($msg->getdata()) { $id = $msg->readInt16(); if ($id == 0x006b) { # Reply with list of chars # Read list of chars my $numchars = ($msg->getLength() - 22) / 106; $msg->skip(2+20); for (my $i=0; $i < $numchars; $i++) { %char = readPlayerData(); print "$char{name} $char{id}\n"; } # Request char selection $msg->writeInt16(0x066); $msg->writeInt8($char{'slot'}); } if ($id == 0x0071) { # Character selection reply $msg->skip(4); # CharID $map_path = $msg->readStr(16); $msg->readInt32(); # map server IP $server_port = $msg->readInt16(); $msg->disconnect(); # Done with char server } } # Map server login seq $msg->connect("$host:$server_port"); $msg->writeInt16(0x0072); $msg->writeInt32($account_ID); $msg->writeInt32($char{'id'}); $msg->writeInt32($session_ID1); $msg->writeInt32($session_ID2); $msg->writeInt8($sex); $msg->skip(4); while ($msg->getdata()) { #$msg->hexdump(); $id = $msg->readInt16(); printf("ID %x\n", $id); # Login Success if ($id == 0x0073) { $msg->readInt32(); # server tick $msg->skip(3); # see MessageIn::readCoordinates $msg->skip(2); # unknown print("map login success\n"); # We need to send a "PING" packet to get the server talking to us $msg->writeInt16(0x007e); $msg->writeInt32(time()); # And say we've loaded the map $msg->writeInt16(0x007d); } if ($id == 0x008e || $id == 0x009a) { # SMSG_PLAYER_CHAT / SMSG_GM_CHAT my $msglen = $msg->readInt16() - 4; my $chatmsg = $msg->readStr($msglen); print("CHAT: $chatmsg\n"); } $msg->skip(4) if ($id == 0x007f); # Dunno wtf this is.. maybe tick? if ($id == 0x010f) { # SMSG_PLAYER_SKILLS $msg->skip(39); # Assumming only one skill } $msg->skip(6) if ($id == 0x00b0); # SMSG_PLAYER_STAT_UPDATE_1 $msg->skip(6) if ($id == 0x00b1); # SMSG_PLAYER_STAT_UPDATE_2 $msg->skip(12) if ($id == 0x0141); # SMSG_PLAYER_STAT_UPDATE_3 $msg->skip(4) if ($id == 0x00bc); # SMSG_PLAYER_STAT_UPDATE_4 $msg->skip(40) if ($id == 0x00bd); # SMSG_PLAYER_STAT_UPDATE_5 $msg->skip(2) if ($id == 0x00be); # SMSG_PLAYER_STAT_UPDATE_6 if ($id == 0x00a4) { my $len = $msg->readInt16(); my $itemcount = ($len-4) / 20; print("LEN $len - EQU ITM CNT: $itemcount\n"); for (my $i=0; $i<$itemcount; $i++) { $msg->skip(13); } } if ($id == 0x0078 || $id == 0x007b) { # SMSG_BEING_VISIBLE || SMSG_BEING_MOVE print("MOVE!\n"); $msg->skip(16); if ($id == 0x007b) { $msg->skip(4); $msg->skip(5); } else { $msg->skip(3); } $msg->skip(27); } } return(undef); } sub readPlayerData { my %char; $char{'id'} = $msg->readInt32(); $char{'xp'} = $msg->readInt32(); $char{'gp'} = $msg->readInt32(); $char{'jobxp'} = $msg->readInt32(); $char{'joblbl'} = $msg->readInt32(); $char{'shoe_sprite'} = $msg->readInt16(); $char{'glove_sprite'} = $msg->readInt16(); $char{'cape_sprite'} = $msg->readInt16(); $char{'misc1sprite'} = $msg->readInt16(); $msg->readInt32(); # option $msg->readInt32(); # karma $msg->readInt32(); # manner $msg->skip(2); # dunno $char{'hp'} = $msg->readInt16(); $char{'maxhp'} = $msg->readInt16(); $char{'mp'} = $msg->readInt16(); $char{'maxmp'} = $msg->readInt16(); $msg->readInt16(); # Speed $msg->readInt16(); # class $char{'hairstyle'} = $msg->readInt16(); $char{'weapon'} = $msg->readInt16(); $char{'level'} = $msg->readInt16(); $char{'skillpoint'} = $msg->readInt16(); $char{'bottomclothes_sprite'} = $msg->readInt16(); $char{'shield_sprite'} = $msg->readInt16(); $char{'hat_sprite'} = $msg->readInt16(); $char{'topclothes_sprite'} = $msg->readInt16(); $char{'haircolor'} = $msg->readInt16(); $char{'misc2_sprite'} = $msg->readInt16(); $char{'name'} = $msg->readStr(24); $char{'attr0'} = $msg->readInt8(); $char{'attr1'} = $msg->readInt8(); $char{'attr2'} = $msg->readInt8(); $char{'attr3'} = $msg->readInt8(); $char{'attr4'} = $msg->readInt8(); $char{'attr5'} = $msg->readInt8(); $char{'slot'} = $msg->readInt8(); $msg->readInt8(); # dunno return %char; } package eAnet; use IO::Socket; my $buf; my $sock; sub new { my $self = {}; bless($self); return($self); } sub DESTROY { close($sock) if ($sock); undef($buf); } sub connect { my ($self, $host) = @_; $sock = IO::Socket::INET->new(PeerAddr => $host) or die; $sock->autoflush(1); } sub disconnect { close($sock); undef($sock); } sub getdata { return length($buf) if (length($buf) > 0); return sysread($sock, $buf, 8192); } sub getLength { return(length($buf)); } sub readInt32 { my $self = shift; my $ret = unpack("L", $buf); skip($self, 4); return($ret); } sub writeInt32 { my ($self, $data) = @_; $data = pack("L", $data); return mwrite($self, $data); } sub readInt16 { my $self = shift; my $ret = unpack("S", $buf); skip($self, 2); return($ret); } sub writeInt16 { my ($self, $data) = @_; $data = pack("S", $data); return mwrite($sock, $data); } sub readInt8 { my $self = shift; my $ret = unpack("C", $buf); skip($self, 1); return($ret); } sub writeInt8 { my ($self, $data) = @_; $data = pack("C", $data); return mwrite($sock, $data); } sub readStr { my ($self, $len) = @_; my $str = substr($buf, 0, $len); skip($self, $len); return($str); } sub writeStr { my ($self, $data, $len) = @_; $data = pack("a$len", $data); return mwrite($sock, $data); } sub skip { my ($self, $amount) = @_; $buf = substr($buf, $amount); } sub mwrite { my ($self, $data) = @_; return syswrite($sock, $data); } sub hexdump { my $self = shift; my $data = shift; $data = $buf unless $data; my @data = ( $data =~ /.{1,16}/gs ); my $num; foreach (@data) { my ($lhs, $rhs) = ("", ""); my $i; if (($num & 0xFF) == 0) { print("\n\t/0 /1 /2 /3 /4 /5 /6 /7 /8 /9 /A /B /C /D /E /F 0123456789ABCDEF\n"); } printf("%04X : ", $num); foreach $i ($_ =~ m/./gs) { $lhs .= sprintf(" %02X",ord($i)); if ($i =~ m/[ -~]/) { $rhs .= $i; } else { $rhs .= "."; } $num++; } printf("%-50s %s\n",$lhs,$rhs); } }