No more parseIncomingMessage and parseOutgoingClientMessage.
Code: Select all
Index: src/Globals.pm
===================================================================
--- src/Globals.pm (revision 7792)
+++ src/Globals.pm (working copy)
@@ -29,7 +29,7 @@
config => [qw(%arrowcraft_items %avoid @chatResponses %cities_lut %config %consoleColors %directions_lut %equipTypes_lut %equipSlot_rlut %equipSlot_lut %haircolors @headgears_lut %items_control %items_lut %itemSlotCount_lut %itemsDesc_lut %itemTypes_lut %jobs_lut %maps_lut %masterServers %monsters_lut %npcs_lut %packetDescriptions %portals_lut %responses %sex_lut %shop %skillsDesc_lut %lookHandle %skillsArea %skillsEncore %spells_lut %emotions_lut %timeout $char %mon_control %priority %routeWeights %pickupitems %rpackets %itemSlots_lut %statusHandle %statusName %effectName %portals_los %stateHandle %ailmentHandle %mapTypeHandle %mapPropertyTypeHandle %mapPropertyInfoHandle %elements_lut %mapAlias_lut %quests_lut)],
ai => [qw(@ai_seq @ai_seq_args %ai_v $AI $AI_forcedOff %targetTimeout)],
state => [qw($accountID $cardMergeIndex @cardMergeItemsID $charID @chars @chars_old %cart @friendsID %friends %incomingFriend $field %homunculus $itemsList @itemsID %items $monstersList @monstersID %monsters @npcsID %npcs $npcsList @playersID %players @portalsID @portalsID_old %portals %portals_old $portalsList @storeList $currentChatRoom @currentChatRoomUsers @chatRoomsID %createdChatRoom %chatRooms @skillsID %storage @storageID @arrowCraftID %guild %incomingGuild @spellsID %spells @unknownPlayers @unknownNPCs $useArrowCraft %currentDeal %incomingDeal %outgoingDeal @identifyID @partyUsersID %incomingParty @petsID %pets @venderItemList $venderID $venderCID @venderListsID @buyerItemList $buyerID $buyerCID @buyerListsID @articles $articles %venderLists %buyerLists %monsters_old @monstersID_old %npcs_old %items_old %players_old @playersID_old @servers $sessionID $sessionID2 $accountSex $accountSex2 $map_ip $map_port $KoreStartTime $secureLoginKey $initSync $lastConfChangeTime $petsList $playersList $portalsList @playerNameCacheIDs %playerNameCache %pet $pvp @cashList $slavesList @slavesID %slaves)],
- network => [qw($remote_socket $net $messageSender $charServer $conState $conState_tries $encryptVal $ipc $bus $lastPacketTime $masterServer $lastswitch $packetParser $bytesSent $bytesReceived $incomingMessages $outgoingClientMessages $enc_val1 $enc_val2 $captcha_state)],
+ network => [qw($remote_socket $net $messageSender $charServer $conState $conState_tries $encryptVal $ipc $bus $masterServer $lastswitch $packetParser $clientPacketHandler $bytesSent $incomingMessages $outgoingClientMessages $enc_val1 $enc_val2 $captcha_state)],
interface => [qw($interface)],
misc => [qw($quit $reconnectCount @lastpm %lastpm @privMsgUsers %timeout_ex $shopstarted $dmgpsec $totalelasped $elasped $totaldmg %overallAuth %responseVars %talk $startTime_EXP $startingzeny @monsters_Killed $bExpSwitch $jExpSwitch $totalBaseExp $totalJobExp $shopEarned %itemChange $XKore_dontRedirect $monkilltime $monstarttime $startedattack $firstLoginMap $sentWelcomeMessage $versionSearch $monsterBaseExp $monsterJobExp %descriptions %flags %damageTaken $logAppend @sellList $userSeed $taskManager $repairList $mailList $auctionList $questList $hotkeyList $devotionList $cookingList)],
syncs => [qw($syncSync $syncMapSync)],
@@ -455,14 +455,16 @@
# Network
our $remote_socket; # Unused, but required for outdated plugins
our $net;
+our $packetParser;
our $messageSender;
+our $clientPacketHandler;
our $charServer;
our $conState;
our $conState_tries;
our $encryptVal;
our $ipc;
our $bus;
-our $lastPacketTime;
+#our $lastPacketTime; # replaced by $packetParser->{lastPacketTime}
our $masterServer;
our $incomingMessages;
our $outgoingClientMessages;
@@ -516,7 +518,7 @@
our $taskManager;
our $bytesSent = 0;
-our $bytesReceived = 0;
+#our $bytesReceived = 0; # replaced by $packetParser->{bytesProcessed}
our $syncSync;
our $syncMapSync;
Index: src/Interface/Wx/StatView/Exp.pm
===================================================================
--- src/Interface/Wx/StatView/Exp.pm (revision 7792)
+++ src/Interface/Wx/StatView/Exp.pm (working copy)
@@ -3,7 +3,7 @@
use strict;
use base 'Interface::Wx::StatView';
-use Globals qw/$char $conState $startTime_EXP $startingzeny $totalBaseExp $totalJobExp $bytesSent $bytesReceived/;
+use Globals qw/$char $conState $packetParser $startTime_EXP $startingzeny $totalBaseExp $totalJobExp $bytesSent/;
use Translation qw/T TF/;
use Utils qw/formatNumber timeConvert timeOut/;
@@ -86,7 +86,7 @@
$self->set ('zeny', formatNumber ($value = $char->{zeny} - $startingzeny));
$self->set ('zenyPerHour', formatNumber (int $value / $bottingHours));
$self->set ('bytesSent', formatNumber ($bytesSent));
- $self->set ('bytesReceived', formatNumber ($bytesReceived));
+ $self->set ('bytesReceived', $packetParser && formatNumber($packetParser->{bytesProcessed}));
}
$self->GetSizer->Layout;
Index: src/doc/modules.txt
===================================================================
--- src/doc/modules.txt (revision 7792)
+++ src/doc/modules.txt (working copy)
@@ -21,6 +21,7 @@
Task::Wait
TaskManager
Modules
+Network::ClientReceive
Network::DirectConnection
Network::PacketParser
Network::Receive
Index: src/Base/Ragnarok/CharServer.pm
===================================================================
--- src/Base/Ragnarok/CharServer.pm (revision 7792)
+++ src/Base/Ragnarok/CharServer.pm (working copy)
@@ -8,7 +8,7 @@
use Base::RagnarokServer;
use base qw(Base::RagnarokServer);
use Misc;
-use Globals qw(%config $packetParser);
+use Globals qw(%config);
use constant SESSION_TIMEOUT => 120;
use constant DUMMY_CHARACTER => {
@@ -69,14 +69,19 @@
return $_[0]->{charBlockSize};
}
-sub process_0065 {
+sub game_login {
# Character server login.
- my ($self, $client, $message) = @_;
- my ($accountID, $sessionID, $sessionID2, $gender) = unpack('x2 a4 V V x2 C', $message);
- my $session = $self->{sessionStore}->get($sessionID);
+ my ($self, $args, $client) = @_;
+ # maybe sessionstore should store sessionID as bytes?
+ my $session = $self->{sessionStore}->get(unpack('V', $args->{sessionID}));
- if (!$session || $session->{accountID} ne $accountID || $session->{sessionID} != $sessionID
- || $session->{sex} != $gender || $session->{state} ne 'About to select character') {
+ unless (
+ $session && $session->{accountID} eq $args->{accountID}
+ # maybe sessionstore should store sessionID as bytes?
+ && pack('V', $session->{sessionID}) eq $args->{sessionID}
+ && $session->{sex} == $args->{accountSex}
+ && $session->{state} eq 'About to select character'
+ ) {
$client->close();
} else {
@@ -91,7 +96,7 @@
next if (!$char);
$output .= pack(
- $packetParser->received_characters_unpackString,
+ $self->{recvPacketParser}->received_characters_unpackString,
$char->{charID}, # character ID
$char->{exp}, # base experience
$char->{zeny}, # zeny
@@ -143,11 +148,11 @@
$self->{sessionStore}->mark($session);
$client->{session} = $session;
$session->{time} = time;
- $client->send($accountID);
+ $client->send($args->{accountID});
if ($config{XKore_altCharServer} == 1){
$client->send(pack('C2 v', 0x72, 0x00, length($output) + 4) . $output);
}else{
- $client->send($packetParser->reconstruct({
+ $client->send($self->{recvPacketParser}->reconstruct({
switch => 'received_characters',
charInfo => $output,
@@ -163,19 +168,18 @@
}
-sub process_0066 {
+sub char_login {
# Select character.
- my ($self, $client, $message) = @_;
+ my ($self, $args, $client) = @_;
my $session = $client->{session};
if ($session) {
$self->{sessionStore}->mark($session);
- my ($charIndex) = unpack('x2 C', $message);
my @characters = $self->getCharacters();
- if (!$characters[$charIndex]) {
+ if (!$characters[$args->{slot}]) {
# Invalid character selected.
$client->send(pack('C*', 0x6C, 0x00, 0));
} else {
- my $char = $characters[$charIndex];
+ my $char = $characters[$args->{slot}];
my $charInfo = $self->{mapServer}->getCharInfo($session);
if (!$charInfo) {
# We can't get the character information for some reason.
@@ -186,7 +190,7 @@
$session->{charID} = $char->{charID};
$session->{state} = 'About to load map';
- $client->send($packetParser->reconstruct({
+ $client->send($self->{recvPacketParser}->reconstruct({
switch => 'received_character_ID_and_Map',
charID => $char->{charID},
mapName => $charInfo->{map},
@@ -199,27 +203,27 @@
$client->close();
}
-sub process_0187 {
+sub ban_check {
# Ban check.
# Doing nothing seems to work.
}
-sub process_0067 {
+sub char_create {
# Character creation.
- my ($self, $client) = @_;
+ my ($self, $args, $client) = @_;
# Deny it.
$client->send(pack('C*', 0x6E, 0x00, 2));
}
-sub process_0067 {
+sub char_delete {
# Character deletion.
- my ($self, $client) = @_;
+ my ($self, $args, $client) = @_;
# Deny it.
$client->send(pack('C*', 0x70, 0x00, 1));
}
sub unhandledMessage {
- my ($self, $client) = @_;
+ my ($self, $args, $client) = @_;
$client->close();
}
Index: src/Base/Ragnarok/MapServer.pm
===================================================================
--- src/Base/Ragnarok/MapServer.pm (revision 7792)
+++ src/Base/Ragnarok/MapServer.pm (working copy)
@@ -5,7 +5,6 @@
no encoding 'utf8';
use bytes;
-use Globals;
use Modules 'register';
use Base::RagnarokServer;
use Misc;
@@ -38,13 +37,19 @@
#my ($self, $session) = @_;
}
-sub handleLogin {
- my ($self, $client, $accountID, $charID, $sessionID, $gender) = @_;
- my $session = $self->{sessionStore}->get($sessionID);
+sub map_login {
+ my ($self, $args, $client) = @_;
+ # maybe sessionstore should store sessionID as bytes?
+ my $session = $self->{sessionStore}->get(unpack('V', $args->{sessionID}));
- if (!$session || $session->{accountID} ne $accountID || $session->{sessionID} != $sessionID
- || $session->{sex} != $gender || $session->{charID} ne $charID
- || $session->{state} ne 'About to load map') {
+ unless (
+ $session && $session->{accountID} eq $args->{accountID}
+ # maybe sessionstore should store sessionID as bytes?
+ && pack('V', $session->{sessionID}) eq $args->{sessionID}
+ && $session->{sex} == $args->{sex}
+ && $session->{charID} eq $args->{charID}
+ && $session->{state} eq 'About to load map'
+ ) {
$client->close();
} else {
@@ -52,20 +57,20 @@
$client->{session} = $session;
# # TODO: use result from the server?
- # if (exists $packetParser->{packet_lut}{define_check}) {
- # $client->send($packetParser->reconstruct({
+ # if (exists $self->{recvPacketParser}{packet_lut}{define_check}) {
+ # $client->send($self->{recvPacketParser}->reconstruct({
# switch => 'define_check',
# result => Network::Receive::ServerType0::DEFINE__BROADCASTING_SPECIAL_ITEM_OBTAIN | Network::Receive::ServerType0::DEFINE__RENEWAL_ADD_2,
# }));
# }
- if (exists $packetParser->{packet_lut}{account_id}) {
- $client->send($packetParser->reconstruct({
+ if (exists $self->{recvPacketParser}{packet_lut}{account_id}) {
+ $client->send($self->{recvPacketParser}->reconstruct({
switch => 'account_id',
- accountID => $accountID,
+ accountID => $args->{accountID},
}));
} else {
- $client->send($accountID);
+ $client->send($args->{accountID});
}
my $charInfo = $self->getCharInfo($session);
@@ -73,45 +78,16 @@
shiftPack(\$coords, $charInfo->{x}, 10);
shiftPack(\$coords, $charInfo->{y}, 10);
shiftPack(\$coords, 0, 4);
- $client->send($packetParser->reconstruct({
+ $client->send($self->{recvPacketParser}->reconstruct({
switch => 'map_loaded',
syncMapSync => int time,
coords => $coords,
}));
}
+
+ $args->{mangle} = 2;
}
-sub process_0072 {
- my ($self, $client, $message) = @_;
- if ($self->{serverType} == 0 || $self->{serverType} == 21) {
- # Map server login.
- my ($accountID, $charID, $sessionID, $gender) = unpack('x2 a4 a4 V x4 C', $message);
- $self->handleLogin($client, $accountID, $charID, $sessionID, $gender);
- return 1;
- } elsif ($self->getServerType() == 8) {
- # packet sendSkillUse
- $self->unhandledMessage($client, $message);
- return 0;
- } else { #oRO and pRO and idRO
- my ($accountID, $charID, $sessionID, $gender) = unpack('x2 a4 x5 a4 x2 V x4 C', $message);
- $self->handleLogin($client, $accountID, $charID, $sessionID, $gender);
- return 1;
- }
-}
-
-sub process_00F3 {
- my ($self, $client, $message) = @_;
- if ($self->getServerType() == 18) {
- # Map server login.
- my ($charID, $accountID, $sessionID, $gender) = unpack('x5 a4 a4 x V x9 x4 C', $message);
- $self->handleLogin($client, $accountID, $charID, $sessionID, $gender);
- return 1;
- } else {
- $self->unhandledMessage($client, $message);
- return 0;
- }
-}
-
# $msg = pack("C*", 0x9b, 0, 0x39, 0x33) .
# $accountID .
# pack("C*", 0x65) .
@@ -121,36 +97,8 @@
# pack("V", getTickCount()) .
# pack("C*", $sex);
-sub process_009B {
- my ($self, $client, $message) = @_;
-
- if ($self->getServerType() == 8) {
- # Map server login.
- my ($accountID , $charID, $sessionID, $gender) = unpack('x4 a4 x a4 x4 V x4 C', $message);
- $self->handleLogin($client, $accountID, $charID, $sessionID, $gender);
- return 1;
- } else {
- $self->unhandledMessage($client, $message);
- return 0;
- }
-}
-
-sub process_0436 {
- my ($self, $client, $message) = @_;
-
- if ($self->getServerType() =~ m/^8_5$/) {
- # Map server login.
- my ($accountID , $charID, $sessionID, $gender) = unpack('x2 a4 a4 V x4 C', $message);
- $self->handleLogin($client, $accountID, $charID, $sessionID, $gender);
- return 1;
- } else {
- $self->unhandledMessage($client, $message);
- return 0;
- }
-}
-
sub unhandledMessage {
- my ($self, $client) = @_;
+ my ($self, $args, $client) = @_;
if (!$client->{session}) {
$client->close();
return 0;
Index: src/Base/Ragnarok/AccountServer.pm
===================================================================
--- src/Base/Ragnarok/AccountServer.pm (revision 7792)
+++ src/Base/Ragnarok/AccountServer.pm (working copy)
@@ -9,7 +9,6 @@
Base::Ragnarok::AccountServer::AccountBanned
);
-use Globals qw($packetParser);
use Modules 'register';
use Base::RagnarokServer;
use base qw(Base::RagnarokServer);
@@ -61,26 +60,15 @@
die "This is an abstract method and has not been implemented.";
}
-sub process_0064 {
- my ($self, $client, $message) = @_;
- my ($switch, $version, $username, $password, $master_version) = unpack("v V Z24 Z24 C1", $message);
+sub master_login {
+ my ($self, $args, $client) = @_;
- if ($switch == 0x02B0) {
- # TODO: merge back with sendMasterHANLogin
- my $key = pack('C24', (6, 169, 33, 64, 54, 184, 161, 91, 81, 46, 3, 213, 52, 18, 0, 6, 61, 175, 186, 66, 157, 158, 180, 48));
- my $chain = pack('C24', (61, 175, 186, 66, 157, 158, 180, 48, 180, 34, 218, 128, 44, 159, 172, 65, 1, 2, 4, 8, 16, 32, 128));
- my $in = pack('a24', $password);
- my $rijndael = Utils::Rijndael->new();
- $rijndael->MakeKey($key, $chain, 24, 24);
- $password = unpack("Z24", $rijndael->Decrypt($in, undef, 24, 0));
- }
-
my $sessionID = $self->{sessionStore}->generateSessionID();
my %session = (
sessionID => $sessionID,
sessionID2 => $sessionID
);
- my $result = $self->login(\%session, $username, $password);
+ my $result = $self->login(\%session, @{$args}{qw(username password)});
if ($result == LOGIN_SUCCESS) {
my $output = '';
$self->{sessionStore}->add(\%session);
@@ -100,8 +88,9 @@
);
}
- $client->send($packetParser->reconstruct({
+ $client->send($self->{recvPacketParser}->reconstruct({
switch => 'account_server_info',
+ # maybe sessionstore should store sessionID as bytes?
sessionID => pack('V', $session{sessionID}),
accountID => $session{accountID},
sessionID2 => pack('V', $session{sessionID2}),
@@ -111,19 +100,19 @@
$client->close();
} elsif ($result == ACCOUNT_NOT_FOUND) {
- $client->send($packetParser->reconstruct({
+ $client->send($self->{recvPacketParser}->reconstruct({
switch => 'login_error',
type => Network::Receive::ServerType0::REFUSE_INVALID_ID,
}));
$client->close();
} elsif ($result == PASSWORD_INCORRECT) {
- $client->send($packetParser->reconstruct({
+ $client->send($self->{recvPacketParser}->reconstruct({
switch => 'login_error',
type => Network::Receive::ServerType0::REFUSE_INVALID_PASSWD,
}));
$client->close();
} elsif ($result == ACCOUNT_BANNED) {
- $client->send($packetParser->reconstruct({
+ $client->send($self->{recvPacketParser}->reconstruct({
switch => 'login_error',
type => Network::Receive::ServerType0::REFUSE_NOT_CONFIRMED,
}));
@@ -133,11 +122,10 @@
}
}
-# sendClientMD5Hash
-sub process_0204 {}
+sub client_hash {}
sub unhandledMessage {
- my ($self, $client) = @_;
+ my ($self, $args, $client) = @_;
$client->close();
}
Index: src/Base/RagnarokServer.pm
===================================================================
--- src/Base/RagnarokServer.pm (revision 7792)
+++ src/Base/RagnarokServer.pm (working copy)
@@ -17,6 +17,8 @@
my $self = $class->SUPER::new($port, $host);
$self->{serverType} = $serverType;
$self->{rpackets} = $rpackets;
+ $self->{recvPacketParser} = Network::Receive->create(undef, $serverType);
+ $self->{sendPacketParser} = Network::Send->create(undef, $serverType);
return $self;
}
@@ -50,20 +52,10 @@
sub onClientData {
my ($self, $client, $data) = @_;
$client->{tokenizer}->add($data);
- my $type;
- while (my $message = $client->{tokenizer}->readNext(\$type)) {
- if ($type == Network::MessageTokenizer::KNOWN_MESSAGE) {
- my $ID = Network::MessageTokenizer::getMessageID($message);
- my $handler = $self->can('process_' . (($ID eq $masterServer->{masterLogin_packet}) ? '0064' : $ID)); # temporary fix for servers that changed the masterLogin_packet switch
- if ($handler) {
- $handler->($self, $client, $message);
- } else {
- $self->unhandledMessage($client, $message);
- }
- } else {
- $client->close();
- }
- }
+
+ $client->{outbox} && $client->{outbox}->add($_) for $self->{sendPacketParser}->process(
+ $client->{tokenizer}, $self, $client
+ );
}
sub displayMessage {
@@ -75,4 +67,9 @@
sub unhandledMessage {
}
+sub unknownMessage {
+ my ($self, $args, $client) = @_;
+ $client->close;
+}
+
1;
Index: src/Commands.pm
===================================================================
--- src/Commands.pm (revision 7792)
+++ src/Commands.pm (working copy)
@@ -1410,9 +1410,9 @@
configModify("debug", 2);
} elsif ($arg1 eq "info") {
- my $connected = "server=".($net->serverAlive ? "yes" : "no").
+ my $connected = $net && "server=".($net->serverAlive ? "yes" : "no").
",client=".($net->clientAlive ? "yes" : "no");
- my $time = sprintf("%.2f", time - $lastPacketTime);
+ my $time = $packetParser && sprintf("%.2f", time - $packetParser->{lastPacketTime});
my $ai_timeout = sprintf("%.2f", time - $timeout{'ai'}{'time'});
my $ai_time = sprintf("%.4f", time - $ai_v{'AI_last_finished'});
@@ -1424,7 +1424,7 @@
"\$timeout{ai}: %.2f secs ago (value should be >%s)\n" .
"Last AI() call: %.2f secs ago\n" .
"-------------------------------------------\n",
- $conState, $connected, $AI, $AI_forcedOff, @ai_seq, $time, $ai_timeout,
+ $conState, $connected, $AI, $AI_forcedOff, "@ai_seq", $time, $ai_timeout,
$timeout{'ai'}{'timeout'}, $ai_time), "list";
}
}
@@ -1601,7 +1601,7 @@
$totalelasped = 0;
undef %itemChange;
$bytesSent = 0;
- $bytesReceived = 0;
+ $packetParser->{bytesProcessed} = 0 if $packetParser;
message T("Exp counter reset.\n"), "success";
return;
}
@@ -1649,7 +1649,7 @@
timeConvert($w_sec), formatNumber($totalBaseExp), $percentB, formatNumber($totalJobExp), $percentJ,
formatNumber($bExpPerHour), $percentBhr, formatNumber($jExpPerHour), $percentJhr,
formatNumber($zenyMade), formatNumber($zenyPerHour), timeConvert($EstB_sec), timeConvert($EstJ_sec),
- $char->{'deathCount'}, formatNumber($bytesSent), formatNumber($bytesReceived)), "info";
+ $char->{'deathCount'}, formatNumber($bytesSent), $packetParser && formatNumber($packetParser->{bytesProcessed})), "info";
if ($arg1 eq "") {
message("---------------------------------\n", "list");
Index: src/functions.pl
===================================================================
--- src/functions.pl (revision 7792)
+++ src/functions.pl (working copy)
@@ -23,6 +23,7 @@
use Interface;
use Network::Receive;
use Network::Send ();
+use Network::ClientReceive;
use Network::PaddedPackets;
use Network::MessageTokenizer;
use Commands;
@@ -306,6 +307,8 @@
our $XKore_dontRedirect = 0;
my $XKore_version = $config{XKore};
eval {
+ $clientPacketHandler = Network::ClientReceive->new;
+
if ($XKore_version eq "1") {
# Inject DLL to running Ragnarok process
require Network::XKore;
@@ -694,26 +697,10 @@
if (defined($data) && length($data) > 0) {
Benchmark::begin("parseMsg") if DEBUG;
- my $type;
$incomingMessages->add($data);
- while ($data = $incomingMessages->readNext(\$type)) {
- if ($type == Network::MessageTokenizer::KNOWN_MESSAGE) {
- parseIncomingMessage($data);
- } else {
- if ($type == Network::MessageTokenizer::UNKNOWN_MESSAGE) {
- # Unknown message - ignore it
- my $messageID = Network::MessageTokenizer::getMessageID($data);
- if (!existsInList($config{debugPacket_exclude}, $messageID)) {
- warning TF("Packet Tokenizer: Unknown switch: %s\n", $messageID), "connection";
- visualDump($data, "<< Received unknown packet") if ($config{debugPacket_unparsed});
- }
- } elsif ($config{debugPacket_received}) {
- debug "Received account ID\n", "parseMsg", 0 ;
- }
- # Pass it along to the client, whatever it is
- $net->clientSend($data);
- }
- }
+ $net->clientSend($_) for $packetParser->process(
+ $incomingMessages, $packetParser
+ );
$net->clientFlush() if (UNIVERSAL::isa($net, 'Network::XKoreProxy'));
Benchmark::end("parseMsg") if DEBUG;
}
@@ -723,9 +710,9 @@
if (defined($data) && length($data) > 0) {
my $type;
$outgoingClientMessages->add($data);
- while ($data = $outgoingClientMessages->readNext(\$type)) {
- parseOutgoingClientMessage($data);
- }
+ $net->serverSend($_) for $messageSender->process(
+ $outgoingClientMessages, $clientPacketHandler
+ );
}
# GameGuard support
@@ -987,6 +974,7 @@
}
+=pod
#######################################
#######################################
# Parse RO Client Send Message
@@ -1004,7 +992,7 @@
}
my $switch = Network::MessageTokenizer::getMessageID($msg);
- parseMessage_pre('ro_sent', $switch, $msg, $sendMsg);
+ parseMessage_pre('Network::Send', $switch, $msg, $sendMsg);
my $serverType = $masterServer->{serverType};
@@ -1311,12 +1299,13 @@
}
}
}
+=cut
sub parseMessage_pre {
my ($mode, $switch, $msg, $realMsg) = @_;
my ($title, $config_suffix, $desc_key, $dumpMethod5_word, $hook) = @{{
- 'received' => ['<< Received packet:', 'received', 'Recv', 'recv', 'parseMsg/pre'],
- 'ro_sent' => ['<< Sent by RO client:', 'ro_sent', 'Send', 'send', 'RO_sendMsg_pre'],
+ 'Network::Receive' => ['<< Received packet:', 'received', 'Recv', 'recv', 'parseMsg/pre'],
+ 'Network::ClientReceive' => ['<< Sent by RO client:', 'ro_sent', 'Send', 'send', 'RO_sendMsg_pre'],
}->{$mode}};
if ($config{'debugPacket_'.$config_suffix} && !existsInList($config{'debugPacket_exclude'}, $switch) ||
Index: src/Network/PacketParser.pm
===================================================================
--- src/Network/PacketParser.pm (revision 7792)
+++ src/Network/PacketParser.pm (working copy)
@@ -23,11 +23,13 @@
use encoding 'utf8';
use Carp::Assert;
use Scalar::Util;
+use Time::HiRes qw(time);
use Globals;
#use Settings;
use Log qw(message warning error debug);
#use FileParsers;
+use I18N qw(bytesToString stringToBytes);
use Interface;
use Network;
use Network::MessageTokenizer;
@@ -69,6 +71,7 @@
$self->{packet_list} = {};
$self->{packet_lut} = {};
+ $self->{bytesProcessed} = 0;
return bless $self, $class;
}
@@ -181,9 +184,8 @@
# - KEYS: list of argument names from {packet_list}
# `l`
sub parse {
- my ($self, $msg) = @_;
+ my ($self, $msg, $handleContainer, @handleArguments) = @_;
- $bytesReceived += length($msg);
my $switch = Network::MessageTokenizer::getMessageID($msg);
my $handler = $self->{packet_list}{$switch};
@@ -192,9 +194,7 @@
return undef;
}
- # set this alternative (if any) as the one in use with that server
- # TODO: permanent storage (with saving)?
- $self->{packet_lut}{$handler->[0]} = $switch;
+ # $handler->[0] may be (re)binded to $switch here for current serverType
debug "Received packet: $switch Handler: $handler->[0]\n", "packetParser", 2;
@@ -212,21 +212,21 @@
$self->$custom_parse(\%args);
}
- my $callback = $self->can($handler->[0]);
+ my $callback = $handleContainer->can($handler->[0]);
if ($callback) {
# Hook names can be made more uniform,
# but the ones for Receive must be kept for compatibility anyway.
+ # TODO: restrict to $Globals::packetParser and $Globals::messageSender?
if ($self->{hook_prefix} eq 'Network::Receive') {
Plugins::callHook("packet_pre/$handler->[0]", \%args);
} else {
Plugins::callHook("$self->{hook_prefix}/packet_pre/$handler->[0]", \%args);
}
Misc::checkValidity("Packet: " . $handler->[0] . " (pre)");
- $self->$callback(\%args);
+ $handleContainer->$callback(\%args, @handleArguments);
Misc::checkValidity("Packet: " . $handler->[0]);
} else {
- warning "Packet Parser: Unhandled Packet: $switch Handler: $handler->[0]\n";
- debug ("Unpacked: " . join(', ', @{\%args}{@{$handler->[2]}}) . "\n"), "packetParser", 2 if $handler->[2];
+ $handleContainer->unhandledMessage(\%args, @handleArguments);
}
if ($self->{hook_prefix} eq 'Network::Receive') {
@@ -237,6 +237,13 @@
return \%args;
}
+sub unhandledMessage {
+ my ($self, $args) = @_;
+
+ warning "Packet Parser: Unhandled Packet: $args->{switch} Handler: $self->{packet_list}{$args->{switch}}[0]\n";
+ debug ("Unpacked: " . join(', ', @{$args}{@{$args->{KEYS}}}) . "\n"), "packetParser", 2 if $args->{KEYS};
+}
+
##
# boolean $packetParser->willMangle(Bytes messageID)
# messageID: a message ID, such as "008A".
@@ -310,4 +317,103 @@
return $hook_args{return};
}
+sub process {
+ my ($self, $tokenizer, $handleContainer, @handleArguments) = @_;
+
+ my @result;
+ my $type;
+ while (my $message = $tokenizer->readNext(\$type)) {
+ $handleContainer->{bytesProcessed} += length($message);
+ $handleContainer->{lastPacketTime} = time;
+
+ my $args;
+
+ if ($type == Network::MessageTokenizer::KNOWN_MESSAGE) {
+ my $switch = Network::MessageTokenizer::getMessageID($message);
+
+ main::parseMessage_pre($handleContainer->{hook_prefix} || 'Network::ClientReceive', $switch, $message, $message);
+
+ my $willMangle = $handleContainer->can('willMangle') && $handleContainer->willMangle($switch);
+
+ if ($args = $self->parse($message, $handleContainer, @handleArguments)) {
+ $args->{mangle} ||= $willMangle && $handleContainer->mangle($args);
+ } else {
+ $args = {
+ switch => $switch,
+ RAW_MSG => $message,
+ (mangle => 2) x!! $willMangle,
+ };
+ }
+
+ } elsif ($type == Network::MessageTokenizer::ACCOUNT_ID) {
+ $args = {
+ RAW_MSG => $message
+ };
+
+ } elsif ($type == Network::MessageTokenizer::UNKNOWN_MESSAGE) {
+ $args = {
+ switch => Network::MessageTokenizer::getMessageID($message),
+ RAW_MSG => $message,
+ # RAW_MSG_SIZE => length($message),
+ };
+ $handleContainer->unknownMessage($args, @handleArguments);
+
+ } else {
+ die "Packet Tokenizer: Unknown type: $type";
+ }
+
+ unless ($args->{mangle}) {
+ # Packet was not mangled
+ push @result, $args->{RAW_MSG};
+ #$result .= $args->{RAW_MSG};
+ } elsif ($args->{mangle} == 1) {
+ # Packet was mangled
+ push @result, $self->reconstruct($args);
+ #$result .= $self->reconstruct($args);
+ } else {
+ # Packet was suppressed
+ }
+ }
+
+ # If we're running in X-Kore mode, pass messages back to the RO client.
+
+ # It seems like messages can't be just concatenated safely
+ # (without "use bytes" pragma or messing with unicode stuff)
+ # http://perldoc.perl.org/perlunicode.html#The-%22Unicode-Bug%22
+ return @result;
+}
+
+sub unknownMessage {
+ my ($self, $args) = @_;
+
+ # Unknown message - ignore it
+ unless (existsInList($config{debugPacket_exclude}, $args->{switch})) {
+ warning TF("Packet Tokenizer: Unknown switch: %s\n", $args->{switch}), 'connection';
+ visualDump($args->{RAW_MSG}, "<< Received unknown packet") if $config{debugPacket_unparsed};
+ }
+
+ # Pass it along to the client, whatever it is
+}
+
+# Utility methods used by both Receive and Send
+
+sub parseChat {
+ my ($self, $args) = @_;
+ $args->{message} = bytesToString($args->{message});
+ if ($args->{message} =~ /^(.*?)\s{1,2}:\s{1,2}(.*)$/) {
+ $args->{name} = $1;
+ $args->{message} = $2;
+ stripLanguageCode(\$args->{message});
+ }
+ if (exists $args->{ID}) {
+ $args->{actor} = Actor::get($args->{ID});
+ }
+}
+
+sub reconstructChat {
+ my ($self, $args) = @_;
+ $args->{message} = '|00' . $args->{message} if $config{chatLangCode} && $config{chatLangCode} ne 'none';
+ $args->{message} = stringToBytes($char->{name}) . ' : ' . stringToBytes($args->{message});
+}
+
1;
Index: src/Network/Receive/ServerType0.pm
===================================================================
--- src/Network/Receive/ServerType0.pm (revision 7792)
+++ src/Network/Receive/ServerType0.pm (working copy)
@@ -109,7 +109,7 @@
'0097' => ['private_message', 'v Z24 Z*', [qw(len privMsgUser privMsg)]],
'0098' => ['private_message_sent', 'C', [qw(type)]],
'009A' => ['system_chat', 'v a*', [qw(len message)]], #maybe use a* instead and $message =~ /\000$//; if there are problems
- '009C' => ['actor_look_at', 'a4 C x C', [qw(ID head body)]],
+ '009C' => ['actor_look_at', 'a4 v C', [qw(ID head body)]],
'009D' => ['item_exists', 'a4 v C v3 C2', [qw(ID nameID identified x y amount subx suby)]],
'009E' => ['item_appeared', 'a4 v C v2 C2 v', [qw(ID nameID identified x y subx suby amount)]],
'00A0' => ['inventory_item_added', 'v3 C3 a8 v C2', [qw(index amount nameID identified broken upgrade cards type_equip type fail)]],
@@ -3176,6 +3176,7 @@
message T("Server granted login request to account server\n"), "poseidon";
} else {
message T("Server granted login request to char/map server\n"), "poseidon";
+ # FIXME
change_to_constate25 if ($config{'gameGuard'} eq "2");
}
$net->setState(1.3) if ($net->getState() == 1.2);
Index: src/Network/Receive/iRO.pm
===================================================================
--- src/Network/Receive/iRO.pm (revision 7792)
+++ src/Network/Receive/iRO.pm (working copy)
@@ -35,6 +35,12 @@
$self->{packet_list}{$switch} = $packets{$switch};
}
+ my %handlers = qw(
+ received_characters 082D
+ account_id 0283
+ );
+ $self->{packet_lut}{$_} = $handlers{$_} for keys %handlers;
+
return $self;
}
Index: src/Network/Send/ServerType0.pm
===================================================================
--- src/Network/Send/ServerType0.pm (revision 7792)
+++ src/Network/Send/ServerType0.pm (working copy)
@@ -25,7 +25,7 @@
use Globals qw($accountID $sessionID $sessionID2 $accountSex $char $charID %config %guild @chars $masterServer $syncSync);
use Log qw(debug);
use Translation qw(T TF);
-use I18N qw(stringToBytes);
+use I18N qw(bytesToString stringToBytes);
use Utils;
use Utils::Exceptions;
use Utils::Rijndael;
@@ -39,16 +39,45 @@
my $self = $class->SUPER::new(@_);
my %packets = (
- '0064' => ['master_login', 'V a24 a24 C', [qw(version username password master_version)]],
- '0134' => ['buy_bulk_vender', 'v a4 a*', [qw(len venderID itemInfo)]],
- '02B0' => ['master_login', 'V a24 a24 C H32 H26 C', [qw(version username password_rijndael master_version ip mac isGravityID)]],
- '0801' => ['buy_bulk_vender', 'v a4 a4 a*', [qw(len venderID venderCID itemInfo)]],
+ '0064' => ['master_login', 'V Z24 Z24 C', [qw(version username password master_version)]],
+ '0065' => ['game_login', 'a4 a4 a4 v C', [qw(accountID sessionID sessionID2 userLevel accountSex)]],
+ '0066' => ['char_login', 'C', [qw(slot)]],
+ '0067' => ['char_create'], # TODO
+ '0068' => ['char_delete'], # TODO
+ '0072' => ['map_login', 'a4 a4 a4 V C', [qw(accountID charID sessionID tick sex)]],
+ '007D' => ['map_loaded'], # len 2
+ '007E' => ['sync'], # TODO
+ '0089' => ['actor_action', 'a4 C', [qw(targetID type)]],
+ '008C' => ['public_chat', 'x2 Z*', [qw(message)]],
+ '0096' => ['private_message', 'x2 Z24 Z*', [qw(privMsgUser privMsg)]],
+ '009B' => ['actor_look_at', 'v C', [qw(head body)]],
+ '009F' => ['item_take', 'a4', [qw(ID)]],
+ '00B2' => ['restart', 'C', [qw(type)]],
+ '00F3' => ['map_login', '', [qw()]],
+ '0108' => ['party_chat', 'x2 Z*', [qw(message)]],
+ '0134' => ['buy_bulk_vender', 'x2 a4 a*', [qw(venderID itemInfo)]],
+ '0149' => ['alignment', 'a4 C v', [qw(targetID type point)]],
+ '014D' => ['guild_check'], # len 2
+ '014F' => ['guild_info_request', 'V', [qw(type)]],
+ '017E' => ['guild_chat', 'x2 Z*', [qw(message)]],
+ '0187' => ['ban_check', 'a4', [qw(accountID)]],
+ '018A' => ['quit_request', 'v', [qw(type)]],
+ '01B2' => ['shop_open'], # TODO
+ '012E' => ['shop_close'], # len 2
+ '0204' => ['client_hash'], # TODO
+ '021D' => ['less_effect'], # TODO
+ '0275' => ['game_login', 'a4 a4 a4 v C x16 v', [qw(accountID sessionID sessionID2 userLevel accountSex iAccountSID)]],
+ '02B0' => ['master_login', 'V Z24 Z24 C H32 H26 C', [qw(version username password_rijndael master_version ip mac isGravityID)]],
+ '0436' => ['map_login', 'a4 a4 a4 V C', [qw(accountID charID sessionID tick sex)]],
+ '0801' => ['buy_bulk_vender', 'x2 a4 a4 a*', [qw(venderID venderCID itemInfo)]],
);
$self->{packet_list}{$_} = $packets{$_} for keys %packets;
# # it would automatically use the first available if not set
# my %handlers = qw(
# master_login 0064
+ # game_login 0065
+ # map_login 0072
# buy_bulk_vender 0134
# );
# $self->{packet_lut}{$_} = $handlers{$_} for keys %handlers;
@@ -74,8 +103,11 @@
sub sendAlignment {
my ($self, $ID, $alignment) = @_;
- my $msg = pack("C*", 0x49, 0x01) . $ID . pack("C*", $alignment);
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({
+ switch => 'alignment',
+ targetID => $ID,
+ type => $alignment,
+ }));
debug "Sent Alignment: ".getHex($ID).", $alignment\n", "sendPacket", 2;
}
@@ -101,8 +133,7 @@
return;
}
- my $msg = pack('v a4 C', 0x0089, $monID, $flag);
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({switch => 'actor_action', targetID => $monID, type => $flag}));
debug "Sent Action: " .$flag. " on: " .getHex($monID)."\n", "sendPacket", 2;
}
@@ -126,8 +157,10 @@
sub sendBanCheck {
my ($self, $ID) = @_;
- my $msg = pack("C*", 0x87, 0x01) . $ID;
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({
+ switch => 'ban_check',
+ accountID => $ID,
+ }));
debug "Sent Account Ban Check Request : " . getHex($ID) . "\n", "sendPacket", 2;
}
@@ -223,23 +256,22 @@
sub sendCharLogin {
my ($self, $char) = @_;
- my $msg = pack("C*", 0x66,0) . pack("C*",$char);
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({switch => 'char_login', slot => $char}));
}
+sub parse_public_chat {
+ my ($self, $args) = @_;
+ $self->parseChat($args);
+}
+
+sub reconstruct_public_chat {
+ my ($self, $args) = @_;
+ $self->reconstructChat($args);
+}
+
sub sendChat {
my ($self, $message) = @_;
- $message = "|00$message" if ($config{chatLangCode} && $config{chatLangCode} ne "none");
-
- my ($data, $charName); # Type: Bytes
- $message = stringToBytes($message); # Type: Bytes
- $charName = stringToBytes($char->{name});
-
- $data = pack("C*", 0x8C, 0x00) .
- pack("v*", length($charName) + length($message) + 8) .
- $charName . " : " . $message . chr(0);
-
- $self->sendToServer($data);
+ $self->sendToServer($self->reconstruct({switch => 'public_chat', message => $message}));
}
sub sendChatRoomBestow {
@@ -313,8 +345,7 @@
sub sendCloseShop {
my $self = shift;
- my $msg = pack("C*", 0x2E, 0x01);
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({switch => 'shop_close'}));
debug "Shop Closed\n", "sendPacket", 2;
}
@@ -467,14 +498,21 @@
debug "Sent Forge, Produce Item: $ID\n" , 2;
}
+sub reconstruct_game_login {
+ my ($self, $args) = @_;
+ $args->{userLevel} = 0 unless exists $args->{userLevel};
+ ($args->{iAccountSID}) = $masterServer->{ip} =~ /\d+\.\d+\.\d+\.(\d+)/ unless exists $args->{iAccountSID};
+}
+
sub sendGameLogin {
my ($self, $accountID, $sessionID, $sessionID2, $sex) = @_;
- my $msg = pack("v1", hex($masterServer->{gameLogin_packet}) || 0x65) . $accountID . $sessionID . $sessionID2 . pack("C*", 0, 0, $sex);
- if (hex($masterServer->{gameLogin_packet}) == 0x0273 || hex($masterServer->{gameLogin_packet}) == 0x0275) {
- my ($serv) = $masterServer->{ip} =~ /\d+\.\d+\.\d+\.(\d+)/;
- $msg .= pack("x16 C1 x3", $serv);
- }
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({
+ switch => 'game_login',
+ accountID => $accountID,
+ sessionID => $sessionID,
+ sessionID2 => $sessionID2,
+ accountSex => $sex,
+ }));
debug "Sent sendGameLogin\n", "sendPacket", 2;
}
@@ -536,18 +574,19 @@
debug "Sent Guild Break: $guildName\n", "sendPacket", 2;
}
+sub parse_guild_chat {
+ my ($self, $args) = @_;
+ $self->parseChat($args);
+}
+
+sub reconstruct_guild_chat {
+ my ($self, $args) = @_;
+ $self->reconstructChat($args);
+}
+
sub sendGuildChat {
my ($self, $message) = @_;
-
- my ($charName);
- $message = "|00$message" if ($config{chatLangCode} && $config{chatLangCode} ne "none");
- $message = stringToBytes($message);
- $charName = stringToBytes($char->{name});
-
- my $data = pack("C*",0x7E, 0x01) .
- pack("v*", length($charName) + length($message) + 8) .
- $charName . " : " . $message . chr(0);
- $self->sendToServer($data);
+ $self->sendToServer($self->reconstruct({switch => 'guild_chat', message => $message}));
}
sub sendGuildCreate {
@@ -560,8 +599,7 @@
sub sendGuildMasterMemberCheck {
my ($self, $ID) = @_;
- my $msg = pack("v", 0x014D);
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({switch => 'guild_check'}));
debug "Sent Guild Master/Member Check.\n", "sendPacket";
}
@@ -654,8 +692,10 @@
sub sendGuildRequestInfo {
my ($self, $page) = @_; # page 0-4
- my $msg = pack("C*", 0x4f, 0x01).pack("V1", $page);
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({
+ switch => 'guild_info_request',
+ type => $page,
+ }));
debug "Sent Guild Request Page : ".$page."\n", "sendPacket";
}
@@ -765,9 +805,7 @@
sub sendLook {
my ($self, $body, $head) = @_;
- my $msg;
- $msg = pack("C*", 0x9B, 0x00, $head, 0x00, $body);
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({switch => 'actor_look_at', body => $body, head => $head}));
debug "Sent look: $body $head\n", "sendPacket", 2;
$char->{look}{head} = $head;
$char->{look}{body} = $body;
@@ -775,11 +813,9 @@
sub sendMapLoaded {
my $self = shift;
- my $msg;
$syncSync = pack("V", getTickCount());
- $msg = pack("C*", 0x7D,0x00);
debug "Sending Map Loaded\n", "sendPacket";
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({switch => 'map_loaded'}));
Plugins::callHook('packet/sendMapLoaded');
}
@@ -789,13 +825,14 @@
$sex = 0 if ($sex > 1 || $sex < 0); # Sex can only be 0 (female) or 1 (male)
if ($self->{serverType} == 0 || $self->{serverType} == 21 || $self->{serverType} == 22) {
- # Server Type 21 is tRO (2008-09-16Ragexe12_Th), 22 is idRO
- $msg = pack("C*", 0x72,0) .
- $accountID .
- $charID .
- $sessionID .
- pack("V1", getTickCount()) .
- pack("C*",$sex);
+ $msg = $self->reconstruct({
+ switch => 'map_login',
+ accountID => $accountID,
+ charID => $charID,
+ sessionID => $sessionID,
+ tick => getTickCount,
+ sex => $sex,
+ });
} else { #oRO and pRO
my $key;
@@ -859,9 +896,9 @@
sub reconstruct_master_login {
my ($self, $args) = @_;
- exists $args->{ip} or $args->{ip} = '3139322e3136382e322e3400685f4c40'; # gibberish
- exists $args->{mac} or $args->{mac} = '31313131313131313131313100'; # gibberish
- exists $args->{isGravityID} or $args->{isGravityID} = 0;
+ $args->{ip} = '3139322e3136382e322e3400685f4c40' unless exists $args->{ip}; # gibberish
+ $args->{mac} = '31313131313131313131313100' unless exists $args->{mac}; # gibberish
+ $args->{isGravityID} = 0 unless exists $args->{isGravityID};
my $key = pack('C24', (6, 169, 33, 64, 54, 184, 161, 91, 81, 46, 3, 213, 52, 18, 0, 6, 61, 175, 186, 66, 157, 158, 180, 48));
my $chain = pack('C24', (61, 175, 186, 66, 157, 158, 180, 48, 180, 34, 218, 128, 44, 159, 172, 65, 1, 2, 4, 8, 16, 32, 128));
@@ -982,18 +1019,20 @@
$self->sendToServer($msg);
}
+sub parse_party_chat {
+ my ($self, $args) = @_;
+ $self->parseChat($args);
+}
+
+sub reconstruct_party_chat {
+ my ($self, $args) = @_;
+ $self->reconstructChat($args);
+}
+
sub sendPartyChat {
my $self = shift;
my $message = shift;
-
- my $charName;
- $message = "|00$message" if ($config{chatLangCode} && $config{chatLangCode} ne "none");
- $message = stringToBytes($message);
- $charName = stringToBytes($char->{name});
-
- my $msg = pack("C*",0x08, 0x01) . pack("v*", length($charName) + length($message) + 8) .
- $charName . " : " . $message . chr(0);
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({switch => 'party_chat', message => $message}));
}
sub sendPartyJoin {
@@ -1144,22 +1183,32 @@
debug "Sent pre-login packet $type\n", "sendPacket", 2;
}
+sub parse_private_message {
+ my ($self, $args) = @_;
+ $args->{privMsg} = bytesToString($args->{privMsg});
+ stripLanguageCode(\$args->{privMsg});
+ $args->{privMsgUser} = bytesToString($args->{privMsgUser});
+}
+
+sub reconstruct_private_message {
+ my ($self, $args) = @_;
+ $args->{privMsg} = '|00' . $args->{privMsg} if $config{chatLangCode} && $config{chatLangCode} ne 'none';
+ $args->{privMsg} = stringToBytes($args->{privMsg});
+ $args->{privMsgUser} = stringToBytes($args->{privMsgUser});
+}
+
sub sendPrivateMsg {
my ($self, $user, $message) = @_;
-
- $message = "|00$message" if ($config{chatLangCode} && $config{chatLangCode} ne "none");
- $message = stringToBytes($message);
- $user = stringToBytes($user);
-
- my $msg = pack("C*", 0x96, 0x00) . pack("v*", length($message) + 29) . $user .
- chr(0) x (24 - length($user)) . $message . chr(0);
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({
+ switch => 'private_message',
+ privMsg => $message,
+ privMsgUser => $user,
+ }));
}
sub sendQuit {
my $self = shift;
- my $msg = pack("C*", 0x8A, 0x01, 0x00, 0x00);
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({switch => 'quit_request', type => 0}));
debug "Sent Quit\n", "sendPacket", 2;
}
@@ -1220,7 +1269,7 @@
# type: 0=respawn ; 1=return to char select
sub sendRestart {
my ($self, $type) = @_;
- $self->sendToServer(pack('v C', 0x00B2, $type));
+ $self->sendToServer($self->reconstruct({switch => 'restart', type => $type}));
debug "Sent Restart: " . ($type ? 'Quit To Char Selection' : 'Respawn') . "\n", "sendPacket", 2;
}
@@ -1425,9 +1474,7 @@
sub sendTake {
my ($self, $itemID) = @_;
- my $msg;
- $msg = pack("C*", 0x9F, 0x00) . $itemID;
- $self->sendToServer($msg);
+ $self->sendToServer($self->reconstruct({switch => 'item_take', ID => $itemID}));
debug "Sent take\n", "sendPacket", 2;
}
Index: src/Network/XKore2/MapServer.pm
===================================================================
--- src/Network/XKore2/MapServer.pm (revision 7792)
+++ src/Network/XKore2/MapServer.pm (working copy)
@@ -20,7 +20,7 @@
$portalsList $npcsList $monstersList $playersList $petsList
@friendsID %friends %pet @partyUsersID %spells
@chatRoomsID %chatRooms @venderListsID %venderLists $hotkeyList
- $packetParser %config
+ %config
);
use Base::Ragnarok::MapServer;
use base qw(Base::Ragnarok::MapServer);
@@ -56,10 +56,10 @@
}
}
-sub handleMapLoaded {
+sub map_loaded {
# The RO client has finished loading the map.
# Send character information to the RO client.
- my ($self, $client) = @_;
+ my ($self, $args, $client) = @_;
no encoding 'utf8';
use bytes;
@@ -239,7 +239,7 @@
shiftPack(\$coords, $portal->{pos}{x}, 10);
shiftPack(\$coords, $portal->{pos}{y}, 10);
shiftPack(\$coords, 0, 4);
- $output .= $packetParser->reconstruct({
+ $output .= $self->{recvPacketParser}->reconstruct({
switch => '0078',
coords => $coords,
map { $_ => $portal->{$_} } qw(ID type)
@@ -254,7 +254,7 @@
shiftPack(\$coords, $npc->{pos}{x}, 10);
shiftPack(\$coords, $npc->{pos}{y}, 10);
shiftPack(\$coords, $npc->{look}{body}, 4);
- $output .= $packetParser->reconstruct({
+ $output .= $self->{recvPacketParser}->reconstruct({
switch => '0078',
coords => $coords,
map { $_ => $npc->{$_} } qw(ID opt1 opt2 option type)
@@ -269,7 +269,7 @@
shiftPack(\$coords, $monster->{pos_to}{x}, 10);
shiftPack(\$coords, $monster->{pos_to}{y}, 10);
shiftPack(\$coords, $monster->{look}{body}, 4);
- $output .= $packetParser->reconstruct({
+ $output .= $self->{recvPacketParser}->reconstruct({
switch => '0078',
walk_speed => $monster->{walk_speed} * 1000,
coords => $coords,
@@ -285,7 +285,7 @@
shiftPack(\$coords, $pet->{pos_to}{x}, 10);
shiftPack(\$coords, $pet->{pos_to}{y}, 10);
shiftPack(\$coords, $pet->{look}{body}, 4);
- $output .= $packetParser->reconstruct({
+ $output .= $self->{recvPacketParser}->reconstruct({
switch => '0078',
walk_speed => $pet->{walk_speed} * 1000,
coords => $coords,
@@ -420,76 +420,52 @@
$client->send($output);
if ($config{verbose} && !$config{XKore_silent}) {
- $client->send($packetParser->reconstruct({switch => '009A', message => $Settings::welcomeText}));
+ $client->send($self->{recvPacketParser}->reconstruct({
+ switch => 'system_chat',
+ message => $Settings::welcomeText,
+ }));
}
+
+ $args->{mangle} = 2;
}
-sub process_007D {
- my ($self, $client) = @_;
- handleMapLoaded($self, $client);
-}
-
-sub process_01C0 {
- my ($self, $client) = @_;
- handleMapLoaded($self, $client);
-}
-
-sub process_00B2 {
- my ($self, $client) = @_;
+sub restart {
+ my ($self, $args, $client) = @_;
# If they want to character select/respawn, kick them to the login screen
# immediately (GM kick)
$client->send(pack('C3', 0x81, 0, 15));
+
+ $args->{mangle} = 2;
}
-sub process_018A {
- my ($self, $client) = @_;
+sub quit_request {
+ my ($self, $args, $client) = @_;
# Client wants to quit
$client->send(pack('C*', 0x8B, 0x01, 0, 0));
+
+ $args->{mangle} = 2;
}
-sub handleSync {
- my ($self, $client, $message) = @_;
- my $ID = Network::MessageTokenizer::getMessageID($message);
- my $serverType = $self->getServerType();
- if (
- ($ID eq "007E" && (
- $serverType == 0 ||
- $serverType == 1 ||
- $serverType == 2 ||
- $serverType == 6 ||
- $serverType == 21)
- )
- || ($ID eq "0089" && (
- $serverType == 3 ||
- $serverType == 5 ||
- $serverType == 8)
- )
- || ($ID eq "0116" &&
- $serverType == 4 )
- || ($ID eq "00A7" &&
- $serverType == 18)
- ) {
- # Surpress client sync message.
- } else {
- &unhandledMessage;
- }
+sub sync {
+ my ($self, $args, $client) = @_;
+ $args->{mangle} = 2;
}
-sub process_007E { &handleSync; }
-sub process_0089 { &handleSync; }
-sub process_0116 { &handleSync; }
-sub process_00A7 { &handleSync }
-
# Not sure what these are, but don't let it get to the RO server.
-sub process_021D {}
-sub process_014D {}
-sub process_014F {}
-sub process_0181 {}
+sub less_effect {
+ my ($self, $args, $client) = @_;
+ $args->{mangle} = 2;
+}
-sub unhandledMessage {
- my ($self, $client, $message) = @_;
- $client->{outbox}->add($message);
+sub guild_check {
+ my ($self, $args, $client) = @_;
+ $args->{mangle} = 2;
}
+sub guild_info_request {
+ my ($self, $args, $client) = @_;
+ $args->{mangle} = 2;
+}
+
1;
Index: src/Network/ClientReceive.pm
===================================================================
--- src/Network/ClientReceive.pm (revision 0)
+++ src/Network/ClientReceive.pm (revision 0)
@@ -0,0 +1,221 @@
+#########################################################################
+# This software is open source, licensed under the GNU General Public
+# License, version 2.
+# Basically, this means that you're allowed to modify and distribute
+# this software. However, if you distribute modified versions, you MUST
+# also distribute the source code.
+# See http://www.gnu.org/licenses/gpl.html for the full license.
+#########################################################################
+##
+# MODULE DESCRIPTION: Outgoing client messages handling
+#
+# This class contains only handler functions
+# which are used to handle messages
+# that are sent by the RO client, if it's present.
+
+package Network::ClientReceive;
+
+use strict;
+use Modules 'register';
+use Time::HiRes qw(time);
+
+use Globals qw($packetParser $incomingMessages %config $char %ai_v %timeout $shopstarted $firstLoginMap $sentWelcomeMessage @lastpm %lastpm);
+use Misc qw(configModify visualDump);
+use Log qw(message warning);
+use Translation;
+use Utils qw(existsInList);
+
+sub new {
+ my $self = {};
+
+ $self->{hook_prefix} = 'Network::ClientReceive';
+
+ bless $self, $_[0];
+}
+
+sub handleChat {
+ my ($self, $args, $chat) = @_;
+
+ my $prefix = quotemeta $config{commandPrefix};
+ if ($chat =~ /^$prefix/) {
+ $chat =~ s/^$prefix//;
+ $chat =~ s/^\s*//;
+ $chat =~ s/\s*$//;
+ main::parseInput($chat, 1);
+ $args->{mangle} = 2;
+ return 1;
+ }
+}
+
+sub game_login {
+ $incomingMessages->nextMessageMightBeAccountID;
+}
+
+sub char_login {
+ my ($self, $args) = @_;
+
+ configModify('char', $args->{slot});
+}
+
+sub map_login {
+ my ($self, $args) = @_;
+
+ $incomingMessages->nextMessageMightBeAccountID;
+
+ if ($config{sex} ne '') {
+ $args->{sex} = $config{sex};
+ $args->{mangle} = 1;
+ }
+}
+
+sub map_loaded {
+ $packetParser->changeToInGameState;
+ AI::clear('clientSuspend');
+ $timeout{ai}{time} = time;
+ if ($firstLoginMap) {
+ undef $sentWelcomeMessage;
+ undef $firstLoginMap;
+ }
+ $timeout{welcomeText}{time} = time;
+ $ai_v{portalTrace_mapChanged} = time;
+ message T("Map loaded\n"), 'connection';
+
+ Plugins::callHook('map_loaded');
+}
+
+sub actor_action {
+ my ($self, $args) = @_;
+
+ unless ($config{tankMode} || AI::inQueue('attack')) {
+ AI::clear('clientSuspend');
+ $char->clientSuspend($args->{switch}, 2, $args->{type}, $args->{targetID});
+ } else {
+ $args->{mangle} = 2;
+ }
+}
+
+sub public_chat {
+ my ($self, $args) = @_;
+
+ $self->handleChat($args, $args->{message});
+}
+
+sub private_message {
+ my ($self, $args) = @_;
+
+ unless ($self->handleChat($args, $args->{privMsg})) {
+ undef %lastpm;
+ @lastpm{qw(msg user)} = @{$args}{qw(privMsg privMsgUser)};
+ push @lastpm, {%lastpm};
+ }
+}
+
+sub actor_look_at {
+ my ($self, $args) = @_;
+
+ @{$char->{look}}{qw(head body)} = @{$args}{qw(head body)};
+}
+
+sub item_take {
+ my ($self, $args) = @_;
+
+ AI::clear('clientSuspend');
+ $char->clientSuspend($args->{switch}, 2, $args->{ID});
+}
+
+sub restart {
+ my ($self, $args) = @_;
+
+ AI::clear('clientSuspend');
+ $char->clientSuspend($args->{switch}, 10);
+}
+
+sub party_chat {
+ my ($self, $args) = @_;
+
+ $self->handleChat($args, $args->{message});
+}
+
+sub alignment {
+ my ($self, $args) = @_;
+
+ # Chat/skill mute
+ $args->{mangle} = 2;
+}
+
+sub guild_chat {
+ my ($self, $args) = @_;
+
+ $self->handleChat($args, $args->{message});
+}
+
+sub quit_request {
+ my ($self, $args) = @_;
+
+ AI::clear('clientSuspend');
+ $char->clientSuspend($args->{switch}, 10);
+}
+
+sub shop_open {
+ # client started a shop manually
+ $shopstarted = 1;
+}
+
+sub shop_close {
+ # client stopped shop manually
+ $shopstarted = 0;
+}
+
+=pod
+# sendSync
+ if ($masterServer->{syncID} && $switch eq sprintf('%04X', hex($masterServer->{syncID}))) {
+ #syncSync support for XKore 1 mode
+ $syncSync = substr($msg, $masterServer->{syncTickOffset}, 4);
+
+# sendSync
+ } elsif ($switch eq "00A7") {
+ if($masterServer && $masterServer->{paddedPackets}) {
+ $syncSync = substr($msg, 8, 4);
+ }
+
+# sendSync
+ } elsif ($switch eq "007E") {
+ if ($masterServer && $masterServer->{paddedPackets}) {
+ $syncSync = substr($msg, 4, 4);
+ }
+
+# sendMapLoaded
+ } elsif ($switch eq "007D") {
+ # syncSync support for XKore 1 mode
+ if($masterServer->{serverType} == 11) {
+ $syncSync = substr($msg, 8, 4);
+ } else {
+ # formula: MapLoaded_len + Sync_len - 4 - Sync_packet_last_junk
+ $syncSync = substr($msg, $masterServer->{mapLoadedTickOffset}, 4);
+ }
+
+# sendMove
+ } elsif ($switch eq "0085") {
+ #if ($masterServer->{serverType} == 0 || $masterServer->{serverType} == 1 || $masterServer->{serverType} == 2) {
+ # #Move
+ # AI::clear("clientSuspend");
+ # makeCoordsDir(\%coords, substr($msg, 2, 3));
+ # ai_clientSuspend($switch, (distance($char->{'pos'}, \%coords) * $char->{walk_speed}) + 4);
+ #}
+=cut
+
+sub unhandledMessage {}
+
+sub unknownMessage {
+ my ($self, $args) = @_;
+
+ # Unknown message - ignore it
+ unless (existsInList($config{debugPacket_exclude}, $args->{switch})) {
+ warning TF("Packet Tokenizer: Unknown outgoing switch: %s\n", $args->{switch}), 'connection';
+ visualDump($args->{RAW_MSG}, "<< Outgoing unknown packet") if $config{debugPacket_unparsed};
+ }
+
+ # Pass it along to the server, whatever it is
+}
+
+1;
Index: src/Network/Receive.pm
===================================================================
--- src/Network/Receive.pm (revision 7792)
+++ src/Network/Receive.pm (working copy)
@@ -43,6 +43,25 @@
### CATEGORY: Class methods
######################################
+# Just a wrapper for SUPER::parse.
+sub parse {
+ my $self = shift;
+ my $args = $self->SUPER::parse(@_);
+
+ if ($config{debugPacket_received} == 3 &&
+ existsInList($config{'debugPacket_include'}, $args->{switch})) {
+ my $packet = $self->{packet_list}{$args->{switch}};
+ my ($name, $packString, $varNames) = @{$packet};
+
+ my @vars = ();
+ for my $varName (@{$varNames}) {
+ message "$varName = $args->{$varName}\n";
+ }
+ }
+
+ return $args;
+}
+
##
# Network::Receive->decrypt(r_msg, themsg)
# r_msg: a reference to a scalar.
Fix parseMessage_pre and other debug output.
Update tRO serverType to get rid of gameLogin_packet in server settings.
XKore modes wouldn't work properly with kRO ST until it would have packet_list with all required packets in Send part.
XKore 2 won't work properly until used ST would have packet_lut with "account_id 0283" if needed, and possibly other ambiguous packets in Receive part which are used to login and construct initial game state (like "received_characters 082D" for iRO).
Rewrite Poseidon using Base::AccountServer etc.
Handler implementations should be separated from packet information in Receive, like in new module ClientReceive.
Some policy for naming packet handlers.
Structures for all packets in Send.