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

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. #  OpenKore - Networking subsystem
  3. #  This module contains functions for sending packets to the server.
  4. #
  5. #  This software is open source, licensed under the GNU General Public
  6. #  License, version 2.
  7. #  Basically, this means that you're allowed to modify and distribute
  8. #  this software. However, if you distribute modified versions, you MUST
  9. #  also distribute the source code.
  10. #  See http://www.gnu.org/licenses/gpl.html for the full license.
  11. #
  12. #  $Revision: 5249 $
  13. #  $Id: Network.pm 5249 2006-12-25 14:40:58Z vcl_kore $
  14. #
  15. #########################################################################
  16. ##
  17. # MODULE DESCRIPTION: Connection handling
  18. #
  19. # The Network module handles connections to the Ragnarok Online server.
  20. # This module only handles connection issues, and nothing else. It doesn't do
  21. # anything with the actual data. Network data handling is performed by
  22. # the @MODULE(Network::Receive) and Network::Receive::ServerTypeX classes.
  23. #
  24. # The submodule @MODULE(Network::Send) contains functions for sending all
  25. # kinds of messages to the RO server.
  26. #
  27. # Please also read <a href="http://www.openkore.com/wiki/index.php/Network_subsystem">the
  28. # network subsystem overview.</a>
  29. #
  30. # This implementation establishes a direct connection to the RO server.
  31. # Note that there are alternative implementations for this interface: @MODULE(Network::XKore),
  32. # @MODULE(Network::XKore2) and @MODULE(Network::XKoreProxy)
  33. package Network::DirectConnection;
  34. use strict;
  35. use Modules 'register';
  36. use Exporter;
  37. use base qw(Exporter);
  38. use Time::HiRes qw(time);
  39. use IO::Socket::INET;
  40. use encoding 'utf8';
  41. use Scalar::Util;
  42. use File::Spec;
  43. use Globals;
  44. use Log qw(message error);
  45. use Network;
  46. use Network::Send ();
  47. use Plugins;
  48. use Settings;
  49. use Interface;
  50. use Utils qw(dataWaiting timeOut);
  51. use Utils::Exceptions;
  52. use Misc qw(chatLog);
  53. use Translation;
  54. ##
  55. # Network::DirectConnection->new([wrapper])
  56. # wrapper: If this object is to be wrapped by another object which is interface-compatible
  57. #          with the Network::DirectConnection class, then specify the wrapper object here. The message
  58. #          sender will use this wrapper to send socket data. Internally, the reference to the wrapper
  59. #          will be stored as a weak reference.
  60. #
  61. # Create a new Network::DirectConnection object. The connection is not yet established.
  62. sub new {
  63. my ($class, $wrapper) = @_;
  64. my %self;
  65. $self{remote_socket} = new IO::Socket::INET;
  66. if ($wrapper) {
  67. $self{wrapper} = $wrapper;
  68. Scalar::Util::weaken($self{wrapper});
  69. }
  70. return bless %self, $class;
  71. }
  72. ##
  73. # int $net->version()
  74. #
  75. # Returns the implementation number this object.
  76. sub version {
  77. return 0;
  78. }
  79. sub DESTROY {
  80. my $self = shift;
  81. $self->serverDisconnect();
  82. }
  83. ######################
  84. ## Server Functions ##
  85. ######################
  86. ##
  87. # boolean $net->serverAliveServer()
  88. #
  89. # Check whether the connection to the server is alive.
  90. sub serverAlive {
  91. return $_[0]->{remote_socket} && $_[0]->{remote_socket}->connected();
  92. }
  93. ##
  94. # String $net->serverPeerHost()
  95. #
  96. # If the connection to the server is alive, returns the host name of the server.
  97. # Otherwise, returns undef.
  98. sub serverPeerHost {
  99. return $_[0]->{remote_socket}->peerhost if ($_[0]->serverAlive);
  100. return undef;
  101. }
  102. ##
  103. # int $net->serverPeerPort()
  104. #
  105. # If the connection to the server is alive, returns the port number of the server.
  106. # Otherwise, returns undef.
  107. sub serverPeerPort {
  108. return $_[0]->{remote_socket}->peerport if ($_[0]->serverAlive);
  109. return undef;
  110. }
  111. ##
  112. # $net->serverConnect(String host, int port)
  113. # host: the host name/IP of the RO server to connect to.
  114. # port: the port number of the RO server to connect to.
  115. #
  116. # Establish a connection to a Ragnarok Online server.
  117. #
  118. # This function is used internally by $net->checkConnection() and should not be used directly.
  119. sub serverConnect {
  120. my $self = shift;
  121. my $host = shift;
  122. my $port = shift;
  123. my $return = 0;
  124. Plugins::callHook('Network::connectTo', {
  125. socket => $self->{remote_socket},
  126. return => $return,
  127. host => $host,
  128. port => $port
  129. });
  130. return if ($return);
  131. message TF("Connecting (%s:%s)... ", $host, $port), "connection";
  132. $self->{remote_socket} = new IO::Socket::INET(
  133. LocalAddr => $config{bindIp} || undef,
  134. PeerAddr => $host,
  135. PeerPort => $port,
  136. Proto => 'tcp',
  137. Timeout => 4);
  138. ($self->{remote_socket} && inet_aton($self->{remote_socket}->peerhost()) eq inet_aton($host)) ?
  139. message T("connectedn"), "connection" :
  140. error(TF("couldn't connect: %s (error code %d)n", "$!", int($!)), "connection");
  141. if ($self->getState() != Network::NOT_CONNECTED) {
  142. $incomingMessages->nextMessageMightBeAccountID();
  143. }
  144. }
  145. ##
  146. # void $net->serverSend(Bytes data)
  147. #
  148. # If the connection to the server is alive, send data to the server.
  149. # Otherwise, this method does nothing.
  150. sub serverSend {
  151. my $self = shift;
  152. my $msg = shift;
  153. if ($self->serverAlive) {
  154. if (Plugins::hasHook("Network::serverSend/pre")) {
  155. Plugins::callHook("Network::serverSend/pre", { msg => $msg });
  156. }
  157. if (defined $msg) {
  158. $self->{remote_socket}->send($msg);
  159. if (Plugins::hasHook("Network::serverSend")) {
  160. Plugins::callHook("Network::serverSend", { msg => $msg });
  161. }
  162. }
  163. }
  164. }
  165. ##
  166. # Bytes $net->serverRecv()
  167. #
  168. # Receive data from the RO server.
  169. sub serverRecv {
  170. my $self = shift;
  171. my $msg;
  172. return undef unless (dataWaiting($self->{remote_socket}));
  173. $self->{remote_socket}->recv($msg, 1024 * 32);
  174. if (Plugins::hasHook("Network::serverRecv")) {
  175. Plugins::callHook("Network::serverRecv", { msg => $msg });
  176. }
  177. if (!defined($msg) || length($msg) == 0) {
  178. # Connection from server closed.
  179. close($self->{remote_socket});
  180. return undef;
  181. }
  182. return $msg;
  183. }
  184. ##
  185. # Bytes $net->serverAddress()
  186. #
  187. # Return the server's raw address.
  188. sub serverAddress {
  189. my ($self) = @_;
  190. return $self->{remote_socket}->sockaddr();
  191. }
  192. ##
  193. # $net->serverDisconnect()
  194. #
  195. # Disconnect from the current Ragnarok Online server.
  196. #
  197. # This function is used internally by $net->checkConnection() and should not be used directly.
  198. sub serverDisconnect {
  199. my $self = shift;
  200. if ($self->serverAlive) {
  201. $messageSender->sendQuit() if ($self->getState() == Network::IN_GAME);
  202. message TF("Disconnecting (%s:%s)...", $self->{remote_socket}->peerhost(), 
  203. $self->{remote_socket}->peerport()), "connection";
  204. close($self->{remote_socket});
  205. !$self->serverAlive() ?
  206. message T("disconnectedn"), "connection" :
  207. error T("couldn't disconnectn"), "connection";
  208. }
  209. }
  210. sub getState {
  211. return $conState;
  212. }
  213. sub setState {
  214. my ($self, $state) = @_;
  215. $conState = $state;
  216. Plugins::callHook('Network::stateChanged');
  217. }
  218. ######################
  219. ## Client Functions ##
  220. ######################
  221. ##
  222. # boolean $net->clientAlive()
  223. #
  224. # Check whether there are one or more clients connected to Kore.
  225. sub clientAlive {
  226. my %args = (net => $_[0]);
  227. Plugins::callHook('Network::clientAlive', %args);
  228. return $args{return};
  229. }
  230. ##
  231. # $net->clientSend(Bytes data)
  232. #
  233. # Make the RO client think that it has received $data.
  234. sub clientSend {
  235. my ($self) = @_;
  236. if ($self->clientAlive && Plugins::hasHook('Network::clientSend')) {
  237. my %args = (net => $self, data => $_[1]);
  238. Plugins::callHook('Network::clientSend', %args);
  239. return $args{return};
  240. } else {
  241. return undef;
  242. }
  243. }
  244. ##
  245. # Bytes $net->clientRecv()
  246. #
  247. # Receive data that the RO client wants to send to the RO server.
  248. sub clientRecv {
  249. my ($self) = @_;
  250. if ($self->clientAlive) {
  251. my %args = (net => $_[0]);
  252. Plugins::callHook('Network::clientRecv', %args);
  253. return $args{return};
  254. } else {
  255. return undef;
  256. }
  257. }
  258. #######################
  259. ## Utility Functions ##
  260. #######################
  261. #######################################
  262. #######################################
  263. # Check Connection
  264. #######################################
  265. #######################################
  266. ##
  267. # $net->checkConnection()
  268. #
  269. # Handles any connection issues. Based on the current situation, this function may
  270. # re-connect to the RO server, disconnect, do nothing, etc.
  271. #
  272. # This function is meant to be run in the Kore main loop.
  273. sub checkConnection {
  274. my $self = shift;
  275. return if ($Settings::no_connect);
  276. if ($self->getState() == Network::NOT_CONNECTED && (!$self->{remote_socket} || !$self->{remote_socket}->connected) && timeOut($timeout_ex{'master'}) && !$conState_tries) {
  277. my $master = $masterServer = $masterServers{$config{master}};
  278. foreach my $serverOption ('serverType', 'chatLangCode', 'storageEncryptKey', 'gameGuard', 'charBlockSize',
  279. 'paddedPackets', 'paddedPackets_attackID', 'paddedPackets_skillUseID',
  280. 'mapServer_ip', 'mapServer_port') {
  281. if ($master->{$serverOption} ne '' && $config{$serverOption} ne $master->{$serverOption}) {
  282. main::configModify($serverOption, $master->{$serverOption});
  283. }
  284. }
  285. if ($master->{serverEncoding} ne '' && $config{serverEncoding} ne $master->{serverEncoding}) {
  286. main::configModify('serverEncoding', $master->{serverEncoding});
  287. } elsif ($config{serverEncoding} eq '') {
  288. main::configModify('serverEncoding', 'Western');
  289. }
  290. if (Settings::setRecvPacketsName($masterServer->{recvpackets})) {
  291. my (undef, undef, $basename) = File::Spec->splitpath(Settings::getRecvPacketsFilename());
  292. Settings::loadByRegexp(quotemeta $basename, sub {
  293. my ($filename) = @_;
  294. message TF("Loading %s...n", $filename);
  295. });
  296. }
  297. message T("Connecting to Account Server...n", "connection");
  298. $shopstarted = 1;
  299. $conState_tries++;
  300. $initSync = 1;
  301. $incomingMessages->clear();
  302. eval {
  303. $packetParser = Network::Receive->create($config{serverType});
  304. my $wrapper = ($self->{wrapper}) ? $self->{wrapper} : $self;
  305. $messageSender = Network::Send->create($wrapper, $config{serverType});
  306. };
  307. if ($@) {
  308. $interface->errorDialog("$@");
  309. $quit = 1;
  310. return;
  311. }
  312. $reconnectCount++;
  313. $self->serverConnect($master->{ip}, $master->{port});
  314. # call plugin's hook to determine if we can continue the work
  315. if ($self->serverAlive) {
  316. Plugins::callHook("Network::serverConnect/master");
  317. return if ($conState == 1.5);
  318. }
  319. # GameGuard support
  320. if ($self->serverAlive && $config{gameGuard} == 2) {
  321. my $msg = pack("C*", 0x58, 0x02);
  322. $net->serverSend($msg);
  323. message T("Requesting permission to logon on account server...n");
  324. $conState = 1.2;
  325. return;
  326. }
  327. if ($self->serverAlive && $master->{secureLogin} >= 1) {
  328. my $code;
  329. message T("Secure Login...n"), "connection";
  330. undef $secureLoginKey;
  331. if ($master->{secureLogin_requestCode} ne '') {
  332. $code = $master->{secureLogin_requestCode};
  333. } elsif ($config{secureLogin_requestCode} ne '') {
  334. $code = $config{secureLogin_requestCode};
  335. }
  336. if ($code ne '') {
  337. $messageSender->sendMasterCodeRequest('code', $code);
  338. } else {
  339. $messageSender->sendMasterCodeRequest('type', $master->{secureLogin_type});
  340. }
  341. } elsif ($self->serverAlive) {
  342. $messageSender->sendPreLoginCode($master->{preLoginCode}) if ($master->{preLoginCode});
  343. $messageSender->sendMasterLogin($config{'username'}, $config{'password'},
  344. $master->{master_version}, $master->{version});
  345. }
  346. $timeout{'master'}{'time'} = time;
  347. # we skipped some required connection operations while waiting for the server to allow as to login,
  348. # after we have successfully sent in the reply to the game guard challenge (using the poseidon server)
  349. # this conState will allow us to continue from where we left off.
  350. } elsif ($conState == 1.3) {
  351. $conState = 1;
  352. my $master = $masterServer = $masterServers{$config{'master'}};
  353. if ($master->{secureLogin} >= 1) {
  354. my $code;
  355. message T("Secure Login...n"), "connection";
  356. undef $secureLoginKey;
  357. if ($master->{secureLogin_requestCode} ne '') {
  358. $code = $master->{secureLogin_requestCode};
  359. } elsif ($config{secureLogin_requestCode} ne '') {
  360.   $code = $config{secureLogin_requestCode};
  361. }
  362. if ($code ne '') {
  363. $messageSender->sendMasterCodeRequest('code', $code);
  364. } else {
  365. $messageSender->sendMasterCodeRequest('type', $master->{secureLogin_type});
  366. }
  367. } else {
  368. $messageSender->sendPreLoginCode($master->{preLoginCode}) if ($master->{preLoginCode});
  369. $messageSender->sendMasterLogin($config{'username'}, $config{'password'},
  370. $master->{master_version}, $master->{version});
  371. }
  372. $timeout{'master'}{'time'} = time;
  373. } elsif ($conState == 1 && $masterServer->{secureLogin} >= 1 && $secureLoginKey ne ""
  374.    && !timeOut($timeout{'master'}) && $conState_tries) {
  375. my $master = $masterServer;
  376. message T("Sending encoded password...n"), "connection";
  377. $messageSender->sendMasterSecureLogin($config{'username'}, $config{'password'}, $secureLoginKey,
  378. $master->{version}, $master->{master_version},
  379. $master->{secureLogin}, $master->{secureLogin_account});
  380. undef $secureLoginKey;
  381. } elsif ($conState == 1 && timeOut($timeout{'master'}) && timeOut($timeout_ex{'master'})) {
  382. if ($config{dcOnMaxReconnections} && $config{dcOnMaxReconnections} <= $reconnectCount) {
  383. $quit = 1;
  384. return;
  385. }
  386. error T("Timeout on Account Server, reconnecting...n"), "connection";
  387. $timeout_ex{'master'}{'time'} = time;
  388. $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'};
  389. $self->serverDisconnect;
  390. undef $conState_tries;
  391. } elsif ($conState == 1.5) {
  392. if (!$self->serverAlive) {
  393. $self->setState(Network::NOT_CONNECTED);
  394. undef $conState_tries;
  395. return;
  396. }
  397. # on this special stage, the plugin will know what to do next.
  398. Plugins::callHook("Network::serverConnect/special");
  399. } elsif ($conState == 2 && !$self->serverAlive()
  400.   && ($config{'server'} ne "" || $masterServer->{charServer_ip})
  401.   && !$conState_tries) {
  402. if ($config{pauseCharServer}) {
  403. message "Pausing for $config{pauseCharServer} second(s)...n", "system";
  404. sleep $config{pauseCharServer};
  405. }
  406. my $master = $masterServer;
  407. message T("Connecting to Character Server...n"), "connection";
  408. $conState_tries++;
  409. if ($master->{charServer_ip}) {
  410. $self->serverConnect($master->{charServer_ip}, $master->{charServer_port});
  411. } elsif ($servers[$config{'server'}]) {
  412. $self->serverConnect($servers[$config{'server'}]{'ip'}, $servers[$config{'server'}]{'port'});
  413. } else {
  414. error TF("Invalid server specified, server %s does not exist...n", $config{server}), "connection";
  415. my @serverList;
  416. foreach my $server (@servers) {
  417. push @serverList, $server->{name};
  418. }
  419. my $ret = $interface->showMenu(
  420. T("Please select your login server."),
  421. @serverList,
  422. title => T("Select Login Server"));
  423. if ($ret == -1) {
  424. quit();
  425. } else {
  426. main::configModify('server', $ret, 1);
  427. undef $conState_tries;
  428. }
  429. return;
  430. }
  431. # call plugin's hook to determine if we can continue the connection
  432. if ($self->serverAlive) {
  433. Plugins::callHook("Network::serverConnect/char");
  434. $reconnectCount = 0;
  435. return if ($conState == 1.5);
  436. }
  437. $messageSender->sendGameLogin($accountID, $sessionID, $sessionID2, $accountSex);
  438. $timeout{'gamelogin'}{'time'} = time;
  439. } elsif ($conState == 2 && timeOut($timeout{'gamelogin'})
  440.   && ($config{'server'} ne "" || $masterServer->{'charServer_ip'})) {
  441. error T("Timeout on Character Server, reconnecting...n"), "connection";
  442. $timeout_ex{'master'}{'time'} = time;
  443. $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'};
  444. $self->serverDisconnect;
  445. undef $conState_tries;
  446. $self->setState(Network::NOT_CONNECTED);
  447. } elsif ($conState == 3 && !$self->serverAlive() && $config{'char'} ne "" && !$conState_tries) {
  448. message T("Connecting to Character Select Server...n"), "connection";
  449. $conState_tries++;
  450. $self->serverConnect($servers[$config{'server'}]{'ip'}, $servers[$config{'server'}]{'port'});
  451. # call plugin's hook to determine if we can continue the connection
  452. if ($self->serverAlive) {
  453. Plugins::callHook("Network::serverConnect/charselect");
  454. return if ($conState == 1.5);
  455. }
  456. $messageSender->sendCharLogin($config{'char'});
  457. $timeout{'charlogin'}{'time'} = time;
  458. } elsif ($conState == 3 && timeOut($timeout{'charlogin'}) && $config{'char'} ne "") {
  459. error T("Timeout on Character Select Server, reconnecting...n"), "connection";
  460. $timeout_ex{'master'}{'time'} = time;
  461. $timeout_ex{'master'}{'timeout'} = $timeout{'reconnect'}{'timeout'};
  462. $self->serverDisconnect;
  463. $self->setState(Network::NOT_CONNECTED);
  464. undef $conState_tries;
  465. } elsif ($conState == 4 && !$self->serverAlive() && !$conState_tries) {
  466. if ($config{pauseMapServer}) {
  467. message "Pausing for $config{pauseMapServer} second(s)...n", "system";
  468. sleep($config{pauseMapServer});
  469. }
  470. message T("Connecting to Map Server...n"), "connection";
  471. $conState_tries++;
  472. main::initConnectVars();
  473. my $master = $masterServer;
  474. my ($ip, $port);
  475. if ($master->{private}) {
  476. $ip = $config{forceMapIP} || $master->{ip};
  477. $port = $map_port;
  478. } else {
  479. $ip = $master->{mapServer_ip} || $config{forceMapIP} || $map_ip;
  480. $port = $master->{mapServer_port} || $map_port;
  481. }
  482. $self->serverConnect($ip, $port);
  483. # call plugin's hook to determine if we can continue the connection
  484. if ($self->serverAlive) {
  485. Plugins::callHook("Network::serverConnect/mapserver");
  486. return if ($conState == 1.5);
  487. }
  488. $messageSender->sendMapLogin($accountID, $charID, $sessionID, $accountSex2);
  489. $timeout_ex{master}{time} = time;
  490. $timeout_ex{master}{timeout} = $timeout{reconnect}{timeout};
  491. $timeout{maplogin}{time} = time;
  492. } elsif ($conState == 4 && timeOut($timeout{maplogin})) {
  493. message T("Timeout on Map Server, connecting to Account Server...n"), "connection";
  494. $timeout_ex{master}{timeout} = $timeout{reconnect}{timeout};
  495. $self->serverDisconnect;
  496. $self->setState(Network::NOT_CONNECTED);
  497. undef $conState_tries;
  498. } elsif ($conState == 5 && !$self->serverAlive()) {
  499. Plugins::callHook('disconnected');
  500. if ($config{dcOnDisconnect}) {
  501. chatLog("k", T("*** You disconnected, auto quit! ***n"));
  502. error T("Disconnected from Map Server, exiting...n"), "connection";
  503. $quit = 1;
  504. } else {
  505. error TF("Disconnected from Map Server, connecting to Account Server in %s seconds...n", $timeout{reconnect}{timeout}), "connection";
  506. $timeout_ex{master}{time} = time;
  507. $timeout_ex{master}{timeout} = $timeout{reconnect}{timeout};
  508. $self->setState(Network::NOT_CONNECTED);
  509. undef $conState_tries;
  510. }
  511. } elsif ($conState == 5 && timeOut($timeout{play})) {
  512. error T("Timeout on Map Server, "), "connection";
  513. Plugins::callHook('disconnected');
  514. if ($config{dcOnDisconnect}) {
  515. error T("exiting...n"), "connection";
  516. $quit = 1;
  517. } else {
  518. error TF("connecting to Account Server in %s seconds...n", $timeout{reconnect}{timeout}), "connection";
  519. $timeout_ex{master}{time} = time;
  520. $timeout_ex{master}{timeout} = $timeout{reconnect}{timeout};
  521. $self->serverDisconnect;
  522. $self->setState(Network::NOT_CONNECTED);
  523. undef $conState_tries;
  524. }
  525. }
  526. }
  527. return 1;