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

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. # This software is open source, licensed under the GNU General Public
  3. # License, version 2.
  4. # Basically, this means that you're allowed to modify and distribute
  5. # this software. However, if you distribute modified versions, you MUST
  6. # also distribute the source code.
  7. # See http://www.gnu.org/licenses/gpl.html for the full license.
  8. #########################################################################
  9. package main;
  10. use strict;
  11. use Time::HiRes qw(time usleep);
  12. use IO::Socket;
  13. use Text::ParseWords;
  14. use Carp::Assert;
  15. use Data::YAML::Writer;
  16. use Config;
  17. use encoding 'utf8';
  18. use Globals;
  19. use Modules;
  20. use Settings qw(%sys);
  21. use Log qw(message warning error debug);
  22. use Interface;
  23. use Network::Receive;
  24. use Network::Send ();
  25. use Network::PaddedPackets;
  26. use Network::MessageTokenizer;
  27. use Commands;
  28. use Misc;
  29. use Plugins;
  30. use Utils;
  31. use ChatQueue;
  32. use I18N;
  33. use Utils::Benchmark;
  34. use Utils::HttpReader;
  35. #######################################
  36. # PROGRAM INITIALIZATION
  37. #######################################
  38. use constant {
  39. STATE_LOAD_PLUGINS          => 0,
  40. STATE_LOAD_DATA_FILES       => 1,
  41. STATE_INIT_NETWORKING       => 2,
  42. STATE_INIT_PORTALS_DATABASE => 3,
  43. STATE_PROMPT                => 4,
  44. STATE_FINAL_INIT            => 5,
  45. STATE_INITIALIZED           => 6
  46. };
  47. our $state;
  48. sub mainLoop {
  49. Benchmark::begin('mainLoop') if DEBUG;
  50. $state = STATE_LOAD_PLUGINS if (!defined $state);
  51. # Parse command input
  52. my $input;
  53. if (defined($input = $interface->getInput(0))) {
  54. Misc::checkValidity("parseInput (pre)");
  55. parseInput($input);
  56. Misc::checkValidity("parseInput");
  57. }
  58. if ($state == STATE_INITIALIZED) {
  59. Plugins::callHook('mainLoop_pre');
  60. mainLoop_initialized();
  61. Plugins::callHook('mainLoop_post');
  62. } elsif ($state == STATE_LOAD_PLUGINS) {
  63. Log::message("$Settings::versionTextn");
  64. loadPlugins();
  65. Log::message("n");
  66. Plugins::callHook('start');
  67. $state = STATE_LOAD_DATA_FILES;
  68. } elsif ($state == STATE_LOAD_DATA_FILES) {
  69. loadDataFiles();
  70. $state = STATE_INIT_NETWORKING;
  71. } elsif ($state == STATE_INIT_NETWORKING) {
  72. initNetworking();
  73. $state = STATE_INIT_PORTALS_DATABASE;
  74. } elsif ($state == STATE_INIT_PORTALS_DATABASE) {
  75. initPortalsDatabase();
  76. $state = STATE_PROMPT;
  77. } elsif ($state == STATE_PROMPT) {
  78. promptFirstTimeInformation();
  79. $state = STATE_FINAL_INIT;
  80. } elsif ($state == STATE_FINAL_INIT) {
  81. finalInitialization();
  82. $state = STATE_INITIALIZED;
  83. } else {
  84. die "Unknown state $state.";
  85. }
  86. Benchmark::end('mainLoop') if DEBUG;
  87. # Reload any modules that requested to be reloaded
  88. Modules::reloadAllInQueue();
  89. }
  90. sub loadPlugins {
  91. eval {
  92. Plugins::loadAll();
  93. };
  94. if (my $e = caught('Plugin::LoadException')) {
  95. $interface->errorDialog(TF("This plugin cannot be loaded because of a problem in the plugin. " .
  96. "Please notify the plugin's author about this problem, " .
  97. "or remove the plugin so %s can start.nn" .
  98. "The error message is:n" .
  99. "%s",
  100. $Settings::NAME, $e->message));
  101. exit 1;
  102. } elsif (my $e = caught('Plugin::DeniedException')) {
  103. $interface->errorDialog($e->message);
  104. exit 1;
  105. } elsif ($@) {
  106. die $@;
  107. }
  108. }
  109. sub loadDataFiles {
  110. # These pragmas are necessary in order to support non-ASCII filenames.
  111. # If we use UTF-8 strings then Perl will think the file doesn't exist,
  112. # if $Settings::control_folder or $Settings::tables_folder contains
  113. # non-ASCII characters.
  114. no encoding 'utf8';
  115. Settings::addControlFile(Settings::getConfigFilename(),
  116. loader => [&parseConfigFile, %config],
  117. autoSearch => 0);
  118. Settings::addControlFile(Settings::getMonControlFilename(),
  119. loader => [&parseMonControl, %mon_control],
  120. autoSearch => 0);
  121. Settings::addControlFile(Settings::getItemsControlFilename(),
  122. loader => [&parseItemsControl, %items_control],
  123. autoSearch => 0);
  124. Settings::addControlFile(Settings::getShopFilename(),
  125. loader => [&parseShopControl, %shop],
  126. autoSearch => 0);
  127. Settings::addControlFile('overallAuth.txt', loader => [&parseDataFile, %overallAuth]);
  128. Settings::addControlFile('pickupitems.txt', loader => [&parseDataFile_lc, %pickupitems]);
  129. Settings::addControlFile('responses.txt',   loader => [&parseResponses, %responses]);
  130. Settings::addControlFile('timeouts.txt',    loader => [&parseTimeouts, %timeout]);
  131. Settings::addControlFile('chat_resp.txt',   loader => [&parseChatResp, @chatResponses]);
  132. Settings::addControlFile('avoid.txt',       loader => [&parseAvoidControl, %avoid]);
  133. Settings::addControlFile('priority.txt',    loader => [&parsePriority, %priority]);
  134. Settings::addControlFile('consolecolors.txt', loader => [&parseSectionedFile, %consoleColors]);
  135. Settings::addControlFile('routeweights.txt',  loader => [&parseDataFile, %routeWeights]);
  136. Settings::addControlFile('arrowcraft.txt',  loader => [&parseDataFile_lc, %arrowcraft_items]);
  137. Settings::addTableFile(Settings::getRecvPacketsFilename(),
  138. loader => [&parseDataFile2, %rpackets],
  139. autoSearch => 0);
  140. Settings::addTableFile('cities.txt',      loader => [&parseROLUT, %cities_lut]);
  141. Settings::addTableFile('commanddescriptions.txt', loader => [&parseCommandsDescription, %descriptions]);
  142. Settings::addTableFile('directions.txt',  loader => [&parseDataFile2, %directions_lut]);
  143. Settings::addTableFile('elements.txt',    loader => [&parseROLUT, %elements_lut]);
  144. Settings::addTableFile('emotions.txt',    loader => [&parseEmotionsFile, %emotions_lut]);
  145. Settings::addTableFile('equiptypes.txt',  loader => [&parseDataFile2, %equipTypes_lut]);
  146. Settings::addTableFile('haircolors.txt',  loader => [&parseDataFile2, %haircolors]);
  147. Settings::addTableFile('headgears.txt',   loader => [&parseArrayFile, @headgears_lut]);
  148. Settings::addTableFile('items.txt',       loader => [&parseROLUT, %items_lut]);
  149. Settings::addTableFile('itemsdescriptions.txt',   loader => [&parseRODescLUT, %itemsDesc_lut]);
  150. Settings::addTableFile('itemslots.txt',   loader => [&parseROSlotsLUT, %itemSlots_lut]);
  151. Settings::addTableFile('itemslotcounttable.txt',  loader => [&parseROLUT, %itemSlotCount_lut]);
  152. Settings::addTableFile('itemtypes.txt',   loader => [&parseDataFile2, %itemTypes_lut]);
  153. Settings::addTableFile('maps.txt',        loader => [&parseROLUT, %maps_lut]);
  154. Settings::addTableFile('monsters.txt',    loader => [&parseDataFile2, %monsters_lut]);
  155. Settings::addTableFile('npcs.txt',        loader => [&parseNPCs, %npcs_lut]);
  156. Settings::addTableFile('packetdescriptions.txt',  loader => [&parseSectionedFile, %packetDescriptions]);
  157. Settings::addTableFile('portals.txt',     loader => [&parsePortals, %portals_lut]);
  158. Settings::addTableFile('portalsLOS.txt',  loader => [&parsePortalsLOS, %portals_los]);
  159. Settings::addTableFile('servers.txt',     loader => [&parseSectionedFile, %masterServers]);
  160. Settings::addTableFile('sex.txt',         loader => [&parseDataFile2, %sex_lut]);
  161. Settings::addTableFile('skills.txt',      loader => &Skill::StaticInfo::parseSkillsDatabase);
  162. Settings::addTableFile('spells.txt',      loader => [&parseDataFile2, %spells_lut]);
  163. Settings::addTableFile('skillsdescriptions.txt',  loader => [&parseRODescLUT, %skillsDesc_lut]);
  164. Settings::addTableFile('skillssp.txt',    loader => [&parseSkillsSPLUT, %skillsSP_lut]);
  165. Settings::addTableFile('skillssp.txt',    loader => &Skill::StaticInfo::parseSPDatabase);
  166. Settings::addTableFile('skillsstatus.txt',        loader => [&parseDataFile2, %skillsStatus]);
  167. Settings::addTableFile('skillsailments.txt',      loader => [&parseDataFile2, %skillsAilments]);
  168. Settings::addTableFile('skillsstate.txt', loader => [&parseDataFile2, %skillsState]);
  169. Settings::addTableFile('skillslooks.txt', loader => [&parseDataFile2, %skillsLooks]);
  170. Settings::addTableFile('skillsarea.txt',  loader => [&parseDataFile2, %skillsArea]);
  171. Settings::addTableFile('skillsencore.txt',        loader => [&parseList, %skillsEncore]);
  172. use encoding 'utf8';
  173. Plugins::callHook('start2');
  174. eval {
  175. my $progressHandler = sub {
  176. my ($filename) = @_;
  177. message TF("Loading %s...n", $filename);
  178. };
  179. Settings::loadAll($progressHandler);
  180. };
  181. if (my $e = caught('UTF8MalformedException')) {
  182. $interface->errorDialog(TF(
  183. "The file %s must be valid UTF-8 encoded, which it is n" .
  184. "currently not. To solve this prolem, please use Notepadn" .
  185. "to save that file as valid UTF-8.",
  186. $e->textfile));
  187. exit 1;
  188. } elsif (my $e = caught('FileNotFoundException')) {
  189. $interface->errorDialog(TF("Unable to load the file %s.", $e->filename));
  190. exit 1;
  191. } elsif ($@) {
  192. die $@;
  193. }
  194. Plugins::callHook('start3');
  195. if ($config{'adminPassword'} eq 'x' x 10) {
  196. Log::message(T("nAuto-generating Admin Password due to default...n"));
  197. configModify("adminPassword", vocalString(8));
  198. #} elsif ($config{'adminPassword'} eq '') {
  199. # # This is where we protect the stupid from having a blank admin password
  200. # Log::message(T("nAuto-generating Admin Password due to blank...n"));
  201. # configModify("adminPassword", vocalString(8));
  202. } elsif ($config{'secureAdminPassword'} eq '1') {
  203. # This is where we induldge the paranoid and let them have session generated admin passwords
  204. Log::message(T("nGenerating session Admin Password...n"));
  205. configModify("adminPassword", vocalString(8));
  206. }
  207. Log::message("n");
  208. }
  209. sub initNetworking {
  210. our $XKore_dontRedirect = 0;
  211. my $XKore_version = $config{XKore} ? $config{XKore} : $sys{XKore};
  212. eval {
  213. if ($XKore_version eq "1" || $XKore_version eq "inject") {
  214. # Inject DLL to running Ragnarok process
  215. require Network::XKore;
  216. $net = new Network::XKore;
  217. } elsif ($XKore_version eq "2") {
  218. # Run as a proxy bot, allowing Ragnarok to connect while botting
  219. require Network::DirectConnection;
  220. require Network::XKore2;
  221. $net = new Network::DirectConnection;
  222. Network::XKore2::start();
  223. } elsif ($XKore_version eq "3" || $XKore_version eq "proxy") {
  224. # Proxy Ragnarok client connection
  225. require Network::XKoreProxy;
  226. $net = new Network::XKoreProxy;
  227. } else {
  228. # Run as a standalone bot, with no interface to the official RO client
  229. require Network::DirectConnection;
  230. $net = new Network::DirectConnection;
  231. }
  232. };
  233. if ($@) {
  234. # Problem with networking.
  235. $interface->errorDialog($@);
  236. exit 1;
  237. }
  238. if ($sys{bus}) {
  239. require Bus::Client;
  240. require Bus::Handlers;
  241. my $host = $sys{bus_server_host};
  242. my $port = $sys{bus_server_port};
  243. $host = undef if ($host eq '');
  244. $port = undef if ($port eq '');
  245. $bus = new Bus::Client(host => $host, port => $port);
  246. our $busMessageHandler = new Bus::Handlers($bus);
  247. }
  248. Network::PaddedPackets::init();
  249. }
  250. sub initPortalsDatabase {
  251. Log::message(T("Checking for new portals... "));
  252. if (compilePortals_check()) {
  253. Log::message(T("found new portals!n"));
  254. my $choice = $interface->showMenu(
  255. T("New portals have been added to the portals database. " .
  256. "The portals database must be compiled before the new portals can be used. " .
  257. "Would you like to compile portals now?n"),
  258. [T("Yes, compile now."), T("No, don't compile it.")],
  259. title => T("Compile portals?"));
  260. if ($choice == 0) {
  261. Log::message(T("compiling portals") . "nn");
  262. compilePortals();
  263. } else {
  264. Log::message(T("skipping compile") . "nn");
  265. }
  266. } else {
  267. Log::message(T("none foundnn"));
  268. }
  269. }
  270. sub promptFirstTimeInformation {
  271. if ($net->version != 1) {
  272. my $msg;
  273. if (!$config{username}) {
  274. $msg = $interface->query(T("Please enter your Ragnarok Online username."));
  275. if (!defined($msg)) {
  276. exit;
  277. }
  278. configModify('username', $msg, 1);
  279. }
  280. if (!$config{password}) {
  281. $msg = $interface->query(T("Please enter your Ragnarok Online password."), isPassword => 1);
  282. if (!defined($msg)) {
  283. exit;
  284. }
  285. configModify('password', $msg, 1);
  286. }
  287. }
  288. if ($config{master} eq "" || $config{master} =~ /^d+$/ || !exists $masterServers{$config{master}}) {
  289. my @servers = sort { lc($a) cmp lc($b) } keys(%masterServers);
  290. my $choice = $interface->showMenu(
  291. T("Please choose a master server to connect to."),
  292. @servers,
  293. title => T("Master servers"));
  294. if ($choice == -1) {
  295. exit;
  296. } else {
  297. configModify('master', $servers[$choice], 1);
  298. }
  299. }
  300. }
  301. sub finalInitialization {
  302. $incomingMessages = new Network::MessageTokenizer(%rpackets);
  303. $outgoingClientMessages = new Network::MessageTokenizer(%rpackets);
  304. $KoreStartTime = time;
  305. $conState = 1;
  306. our $nextConfChangeTime;
  307. $bExpSwitch = 2;
  308. $jExpSwitch = 2;
  309. $totalBaseExp = 0;
  310. $totalJobExp = 0;
  311. $startTime_EXP = time;
  312. $taskManager = new TaskManager();
  313. $itemsList = new ActorList('Actor::Item');
  314. $monstersList = new ActorList('Actor::Monster');
  315. $playersList = new ActorList('Actor::Player');
  316. $petsList = new ActorList('Actor::Pet');
  317. $npcsList = new ActorList('Actor::NPC');
  318. $portalsList = new ActorList('Actor::Portal');
  319. $slavesList = new ActorList('Actor::Slave');
  320. foreach my $list ($itemsList, $monstersList, $playersList, $petsList, $npcsList, $portalsList, $slavesList) {
  321. $list->onAdd()->add(undef, &actorAdded);
  322. $list->onRemove()->add(undef, &actorRemoved);
  323. $list->onClearBegin()->add(undef, &actorListClearing);
  324. }
  325. StdHttpReader::init();
  326. initStatVars();
  327. initRandomRestart();
  328. initUserSeed();
  329. initConfChange();
  330. Log::initLogFiles();
  331. $timeout{'injectSync'}{'time'} = time;
  332. Log::message("n");
  333. Plugins::callHook('initialized');
  334. XSTools::initVersion();
  335. }
  336. #######################################
  337. # VARIABLE INITIALIZATION FUNCTIONS
  338. #######################################
  339. # Calculate next random restart time.
  340. # The restart time will be autoRestartMin + rand(autoRestartSeed)
  341. sub initRandomRestart {
  342. if ($config{'autoRestart'}) {
  343. my $autoRestart = $config{'autoRestartMin'} + int(rand $config{'autoRestartSeed'});
  344. message TF("Next restart in %sn", timeConvert($autoRestart)), "system";
  345. configModify("autoRestart", $autoRestart, 1);
  346. }
  347. }
  348. # Initialize random configuration switching time
  349. sub initConfChange {
  350. my $i = 0;
  351. while (exists $ai_v{"autoConfChange_${i}_timeout"}) {
  352. delete $ai_v{"autoConfChange_${i}_timeout"};
  353. $i++;
  354. }
  355. $i = 0;
  356. while (exists $config{"autoConfChange_$i"}) {
  357. $ai_v{"autoConfChange_${i}_timeout"} = $config{"autoConfChange_${i}_minTime"} +
  358. int(rand($config{"autoConfChange_${i}_varTime"}));
  359. $i++;
  360. }
  361. $lastConfChangeTime = time;
  362. }
  363. # Initialize variables when you start a connection to a map server
  364. sub initConnectVars {
  365. # we must use $chars[$config{char}] here because $char may not be set
  366. initMapChangeVars();
  367. if ($char) {
  368. $char->{skills} = {};
  369. delete $char->{mute_period};
  370. delete $char->{muted};
  371. }
  372. undef @skillsID;
  373. $useArrowCraft = 1;
  374. }
  375. # Initialize variables when you change map (after a teleport or after you walked into a portal)
  376. sub initMapChangeVars {
  377. # we must use $chars[$config{char}] here because $char may not be set
  378. @portalsID_old = @portalsID;
  379. %portals_old = %portals;
  380. foreach (@portalsID_old) {
  381. next if (!$_ || !$portals_old{$_});
  382. $portals_old{$_}{gone_time} = time if (!$portals_old{$_}{gone_time});
  383. }
  384. # this is just used for portalRecord (add opposite portal by guessing method)
  385. if ($char) {
  386. $char->{old_pos_to} = {%{$char->{pos_to}}} if ($char->{pos_to});
  387. delete $char->{sitting};
  388. delete $char->{dead};
  389. delete $char->{warp};
  390. delete $char->{casting};
  391. delete $char->{homunculus}{appear_time};
  392. $char->inventory->clear();
  393. }
  394. $timeout{play}{time} = time;
  395. $timeout{ai_sync}{time} = time;
  396. $timeout{ai_sit_idle}{time} = time;
  397. $timeout{ai_teleport}{time} = time;
  398. $timeout{ai_teleport_idle}{time} = time;
  399. $timeout{ai_teleport_safe_force}{time} = time;
  400. delete $timeout{ai_teleport_retry}{time};
  401. delete $timeout{ai_teleport_delay}{time};
  402. undef %incomingDeal;
  403. undef %outgoingDeal;
  404. undef %currentDeal;
  405. undef $currentChatRoom;
  406. undef @currentChatRoomUsers;
  407. undef @itemsID;
  408. undef @identifyID;
  409. undef %repairList;
  410. undef @spellsID;
  411. undef @arrowCraftID;
  412. undef %items;
  413. undef %spells;
  414. undef %incomingParty;
  415. undef %talk;
  416. $ai_v{cart_time} = time + 60;
  417. $ai_v{inventory_time} = time + 60;
  418. $ai_v{temp} = {};
  419. $cart{inventory} = [];
  420. undef @venderItemList;
  421. undef $venderID;
  422. undef @venderListsID;
  423. undef %venderLists;
  424. undef %incomingGuild;
  425. undef @chatRoomsID;
  426. undef %chatRooms;
  427. undef %createdChatRoom;
  428. undef @lastpm;
  429. undef %incomingFriend;
  430. $itemsList->clear();
  431. $monstersList->clear();
  432. $playersList->clear();
  433. $petsList->clear();
  434. $portalsList->clear();
  435. $npcsList->clear();
  436. $slavesList->clear();
  437. @unknownPlayers = ();
  438. @unknownNPCs = ();
  439. @sellList = ();
  440. $shopstarted = 0;
  441. $timeout{ai_shop}{time} = time;
  442. $timeout{ai_storageAuto}{time} = time + 5;
  443. $timeout{ai_buyAuto}{time} = time + 5;
  444. $timeout{ai_shop}{time} = time;
  445. AI::clear("attack", "move");
  446. AI::SlaveManager::clear("attack", "route", "move");
  447. ChatQueue::clear;
  448. Plugins::callHook('packet_mapChange');
  449. $logAppend = ($config{logAppendUsername}) ? "_$config{username}_$config{char}" : '';
  450. if ($config{logAppendUsername} && index($Settings::storage_log_file, $logAppend) == -1) {
  451. $Settings::chat_log_file     = substr($Settings::chat_log_file,    0, length($Settings::chat_log_file)    - 4) . "$logAppend.txt";
  452. $Settings::storage_log_file  = substr($Settings::storage_log_file, 0, length($Settings::storage_log_file) - 4) . "$logAppend.txt";
  453. $Settings::shop_log_file     = substr($Settings::shop_log_file,    0, length($Settings::shop_log_file)    - 4) . "$logAppend.txt";
  454. $Settings::monster_log_file  = substr($Settings::monster_log_file, 0, length($Settings::monster_log_log)  - 4) . "$logAppend.txt";
  455. $Settings::item_log_file     = substr($Settings::item_log_file,    0, length($Settings::item_log_file)    - 4) . "$logAppend.txt";
  456. }
  457. }
  458. # Initialize variables when your character logs in
  459. sub initStatVars {
  460. $totaldmg = 0;
  461. $dmgpsec = 0;
  462. $startedattack = 0;
  463. $monstarttime = 0;
  464. $monkilltime = 0;
  465. $elasped = 0;
  466. $totalelasped = 0;
  467. $statChanged = 0;
  468. $skillChanged = 0;
  469. }
  470. #####################################################
  471. # MISC. MAIN LOOP FUNCTIONS
  472. #####################################################
  473. # This function is called every time in the main loop, when OpenKore has been
  474. # fully initialized.
  475. sub mainLoop_initialized {
  476. Benchmark::begin("mainLoop_part1") if DEBUG;
  477. # Handle connection states
  478. $net->checkConnection();
  479. # Receive and handle data from the RO server
  480. my $data = $net->serverRecv;
  481. if (defined($data) && length($data) > 0) {
  482. Benchmark::begin("parseMsg") if DEBUG;
  483. my $type;
  484. $incomingMessages->add($data);
  485. while ($data = $incomingMessages->readNext($type)) {
  486. if ($type == Network::MessageTokenizer::KNOWN_MESSAGE) {
  487. parseIncomingMessage($data);
  488. } else {
  489. if ($type == Network::MessageTokenizer::UNKNOWN_MESSAGE) {
  490. # Unknown message - ignore it
  491. my $messageID = Network::MessageTokenizer::getMessageID($data);
  492. if (!existsInList($config{debugPacket_exclude}, $messageID)) {
  493. warning TF("Unknown packet - %sn", $messageID), "connection";
  494. visualDump($data, "<< Received unknown packet") if ($config{debugPacket_unparsed});
  495. }
  496. } elsif ($config{debugPacket_received}) {
  497. debug "Received account IDn", "parseMsg", 0 ;
  498. }
  499. # Pass it along to the client, whatever it is
  500. $net->clientSend($data);
  501. }
  502. }
  503. $net->clientFlush() if (UNIVERSAL::isa($net, 'Network::XKoreProxy'));
  504. Benchmark::end("parseMsg") if DEBUG;
  505. }
  506. # Receive and handle data from the RO client
  507. $data = $net->clientRecv;
  508. if (defined($data) && length($data) > 0) {
  509. my $type;
  510. $outgoingClientMessages->add($data);
  511. while ($data = $outgoingClientMessages->readNext($type)) {
  512. parseOutgoingClientMessage($data);
  513. }
  514. }
  515. # GameGuard support
  516. if ($config{gameGuard} && ($net->version != 1 || ($net->version == 1 && $config{gameGuard} eq '2'))) {
  517. my $result = Poseidon::Client::getInstance()->getResult();
  518. if (defined($result)) {
  519. debug "Received Poseidon result.n", "poseidon";
  520. $messageSender->encryptMessageID($result);
  521. $net->serverSend($result);
  522. }
  523. }
  524. Benchmark::end("mainLoop_part1") if DEBUG;
  525. Benchmark::begin("mainLoop_part2") if DEBUG;
  526. # Process AI
  527. if ($net->getState() == Network::IN_GAME && timeOut($timeout{ai}) && $net->serverAlive()) {
  528. Misc::checkValidity("AI (pre)");
  529. Benchmark::begin("ai") if DEBUG;
  530. AI::CoreLogic::iterate();
  531. Benchmark::end("ai") if DEBUG;
  532. Benchmark::begin("ai_homunculus") if DEBUG;
  533. AI::SlaveManager::iterate();
  534. Benchmark::end("ai_homunculus") if DEBUG;
  535. Misc::checkValidity("AI");
  536. return if $quit;
  537. }
  538. Misc::checkValidity("mainLoop_part2.1");
  539. $taskManager->iterate();
  540. Benchmark::end("mainLoop_part2") if DEBUG;
  541. Benchmark::begin("mainLoop_part3") if DEBUG;
  542. # Process bus events.
  543. $bus->iterate() if ($bus);
  544. Misc::checkValidity("mainLoop_part2.2");
  545. ###### Other stuff that's run in the main loop #####
  546. if ($config{'autoRestart'} && time - $KoreStartTime > $config{'autoRestart'}
  547.  && $net->getState() == Network::IN_GAME && !AI::inQueue(qw/attack take items_take/)) {
  548. message T("nAuto-restarting!!n"), "system";
  549. if ($config{'autoRestartSleep'}) {
  550. my $sleeptime = $config{'autoSleepMin'} + int(rand $config{'autoSleepSeed'});
  551. $timeout_ex{'master'}{'timeout'} = $sleeptime;
  552. $sleeptime = $timeout{'reconnect'}{'timeout'} if ($sleeptime < $timeout{'reconnect'}{'timeout'});
  553. message TF("Sleeping for %sn", timeConvert($sleeptime)), "system";
  554. } else {
  555. $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'};
  556. }
  557. $timeout_ex{'master'}{'time'} = time;
  558. $KoreStartTime = time + $timeout_ex{'master'}{'timeout'};
  559. AI::clear();
  560. AI::SlaveManager::clear();
  561. undef %ai_v;
  562. $net->serverDisconnect;
  563. $net->setState(Network::NOT_CONNECTED);
  564. undef $conState_tries;
  565. initRandomRestart();
  566. }
  567. Misc::checkValidity("mainLoop_part2.3");
  568. # Automatically switch to a different config file
  569. # based on certain conditions
  570. if ($net->getState() == Network::IN_GAME && timeOut($AI::Timeouts::autoConfChangeTime, 0.5)
  571.  && !AI::inQueue(qw/attack take items_take/)) {
  572. my $selected;
  573. my $i = 0;
  574. while (exists $config{"autoConfChange_$i"}) {
  575. if ($config{"autoConfChange_$i"}
  576.  && ( !$config{"autoConfChange_${i}_minTime"} || timeOut($lastConfChangeTime, $ai_v{"autoConfChange_${i}_timeout"}) )
  577.  && inRange($char->{lv}, $config{"autoConfChange_${i}_lvl"})
  578.  && inRange($char->{lv_job}, $config{"autoConfChange_${i}_joblvl"})
  579.  && ( !$config{"autoConfChange_${i}_isJob"} || $jobs_lut{$char->{jobID}} eq $config{"autoConfChange_${i}_isJob"} )
  580. ) {
  581. $selected = $config{"autoConfChange_$i"};
  582. last;
  583. }
  584. $i++;
  585. }
  586. if ($selected) {
  587. # Choose a random configuration file
  588. my @files = split(/,+/, $selected);
  589. my $file = $files[rand(@files)];
  590. message TF("Changing configuration file (from "%s" to "%s")...n", $Settings::config_file, $file), "system";
  591. # A relogin is necessary if the server host/port, username
  592. # or char is different.
  593. my $oldMaster = $masterServer;
  594. my $oldUsername = $config{'username'};
  595. my $oldChar = $config{'char'};
  596. switchConfigFile($file);
  597. my $master = $masterServer = $masterServers{$config{'master'}};
  598. if ($net->version != 1
  599.  && $oldMaster->{ip} ne $master->{ip}
  600.  || $oldMaster->{port} ne $master->{port}
  601.  || $oldMaster->{master_version} ne $master->{master_version}
  602.  || $oldMaster->{version} ne $master->{version}
  603.  || $oldUsername ne $config{'username'}
  604.  || $oldChar ne $config{'char'}) {
  605. AI::clear;
  606. AI::SlaveManager::clear();
  607. relog();
  608. } else {
  609. AI::clear("move", "route", "mapRoute");
  610. AI::SlaveManager::clear("move", "route", "mapRoute");
  611. }
  612. initConfChange();
  613. }
  614. $AI::Timeouts::autoConfChangeTime = time;
  615. }
  616. processStatisticsReporting() unless ($sys{sendAnonymousStatisticReport} eq "0");
  617. # Update state.yml
  618. if (timeOut($AI::Timeouts::stateUpdate, 0.5)) {
  619. my %state;
  620. my $f;
  621. $AI::Timeouts::stateUpdate = time;
  622. if ($field{name} && $net->getState() == Network::IN_GAME) {
  623. my $pos = calcPosition($char);
  624. %state = (
  625. connectionState => 'in game',
  626. fieldName => $field{name},
  627. fieldBaseName => $field{baseName},
  628. charName => $char->{name},
  629. x => $pos->{x},
  630. y => $pos->{y}
  631. );
  632. $state{actors} = {};
  633. foreach my $actor (@{$npcsList->getItems()}, @{$playersList->getItems()}, @{$monstersList->getItems()}, @{$slavesList->getItems()}) {
  634. my $actorType = $actor->{actorType};
  635. $state{actors}{$actorType} ||= [];
  636. push @{$state{actors}{$actorType}}, {
  637. x => $actor->{pos_to}{x},
  638. y => $actor->{pos_to}{y}
  639. };
  640. }
  641. } else {
  642. %state = (
  643. connectionState => 'not logged in'
  644. );
  645. }
  646. if ($bus && $bus->getState() == Bus::Client::CONNECTED()) {
  647. $state{bus}{host} = $bus->serverHost();
  648. $state{bus}{port} = $bus->serverPort();
  649. $state{bus}{clientID} = $bus->ID();
  650. }
  651. if (open($f, ">:utf8", "$Settings::logs_folder/state_".$config{'username'}.".yml")) {
  652. my $writer = new Data::YAML::Writer();
  653. $writer->write(%state, $f);
  654. close $f;
  655. }
  656. }
  657. Misc::checkValidity("mainLoop_part2.4");
  658. # Set interface title
  659. my $charName;
  660. my $title;
  661. $charName = "$char->{name}: " if ($char);
  662. if ($net->getState() == Network::IN_GAME) {
  663. my ($basePercent, $jobPercent, $weight, $pos);
  664. assert(defined $char);
  665. $basePercent = sprintf("%.2f", $char->{exp} / $char->{exp_max} * 100) if ($char->{exp_max});
  666. $jobPercent = sprintf("%.2f", $char->{exp_job} / $char->{exp_job_max} * 100) if ($char->{exp_job_max});
  667. $weight = int($char->{weight} / $char->{weight_max} * 100) . "%" if ($char->{weight_max});
  668. $pos = " : $char->{pos_to}{x},$char->{pos_to}{y} " . $field->name() if ($char->{pos_to} && $field);
  669. # Translation Comment: Interface Title with character status
  670. $title = TF("%s B%s (%s), J%s (%s) : w%s%s - %s",
  671. $charName, $char->{lv}, $basePercent . '%',
  672. $char->{lv_job}, $jobPercent . '%',
  673. $weight, $pos, $Settings::NAME);
  674. } elsif ($net->getState() == Network::NOT_CONNECTED) {
  675. # Translation Comment: Interface Title
  676. $title = TF("%sNot connected - %s", $charName, $Settings::NAME);
  677. } else {
  678. # Translation Comment: Interface Title
  679. $title = TF("%sConnecting - %s", $charName, $Settings::NAME);
  680. }
  681. my %args = (return => $title);
  682. Plugins::callHook('mainLoop::setTitle',%args);
  683. $interface->title($args{return});
  684. Misc::checkValidity("mainLoop_part3");
  685. Benchmark::end("mainLoop_part3") if DEBUG;
  686. }
  687. # Anonymous statistics reporting. This gives us insight about
  688. # servers that our users bot on.
  689. sub processStatisticsReporting {
  690. our %statisticsReporting;
  691. if (!$statisticsReporting{reported} && $config{master} && $config{username}) {
  692. if (!$statisticsReporting{http}) {
  693. use Utils qw(urlencode);
  694. import Utils::Whirlpool qw(whirlpool_hex);
  695. # Note that ABSOLUTELY NO SENSITIVE INFORMATION about the
  696. # user is sent. The username is filtered through an
  697. # irreversible hashing algorithm before it is sent to the
  698. # server. It is impossible to deduce the user's username
  699. # from the data sent to the server.
  700. #
  701. # If you're still not convinced about the security of this,
  702. # please read the following web pages for more details and explanation:
  703. #   http://www.openkore.com/statistics.php
  704. # -and-
  705. #   http://forums.openkore.com/viewtopic.php?t=28044
  706. my $url = "http://www.openkore.com/statistics.php";
  707. my $post = "server=" . urlencode($config{master});
  708. $post .= "&product=" . urlencode($Settings::NAME);
  709. $post .= "&version=" . urlencode($Settings::VERSION);
  710. $post .= "&uid=" . urlencode(whirlpool_hex($config{master} . $config{username} . $userSeed));
  711. $statisticsReporting{http} = new StdHttpReader($url, $post);
  712. debug "Posting anonymous usage statistics to $urln", "statisticsReporting";
  713. }
  714. my $http = $statisticsReporting{http};
  715. if ($http->getStatus() == HttpReader::DONE) {
  716. $statisticsReporting{reported} = 1;
  717. delete $statisticsReporting{http};
  718. debug "Statistics posting completed.n", "statisticsReporting";
  719. } elsif ($http->getStatus() == HttpReader::ERROR) {
  720. $statisticsReporting{reported} = 1;
  721. delete $statisticsReporting{http};
  722. debug "Statistics posting failed: " . $http->getError() . "n", "statisticsReporting";
  723. }
  724. } elsif (!$statisticsReporting{infoPosted} && $masterServer && $masterServer->{ip}
  725.       && $config{master} && $net && $net->getState() == Network::IN_GAME && $monstarttime) {
  726. if (!$statisticsReporting{http}) {
  727. my $url = "http://www.openkore.com/server-info.php";
  728. my $serverData = "";
  729. foreach my $key (sort keys %{$masterServer}) {
  730. $serverData .= "$key $masterServer->{$key}n";
  731. }
  732. my $post = "server=" . urlencode($config{master}) . "&data=" . urlencode($serverData);
  733. $statisticsReporting{http} = new StdHttpReader($url, $post);
  734. debug "Posting server info to $urln", "statisticsReporting";
  735. }
  736. my $http = $statisticsReporting{http};
  737. if ($http->getStatus() == HttpReader::DONE) {
  738. $statisticsReporting{infoPosted} = 1;
  739. delete $statisticsReporting{http};
  740. debug "Server info posting completed.n", "statisticsReporting";
  741. } elsif ($http->getStatus() == HttpReader::ERROR) {
  742. $statisticsReporting{infoPosted} = 1;
  743. delete $statisticsReporting{http};
  744. debug "Server info posting failed: " . $http->getError() . "n", "statisticsReporting";
  745. }
  746. }
  747. }
  748. sub parseInput {
  749. my $input = shift;
  750. my $printType;
  751. my ($hook, $msg);
  752. $printType = shift if ($net && $net->clientAlive);
  753. debug("Input: $inputn", "parseInput", 2);
  754. if ($printType) {
  755. my $hookOutput = sub {
  756. my ($type, $domain, $level, $globalVerbosity, $message, $user_data) = @_;
  757. $msg .= $message if ($type ne 'debug' && $level <= $globalVerbosity);
  758. };
  759. $hook = Log::addHook($hookOutput);
  760. $interface->writeOutput("console", "$inputn");
  761. }
  762. $XKore_dontRedirect = 1;
  763. Commands::run($input);
  764. if ($printType) {
  765. Log::delHook($hook);
  766. if (defined $msg && $net->getState() == Network::IN_GAME && $config{XKore_silent}) {
  767. $msg =~ s/n*$//s;
  768. $msg =~ s/n/\n/g;
  769. sendMessage($messageSender, "k", $msg);
  770. }
  771. }
  772. $XKore_dontRedirect = 0;
  773. }
  774. #######################################
  775. #######################################
  776. # Parse RO Client Send Message
  777. #######################################
  778. #######################################
  779. sub parseOutgoingClientMessage {
  780. use bytes;
  781. no encoding 'utf8';
  782. my ($msg) = @_;
  783. my $sendMsg = $msg;
  784. if (length($msg) >= 4 && $net->getState() >= 4 && length($msg) >= unpack("v1", substr($msg, 0, 2))) {
  785. Network::Receive->decrypt($msg, $msg);
  786. }
  787. my $switch = Network::MessageTokenizer::getMessageID($msg);
  788. if ($config{'debugPacket_ro_sent'} && !existsInList($config{'debugPacket_exclude'}, $switch)
  789.    || $config{debugPacket_include_dumpMethod} && existsInList($config{'debugPacket_include'}, $switch)) {
  790. my $label = $packetDescriptions{Send}{$switch} ?
  791. " - $packetDescriptions{Send}{$switch}" : '';
  792. if ($config{debugPacket_ro_sent} == 1) {
  793. debug "Packet SENT_BY_CLIENT: $switch$labeln", "parseSendMsg", 0;
  794. } elsif ($config{debugPacket_ro_sent} == 2) {
  795. visualDump($sendMsg, $switch . $label);
  796. }
  797. if ($config{debugPacket_include_dumpMethod} == 1) {
  798. debug "Packet: $switch$labeln", "parseMsg", 0;
  799. } elsif ($config{debugPacket_include_dumpMethod} == 2) {
  800. visualDump($sendMsg, $switch . $label);
  801. } elsif ($config{debugPacket_include_dumpMethod} == 3) {
  802. dumpData($msg,1);
  803. } elsif ($config{debugPacket_include_dumpMethod} == 4) {
  804. open DUMP, ">> DUMP_lines.txt";
  805. print DUMP sprintf(unpack('H*', $msg) . "n");
  806. close DUMP;
  807. }
  808. }
  809. Plugins::callHook('RO_sendMsg_pre', {switch => $switch, msg => $msg, realMsg => $sendMsg});
  810. my $serverType = $masterServer->{serverType};
  811. # If the player tries to manually do something in the RO client, disable AI for a small period
  812. # of time using ai_clientSuspend().
  813. if ($masterServer->{syncID} && $switch eq sprintf('%04X', hex($masterServer->{syncID}))) {
  814. #syncSync support for XKore 1 mode
  815. $syncSync = substr($msg, $masterServer->{syncTickOffset}, 4);
  816. } elsif ($switch eq "0065") {
  817. # Login to character server
  818. $incomingMessages->nextMessageMightBeAccountID();
  819. } elsif ($switch eq "0066") {
  820. # Login character selected
  821. configModify("char", unpack("C*",substr($msg, 2, 1)));
  822. } elsif (
  823. ($switch eq "0072" && ($serverType == 0 || $serverType == 21 || $serverType == 22)) ||
  824. ($switch eq "00F3" && $serverType == 18)
  825. ) {
  826. # Map login
  827. $incomingMessages->nextMessageMightBeAccountID();
  828. if ($serverType == 0 && $config{sex} ne "") {
  829. $sendMsg = substr($sendMsg, 0, 18) . pack("C",$config{'sex'});
  830. }
  831. } elsif ($switch eq "00A7") {
  832. if($masterServer && $masterServer->{paddedPackets}) {
  833. $syncSync = substr($msg, 8, 4);
  834. }
  835. } elsif ($switch eq "007E") {
  836. if ($masterServer && $masterServer->{paddedPackets}) {
  837. $syncSync = substr($msg, 4, 4);
  838. }
  839. } elsif ($switch eq "007D") {
  840. # Map loaded
  841. $packetParser->changeToInGameState();
  842. AI::clear("clientSuspend");
  843. $timeout{ai}{time} = time;
  844. if ($firstLoginMap) {
  845. undef $sentWelcomeMessage;
  846. undef $firstLoginMap;
  847. }
  848. $timeout{'welcomeText'}{'time'} = time;
  849. $ai_v{portalTrace_mapChanged} = time;
  850. # syncSync support for XKore 1 mode
  851. if($masterServer->{serverType} == 11) {
  852. $syncSync = substr($msg, 8, 4);
  853. } else {
  854. # formula: MapLoaded_len + Sync_len - 4 - Sync_packet_last_junk
  855. $syncSync = substr($msg, $masterServer->{mapLoadedTickOffset}, 4);
  856. }
  857. message T("Map loadedn"), "connection";
  858. Plugins::callHook('map_loaded');
  859. } elsif ($switch eq "0085") {
  860. #if ($masterServer->{serverType} == 0 || $masterServer->{serverType} == 1 || $masterServer->{serverType} == 2) {
  861. # #Move
  862. # AI::clear("clientSuspend");
  863. # makeCoords(%coords, substr($msg, 2, 3));
  864. # ai_clientSuspend($switch, (distance($char->{'pos'}, %coords) * $char->{walk_speed}) + 4);
  865. #}
  866. } elsif ($switch eq "0089") {
  867. if ($masterServer->{serverType} == 0) {
  868. # Attack
  869. if (!$config{'tankMode'} && !AI::inQueue("attack")) {
  870. AI::clear("clientSuspend");
  871. ai_clientSuspend($switch, 2, unpack("C*",substr($msg,6,1)), substr($msg,2,4));
  872. } else {
  873. undef $sendMsg;
  874. }
  875. }
  876. #undef $sendMsg;
  877. } elsif (($switch eq "008C" && ($masterServer->{serverType} == 0 || $masterServer->{serverType} == 1 || $masterServer->{serverType} == 2 || $masterServer->{serverType} == 6 || $masterServer->{serverType} == 7 || $masterServer->{serverType} == 10 || $masterServer->{serverType} == 11 || $masterServer->{serverType} == 21 || $masterServer->{serverType} == 22)) ||
  878. ($switch eq "00F3" && ($masterServer->{serverType} == 3 || $masterServer->{serverType} == 5 || $masterServer->{serverType} == 8 || $masterServer->{serverType} == 9 || $masterServer->{serverType} == 15)) ||
  879. ($switch eq "009F" && $masterServer->{serverType} == 4) ||
  880. ($switch eq "007E" && $masterServer->{serverType} == 12) ||
  881. ($switch eq "0190" && ($masterServer->{serverType} == 13 || $masterServer->{serverType} == 18)) ||
  882. ($switch eq "0085" && $masterServer->{serverType} == 14) || # Public chat
  883. $switch eq "0108" || # Party chat
  884. $switch eq "017E") { # Guild chat
  885. my $length = unpack("v",substr($msg,2,2));
  886. my $message = substr($msg, 4, $length - 4);
  887. my ($chat) = $message =~ /^[sS]*? : ([sS]*)00?/;
  888. $chat =~ s/^s*//;
  889. stripLanguageCode($chat);
  890. my $prefix = quotemeta $config{'commandPrefix'};
  891. if ($chat =~ /^$prefix/) {
  892. $chat =~ s/^$prefix//;
  893. $chat =~ s/^s*//;
  894. $chat =~ s/s*$//;
  895. $chat =~ s/00*$//;
  896. parseInput($chat, 1);
  897. undef $sendMsg;
  898. }
  899. } elsif ($switch eq "0096") {
  900. # Private message
  901. my $length = unpack("v",substr($msg,2,2));
  902. my ($user) = substr($msg, 4, 24) =~ /([sS]*?)00/;
  903. my $chat = substr($msg, 28, $length - 29);
  904. $chat =~ s/^s*//;
  905. # Ensures: $user and $chat are String
  906. $user = I18N::bytesToString($user);
  907. $chat = I18N::bytesToString($chat);
  908. stripLanguageCode($chat);
  909. my $prefix = quotemeta $config{commandPrefix};
  910. if ($chat =~ /^$prefix/) {
  911. $chat =~ s/^$prefix//;
  912. $chat =~ s/^s*//;
  913. $chat =~ s/s*$//;
  914. parseInput($chat, 1);
  915. undef $sendMsg;
  916. } else {
  917. undef %lastpm;
  918. $lastpm{msg} = $chat;
  919. $lastpm{user} = $user;
  920. push @lastpm, {%lastpm};
  921. }
  922. } elsif (($switch eq "009B" && $masterServer->{serverType} == 0) ||
  923. ($switch eq "009B" && $masterServer->{serverType} == 1) ||
  924. ($switch eq "009B" && $masterServer->{serverType} == 2) ||
  925. ($switch eq "0085" && $masterServer->{serverType} == 3) ||
  926. ($switch eq "00F3" && $masterServer->{serverType} == 4) ||
  927. ($switch eq "0085" && $masterServer->{serverType} == 5) ||
  928. #($switch eq "009B" && $masterServer->{serverType} == 6) || serverType 6 uses what?
  929. ($switch eq "009B" && $masterServer->{serverType} == 7) ||
  930. ($switch eq "0072" && $masterServer->{serverType} == 13)) { # rRO
  931. # Look
  932. if ($char) {
  933. if ($masterServer->{serverType} == 0) {
  934. $char->{look}{head} = unpack("C", substr($msg, 2, 1));
  935. $char->{look}{body} = unpack("C", substr($msg, 4, 1));
  936. } elsif ($masterServer->{serverType} == 1 ||
  937. $masterServer->{serverType} == 2 ||
  938. $masterServer->{serverType} == 4 ||
  939. $masterServer->{serverType} == 7) {
  940. $char->{look}{head} = unpack("C", substr($msg, 6, 1));
  941. $char->{look}{body} = unpack("C", substr($msg, 14, 1));
  942. } elsif ($masterServer->{serverType} == 3) {
  943. $char->{look}{head} = unpack("C", substr($msg, 12, 1));
  944. $char->{look}{body} = unpack("C", substr($msg, 22, 1));
  945. } elsif ($masterServer->{serverType} == 5) {
  946. $char->{look}{head} = unpack("C", substr($msg, 8, 1));
  947. $char->{look}{body} = unpack("C", substr($msg, 16, 1));
  948. } elsif ($masterServer->{serverType} == 13) { # rRO
  949. $char->{look}{head} = unpack("C", substr($msg, 2, 1));
  950. $char->{look}{body} = unpack("C", substr($msg, 4, 1));
  951. }
  952. }
  953. } elsif ($switch eq "009F") {
  954. if ($masterServer->{serverType} == 0) {
  955. # Take
  956. AI::clear("clientSuspend");
  957. ai_clientSuspend($switch, 2, substr($msg,2,4));
  958. }
  959. } elsif ($switch eq "00B2") {
  960. # Trying to exit (respawn)
  961. AI::clear("clientSuspend");
  962. ai_clientSuspend($switch, 10);
  963. } elsif ($switch eq "018A") {
  964. # Trying to exit
  965. AI::clear("clientSuspend");
  966. ai_clientSuspend($switch, 10);
  967. } elsif ($switch eq "0149") {
  968. # Chat/skill mute
  969. undef $sendMsg;
  970. } elsif ($switch eq "01B2") {
  971. # client started a shop manually
  972. $shopstarted = 1;
  973. } elsif ($switch eq "012E") {
  974. # client stopped shop manually
  975. $shopstarted = 0;
  976. }
  977. if ($sendMsg ne "") {
  978. $messageSender->encryptMessageID($sendMsg);
  979. $net->serverSend($sendMsg);
  980. }
  981. # This should be changed to packets that haven't been parsed yet, in a similar manner
  982. # as parseMsg
  983. return "";
  984. }
  985. #######################################
  986. #######################################
  987. #Parse Message
  988. #######################################
  989. #######################################
  990. ##
  991. # void parseIncomingMessage(Bytes msg)
  992. # msg: The data to parse, as received from the socket.
  993. #
  994. # Parse network data sent by the RO server.
  995. sub parseIncomingMessage {
  996. my ($msg) = @_;
  997. # Determine packet switch
  998. my $switch = Network::MessageTokenizer::getMessageID($msg);
  999. if (length($msg) >= 4 && substr($msg, 0, 4) ne $accountID && $net->getState() >= Network::CONNECTED_TO_CHAR_SERVER
  1000.  && $lastswitch ne $switch && length($msg) >= unpack("v1", substr($msg, 0, 2))) {
  1001. # The decrypt below casued annoying unparsed errors (at least in serverType  2)
  1002. if ($masterServer->{serverType} != 2) {
  1003. Network::Receive->decrypt($msg, $msg);
  1004. $switch = Network::MessageTokenizer::getMessageID($msg);
  1005. }
  1006. }
  1007. # The user is running in X-Kore mode and wants to switch character or gameGuard type 2 after 0259 tag 02.
  1008. # We're now expecting an accountID, unless the server has replicated packet 0259 (server-side bug).
  1009. if ($net->getState() == 2.5 && (!$config{gameGuard} || ($switch ne '0259' && $config{gameGuard} eq "2"))) {
  1010. if (length($msg) >= 4) {
  1011. $net->setState(Network::CONNECTED_TO_MASTER_SERVER);
  1012. $accountID = substr($msg, 0, 4);
  1013. debug "Selecting character, new accountID: ".unpack("V", $accountID)."n", "connection";
  1014. $net->clientSend($accountID);
  1015. return substr($msg, 4);
  1016. } else {
  1017. return $msg;
  1018. }
  1019. }
  1020. $lastswitch = $switch;
  1021. if ($config{debugPacket_received} && !existsInList($config{'debugPacket_exclude'}, $switch)) {
  1022. my $label = $packetDescriptions{Recv}{$switch} ?
  1023. "[$packetDescriptions{Recv}{$switch}]" : '';
  1024. if ($config{debugPacket_received} == 1) {
  1025. debug sprintf("Received packet: %-4s    [%2d bytes]  %sn", $switch, length($msg), $label),
  1026. "parseMsg", 0;
  1027. } else {
  1028. visualDump($msg, "<< Received packet: $switch  $label");
  1029. }
  1030. }
  1031. if ($config{debugPacket_include_dumpMethod} && existsInList($config{'debugPacket_include'}, $switch)) {
  1032. my $label = $packetDescriptions{Recv}{$switch} ?
  1033. " ($packetDescriptions{Recv}{$switch})" : '';
  1034. if ($config{debugPacket_include_dumpMethod} == 1) {
  1035. debug "Packet: $switch$labeln", "parseMsg", 0;
  1036. } elsif ($config{debugPacket_include_dumpMethod} == 2) {
  1037. visualDump($msg, "$switch$label");
  1038. } elsif ($config{debugPacket_include_dumpMethod} == 3) {
  1039. dumpData($msg,1);
  1040. } elsif ($config{debugPacket_include_dumpMethod} == 4) {
  1041. open DUMP, ">> DUMP_lines.txt";
  1042. print DUMP sprintf(unpack('H*', $msg) . "n");
  1043. close DUMP;
  1044. }
  1045. }
  1046. Plugins::callHook('parseMsg/pre', {switch => $switch, msg => $msg, msg_size => length($msg)});
  1047. if (!$packetParser->willMangle($switch)) {
  1048. # If we're running in X-Kore mode, pass the message back to the RO client.
  1049. $net->clientSend($msg);
  1050. }
  1051. $lastPacketTime = time;
  1052. if ($packetParser &&
  1053. (my $args = $packetParser->parse($msg))) {
  1054. # Use the new object-oriented packet parser
  1055. if ($config{debugPacket_received} > 2 &&
  1056.     !existsInList($config{'debugPacket_exclude'}, $switch)) {
  1057. my $switch = $args->{switch};
  1058. my $packet = $packetParser->{packet_list}{$switch};
  1059. my ($name, $packString, $varNames) = @{$packet};
  1060. my @vars = ();
  1061. for my $varName (@{$varNames}) {
  1062. message "$varName = $args->{$varName}n";
  1063. }
  1064. }
  1065. if ($packetParser->willMangle($switch)) {
  1066. my $ret = $packetParser->mangle($args);
  1067. if (!$ret) {
  1068. # Packet was not mangled
  1069. $net->clientSend($args->{RAW_MSG});
  1070. } elsif ($ret == 1) {
  1071. # Packet was mangled
  1072. $net->clientSend($packetParser->reconstruct($args));
  1073. } else {
  1074. # Packet was suppressed
  1075. }
  1076. }
  1077. }
  1078. }
  1079. return 1;