Horizon Official Technical Documentation
Horizon::Char::CharClientInterface Class Reference

#include <CharClientInterface.hpp>

+ Inheritance diagram for Horizon::Char::CharClientInterface:
+ Collaboration diagram for Horizon::Char::CharClientInterface:

Public Member Functions

 CharClientInterface (std::shared_ptr< CharSession > s)
 
 ~CharClientInterface ()
 
bool authorize_new_connection (uint32_t account_id, uint32_t auth_code, uint32_t account_level, uint8_t gender)
 
bool make_new_character (std::string name, uint8_t slot, uint32_t hair_color, uint32_t hair_style, uint32_t job_class, uint8_t gender)
 
bool make_new_character (std::string name, uint8_t slot, uint8_t hair_color, uint8_t hair_style)
 
character_delete_result character_delete_soft (uint32_t character_id)
 
bool character_delete_reserve (uint32_t character_id)
 
bool character_delete_email (uint32_t character_id, std::string email)
 
bool character_delete_birthdate (uint32_t character_id, std::string birthdate)
 
bool character_delete_cancel (uint32_t character_id)
 
bool pincode_create (uint32_t account_id, char *new_pin)
 
bool pincode_change (uint32_t account_id, char *old_pin, char *new_pin)
 
bool pincode_verify (uint32_t account_id, char *pincode)
 
void pincode_decrypt (uint32_t seed, char *input, char *output)
 
bool select_character (int slot)
 
bool update_session (int32_t account_id)
 
- Public Member Functions inherited from Horizon::ClientInterface< CharSession >
 ClientInterface (std::shared_ptr< CharSession > s)
 
 ~ClientInterface ()
 
std::shared_ptr< CharSession > get_session ()
 
void set_session (std::shared_ptr< CharSession > s)
 

Protected Attributes

char _pincode_confirm [MAX_PINCODE_STRING_LENGTH]
 

Constructor & Destructor Documentation

◆ CharClientInterface()

CharClientInterface::CharClientInterface ( std::shared_ptr< CharSession s)
41{
42
43}
ClientInterface(std::shared_ptr< CharSession > s)
Definition: ClientInterface.hpp:40

◆ ~CharClientInterface()

CharClientInterface::~CharClientInterface ( )
46{
47}

Member Function Documentation

◆ authorize_new_connection()

bool CharClientInterface::authorize_new_connection ( uint32_t  account_id,
uint32_t  auth_code,
uint32_t  account_level,
uint8_t  gender 
)
50{
51 HLog(warning) << "A new connection has been established from I.P. " << get_session()->get_socket()->remote_ip_address();
52
53 std::shared_ptr<boost::mysql::tcp_ssl_connection> conn = sChar->get_database_connection();
54
55 try {
56 boost::mysql::statement stmt = conn->prepare_statement("SELECT * FROM `session_data` WHERE `game_account_id` = ?");
57 auto b1 = stmt.bind(account_id);
58 boost::mysql::results results;
59 conn->execute(b1, results);
60
61 HC_ACCOUNT_ID hcad(get_session()); // first packet sent no matter what.
62 hcad.deliver(account_id);
63
64 if (results.rows().empty()) {
66 HLog(warning) << "Invalid connection for account with ID " << account_id << ", session wasn't found.";
68 return false;
69 }
70
71 stmt = conn->prepare_statement("SELECT * FROM `game_accounts` WHERE `id` = ?");
72 auto b2 = stmt.bind(account_id);
73 boost::mysql::results results2;
74 conn->execute(b2, results2);
75
76 if (results2.rows().empty()) {
78 HLog(error) << "Game Account with ID " << account_id << " does not exist.";
80 return false;
81 }
82
86
87 int character_slots = results2.rows()[0][14].as_uint64();
88
89 hcae2.deliver(character_slots, MAX_CHARACTER_SLOTS - character_slots);
90
91 hcae.prepare(account_id, MAX_CHARACTER_SLOTS, character_slots, MAX_CHARACTER_SLOTS - character_slots);
92 hcae.deliver();
93
94 hcbc.deliver();
95
97 sd._account_id = account_id;
98 sd._auth_id = auth_code;
99 sd._group_id = account_level;
100 sd._connect_time = std::chrono::system_clock::now();
101 sd._character_slots = character_slots;
102 get_session()->set_session_data(sd);
103
104 { // Pin-code related
105 // Session data is updated here after being set prior.
106 std::string pincode = results2.rows()[0][15].as_string();
107 std::time_t pexp_t = results2.rows()[0][16].as_uint64();
108
110
111 if (pincode.size() == 0)
112 hcspl.deliver(PINCODE_REQUEST_NEW);
113 else if (std::time(0) > pexp_t)
114 hcspl.deliver(PINCODE_REQUEST_CHANGE);
115 else
116 hcspl.deliver(PINCODE_REQUEST_PIN);
117 }
118
119 stmt = conn->prepare_statement("UPDATE `session_data` SET `current_server` = 'C' WHERE `game_account_id` = ?");
120 auto b3 = stmt.bind(account_id);
121 conn->execute(b3, results);
122 }
123 catch (boost::mysql::error_with_diagnostics &err) {
124 HLog(error) << "CharClientInterface::authorize_new_connection:" << err.what();
125 return false;
126 }
127 catch (std::exception& err) {
128 HLog(error) << "CharClientInterface::authorize_new_connection:" << err.what();
129 return false;
130 }
131
132 // update with auth code.
133 return true;
134}
#define sChar
Definition: Char.hpp:143
#define MAX_CHARACTER_SLOTS
Definition: Horizon.hpp:57
#define HLog(type)
Definition: Logger.hpp:122
Main object for the aegis packet: HC_ACCEPT_ENTER2.
Definition: TransmittedPackets.hpp:163
Main object for the aegis packet: HC_ACCEPT_ENTER.
Definition: TransmittedPackets.hpp:120
Main object for the aegis packet: HC_ACCOUNT_ID.
Definition: TransmittedPackets.hpp:44
Main object for the aegis packet: HC_BLOCK_CHARACTER.
Definition: TransmittedPackets.hpp:428
Main object for the aegis packet: HC_REFUSE_ENTER.
Definition: TransmittedPackets.hpp:1045
Main object for the aegis packet: HC_SECOND_PASSWD_LOGIN.
Definition: TransmittedPackets.hpp:1234
std::shared_ptr< CharSession > get_session()
Definition: ClientInterface.hpp:43
@ CHAR_ERR_REJECTED_FROM_SERVER
Definition: TransmittedPackets.hpp:1038
@ PINCODE_REQUEST_CHANGE
Definition: TransmittedPackets.hpp:1211
@ PINCODE_REQUEST_PIN
Definition: TransmittedPackets.hpp:1209
@ PINCODE_REQUEST_NEW
Definition: TransmittedPackets.hpp:1210
Definition: CharSession.hpp:56
uint32_t _account_id
Definition: CharSession.hpp:57
uint32_t _group_id
Definition: CharSession.hpp:59
uint8_t _character_slots
Definition: CharSession.hpp:61
uint32_t _auth_id
Definition: CharSession.hpp:58
std::chrono::system_clock::time_point _connect_time
Definition: CharSession.hpp:60

