tables/servers.txt
Code: Select all
[Philippines - pRO: Valkyrie]
ip 202.57.118.235
port 6900
master_version 15
version 1
secureLogin 1
secureLogin_type 0
secureLogin_requestCode
secureLogin_account 0
serverType pRO
patchserver wpatch.ragnarok.com.ph
patchpath /patch50
storageEncryptKey 0x050B6F79, 0x0202C179, 0x0E20120, 0x04FA43E3, 0x0179B6C8, 0x05973DF2, 0x07D8D6B, 0x08CB9ED9
serverEncoding Tagalog
charBlockSize 144
gameGuard 1
recvpackets recvpackets.txt
addTableFolders pRO
pinCode 1
Heres my pRO.pm
\src\Network\Receive\pRO.pm
Code: Select all
#########################################################################
# OpenKore - Network subsystem
# Copyright (c) 2006 OpenKore Team
#
# 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.
#########################################################################
# pRO (Philippines)
# Servertype overview: http://wiki.openkore.com/index.php/ServerType
package Network::Receive::pRO;
use strict;
use base qw(Network::Receive::ServerType0);
use Globals;
use Log qw(message warning error debug);
use Translation;
use Misc;
use Utils qw(timeOut getFormattedDate);
use I18N qw(bytesToString stringToBytes);
use Time::HiRes qw(time);
sub new {
my ($class) = @_;
my $self = $class->SUPER::new(@_);
my %packets = (
#'006D' => ['character_creation_successful', 'a4 V9 v V2 v14 Z24 C6 v2', [qw(charID exp zeny exp_job lv_job opt1 opt2 option stance manner points_free hp hp_max sp sp_max walk_speed type hair_style weapon lv points_skill lowhead shield tophead midhead hair_color clothes_color name str agi vit int dex luk slot renameflag)]],
'0097' => ['private_message', 'v Z24 V Z*', [qw(len privMsgUser flag privMsg)]], # -1
#'082D' => ['received_characters_info', 'x2 C5 x20', [qw(normal_slot premium_slot billing_slot producible_slot valid_slot)]],
#'099D' => ['received_characters', 'v a*', [qw(len charInfo)]],
);
foreach my $switch (keys %packets) {
$self->{packet_list}{$switch} = $packets{$switch};
}
my %handlers = qw(
received_characters 099D
actor_exists 0856
actor_connected 0857
actor_moved 0858
account_id 0283
);
$self->{packet_lut}{$_} = $handlers{$_} for keys %handlers;
return $self;
}
=pod
sub received_characters_info {
my ($self, $args) = @_;
Scalar::Util::weaken(my $weak = $self);
my $timeout = {timeout => 6, time => time};
$self->{charSelectTimeoutHook} = Plugins::addHook('Network::serverConnect/special' => sub {
if ($weak && timeOut($timeout)) {
$weak->received_characters({charInfo => '', RAW_MSG_SIZE => 4});
}
});
$self->{charSelectHook} = Plugins::addHook(charSelectScreen => sub {
if ($weak) {
Plugins::delHook(delete $weak->{charSelectTimeoutHook}) if $weak->{charSelectTimeoutHook};
}
});
$timeout{charlogin}{time} = time;
$self->received_characters($args);
}
sub received_characters {
return if ($net->getState() == Network::IN_GAME);
my ($self, $args) = @_;
$net->setState(Network::CONNECTED_TO_LOGIN_SERVER);
$charSvrSet{normal_slot} = $args->{normal_slot} if (exists $args->{normal_slot});
$charSvrSet{premium_slot} = $args->{premium_slot} if (exists $args->{premium_slot});
$charSvrSet{billing_slot} = $args->{billing_slot} if (exists $args->{billing_slot});
$charSvrSet{producible_slot} = $args->{producible_slot} if (exists $args->{producible_slot});
$charSvrSet{valid_slot} = $args->{valid_slot} if (exists $args->{valid_slot});
undef $conState_tries;
Plugins::callHook('parseMsg/recvChars', $args->{options});
if ($args->{options} && exists $args->{options}{charServer}) {
$charServer = $args->{options}{charServer};
} else {
$charServer = $net->serverPeerHost . ":" . $net->serverPeerPort;
}
# PACKET_HC_ACCEPT_ENTER2 contains no character info
return unless exists $args->{charInfo};
my $blockSize = $self->received_characters_blockSize();
for (my $i = $args->{RAW_MSG_SIZE} % $blockSize; $i < $args->{RAW_MSG_SIZE}; $i += $blockSize) {
#exp display bugfix - chobit andy 20030129
my $unpack_string = $self->received_characters_unpackString;
# TODO: What would be the $unknown ?
my ($cID,$exp,$zeny,$jobExp,$jobLevel, $opt1, $opt2, $option, $stance, $manner, $statpt,
$hp,$maxHp,$sp,$maxSp, $walkspeed, $jobId,$hairstyle, $weapon, $level, $skillpt,$headLow, $shield,$headTop,$headMid,$hairColor,
$clothesColor,$name,$str,$agi,$vit,$int,$dex,$luk,$slot, $rename, $unknown, $mapname, $deleteDate) =
unpack($unpack_string, substr($args->{RAW_MSG}, $i));
$chars[$slot] = new Actor::You;
# Re-use existing $char object instead of re-creating it.
# Required because existing AI sequences (eg, route) keep a reference to $char.
$chars[$slot] = $char if $char && $char->{ID} eq $accountID && $char->{charID} eq $cID;
$chars[$slot]{ID} = $accountID;
$chars[$slot]{charID} = $cID;
$chars[$slot]{exp} = $exp;
$chars[$slot]{zeny} = $zeny;
$chars[$slot]{exp_job} = $jobExp;
$chars[$slot]{lv_job} = $jobLevel;
$chars[$slot]{hp} = $hp;
$chars[$slot]{hp_max} = $maxHp;
$chars[$slot]{sp} = $sp;
$chars[$slot]{sp_max} = $maxSp;
$chars[$slot]{jobID} = $jobId;
$chars[$slot]{hair_style} = $hairstyle;
$chars[$slot]{lv} = $level;
$chars[$slot]{headgear}{low} = $headLow;
$chars[$slot]{headgear}{top} = $headTop;
$chars[$slot]{headgear}{mid} = $headMid;
$chars[$slot]{hair_color} = $hairColor;
$chars[$slot]{clothes_color} = $clothesColor;
$chars[$slot]{name} = $name;
$chars[$slot]{str} = $str;
$chars[$slot]{agi} = $agi;
$chars[$slot]{vit} = $vit;
$chars[$slot]{int} = $int;
$chars[$slot]{dex} = $dex;
$chars[$slot]{luk} = $luk;
$chars[$slot]{sex} = $accountSex2;
$chars[$slot]{deleteDate} = getFormattedDate($deleteDate) if ($deleteDate);
$chars[$slot]{nameID} = unpack("V", $chars[$slot]{ID});
$chars[$slot]{name} = bytesToString($chars[$slot]{name});
}
my $nChars = 0;
foreach (@chars) { $nChars++ if($_); }
# FIXME better support for multiple received_characters packets
if ($args->{switch} eq '099D' && $args->{RAW_MSG_SIZE} >= ($blockSize * 3)) {
$net->setState(1.5);
return;
}
message T("Received characters from Character Server\n"), "connection";
if (charSelectScreen(1) == 1) {
$firstLoginMap = 1;
$startingzeny = $chars[$config{'char'}]{'zeny'} unless defined $startingzeny;
$sentWelcomeMessage = 1;
}
}
=cut
*parse_quest_update_mission_hunt = *Network::Receive::ServerType0::parse_quest_update_mission_hunt_v2;
*reconstruct_quest_update_mission_hunt = *Network::Receive::ServerType0::reconstruct_quest_update_mission_hunt_v2;
1;
heres my RagnarokServer.pm
src\Poseidon\RagnarokServer.pm
Code: Select all
###########################################################
# Poseidon server - Ragnarok Online server emulator
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# Copyright (c) 2005-2006 OpenKore Development Team
###########################################################
# This class emulates a Ragnarok Online server.
# The RO client connects to this server. This server
# periodically sends a GameGuard query to the RO client,
# and saves the RO client's response.
###########################################################
# TODO:
# 1) make use of unpack strings to pack our packets depending on serverType
# 2) make plugin like connection algorithms for each serverType or 1 main algo on which serverTypes have hooks
package Poseidon::RagnarokServer;
use strict;
use Base::Server;
use base qw(Base::Server);
use Misc;
use Utils qw(binSize getCoordString timeOut getHex getTickCount);
use Poseidon::Config;
use FileParsers;
use Math::BigInt;
my %clientdata;
# Decryption Keys
my $enc_val1 = 0;
my $enc_val2 = 0;
my $enc_val3 = 0;
my $state = 0;
sub new {
my ($class, @args) = @_;
my $self = $class->SUPER::new(@args);
# int challengeNum
#
# The number of times that the RO client has sent a
# GameGuard challenge packet.
#
# Invariant: challengeNum >= 0
$self->{challengeNum} = 0;
# Bytes response
#
# A response for the last GameGuard query.
$self->{response} = undef;
# Invariant: state ne ''
$self->{state} = 'ready';
# added servertypes support
if (-e 'servertypes.txt') {
parseSectionedFile('servertypes.txt', \%{$self->{type}});
} else {
parseSectionedFile('src/Poseidon/servertypes.txt', \%{$self->{type}});
}
if (!$self->{type}->{$config{server_type}}) {
die "Invalid serverType specified. Please check your poseidon config file.\n";
} else {
print "Building RagnarokServer with serverType $config{server_type}...\n";
}
return $self;
}
##
# $RagnarokServer->query(Bytes packet)
# packet: The raw GameGuard query packet.
# Require: defined($packet) && $self->getState() eq 'ready'
# Ensure: $self->getState() eq 'requesting'
#
# Send a GameGuard query to the RO client.
sub query
{
my ($self, $packet) = @_;
my $clients = $self->clients();
for (my $i = 0; $i < @{$clients}; $i++) {
if ($clients->[$i]) {
if ($clients->[$i]{connectedToMap}) {
$clients->[$i]->send($packet);
$self->{state} = 'requesting';
return;
}
}
}
print "[RagnarokServer]-> Error: no Ragnarok Online client connected.\n";
}
##
# String $RagnarokServer->getState()
#
# Get the state of this RagnarokServer object.
# The result can be one of:
# 'ready' - The RO client is ready to handle another GameGuard query.
# 'requesting' - The query has been sent to the RO client, but it hasn't responded yet.
# 'requested' - The RO client has responded to the last GameGuard query.
# 'not connected' - The RO client hasn't connected to this server yet.
sub getState {
my ($self) = @_;
my $clients = $self->clients();
if ($self->{state} eq 'requested') {
return 'requested';
} elsif (binSize($clients) == 0) {
return 'not connected';
} else {
return $self->{state};
}
}
##
# Bytes $RagnarokServer->readResponse()
# Require: $self->getState() eq 'requested'
# Ensure: defined(result) && $self->getState() eq 'ready'
#
# Read the response for the last GameGuard query.
sub readResponse {
my $resp = $_[0]->{response};
$_[0]->{response} = undef;
$_[0]->{state} = 'ready';
return $resp;
}
#####################################################
sub onClientNew
{
my ($self, $client, $index) = @_;
if ( $state == 0 )
{
# Initialize Decryption
$enc_val1 = 0;
$enc_val2 = 0;
$enc_val3 = 0;
} else { $state = 0; }
$self->{challengeNum} = 0;
print "[RagnarokServer]-> Ragnarok Online client ($index) connected.\n";
}
sub onClientExit
{
my ($self, $client, $index) = @_;
$self->{challengeNum} = 0;
print "[RagnarokServer]-> Ragnarok Online client ($index) disconnected.\n";
}
## constants
my $accountID = pack("a4", "acct");
my $posX = 53;
my $posY = 113;
## Globals
my $charID = pack("a4", "char");
my $sessionID = pack("a4", "sess");
my $sessionID2 = pack("C4", 0xff);
my $npcID1 = pack("a4", "npc1");
my $npcID0 = pack("a4", "npc2");
my $monsterID = pack("a4", "mon1");
my $itemID = pack("a4", "itm1");
sub DecryptMessageID
{
my ($MID) = @_;
# Checking if Decryption is Activated
if ($enc_val1 != 0 && $enc_val2 != 0 && $enc_val3 != 0)
{
# Saving Last Informations for Debug Log
my $oldMID = $MID;
my $oldKey = ($enc_val1 >> 16) & 0x7FFF;
# Calculating the Next Decryption Key
$enc_val1 = $enc_val1->bmul($enc_val3)->badd($enc_val2) & 0xFFFFFFFF;
# Xoring the Message ID [657BE2h] [0x6E0A]
$MID = ($MID ^ (($enc_val1 >> 16) & 0x7FFF));
# Debug Log
# print sprintf("Decrypted MID : [%04X]->[%04X] / KEY : [0x%04X]->[0x%04X]\n", $oldMID, $MID, $oldKey, ($enc_val1 >> 16) & 0x7FFF);
}
return $MID;
}
sub onClientData {
my ($self, $client, $msg, $index) = @_;
my $packet_id = DecryptMessageID(unpack("v",$msg));
my $switch = sprintf("%04X", $packet_id);
# Parsing Packet
ParsePacket($self, $client, $msg, $index, $packet_id, $switch);
}
sub ParsePacket
{
my ($self, $client, $msg, $index, $packet_id, $switch) = @_;
#my $packed_switch = quotemeta substr($msg, 0, 2);
my $packed_switch = $packet_id;
### These variables control the account information ###
my $host = $self->getHost();
my $port = pack("v", $self->getPort());
$host = '127.0.0.1' if ($host eq 'localhost');
my @ipElements = split /\./, $host;
print "Received packet: $switch\n";
# Note:
# The switch packets are pRO specific and assumes the use of secureLogin 1. It may or may not work with other
# countries' clients (except probably oRO). The best way to support other clients would be: use a barebones
# eAthena or Freya as the emulator, or figure out the correct packet switches and include them in the
# if..elsif..else blocks.
if (($switch eq '01DB') || ($switch eq '0204')) { # client sends login packet 0204 packet thanks to elhazard
# '01DC' => ['secure_login_key', 'x2 a*', [qw(secure_key)]],
my $data = pack("C*", 0xdc, 0x01, 0x14) . pack("x17");
$client->send($data);
# save servers.txt info
my $code = substr($msg, 2);
if (length($msg) == 2) {
$clientdata{$index}{secureLogin_type} = 0;
} elsif (length($msg) == 20) {
if ($code eq pack("C*", 0x04, 0x02, 0x7B, 0x8A, 0xA8, 0x90, 0x2F, 0xD8, 0xE8, 0x30, 0xF8, 0xA5, 0x25, 0x7A, 0x0D, 0x3B, 0xCE, 0x52)) {
$clientdata{$index}{secureLogin_type} = 1;
} elsif ($code eq pack("C*", 0x04, 0x02, 0x27, 0x6A, 0x2C, 0xCE, 0xAF, 0x88, 0x01, 0x87, 0xCB, 0xB1, 0xFC, 0xD5, 0x90, 0xC4, 0xED, 0xD2)) {
$clientdata{$index}{secureLogin_type} = 2;
} elsif ($code eq pack("C*", 0x04, 0x02, 0x42, 0x00, 0xB0, 0xCA, 0x10, 0x49, 0x3D, 0x89, 0x49, 0x42, 0x82, 0x57, 0xB1, 0x68, 0x5B, 0x85)) {
$clientdata{$index}{secureLogin_type} = 3;
} elsif ($code eq ("C*", 0x04, 0x02, 0x22, 0x37, 0xD7, 0xFC, 0x8E, 0x9B, 0x05, 0x79, 0x60, 0xAE, 0x02, 0x33, 0x6D, 0x0D, 0x82, 0xC6)) {
$clientdata{$index}{secureLogin_type} = 4;
} elsif ($code eq pack("C*", 0x04, 0x02, 0xc7, 0x0A, 0x94, 0xC2, 0x7A, 0xCC, 0x38, 0x9A, 0x47, 0xF5, 0x54, 0x39, 0x7C, 0xA4, 0xD0, 0x39)) {
$clientdata{$index}{secureLogin_type} = 5;
}
} else {
$clientdata{$index}{secureLogin_requestCode} = getHex($code);
}
} elsif (($switch eq '01DD') || ($switch eq '01FA') || ($switch eq '0064') || ($switch eq '0060') || ($switch eq '0277') || ($switch eq '02B0')) { # 0064 packet thanks to abt123
# #my $data = pack("C*", 0xAD, 0x02, 0x00, 0x00, 0x1E, 0x0A, 0x00, 0x00);
# #$client->send($data);
my $sex = 1;
my $serverName = pack("a20", "Poseidon server"); # server name should be less than or equal to 20 characters
my $serverUsers = pack("V", @{$self->clients()} - 1);
# '0069' => ['account_server_info', 'x2 a4 a4 a4 x30 C1 a*',
# [qw(sessionID accountID sessionID2 accountSex serverInfo)]],
my $data;
if ($switch eq '01FA') {
$data = pack("C*", 0x69, 0x00, 0x53, 0x00) .
$sessionID . $accountID . $sessionID2 .
pack("x30") . pack("C1", $sex) . pack("x4") .
pack("C*", $ipElements[0], $ipElements[1], $ipElements[2], $ipElements[3]) .
$port . $serverName . $serverUsers . pack("x2");
} else {
$data = pack("C*", 0x69, 0x00, 0x4F, 0x00) .
$sessionID . $accountID . $sessionID2 .
pack("C*", 0x00, 0x00, 0x00, 0x00) .
pack("x26") . pack("C1", $sex);
print "$data\n";
$client->send($data);
$data = pack("C*", $ipElements[0], $ipElements[1], $ipElements[2], $ipElements[3]) .
$port . $serverName . $serverUsers . pack("x2");
}
$client->send($data);
# save servers.txt info
$clientdata{$index}{version} = unpack("V", substr($msg, 2, 4));
$clientdata{$index}{master_version} = unpack("C", substr($msg, length($msg) - 1, 1));
if ($switch eq '01DD') {
$clientdata{$index}{secureLogin} = 1;
undef $clientdata{$index}{secureLogin_account};
} elsif ($switch eq '01FA') {
$clientdata{$index}{secureLogin} = 3;
$clientdata{$index}{secureLogin_account} = unpack("C", substr($msg, 47, 1));
} else {
undef $clientdata{$index}{secureLogin};
undef $clientdata{$index}{secureLogin_type};
undef $clientdata{$index}{secureLogin_account};
undef $clientdata{$index}{secureLogin_requestCode};
}
if (($switch ne '01DD') && ($switch ne '01FA') && ($switch ne '0064')) {
$clientdata{$index}{masterLogin_packet} = $switch;
} else {
undef $clientdata{$index}{masterLogin_packet};
}
if($switch eq '02B0') { # kRO uses 02B2 as masterLogin packet when we have <langtype>0</langtype> in the clientinfo.xml
# if other servers do use this packet too that will be a problem.
$clientdata{$index}{kRO} = 1;
}
} elsif (($switch eq '0065') || ($switch eq '0275') || ($msg =~ /^$packed_switch$accountID$sessionID$sessionID2\x0\x0.$/)) { # client sends server choice packet
# Character List
SendCharacterList($self, $client, $msg, $index, $switch);
# save servers.txt info
if ($switch ne '0065') {
$clientdata{$index}{gameLogin_packet} = $switch;
} else {
undef $clientdata{$index}{gameLogin_packet};
}
} elsif ($switch eq '0066') { # client sends character choice packet
# If Using Packet Encrypted Client
if ( $self->{type}->{$config{server_type}}->{decrypt_mid_keys} )
{
# Enable Decryption
my @enc_values = split(/\s+/, $self->{type}->{$config{server_type}}->{decrypt_mid_keys});
($enc_val1, $enc_val2, $enc_val3) = (Math::BigInt->new(@enc_values[0]), Math::BigInt->new(@enc_values[1]), Math::BigInt->new(@enc_values[2]));
}
# State
$state = 1;
$clientdata{$index}{mode} = unpack('C1', substr($msg, 2, 1));
# '0071' => ['received_character_ID_and_Map', 'a4 Z16 a4 v1', [qw(charID mapName mapIP mapPort)]],
my $mapName = pack("a16", "moc_prydb1.gat");
my $data = pack("C*", 0x71, 0x00) . $charID . $mapName .
pack("C*", $ipElements[0], $ipElements[1], $ipElements[2], $ipElements[3]) . $port;
$client->send($data);
} elsif ($switch eq $self->{type}->{$config{server_type}}->{maploginPacket} &&
(length($msg) == 19) &&
(substr($msg, 2, 4) eq $accountID) &&
(substr($msg, 6, 4) eq $charID) &&
(substr($msg, 10, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 0;
} elsif ($switch eq '0072' &&
(length($msg) == 19) &&
(substr($msg, 2, 4) eq $accountID) &&
(substr($msg, 6, 4) eq $charID) &&
(substr($msg, 10, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 0;
} elsif ($switch eq '009B' &&
(length($msg) == 32) &&
(substr($msg, 3, 4) eq $accountID) &&
(substr($msg, 12, 4) eq $charID) &&
(substr($msg, 23, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 3;
} elsif ($switch eq '00F5' &&
(length($msg) == 29) &&
(substr($msg, 5, 4) eq $accountID) &&
(substr($msg, 14, 4) eq $charID) &&
(substr($msg, 20, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 4;
} elsif ($switch eq '009B' &&
(length($msg) == 32) &&
(substr($msg, 9, 4) eq $accountID) &&
(substr($msg, 15, 4) eq $charID) &&
(substr($msg, 23, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 5;
} elsif ($switch eq '0072' &&
(length($msg) == 29) &&
(substr($msg, 3, 4) eq $accountID) &&
(substr($msg, 10, 4) eq $charID) &&
(substr($msg, 20, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 6;
} elsif ($switch eq '0072' &&
(length($msg) == 34) &&
(substr($msg, 7, 4) eq $accountID) &&
(substr($msg, 15, 4) eq $charID) &&
(substr($msg, 25, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 7;
} elsif ($switch eq '009B' &&
(length($msg) == 26) &&
(substr($msg, 4, 4) eq $accountID) &&
(substr($msg, 9, 4) eq $charID) &&
(substr($msg, 17, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 8;
} elsif ($switch eq '009B' &&
(length($msg) == 37) &&
(substr($msg, 9, 4) eq $accountID) &&
(substr($msg, 21, 4) eq $charID) &&
(substr($msg, 28, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 9;
} elsif ($switch eq '0072' &&
(length($msg) == 26) &&
(substr($msg, 4, 4) eq $accountID) &&
(substr($msg, 9, 4) eq $charID) &&
(substr($msg, 17, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 10;
} elsif ($switch eq '0072' &&
(length($msg) == 29) &&
(substr($msg, 5, 4) eq $accountID) &&
(substr($msg, 14, 4) eq $charID) &&
(substr($msg, 20, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 11;
} elsif ($switch eq '0094' &&
(length($msg) == 30) &&
(substr($msg, 12, 4) eq $accountID) &&
(substr($msg, 2, 4) eq $charID) &&
(substr($msg, 6, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = 12;
} elsif ($switch eq '0072' &&
(length($msg) == 29) &&
(substr($msg, 5, 4) eq $accountID) &&
(substr($msg, 14, 4) eq $charID) &&
(substr($msg, 20, 4) eq $sessionID)
) { # client sends the maplogin packet
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
$clientdata{$index}{serverType} = "1 or 2";
} elsif (($switch eq '0436' || $switch eq '022D') &&
(length($msg) == 19) &&
(substr($msg, 2, 4) eq $accountID) &&
(substr($msg, 6, 4) eq $charID) &&
(substr($msg, 10, 4) eq $sessionID)
) { # client sends the maplogin packet
$client->send(pack("v a4", 0x0283, $accountID));
# mapLogin packet
$client->send(pack("H536", "eb0206ad09222b56c0050500000f012c010100000000000900000001004e565f42415349430000000000000000080000000f0000000030000000000000000000010054465f444f55424c4500000000000000090000000f0000000131000000000000000000010054465f4d495353000000000000000000070000000f0000000132000100000000000a00010054465f535445414c0000000000000000080000000f0000000133000400000000000a00010054465f484944494e4700000000000000090000000f0000000034000100000000000c00020054465f504f49534f4e00000000000000090000000f0000000135001000000000000a00090054465f4445544f5849465900000000000b0000000f000000008e00040000000100030001004e565f464952535441494400000000000b0000000f00000000d701df145a000200000000d701df145a0003000000003a010100d701df145a0002f13200003a010100b0001900d06b0000b0001800b2110000b102080000000000b202080000000000b0002a003f000000b0002b0000000000b0002e002d000000b000300000000000d9070039020000000000000000000000000000000000000000000000000000233000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
$client->{connectedToMap} = 1;
} elsif ($msg =~ /^$packed_switch/
&& $msg =~ /$accountID/
&& $msg =~ /$charID/
&& $msg =~ /$sessionID/) { # client sends the maplogin packet (unknown)
print "Received unsupported map login packet $switch.\n";
visualDump($msg, "$switch");
SendMapLogin($self, $client, $msg, $index);
# save servers.txt info
undef $clientdata{$index}{serverType};
#$clientdata{$index}{sendMapLogin} = $msg;
} elsif ($switch eq '007D') { # client sends the map loaded packet
my $data;
# Temporary Hack to Initialized Crypted Client
if ( $self->{type}->{$config{server_type}}->{decrypt_mid_keys} )
{
for ( my $i = 0 ; $i < 64 ; $i++ )
{
$client->send(pack("C C", 0x70, 0x08));
# Forcedly Calculating the Next Decryption Key
$enc_val1 = $enc_val1->bmul($enc_val3)->badd($enc_val2) & 0xFFFFFFFF;
}
}
PerformMapLoadedTasks($self, $client, $msg, $index);
} elsif (
( ( ($switch eq '007E') || ($switch eq '035F' || ($switch eq '0360') ) ) && (($clientdata{$index}{serverType} == 0) || ($clientdata{$index}{serverType} == 1) || ($clientdata{$index}{serverType} == 2) || ($clientdata{$index}{serverType} == 6) || ($clientdata{$index}{serverType} == 7) || ($clientdata{$index}{serverType} == 10) || ($clientdata{$index}{serverType} == 11))) ||
(($switch eq '0089') && (($clientdata{$index}{serverType} == 3) || ($clientdata{$index}{serverType} == 5) || ($clientdata{$index}{serverType} == 8) || ($clientdata{$index}{serverType} == 9))) ||
(($switch eq '0116') && ($clientdata{$index}{serverType} == 4)) ||
(($switch eq '00A7') && ($clientdata{$index}{serverType} == 12))
) { # client sends sync packet
my $data = pack("C*", 0x7F, 0x00) . pack("V", getTickCount);
$client->send($data);
### Check if packet 0228 got tangled up with the sync packet
if (uc(unpack("H2", substr($msg, 7, 1))) . uc(unpack("H2", substr($msg, 6, 1))) eq '0228') {
# queue the response (thanks abt123)
$self->{response} = pack("v", $packet_id) . substr($msg, 8, length($msg)-2);
$self->{state} = 'requested';
}
} elsif ($switch eq '00B2') { # quit to character select screen
SendGoToCharSelection($self, $client, $msg, $index);
# Disable Decryption
$enc_val1 = 0;
$enc_val2 = 0;
$enc_val3 = 0;
} elsif ($switch eq '0187') { # accountid sync (what does this do anyway?)
$client->send($msg);
} elsif ($switch eq '018A') { # client sends quit packet
SendQuitGame($self, $client, $msg, $index);
} elsif ($switch eq '0228' || $switch eq '09D0') { # client sends game guard sync
# Queue the response
# Don't allow other packet's (like Sync) to get to RO server.
my $length = unpack("v",substr($msg,2,2));
if ($length > 0) {
$self->{response} = pack("v", $packet_id) . substr($msg,2,$length);
} else {
$self->{response} = pack("v", $packet_id);
};
$self->{state} = 'requested';
} elsif ($switch eq '02A7') { # client sends hShield response
# Queue the response
$self->{response} = $msg;
$self->{state} = 'requested';
} elsif ($switch eq '0258') { # client sent gameguard's challenge request
# Reply with "gameguard_grant" instead of a 0227 packet. Normally, the server would
# send a 0227 gameguard challenge to the client, then the client will send the
# proper 0228 response. Only after that will the server send 0259 to allow the
# client to continue the login sequence. Since this is just a fake server,
# there is no need to go through all that and we can do a shortcut.
if ($self->{challengeNum} == 0) {
print "Received GameGuard sync request. Client allowed to login account server.\n";
$client->send(pack("C*", 0x59, 0x02, 0x01));
} else {
print "Received GameGuard sync request. Client allowed to login char/map server.\n";
$client->send(pack("C*", 0x59, 0x02, 0x02));
}
$self->{challengeNum}++;
} elsif ($switch eq '09A1') {
SendCharacterList($self, $client, $msg, $index, $switch);
} elsif ($switch eq '08B8') {
my $seed = 'seed';
my $data = pack("C*", 0xB9, 0x08);
$data .= pack("V a4 v", $seed, $accountID, 0);
$client->send($data);
} else {
if ($switch eq '0090' || ($msg =~ /\x90\x0($npcID1|$npcID0)/)) { # npc talk
undef $clientdata{$index}{npc_talk_code};
if ($msg =~ /\x90\x0$npcID1/) {
# Show the kafra image
SendNpcImageShow($self, $client, $msg, $index, "kafra_04.bmp", 0x02);
# Show the messages
SendNPCTalk($self, $client, $msg, $index, $npcID1, "[Kafra]");
SendNPCTalk($self, $client, $msg, $index, $npcID1, "Welcome to Kafra Corp. We will stay with you wherever you go.");
SendNPCTalkContinue($self, $client, $msg, $index, $npcID1);
} else {
SendNPCTalk($self, $client, $msg, $index, $npcID0, "[Hakore]");
SendNPCTalk($self, $client, $msg, $index, $npcID0, "Hello! I was examining your RO client's login packets while you were connecting to Poseidon.");
SendNPCTalkContinue($self, $client, $msg, $index, $npcID0);
}
} elsif ($switch eq '00B8') { # npc talk response
my $npcID = substr($msg, 2, 4);
my $response = unpack("C1", substr($msg, 6, 1));
if ($npcID eq $npcID0) {
if ($response == 1) {
# Check server info
SendNPCTalk($self, $client, $msg, $index, $npcID, "[Hakore]");
SendNPCTalk($self, $client, $msg, $index, $npcID, "Your RO client uses the following server details:");
SendNPCTalk($self, $client, $msg, $index, $npcID, "^2222DDversion: $clientdata{$index}{version}");
SendNPCTalk($self, $client, $msg, $index, $npcID, "master_version: $clientdata{$index}{master_version}");
SendNPCTalk($self, $client, $msg, $index, "serverType: " . ((defined $clientdata{$index}{serverType}) ? $clientdata{$index}{serverType} : 'Unknown'));
if ($clientdata{$index}{secureLogin}) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "secureLogin: $clientdata{$index}{secureLogin}");
if ($clientdata{$index}{secureLogin_requestCode}) {
SendNPCTalk($self, $client, $msg, $index, $npcID, $npcID, "secureLogin_requestCode: $clientdata{$index}{secureLogin_requestCode}");
} elsif (defined $clientdata{$index}{secureLogin_type}) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "secureLogin_type: $clientdata{$index}{secureLogin_type}");
}
if ($clientdata{$index}{secureLogin_account}) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "secureLogin_account: $clientdata{$index}{secureLogin_account}");
}
}
if ($clientdata{$index}{masterLogin_packet}) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "masterLogin_packet: $clientdata{$index}{masterLogin_packet}");
}
if ($clientdata{$index}{gameLogin_packet}) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "gameLogin_packet: $clientdata{$index}{gameLogin_packet}");
}
SendNPCTalkContinue($self, $client, $msg, $index, $npcID);
if (defined $clientdata{$index}{serverType}) {
$clientdata{$index}{npc_talk_code} = 3;
} else {
$clientdata{$index}{npc_talk_code} = 2.5;
}
} elsif ($response == 2) {
# Use storage
SendNPCTalk($self, $client, $msg, $index, $npcID, "[Hakore]");
SendNPCTalk($self, $client, $msg, $index, $npcID, "Thank you for the visit. Go and multiply!");
SendNpcTalkClose($self, $client, $msg, $index, $npcID);
}
} elsif ($npcID eq $npcID1) {
if ($response == 1) {
# Use storage
my $data;
$data .= pack("C2 v1", 0xF0, 0x01, 40) .
pack("v2 C2 v1 x10", 3, 501, 0, 1, 16) .
pack("v2 C2 v1 x10", 4, 909, 3, 1, 144);
$data .= pack("v3", 0xF2, 2, 300);
SendNpcImageShow($self, $client, $msg, $index, "kafra_04.bmp", 0xFF);
SendNpcTalkClose($self, $client, $msg, $index, $npcID);
$client->send($data);
} elsif ($response == 2) {
# Use storage
SendNPCTalk($self, $client, $msg, $index, $npcID, "[Kafra]");
SendNPCTalk($self, $client, $msg, $index, $npcID, "We Kafra Corp. always try to serve you the best.");
SendNPCTalk($self, $client, $msg, $index, $npcID, "Please come again.");
SendNpcTalkClose($self, $client, $msg, $index, $npcID);
}
}
} elsif ($switch eq '00B9') { # npc talk continue
my $npcID = substr($msg, 2, 4);
if ($npcID eq $npcID0) {
if ($clientdata{$index}{npc_talk_code} == 2) {
# Show NPC response list
SendNpcTalkResponses($self, $client, $msg, $index, $npcID, "Yes, please:No, thanks:");
$clientdata{$index}{npc_talk_code} = 3;
} else {
SendNPCTalk($self, $client, $msg, $index, $npcID, "[Hakore]");
if (!$clientdata{$index}{npc_talk_code}) {
if (!defined $clientdata{$index}{serverType}) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "However, I regret that Openkore may not currently support your server.");
} elsif ($clientdata{$index}{serverType} == 7 || $clientdata{$index}{serverType} == 12) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "However, I regret that Openkore does not yet fully support your server this time.");
} else {
SendNPCTalk($self, $client, $msg, $index, $npcID, "Based on my examination, I think Openkore supports your server.");
SendNPCTalk($self, $client, $msg, $index, $npcID, "I can tell you the possible server details you can use to make Openkore to connect to your server.");
}
SendNPCTalkContinue($self, $client, $msg, $index, $npcID);
$clientdata{$index}{npc_talk_code} = 1;
} elsif ($clientdata{$index}{npc_talk_code} == 1) {
if ((!defined $clientdata{$index}{serverType}) || ($clientdata{$index}{serverType} == 7)) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "Would you still like to hear the details?");
} else {
SendNPCTalk($self, $client, $msg, $index, $npcID, "Would you like to hear the details?");
}
SendNPCTalkContinue($self, $client, $msg, $index, $npcID);
$clientdata{$index}{npc_talk_code} = 2;
} elsif ($clientdata{$index}{npc_talk_code} == 2.5) {
if (!defined $clientdata{$index}{serverType}) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "As you can see, I can't find a matching serverType for your server.");
SendNPCTalk($self, $client, $msg, $index, $npcID, "Please make a trial-and-error using all available serverTypes, one of them might be able to work.");
} elsif ($clientdata{$index}{serverType} == 7 || $clientdata{$index}{serverType} == 12) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "Like I said, your server is not yet fully supported by Openkore.");
SendNPCTalk($self, $client, $msg, $index, $npcID, "You can login to the server and do most basic tasks, but you cannot attack, sit or stand, or use skills.");
}
SendNPCTalkContinue($self, $client, $msg, $index, $npcID);
$clientdata{$index}{npc_talk_code} = 4;
} elsif ($clientdata{$index}{npc_talk_code} == 3) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "The values of ^2222DDip^000000 and ^2222DDport^000000 can be found on your client's (s)clientinfo.xml.");
SendNPCTalkContinue($self, $client, $msg, $index, $npcID);
$clientdata{$index}{npc_talk_code} = 4;
} elsif ($clientdata{$index}{npc_talk_code} == 4) {
if (!defined $clientdata{$index}{serverType}) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "If none of the serverTypes work, please inform the developers about this so we can support your server in future releases of Openkore.");
SendNPCTalk($self, $client, $msg, $index, $npcID, "Please visit ^2222DDhttp://forums.openkore.com/^000000");
SendNPCTalk($self, $client, $msg, $index, $npcID, "Thank you.");
} else {
if (($clientdata{$index}{serverType} == 7)
|| ($clientdata{$index}{serverType} == 8)
|| ($clientdata{$index}{serverType} == 9)
|| ($clientdata{$index}{serverType} == 10)
|| ($clientdata{$index}{serverType} == 11)
|| ($clientdata{$index}{serverType} == 12)
|| ($clientdata{$index}{masterLogin_packet})
|| ($clientdata{$index}{gameLogin_packet})
) {
SendNPCTalk($self, $client, $msg, $index, $npcID, "Please note that you can only connect to your server using Openkore SVN.");
} else {
SendNPCTalk($self, $client, $msg, $index, $npcID, "Openkore v.1.6.6 or later will work on your server.");
}
SendNPCTalk($self, $client, $msg, $index, $npcID, "For more info, please visit ^2222DDhttp://www.openkore.com/^000000");
SendNPCTalk($self, $client, $msg, $index, $npcID, "Good luck!");
}
SendNpcTalkClose($self, $client, $msg, $index, $npcID);
}
}
} elsif ($npcID eq $npcID1) {
# Show kafra response list
SendNpcTalkResponses($self, $client, $msg, $index, $npcID, "Use Storage:Cancel:");
}
} elsif ($switch eq '0146') { # talk cancel
my $npcID = substr($msg, 2, 4);
if ($npcID eq $npcID1) {
SendNpcImageShow($self, $client, $msg, $index, "kafra_04.bmp", 0xFF);
}
} elsif ($clientdata{$index}{mode}) {
if (($switch eq '00F7' || $switch eq '0193') && (length($msg) == 2)) { # storage close
my $data = pack("v1", 0xF8);
$client->send($data);
} elsif ($switch eq '00BF') { # emoticon
my ($client, $code) = @_;
my $data = pack("v1 a4", 0xC0, $accountID) . substr($msg, 2, 1);
$clientdata{$index}{emoticonTime} = time;
$client->send($data);
} else {
print "\nReceived packet $switch:\n";
visualDump($msg, "$switch");
# Just provide feedback in the RO Client about the unhandled packet
# '008E' => ['self_chat', 'x2 Z*', [qw(message)]],
my $data = pack("v2 a31", 0x8E, 35, "Sent packet $switch (" . length($msg) . " bytes).");
if (timeOut($clientdata{$index}{emoticonTime}, 1.8)) {
$clientdata{$index}{emoticonTime} = time;
$data .= pack("v1 a4 C1", 0xC0, $accountID, 1);
}
# These following packets should reset the item inventory.
# If you drop something from your inventory and the server didn't respond,
# you will not be able to drop the item for the second test
# This, however, does not cover item_use. YOu would have to relog
# to test another item_use packet.
#$data .= pack("v3", 0xAF, 3, 0);
#$data .= pack("v3", 0xAF, 4, 0);
# There are no other send packet that contains NPC ids as the last four byte
# other than the talk and sendGetPlayerInfo packets.
# Since most possible talk packets are handled above, we can assume that this is
# a sendGetPlayerInfo packet.
# Note that we have an NPC that is not named initially to allow a
# sendGetPlayerInfo packet to be captured.)
# '0095' => ['actor_info', 'a4 Z24', [qw(ID name)]],
my $ID = substr($msg, length($msg) - 4, 4);
if ($ID eq $npcID0) {
$data .= pack("v1 a4 a24", 0x95, $npcID0, "Server Details Guide");
} elsif ($ID eq $npcID1) {
$data .= pack("v1 a4 a24", 0x95, $npcID1, "Kafra");
}
$client->send($data);
}
}
}
}
# PACKET SENDING S->C
sub SendCharacterList
{
my ($self, $client, $msg, $index, $switch) = @_;
# Log
print "Requested Char List (Standard)\n";
if ($switch eq '0065') {
my $blocksize = $self->{type}->{$config{server_type}}->{charBlockSize} || 116; #defaults to 116
my $totalslots = 12;
my $data;
$data = pack("V", getTickCount);
$client->send($data);
$data = pack("C*", 0x2D, 0x08);
$data .= pack("C*", 0x1D, 0x00);
$data .= pack("C*", 0x0C, 0x00, 0x00, 0x0C, 0x0C);
$data .= pack("x20");
print "$data\n";
print "Wanted CharBlockSize : $blocksize\n";
#print "Built CharBlockSize : " . length($block) . "\n";
$client->send($data);
$data = pack("C3 x3", 0xA0, 0x09, 0x01);
$client->send($data);
} elsif ($switch eq '09A1') {
# Wanted Block Size
my $blocksize = $self->{type}->{$config{server_type}}->{charBlockSize} || 116; #defaults to 116
# Packet Len, Total Characters and Total Slots
my $totalchars = 2;
my $totalslots = 12;
my $len = $blocksize * $totalchars;
# Character Block Pack String
my $packstring = '';
$packstring = 'a4 V9 v V2 v14 Z24 C8 v Z16 V x4 x4 x4' if $blocksize == 144;
$packstring = 'a4 V9 v V2 v14 Z24 C8 v Z16 x4 x4' if $blocksize == 136;
$packstring = 'a4 V9 v V2 v14 Z24 C8 v Z16 x4' if $blocksize == 132;
$packstring = 'a4 V9 v V2 v14 Z24 C8 v Z16' if $blocksize == 128;
$packstring = 'a4 V9 v V2 v14 Z24 C6 v2 x4' if $blocksize == 116;
$packstring = 'a4 V9 v V2 v14 Z24 C6 v2' if $blocksize == 112;
$packstring = 'a4 V9 v17 Z24 C6 v2' if $blocksize == 108;
$packstring = 'a4 V9 v17 Z24 C6 v' if $blocksize == 106;
# Unknown CharBlockSize
if ( length($packstring) == 0 ) { print "Unknown CharBlockSize : $blocksize\n"; return; }
# Character Block Format
my($cID,$exp,$zeny,$jobExp,$jobLevel,$opt1,$opt2,$option,$stance,$manner,$statpt,$hp,$maxHp,$sp,$maxSp,$walkspeed,$jobId,$hairstyle,$weapon,$level,$skillpt,$headLow,$shield,$headTop,$headMid,$hairColor,$clothesColor,$name,$str,$agi,$vit,$int,$dex,$luk,$slot,$rename) = 0;
# Preparing Begin of Character List Packet
my $data;
$data = pack("C2 v", 0x9D, 0x09, $len+4);
=pod
if ($self->{type}->{$config{server_type}}->{charListPacket} eq '0x82d') {
$data = $accountID . pack("v2 C5 a20", 0x82d, $len + 29,$totalchars,0,0,0,$totalchars,-0); # 29 = v2 C5 a20 size for bRO
} else {
$data = $accountID . pack("v v C3", 0x6b, $len + 7, $totalslots, -1, -1);
}
=cut
# Character Block
my $block;
# Filling Character 1 Block
$cID = $charID; $hp = 10000; $maxHp = 10000; $sp = 10000; $maxSp = 10000; $hairstyle = 1; $level = 99; $headTop = 0; $hairColor = 6;
$name = "Poseidon"; $str = 1; $agi = 1; $vit = 1; $int = 1; $dex = 1; $luk = 1; $exp = 1; $zeny = 1; $jobExp = 1; $jobLevel = 50; $slot = 0; $rename = 0;
# Preparing Character 1 Block
$block = pack($packstring,$cID,$exp,$zeny,$jobExp,$jobLevel,$opt1,$opt2,$option,$stance,$manner,$statpt,$hp,$maxHp,$sp,$maxSp,$walkspeed,$jobId,$hairstyle,$weapon,$level,$skillpt,$headLow,$shield,$headTop,$headMid,$hairColor,$clothesColor,$name,$str,$agi,$vit,$int,$dex,$luk,$slot,$rename);
# Attaching Block
$data .= $block;
# Filling Character 2 Block
$cID = $charID; $hp = 10000; $maxHp = 10000; $sp = 10000; $maxSp = 10000; $hairstyle = 1; $level = 99; $headTop = 0; $hairColor = 6;
$name = "Poseidon Dev"; $str = 1; $agi = 1; $vit = 1; $int = 1; $dex = 1; $luk = 1; $exp = 1; $zeny = 1; $jobExp = 1; $jobLevel = 50; $slot = 1; $rename = 0;
# Preparing Character 2 Block
$block = pack($packstring,$cID,$exp,$zeny,$jobExp,$jobLevel,$opt1,$opt2,$option,$stance,$manner,$statpt,$hp,$maxHp,$sp,$maxSp,$walkspeed,$jobId,$hairstyle,$weapon,$level,$skillpt,$headLow,$shield,$headTop,$headMid,$hairColor,$clothesColor,$name,$str,$agi,$vit,$int,$dex,$luk,$slot,$rename);
# Attaching Block
$data .= $block;
# Measuring Size of Block
print "Wanted CharBlockSize : $blocksize\n";
print "Built CharBlockSize : " . length($block) . "\n";
$client->send($data);
#pinCode
my $seed = 'seed';
$data = pack("C*", 0xB9, 0x08);
$data .= pack("V a4 v", $seed, $accountID, 1);
$client->send($data);
}
}
sub SendMapLogin {
my ($self, $client, $msg, $index) = @_;
# '0073' => ['map_loaded','x4 a3',[qw(coords)]]
my $data;
if ( $config{server_type} !~ /^bRO/ ) { $data .= pack("C*", 0x83, 0x02) . $accountID; } #<- This is Server Type Based !!
$data .= pack("C*", 0xEB, 0x02) . pack("V", getTickCount) . getCoordString($posX, $posY, 1) . pack("C*", 0x05, 0x05) . pack("C*", 0x05, 0x05);
if ($clientdata{$index}{mode}) {
$data .= pack("C2 v1", 0x0F, 0x01, 226) .
# skillID targetType level sp range skillName
pack("v2 x2 v3 a24 C1", 1, 0, 9, 0, 1, "NV_BASIC" . chr(0) . "GetMapInfo" . chr(0x0A), 0) .
pack("v2 x2 v3 a24 C1", 24, 4, 1, 10, 10, "AL_RUWACH", 0) . # self skill test
pack("v2 x2 v3 a24 C1", 25, 2, 1, 10, 9, "AL_PNEUMA", 0) . # location skill test
pack("v2 x2 v3 a24 C1", 26, 4, 2, 9, 1, "AL_TELEPORT", 0) . # self skill test
pack("v2 x2 v3 a24 C1", 27, 2, 4, 26, 9, "AL_WARP", 0) . # location skill test
pack("v2 x2 v3 a24 C1", 28, 16, 10, 40, 9, "AL_HEAL", 0); # target skill test
}
$client->send($data);
$client->{connectedToMap} = 1;
}
sub SendGoToCharSelection
{
my ($self, $client, $msg, $index) = @_;
# Log
print "Requested Char Selection Screen\n";
$client->send(pack("v v", 0xB3, 1));
}
sub SendQuitGame
{
my ($self, $client, $msg, $index) = @_;
# Log
print "Requested Quit Game...\n";
$client->send(pack("v v", 0x18B, 0));
}
sub SendLookTo
{
my ($self, $client, $msg, $index, $ID, $to) = @_;
# Make Poseidon look to front
$client->send(pack('v1 a4 C1 x1 C1', 0x9C, $ID, 0, $to));
}
sub SendUnitInfo
{
my ($self, $client, $msg, $index, $ID, $name) = @_;
# Let's not wait for the client to ask for the unit info
# '0095' => ['actor_info', 'a4 Z24', [qw(ID name)]],
$client->send(pack("v1 a4 a24", 0x95, $ID, $name));
}
sub SendSystemChatMessage
{
my ($self, $client, $msg, $index, $message) = @_;
# '009A' => ['system_chat', 'v Z*', [qw(len message)]],
$client->send(pack("v2 a32", 0x9A, 36, $message));
}
sub SendShowNPC
{
my ($self, $client, $msg, $index, $obj_type, $GID, $SpriteID, $X, $Y, $MobName) = @_;
# Packet Structure
my ($object_type,$NPCID,$walk_speed,$opt1,$opt2,$option,$type,$hair_style,$weapon,$lowhead,$shield,$tophead,$midhead,$hair_color,$clothes_color,$head_dir,$guildID,$emblemID,$manner,$opt3,$stance,$sex,$xSize,$ySize,$lv,$font,$name) = 0;
# Building NPC Data
$object_type = $obj_type;
$NPCID = $GID;
$walk_speed = 0x1BD;
$type = $SpriteID;
$lv = 1;
$name = $MobName;
# '0856' => ['actor_exists', 'v C a4 v3 V v11 a4 a2 v V C2 a3 C3 v2 Z*', [qw(len object_type ID walk_speed opt1 opt2 option type hair_style weapon shield lowhead tophead midhead hair_color clothes_color head_dir costume guildID emblemID manner opt3 stance sex coords xSize ySize lv font name)]], # -1 # spawning provided by try71023
my $dbuf;
if ( $config{server_type} !~ /^bRO/ ) { $dbuf .= pack("C", $object_type); } #<- This is Server Type Based !!
$dbuf .= pack("a4 v3 V v11 a4 a2 v V C2",$NPCID,$walk_speed,$opt1,$opt2,$option,$type,$hair_style,$weapon,$lowhead,$shield,$tophead,$midhead,$hair_color,$clothes_color,$head_dir,$guildID,$emblemID,$manner,$opt3,$stance,$sex);
$dbuf .= getCoordString($X, $Y, 1);
$dbuf .= pack("C2 v2",$xSize,$ySize,$lv,$font);
$dbuf .= pack("Z" . length($name),$name);
my $opcode;
if ( $config{server_type} !~ /^bRO/ ) { $opcode = 0x858; } #<- This is Server Type Based !!
$client->send(pack("v v",$opcode,length($dbuf) + 4) . $dbuf);
}
sub SendShowItemOnGround
{
my ($self, $client, $msg, $index, $ID, $SpriteID, $X, $Y) = @_;
# '009D' => ['item_exists', 'a4 v1 x1 v3', [qw(ID type x y amount)]]
$client->send(pack("v1 a4 v1 x1 v3 x2", 0x9D, $ID, $SpriteID, $posX + 1, $posY - 1, 1));
}
sub SendNPCTalk
{
my ($self, $client, $msg, $index, $npcID, $message) = @_;
# '00B4' => ['npc_talk', 'v a4 Z*', [qw(len ID msg)]]
my $dbuf = pack("a" . length($message), $message);
$client->send(pack("v2 a4", 0xB4, (length($dbuf) + 8), $npcID) . $dbuf);
}
sub SendNPCTalkContinue
{
my ($self, $client, $msg, $index, $npcID) = @_;
# '00B5' => ['npc_talk_continue', 'a4', [qw(ID)]]
$client->send(pack("v a4", 0xB5, $npcID));
}
sub SendNpcTalkClose
{
my ($self, $client, $msg, $index, $npcID) = @_;
# '00B6' => ['npc_talk_close', 'a4', [qw(ID)]]
$client->send(pack("v a4", 0xB6, $npcID));
}
sub SendNpcTalkResponses
{
my ($self, $client, $msg, $index, $npcID, $message) = @_;
# '00B7' => ['npc_talk', 'v a4 Z*', [qw(len ID msg)]]
my $dbuf = pack("a" . length($message), $message);
$client->send(pack("v2 a4", 0xB7, (length($dbuf) + 8), $npcID) . $dbuf);
}
sub SendNpcImageShow
{
my ($self, $client, $msg, $index, $image, $type) = @_;
# Type = 0xFF = Hide Image
# Type = 0x02 = Show Image
# '01B3' => ['npc_image', 'Z64 C', [qw(npc_image type)]]
$client->send(pack("v a64 C1", 0x1B3, $image, $type));
}
# SERVER TASKS
sub PerformMapLoadedTasks
{
my ($self, $client, $msg, $index) = @_;
# Looking to Front
SendLookTo($self, $client, $msg, $index, $accountID, 4);
# Let's not wait for the client to ask for the unit info
SendUnitInfo($self, $client, $msg, $index, $accountID, 'Poseidon' . (($clientdata{$index}{mode} ? ' Dev' : '')));
# Global Announce
SendSystemChatMessage($self, $client, $msg, $index, "Welcome to the Poseidon Server !");
# Show an NPC
SendShowNPC($self, $client, $msg, $index, 1, $npcID0, 86, $posX + 3, $posY + 4, "Server Details Guide");
SendLookTo($self, $client, $msg, $index, $npcID0, 3);
SendUnitInfo($self, $client, $msg, $index, $npcID0, "Server Details Guide");
# Dev Mode (Char Slot 1)
if ($clientdata{$index}{mode})
{
# Show an NPC (Kafra)
SendShowNPC($self, $client, $msg, $index, 1, $npcID1, 114, $posX + 5, $posY + 3, "Kafra NPC");
SendLookTo($self, $client, $msg, $index, $npcID1, 4);
SendUnitInfo($self, $client, $msg, $index, $npcID1, "Kafra NPC");
# Show a monster
SendShowNPC($self, $client, $msg, $index, 5, $monsterID, 1002, $posX - 2, $posY - 1, "Poring");
SendLookTo($self, $client, $msg, $index, $monsterID, 3);
SendUnitInfo($self, $client, $msg, $index, $monsterID, "Poring");
# Show an item on ground
SendShowItemOnGround($self, $client, $msg, $index, $itemID, 512, $posX + 1, $posY - 1);
}
}
1;