RagnarokServer.pm
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:32k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. ###########################################################
  2. # Poseidon server - Ragnarok Online server emulator
  3. #
  4. # This program is free software; you can redistribute it and/or 
  5. # modify it under the terms of the GNU General Public License 
  6. # as published by the Free Software Foundation; either version 2 
  7. # of the License, or (at your option) any later version.
  8. #
  9. # Copyright (c) 2005-2006 OpenKore Development Team
  10. ###########################################################
  11. # This class emulates a Ragnarok Online server.
  12. # The RO client connects to this server. This server
  13. # periodically sends a GameGuard query to the RO client,
  14. # and saves the RO client's response.
  15. ###########################################################
  16. package Poseidon::RagnarokServer;
  17. use strict;
  18. use Base::Server;
  19. use base qw(Base::Server);
  20. use Misc;
  21. use Utils qw(binSize getCoordString timeOut getHex);
  22. my %clientdata;
  23. sub new {
  24. my ($class, @args) = @_;
  25. my $self = $class->SUPER::new(@args);
  26. # int challengeNum
  27. #
  28. # The number of times that the RO client has sent a
  29. # GameGuard challenge packet.
  30. #
  31. # Invariant: challengeNum >= 0
  32. $self->{challengeNum} = 0;
  33. # Bytes response
  34. #
  35. # A response for the last GameGuard query.
  36. $self->{response} = undef;
  37. # Invariant: state ne ''
  38. $self->{state} = 'ready';
  39. return $self;
  40. }
  41. ##
  42. # $RagnarokServer->query(Bytes packet)
  43. # packet: The raw GameGuard query packet.
  44. # Require: defined($packet) && $self->getState() eq 'ready'
  45. # Ensure: $self->getState() eq 'requesting'
  46. #
  47. # Send a GameGuard query to the RO client.
  48. sub query {
  49. my ($self, $packet) = @_;
  50. my $clients = $self->clients();
  51. for (my $i = 0; $i < @{$clients}; $i++) {
  52. if ($clients->[$i]) {
  53. if ($clients->[$i]{connectedToMap}) {
  54.                  $clients->[$i]->send($packet);
  55.                  $self->{state} = 'requesting';
  56.                  return;
  57. }
  58. }
  59. }
  60. print "Error: no Ragnarok Online client connected.n";
  61. }
  62. ##
  63. # String $RagnarokServer->getState()
  64. #
  65. # Get the state of this RagnarokServer object.
  66. # The result can be one of:
  67. # 'ready' - The RO client is ready to handle another GameGuard query.
  68. # 'requesting' - The query has been sent to the RO client, but it hasn't responded yet.
  69. # 'requested' - The RO client has responded to the last GameGuard query.
  70. # 'not connected' - The RO client hasn't connected to this server yet.
  71. sub getState {
  72. my ($self) = @_;
  73. my $clients = $self->clients();
  74. if ($self->{state} eq 'requested') {
  75. return 'requested';
  76. } elsif (binSize($clients) == 0) {
  77. return 'not connected';
  78. } else {
  79. return $self->{state};
  80. }
  81. }
  82. ##
  83. # Bytes $RagnarokServer->readResponse()
  84. # Require: $self->getState() eq 'requested'
  85. # Ensure: defined(result) && $self->getState() eq 'ready'
  86. #
  87. # Read the response for the last GameGuard query.
  88. sub readResponse {
  89. my $resp = $_[0]->{response};
  90. $_[0]->{response} = undef;
  91. $_[0]->{state} = 'ready';
  92. return $resp;
  93. }
  94. #####################################################
  95. sub onClientNew {
  96. my ($self, $client, $index) = @_;
  97. print "Ragnarok Online client ($index) connected.n";
  98. $self->{state} = 'ready';
  99. }
  100. sub onClientExit {
  101. my ($self, $client, $index) = @_;
  102. print "Ragnarok Online client ($index) disconnected.n";
  103. }
  104. ## constants
  105. my $accountID = pack("a4", "acct");
  106. my $posX = 53;
  107. my $posY = 111;
  108. sub onClientData {
  109. my ($self, $client, $msg, $index) = @_;
  110. ### These variables control the account information ###
  111. my $host = $self->getHost();
  112. my $port = pack("v", $self->getPort());
  113. $host = '127.0.0.1' if ($host eq 'localhost');
  114. my @ipElements = split /./, $host;
  115. my $charID = pack("a4", "char");
  116. my $sessionID = pack("a4", "sess");
  117. my $sessionID2 = pack("C4", 0xff);
  118. my $npcID1 = pack("a4", "npc1");
  119. my $npcID0 = pack("a4", "npc2");
  120. my $monsterID = pack("a4", "mon1");
  121. my $itemID = pack("a4", "itm1");
  122. my $switch = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1)));
  123. my $packed_switch = quotemeta substr($msg, 0, 2);
  124. # Note:
  125. # The switch packets are pRO specific and assumes the use of secureLogin 1. It may or may not work with other
  126. # countries' clients (except probably oRO). The best way to support other clients would be: use a barebones
  127. # eAthena or Freya as the emulator, or figure out the correct packet switches and include them in the
  128. # if..elsif..else blocks.
  129. if (($switch eq '01DB') || ($switch eq '0204')) { # client sends login packet 0204 packet thanks to elhazard
  130. # '01DC' => ['secure_login_key', 'x2 a*', [qw(secure_key)]],
  131. my $data = pack("C*", 0xdc, 0x01, 0x14) . pack("x17");
  132. $client->send($data);
  133. # save servers.txt info
  134. my $code = substr($msg, 2);
  135. if (length($msg) == 2) {
  136. $clientdata{$index}{secureLogin_type} = 0;
  137. } elsif (length($msg) == 20) {
  138. if ($code eq pack("C*", 0x04, 0x02, 0x7B, 0x8A, 0xA8, 0x90, 0x2F, 0xD8, 0xE8, 0x30, 0xF8, 0xA5, 0x25, 0x7A, 0x0D, 0x3B, 0xCE, 0x52)) {
  139. $clientdata{$index}{secureLogin_type} = 1;
  140. } elsif ($code eq pack("C*", 0x04, 0x02, 0x27, 0x6A, 0x2C, 0xCE, 0xAF, 0x88, 0x01, 0x87, 0xCB, 0xB1, 0xFC, 0xD5, 0x90, 0xC4, 0xED, 0xD2)) {
  141. $clientdata{$index}{secureLogin_type} = 2;
  142. } elsif ($code eq pack("C*", 0x04, 0x02, 0x42, 0x00, 0xB0, 0xCA, 0x10, 0x49, 0x3D, 0x89, 0x49, 0x42, 0x82, 0x57, 0xB1, 0x68, 0x5B, 0x85)) {
  143. $clientdata{$index}{secureLogin_type} = 3;
  144. } elsif ($code eq ("C*", 0x04, 0x02, 0x22, 0x37, 0xD7, 0xFC, 0x8E, 0x9B, 0x05, 0x79, 0x60, 0xAE, 0x02, 0x33, 0x6D, 0x0D, 0x82, 0xC6)) {
  145. $clientdata{$index}{secureLogin_type} = 4;
  146. } elsif ($code eq pack("C*", 0x04, 0x02, 0xc7, 0x0A, 0x94, 0xC2, 0x7A, 0xCC, 0x38, 0x9A, 0x47, 0xF5, 0x54, 0x39, 0x7C, 0xA4, 0xD0, 0x39)) {
  147. $clientdata{$index}{secureLogin_type} = 5;
  148. }
  149. } else {
  150. $clientdata{$index}{secureLogin_requestCode} = getHex($code);
  151. }
  152. } elsif (($switch eq '01DD') || ($switch eq '01FA') || ($switch eq '0064') || ($switch eq '0060') || ($switch eq '0277')) { # 0064 packet thanks to abt123
  153. # my $data = pack("C*", 0xAD, 0x02, 0x00, 0x00, 0x1E, 0x0A, 0x00, 0x00);
  154. # $client->send($data);
  155. my $sex = 1;
  156. my $serverName = pack("a20", "Poseidon server"); # server name should be less than or equal to 20 characters
  157. my $serverUsers = pack("V", @{$self->clients()} - 1);
  158. # '0069' => ['account_server_info', 'x2 a4 a4 a4 x30 C1 a*',
  159. #  [qw(sessionID accountID sessionID2 accountSex serverInfo)]],
  160. my $data;
  161. if ($switch eq '01FA') {
  162. $data = pack("C*", 0x69, 0x00, 0x53, 0x00) . 
  163. $sessionID . $accountID . $sessionID2 . 
  164. pack("x30") . pack("C1", $sex) . pack("x4") .
  165. pack("C*", $ipElements[0], $ipElements[1], $ipElements[2], $ipElements[3]) .
  166. $port . $serverName . $serverUsers . pack("x2");
  167. } else {
  168. $data = pack("C*", 0x69, 0x00, 0x4F, 0x00) . 
  169. $sessionID . $accountID . $sessionID2 . 
  170. pack("x30") . pack("C1", $sex) .
  171. pack("C*", $ipElements[0], $ipElements[1], $ipElements[2], $ipElements[3]) .
  172. $port . $serverName . $serverUsers . pack("x2");
  173. }
  174. $client->send($data);
  175. # save servers.txt info
  176. $clientdata{$index}{version} = unpack("V", substr($msg, 2, 4));
  177. $clientdata{$index}{master_version} = unpack("C", substr($msg, length($msg) - 1, 1));
  178. if ($switch eq '01DD') {
  179. $clientdata{$index}{secureLogin} = 1;
  180. undef $clientdata{$index}{secureLogin_account};
  181. } elsif ($switch eq '01FA') {
  182. $clientdata{$index}{secureLogin} = 3;
  183. $clientdata{$index}{secureLogin_account} = unpack("C", substr($msg, 47, 1));
  184. } else {
  185. undef $clientdata{$index}{secureLogin};
  186. undef $clientdata{$index}{secureLogin_type};
  187. undef $clientdata{$index}{secureLogin_account};
  188. undef $clientdata{$index}{secureLogin_requestCode};
  189. }
  190. if (($switch ne '01DD') && ($switch ne '01FA') && ($switch ne '0064')) {
  191. $clientdata{$index}{masterLogin_packet} = $switch;
  192. } else {
  193. undef $clientdata{$index}{masterLogin_packet};
  194. }
  195. } elsif (($switch eq '0065') || ($switch eq '0275') || ($msg =~ /^$packed_switch$accountID$sessionID$sessionID2x0x0.$/)) { # client sends server choice packet
  196. my $exp = pack("V", 0);
  197. my $zeny = pack("V", 0);
  198. my $exp_job = pack("V", 0);
  199. my $lvl_job = pack("v", 70);
  200. my $hp = pack("v", 0x0fff);
  201. my $hp_max = $hp;
  202. my $sp = pack("v", 0x0fff);
  203. my $sp_max = $sp;
  204. my $job_id1 = pack("v", 0);
  205. my $job_id2 = pack("v", 23);
  206. my $hairStyle = pack("v", 16);
  207. my $level = pack("v", 99);
  208. my $head_low = pack("v", 0);
  209. my $head_top = pack("v", 5016);
  210. my $head_mid = pack("v", 0);
  211. my $hairColor = pack("v", 6);
  212. my $charName1 = pack("a24", "Poseidon");
  213. my $charName2 = pack("a24", "Poseidon Dev");
  214. my ($str, $agi, $vit, $int, $dex, $luk) = (99, 99, 99, 99, 99, 99);
  215. my $charStats = pack("C*", $str, $agi, $vit, $int, $dex, $luk);
  216. my $data = $accountID .
  217. pack("v2 x20", 0x6b, 0xEC) .
  218. $charID . $exp . $zeny . $exp_job . $lvl_job .
  219. pack("x24") . $hp . $hp_max . $sp . $sp_max .
  220. pack("x2") . $job_id1 . $hairStyle .
  221. pack("x2") . $level .
  222. #pack("x2") . $head_low . pack("x2") . $head_top . $head_mid . $hairColor .
  223. pack("C*", 0x01, 0x00, 0x38, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x9E, 0x00, 0x06, 0x00) .
  224. #pack("C*", 0x01, 0x00, 0x38, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x06, 0x00) .
  225. pack("x2") . $charName1 . $charStats . pack("v1", 0) .
  226. $charID . $exp . $zeny . $exp_job . $lvl_job .
  227. pack("x24") . $hp . $hp_max . $sp . $sp_max .
  228. pack("x2") . $job_id2 . $hairStyle .
  229. pack("x2") . $level .
  230. #pack("x2") . $head_low . pack("x2") . $head_top . $head_mid . $hairColor .
  231. pack("C*", 0x01, 0x00, 0x39, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x98, 0x00, 0x06, 0x00) .
  232. pack("x2") . $charName2 . $charStats . pack("v1", 1);
  233. # NOTE: ideally, all character slots are filled with the same character, for idiot-proofing
  234. # NOTE: also, the character's appearance may be made to be modifiable
  235. $client->send($data);
  236. # save servers.txt info
  237. if ($switch ne '0065') {
  238. $clientdata{$index}{gameLogin_packet} = $switch;
  239. } else {
  240. undef $clientdata{$index}{gameLogin_packet};
  241. }
  242. } elsif ($switch eq '0066') { # client sends character choice packet
  243. $clientdata{$index}{mode} = unpack('C1', substr($msg, 2, 1));
  244. # '0071' => ['received_character_ID_and_Map', 'a4 Z16 a4 v1', [qw(charID mapName mapIP mapPort)]],
  245. my $mapName = pack("a16", "new_1-1.gat");
  246. my $data = pack("C*", 0x71, 0x00) . $charID . $mapName . 
  247. pack("C*", $ipElements[0], $ipElements[1], $ipElements[2], $ipElements[3]) . $port;
  248. $client->send($data);
  249. } elsif ($switch eq '0072' &&
  250. (length($msg) == 19) &&
  251. (substr($msg, 2, 4) eq $accountID) &&
  252. (substr($msg, 6, 4) eq $charID) &&
  253. (substr($msg, 10, 4) eq $sessionID)
  254. ) { # client sends the maplogin packet
  255. mapLogin($self, $client, $msg, $index);
  256. # save servers.txt info
  257. $clientdata{$index}{serverType} = 0;
  258. } elsif ($switch eq '009B' &&
  259. (length($msg) == 32) &&
  260. (substr($msg, 3, 4) eq $accountID) &&
  261. (substr($msg, 12, 4) eq $charID) &&
  262. (substr($msg, 23, 4) eq $sessionID)
  263. ) { # client sends the maplogin packet
  264. mapLogin($self, $client, $msg, $index);
  265. # save servers.txt info
  266. $clientdata{$index}{serverType} = 3;
  267. } elsif ($switch eq '00F5' &&
  268. (length($msg) == 29) &&
  269. (substr($msg, 5, 4) eq $accountID) &&
  270. (substr($msg, 14, 4) eq $charID) &&
  271. (substr($msg, 20, 4) eq $sessionID)
  272. ) { # client sends the maplogin packet
  273. mapLogin($self, $client, $msg, $index);
  274. # save servers.txt info
  275. $clientdata{$index}{serverType} = 4;
  276. } elsif ($switch eq '009B' &&
  277. (length($msg) == 32) &&
  278. (substr($msg, 9, 4) eq $accountID) &&
  279. (substr($msg, 15, 4) eq $charID) &&
  280. (substr($msg, 23, 4) eq $sessionID)
  281. ) { # client sends the maplogin packet
  282. mapLogin($self, $client, $msg, $index);
  283. # save servers.txt info
  284. $clientdata{$index}{serverType} = 5;
  285. } elsif ($switch eq '0072' &&
  286. (length($msg) == 29) &&
  287. (substr($msg, 3, 4) eq $accountID) &&
  288. (substr($msg, 10, 4) eq $charID) &&
  289. (substr($msg, 20, 4) eq $sessionID)
  290. ) { # client sends the maplogin packet
  291. mapLogin($self, $client, $msg, $index);
  292. # save servers.txt info
  293. $clientdata{$index}{serverType} = 6;
  294. } elsif ($switch eq '0072' &&
  295. (length($msg) == 34) &&
  296. (substr($msg, 7, 4) eq $accountID) &&
  297. (substr($msg, 15, 4) eq $charID) &&
  298. (substr($msg, 25, 4) eq $sessionID)
  299. ) { # client sends the maplogin packet
  300. mapLogin($self, $client, $msg, $index);
  301. # save servers.txt info
  302. $clientdata{$index}{serverType} = 7;
  303. } elsif ($switch eq '009B' &&
  304. (length($msg) == 26) &&
  305. (substr($msg, 4, 4) eq $accountID) &&
  306. (substr($msg, 9, 4) eq $charID) &&
  307. (substr($msg, 17, 4) eq $sessionID)
  308. ) { # client sends the maplogin packet
  309. mapLogin($self, $client, $msg, $index);
  310. # save servers.txt info
  311. $clientdata{$index}{serverType} = 8;
  312. } elsif ($switch eq '009B' &&
  313. (length($msg) == 37) &&
  314. (substr($msg, 9, 4) eq $accountID) &&
  315. (substr($msg, 21, 4) eq $charID) &&
  316. (substr($msg, 28, 4) eq $sessionID)
  317. ) { # client sends the maplogin packet
  318. mapLogin($self, $client, $msg, $index);
  319. # save servers.txt info
  320. $clientdata{$index}{serverType} = 9;
  321. } elsif ($switch eq '0072' &&
  322. (length($msg) == 26) &&
  323. (substr($msg, 4, 4) eq $accountID) &&
  324. (substr($msg, 9, 4) eq $charID) &&
  325. (substr($msg, 17, 4) eq $sessionID)
  326. ) { # client sends the maplogin packet
  327. mapLogin($self, $client, $msg, $index);
  328. # save servers.txt info
  329. $clientdata{$index}{serverType} = 10;
  330. } elsif ($switch eq '0072' &&
  331. (length($msg) == 29) &&
  332. (substr($msg, 5, 4) eq $accountID) &&
  333. (substr($msg, 14, 4) eq $charID) &&
  334. (substr($msg, 20, 4) eq $sessionID)
  335. ) { # client sends the maplogin packet
  336. mapLogin($self, $client, $msg, $index);
  337. # save servers.txt info
  338. $clientdata{$index}{serverType} = 11;
  339. } elsif ($switch eq '0094' &&
  340. (length($msg) == 30) &&
  341. (substr($msg, 12, 4) eq $accountID) &&
  342. (substr($msg, 2, 4) eq $charID) &&
  343. (substr($msg, 6, 4) eq $sessionID)
  344. ) { # client sends the maplogin packet
  345. mapLogin($self, $client, $msg, $index);
  346. # save servers.txt info
  347. $clientdata{$index}{serverType} = 12;
  348. } elsif ($switch eq '0072' &&
  349. (length($msg) == 29) &&
  350. (substr($msg, 5, 4) eq $accountID) &&
  351. (substr($msg, 14, 4) eq $charID) &&
  352. (substr($msg, 20, 4) eq $sessionID)
  353. ) { # client sends the maplogin packet
  354. mapLogin($self, $client, $msg, $index);
  355. # save servers.txt info
  356. $clientdata{$index}{serverType} = "1 or 2";
  357. } elsif ($msg =~ /^$packed_switch/
  358. && $msg =~ /$accountID/
  359. && $msg =~ /$charID/
  360. && $msg =~ /$sessionID/) { # client sends the maplogin packet (unknown)
  361. print "Received unsupported map login packet $switch.n";
  362. visualDump($msg, "$switch");
  363. mapLogin($self, $client, $msg, $index);
  364. # save servers.txt info
  365. undef $clientdata{$index}{serverType};
  366. #$clientdata{$index}{sendMapLogin} = $msg;
  367. } elsif ($switch eq '007D') { # client sends the map loaded packet
  368. my $data;
  369. # Make Poseidon look to front
  370. $data .= pack('v1 a4 C1 x1 C1', 0x9C, $accountID, 0, 4);
  371. # Let's not wait for the client to ask for the unit info
  372. # '0095' => ['actor_info', 'a4 Z24', [qw(ID name)]],
  373. $data .= pack("v1 a4 a24", 0x95, $accountID, 'Poseidon' . (($clientdata{$index}{mode} ? ' Dev' : '')));
  374. # '009A' => ['system_chat', 'x2 Z*', [qw(message)]],
  375. $data .= pack("v2 a32", 0x9A, 36, "Welcome to the Poseidon Server!");
  376. # Show an NPC
  377. # '0078' => ['actor_display', 'a4 v14 a4 x7 C1 a3 x2 C1 v1', [qw(ID walk_speed param1 param2 param3 type hair_style weapon lowhead shield tophead midhead hair_color clothes_color head_dir guildID sex coords act lv)]],
  378. $data .= pack("v1 a4 v1 x6 v1 x30", 0x78, $npcID0, 300, 86) . getCoordString($posX + 3, $posY + 4, 1) . pack("C2 x3", 0x05, 0x05);
  379. $data .= pack('v1 a4 C1 x1 C1', 0x9C, $npcID0, 0, 3);
  380. # Let's not wait for the client to ask for the unit info
  381. # '0095' => ['actor_info', 'a4 Z24', [qw(ID name)]],
  382. $data .= pack("v1 a4 a24", 0x95, $npcID0, "Server Details Guide");
  383. if ($clientdata{$index}{mode}) {
  384. # Show some items in inventory
  385. # '01EE' => ['item_inventory_stackable', 'v4', [qw(index ID type amount)]]
  386. $data .= pack("C2 v1", 0xEE, 0x01, 40) .
  387. pack("v2 C2 v1 x10", 3, 501, 0, 1, 16) .
  388. pack("v2 C2 v1 x10", 4, 909, 3, 1, 144);
  389. # Show a kafra NPC
  390. # '0078' => ['actor_display', 'a4 v14 a4 x7 C1 a3 x2 C1 v1', [qw(ID walk_speed param1 param2 param3 type hair_style weapon lowhead shield tophead midhead hair_color clothes_color head_dir guildID sex coords act lv)]],
  391. $data .= pack("v1 a4 v1 x6 v1 x30", 0x78, $npcID1, 300, 114) . getCoordString($posX + 5, $posY + 3, 1) . pack("C2 x3", 0x05, 0x05);
  392. $data .= pack('v1 a4 C1 x1 C1', 0x9C, $npcID1, 0, 3);
  393. # Show a monster
  394. # '0078' => ['actor_display', 'a4 v14 a4 x7 C1 a3 x2 C1 v1', [qw(ID walk_speed param1 param2 param3 type hair_style weapon lowhead shield tophead midhead hair_color clothes_color head_dir guildID sex coords act lv)]],
  395. $data .= pack("v1 a4 v1 x6 v1 x30", 0x78, $monsterID, 1000, 1002) . getCoordString($posX - 2, $posY - 1, 1) . pack("x3 v1", 3);
  396. $data .= pack('v1 a4 C1 x1 C1', 0x9C, $monsterID, 0, 5);
  397. # Show an item on ground
  398. # '009D' => ['item_exists', 'a4 v1 x1 v3', [qw(ID type x y amount)]]
  399. $data .= pack("v1 a4 v1 x1 v3 x2", 0x9D, $itemID, 512, $posX + 1, $posY - 1, 1);
  400. # Let's not wait for the client to ask for the unit info
  401. # '0095' => ['actor_info', 'a4 Z24', [qw(ID name)]],
  402. $data .= pack("v1 a4 a24", 0x95, $monsterID, "Poring");
  403. }
  404. $client->send($data);
  405. } elsif (
  406. (($switch eq '007E') && (($clientdata{$index}{serverType} == 0) || ($clientdata{$index}{serverType} == 1) || ($clientdata{$index}{serverType} == 2) || ($clientdata{$index}{serverType} == 6) || ($clientdata{$index}{serverType} == 7) || ($clientdata{$index}{serverType} == 10) || ($clientdata{$index}{serverType} == 11))) ||
  407. (($switch eq '0089') && (($clientdata{$index}{serverType} == 3) || ($clientdata{$index}{serverType} == 5) || ($clientdata{$index}{serverType} == 8) || ($clientdata{$index}{serverType} == 9))) ||
  408. (($switch eq '0116') && ($clientdata{$index}{serverType} == 4)) ||
  409. (($switch eq '00A7') && ($clientdata{$index}{serverType} == 12))
  410. ) { # client sends sync packet
  411. my $data = pack("C*", 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00);
  412. $client->send($data);
  413. ### Check if packet 0228 got tangled up with the sync packet
  414. if (uc(unpack("H2", substr($msg, 7, 1))) . uc(unpack("H2", substr($msg, 6, 1))) eq '0228') {
  415. # queue the response (thanks abt123)
  416. $self->{response} = substr($msg, 6, length($msg));
  417. $self->{state} = 'requested';
  418. }
  419. } elsif ($switch eq '00B2') { # quit to character select screen
  420. if (unpack("C1", substr($msg, 2, 1)) == 1) {
  421. my $data = pack("C*", 0xB3, 0x00, 0x01);
  422. $client->send($data);
  423. }
  424. } elsif ($switch eq '0187') { # accountid sync (what does this do anyway?)
  425. $client->send($msg);
  426. } elsif ($switch eq '018A') { # client sends quit packet
  427. $self->{challengeNum} = 0;
  428. $client->send(pack("C*", 0x8B, 0x01, 0x00, 0x00));
  429. } elsif ($switch eq '0228') { # client sends game guard sync
  430. # Queue the response
  431. # Don't allow other packet's (like Sync) to get to RO server.
  432. my $length = unpack("v",substr($msg,2,2));
  433. if ($length > 0) {
  434. $self->{response} = substr($msg,0,$length+2);
  435. } else {
  436. $self->{response} = $msg;
  437. };
  438. $self->{state} = 'requested';
  439. } elsif ($switch eq '02A7') { # client sends hShield response
  440. # Queue the response
  441. $self->{response} = $msg;
  442. $self->{state} = 'requested';
  443. } elsif ($switch eq '0258') { # client sent gameguard's challenge request
  444. # Reply with "gameguard_grant" instead of a 0227 packet. Normally, the server would
  445. # send a 0227 gameguard challenge to the client, then the client will send the
  446. # proper 0228 response. Only after that will the server send 0259 to allow the
  447. # client to continue the login sequence. Since this is just a fake server,
  448. # there is no need to go through all that and we can do a shortcut.
  449. if ($self->{challengeNum} == 0) {
  450. print "Received GameGuard sync request. Client allowed to login account server.n";
  451. $client->send(pack("C*", 0x59, 0x02, 0x01));
  452. } else {
  453. print "Received GameGuard sync request. Client allowed to login char/map server.n";
  454. $client->send(pack("C*", 0x59, 0x02, 0x02));
  455. }
  456. $self->{challengeNum}++;
  457. #} elsif ($switch eq '0085') { # sendMove
  458. # print "Received packet $switch: sendMove.n";
  459. # visualDump($msg, "$switch");
  460. #} elsif ($switch eq '0089') { # sendAttack
  461. # print "Received packet $switch: sendAttack.n";
  462. # visualDump($msg, "$switch");
  463. #} elsif ($switch eq '008C') { # public chat
  464. # print "Received packet $switch: public chat.n";
  465. #} elsif ($switch eq '0094') { # getPlayerInfo
  466. # print "Received packet $switch: getPlayerInfo.n";
  467. } else {
  468. if ($switch eq '0090' || ($msg =~ /x90x0($npcID1|$npcID0)/)) { # npc talk
  469. undef $clientdata{$index}{npc_talk_code};
  470. if ($msg =~ /x90x0$npcID1/) {
  471. # Show the kafra image
  472. # '01B3' => ['npc_image', 'Z63 C1', [qw(npc_image type)]],
  473. my $data = pack("C2 a64 C1", 0xB3, 0x01, "kafra_04.bmp", 0x02);
  474. $client->send($data);
  475. # Show the messages
  476. $data = pack("v2 a4 a8", 0xB4, 16, $npcID1, "[Kafra]");
  477. $data .= pack("v2 a4 a62", 0xB4, 70, $npcID1, "Welcome to Kafra Corp. We will stay with you wherever you go.");
  478. $data .= pack("v1 a4", 0xB5, $npcID1);
  479. $client->send($data);
  480. } else {
  481. my $data = pack("v2 a4 a9", 0xB4, 17, $npcID0, "[Hakore]");
  482. $data .= pack("v2 a4 a93", 0xB4, 101, $npcID0, "Hello! I was examining your RO client's login packets while you were connecting to Poseidon.");
  483. $data .= pack("v1 a4", 0xB5, $npcID0);
  484. $client->send($data);
  485. }
  486. } elsif ($switch eq '00B8') { # npc talk response
  487. my $npcID = substr($msg, 2, 4);
  488. my $response = unpack("C1", substr($msg, 6, 1));
  489. if ($npcID eq $npcID0) {
  490. if ($response == 1) {
  491. # Check server info
  492. my $data = pack("v2 a4 a9", 0xB4, 17, $npcID, "[Hakore]");
  493. $data .= pack("v2 a4 a50", 0xB4, 58, $npcID, "Your RO client uses the following server details:");
  494. $data .= pack("v2 a4 a27", 0xB4, 35, $npcID, "^2222DDversion: $clientdata{$index}{version}");
  495. $data .= pack("v2 a4 a20", 0xB4, 28, $npcID, "master_version: $clientdata{$index}{master_version}");
  496. $data .= pack("v2 a4 a20", 0xB4, 28, $npcID, "serverType: " . ((defined $clientdata{$index}{serverType}) ? $clientdata{$index}{serverType} : 'Unknown'));
  497. if ($clientdata{$index}{secureLogin}) {
  498. $data .= pack("v2 a4 a15", 0xB4, 23, $npcID, "secureLogin: $clientdata{$index}{secureLogin}");
  499. if ($clientdata{$index}{secureLogin_requestCode}) {
  500. $data .= pack("v2 a4 a" . (length($clientdata{$index}{secureLogin_requestCode}) + 26), 0xB4, (length($clientdata{$index}{secureLogin_requestCode}) + 34), $npcID, "secureLogin_requestCode: $clientdata{$index}{secureLogin_requestCode}");
  501. } elsif (defined $clientdata{$index}{secureLogin_type}) {
  502. $data .= pack("v2 a4 a20", 0xB4, 28, $npcID, "secureLogin_type: $clientdata{$index}{secureLogin_type}");
  503. }
  504. if ($clientdata{$index}{secureLogin_account}) {
  505. $data .= pack("v2 a4 a25", 0xB4, 33, $npcID, "secureLogin_account: $clientdata{$index}{secureLogin_account}");
  506. }
  507. }
  508. if ($clientdata{$index}{masterLogin_packet}) {
  509. $data .= pack("v2 a4 a25", 0xB4, 33, $npcID, "masterLogin_packet: $clientdata{$index}{masterLogin_packet}");
  510. }
  511. if ($clientdata{$index}{gameLogin_packet}) {
  512. $data .= pack("v2 a4 a23", 0xB4, 31, $npcID, "gameLogin_packet: $clientdata{$index}{gameLogin_packet}");
  513. }
  514. $data .= pack("v1 a4", 0xB5, $npcID);
  515. $client->send($data);
  516. if (defined $clientdata{$index}{serverType}) {
  517. $clientdata{$index}{npc_talk_code} = 3;
  518. } else {
  519. $clientdata{$index}{npc_talk_code} = 2.5;
  520. }
  521. } elsif ($response == 2) {
  522. # Use storage
  523. my $data;
  524. $data = pack("v2 a4 a8", 0xB4, 16, $npcID, "[Hakore]");
  525. $data .= pack("v2 a4 a42", 0xB4, 50, $npcID, "Thank you for the visit. Go and multiply!");
  526. $data .= pack("v1 a4", 0xB6, $npcID);
  527. $client->send($data);
  528. }
  529. } elsif ($npcID eq $npcID1) {
  530. if ($response == 1) {
  531. # Use storage
  532. my $data;
  533. $data .= pack("C2 v1", 0xF0, 0x01, 40) .
  534. pack("v2 C2 v1 x10", 3, 501, 0, 1, 16) .
  535. pack("v2 C2 v1 x10", 4, 909, 3, 1, 144);
  536. $data .= pack("v3", 0xF2, 2, 300);
  537. $data .= pack("C2 a64 C1", 0xB3, 0x01, "kafra_04.bmp", 0xFF);
  538. $data .= pack("v1 a4", 0xB6, $npcID);
  539. $client->send($data);
  540. } elsif ($response == 2) {
  541. # Use storage
  542. my $data;
  543. $data = pack("v2 a4 a8", 0xB4, 16, $npcID, "[Kafra]");
  544. $data .= pack("v2 a4 a49", 0xB4, 57, $npcID, "We Kafra Corp. always try to serve you the best.");
  545. $data .= pack("v2 a4 a19", 0xB4, 27, $npcID, "Please come again.");
  546. $data .= pack("v1 a4", 0xB6, $npcID);
  547. $client->send($data);
  548. }
  549. }
  550. } elsif ($switch eq '00B9') { # npc talk continue
  551. my $npcID = substr($msg, 2, 4);
  552. if ($npcID eq $npcID0) {
  553. if ($clientdata{$index}{npc_talk_code} == 2) {
  554. # Show NPC response list
  555. my $data = pack("v2 a4 a24", 0xB7, 32, $npcID, "Yes, please:No, thanks:");
  556. $client->send($data);
  557. $clientdata{$index}{npc_talk_code} = 3;
  558. } else {
  559. my $data = pack("v2 a4 a9", 0xB4, 17, $npcID, "[Hakore]");
  560. if (!$clientdata{$index}{npc_talk_code}) {
  561. if (!defined $clientdata{$index}{serverType}) {
  562. $data .= pack("v2 a4 a71", 0xB4, 79, $npcID, "However, I regret that Openkore may not currently support your server.");
  563. } elsif ($clientdata{$index}{serverType} == 7 || $clientdata{$index}{serverType} == 12) {
  564. $data .= pack("v2 a4 a82", 0xB4, 90, $npcID, "However, I regret that Openkore does not yet fully support your server this time.");
  565. } else {
  566. $data .= pack("v2 a4 a64", 0xB4, 72, $npcID, "Based on my examination, I think Openkore supports your server.");
  567. $data .= pack("v2 a4 a99", 0xB4, 107, $npcID, "I can tell you the possible server details you can use to make Openkore to connect to your server.");
  568. }
  569. $data .= pack("v1 a4", 0xB5, $npcID);
  570. $clientdata{$index}{npc_talk_code} = 1;
  571. } elsif ($clientdata{$index}{npc_talk_code} == 1) {
  572. if ((!defined $clientdata{$index}{serverType}) || ($clientdata{$index}{serverType} == 7)) {
  573. $data .= pack("v2 a4 a42", 0xB4, 50, $npcID, "Would you still like to hear the details?");
  574. } else {
  575. $data .= pack("v2 a4 a36", 0xB4, 44, $npcID, "Would you like to hear the details?");
  576. }
  577. $data .= pack("v1 a4", 0xB5, $npcID);
  578. $clientdata{$index}{npc_talk_code} = 2;
  579. } elsif ($clientdata{$index}{npc_talk_code} == 2.5) {
  580. if (!defined $clientdata{$index}{serverType}) {
  581. $data .= pack("v2 a4 a68", 0xB4, 76, $npcID, "As you can see, I can't find a matching serverType for your server.");
  582. $data .= pack("v2 a4 a119", 0xB4, 127, $npcID, "Please make a trial-and-error using all available serverTypes, one of them might be able to work.");
  583. } elsif ($clientdata{$index}{serverType} == 7 || $clientdata{$index}{serverType} == 12) {
  584. $data .= pack("v2 a4 a65", 0xB4, 73, $npcID, "Like I said, your server is not yet fully supported by Openkore.");
  585. $data .= pack("v2 a4 a105", 0xB4, 113, $npcID, "You can login to the server and do most basic tasks, but you cannot attack, sit or stand, or use skills.");
  586. }
  587. $data .= pack("v1 a4", 0xB5, $npcID);
  588. $clientdata{$index}{npc_talk_code} = 4;
  589. } elsif ($clientdata{$index}{npc_talk_code} == 3) {
  590. $data .= pack("v2 a4 a103", 0xB4, 111, $npcID, "The values of ^2222DDip^000000 and ^2222DDport^000000 can be found on your client's (s)clientinfo.xml.");
  591. $data .= pack("v1 a4", 0xB5, $npcID);
  592. $clientdata{$index}{npc_talk_code} = 4;
  593. } elsif ($clientdata{$index}{npc_talk_code} == 4) {
  594. if (!defined $clientdata{$index}{serverType}) {
  595. $data .= pack("v2 a4 a135", 0xB4, 143, $npcID, "If none of the serverTypes work, please inform the developers about this so we can support your server in future releases of Openkore.");
  596. $data .= pack("v2 a4 a55", 0xB4, 63, $npcID, "Please visit ^2222DDhttp://forums.openkore.com/^000000");
  597. $data .= pack("v2 a4 a28", 0xB4, 36, $npcID, "Thank you.");
  598. } else {
  599. if (($clientdata{$index}{serverType} == 7)
  600. || ($clientdata{$index}{serverType} == 8)
  601. || ($clientdata{$index}{serverType} == 9)
  602. || ($clientdata{$index}{serverType} == 10)
  603. || ($clientdata{$index}{serverType} == 11)
  604. || ($clientdata{$index}{serverType} == 12)
  605. || ($clientdata{$index}{masterLogin_packet})
  606. || ($clientdata{$index}{gameLogin_packet})
  607. ) {
  608. $data .= pack("v2 a4 a73", 0xB4, 81, $npcID, "Please note that you can only connect to your server using Openkore SVN.");
  609. } else {
  610. $data .= pack("v2 a4 a52", 0xB4, 60, $npcID, "Openkore v.1.6.6 or later will work on your server.");
  611. }
  612. $data .= pack("v2 a4 a67", 0xB4, 75, $npcID, "For more info, please visit ^2222DDhttp://www.openkore.com/^000000");
  613. $data .= pack("v2 a4 a11", 0xB4, 19, $npcID, "Good luck!");
  614. }
  615. $data .= pack("v1 a4", 0xB6, $npcID);
  616. }
  617. $client->send($data);
  618. }
  619. } elsif ($npcID eq $npcID1) {
  620. # Show kafra response list
  621. my $data = pack("v2 a4 a20", 0xB7, 28, $npcID, "Use Storage:Cancel:");
  622. $client->send($data);
  623. }
  624. } elsif ($switch eq '0146') { # talk cancel
  625. my $npcID = substr($msg, 2, 4);
  626. if ($npcID eq $npcID1) {
  627. my $data = pack("C2 a64 C1", 0xB3, 0x01, "kafra_04.bmp", 0xFF);
  628. $client->send($data);
  629. }
  630. } elsif ($clientdata{$index}{mode}) {
  631. if (($switch eq '00F7' || $switch eq '0193') && (length($msg) == 2)) { # storage close
  632. my $data = pack("v1", 0xF8);
  633. $client->send($data);
  634. } elsif ($switch eq '00BF') { # emoticon
  635. my ($client, $code) = @_;
  636. my $data = pack("v1 a4", 0xC0, $accountID) . substr($msg, 2, 1);
  637. $clientdata{$index}{emoticonTime} = time;
  638. $client->send($data);
  639. } else {
  640. print "nReceived packet $switch:n";
  641. visualDump($msg, "$switch");
  642. # Just provide feedback in the RO Client about the unhandled packet
  643. # '008E' => ['self_chat', 'x2 Z*', [qw(message)]],
  644. my $data = pack("v2 a31", 0x8E, 35, "Sent packet $switch (" . length($msg) . " bytes).");
  645. if (timeOut($clientdata{$index}{emoticonTime}, 1.8)) {
  646. $clientdata{$index}{emoticonTime} = time;
  647. $data .= pack("v1 a4 C1", 0xC0, $accountID, 1);
  648. }
  649. # These following packets should reset the item inventory.
  650. # If you drop something from your inventory and the server didn't respond,
  651. # you will not be able to drop the item for the second test
  652. # This, however, does not cover item_use. YOu would have to relog
  653. # to test another item_use packet.
  654. #$data .= pack("v3", 0xAF, 3, 0);
  655. #$data .= pack("v3", 0xAF, 4, 0);
  656. # There are no other send packet that contains NPC ids as the last four byte
  657. # other than the talk and sendGetPlayerInfo packets.
  658. # Since most possible talk packets are handled above, we can assume that this is
  659. # a sendGetPlayerInfo packet.
  660. # Note that we have an NPC that is not named initially to allow a
  661. # sendGetPlayerInfo packet to be captured.)
  662. # '0095' => ['actor_info', 'a4 Z24', [qw(ID name)]],
  663. my $ID = substr($msg, length($msg) - 4, 4);
  664. if ($ID eq $npcID0) {
  665. $data .= pack("v1 a4 a24", 0x95, $npcID0, "Server Details Guide");
  666. } elsif ($ID eq $npcID1) {
  667. $data .= pack("v1 a4 a24", 0x95, $npcID1, "Kafra");
  668. }
  669. $client->send($data);
  670. }
  671. }
  672. }
  673. }
  674. sub mapLogin {
  675. my ($self, $client, $msg, $index) = @_;
  676. # '0073' => ['map_loaded','x4 a3',[qw(coords)]]
  677. my $data = $accountID .
  678. pack("C*", 0x73, 0x00, 0x31, 0x63, 0xe9, 0x02) .
  679. getCoordString($posX, $posY, 1) .
  680. pack("C*", 0x05, 0x05);
  681. if ($clientdata{$index}{mode}) {
  682. $data .= pack("C2 v1", 0x0F, 0x01, 226) .
  683. # skillID targetType level sp range skillName
  684. pack("v2 x2 v3 a24 C1", 1, 0, 9, 0, 1, "NV_BASIC" . chr(0) . "GetMapInfo" . chr(0x0A), 0) .
  685. pack("v2 x2 v3 a24 C1", 24, 4, 1, 10, 10, "AL_RUWACH", 0) . # self skill test
  686. pack("v2 x2 v3 a24 C1", 25, 2, 1, 10, 9, "AL_PNEUMA", 0) . # location skill test
  687. pack("v2 x2 v3 a24 C1", 26, 4, 2, 9, 1, "AL_TELEPORT", 0) . # self skill test
  688. pack("v2 x2 v3 a24 C1", 27, 2, 4, 26, 9, "AL_WARP", 0) . # location skill test
  689. pack("v2 x2 v3 a24 C1", 28, 16, 10, 40, 9, "AL_HEAL", 0); # target skill test
  690. }
  691. $client->send($data);
  692. $client->{connectedToMap} = 1;
  693. }
  694. 1;