References Horizon::Char::s_session_data::_account_id, Horizon::Char::s_session_data::_auth_id, Horizon::Char::s_session_data::_character_slots, Horizon::Char::s_session_data::_connect_time, Horizon::Char::s_session_data::_group_id, Horizon::Char::CHAR_ERR_REJECTED_FROM_SERVER, Horizon::Char::HC_ACCEPT_ENTER::deliver(), Horizon::Char::HC_BLOCK_CHARACTER::deliver(), Horizon::Char::HC_REFUSE_ENTER::deliver(), Horizon::Char::HC_SECOND_PASSWD_LOGIN::deliver(), Horizon::Char::HC_ACCOUNT_ID::deliver(), Horizon::Char::HC_ACCEPT_ENTER2::deliver(), Horizon::ClientInterface< CharSession >::get_session(), HLog, MAX_CHARACTER_SLOTS, Horizon::Char::PINCODE_REQUEST_CHANGE, Horizon::Char::PINCODE_REQUEST_NEW, Horizon::Char::PINCODE_REQUEST_PIN, Horizon::Char::HC_ACCEPT_ENTER::prepare(), and sChar.

+ Here is the call graph for this function:

◆ character_delete_birthdate()

bool CharClientInterface::character_delete_birthdate ( uint32_t  character_id,
std::string  birthdate 
)
279{
281
282 std::shared_ptr<boost::mysql::tcp_ssl_connection> conn = sChar->get_database_connection();
283
284 try {
285 boost::mysql::statement stmt = conn->prepare_statement("SELECT `birth_date`, `character_slots` FROM `game_accounts` WHERE `id` = ?");
286 auto b1 = stmt.bind(get_session()->get_session_data()._account_id);
287 boost::mysql::results results;
288 conn->execute(b1, results);
289
290 if (results.rows().empty()) {
291 HLog(error) << "Attempt to delete character with non-existent game-account. (CID: " << character_id << ", AID:" << get_session()->get_session_data()._account_id << ")";
292 dc3.deliver(character_id, CHAR_DEL_ACCEPT_RESULT_SYSTEM_ERR);
293 return false;
294 }
295
296 std::string bd = "000000";
297
298 std::string b = results.rows()[0][0].as_string();
299 HLog(debug) << b;
300
301 bd = b.substr(2, 2);
302 bd.append(b.substr(5, 2));
303 bd.append(b.substr(8, 2));
304
305 if (birthdate.length() > 6)
306 birthdate = birthdate.substr(0, 6);
307
308 HLog(debug) << birthdate << " - " << bd;
309 if (bd.compare(birthdate) != 0) {
310 dc3.deliver(character_id, CHAR_DEL_ACCEPT_RESULT_BIRTHDAY_ERR);
311 return false;
312 }
313
314 if (sChar->config().char_hard_delete()) {
315 boost::mysql::results results;
316 stmt = conn->prepare_statement("REMOVE FROM `character_status` WHERE `id` = ?");
317 auto b2 = stmt.bind(character_id);
318 conn->execute(b2, results);
319
320 stmt = conn->prepare_statement("REMOVE FROM `character_inventory` WHERE `char_id` = ?");
321 auto b3 = stmt.bind(character_id);
322 conn->execute(b3, results);
323
324 stmt = conn->prepare_statement("REMOVE FROM `characters` WHERE `id` = ?");
325 auto b4 = stmt.bind(character_id);
326 conn->execute(b4, results);
327 }
328 else {
329 boost::mysql::results results;
330 stmt = conn->prepare_statement("UPDATE `characters` SET `deleted_at` = ? WHERE `id` = ?");
331 auto b2 = stmt.bind(std::time(nullptr), character_id);
332 conn->execute(b2, results);
333 }
334
335 }
336 catch (boost::mysql::error_with_diagnostics &err) {
337 HLog(error) << "CharClientInterface::character_delete_birthdate:" << err.what();
338 return false;
339 }
340 catch (std::exception& err) {
341 HLog(error) << "CharClientInterface::character_delete_birthdate:" << err.what();
342 return false;
343 }
344
345 dc3.deliver(character_id, CHAR_DEL_ACCEPT_RESULT_SUCCESS);
346
348 int count = chpp.prepare();
349 chpp.deliver();
350
351 // Hercules has this snippet to send an empty packet for the finishing of the previously sent one.
352 // Apparently it doesn't trigger the end code in the client if this isn't sent.
353 // And this only occurs if the characters sent previously are 3.
354 if (count == 3) {
356 chpp2.prepare(true);
357 chpp2.deliver();
358 }
359
360 return true;
361}
@ CHAR_DEL_ACCEPT_RESULT_SUCCESS
0 (0x718): An unknown error has occurred.
Definition: Client.hpp:146
@ CHAR_DEL_ACCEPT_RESULT_BIRTHDAY_ERR
4: Deleting is not yet possible.
Definition: Client.hpp:150
@ CHAR_DEL_ACCEPT_RESULT_SYSTEM_ERR
1: none/success
Definition: Client.hpp:147
Main object for the aegis packet: HC_ACK_CHARINFO_PER_PAGE.
Definition: TransmittedPackets.hpp:317
Main object for the aegis packet: HC_DELETE_CHAR3.
Definition: TransmittedPackets.hpp:630
size_t count(GridTypeListContainer< SPECIFIC_TYPE > const &elements, SPECIFIC_TYPE *)
Definition: GridReferenceContainer.hpp:100

