Advertisement
Guest User

Untitled

a guest
Jul 30th, 2016
341
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 37.26 KB | None | 0 0
  1. #########################################################################
  2. # OpenKore - Message sending
  3. # This module contains functions for sending messages to the RO server.
  4. #
  5. # This software is open source, licensed under the GNU General Public
  6. # License, version 2.
  7. # Basically, this means that you're allowed to modify and distribute
  8. # this software. However, if you distribute modified versions, you MUST
  9. # also distribute the source code.
  10. # See http://www.gnu.org/licenses/gpl.html for the full license.
  11. #
  12. # $Revision$
  13. # $Id$
  14. #
  15. #########################################################################
  16. ##
  17. # MODULE DESCRIPTION: Sending messages to RO server
  18. #
  19. # This class contains convenience methods for sending messages to the RO
  20. # server.
  21. #
  22. # Please also read <a href="http://wiki.openkore.com/index.php/Network_subsystem">the
  23. # network subsystem overview.</a>
  24. package Network::Send;
  25.  
  26. use strict;
  27. use Network::PacketParser; # import
  28. use base qw(Network::PacketParser);
  29. use utf8;
  30. use Carp::Assert;
  31. use Digest::MD5;
  32. use Math::BigInt;
  33.  
  34. use Globals qw(%config $encryptVal $bytesSent $conState %packetDescriptions $enc_val1 $enc_val2 $char $masterServer $syncSync $accountID %timeout %talk);
  35. use I18N qw(bytesToString stringToBytes);
  36. use Utils qw(existsInList getHex getTickCount getCoordString makeCoordsDir);
  37. use Misc;
  38. use Log qw(debug);
  39.  
  40. sub import {
  41. # This code is for backward compatibility reasons, so that you can still
  42. # write:
  43. # sendFoo(\$remote_socket, args);
  44.  
  45. my ($package) = caller;
  46. # This is necessary for some weird reason.
  47. return if ($package =~ /^Network::Send/);
  48.  
  49. package Network::Send::Compatibility;
  50. require Exporter;
  51. our @ISA = qw(Exporter);
  52. require Network::Send::ServerType0;
  53. no strict 'refs';
  54.  
  55. our @EXPORT_OK;
  56. @EXPORT_OK = ();
  57.  
  58. my $class = shift;
  59. if (@_) {
  60. @EXPORT_OK = @_;
  61. } else {
  62. @EXPORT_OK = grep {/^send/} keys(%{Network::Send::ServerType0::});
  63. }
  64.  
  65. foreach my $symbol (@EXPORT_OK) {
  66. *{$symbol} = sub {
  67. my $remote_socket = shift;
  68. my $func = $Globals::messageSender->can($symbol);
  69. if (!$func) {
  70. die "No such function: $symbol";
  71. } else {
  72. return $func->($Globals::messageSender, @_);
  73. }
  74. };
  75. }
  76. Network::Send::Compatibility->export_to_level(1, undef, @EXPORT_OK);
  77. }
  78.  
  79. ### CATEGORY: Class methods
  80.  
  81. ##
  82. # void Network::Send::encrypt(r_msg, themsg)
  83. #
  84. # This is an old method used back in the iRO beta 2 days when iRO had encrypted packets.
  85. # At the moment (December 20 2006) there are no servers that still use encrypted packets.
  86. sub encrypt {
  87. use bytes;
  88. my $r_msg = shift;
  89. my $themsg = shift;
  90. my @mask;
  91. my $newmsg;
  92. my ($in, $out);
  93. my $temp;
  94. my $i;
  95.  
  96. if ($config{encrypt} == 1 && $conState >= 5) {
  97. $out = 0;
  98. for ($i = 0; $i < 13;$i++) {
  99. $mask[$i] = 0;
  100. }
  101. {
  102. use integer;
  103. $temp = ($encryptVal * $encryptVal * 1391);
  104. }
  105. $temp = ~(~($temp));
  106. $temp = $temp % 13;
  107. $mask[$temp] = 1;
  108. {
  109. use integer;
  110. $temp = $encryptVal * 1397;
  111. }
  112. $temp = ~(~($temp));
  113. $temp = $temp % 13;
  114. $mask[$temp] = 1;
  115. for($in = 0; $in < length($themsg); $in++) {
  116. if ($mask[$out % 13]) {
  117. $newmsg .= pack("C1", int(rand() * 255) & 0xFF);
  118. $out++;
  119. }
  120. $newmsg .= substr($themsg, $in, 1);
  121. $out++;
  122. }
  123. $out += 4;
  124. $newmsg = pack("v2", $out, $encryptVal) . $newmsg;
  125. while ((length($newmsg) - 4) % 8 != 0) {
  126. $newmsg .= pack("C1", (rand() * 255) & 0xFF);
  127. }
  128. } elsif ($config{encrypt} >= 2 && $conState >= 5) {
  129. $out = 0;
  130. for ($i = 0; $i < 17;$i++) {
  131. $mask[$i] = 0;
  132. }
  133. {
  134. use integer;
  135. $temp = ($encryptVal * $encryptVal * 34953);
  136. }
  137. $temp = ~(~($temp));
  138. $temp = $temp % 17;
  139. $mask[$temp] = 1;
  140. {
  141. use integer;
  142. $temp = $encryptVal * 2341;
  143. }
  144. $temp = ~(~($temp));
  145. $temp = $temp % 17;
  146. $mask[$temp] = 1;
  147. for($in = 0; $in < length($themsg); $in++) {
  148. if ($mask[$out % 17]) {
  149. $newmsg .= pack("C1", int(rand() * 255) & 0xFF);
  150. $out++;
  151. }
  152. $newmsg .= substr($themsg, $in, 1);
  153. $out++;
  154. }
  155. $out += 4;
  156. $newmsg = pack("v2", $out, $encryptVal) . $newmsg;
  157. while ((length($newmsg) - 4) % 8 != 0) {
  158. $newmsg .= pack("C1", (rand() * 255) & 0xFF);
  159. }
  160. } else {
  161. $newmsg = $themsg;
  162. }
  163.  
  164. $$r_msg = $newmsg;
  165. }
  166.  
  167. ### CATEGORY: Methods
  168.  
  169. ##
  170. # void $messageSender->encryptMessageID(r_message)
  171. sub encryptMessageID {
  172. my ($self, $r_message) = @_;
  173. my $messageID = unpack("v", $$r_message);
  174.  
  175. if ($self->{encryption}->{crypt_key_3}) {
  176. if (sprintf("%04X",$messageID) eq $self->{packet_lut}{map_login}) {
  177. $self->{encryption}->{crypt_key} = $self->{encryption}->{crypt_key_1};
  178. } elsif ($self->{net}->getState() != Network::IN_GAME) {
  179. # Turn off keys
  180. $self->{encryption}->{crypt_key} = 0; return;
  181. }
  182.  
  183. # Checking if Encryption is Activated
  184. if ($self->{encryption}->{crypt_key} > 0) {
  185. # Saving Last Informations for Debug Log
  186. my $oldMID = $messageID;
  187. my $oldKey = ($self->{encryption}->{crypt_key} >> 16) & 0x7FFF;
  188.  
  189. # Calculating the Encryption Key
  190. $self->{encryption}->{crypt_key} = ($self->{encryption}->{crypt_key} * $self->{encryption}->{crypt_key_3} + $self->{encryption}->{crypt_key_2}) & 0xFFFFFFFF;
  191.  
  192. # Xoring the Message ID
  193. $messageID = ($messageID ^ (($self->{encryption}->{crypt_key} >> 16) & 0x7FFF)) & 0xFFFF;
  194. $$r_message = pack("v", $messageID) . substr($$r_message, 2);
  195.  
  196. # Debug Log
  197. debug (sprintf("Encrypted MID : [%04X]->[%04X] / KEY : [0x%04X]->[0x%04X]\n", $oldMID, $messageID, $oldKey, ($self->{encryption}->{crypt_key} >> 16) & 0x7FFF), "sendPacket", 0) if $config{debugPacket_sent};
  198. }
  199. } else {
  200. use bytes;
  201. if ($self->{net}->getState() != Network::IN_GAME) {
  202. $enc_val1 = 0;
  203. $enc_val2 = 0;
  204. return;
  205. }
  206.  
  207. my $messageID = unpack("v", $$r_message);
  208. if ($enc_val1 != 0 && $enc_val2 != 0) {
  209. # Prepare encryption
  210. $enc_val1 = ((0x000343FD * $enc_val1) + $enc_val2)& 0xFFFFFFFF;
  211. debug (sprintf("enc_val1 = %x", $enc_val1) . "\n", "sendPacket", 2);
  212. # Encrypt message ID
  213. $messageID = ($messageID ^ (($enc_val1 >> 16) & 0x7FFF)) & 0xFFFF;
  214. $$r_message = pack("v", $messageID) . substr($$r_message, 2);
  215. }
  216. }
  217. }
  218.  
  219. sub cryptKeys {
  220. my $self = shift;
  221. $self->{encryption} = {
  222. 'crypt_key_1' => Math::BigInt->new(shift),
  223. 'crypt_key_2' => Math::BigInt->new(shift),
  224. 'crypt_key_3' => Math::BigInt->new(shift),
  225. };
  226. }
  227.  
  228. ##
  229. # void $messageSender->injectMessage(String message)
  230. #
  231. # Send text message to the connected client's party chat.
  232. sub injectMessage {
  233. my ($self, $message) = @_;
  234. my $name = stringToBytes("|");
  235. my $msg .= $name . stringToBytes(" : $message") . chr(0);
  236. # encrypt(\$msg, $msg);
  237.  
  238. # Packet Prefix Encryption Support
  239. #$self->encryptMessageID(\$msg);
  240.  
  241. $msg = pack("C*", 0x09, 0x01) . pack("v*", length($name) + length($message) + 12) . pack("C*",0,0,0,0) . $msg;
  242. ## encrypt(\$msg, $msg);
  243. $self->{net}->clientSend($msg);
  244. }
  245.  
  246. ##
  247. # void $messageSender->injectAdminMessage(String message)
  248. #
  249. # Send text message to the connected client's system chat.
  250. sub injectAdminMessage {
  251. my ($self, $message) = @_;
  252. $message = stringToBytes($message);
  253. $message = pack("C*",0x9A, 0x00) . pack("v*", length($message)+5) . $message .chr(0);
  254. # encrypt(\$message, $message);
  255.  
  256. # Packet Prefix Encryption Support
  257. #$self->encryptMessageID(\$message);
  258. $self->{net}->clientSend($message);
  259. }
  260.  
  261. ##
  262. # String pinEncode(int seed, int pin)
  263. # pin: the PIN code
  264. # key: the encryption seed/key
  265. #
  266. # Another version of the PIN Encode Function, used to hide the real PIN code, using seed/key.
  267. sub pinEncode {
  268. # randomizePin function/algorithm by Kurama, ever_boy_, kLabMouse and Iniro. cleanups by Revok
  269. my ($seed, $pin) = @_;
  270.  
  271. $seed = Math::BigInt->new($seed);
  272. my $mulfactor = 0x3498;
  273. my $addfactor = 0x881234;
  274. my @keypad_keys_order = ('0'..'9');
  275.  
  276. # calculate keys order (they are randomized based on seed value)
  277. if (@keypad_keys_order >= 1) {
  278. my $k = 2;
  279. for (my $pos = 1; $pos < @keypad_keys_order; $pos++) {
  280. $seed = $addfactor + $seed * $mulfactor & 0xFFFFFFFF; # calculate next seed value
  281. my $replace_pos = $seed % $k;
  282. if ($pos != $replace_pos) {
  283. my $old_value = $keypad_keys_order[$pos];
  284. $keypad_keys_order[$pos] = $keypad_keys_order[$replace_pos];
  285. $keypad_keys_order[$replace_pos] = $old_value;
  286. }
  287. $k++;
  288. }
  289. }
  290. # associate keys values with their position using a hash
  291. my %keypad;
  292. for (my $pos = 0; $pos < @keypad_keys_order; $pos++) { $keypad{@keypad_keys_order[$pos]} = $pos; }
  293. my $pin_reply = '';
  294. my @pin_numbers = split('',$pin);
  295. foreach (@pin_numbers) { $pin_reply .= $keypad{$_}; }
  296. return $pin_reply;
  297. }
  298.  
  299. ##
  300. # void $messageSender->sendToServer(Bytes msg)
  301. #
  302. # Send a raw data to the server.
  303. sub sendToServer {
  304. my ($self, $msg) = @_;
  305. my $net = $self->{net};
  306.  
  307. shouldnt(length($msg), 0);
  308. return unless ($net->serverAlive);
  309.  
  310. my $messageID = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1)));
  311.  
  312. my $hookName = "packet_send/$messageID";
  313. if (Plugins::hasHook($hookName)) {
  314. my %args = (
  315. switch => $messageID,
  316. data => $msg
  317. );
  318. Plugins::callHook($hookName, \%args);
  319. return if ($args{return});
  320. }
  321.  
  322. # encrypt(\$msg, $msg);
  323.  
  324. # Packet Prefix Encryption Support
  325. $self->encryptMessageID(\$msg);
  326.  
  327. $net->serverSend($msg);
  328. $bytesSent += length($msg);
  329.  
  330. if ($config{debugPacket_sent} && !existsInList($config{debugPacket_exclude}, $messageID) && $config{debugPacket_include_dumpMethod} < 3) {
  331. my $label = $packetDescriptions{Send}{$messageID} ?
  332. "[$packetDescriptions{Send}{$messageID}]" : '';
  333. if ($config{debugPacket_sent} == 1) {
  334. debug(sprintf("Sent packet : %-4s [%2d bytes] %s\n", $messageID, length($msg), $label), "sendPacket", 0);
  335. } else {
  336. Misc::visualDump($msg, ">> Sent packet: $messageID $label");
  337. }
  338. }
  339.  
  340. if ($config{'debugPacket_include_dumpMethod'} && !existsInList($config{debugPacket_exclude}, $messageID) && existsInList($config{'debugPacket_include'}, $messageID)) {
  341. my $label = $packetDescriptions{Send}{$messageID} ?
  342. "[$packetDescriptions{Send}{$messageID}]" : '';
  343. if ($config{debugPacket_include_dumpMethod} == 3 && existsInList($config{'debugPacket_include'}, $messageID)) {
  344. #Security concern: Dump only when you included the header in config
  345. Misc::dumpData($msg, 1, 1);
  346. } elsif ($config{debugPacket_include_dumpMethod} == 4) {
  347. open my $dump, '>>', 'DUMP_LINE.txt';
  348. print $dump unpack('H*', $msg) . "\n";
  349. } elsif ($config{debugPacket_include_dumpMethod} == 5 && existsInList($config{'debugPacket_include'}, $messageID)) {
  350. #Security concern: Dump only when you included the header in config
  351. open my $dump, '>>', 'DUMP_HEAD.txt';
  352. print $dump sprintf("%-4s %2d %s%s\n", $messageID, length($msg), 'Send', $label);
  353. }
  354. }
  355. }
  356.  
  357. ##
  358. # void $messageSender->sendRaw(String raw)
  359. # raw: space-delimited list of hex byte values
  360. #
  361. # Send a raw data to the server.
  362. sub sendRaw {
  363. my ($self, $raw) = @_;
  364. my $msg;
  365. my @raw = split / /, $raw;
  366. foreach (@raw) {
  367. $msg .= pack("C", hex($_));
  368. }
  369. $self->sendToServer($msg);
  370. debug "Sent Raw Packet: @raw\n", "sendPacket", 2;
  371. }
  372.  
  373. # parse/reconstruct callbacks and send* subs left for compatibility
  374.  
  375. sub parse_master_login {
  376. my ($self, $args) = @_;
  377.  
  378. if (exists $args->{password_md5_hex}) {
  379. $args->{password_md5} = pack 'H*', $args->{password_md5_hex};
  380. }
  381.  
  382. if (exists $args->{password_rijndael}) {
  383. my $key = pack('C24', (6, 169, 33, 64, 54, 184, 161, 91, 81, 46, 3, 213, 52, 18, 0, 6, 61, 175, 186, 66, 157, 158, 180, 48));
  384. my $chain = pack('C24', (61, 175, 186, 66, 157, 158, 180, 48, 180, 34, 218, 128, 44, 159, 172, 65, 1, 2, 4, 8, 16, 32, 128));
  385. my $in = pack('a24', $args->{password_rijndael});
  386. my $rijndael = Utils::Rijndael->new;
  387. $rijndael->MakeKey($key, $chain, 24, 24);
  388. $args->{password} = unpack("Z24", $rijndael->Decrypt($in, undef, 24, 0));
  389. }
  390. }
  391.  
  392. sub reconstruct_master_login {
  393. my ($self, $args) = @_;
  394.  
  395. $args->{ip} = '192.168.0.2' unless exists $args->{ip}; # gibberish
  396. $args->{mac} = '111111111111' unless exists $args->{mac}; # gibberish
  397. $args->{mac_hyphen_separated} = join '-', $args->{mac} =~ /(..)/g;
  398. $args->{isGravityID} = 0 unless exists $args->{isGravityID};
  399.  
  400. if (exists $args->{password}) {
  401. for (Digest::MD5->new) {
  402. $_->add($args->{password});
  403. $args->{password_md5} = $_->clone->digest;
  404. $args->{password_md5_hex} = $_->hexdigest;
  405. }
  406.  
  407. my $key = pack('C24', (6, 169, 33, 64, 54, 184, 161, 91, 81, 46, 3, 213, 52, 18, 0, 6, 61, 175, 186, 66, 157, 158, 180, 48));
  408. my $chain = pack('C24', (61, 175, 186, 66, 157, 158, 180, 48, 180, 34, 218, 128, 44, 159, 172, 65, 1, 2, 4, 8, 16, 32, 128));
  409. my $in = pack('a24', $args->{password});
  410. my $rijndael = Utils::Rijndael->new;
  411. $rijndael->MakeKey($key, $chain, 24, 24);
  412. $args->{password_rijndael} = $rijndael->Encrypt($in, undef, 24, 0);
  413. }
  414. }
  415.  
  416. sub sendMasterLogin {
  417. my ($self, $username, $password, $master_version, $version) = @_;
  418. my $msg;
  419.  
  420. if (
  421. $masterServer->{masterLogin_packet} eq ''
  422. # TODO a way to select any packet, handled globally, something like "packet_<handler> <switch>"?
  423. or $self->{packet_list}{$masterServer->{masterLogin_packet}}
  424. && $self->{packet_list}{$masterServer->{masterLogin_packet}}[0] eq 'master_login'
  425. && ($self->{packet_lut}{master_login} = $masterServer->{masterLogin_packet})
  426. ) {
  427. $self->sendClientMD5Hash() unless $masterServer->{clientHash} eq ''; # this is a hack, just for testing purposes, it should be moved to the login algo later on
  428.  
  429. $msg = $self->reconstruct({
  430. switch => 'master_login',
  431. version => $version || $self->version,
  432. master_version => $master_version,
  433. username => $username,
  434. password => $password,
  435. });
  436. } else {
  437. $msg = pack("v1 V", hex($masterServer->{masterLogin_packet}) || 0x64, $version || $self->version) .
  438. pack("a24", $username) .
  439. pack("a24", $password) .
  440. pack("C*", $master_version);
  441. }
  442.  
  443. $self->sendToServer($msg);
  444. debug "Sent sendMasterLogin\n", "sendPacket", 2;
  445. }
  446.  
  447. sub secureLoginHash {
  448. my ($self, $password, $salt, $type) = @_;
  449. my $md5 = Digest::MD5->new;
  450.  
  451. $password = stringToBytes($password);
  452. if ($type % 2) {
  453. $salt = $salt . $password;
  454. } else {
  455. $salt = $password . $salt;
  456. }
  457. $md5->add($salt);
  458.  
  459. $md5->digest
  460. }
  461.  
  462. sub sendMasterSecureLogin {
  463. my ($self, $username, $password, $master_version, $version) = @_;
  464. my $msg;
  465.  
  466. $msg = pack("C*",<font color="Red"> 0x76, 0x0a, 0x18, 0x00, 0x00, 0x00, 0x74, 0x68, 0x65, 0x73, 0x61, 0x69, 0x6e, 0x74, 0x30, 0x30, 0x31, 0x00, 0x17, 0x01, 0x00, 0x00, 0x0b, 0x01, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00, 0x00, 0x45, 0xe4, 0x73, 0x3c, 0xaf, 0x26, 0x7c, 0xd5, 0x68, 0xe6, 0xcc, 0xa9, 0x4d, 0xbc, 0xb4, 0x90, 0x47, 0x16, 0xa0, 0x35, 0x54, 0x10, 0x8c, 0x13, 0xaf, 0xdb, 0x26, 0xcb, 0xe7, 0x96, 0x6d, 0xd9, 0x07, 0x00 </font>);
  467.  
  468. $self->sendToServer($msg);
  469.  
  470. debug "Sent sendMasterLogin\n", "sendPacket", 2;
  471. }
  472.  
  473. sub reconstruct_game_login {
  474. my ($self, $args) = @_;
  475. $args->{userLevel} = 0 unless exists $args->{userLevel};
  476. ($args->{iAccountSID}) = $masterServer->{ip} =~ /\d+\.\d+\.\d+\.(\d+)/ unless exists $args->{iAccountSID};
  477. }
  478.  
  479. # TODO: $masterServer->{gameLogin_packet}?
  480. sub sendGameLogin {
  481. my ($self, $accountID, $sessionID, $sessionID2, $sex) = @_;
  482. $self->sendToServer($self->reconstruct({
  483. switch => 'game_login',
  484. accountID => $accountID,
  485. sessionID => $sessionID,
  486. sessionID2 => $sessionID2,
  487. accountSex => $sex,
  488. }));
  489. debug "Sent sendGameLogin\n", "sendPacket", 2;
  490. }
  491.  
  492. sub sendCharLogin {
  493. my ($self, $char) = @_;
  494. $self->sendToServer($self->reconstruct({switch => 'char_login', slot => $char}));
  495. debug "Sent sendCharLogin\n", "sendPacket", 2;
  496. }
  497.  
  498. sub sendMapLogin {
  499. my ($self, $accountID, $charID, $sessionID, $sex) = @_;
  500. my $msg;
  501. $sex = 0 if ($sex > 1 || $sex < 0); # Sex can only be 0 (female) or 1 (male)
  502.  
  503. if ($self->{serverType} == 0 || $self->{serverType} == 21 || $self->{serverType} == 22) {
  504. $msg = $self->reconstruct({
  505. switch => 'map_login',
  506. accountID => $accountID,
  507. charID => $charID,
  508. sessionID => $sessionID,
  509. tick => getTickCount,
  510. sex => $sex,
  511. });
  512.  
  513. } else { #oRO and pRO
  514. my $key;
  515.  
  516. $key = pack("C*", 0xFA, 0x12, 0, 0x50, 0x83);
  517. $msg = pack("C*", 0x72, 0, 0, 0, 0) .
  518. $accountID .
  519. $key .
  520. $charID .
  521. pack("C*", 0xFF, 0xFF) .
  522. $sessionID .
  523. pack("V", getTickCount()) .
  524. pack("C", $sex);
  525. }
  526.  
  527. $self->sendToServer($msg);
  528. debug "Sent sendMapLogin\n", "sendPacket", 2;
  529. }
  530.  
  531. sub sendMapLoaded {
  532. my $self = shift;
  533. $syncSync = pack("V", getTickCount());
  534. debug "Sending Map Loaded\n", "sendPacket";
  535. $self->sendToServer($self->reconstruct({switch => 'map_loaded'}));
  536. Plugins::callHook('packet/sendMapLoaded');
  537. }
  538.  
  539. sub reconstruct_sync {
  540. my ($self, $args) = @_;
  541. $args->{time} = getTickCount;
  542. }
  543.  
  544. sub sendSync {
  545. my ($self, $initialSync) = @_;
  546. # XKore mode 1 lets the client take care of syncing.
  547. return if ($self->{net}->version == 1);
  548.  
  549. $self->sendToServer($self->reconstruct({switch => 'sync'}));
  550. debug "Sent Sync\n", "sendPacket", 2;
  551. }
  552.  
  553. sub parse_character_move {
  554. my ($self, $args) = @_;
  555. makeCoordsDir($args, $args->{coords});
  556. }
  557.  
  558. sub reconstruct_character_move {
  559. my ($self, $args) = @_;
  560. $args->{coords} = getCoordString(@{$args}{qw(x y)}, $masterServer->{serverType} == 0);
  561. }
  562.  
  563. sub sendMove {
  564. my ($self, $x, $y) = @_;
  565. $self->sendToServer($self->reconstruct({switch => 'character_move', x => $x, y => $y}));
  566. debug "Sent move to: $x, $y\n", "sendPacket", 2;
  567. }
  568.  
  569. sub sendAction { # flag: 0 attack (once), 7 attack (continuous), 2 sit, 3 stand
  570. my ($self, $monID, $flag) = @_;
  571.  
  572. my %args;
  573. $args{monID} = $monID;
  574. $args{flag} = $flag;
  575. # eventually we'll trow this hooking out so...
  576. Plugins::callHook('packet_pre/sendAttack', \%args) if $flag == ACTION_ATTACK || $flag == ACTION_ATTACK_REPEAT;
  577. Plugins::callHook('packet_pre/sendSit', \%args) if $flag == ACTION_SIT || $flag == ACTION_STAND;
  578. if ($args{return}) {
  579. $self->sendToServer($args{msg});
  580. return;
  581. }
  582.  
  583. $self->sendToServer($self->reconstruct({switch => 'actor_action', targetID => $monID, type => $flag}));
  584. debug "Sent Action: " .$flag. " on: " .getHex($monID)."\n", "sendPacket", 2;
  585. }
  586.  
  587. sub parse_public_chat {
  588. my ($self, $args) = @_;
  589. $self->parseChat($args);
  590. }
  591.  
  592. sub reconstruct_public_chat {
  593. my ($self, $args) = @_;
  594. $self->reconstructChat($args);
  595. }
  596.  
  597. sub sendChat {
  598. my ($self, $message) = @_;
  599. $self->sendToServer($self->reconstruct({switch => 'public_chat', message => $message}));
  600. }
  601.  
  602. sub sendGetPlayerInfo {
  603. my ($self, $ID) = @_;
  604. $self->sendToServer($self->reconstruct({switch => 'actor_info_request', ID => $ID}));
  605. debug "Sent get player info: ID - ".getHex($ID)."\n", "sendPacket", 2;
  606. }
  607.  
  608. sub sendGetCharacterName {
  609. my ($self, $ID) = @_;
  610. $self->sendToServer($self->reconstruct({switch => 'actor_name_request', ID => $ID}));
  611. debug "Sent get character name: ID - ".getHex($ID)."\n", "sendPacket", 2;
  612. }
  613.  
  614. sub sendTalk {
  615. my ($self, $ID) = @_;
  616. $talk{msg} = $talk{image} = '';
  617. $self->sendToServer($self->reconstruct({switch => 'npc_talk', ID => $ID, type => 1}));
  618. debug "Sent talk: ".getHex($ID)."\n", "sendPacket", 2;
  619. }
  620.  
  621. sub sendTalkCancel {
  622. my ($self, $ID) = @_;
  623. $talk{msg} = $talk{image} = '';
  624. $self->sendToServer($self->reconstruct({switch => 'npc_talk_cancel', ID => $ID}));
  625. debug "Sent talk cancel: ".getHex($ID)."\n", "sendPacket", 2;
  626. }
  627.  
  628. sub sendTalkContinue {
  629. my ($self, $ID) = @_;
  630. $self->sendToServer($self->reconstruct({switch => 'npc_talk_continue', ID => $ID}));
  631. debug "Sent talk continue: ".getHex($ID)."\n", "sendPacket", 2;
  632. }
  633.  
  634. sub sendTalkResponse {
  635. my ($self, $ID, $response) = @_;
  636. $talk{msg} = $talk{image} = '';
  637. $self->sendToServer($self->reconstruct({switch => 'npc_talk_response', ID => $ID, response => $response}));
  638. debug "Sent talk respond: ".getHex($ID).", $response\n", "sendPacket", 2;
  639. }
  640.  
  641. sub sendTalkNumber {
  642. my ($self, $ID, $number) = @_;
  643. $talk{msg} = $talk{image} = '';
  644. $self->sendToServer($self->reconstruct({switch => 'npc_talk_number', ID => $ID, value => $number}));
  645. debug "Sent talk number: ".getHex($ID).", $number\n", "sendPacket", 2;
  646. }
  647.  
  648. sub sendTalkText {
  649. my ($self, $ID, $input) = @_;
  650. $talk{msg} = $talk{image} = '';
  651. $input = stringToBytes($input);
  652. $self->sendToServer($self->reconstruct({
  653. switch => 'npc_talk_text',
  654. len => length($input)+length($ID)+5,
  655. ID => $ID,
  656. text => $input
  657. }));
  658. debug "Sent talk text: ".getHex($ID).", $input\n", "sendPacket", 2;
  659. }
  660.  
  661. sub parse_private_message {
  662. my ($self, $args) = @_;
  663. $args->{privMsg} = bytesToString($args->{privMsg});
  664. Misc::stripLanguageCode(\$args->{privMsg});
  665. $args->{privMsgUser} = bytesToString($args->{privMsgUser});
  666. }
  667.  
  668. sub reconstruct_private_message {
  669. my ($self, $args) = @_;
  670. $args->{privMsg} = '|00' . $args->{privMsg} if $masterServer->{chatLangCode};
  671. $args->{privMsg} = stringToBytes($args->{privMsg});
  672. $args->{privMsgUser} = stringToBytes($args->{privMsgUser});
  673. }
  674.  
  675. sub sendPrivateMsg
  676. {
  677. my ($self, $user, $message) = @_;
  678. Misc::validate($user)?$self->sendToServer($self->reconstruct({ switch => 'private_message', privMsg => $message, privMsgUser => $user, })):return;
  679. }
  680.  
  681. sub sendLook {
  682. my ($self, $body, $head) = @_;
  683. $self->sendToServer($self->reconstruct({switch => 'actor_look_at', body => $body, head => $head}));
  684. debug "Sent look: $body $head\n", "sendPacket", 2;
  685. @{$char->{look}}{qw(body head)} = ($body, $head);
  686. }
  687.  
  688. sub sendTake {
  689. my ($self, $itemID) = @_;
  690. $self->sendToServer($self->reconstruct({switch => 'item_take', ID => $itemID}));
  691. debug "Sent take\n", "sendPacket", 2;
  692. }
  693.  
  694. sub sendDrop {
  695. my ($self, $index, $amount) = @_;
  696. $self->sendToServer($self->reconstruct({switch => 'item_drop', index => $index, amount => $amount}));
  697. debug "Sent drop: $index x $amount\n", "sendPacket", 2;
  698. }
  699.  
  700. sub sendItemUse {
  701. my ($self, $index, $targetID) = @_;
  702. $self->sendToServer($self->reconstruct({switch => 'item_use', index => $index, targetID => $targetID}));
  703. debug "Item Use: $index\n", "sendPacket", 2;
  704. }
  705.  
  706. # for old plugin compatibility, use sendRestart instead!
  707. sub sendRespawn { $_[0]->sendRestart(0) }
  708.  
  709. # for old plugin compatibility, use sendRestart instead!
  710. sub sendQuitToCharSelect { $_[0]->sendRestart(1) }
  711.  
  712. # 0x00b2,3,restart,2
  713. # type: 0=respawn ; 1=return to char select
  714. sub sendRestart {
  715. my ($self, $type) = @_;
  716. $self->sendToServer($self->reconstruct({switch => 'restart', type => $type}));
  717. debug "Sent Restart: " . ($type ? 'Quit To Char Selection' : 'Respawn') . "\n", "sendPacket", 2;
  718. }
  719.  
  720. sub sendStorageAdd {
  721. my ($self, $index, $amount) = @_;
  722. $self->sendToServer($self->reconstruct({switch => 'storage_item_add', index => $index, amount => $amount}));
  723. debug "Sent Storage Add: $index x $amount\n", "sendPacket", 2;
  724. }
  725.  
  726. sub sendStorageGet {
  727. my ($self, $index, $amount) = @_;
  728. $self->sendToServer($self->reconstruct({switch => 'storage_item_remove', index => $index, amount => $amount}));
  729. debug "Sent Storage Get: $index x $amount\n", "sendPacket", 2;
  730. }
  731.  
  732. sub sendStoragePassword {
  733. my $self = shift;
  734. # 16 byte packed hex data
  735. my $pass = shift;
  736. # 2 = set password ?
  737. # 3 = give password ?
  738. my $type = shift;
  739. my $msg;
  740. my $mid = hex($self->{packet_lut}{storage_password});
  741. if ($type == 3) {
  742. $msg = pack("v v", $mid, $type).$pass.pack("H*", "EC62E539BB6BBC811A60C06FACCB7EC8");
  743. } elsif ($type == 2) {
  744. $msg = pack("v v", $mid, $type).pack("H*", "EC62E539BB6BBC811A60C06FACCB7EC8").$pass;
  745. } else {
  746. ArgumentException->throw("The 'type' argument has invalid value ($type).");
  747. }
  748. $self->sendToServer($msg);
  749. }
  750.  
  751. sub parse_party_chat {
  752. my ($self, $args) = @_;
  753. $self->parseChat($args);
  754. }
  755.  
  756. sub reconstruct_party_chat {
  757. my ($self, $args) = @_;
  758. $self->reconstructChat($args);
  759. }
  760.  
  761. sub sendPartyChat {
  762. my ($self, $message) = @_;
  763. $self->sendToServer($self->reconstruct({switch => 'party_chat', message => $message}));
  764. }
  765.  
  766.  
  767. sub parse_buy_bulk_vender {
  768. my ($self, $args) = @_;
  769. @{$args->{items}} = map {{ amount => unpack('v', $_), itemIndex => unpack('x2 v', $_) }} unpack '(a4)*', $args->{itemInfo};
  770. }
  771.  
  772. sub reconstruct_buy_bulk_vender {
  773. my ($self, $args) = @_;
  774. # ITEM index. There were any other indexes expected to be in item buying packet?
  775. $args->{itemInfo} = pack '(a4)*', map { pack 'v2', @{$_}{qw(amount itemIndex)} } @{$args->{items}};
  776. }
  777.  
  778. # not "buy", it sells items!
  779. sub sendBuyBulkVender {
  780. my ($self, $venderID, $r_array, $venderCID) = @_;
  781. $self->sendToServer($self->reconstruct({
  782. switch => 'buy_bulk_vender',
  783. venderID => $venderID,
  784. venderCID => $venderCID,
  785. items => $r_array,
  786. }));
  787. debug "Sent bulk buy vender: ".(join ', ', map {"$_->{itemIndex} x $_->{amount}"} @$r_array)."\n", "sendPacket";
  788. }
  789.  
  790. sub parse_buy_bulk_buyer {
  791. my ($self, $args) = @_;
  792. @{$args->{items}} = map {{ amount => unpack('v', $_), itemIndex => unpack('x2 v', $_) }} unpack '(a4)*', $args->{itemInfo};
  793. }
  794.  
  795. sub reconstruct_buy_bulk_buyer {
  796. my ($self, $args) = @_;
  797. # ITEM index. There were any other indexes expected to be in item buying packet?
  798. $args->{itemInfo} = pack '(a4)*', map { pack 'v2', @{$_}{qw(amount itemIndex)} } @{$args->{items}};
  799. }
  800.  
  801. sub sendBuyBulkBuyer {
  802. #FIXME not working yet
  803. #field index still wrong and remain unknown
  804. my ($self, $buyerID, $r_array, $buyingStoreID) = @_;
  805. my $msg = pack('v2', 0x0819, 12+6*@{$r_array});
  806. $msg .= pack ('a4 a4', $buyerID, $buyingStoreID);
  807. for (my $i = 0; $i < @{$r_array}; $i++) {
  808. debug 'Send Buying Buyer Request: '.$r_array->[$i]{itemIndex}.' '.$r_array->[$i]{itemID}.' '.$r_array->[$i]{amount}."\n", "sendPacket", 2;
  809. $msg .= pack('v3', $r_array->[$i]{itemIndex}, $r_array->[$i]{itemID}, $r_array->[$i]{amount});
  810. }
  811. $self->sendToServer($msg);
  812. }
  813.  
  814. sub sendEnteringBuyer {
  815. my ($self, $ID) = @_;
  816. $self->sendToServer($self->reconstruct({switch => 'buy_bulk_request', ID => $ID}));
  817. debug "Sent Entering Buyer: ID - ".getHex($ID)."\n", "sendPacket", 2;
  818. }
  819.  
  820. sub sendSkillUse {
  821. my ($self, $ID, $lv, $targetID) = @_;
  822. ### need to check Hook###
  823. my %args;
  824. Plugins::callHook('packet_pre/sendSkillUse', \%args);
  825. if ($args{return}) {
  826. $self->sendToServer($args{msg});
  827. return;
  828. }
  829. ##########################
  830. $self->sendToServer($self->reconstruct({switch => 'skill_use', lv => $lv, skillID => $ID, targetID => $targetID}));
  831. debug "Skill Use: $ID\n", "sendPacket", 2;
  832. }
  833.  
  834. sub sendSkillUseLoc {
  835. my ($self, $ID, $lv, $x, $y) = @_;
  836. $self->sendToServer($self->reconstruct({switch => 'skill_use_location', lv => $lv, skillID => $ID, x => $x, y => $y}));
  837. debug "Skill Use on Location: $ID, ($x, $y)\n", "sendPacket", 2;
  838. }
  839.  
  840. sub sendGuildMasterMemberCheck {
  841. my ($self, $ID) = @_;
  842. $self->sendToServer($self->reconstruct({switch => 'guild_check'}));
  843. debug "Sent Guild Master/Member Check.\n", "sendPacket";
  844. }
  845.  
  846. sub sendGuildRequestInfo {
  847. my ($self, $page) = @_; # page 0-4
  848. $self->sendToServer($self->reconstruct({
  849. switch => 'guild_info_request',
  850. type => $page,
  851. }));
  852. debug "Sent Guild Request Page : ".$page."\n", "sendPacket";
  853. }
  854.  
  855. sub parse_guild_chat {
  856. my ($self, $args) = @_;
  857. $self->parseChat($args);
  858. }
  859.  
  860. sub reconstruct_guild_chat {
  861. my ($self, $args) = @_;
  862. $self->reconstructChat($args);
  863. }
  864.  
  865. sub sendGuildChat {
  866. my ($self, $message) = @_;
  867. $self->sendToServer($self->reconstruct({switch => 'guild_chat', message => $message}));
  868. }
  869.  
  870. sub sendQuit {
  871. my $self = shift;
  872. $self->sendToServer($self->reconstruct({switch => 'quit_request', type => 0}));
  873. debug "Sent Quit\n", "sendPacket", 2;
  874. }
  875.  
  876. sub sendCloseShop {
  877. my $self = shift;
  878. $self->sendToServer($self->reconstruct({switch => 'shop_close'}));
  879. debug "Shop Closed\n", "sendPacket", 2;
  880. }
  881.  
  882. # 0x7DA
  883. sub sendPartyLeader {
  884. my $self = shift;
  885. my $ID = shift;
  886. my $msg = pack("C*", 0xDA, 0x07).$ID;
  887. $self->sendToServer($msg);
  888. debug "Sent Change Party Leader ".getHex($ID)."\n", "sendPacket", 2;
  889. }
  890.  
  891. # 0x802
  892. sub sendPartyBookingRegister {
  893. my ($self, $level, $MapID, @jobList) = @_;
  894.  
  895. $self->sendToServer($self->reconstruct({switch => 'booking_register', level => $level, MapID => $MapID,
  896. job0 => @jobList[0], job1 => @jobList[1], job2 => @jobList[2],
  897. job3 => @jobList[3], job4 => @jobList[4], job5 => @jobList[5]}));
  898.  
  899. debug "Sent Booking Register\n", "sendPacket", 2;
  900. }
  901.  
  902. # 0x804
  903. sub sendPartyBookingReqSearch {
  904. my ($self, $level, $MapID, $job, $LastIndex, $ResultCount) = @_;
  905.  
  906. $job = "65535" if ($job == 0); # job null = 65535
  907. $ResultCount = "10" if ($ResultCount == 0); # ResultCount defaut = 10
  908.  
  909. $self->sendToServer($self->reconstruct({switch => 'booking_search', level => $level, MapID => $MapID, job => $job, LastIndex => $LastIndex, ResultCount => $ResultCount}));
  910. debug "Sent Booking Search\n", "sendPacket", 2;
  911. }
  912.  
  913. # 0x806
  914. sub sendPartyBookingDelete {
  915. my $self = shift;
  916. $self->sendToServer($self->reconstruct({switch => 'booking_delete'}));
  917. debug "Booking Deleted\n", "sendPacket", 2;
  918. }
  919.  
  920. # 0x808
  921. sub sendPartyBookingUpdate {
  922. my ($self, @jobList) = @_;
  923.  
  924. $self->sendToServer($self->reconstruct({switch => 'booking_update', job0 => @jobList[0],
  925. job1 => @jobList[1], job2 => @jobList[2], job3 => @jobList[3],
  926. job4 => @jobList[4], job5 => @jobList[5]}));
  927.  
  928. debug "Sent Booking Update\n", "sendPacket", 2;
  929. }
  930.  
  931. # 0x0827,6
  932. sub sendCharDelete2 {
  933. my ($self, $charID) = @_;
  934.  
  935. $self->sendToServer($self->reconstruct({switch => 'char_delete2', charID => $charID}));
  936. debug "Sent sendCharDelete2\n", "sendPacket", 2;
  937. }
  938.  
  939. ##
  940. # switch: 0x0829,12: '0829' => ['char_delete2_accept', 'a4 a6', [qw(charID code)]], # 12 -> kRO
  941. # switch: 0x098F,-1: '098f' => ['char_delete2_accept', 'v a4 a*', [qw(length charID code)]], -> idRO, iRO Renewal
  942. sub sendCharDelete2Accept {
  943. my ($self, $charID, $code) = @_;
  944.  
  945. $self->sendToServer($self->reconstruct({switch => 'char_delete2_accept', charID => $charID, code => $code}));
  946. }
  947.  
  948. sub reconstruct_char_delete2_accept {
  949. my ($self, $args) = @_;
  950. debug "Sent sendCharDelete2Accept. CharID: $args->{CharID}, Code: $args->{code}\n", "sendPacket", 2;
  951. }
  952.  
  953. # 0x082B,6
  954. sub sendCharDelete2Cancel {
  955. my ($self, $charID) = @_;
  956.  
  957. $self->sendToServer($self->reconstruct({switch => 'char_delete2_cancel', charID => $charID}));
  958. debug "Sent sendCharDelete2Cancel\n", "sendPacket", 2;
  959. }
  960.  
  961. sub reconstruct_client_hash {
  962. my ($self, $args) = @_;
  963.  
  964. if (defined $args->{code}) {
  965. # FIXME there's packet switch in that code. How to handle it correctly?
  966. my $code = $args->{code};
  967. $code =~ s/^02 04 //;
  968.  
  969. $args->{hash} = pack 'C*', map hex, split / /, $code;
  970.  
  971. } elsif ($args->{type}) {
  972. if ($args->{type} == 1) {
  973. $args->{hash} = pack('C*', 0x7B, 0x8A, 0xA8, 0x90, 0x2F, 0xD8, 0xE8, 0x30, 0xF8, 0xA5, 0x25, 0x7A, 0x0D, 0x3B, 0xCE, 0x52);
  974. } elsif ($args->{type} == 2) {
  975. $args->{hash} = pack('C*', 0x27, 0x6A, 0x2C, 0xCE, 0xAF, 0x88, 0x01, 0x87, 0xCB, 0xB1, 0xFC, 0xD5, 0x90, 0xC4, 0xED, 0xD2);
  976. } elsif ($args->{type} == 3) {
  977. $args->{hash} = pack('C*', 0x42, 0x00, 0xB0, 0xCA, 0x10, 0x49, 0x3D, 0x89, 0x49, 0x42, 0x82, 0x57, 0xB1, 0x68, 0x5B, 0x85);
  978. } elsif ($args->{type} == 4) {
  979. $args->{hash} = pack('C*', 0x22, 0x37, 0xD7, 0xFC, 0x8E, 0x9B, 0x05, 0x79, 0x60, 0xAE, 0x02, 0x33, 0x6D, 0x0D, 0x82, 0xC6);
  980. } elsif ($args->{type} == 5) {
  981. $args->{hash} = pack('C*', 0xc7, 0x0A, 0x94, 0xC2, 0x7A, 0xCC, 0x38, 0x9A, 0x47, 0xF5, 0x54, 0x39, 0x7C, 0xA4, 0xD0, 0x39);
  982. }
  983. }
  984. }
  985.  
  986. # TODO: clientHash and secureLogin_requestCode is almost the same, merge
  987. sub sendClientMD5Hash {
  988. my ($self) = @_;
  989. $self->sendToServer($self->reconstruct({
  990. switch => 'client_hash',
  991. hash => pack('H32', $masterServer->{clientHash}), # ex 82d12c914f5ad48fd96fcf7ef4cc492d (kRO sakray != kRO main)
  992. }));
  993. }
  994.  
  995. sub parse_actor_move {
  996. my ($self, $args) = @_;
  997. makeCoordsDir($args, $args->{coords});
  998. }
  999.  
  1000. sub reconstruct_actor_move {
  1001. my ($self, $args) = @_;
  1002. $args->{coords} = getCoordString(@{$args}{qw(x y)}, !($masterServer->{serverType} > 0));
  1003. }
  1004.  
  1005. # should be called sendSlaveMove...
  1006. sub sendHomunculusMove {
  1007. my ($self, $ID, $x, $y) = @_;
  1008. $self->sendToServer($self->reconstruct({switch => 'actor_move', ID => $ID, x => $x, y => $y}));
  1009. debug sprintf("Sent %s move to: %d, %d\n", Actor::get($ID), $x, $y), "sendPacket", 2;
  1010. }
  1011.  
  1012. sub sendFriendListReply {
  1013. my ($self, $accountID, $charID, $flag) = @_;
  1014. $self->sendToServer($self->reconstruct({
  1015. switch => 'friend_response',
  1016. friendAccountID => $accountID,
  1017. friendCharID => $charID,
  1018. type => $flag,
  1019. }));
  1020. debug "Sent Reject friend request\n", "sendPacket";
  1021. }
  1022.  
  1023. sub sendFriendRequest {
  1024. my ($self, $name) = @_;
  1025. my $binName = stringToBytes($name);
  1026. $binName = substr($binName, 0, 24) if (length($binName) > 24);
  1027. $binName = $binName . chr(0) x (24 - length($binName));
  1028. $self->sendToServer($self->reconstruct({
  1029. switch => 'friend_request',
  1030. username => $binName,
  1031.  
  1032. }));
  1033. debug "Sent Request to be a friend: $name\n", "sendPacket";
  1034. }
  1035.  
  1036. sub sendHomunculusCommand {
  1037. my ($self, $command, $type) = @_; # $type is ignored, $command can be 0:get stats, 1:feed or 2:fire
  1038. $self->sendToServer($self->reconstruct({
  1039. switch => 'homunculus_command',
  1040. commandType => $type,
  1041. commandID => $command,
  1042. }));
  1043. debug "Sent Homunculus Command $command", "sendPacket", 2;
  1044. }
  1045.  
  1046. sub sendPartyJoinRequestByName
  1047. {
  1048. my ($self, $name) = @_;
  1049.  
  1050. $self->sendToServer($self->reconstruct({
  1051. switch => 'party_join_request_by_name',
  1052. partyName => stringToBytes ($name),
  1053. }));
  1054.  
  1055. debug "Sent Request Join Party (by name): $name\n", "sendPacket", 2;
  1056. }
  1057.  
  1058. sub sendSkillSelect {
  1059. my ($self, $skillID, $why) = @_;
  1060. $self->sendToServer($self->reconstruct({
  1061. switch => 'skill_select',
  1062. skillID => $skillID,
  1063. why => $why,
  1064. }));
  1065. debug sprintf("Sent Skill Select (skillID: %d, why: %d)", $skillID, $why), 'sendPacket', 2;
  1066. }
  1067.  
  1068. sub sendReplySyncRequestEx
  1069. {
  1070. my ($self, $SyncID) = @_;
  1071. # Packing New Message and Dispatching
  1072. my $pid = sprintf("%04X", $SyncID);
  1073. $self->sendToServer(pack("C C", hex(substr($pid, 2, 2)), hex(substr($pid, 0, 2))));
  1074. # Debug Log
  1075. # print "Dispatching Sync Ex Reply : 0x" . $pid . "\n";
  1076. # Debug Log
  1077. debug "Sent Reply Sync Request Ex\n", "sendPacket", 2;
  1078. }
  1079.  
  1080. sub sendLoginPinCode {
  1081. my ($self, $seed, $type) = @_;
  1082.  
  1083. my $pin = pinEncode($seed, $config{loginPinCode});
  1084. my $msg;
  1085. if ($type == 0) {
  1086. $msg = $self->reconstruct({
  1087. switch => 'send_pin_password',
  1088. accountID => $accountID,
  1089. pin => $pin,
  1090. });
  1091. } elsif ($type == 1) {
  1092. $msg = $self->reconstruct({
  1093. switch => 'new_pin_password',
  1094. accountID => $accountID,
  1095. pin => $pin,
  1096. });
  1097. }
  1098. $self->sendToServer($msg);
  1099. $timeout{charlogin}{time} = time;
  1100. debug "Sent loginPinCode\n", "sendPacket", 2;
  1101. }
  1102.  
  1103. sub sendCloseBuyShop {
  1104. my $self = shift;
  1105. $self->sendToServer($self->reconstruct({switch => 'buy_bulk_closeShop'}));
  1106. debug "Buying Shop Closed\n", "sendPacket", 2;
  1107. }
  1108.  
  1109. sub sendRequestCashItemsList {
  1110. my $self = shift;
  1111. $self->sendToServer($self->reconstruct({switch => 'request_cashitems'}));
  1112. debug "Requesting cashItemsList\n", "sendPacket", 2;
  1113. }
  1114.  
  1115. sub sendCashShopOpen {
  1116. my $self = shift;
  1117. $self->sendToServer($self->reconstruct({switch => 'cash_shop_open'}));
  1118. debug "Requesting sendCashShopOpen\n", "sendPacket", 2;
  1119. }
  1120.  
  1121. sub sendCashBuy {
  1122. my $self = shift;
  1123. my ($item_id, $item_amount, $tab_code) = @_;
  1124. #"len count item_id item_amount tab_code"
  1125. $self->sendToServer($self->reconstruct({
  1126. switch => 'cash_shop_buy_items',
  1127. len => 16, # always 16 for current implementation
  1128. count => 1, # current _kore_ implementation only allow us to buy 1 item at time
  1129. item_id => $item_id,
  1130. item_amount => $item_amount,
  1131. tab_code => $tab_code
  1132. }
  1133. )
  1134. );
  1135. debug "Requesting sendCashShopOpen\n", "sendPacket", 2;
  1136. }
  1137.  
  1138. sub sendShowEquipPlayer {
  1139. my ($self, $ID) = @_;
  1140. $self->sendToServer($self->reconstruct({
  1141. switch => 'view_player_equip_request',
  1142. ID => $ID
  1143. }
  1144. )
  1145. );
  1146. debug "Sent Show Equip Player.\n", "sendPacket", 2;
  1147. }
  1148.  
  1149. sub sendShowEquipTickbox {
  1150. my ($self, $flag) = @_;
  1151. $self->sendToServer($self->reconstruct({
  1152. switch => 'equip_window_tick',
  1153. type => 0, # is it always zero?
  1154. value => $flag
  1155. }
  1156. )
  1157. );
  1158. debug "Sent Show Equip Tickbox: flag.\n", "sendPacket", 2;
  1159. }
  1160.  
  1161. # should be sendSlaveAttack...
  1162. sub sendHomunculusAttack {
  1163. my $self = shift;
  1164. my $slaveID = shift;
  1165. my $targetID = shift;
  1166. my $flag = shift;
  1167. $self->sendToServer($self->reconstruct({
  1168. switch => 'slave_attack',
  1169. slaveID => $slaveID,
  1170. targetID => $targetID,
  1171. flag => $flag
  1172. }
  1173. )
  1174. );
  1175. debug "Sent Slave attack: ".getHex($targetID)."\n", "sendPacket", 2;
  1176. }
  1177.  
  1178. # should be sendSlaveStandBy...
  1179. sub sendHomunculusStandBy {
  1180. my $self = shift;
  1181. my $slaveID = shift;
  1182. $self->sendToServer($self->reconstruct({
  1183. switch => 'slave_move_to_master',
  1184. slaveID => $slaveID
  1185. }
  1186. )
  1187. );
  1188. debug "Sent Slave standby\n", "sendPacket", 2;
  1189. }
  1190.  
  1191. sub sendEquip {
  1192. my ($self, $index, $type) = @_;
  1193. $self->sendToServer($self->reconstruct({
  1194. switch => 'send_equip',
  1195. index => $index,
  1196. type => $type
  1197. }
  1198. )
  1199. );
  1200. debug "Sent Equip: $index Type: $type\n" , 2;
  1201. }
  1202.  
  1203. sub sendProgress {
  1204. my ($self) = @_;
  1205. my $msg = pack("C*", 0xf1, 0x02);
  1206. $self->sendToServer($msg);
  1207. debug "Sent Progress Bar Finish\n", "sendPacket", 2;
  1208. }
  1209.  
  1210. sub sendProduceMix {
  1211. my ($self, $ID,
  1212. # nameIDs for added items such as Star Crumb or Flame Heart
  1213. $item1, $item2, $item3) = @_;
  1214.  
  1215. my $msg = pack('v5', 0x018E, $ID, $item1, $item2, $item3);
  1216. $self->sendToServer($msg);
  1217. debug "Sent Forge, Produce Item: $ID\n" , 2;
  1218. }
  1219.  
  1220. 1;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement