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

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. #  OpenKore - X-Kore Mode 2
  3. #  Copyright (c) 2007 OpenKore developers
  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. ##
  13. # MODULE DESCRIPTION: Map server implementation.
  14. package Network::XKore2::MapServer;
  15. use strict;
  16. use Globals qw(
  17. $char $field %skillsStatus @skillsID @itemsID %items
  18. $portalsList $npcsList $monstersList $playersList $petsList
  19. @friendsID %friends %pet @partyUsersID %spells
  20. @chatRoomsID %chatRooms @venderListsID %venderLists $hotkeyList
  21. );
  22. use Base::Ragnarok::MapServer;
  23. use base qw(Base::Ragnarok::MapServer);
  24. use Network::MessageTokenizer;
  25. use I18N qw(stringToBytes);
  26. use Utils qw(shiftPack);
  27. use Log qw(debug);
  28. # Overrided method.
  29. sub onClientNew {
  30. my ($self, $client, $index) = @_;
  31. $self->SUPER::onClientNew($client, $index);
  32. # In here we store messages that the RO client wants to
  33. # send to the server.
  34. $client->{outbox} = new Network::MessageTokenizer($self->getRecvPackets());
  35. }
  36. # Overrided method.
  37. sub getCharInfo {
  38. my ($self, $session) = @_;
  39. if ($char && $field && !$session->{dummy}) {
  40. return {
  41. map => $field->name() . ".gat",
  42. x => $char->{pos_to}{x},
  43. y => $char->{pos_to}{y}
  44. };
  45. } else {
  46. $session->{dummy} = 1;
  47. return Base::Ragnarok::MapServer::DUMMY_POSITION;
  48. }
  49. }
  50. sub handleMapLoaded {
  51. # The RO client has finished loading the map.
  52. # Send character information to the RO client.
  53. my ($self, $client) = @_;
  54. no encoding 'utf8';
  55. use bytes;
  56. my $char;
  57. # TODO: Character vending, character in chat, character in deal
  58. # TODO: Cart Items, Guild Notice
  59. # TODO: Fix walking speed? Might that be part of the map login packet? Or 00BD?
  60. if (!$client->{session}) {
  61. $client->close();
  62. return;
  63. } elsif ($client->{session}{dummy}) {
  64. $char = Base::Ragnarok::CharServer::DUMMY_CHARACTER;
  65. } elsif ($Globals::char) {
  66. $char = $Globals::char;
  67. } else {
  68. $char = Base::Ragnarok::CharServer::DUMMY_CHARACTER;
  69. $client->{session}{dummy} = 1;
  70. }
  71. # Do this just in case $client->{session}{dummy} was set after
  72. # the user logs in.
  73. $char->{ID} = $client->{session}{accountID};
  74. my $output = '';
  75. # Player stats.
  76. $output .= pack('C2 v1 C12 v12 x4',
  77. 0xBD, 0x00,
  78. $char->{points_free},
  79. $char->{str}, $char->{points_str}, $char->{agi}, $char->{points_agi},
  80. $char->{vit}, $char->{points_vit}, $char->{int}, $char->{points_int}, $char->{dex},
  81. $char->{points_dex}, $char->{luk}, $char->{points_luk},
  82. $char->{attack}, $char->{attack_bonus},
  83. $char->{attack_magic_min}, $char->{attack_magic_max},
  84. $char->{def}, $char->{def_bonus},
  85. $char->{def_magic}, $char->{def_magic_bonus},
  86. $char->{hit},
  87. $char->{flee},
  88. $char->{flee_bonus}, $char->{critical}
  89. );
  90. $client->send($output);
  91. # More stats
  92. $output  = pack('C2 v V', 0xB0, 0x00, 0, $char->{walk_speed} * 1000); # Walk speed
  93. $output .= pack('C2 v V', 0xB0, 0x00, 5, $char->{hp}); # Current HP
  94. $output .= pack('C2 v V', 0xB0, 0x00, 6, $char->{hp_max}); # Max HP
  95. $output .= pack('C2 v V', 0xB0, 0x00, 7, $char->{sp}); # Current SP
  96. $output .= pack('C2 v V', 0xB0, 0x00, 8, $char->{sp_max}); # Max SP
  97. $output .= pack('C2 v V', 0xB0, 0x00, 12, $char->{points_skill}); # Skill points left
  98. $output .= pack('C2 v V', 0xB0, 0x00, 24, $char->{weight} * 10); # Current weight
  99. $output .= pack('C2 v V', 0xB0, 0x00, 25, $char->{weight_max} * 10); # Max weight
  100. $output .= pack('C2 v V', 0xB0, 0x00, 53, $char->{attack_delay}); # Attack speed
  101. $client->send($output);
  102. # Base stat info (str, agi, vit, int, dex, luk) this time with bonus
  103. $output  = pack('C2 V3', 0x41, 0x01, 13, $char->{str}, $char->{str_bonus});
  104. $output .= pack('C2 V3', 0x41, 0x01, 14, $char->{agi}, $char->{agi_bonus});
  105. $output .= pack('C2 V3', 0x41, 0x01, 15, $char->{vit}, $char->{vit_bonus});
  106. $output .= pack('C2 V3', 0x41, 0x01, 16, $char->{int}, $char->{int_bonus});
  107. $output .= pack('C2 V3', 0x41, 0x01, 17, $char->{dex}, $char->{dex_bonus});
  108. $output .= pack('C2 V3', 0x41, 0x01, 18, $char->{luk}, $char->{luk_bonus});
  109. $client->send($output);
  110. # Make the character face the correct direction
  111. $client->send(pack('C2 a4 C1 x1 C1', 0x9C, 0x00,
  112. $char->{ID}, $char->{look}{head}, $char->{look}{body})
  113. );
  114. # Send attack range
  115. $output  = pack('C2 v', 0x3A, 0x01, $char->{attack_range});
  116. # Send weapon/shield appearance
  117. $output .= pack('C2 a4 C v2', 0xD7, 0x01, $char->{ID}, 2, $char->{weapon}, $char->{shield});
  118. # Send status info
  119. $output .= pack('C2 a4 v3 x', 0x19, 0x01, $char->{ID}, $char->{param1}, $char->{param2}, $char->{param3});
  120. $client->send($output);
  121. # Send more status information
  122. # TODO: Find a faster/better way of doing this? This seems cumbersome.
  123. $output = '';
  124. foreach my $ID (keys %{$char->{statuses}}) {
  125. while (my ($statusID, $statusName) = each %skillsStatus) {
  126. if ($ID eq $statusName) {
  127. $output .= pack('C2 v a4 C', 0x96, 0x01, $statusID, $char->{ID}, 1);
  128. }
  129. }
  130. }
  131. $client->send($output) if (length($output) > 0);
  132. # Send spirit sphere information
  133. $output  = pack('C2 a4 v', 0xD0, 0x01, $char->{ID}, $char->{spirits}) if ($char->{spirits});
  134. # Send exp-required-to-level-up info
  135. $output .= pack('C2 v V', 0xB1, 0x00, 22, $char->{exp_max});
  136. $output .= pack('C2 v V', 0xB1, 0x00, 23, $char->{exp_job_max});
  137. $client->send($output);
  138. # Send skill information
  139. $output = '';
  140. foreach my $ID (@skillsID) {
  141. $output .= pack('v2 x2 v3 a24 C',
  142. $char->{skills}{$ID}{ID}, $char->{skills}{$ID}{targetType},
  143. $char->{skills}{$ID}{lv}, $char->{skills}{$ID}{sp},
  144. $char->{skills}{$ID}{range}, $ID, $char->{skills}{$ID}{up});
  145. }
  146. $output = pack('C2 v', 0x0F, 0x01, length($output) + 4) . $output;
  147. $client->send($output);
  148. # Send Hotkeys
  149. $output = '';
  150. $output .=  pack('C2', 0xB9, 0x02);
  151. for (my $i = 0; $i < @{$hotkeyList}; $i++) {
  152. $output .= pack('C1 V1 v1', $hotkeyList->[$i]->{type}, $hotkeyList->[$i]->{ID}, $hotkeyList->[$i]->{lvl});
  153. }
  154. $client->send($output) if (@{$hotkeyList});
  155. # Sort items into stackable and non-stackable
  156. if (UNIVERSAL::isa($char, 'Actor::You')) {
  157. my @stackable;
  158. my @nonstackable;
  159. foreach my $item (@{$char->inventory->getItems()}) {
  160. if ($item->{type} <= 3 || $item->{type} == 6 || $item->{type} == 10 || $item->{type} == 16) {
  161. push @stackable, $item;
  162. } else {
  163. push @nonstackable, $item;
  164. }
  165. }
  166. # Send stackable item information
  167. $output = '';
  168. foreach my $item (@stackable) {
  169. $output .= pack('v2 C2 v1 x2',
  170. $item->{index},
  171. $item->{nameID},
  172. $item->{type},
  173. 1,  # identified
  174. $item->{amount}
  175. );
  176. }
  177. $output = pack('C2 v', 0xA3, 0x00, length($output) + 4) . $output;
  178. $client->send($output);
  179. # Send non-stackable item (mostly equipment) information
  180. $output = '';
  181. foreach my $item (@nonstackable) {
  182. $output .= pack('v2 C2 v2 C2 a8',
  183. $item->{index}, $item->{nameID}, $item->{type},
  184. $item->{identified}, $item->{type_equip}, $item->{equipped}, $item->{broken},
  185. $item->{upgrade}, $item->{cards});
  186. }
  187. $output = pack('C2 v', 0xA4, 0x00, length($output) + 4) . $output;
  188. $client->send($output);
  189. }
  190. # Send equipped arrow information
  191. $client->send(pack('C2 v', 0x3C, 0x01, $char->{arrow})) if ($char->{arrow});
  192. # Send info about items on the ground
  193. $output = '';
  194. foreach my $ID (@itemsID) {
  195. next if !defined($ID) || !$items{$ID};
  196. $output .= pack('C2 a4 v x v3 x2', 0x9D, 0x00,
  197. $ID, $items{$ID}{nameID},
  198. $items{$ID}{pos}{x}, $items{$ID}{pos}{y}, $items{$ID}{amount});
  199. }
  200. $client->send($output) if (length($output) > 0);
  201. # Send all portal info
  202. $output = '';
  203. foreach my $portal (@{$portalsList->getItems()}) {
  204. my $coords = '';
  205. shiftPack($coords, $portal->{pos}{x}, 10);
  206. shiftPack($coords, $portal->{pos}{y}, 10);
  207. shiftPack($coords, 0, 4);
  208. $output .= pack('C2 a4 x8 v1 x30 a3 x5', 0x78, 0x00,
  209. $portal->{ID}, $portal->{type}, $coords);
  210. }
  211. $client->send($output) if (length($output) > 0);
  212. # Send all NPC info
  213. $output = '';
  214. foreach my $npc (@{$npcsList->getItems()}) {
  215. my $coords = '';
  216. shiftPack($coords, $npc->{pos}{x}, 10);
  217. shiftPack($coords, $npc->{pos}{y}, 10);
  218. shiftPack($coords, $npc->{look}{body}, 4);
  219. $output .= pack('C2 a4 x2 v4 x30 a3 x5',
  220. 0x78, 0x00, $npc->{ID},
  221. $npc->{param1}, $npc->{param2}, $npc->{param3},
  222. $npc->{type}, $coords);
  223. }
  224. $client->send($output) if (length($output) > 0);
  225. # Send all monster info
  226. $output = '';
  227. foreach my $monster (@{$monstersList->getItems()}) {
  228. my $coords = '';
  229. shiftPack($coords, $monster->{pos_to}{x}, 10);
  230. shiftPack($coords, $monster->{pos_to}{y}, 10);
  231. shiftPack($coords, $monster->{look}{body}, 4);
  232. $output .= pack('C2 a4 v5 x30 a3 x3 v1',
  233. 0x78, 0x00, $monster->{ID}, $monster->{walk_speed} * 1000,
  234. $monster->{param1}, $monster->{param2}, $monster->{param3},
  235. $monster->{nameID}, $coords, $monster->{lv});
  236. }
  237. $client->send($output) if (length($output) > 0);
  238. # Send info about pets
  239. $output = '';
  240. foreach my $pet (@{$petsList->getItems()}) {
  241. my $coords = '';
  242. shiftPack($coords, $pet->{pos_to}{x}, 10);
  243. shiftPack($coords, $pet->{pos_to}{y}, 10);
  244. shiftPack($coords, $pet->{look}{body}, 4);
  245. $output .= pack('C2 a4 v x6 v2 x28 a3 x3 v',
  246. 0x78, 0x00, $pet->{ID}, $pet->{walk_speed} * 1000,
  247. $pet->{nameID}, $pet->{hair_style}, $coords, $pet->{lv});
  248. }
  249. $client->send($output) if (length($output) > 0);
  250. # Send info about surrounding players
  251. $output = '';
  252. foreach my $player (@{$playersList->getItems()}) {
  253. my $coords = '';
  254. shiftPack($coords, $player->{pos_to}{x}, 10);
  255. shiftPack($coords, $player->{pos_to}{y}, 10);
  256. shiftPack($coords, $player->{look}{body}, 4);
  257. $output .= pack('C2 a4 v4 x2 v8 x2 v a4 a4 v x2 C2 a3 x2 C v',
  258. 0x2A, 0x02, $player->{ID}, $player->{walk_speed} * 1000,
  259. $player->{param1}, $player->{param2}, $player->{param3},
  260. $player->{jobID}, $player->{hair_style}, $player->{weapon}, $player->{shield},
  261. $player->{headgear}{low}, $player->{headgear}{top}, $player->{headgear}{mid},
  262. $player->{hair_color}, $player->{look}{head}, $player->{guildID}, $player->{guildEmblem},
  263. $player->{visual_effects}, $player->{stance}, $player->{sex}, $coords,
  264. ($player->{dead}? 1 : ($player->{sitting}? 2 : 0)), $player->{lv});
  265. }
  266. $client->send($output) if (length($output) > 0);
  267. # Send vendor list
  268. $output = '';
  269. foreach my $ID (@venderListsID) {
  270. next if !defined($ID) || !$venderLists{$ID};
  271. $output .= pack('C2 a4 a30 x50', 0x31, 0x01, $ID, $venderLists{$ID}{title});
  272. }
  273. $client->send($output) if (length($output) > 0);
  274. # Send chatrooms
  275. $output = '';
  276. foreach my $ID (@chatRoomsID) {
  277. next if !defined($ID) || !$chatRooms{$ID} || !$chatRooms{$ID}{ownerID};
  278. # '00D7' => ['chat_info', 'x2 a4 a4 v1 v1 C1 a*', [qw(ownerID ID limit num_users public title)]],
  279. my $chatMsg = pack('a4 a4 v2 C1 a* x1',
  280. $chatRooms{$ID}{ownerID}, $ID, $chatRooms{$ID}{limit},
  281. $chatRooms{$ID}{num_users}, $chatRooms{$ID}{public},
  282. $chatRooms{$ID}{title});
  283. $output .= pack('C2 v', 0xD7, 0x00, length($chatMsg) + 4) . $chatMsg;
  284. }
  285. $client->send($output) if (length($output) > 0);
  286. # Send active ground effect skills
  287. $output = '';
  288. foreach my $ID (@skillsID) {
  289. next if !defined($ID) || !$spells{$ID};
  290. $output .= pack('C2 a4 a4 v2 C2 x81', 0xC9, 0x01,
  291. $ID, $spells{$ID}{sourceID},
  292. $spells{$ID}{pos}{x}, $spells{$ID}{pos}{y}, $spells{$ID}{type},
  293. $spells{$ID}{fail});
  294. }
  295. $client->send($output) if (length($output) > 0);
  296. # Send friend list
  297. my ($friendMsg, $friendOnlineMsg);
  298. foreach my $ID (@friendsID) {
  299. next if !defined($ID) || !$friends{$ID};
  300. $friendMsg .= pack('a4 a4 Z24',
  301. $friends{$ID}{accountID},
  302. $friends{$ID}{charID},
  303. stringToBytes($friends{$ID}{name}));
  304. if ($friends{$ID}{online}) {
  305. $friendOnlineMsg .= pack('C2 a4 a4 C',
  306. 0x06, 0x02,
  307. $friends{$ID}{accountID},
  308. $friends{$ID}{charID},
  309. 0);
  310. };
  311. }
  312. $output = pack('C2 v', 0x01, 0x02, length($friendMsg) + 4) . $friendMsg;
  313. $client->send($output);
  314. $client->send($friendOnlineMsg);
  315. undef $friendMsg;
  316. undef $friendOnlineMsg;
  317. # Send party list
  318. if ($char->{party}) {
  319. my $num = 0;
  320. $output = '';
  321. foreach my $ID (@partyUsersID) {
  322. next if !defined($ID) || !$char->{party}{users}{$ID};
  323. if (!$char->{party}{users}{$ID}{admin}) {
  324. $num++;
  325. }
  326. $output .= pack("a4 Z24 Z16 C2",
  327. $ID, stringToBytes($char->{party}{users}{$ID}{name}),
  328. $char->{party}{users}{$ID}{map},
  329. $char->{party}{users}{$ID}{admin} ? 0 : $num,
  330. 1 - $char->{party}{users}{$ID}{online});
  331. }
  332. $output = pack('C2 v Z24', 0xFB, 0x00,
  333. length($output) + 28,
  334. stringToBytes($char->{party}{name})) .
  335. $output;
  336. $client->send($output);
  337. }
  338. # Send pet information
  339. if (defined $pet{ID}) {
  340. $output  = pack('C2 C a4 V', 0xA4, 0x01, 0, $pet{ID}, 0);
  341. $output .= pack('C2 C a4 V', 0xA4, 0x01, 5, $pet{ID}, 0x64);
  342. $output .= pack('C2 Z24 C v4', 0xA2, 0x01,
  343. stringToBytes($pet{name}), $pet{nameflag}, $pet{level},
  344. $pet{hungry}, $pet{friendly}, $pet{accessory});
  345. $client->send($output);
  346. }
  347. # Send guild info
  348. if ($char->{guildID}) {
  349. $output = pack('C2 V3 x5 Z24', 0x6C, 0x01,
  350. $char->{guildID}, $char->{guild}{emblem}, $char->{guild}{mode},
  351. stringToBytes($char->{guild}{name}));
  352. $client->send($output);
  353. }
  354. # Send "sitting" if the char is sitting
  355. if ($char->{sitting}) {
  356. $client->send(pack('C2 a4 x20 C1 x2', 0x8A, 0x00, $char->{ID}, 2));
  357. }
  358.    #Hack to Avoid Sprite Error Crash if you are level 99
  359.    #This is acomplished by sending gm hide and unhiding again
  360.    $output = pack('C15', 0x29, 0x02, 0xA7, 0x94, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40);
  361.    $client->send($output);
  362.    $output = pack('C15', 0x29, 0x02, 0xA7, 0x94, 0x04);
  363.    $client->send($output);
  364. }
  365. sub process_007D {
  366. my ($self, $client) = @_;
  367. handleMapLoaded($self, $client);
  368. }
  369. sub process_01C0 {
  370. my ($self, $client) = @_;
  371. handleMapLoaded($self, $client);
  372. }
  373. sub process_00B2 {
  374. my ($self, $client) = @_;
  375. # If they want to character select/respawn, kick them to the login screen
  376. # immediately (GM kick)
  377. $client->send(pack('C3', 0x81, 0, 15));
  378. }
  379. sub process_018A {
  380. my ($self, $client) = @_;
  381. # Client wants to quit
  382. $client->send(pack('C*', 0x8B, 0x01, 0, 0));
  383. }
  384. sub handleSync {
  385. my ($self, $client, $message) = @_;
  386. my $ID = Network::MessageTokenizer::getMessageID($message);
  387. my $serverType = $self->getServerType();
  388. if (
  389. ($ID eq "007E" && (
  390. $serverType == 0 ||
  391. $serverType == 1 ||
  392. $serverType == 2 ||
  393. $serverType == 6 ||
  394. $serverType == 21)
  395. )
  396. || ($ID eq "0089" && (
  397. $serverType == 3 ||
  398. $serverType == 5 ||
  399. $serverType == 8)
  400. )
  401. || ($ID eq "0116" &&
  402. $serverType == 4 )
  403. || ($ID eq "00A7" &&
  404. $serverType == 18)
  405. ) {
  406. # Surpress client sync message.
  407. } else {
  408. &unhandledMessage;
  409. }
  410. }
  411. sub process_007E { &handleSync; }
  412. sub process_0089 { &handleSync; }
  413. sub process_0116 { &handleSync; }
  414. sub process_00A7 { &handleSync }
  415. # Not sure what these are, but don't let it get to the RO server.
  416. sub process_021D {}
  417. sub process_014D {}
  418. sub process_014F {}
  419. sub process_0181 {}
  420. sub unhandledMessage {
  421. my ($self, $client, $message) = @_;
  422. $client->{outbox}->add($message);
  423. }
  424. 1;