References CHAR_DEL_ACCEPT_RESULT_BIRTHDAY_ERR, CHAR_DEL_ACCEPT_RESULT_SUCCESS, CHAR_DEL_ACCEPT_RESULT_SYSTEM_ERR, GridTypeListIterator::count(), Horizon::Char::HC_ACK_CHARINFO_PER_PAGE::deliver(), Horizon::Char::HC_DELETE_CHAR3::deliver(), Horizon::ClientInterface< CharSession >::get_session(), HLog, Horizon::Char::HC_ACK_CHARINFO_PER_PAGE::prepare(), and sChar.

+ Here is the call graph for this function:

◆ character_delete_cancel()

bool CharClientInterface::character_delete_cancel ( uint32_t  character_id)
380{
382
383 std::shared_ptr<boost::mysql::tcp_ssl_connection> conn = sChar->get_database_connection();
384
385 try {
386 boost::mysql::statement stmt = conn->prepare_statement("SELECT `delete_reserved_at` FROM `characters` WHERE `id` = ?");
387 boost::mysql::results results;
388 auto b1 = stmt.bind(char_id);
389 conn->execute(b1, results);
390
391 if (results.rows().empty()) {
392 dcc.deliver(char_id, CHAR3_DEL_CANCEL_FAILURE);
393 return false;
394 }
395
396 stmt = conn->prepare_statement("UPDATE `characters` SET `delete_reserved_at` = ? WHERE `id` = ?");
397 auto b2 = stmt.bind(0, char_id);
398 conn->execute(b2, results);
399 }
400 catch (boost::mysql::error_with_diagnostics &err) {
401 HLog(error) << "CharClientInterface::character_delete_cancel:" << err.what();
402 return false;
403 }
404 catch (std::exception& err) {
405 HLog(error) << "CharClientInterface::character_delete_cancel:" << err.what();
406 return false;
407 }
408
409 dcc.deliver(char_id, CHAR3_DEL_CANCEL_SUCCESS);
410
411 return true;
412}
Main object for the aegis packet: HC_DELETE_CHAR3_CANCEL.
Definition: TransmittedPackets.hpp:668
@ CHAR3_DEL_CANCEL_FAILURE
Definition: TransmittedPackets.hpp:647
@ CHAR3_DEL_CANCEL_SUCCESS
Definition: TransmittedPackets.hpp:646

