diff --git a/common/string_util.cpp b/common/string_util.cpp index 77961dfc5..f0a42b91d 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -19,32 +19,33 @@ #include #ifdef _WINDOWS - #include +#include - #define snprintf _snprintf - #define strncasecmp _strnicmp - #define strcasecmp _stricmp +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp #else - #include - #include + +#include +#include #include #endif #ifndef va_copy - #define va_copy(d,s) ((d) = (s)) +#define va_copy(d,s) ((d) = (s)) #endif // original source: // https://github.com/facebook/folly/blob/master/folly/String.cpp // -const std::string vStringFormat(const char* format, va_list args) +const std::string vStringFormat(const char *format, va_list args) { std::string output; - va_list tmpargs; + va_list tmpargs; - va_copy(tmpargs,args); + va_copy(tmpargs, args); int characters_used = vsnprintf(nullptr, 0, format, tmpargs); va_end(tmpargs); @@ -52,7 +53,7 @@ const std::string vStringFormat(const char* format, va_list args) if (characters_used > 0) { output.resize(characters_used + 1); - va_copy(tmpargs,args); + va_copy(tmpargs, args); characters_used = vsnprintf(&output[0], output.capacity(), format, tmpargs); va_end(tmpargs); @@ -60,8 +61,9 @@ const std::string vStringFormat(const char* format, va_list args) // We shouldn't have a format error by this point, but I can't imagine what error we // could have by this point. Still, return empty string; - if (characters_used < 0) + if (characters_used < 0) { output.clear(); + } } return output; } @@ -87,8 +89,9 @@ const std::string str_toupper(std::string s) const std::string ucfirst(std::string s) { std::string output = s; - if (!s.empty()) + if (!s.empty()) { output[0] = static_cast(::toupper(s[0])); + } return output; } @@ -102,18 +105,20 @@ const std::string StringFormat(const char *format, ...) return output; } -std::vector SplitString(const std::string &str, const char delim) { +std::vector SplitString(const std::string &str, const char delim) +{ std::vector ret; - std::string::size_type start = 0; - auto end = str.find(delim); + std::string::size_type start = 0; + auto end = str.find(delim); while (end != std::string::npos) { ret.emplace_back(str, start, end - start); start = end + 1; - end = str.find(delim, start); + end = str.find(delim, start); } // this will catch the last word since the string is unlikely to end with a delimiter - if (str.length() > start) + if (str.length() > start) { ret.emplace_back(str, start, str.length() - start); + } return ret; } @@ -153,14 +158,16 @@ std::string get_between(const std::string &s, std::string start_delim, std::stri return ""; } -std::string::size_type search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator) +std::string::size_type +search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator) { // this shouldn't go out of bounds, even without obvious bounds checks auto pos = haystack.find(needle); while (pos != std::string::npos) { auto c = haystack[pos + needle.length()]; - if ((c == '\0' || c == deliminator) && (pos == 0 || haystack[pos - 1] == deliminator)) + if ((c == '\0' || c == deliminator) && (pos == 0 || haystack[pos - 1] == deliminator)) { return pos; + } pos = haystack.find(needle, pos + needle.length()); } return std::string::npos; @@ -180,7 +187,7 @@ std::string implode(std::string glue, std::vector src) } std::string final_output = output.str(); - final_output.resize (output.str().size () - glue.size()); + final_output.resize(output.str().size() - glue.size()); return final_output; } @@ -202,80 +209,83 @@ std::vector wrap(std::vector &src, std::string charact return new_vector; } -std::string EscapeString(const std::string &s) { +std::string EscapeString(const std::string &s) +{ std::string ret; - size_t sz = s.length(); - for(size_t i = 0; i < sz; ++i) { + size_t sz = s.length(); + for (size_t i = 0; i < sz; ++i) { char c = s[i]; - switch(c) { - case '\x00': - ret += "\\x00"; - break; - case '\n': - ret += "\\n"; - break; - case '\r': - ret += "\\r"; - break; - case '\\': - ret += "\\\\"; - break; - case '\'': - ret += "\\'"; - break; - case '\"': - ret += "\\\""; - break; - case '\x1a': - ret += "\\x1a"; - break; - default: - ret.push_back(c); - break; + switch (c) { + case '\x00': + ret += "\\x00"; + break; + case '\n': + ret += "\\n"; + break; + case '\r': + ret += "\\r"; + break; + case '\\': + ret += "\\\\"; + break; + case '\'': + ret += "\\'"; + break; + case '\"': + ret += "\\\""; + break; + case '\x1a': + ret += "\\x1a"; + break; + default: + ret.push_back(c); + break; } } return ret; } -std::string EscapeString(const char *src, size_t sz) { +std::string EscapeString(const char *src, size_t sz) +{ std::string ret; - for(size_t i = 0; i < sz; ++i) { + for (size_t i = 0; i < sz; ++i) { char c = src[i]; - switch(c) { - case '\x00': - ret += "\\x00"; - break; - case '\n': - ret += "\\n"; - break; - case '\r': - ret += "\\r"; - break; - case '\\': - ret += "\\\\"; - break; - case '\'': - ret += "\\'"; - break; - case '\"': - ret += "\\\""; - break; - case '\x1a': - ret += "\\x1a"; - break; - default: - ret.push_back(c); - break; + switch (c) { + case '\x00': + ret += "\\x00"; + break; + case '\n': + ret += "\\n"; + break; + case '\r': + ret += "\\r"; + break; + case '\\': + ret += "\\\\"; + break; + case '\'': + ret += "\\'"; + break; + case '\"': + ret += "\\\""; + break; + case '\x1a': + ret += "\\x1a"; + break; + default: + ret.push_back(c); + break; } } return ret; } -bool StringIsNumber(const std::string &s) { +bool StringIsNumber(const std::string &s) +{ try { auto r = stod(s); return true; @@ -285,15 +295,18 @@ bool StringIsNumber(const std::string &s) { } } -void ToLowerString(std::string &s) { +void ToLowerString(std::string &s) +{ std::transform(s.begin(), s.end(), s.begin(), ::tolower); } -void ToUpperString(std::string &s) { +void ToUpperString(std::string &s) +{ std::transform(s.begin(), s.end(), s.begin(), ::toupper); } -std::string JoinString(const std::vector& ar, const std::string &delim) { +std::string JoinString(const std::vector &ar, const std::string &delim) +{ std::string ret; for (size_t i = 0; i < ar.size(); ++i) { if (i != 0) { @@ -313,7 +326,7 @@ void find_replace(std::string &string_subject, const std::string &search_string, } size_t start_pos = 0; - while((start_pos = string_subject.find(search_string, start_pos)) != std::string::npos) { + while ((start_pos = string_subject.find(search_string, start_pos)) != std::string::npos) { string_subject.replace(start_pos, search_string.length(), replace_string); start_pos += replace_string.length(); } @@ -334,9 +347,9 @@ void ParseAccountString(const std::string &s, std::string &account, std::string auto split = SplitString(s, ':'); if (split.size() == 2) { loginserver = split[0]; - account = split[1]; + account = split[1]; } - else if(split.size() == 1) { + else if (split.size() == 1) { account = split[0]; } } @@ -345,9 +358,11 @@ void ParseAccountString(const std::string &s, std::string &account, std::string // normal strncpy doesnt put a null term on copied strings, this one does // ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp -char* strn0cpy(char* dest, const char* source, uint32 size) { - if (!dest) +char *strn0cpy(char *dest, const char *source, uint32 size) +{ + if (!dest) { return 0; + } if (size == 0 || source == 0) { dest[0] = 0; return dest; @@ -359,123 +374,159 @@ char* strn0cpy(char* dest, const char* source, uint32 size) { // String N w/null Copy Truncated? // return value =true if entire string(source) fit, false if it was truncated -bool strn0cpyt(char* dest, const char* source, uint32 size) { - if (!dest) +bool strn0cpyt(char *dest, const char *source, uint32 size) +{ + if (!dest) { return 0; + } if (size == 0 || source == 0) { dest[0] = 0; return false; } strncpy(dest, source, size); dest[size - 1] = 0; - return (bool)(source[strlen(dest)] == 0); + return (bool) (source[strlen(dest)] == 0); } -const char *MakeLowerString(const char *source) { +const char *MakeLowerString(const char *source) +{ static char str[128]; - if (!source) + if (!source) { return nullptr; + } MakeLowerString(source, str); return str; } -void MakeLowerString(const char *source, char *target) { +void MakeLowerString(const char *source, char *target) +{ if (!source || !target) { *target = 0; return; } - while (*source) - { + while (*source) { *target = tolower(*source); - target++; source++; + target++; + source++; } *target = 0; } -uint32 hextoi(const char* num) { - if (num == nullptr) +uint32 hextoi(const char *num) +{ + if (num == nullptr) { return 0; + } int len = strlen(num); - if (len < 3) + if (len < 3) { return 0; + } - if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) { return 0; + } - uint32 ret = 0; - int mul = 1; - for (int i = len - 1; i >= 2; i--) { - if (num[i] >= 'A' && num[i] <= 'F') + uint32 ret = 0; + int mul = 1; + for (int i = len - 1; i >= 2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') { ret += ((num[i] - 'A') + 10) * mul; - else if (num[i] >= 'a' && num[i] <= 'f') + } + else if (num[i] >= 'a' && num[i] <= 'f') { ret += ((num[i] - 'a') + 10) * mul; - else if (num[i] >= '0' && num[i] <= '9') + } + else if (num[i] >= '0' && num[i] <= '9') { ret += (num[i] - '0') * mul; - else + } + else { return 0; + } mul *= 16; } return ret; } -uint64 hextoi64(const char* num) { - if (num == nullptr) +uint64 hextoi64(const char *num) +{ + if (num == nullptr) { return 0; + } int len = strlen(num); - if (len < 3) + if (len < 3) { return 0; + } - if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) { return 0; + } - uint64 ret = 0; - int mul = 1; - for (int i = len - 1; i >= 2; i--) { - if (num[i] >= 'A' && num[i] <= 'F') + uint64 ret = 0; + int mul = 1; + for (int i = len - 1; i >= 2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') { ret += ((num[i] - 'A') + 10) * mul; - else if (num[i] >= 'a' && num[i] <= 'f') + } + else if (num[i] >= 'a' && num[i] <= 'f') { ret += ((num[i] - 'a') + 10) * mul; - else if (num[i] >= '0' && num[i] <= '9') + } + else if (num[i] >= '0' && num[i] <= '9') { ret += (num[i] - '0') * mul; - else + } + else { return 0; + } mul *= 16; } return ret; } -bool atobool(const char* iBool) { +bool atobool(const char *iBool) +{ - if (iBool == nullptr) + if (iBool == nullptr) { return false; - if (!strcasecmp(iBool, "true")) + } + if (!strcasecmp(iBool, "true")) { return true; - if (!strcasecmp(iBool, "false")) + } + if (!strcasecmp(iBool, "false")) { return false; - if (!strcasecmp(iBool, "yes")) + } + if (!strcasecmp(iBool, "yes")) { return true; - if (!strcasecmp(iBool, "no")) + } + if (!strcasecmp(iBool, "no")) { return false; - if (!strcasecmp(iBool, "on")) + } + if (!strcasecmp(iBool, "on")) { return true; - if (!strcasecmp(iBool, "off")) + } + if (!strcasecmp(iBool, "off")) { return false; - if (!strcasecmp(iBool, "enable")) + } + if (!strcasecmp(iBool, "enable")) { return true; - if (!strcasecmp(iBool, "disable")) + } + if (!strcasecmp(iBool, "disable")) { return false; - if (!strcasecmp(iBool, "enabled")) + } + if (!strcasecmp(iBool, "enabled")) { return true; - if (!strcasecmp(iBool, "disabled")) + } + if (!strcasecmp(iBool, "disabled")) { return false; - if (!strcasecmp(iBool, "y")) + } + if (!strcasecmp(iBool, "y")) { return true; - if (!strcasecmp(iBool, "n")) + } + if (!strcasecmp(iBool, "n")) { return false; - if (atoi(iBool)) + } + if (atoi(iBool)) { return true; + } return false; } @@ -484,21 +535,19 @@ char *CleanMobName(const char *in, char *out) { unsigned i, j; - for (i = j = 0; i < strlen(in); i++) - { + for (i = j = 0; i < strlen(in); i++) { // convert _ to space.. any other conversions like this? I *think* this // is the only non alpha char that's not stripped but converted. - if (in[i] == '_') - { + if (in[i] == '_') { out[j++] = ' '; } - else - { - if (isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped + else { + if (isalpha(in[i]) || (in[i] == '`')) { // numbers, #, or any other crap just gets skipped out[j++] = in[i]; + } } } - out[j] = 0; // terimnate the string before returning it + out[j] = 0; // terimnate the string before returning it return out; } @@ -506,8 +555,9 @@ char *CleanMobName(const char *in, char *out) void RemoveApostrophes(std::string &s) { for (unsigned int i = 0; i < s.length(); ++i) - if (s[i] == '\'') + if (s[i] == '\'') { s[i] = '_'; + } } char *RemoveApostrophes(const char *s) @@ -517,8 +567,9 @@ char *RemoveApostrophes(const char *s) strcpy(NewString, s); for (unsigned int i = 0; i < strlen(NewString); ++i) - if (NewString[i] == '\'') + if (NewString[i] == '\'') { NewString[i] = '_'; + } return NewString; } @@ -537,11 +588,12 @@ const char *ConvertArrayF(float input, char *returnchar) bool isAlphaNumeric(const char *text) { - for (unsigned int charIndex = 0; charIndex 'z') && (text[charIndex] < 'A' || text[charIndex] > 'Z') && - (text[charIndex] < '0' || text[charIndex] > '9')) + (text[charIndex] < '0' || text[charIndex] > '9')) { return false; + } } return true; @@ -596,11 +648,10 @@ std::string numberToWords(unsigned long long int n) } // first letter capitalized and rest made lower case -std::string FormatName(const std::string& char_name) +std::string FormatName(const std::string &char_name) { std::string formatted(char_name); - if (!formatted.empty()) - { + if (!formatted.empty()) { std::transform(formatted.begin(), formatted.end(), formatted.begin(), ::tolower); formatted[0] = ::toupper(formatted[0]); } @@ -620,6 +671,12 @@ bool IsAllowedWorldServerCharacterList(char c) void SanitizeWorldServerName(char *name) { std::string server_long_name = name; + + strcpy(name, SanitizeWorldServerName(server_long_name).c_str()); +} + +std::string SanitizeWorldServerName(std::string server_long_name) +{ server_long_name.erase( std::remove_if( server_long_name.begin(), @@ -632,5 +689,333 @@ void SanitizeWorldServerName(char *name) server_long_name = trim(server_long_name); - strcpy(name, server_long_name.c_str()); + // bad word filter + for (auto &piece: split_string(server_long_name, " ")) { + for (auto &word: GetBadWords()) { + // for shorter words that can actually be part of legitimate words + // make sure that it isn't part of another word by matching on a space + if (str_tolower(piece) == word) { + find_replace( + server_long_name, + piece, + repeat("*", (int) word.length()) + ); + continue; + } + + auto pos = str_tolower(piece).find(word); + if (str_tolower(piece).find(word) != std::string::npos && piece.length() > 4 && word.length() > 4) { + auto found_word = piece.substr(pos, word.length()); + std::string replaced_piece = piece.substr(pos, word.length()); + + find_replace( + server_long_name, + replaced_piece, + repeat("*", (int) word.length()) + ); + } + } + } + + return server_long_name; +} + +std::string repeat(std::string s, int n) +{ + std::string s1 = s; + for (int i = 1; i < n; i++) { + s += s1; + } + + return s; +} + +std::vector GetBadWords() +{ + return std::vector{ + "2g1c", + "acrotomophilia", + "anal", + "anilingus", + "anus", + "apeshit", + "arsehole", + "ass", + "asshole", + "assmunch", + "autoerotic", + "babeland", + "bangbros", + "bangbus", + "bareback", + "barenaked", + "bastard", + "bastardo", + "bastinado", + "bbw", + "bdsm", + "beaner", + "beaners", + "beaver", + "beastiality", + "bestiality", + "bimbos", + "birdlock", + "bitch", + "bitches", + "blowjob", + "blumpkin", + "bollocks", + "bondage", + "boner", + "boob", + "boobs", + "bukkake", + "bulldyke", + "bullshit", + "bung", + "bunghole", + "busty", + "butt", + "buttcheeks", + "butthole", + "camel toe", + "camgirl", + "camslut", + "camwhore", + "carpetmuncher", + "cialis", + "circlejerk", + "clit", + "clitoris", + "clusterfuck", + "cock", + "cocks", + "coprolagnia", + "coprophilia", + "cornhole", + "coon", + "coons", + "creampie", + "cum", + "cumming", + "cumshot", + "cumshots", + "cunnilingus", + "cunt", + "darkie", + "daterape", + "deepthroat", + "dendrophilia", + "dick", + "dildo", + "dingleberry", + "dingleberries", + "doggiestyle", + "doggystyle", + "dolcett", + "domination", + "dominatrix", + "dommes", + "hump", + "dvda", + "ecchi", + "ejaculation", + "erotic", + "erotism", + "escort", + "eunuch", + "fag", + "faggot", + "fecal", + "felch", + "fellatio", + "feltch", + "femdom", + "figging", + "fingerbang", + "fingering", + "fisting", + "footjob", + "frotting", + "fuck", + "fuckin", + "fucking", + "fucktards", + "fudgepacker", + "futanari", + "gangbang", + "gangbang", + "gaysex", + "genitals", + "goatcx", + "goatse", + "gokkun", + "goodpoop", + "goregasm", + "grope", + "g-spot", + "guro", + "handjob", + "hentai", + "homoerotic", + "honkey", + "hooker", + "horny", + "humping", + "incest", + "intercourse", + "jailbait", + "jigaboo", + "jiggaboo", + "jiggerboo", + "jizz", + "juggs", + "kike", + "kinbaku", + "kinkster", + "kinky", + "knobbing", + "livesex", + "lolita", + "lovemaking", + "masturbate", + "masturbating", + "masturbation", + "milf", + "mong", + "motherfucker", + "muffdiving", + "nambla", + "nawashi", + "negro", + "neonazi", + "nigga", + "nigger", + "nimphomania", + "nipple", + "nipples", + "nsfw", + "nude", + "nudity", + "nutten", + "nympho", + "nymphomania", + "octopussy", + "omorashi", + "orgasm", + "orgy", + "paedophile", + "paki", + "panties", + "panty", + "pedobear", + "pedophile", + "pegging", + "penis", + "pikey", + "pissing", + "pisspig", + "playboy", + "ponyplay", + "poof", + "poon", + "poontang", + "punany", + "poopchute", + "porn", + "porno", + "pornography", + "pthc", + "pubes", + "pussy", + "queaf", + "queef", + "quim", + "raghead", + "rape", + "raping", + "rapist", + "rectum", + "rimjob", + "rimming", + "sadism", + "santorum", + "scat", + "schlong", + "scissoring", + "semen", + "sex", + "sexcam", + "sexo", + "sexy", + "sexual", + "sexually", + "sexuality", + "shemale", + "shibari", + "shit", + "shitblimp", + "shitty", + "shota", + "shrimping", + "skeet", + "slanteye", + "slut", + "s&m", + "smut", + "snatch", + "snowballing", + "sodomize", + "sodomy", + "spastic", + "spic", + "splooge", + "spooge", + "spunk", + "strapon", + "strappado", + "suck", + "sucks", + "swastika", + "swinger", + "threesome", + "throating", + "thumbzilla", + "tight white", + "tit", + "tits", + "titties", + "titty", + "topless", + "tosser", + "towelhead", + "tranny", + "tribadism", + "tubgirl", + "tushy", + "twat", + "twink", + "twinkie", + "undressing", + "upskirt", + "urophilia", + "vagina", + "viagra", + "vibrator", + "vorarephilia", + "voyeur", + "voyeurweb", + "voyuer", + "vulva", + "wank", + "wetback", + "whore", + "worldsex", + "xx", + "xxx", + "yaoi", + "yiffy", + "zoophilia" + }; } diff --git a/common/string_util.h b/common/string_util.h index 9402bfcb6..0353c5c98 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -208,7 +208,11 @@ void RemoveApostrophes(std::string &s); std::string convert2digit(int n, std::string suffix); std::string numberToWords(unsigned long long int n); std::string FormatName(const std::string& char_name); +bool IsAllowedWorldServerCharacterList(char c); void SanitizeWorldServerName(char *name); +std::string SanitizeWorldServerName(std::string server_long_name); +std::string repeat(std::string s, int n); +std::vector GetBadWords(); template auto CleanMobName(InputIterator first, InputIterator last, OutputIterator result) diff --git a/loginserver/CMakeLists.txt b/loginserver/CMakeLists.txt index 7d60655fc..eec55af2a 100644 --- a/loginserver/CMakeLists.txt +++ b/loginserver/CMakeLists.txt @@ -22,7 +22,7 @@ SET(eqlogin_headers loginserver_command_handler.h loginserver_webserver.h login_server.h - login_structures.h + login_types.h options.h server_manager.h world_server.h diff --git a/loginserver/client.h b/loginserver/client.h index 1a9b931fd..95aa0cfa7 100644 --- a/loginserver/client.h +++ b/loginserver/client.h @@ -7,44 +7,9 @@ #include "../common/eq_stream_intf.h" #include "../common/net/dns.h" #include "../common/net/daybreak_connection.h" -#include "login_structures.h" +#include "login_types.h" #include -enum LSClientVersion { - cv_titanium, - cv_sod -}; - -enum LSClientStatus { - cs_not_sent_session_ready, - cs_waiting_for_login, - cs_creating_account, - cs_failed_to_login, - cs_logged_in -}; - -namespace LS { - namespace ServerStatusFlags { - enum eServerStatusFlags { - Up = 0, // default - Down = 1, - Unused = 2, - Locked = 4 // can be combined with Down to show "Locked (Down)" - }; - } - - namespace ServerTypeFlags { - enum eServerTypeFlags { - None = 0, - Standard = 1, - Unknown2 = 2, - Unknown4 = 4, - Preferred = 8, - Legends = 16 // can be combined with Preferred flag to override color in Legends section with Preferred color (green) - }; - } -} - /** * Client class, controls a single client and it's connection to the login server */ diff --git a/loginserver/login_structures.h b/loginserver/login_types.h similarity index 57% rename from loginserver/login_structures.h rename to loginserver/login_types.h index 01a0bf201..2dfca14fc 100644 --- a/loginserver/login_structures.h +++ b/loginserver/login_types.h @@ -4,39 +4,34 @@ #pragma pack(1) // unencrypted base message header in all packets -struct LoginBaseMessage_Struct -{ +struct LoginBaseMessage_Struct { int32_t sequence; // request type/login sequence (2: handshake, 3: login, 4: serverlist, ...) bool compressed; // true: deflated int8_t encrypt_type; // 1: invert (unused) 2: des (2 for encrypted player logins and order expansions) (client uses what it sent, ignores in reply) int32_t unk3; // unused? }; -struct LoginBaseReplyMessage_Struct -{ +struct LoginBaseReplyMessage_Struct { bool success; // 0: failure (shows error string) 1: success int32_t error_str_id; // last error eqlsstr id, default: 101 (no error) char str[1]; // variable length, unknown (may be unused, this struct is a common pattern elsewhere) }; -struct LoginHandShakeReply_Struct -{ - LoginBaseMessage_Struct base_header; +struct LoginHandShakeReply_Struct { + LoginBaseMessage_Struct base_header; LoginBaseReplyMessage_Struct base_reply; - char unknown[1]; // variable length string + char unknown[1]; // variable length string }; // for reference, login buffer is variable (minimum size 8 due to encryption) -struct PlayerLogin_Struct -{ +struct PlayerLogin_Struct { LoginBaseMessage_Struct base_header; - char username[1]; - char password[1]; + char username[1]; + char password[1]; }; // variable length, can use directly if not serializing strings -struct PlayerLoginReply_Struct -{ +struct PlayerLoginReply_Struct { // base header excluded to make struct data easier to encrypt //LoginBaseMessage_Struct base_header; LoginBaseReplyMessage_Struct base_reply; @@ -59,8 +54,7 @@ struct PlayerLoginReply_Struct }; // variable length, for reference -struct LoginClientServerData_Struct -{ +struct LoginClientServerData_Struct { char ip[1]; int32_t server_type; // legends, preferred, standard int32_t server_id; @@ -72,29 +66,78 @@ struct LoginClientServerData_Struct }; // variable length, for reference -struct ServerListReply_Struct -{ - LoginBaseMessage_Struct base_header; +struct ServerListReply_Struct { + LoginBaseMessage_Struct base_header; LoginBaseReplyMessage_Struct base_reply; - int32_t server_count; + int32_t server_count; LoginClientServerData_Struct servers[0]; }; struct PlayEverquestRequest_Struct { LoginBaseMessage_Struct base_header; - uint32 server_number; + uint32 server_number; }; // SCJoinServerReply struct PlayEverquestResponse_Struct { - LoginBaseMessage_Struct base_header; + LoginBaseMessage_Struct base_header; LoginBaseReplyMessage_Struct base_reply; - uint32 server_number; + uint32 server_number; }; - #pragma pack() +enum LSClientVersion { + cv_titanium, + cv_sod +}; + +enum LSClientStatus { + cs_not_sent_session_ready, + cs_waiting_for_login, + cs_creating_account, + cs_failed_to_login, + cs_logged_in +}; + +namespace LS { + namespace ServerStatusFlags { + enum eServerStatusFlags { + Up = 0, // default + Down = 1, + Unused = 2, + Locked = 4 // can be combined with Down to show "Locked (Down)" + }; + } + + namespace ServerTypeFlags { + enum eServerTypeFlags { + None = 0, + Standard = 1, + Unknown2 = 2, + Unknown4 = 4, + Preferred = 8, + Legends = 16 // can be combined with Preferred flag to override color in Legends section with Preferred color (green) + }; + } + + enum ServerType { + Standard = 3, + Preferred = 2, + Legends = 1, + }; + + namespace ErrStr { + constexpr static int NO_ERROR = 101; // No Error + constexpr static int SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later. + constexpr static int ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information. + constexpr static int ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information. + constexpr static int WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later. + constexpr static int ERROR_1018_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again. + constexpr static int UNKNOWN_ERROR = 102; // Error - Unknown Error Occurred + }; +} + #endif diff --git a/loginserver/login_util/login.json b/loginserver/login_util/login.json index af0f042cf..c25242f7c 100644 --- a/loginserver/login_util/login.json +++ b/loginserver/login_util/login.json @@ -12,6 +12,8 @@ "worldservers": { "unregistered_allowed": true, "show_player_count": false, + "dev_test_servers_list_bottom": false, + "special_character_start_list_bottom": false, "reject_duplicate_servers": false }, "web_api": { diff --git a/loginserver/main.cpp b/loginserver/main.cpp index be925efa6..46f2313b3 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -62,9 +62,30 @@ void LoadServerConfig() "worldservers", "reject_duplicate_servers", false - )); - server.options.AllowUnregistered(server.config.GetVariableBool("worldservers", "unregistered_allowed", true)); + ) + ); server.options.SetShowPlayerCount(server.config.GetVariableBool("worldservers", "show_player_count", false)); + server.options.AllowUnregistered( + server.config.GetVariableBool( + "worldservers", + "unregistered_allowed", + true + ) + ); + server.options.SetWorldDevTestServersListBottom( + server.config.GetVariableBool( + "worldservers", + "dev_test_servers_list_bottom", + false + ) + ); + server.options.SetWorldSpecialCharacterStartListBottom( + server.config.GetVariableBool( + "worldservers", + "special_character_start_list_bottom", + false + ) + ); /** * Account @@ -242,6 +263,14 @@ int main(int argc, char **argv) LogInfo("[Config] [WorldServer] IsRejectingDuplicateServers [{0}]", server.options.IsRejectingDuplicateServers()); LogInfo("[Config] [WorldServer] IsUnregisteredAllowed [{0}]", server.options.IsUnregisteredAllowed()); LogInfo("[Config] [WorldServer] ShowPlayerCount [{0}]", server.options.IsShowPlayerCountEnabled()); + LogInfo( + "[Config] [WorldServer] DevAndTestServersListBottom [{0}]", + server.options.IsWorldDevTestServersListBottom() + ); + LogInfo( + "[Config] [WorldServer] SpecialCharactersStartListBottom [{0}]", + server.options.IsWorldSpecialCharacterStartListBottom() + ); LogInfo("[Config] [Security] GetEncryptionMode [{0}]", server.options.GetEncryptionMode()); LogInfo("[Config] [Security] IsTokenLoginAllowed [{0}]", server.options.IsTokenLoginAllowed()); LogInfo("[Config] [Security] IsPasswordLoginAllowed [{0}]", server.options.IsPasswordLoginAllowed()); diff --git a/loginserver/options.h b/loginserver/options.h index c6fc1589d..cf1de76d3 100644 --- a/loginserver/options.h +++ b/loginserver/options.h @@ -121,6 +121,20 @@ public: { Options::show_player_count = show_player_count; } + inline bool IsWorldDevTestServersListBottom() const { return world_dev_test_servers_list_bottom; } + inline void SetWorldDevTestServersListBottom(bool dev_test_servers_list_bottom) + { + Options::world_dev_test_servers_list_bottom = dev_test_servers_list_bottom; + } + + inline bool IsWorldSpecialCharacterStartListBottom() const + { + return world_special_character_start_list_bottom; + } + inline void SetWorldSpecialCharacterStartListBottom(bool world_special_character_start_list_bottom) + { + Options::world_special_character_start_list_bottom = world_special_character_start_list_bottom; + } private: bool allow_unregistered; @@ -129,6 +143,8 @@ private: bool dump_in_packets; bool dump_out_packets; bool reject_duplicate_servers; + bool world_dev_test_servers_list_bottom; + bool world_special_character_start_list_bottom; bool allow_token_login; bool allow_password_login; bool show_player_count; diff --git a/loginserver/server_manager.cpp b/loginserver/server_manager.cpp index d49809278..05758dc07 100644 --- a/loginserver/server_manager.cpp +++ b/loginserver/server_manager.cpp @@ -1,6 +1,6 @@ #include "server_manager.h" #include "login_server.h" -#include "login_structures.h" +#include "login_types.h" #include #include "../common/eqemu_logsys.h" diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index b6dc6885d..6e72befdf 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -1,6 +1,6 @@ #include "world_server.h" #include "login_server.h" -#include "login_structures.h" +#include "login_types.h" #include "../common/ip_util.h" #include "../common/string_util.h" @@ -198,15 +198,15 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne LogDebug("User-To-World Response received"); } - auto *user_to_world_response = (UsertoWorldResponseLegacy_Struct *) packet.Data(); + auto *r = (UsertoWorldResponseLegacy_Struct *) packet.Data(); - LogDebug("Trying to find client with user id of [{0}]", user_to_world_response->lsaccountid); - Client *client = server.client_manager->GetClient(user_to_world_response->lsaccountid, "eqemu"); + LogDebug("Trying to find client with user id of [{0}]", r->lsaccountid); + Client *client = server.client_manager->GetClient(r->lsaccountid, "eqemu"); if (client) { LogDebug( "Found client with user id of [{0}] and account name of [{1}]", - user_to_world_response->lsaccountid, + r->lsaccountid, client->GetAccountName() ); @@ -217,9 +217,9 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer; per->base_header.sequence = client->GetPlaySequence(); - per->server_number = client->GetPlayServerID(); + per->server_number = client->GetPlayServerID(); - if (user_to_world_response->response > 0) { + if (r->response > 0) { per->base_reply.success = true; SendClientAuth( client->GetConnection()->GetRemoteAddr(), @@ -230,27 +230,27 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne ); } - switch (user_to_world_response->response) { + switch (r->response) { case UserToWorldStatusSuccess: - per->base_reply.error_str_id = 101; + per->base_reply.error_str_id = LS::ErrStr::NO_ERROR; break; case UserToWorldStatusWorldUnavail: - per->base_reply.error_str_id = 326; + per->base_reply.error_str_id = LS::ErrStr::SERVER_UNAVAILABLE; break; case UserToWorldStatusSuspended: - per->base_reply.error_str_id = 337; + per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_SUSPENDED; break; case UserToWorldStatusBanned: - per->base_reply.error_str_id = 338; + per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_BANNED; break; case UserToWorldStatusWorldAtCapacity: - per->base_reply.error_str_id = 339; + per->base_reply.error_str_id = LS::ErrStr::WORLD_MAX_CAPACITY; break; case UserToWorldStatusAlreadyOnline: - per->base_reply.error_str_id = 111; + per->base_reply.error_str_id = LS::ErrStr::ERROR_1018_ACTIVE_CHARACTER; break; default: - per->base_reply.error_str_id = 102; + per->base_reply.error_str_id = LS::ErrStr::UNKNOWN_ERROR; } if (server.options.IsWorldTraceOn()) { @@ -275,7 +275,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne else { LogError( "Received User-To-World Response for [{0}] but could not find the client referenced!", - user_to_world_response->lsaccountid + r->lsaccountid ); } } @@ -335,7 +335,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer; per->base_header.sequence = client->GetPlaySequence(); - per->server_number = client->GetPlayServerID(); + per->server_number = client->GetPlayServerID(); LogDebug( "Found sequence and play of [{0}] [{1}]", @@ -358,25 +358,25 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac switch (user_to_world_response->response) { case UserToWorldStatusSuccess: - per->base_reply.error_str_id = 101; + per->base_reply.error_str_id = LS::ErrStr::NO_ERROR; break; case UserToWorldStatusWorldUnavail: - per->base_reply.error_str_id = 326; + per->base_reply.error_str_id = LS::ErrStr::SERVER_UNAVAILABLE; break; case UserToWorldStatusSuspended: - per->base_reply.error_str_id = 337; + per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_SUSPENDED; break; case UserToWorldStatusBanned: - per->base_reply.error_str_id = 338; + per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_BANNED; break; case UserToWorldStatusWorldAtCapacity: - per->base_reply.error_str_id = 339; + per->base_reply.error_str_id = LS::ErrStr::WORLD_MAX_CAPACITY; break; case UserToWorldStatusAlreadyOnline: - per->base_reply.error_str_id = 111; + per->base_reply.error_str_id = LS::ErrStr::ERROR_1018_ACTIVE_CHARACTER; break; default: - per->base_reply.error_str_id = 102; + per->base_reply.error_str_id = LS::ErrStr::UNKNOWN_ERROR; } if (server.options.IsTraceOn()) { @@ -571,6 +571,12 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info GetServerLongName(), GetRemoteIp() ); + + WorldServer::FormatWorldServerName( + new_world_server_info_packet->server_long_name, + world_registration.server_list_type + ); + SetLongName(new_world_server_info_packet->server_long_name); } /** @@ -1025,21 +1031,21 @@ bool WorldServer::ValidateWorldServerAdminLogin( return false; } -void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_local_ip) const +void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip) const { // see LoginClientServerData_Struct if (use_local_ip) { out.WriteString(GetLocalIP()); - } else { + } + else { out.WriteString(GetRemoteIP()); } - switch (GetServerListID()) - { - case 1: + switch (GetServerListID()) { + case LS::ServerType::Legends: out.WriteInt32(LS::ServerTypeFlags::Legends); break; - case 2: + case LS::ServerType::Preferred: out.WriteInt32(LS::ServerTypeFlags::Preferred); break; default: @@ -1348,3 +1354,37 @@ void WorldServer::OnKeepAlive(EQ::Timer *t) ServerPacket pack(ServerOP_KeepAlive, 0); m_connection->SendPacket(&pack); } + +void WorldServer::FormatWorldServerName(char *name, int8 server_list_type) +{ + std::string server_long_name = name; + server_long_name = trim(server_long_name); + + bool name_set_to_bottom = false; + if (server_list_type == LS::ServerType::Standard) { + if (server.options.IsWorldDevTestServersListBottom()) { + std::string s = str_tolower(server_long_name); + if (s.find("dev") != std::string::npos) { + server_long_name = fmt::format("|D| {}", server_long_name); + name_set_to_bottom = true; + } + else if (s.find("test") != std::string::npos) { + server_long_name = fmt::format("|T| {}", server_long_name); + name_set_to_bottom = true; + } + else if (s.find("installer") != std::string::npos) { + server_long_name = fmt::format("|I| {}", server_long_name); + name_set_to_bottom = true; + } + } + if (server.options.IsWorldSpecialCharacterStartListBottom() && !name_set_to_bottom) { + auto first_char = server_long_name.c_str()[0]; + if (IsAllowedWorldServerCharacterList(first_char) && first_char != '|') { + server_long_name = fmt::format("|*| {}", server_long_name); + name_set_to_bottom = true; + } + } + } + + strn0cpy(name, server_long_name.c_str(), 201); +} diff --git a/loginserver/world_server.h b/loginserver/world_server.h index af63d22a9..c4ea5f87d 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -194,6 +194,7 @@ private: void OnKeepAlive(EQ::Timer *t); std::unique_ptr m_keepalive; + static void FormatWorldServerName(char *name, int8 server_list_type); }; #endif diff --git a/world/login_server.cpp b/world/login_server.cpp index 0dc665f36..796438f35 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -603,8 +603,6 @@ void LoginServer::SendInfo() WorldConfig::SetLocalAddress(l->local_ip_address); } - SanitizeWorldServerName(l->server_long_name); - LogInfo( "[LoginServer::SendInfo] protocol_version [{}] server_version [{}] long_name [{}] short_name [{}] account_name [{}] remote_ip_address [{}] local_ip [{}]", l->protocol_version,