Advertisement
Ucup_Coding

Send.pm

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