1313
1414std::map<CNSocket*, CNLoginData> CNLoginServer::loginSessions;
1515
16+ namespace LoginServer {
17+ std::vector<std::string> WheelFirstNames;
18+ std::vector<std::string> WheelMiddleNames;
19+ std::vector<std::string> WheelLastNames;
20+ }
21+
1622CNLoginServer::CNLoginServer (uint16_t p) {
1723 serverType = " login" ;
1824 port = p;
@@ -286,10 +292,29 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
286292 INITSTRUCT (sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC , resp);
287293
288294 int errorCode = 0 ;
289- if (!CNLoginServer::isCharacterNameGood (AUTOU16TOU8 (save->szFirstName ), AUTOU16TOU8 (save->szLastName ))) {
290- errorCode = 4 ;
291- } else if (!Database::isNameFree (AUTOU16TOU8 (save->szFirstName ), AUTOU16TOU8 (save->szLastName ))) {
292- errorCode = 1 ;
295+
296+ std::string firstName = AUTOU16TOU8 (save->szFirstName );
297+ std::string lastName = AUTOU16TOU8 (save->szLastName );
298+ int nameCheck = 0 ;
299+
300+ // if FNCode isn't 0, it's a wheel name
301+ if (save->iFNCode != 0 ) {
302+ if (!CNLoginServer::isNameWheelNameGood (save->iFNCode , save->iMNCode , save->iLNCode , firstName, lastName)) {
303+ errorCode = 4 ;
304+ } else {
305+ nameCheck = settings:: APPROVEWHEELNAMES ? 1 : 0 ;
306+ }
307+ } else {
308+ // custom name
309+ nameCheck = settings::APPROVECUSTOMNAMES ? 1 : 0 ;
310+ }
311+
312+ if (errorCode == 0 ) {
313+ if (!CNLoginServer::isCharacterNameGood (firstName, lastName)) {
314+ errorCode = 4 ;
315+ } else if (!Database::isNameFree (firstName, lastName)) {
316+ errorCode = 1 ;
317+ }
293318 }
294319
295320 if (errorCode != 0 ) {
@@ -307,19 +332,16 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
307332 if (!Database::isSlotFree (loginSessions[sock].userID , save->iSlotNum ))
308333 return invalidCharacter (sock);
309334
310- resp.iPC_UID = Database::createCharacter (save, loginSessions[sock].userID );
335+ resp.iPC_UID = Database::createCharacter (save-> iSlotNum , loginSessions[sock].userID , firstName. c_str (), lastName. c_str (), nameCheck );
311336 // if query somehow failed
312337 if (resp.iPC_UID == 0 ) {
313338 std::cout << " [WARN] Login Server: Database failed to create new character!" << std::endl;
314339 return invalidCharacter (sock);
315340 }
316341
317- Player plr;
318- Database::getPlayer (&plr, (int )resp.iPC_UID );
319-
320342 // fire name check event if needed
321- if (plr. PCStyle . iNameCheck != 1 ) {
322- std::string namereq = std::to_string (resp.iPC_UID ) + " " + AUTOU16TOU8 (save-> szFirstName ) + " " + AUTOU16TOU8 (save-> szLastName ) ;
343+ if (nameCheck != 1 ) {
344+ std::string namereq = std::to_string (resp.iPC_UID ) + " " + firstName + " " + lastName ;
323345 Monitor::namereqs.push_back (namereq);
324346 }
325347
@@ -338,8 +360,8 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
338360 DEBUGLOG (
339361 std::cout << " Login Server: new character created" << std::endl;
340362 std::cout << " \t Slot: " << (int )save->iSlotNum << std::endl;
341- std::cout << " \t Name: " << AUTOU16TOU8 (save-> szFirstName ) << " " << AUTOU16TOU8 (save-> szLastName ) ;
342- if (plr. PCStyle . iNameCheck != 1 ) std::cout << " (pending approval)" ;
363+ std::cout << " \t Name: " << firstName << " " << lastName ;
364+ if (nameCheck != 1 ) std::cout << " (pending approval)" ;
343365 std::cout << std::endl;
344366 )
345367}
@@ -507,11 +529,30 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
507529 auto save = (sP_CL2LS_REQ_CHANGE_CHAR_NAME *)data->buf ;
508530
509531 int errorCode = 0 ;
510- if (!CNLoginServer::isCharacterNameGood (AUTOU16TOU8 (save->szFirstName ), AUTOU16TOU8 (save->szLastName ))) {
511- errorCode = 4 ;
532+
533+ std::string firstName = AUTOU16TOU8 (save->szFirstName );
534+ std::string lastName = AUTOU16TOU8 (save->szLastName );
535+ int nameCheck = 0 ;
536+
537+ // if FNCode isn't 0, it's a wheel name
538+ if (save->iFNCode != 0 ) {
539+ if (!CNLoginServer::isNameWheelNameGood (save->iFNCode , save->iMNCode , save->iLNCode , firstName, lastName)) {
540+ errorCode = 4 ;
541+ } else {
542+ nameCheck = settings::APPROVEWHEELNAMES ? 1 : 0 ;
543+ }
544+ } else {
545+ // custom name
546+ nameCheck = settings::APPROVECUSTOMNAMES ? 1 : 0 ;
512547 }
513- else if (!Database::isNameFree (AUTOU16TOU8 (save->szFirstName ), AUTOU16TOU8 (save->szLastName ))) {
514- errorCode = 1 ;
548+
549+ if (errorCode == 0 ) {
550+ if (!CNLoginServer::isCharacterNameGood (firstName, lastName)) {
551+ errorCode = 4 ;
552+ }
553+ else if (!Database::isNameFree (firstName, lastName)) {
554+ errorCode = 1 ;
555+ }
515556 }
516557
517558 if (errorCode != 0 ) {
@@ -526,15 +567,12 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
526567 return ;
527568 }
528569
529- if (!Database::changeName (save, loginSessions[sock].userID ))
570+ if (!Database::changeName (save-> iPCUID , loginSessions[sock].userID , firstName. c_str (), lastName. c_str (), nameCheck ))
530571 return invalidCharacter (sock);
531572
532- Player plr;
533- Database::getPlayer (&plr, (int )save->iPCUID );
534-
535573 // fire name check event if needed
536- if (plr. PCStyle . iNameCheck != 1 ) {
537- std::string namereq = std::to_string (save->iPCUID ) + " " + AUTOU16TOU8 (save-> szFirstName ) + " " + AUTOU16TOU8 (save-> szLastName ) ;
574+ if (nameCheck != 1 ) {
575+ std::string namereq = std::to_string (save->iPCUID ) + " " + firstName + " " + lastName ;
538576 Monitor::namereqs.push_back (namereq);
539577 }
540578
@@ -550,8 +588,8 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
550588
551589 DEBUGLOG (
552590 std::cout << " Login Server: Name change request for character [" << save->iPCUID << " ]" << std::endl;
553- std::cout << " \t New name: " << AUTOU16TOU8 (save-> szFirstName ) << " " << AUTOU16TOU8 (save-> szLastName ) ;
554- if (plr. PCStyle . iNameCheck != 1 ) std::cout << " (pending approval)" ;
591+ std::cout << " \t New name: " << firstName << " " << lastName ;
592+ if (nameCheck != 1 ) std::cout << " (pending approval)" ;
555593 std::cout << std::endl;
556594 )
557595}
@@ -645,6 +683,42 @@ bool CNLoginServer::isPasswordCorrect(std::string actualPassword, std::string tr
645683 return BCrypt::validatePassword (tryPassword, actualPassword);
646684}
647685
686+ bool CNLoginServer::isNameWheelNameGood (int fnCode, int mnCode, int lnCode, std::string& firstName, std::string& lastName) {
687+ if (fnCode >= LoginServer::WheelFirstNames.size ()
688+ || mnCode >= LoginServer::WheelMiddleNames.size ()
689+ || lnCode >= LoginServer::WheelLastNames.size ()) {
690+ std::cout << " [WARN] Login Server: Invalid name codes received: " << fnCode << " " << mnCode << " " << lnCode << std::endl;
691+ return false ;
692+ }
693+
694+ // client sends 1 if not selected for these. they point to a single blank space. why.
695+ // just change them to 0, which points to an empty string; keeps the code much cleaner
696+ if (mnCode == 1 ) mnCode = 0 ;
697+ if (lnCode == 1 ) lnCode = 0 ;
698+
699+ std::string firstNameFromWheel = LoginServer::WheelFirstNames[fnCode];
700+
701+ std::string middleNamePart = LoginServer::WheelMiddleNames[mnCode];
702+ std::string lastNamePart = LoginServer::WheelLastNames[lnCode];
703+ if (mnCode != 0 && middleNamePart[middleNamePart.size () - 1 ] != ' ' ) {
704+ // If there's a middle name, we need to lowercase the last name
705+ std::transform (lastNamePart.begin (), lastNamePart.end (), lastNamePart.begin (), ::tolower);
706+ }
707+ std::string lastNameFromWheel = middleNamePart + lastNamePart;
708+
709+ if (firstNameFromWheel.empty () || lastNameFromWheel.empty ()) {
710+ std::cout << " [WARN] Login Server: Invalid wheel name combo: " << fnCode << " " << mnCode << " " << lnCode << std::endl;
711+ return false ;
712+ }
713+
714+ if (firstName != firstNameFromWheel || lastName != lastNameFromWheel) {
715+ std::cout << " [WARN] Login Server: Name wheel mismatch. Expected " << firstNameFromWheel << " " << lastNameFromWheel << " , got " << firstName << " " << lastName << std::endl;
716+ return false ;
717+ }
718+
719+ return true ;
720+ }
721+
648722bool CNLoginServer::isCharacterNameGood (std::string Firstname, std::string Lastname) {
649723 // Allow alphanumeric and dot characters in names(disallows dot and space characters at the beginning of a name)
650724 std::regex firstnamecheck (R"( ((?! )(?!\.)[a-zA-Z0-9]*\.{0,1}(?!\.+ +)[a-zA-Z0-9]* {0,1}(?! +))*$)" );
0 commit comments