References Horizon::Char::CHAR3_DEL_CANCEL_FAILURE, Horizon::Char::CHAR3_DEL_CANCEL_SUCCESS, Horizon::Char::HC_DELETE_CHAR3_CANCEL::deliver(), Horizon::ClientInterface< CharSession >::get_session(), HLog, and sChar.

+ Here is the call graph for this function:

◆ character_delete_email()

bool CharClientInterface::character_delete_email ( uint32_t  character_id,
std::string  email 
)
247{
250
251 std::shared_ptr<boost::mysql::tcp_ssl_connection> conn = sChar->get_database_connection();
252 boost::mysql::statement stmt = conn->prepare_statement("SELECT `email` FROM `game_accounts` WHERE `id` = ?");
253 auto b1 = stmt.bind(get_session()->get_session_data()._account_id);
254 boost::mysql::results results;
255 conn->execute(b1, results);
256
257 if (results.rows().empty()) {
258 HLog(error) << "There was an error retrieving `email` from `game_accounts` for `id` = " << get_session()->get_session_data()._account_id;
259 rd.deliver();
260 return false;
261 }
262
263 if (email.compare(results.rows()[0][0].as_string().substr(0, 40)) != 0) {
264 rd.deliver();
265 return false;
266 }
267
268 if (character_delete_soft(character_id) != CHAR_DEL_RESULT_SUCCESS) {
269 rd.deliver();
270 return false;
271 }
272
273 ad.deliver();
274
275 return true;
276}
@ CHAR_DEL_RESULT_SUCCESS
0 (0x718): An unknown error has occurred.
Definition: Client.hpp:156
character_delete_result character_delete_soft(uint32_t character_id)
Definition: CharClientInterface.cpp:206
Main object for the aegis packet: HC_ACCEPT_DELETECHAR.
Definition: TransmittedPackets.hpp:82
Main object for the aegis packet: HC_REFUSE_DELETECHAR.
Definition: TransmittedPackets.hpp:1002

References CHAR_DEL_RESULT_SUCCESS, character_delete_soft(), Horizon::Char::HC_ACCEPT_DELETECHAR::deliver(), Horizon::Char::HC_REFUSE_DELETECHAR::deliver(), Horizon::ClientInterface< CharSession >::get_session(), HLog, and sChar.

+ Here is the call graph for this function:

◆ character_delete_reserve()

bool CharClientInterface::character_delete_reserve ( uint32_t  character_id)
364{
366
367 character_delete_result result = character_delete_soft(character_id);
368
369 if (result != CHAR_DEL_RESULT_SUCCESS) {
370 dc.deliver(character_id, result, 0);
371 return false;
372 }
373
374 dc.deliver(character_id, CHAR_DEL_RESULT_SUCCESS, sChar->config().character_deletion_time());
375
376 return true;
377}
character_delete_result
Definition: Client.hpp:154
Main object for the aegis packet: HC_DELETE_CHAR3_RESERVED.
Definition: TransmittedPackets.hpp:702

References CHAR_DEL_RESULT_SUCCESS, character_delete_soft(), Horizon::Char::HC_DELETE_CHAR3_RESERVED::deliver(), Horizon::ClientInterface< CharSession >::get_session(), and sChar.

+ Here is the call graph for this function:

◆ character_delete_soft()

character_delete_result CharClientInterface::character_delete_soft ( uint32_t  character_id)
207{
208 std::shared_ptr<boost::mysql::tcp_ssl_connection> conn = sChar->get_database_connection();
209
210 try {
211 boost::mysql::statement stmt = conn->prepare_statement("SELECT * FROM `characters` WHERE `id` = ?");
212 auto b1 = stmt.bind(character_id);
213 boost::mysql::results results;
214 conn->execute(b1, results);
215
216 if (results.rows().empty()) {
218 }
219 if (results.rows()[0][21].as_uint64() > 0) { // Guild ID
221 }
222
223 if (results.rows()[0][20].as_uint64() > 0) { // Party ID
225 }
226
227 std::time_t dt = std::time(nullptr) + sChar->config().character_deletion_time();
228
229 stmt = conn->prepare_statement("UPDATE `characters` SET `delete_reserved_at` = ? WHERE `id` = ?");
230 auto b2 = stmt.bind(dt, character_id);
231 conn->execute(b2, results);
232
233 }
234 catch (boost::mysql::error_with_diagnostics &err) {
235 HLog(error) << "CharClientInterface::character_delete_soft:" << err.what();
237 }
238 catch (std::exception& err) {
239 HLog(error) << "CharClientInterface::character_delete_soft:" << err.what();
241 }
242
244}
@ CHAR_DEL_RESULT_PARTY_ERR
4: To delete a character you must withdraw from the guild.
Definition: Client.hpp:160
@ CHAR_DEL_RESULT_SYSTEM_ERR
1: none/success
Definition: Client.hpp:157
@ CHAR_DEL_RESULT_GUILD_ERR
3: A database error occurred.
Definition: Client.hpp:159
@ CHAR_DEL_RESULT_DATABASE_ERR
2: Due to system settings can not be deleted.
Definition: Client.hpp:158

References CHAR_DEL_RESULT_DATABASE_ERR, CHAR_DEL_RESULT_GUILD_ERR, CHAR_DEL_RESULT_PARTY_ERR, CHAR_DEL_RESULT_SUCCESS, CHAR_DEL_RESULT_SYSTEM_ERR, HLog, and sChar.

Referenced by character_delete_email(), and character_delete_reserve().

+ Here is the caller graph for this function:

◆ make_new_character() [1/2]

bool CharClientInterface::make_new_character ( std::string  name,
uint8_t  slot,
uint32_t  hair_color,
uint32_t  hair_style,
uint32_t  job_class,
uint8_t  gender 
)
137{
139
140 try {
141 std::shared_ptr<boost::mysql::tcp_ssl_connection> conn = sChar->get_database_connection();
142 boost::mysql::statement stmt = conn->prepare_statement("SELECT * FROM `characters` WHERE `name` = ?");
143 auto b1 = stmt.bind(name);
144 boost::mysql::results results;
145 conn->execute(b1, results);
146
147 if (!results.rows().empty()) {
148 hcref.deliver(HC_CREATE_ERROR_ALREADY_EXISTS);
149 HLog(info) << "Character creation denied for already existing character '" << name << "'. (Endpoint: " << get_session()->get_socket()->remote_ip_address() << ")";
150 return false;
151 }
152
153 if (slot > get_session()->get_session_data()._character_slots) {
154 hcref.deliver(HC_CREATE_ERROR_CHAR_SLOT);
155 return false;
156 }
157
158 std::string new_map = sChar->config().start_map();
159 uint16_t x = sChar->config().start_x(), y = sChar->config().start_y();
160
161 stmt = conn->prepare_statement("INSERT INTO `characters` (`account_id`, `slot`, `name`, `current_map`, `current_x`, `current_y`, `saved_map`, `saved_x`, `saved_y`, `gender`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
162 auto b2 = stmt.bind(get_session()->get_session_data()._account_id, slot, name,
163 new_map, x, y,
164 new_map, x, y, gender ? "M" : "F");
165 conn->execute(b2, results);
166
167 int c_last_insert_id = results.last_insert_id();
168
169 stmt = conn->prepare_statement("INSERT INTO `character_status` (`id`, `job_id`, `hair_color_id`, `hair_style_id`) VALUES (?, ?, ?, ?)");
170 auto b3 = stmt.bind(c_last_insert_id, job_class, hair_color, hair_style);
171 conn->execute(b3, results);
172
173 for (int j = 0; j < sChar->config().start_item_count(); j++) {
174 std::pair<uint32_t, uint32_t> p = sChar->config().start_item(j);
175 int item = p.first, c = p.second;
176
177 stmt = conn->prepare_statement("INSERT INTO `character_inventory` (`char_id`, `item_id`, `amount`, `is_identified`) VALUES (?, ?, ?, ?)");
178 auto b4 = stmt.bind(c_last_insert_id, item, c, 1);
179 conn->execute(b4, results);
180 }
181
182 uint16_t start_zeny = sChar->config().start_zeny();
183
185 am.deliver(c_last_insert_id, start_zeny, new_map, name, slot, hair_color, hair_style, job_class, gender);
186
187 HLog(info) << "New character '" << name << "' has been created. (Endpoint: " << get_session()->get_socket()->remote_ip_address() << ")";
188 }
189 catch (boost::mysql::error_with_diagnostics &err) {
190 HLog(error) << "CharClientInterface::make_new_character:" << err.what();
191 return false;
192 }
193 catch (std::exception& err) {
194 HLog(error) << "CharClientInterface::make_new_character:" << err.what();
195 return false;
196 }
197
198 return true;
199}
Main object for the aegis packet: HC_ACCEPT_MAKECHAR.
Definition: TransmittedPackets.hpp:211
Main object for the aegis packet: HC_REFUSE_MAKECHAR.
Definition: TransmittedPackets.hpp:1091
@ HC_CREATE_ERROR_CHAR_SLOT
Definition: TransmittedPackets.hpp:1064
@ HC_CREATE_ERROR_ALREADY_EXISTS
Definition: TransmittedPackets.hpp:1060

References Horizon::Char::HC_REFUSE_MAKECHAR::deliver(), Horizon::Char::HC_ACCEPT_MAKECHAR::deliver(), Horizon::ClientInterface< CharSession >::get_session(), Horizon::Char::HC_CREATE_ERROR_ALREADY_EXISTS, Horizon::Char::HC_CREATE_ERROR_CHAR_SLOT, HLog, and sChar.

Referenced by make_new_character().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ make_new_character() [2/2]

bool CharClientInterface::make_new_character ( std::string  name,
uint8_t  slot,
uint8_t  hair_color,
uint8_t  hair_style 
)
202{
203 return make_new_character(name, slot, hair_color, hair_style, 0, 99);
204}
bool make_new_character(std::string name, uint8_t slot, uint32_t hair_color, uint32_t hair_style, uint32_t job_class, uint8_t gender)
Definition: CharClientInterface.cpp:136

References make_new_character().

+ Here is the call graph for this function:

◆ pincode_change()

bool CharClientInterface::pincode_change ( uint32_t  account_id,
char *  old_pin,
char *  new_pin 
)
446{
449
450 char old_decrypted[5] = "\0\0\0\0", new_decrypted[5] = "\0\0\0\0";
451
452 uint32_t seed = get_session()->get_session_data()._pincode_seed;
453
454 pincode_decrypt(seed, old_pin, old_decrypted);
455 pincode_decrypt(seed, new_pin, new_decrypted);
456
457 std::cout << old_decrypted << " - " << new_decrypted << std::endl;
458
459 std::shared_ptr<boost::mysql::tcp_ssl_connection> conn = sChar->get_database_connection();
460
461 try {
462 boost::mysql::statement stmt = conn->prepare_statement("SELECT `pincode` FROM `game_accounts` WHERE `id` = ?");
463 auto b1 = stmt.bind(account_id);
464 boost::mysql::results results;
465 conn->execute(b1, results);
466
467 std::string s_pincode = results.rows()[0][0].as_string();
468
469 if (s_pincode.compare(old_decrypted) != 0) {
470 esp.deliver(PINCODE_EDIT_FAILED);
471#if CLIENT_TYPE == 'M' && PACKET_VERSION >= 20180124 \
472|| CLIENT_TYPE == 'R' && PACKET_VERSION >= 20180124 \
473|| CLIENT_TYPE == 'Z' && PACKET_VERSION >= 20180131
475#else
476 sp.deliver(PINCODE_INCORRECT);
477#endif
478 return false;
479 }
480
481 {
482 uint32_t pincode_expiry = std::time(nullptr) + sChar->config().pincode_expiry();
483 stmt = conn->prepare_statement("UPDATE `game_accounts` SET `pincode` = ?, `pincode_expiry` = ? WHERE `id` = ?");
484 boost::mysql::results results;
485 auto b2 = stmt.bind(new_decrypted, pincode_expiry, account_id);
486 conn->execute(b2, results);
487 }
488 }
489 catch (boost::mysql::error_with_diagnostics &err) {
490 HLog(error) << "CharClientInterface::pincode_change: " << err.what();
491 return false;
492 }
493 catch (std::exception& err) {
494 HLog(error) << "CharClientInterface::pincode_change: " << err.what();
495 return false;
496 }
497
498 esp.deliver(PINCODE_EDIT_SUCCESS);
499 sp.deliver(PINCODE_CORRECT);
500 return true;
501}
void pincode_decrypt(uint32_t seed, char *input, char *output)
Definition: CharClientInterface.cpp:503
Main object for the aegis packet: HC_EDIT_SECOND_PASSWD.
Definition: TransmittedPackets.hpp:775
@ PINCODE_LOGIN_FLAG_WRONG
Definition: TransmittedPackets.hpp:1225
@ PINCODE_EDIT_SUCCESS
Definition: TransmittedPackets.hpp:765
@ PINCODE_EDIT_FAILED
Definition: TransmittedPackets.hpp:766
@ PINCODE_INCORRECT
Definition: TransmittedPackets.hpp:1216
@ PINCODE_CORRECT
Definition: TransmittedPackets.hpp:1208

References Horizon::Char::HC_SECOND_PASSWD_LOGIN::deliver(), Horizon::Char::HC_EDIT_SECOND_PASSWD::deliver(), Horizon::ClientInterface< CharSession >::get_session(), HLog, Horizon::Char::PINCODE_CORRECT, pincode_decrypt(), Horizon::Char::PINCODE_EDIT_FAILED, Horizon::Char::PINCODE_EDIT_SUCCESS, Horizon::Char::PINCODE_INCORRECT, Horizon::Char::PINCODE_LOGIN_FLAG_WRONG, and sChar.

+ Here is the call graph for this function:

◆ pincode_create()

bool CharClientInterface::pincode_create ( uint32_t  account_id,
char *  new_pin 
)
415{
418
419 char decrypted[5] = "\0\0\0\0";
420
421 uint32_t seed = get_session()->get_session_data()._pincode_seed;
422 pincode_decrypt(seed, pincode, decrypted);
423
424 {
425 uint32_t pincode_expiry = std::time(nullptr) + sChar->config().pincode_expiry();
426 std::shared_ptr<boost::mysql::tcp_ssl_connection> conn = sChar->get_database_connection();
427 try {
428 boost::mysql::statement stmt = conn->prepare_statement("UPDATE `game_accounts` SET `pincode` = ?, `pincode_expiry` = ? WHERE `id` = ? ");
429 auto b1 = stmt.bind(decrypted, pincode_expiry, account_id);
430 boost::mysql::results results;
431 conn->execute(b1, results);
432 }
433 catch (boost::mysql::error_with_diagnostics &err) {
434 HLog(error) << "CharClientInterface::pincode_create:" << err.what();
435 return false;
436 }
437 }
438
439 strncpy(_pincode_confirm, decrypted, MAX_PINCODE_STRING_LENGTH);
440 makestate.deliver(PINCODE_MAKE_SUCCESS);
441 login.deliver(PINCODE_REQUEST_PIN);
442 return true;
443}
#define MAX_PINCODE_STRING_LENGTH
Definition: Client.hpp:42
char _pincode_confirm[MAX_PINCODE_STRING_LENGTH]
Definition: CharClientInterface.hpp:67
Main object for the aegis packet: HC_MAKE_SECOND_PASSWD.
Definition: TransmittedPackets.hpp:815
@ PINCODE_MAKE_SUCCESS
Definition: TransmittedPackets.hpp:790

References _pincode_confirm, Horizon::Char::HC_SECOND_PASSWD_LOGIN::deliver(), Horizon::Char::HC_MAKE_SECOND_PASSWD::deliver(), Horizon::ClientInterface< CharSession >::get_session(), HLog, MAX_PINCODE_STRING_LENGTH, pincode_decrypt(), Horizon::Char::PINCODE_MAKE_SUCCESS, Horizon::Char::PINCODE_REQUEST_PIN, and sChar.

+ Here is the call graph for this function:

◆ pincode_decrypt()

void CharClientInterface::pincode_decrypt ( uint32_t  seed,
char *  input,
char *  output 
)
504{
505 int i;
506 char tab[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
507
508 if (input == nullptr || output == nullptr)
509 return;
510
511 for (i = 1; i < 10; i++) {
512 int pos;
513 seed = 0x881234 + seed * 0x3498;
514 pos = seed % (i + 1);
515 if (i != pos) {
516 tab[i] ^= tab[pos];
517 tab[pos] ^= tab[i];
518 tab[i] ^= tab[pos];
519 }
520 }
521
522 for (i = 0; i < 4; i++) {
523 if (input[i] < '0' || input[i] > '9')
524 input[i] = '0';
525 else
526 input[i] = tab[input[i] - '0'];
527 }
528
529 sprintf(output, "%d%d%d%d", input[0], input[1], input[2], input[3]);
530}

Referenced by pincode_change(), pincode_create(), and pincode_verify().

+ Here is the caller graph for this function:

◆ pincode_verify()

bool CharClientInterface::pincode_verify ( uint32_t  account_id,
char *  pincode 
)
533{
535
536 if (get_session()->get_session_data()._pincode_tries == sChar->config().pincode_max_retry()) {
537#if CLIENT_TYPE == 'M' && PACKET_VERSION >= 20180124 \
538|| CLIENT_TYPE == 'R' && PACKET_VERSION >= 20180124 \
539|| CLIENT_TYPE == 'Z' && PACKET_VERSION >= 20180131
541#else
542 sp.deliver(PINCODE_LOGIN_RESTRICT);
543#endif
544 return false;
545 }
546
547 char decrypted[5] = "\0\0\0\0";
548
549 uint32_t seed = get_session()->get_session_data()._pincode_seed;
550 pincode_decrypt(seed, pincode, decrypted);
551
552 std::shared_ptr<boost::mysql::tcp_ssl_connection> conn = sChar->get_database_connection();
553
554 try {
555 boost::mysql::statement stmt = conn->prepare_statement("SELECT `pincode` FROM `game_accounts` WHERE `id` = ?");
556 boost::mysql::results results;
557 auto b1 = stmt.bind(account_id);
558 conn->execute(b1, results);
559
560 if (results.rows().empty()) {
562 HLog(error) << "Attempt to select non-existent account " << account_id << " when verifying pincode.";
564 return false;
565 }
566 std::string s_pincode = results.rows()[0][0].as_string();
567
568 if (s_pincode.compare(decrypted) != 0) {
569 get_session()->get_session_data()._pincode_tries++;
570#if CLIENT_TYPE == 'M' && PACKET_VERSION >= 20180124 \
571|| CLIENT_TYPE == 'R' && PACKET_VERSION >= 20180124 \
572|| CLIENT_TYPE == 'Z' && PACKET_VERSION >= 20180131
574#else
575 sp.deliver(PINCODE_INCORRECT);
576#endif
577 return false;
578 }
579 }
580 catch (boost::mysql::error_with_diagnostics &err) {
581 HLog(error) << "CharClientInterface::pincode_verify:" << err.what();
582 return false;
583 }
584 catch (std::exception& err) {
585 HLog(error) << "CharClientInterface::pincode_verify:" << err.what();
586 return false;
587 }
588
589
590 sp.deliver(PINCODE_CORRECT);
591 return true;
592}
@ PINCODE_LOGIN_FLAG_LOCKED
Definition: TransmittedPackets.hpp:1223
@ PINCODE_LOGIN_RESTRICT
Definition: TransmittedPackets.hpp:1213

References Horizon::Char::CHAR_ERR_REJECTED_FROM_SERVER, Horizon::Char::HC_REFUSE_ENTER::deliver(), Horizon::Char::HC_SECOND_PASSWD_LOGIN::deliver(), Horizon::ClientInterface< CharSession >::get_session(), HLog, Horizon::Char::PINCODE_CORRECT, pincode_decrypt(), Horizon::Char::PINCODE_INCORRECT, Horizon::Char::PINCODE_LOGIN_FLAG_LOCKED, Horizon::Char::PINCODE_LOGIN_FLAG_WRONG, Horizon::Char::PINCODE_LOGIN_RESTRICT, and sChar.

+ Here is the call graph for this function:

◆ select_character()

bool CharClientInterface::select_character ( int  slot)
595{
596 std::shared_ptr<boost::mysql::tcp_ssl_connection> conn = sChar->get_database_connection();
597
598 try {
599 boost::mysql::statement stmt = conn->prepare_statement("SELECT `id`, `current_map` FROM `characters` WHERE `account_id` = ? AND `slot` = ? AND `deleted_at` = ?");
600 auto b1 = stmt.bind(get_session()->get_session_data()._account_id, slot, 0);
601 boost::mysql::results results;
602 conn->execute(b1, results);
603
604 if (results.rows().empty()) {
606 HLog(error) << "Attempt to select non-existent character in slot " << slot << " for account " << get_session()->get_session_data()._account_id << ".";
608 return false;
609 }
610
612 std::string current_map = results.rows()[0][1].as_string();
613 current_map.append(".gat");
614 int char_id = results.rows()[0][0].get_int64();
615 hnz.deliver(char_id, current_map, inet_addr(sChar->config().zone_server_ip().c_str()), sChar->config().zone_server_port());
616 }
617 catch (boost::mysql::error_with_diagnostics &err) {
618 HLog(error) << "CharClientInterface::select_character:" << err.what();
619 }
620 catch (std::exception& err) {
621 HLog(error) << "CharClientInterface::select_character:" << err.what();
622 }
623 return true;
624}
Main object for the aegis packet: HC_NOTIFY_ZONESVR.
Definition: TransmittedPackets.hpp:896

References Horizon::Char::CHAR_ERR_REJECTED_FROM_SERVER, Horizon::Char::HC_REFUSE_ENTER::deliver(), Horizon::Char::HC_NOTIFY_ZONESVR::deliver(), Horizon::ClientInterface< CharSession >::get_session(), HLog, and sChar.

+ Here is the call graph for this function:

◆ update_session()

bool CharClientInterface::update_session ( int32_t  account_id)
627{
628 HLog(debug) << "Updating session from I.P. address " << get_session()->get_socket()->remote_ip_address();
629
630 std::shared_ptr<boost::mysql::tcp_ssl_connection> conn = sChar->get_database_connection();
631
632 try {
633 boost::mysql::statement stmt = conn->prepare_statement("SELECT * FROM `session_data` WHERE `game_account_id` = ?");
634 auto a1 = stmt.bind(account_id);
635 boost::mysql::results results;
636 conn->execute(a1, results);
637
638 if (results.rows().empty()) {
640 HLog(warning) << "Invalid connection for account with ID " << account_id << ", session wasn't found.";
642 return false;
643 }
644
645 std::string current_server = results.rows()[0][7].as_string();
646
647 if (current_server.compare("C") != 0) {
649 HLog(warning) << "Invalid connection for account with ID " << account_id << ", session wasn't found.";
651 return false;
652 }
653
654 stmt = conn->prepare_statement("UPDATE `session_data` SET `last_update` = ? WHERE `game_account_id` = ?");
655 auto a2 = stmt.bind(std::time(nullptr), account_id);
656 conn->execute(a2, results);
657 }
658 catch (boost::mysql::error_with_diagnostics &err) {
659 HLog(error) << "CharClientInterface::update_session:" << err.what();
660 }
661 catch (std::exception& err) {
662 HLog(error) << "CharClientInterface::update_session:" << err.what();
663 }
664
665 return true;
666}

References Horizon::Char::CHAR_ERR_REJECTED_FROM_SERVER, Horizon::Char::HC_REFUSE_ENTER::deliver(), Horizon::ClientInterface< CharSession >::get_session(), HLog, and sChar.

+ Here is the call graph for this function:

Member Data Documentation

◆ _pincode_confirm

char Horizon::Char::CharClientInterface::_pincode_confirm[MAX_PINCODE_STRING_LENGTH]
protected

Referenced by pincode_create().


The documentation for this class was generated from the following files: