diff options
Diffstat (limited to 'src/char/char.cpp')
-rw-r--r-- | src/char/char.cpp | 2140 |
1 files changed, 1179 insertions, 961 deletions
diff --git a/src/char/char.cpp b/src/char/char.cpp index f8e12c0..d5e887b 100644 --- a/src/char/char.cpp +++ b/src/char/char.cpp @@ -22,8 +22,6 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. -#include <arpa/inet.h> -#include <sys/socket.h> #include <sys/wait.h> #include <netdb.h> @@ -33,32 +31,47 @@ #include <cassert> #include <cstdlib> -#include <cstring> -#include <ctime> +#include <algorithm> +#include <array> #include <bitset> +#include <chrono> #include <set> -#include "../compat/alg.hpp" +#include "../ints/cmp.hpp" +#include "../ints/udl.hpp" #include "../strings/mstring.hpp" #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" -#include "../generic/db.hpp" +#include "../generic/array.hpp" #include "../io/cxxstdio.hpp" +#include "../io/cxxstdio_enums.hpp" #include "../io/lock.hpp" #include "../io/read.hpp" #include "../io/tty.hpp" +#include "../io/write.hpp" + +#include "../net/packets.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" + +#include "../proto2/any-user.hpp" +#include "../proto2/login-admin.hpp" +#include "../proto2/login-char.hpp" +#include "../proto2/char-map.hpp" +#include "../proto2/char-user.hpp" #include "../mmo/config_parse.hpp" #include "../mmo/core.hpp" #include "../mmo/extract.hpp" +#include "../mmo/extract_enums.hpp" #include "../mmo/human_time_diff.hpp" -#include "../mmo/socket.hpp" -#include "../mmo/timer.hpp" +#include "../mmo/mmo.hpp" +#include "../mmo/utils.hpp" #include "../mmo/version.hpp" #include "inter.hpp" @@ -67,6 +80,9 @@ #include "../poison.hpp" + +namespace tmwa +{ static Array<struct mmo_map_server, MAX_MAP_SERVERS> server; static @@ -76,11 +92,11 @@ Array<int, MAX_MAP_SERVERS> server_freezeflag; // Map-server anti-freeze syst static int anti_freeze_enable = 0; static -std::chrono::seconds anti_freeze_interval = std::chrono::seconds(6); +std::chrono::seconds anti_freeze_interval = 6_s; constexpr std::chrono::milliseconds DEFAULT_AUTOSAVE_INTERVAL = - std::chrono::minutes(5); + 5_min; static Session *login_session, *char_session; @@ -91,7 +107,7 @@ AccountPass passwd; static ServerName server_name; static -CharName wisp_server_name = stringish<CharName>("Server"); +CharName wisp_server_name = stringish<CharName>("Server"_s); static IP4Address login_ip; static @@ -103,9 +119,9 @@ int char_port = 6121; static AString char_txt; static -CharName unknown_char_name = stringish<CharName>("Unknown"); +CharName unknown_char_name = stringish<CharName>("Unknown"_s); static -AString char_log_filename = "log/char.log"; +AString char_log_filename = "log/char.log"_s; //Added for lan support static IP4Address lan_map_ip = IP4_LOCALHOST; @@ -115,10 +131,14 @@ static int char_name_option = 0; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor] static std::bitset<256> char_name_letters; // list of letters/symbols authorised (or not) in a character name. by [Yor] +static constexpr +GmLevel default_gm_level = GmLevel::from(0_u32); + struct char_session_data : SessionData { - int account_id, login_id1, login_id2; + AccountId account_id; + int login_id1, login_id2; SEX sex; unsigned short packet_tmw_version; AccountEmail email; @@ -132,8 +152,8 @@ void SessionDeleter::operator()(SessionData *sd) struct AuthFifoEntry { - int account_id; - int char_id; + AccountId account_id; + CharId char_id; int login_id1, login_id2; IP4Address ip; int delflag; @@ -150,7 +170,7 @@ static int check_ip_flag = 1; // It's to check IP of a player between char-server and other servers (part of anti-hacking system) static -int char_id_count = 150000; +CharId char_id_count = wrap<CharId>(150000); static std::vector<CharPair> char_keys; static @@ -160,22 +180,22 @@ std::chrono::milliseconds autosave_time = DEFAULT_AUTOSAVE_INTERVAL; // Initial position (it's possible to set it in conf file) static -struct point start_point = { {"001-1.gat"}, 273, 354 }; +Point start_point = { {"001-1.gat"_s}, 273, 354 }; static std::vector<GM_Account> gm_accounts; // online players by [Yor] static -AString online_txt_filename = "online.txt"; +AString online_txt_filename = "online.txt"_s; static -AString online_html_filename = "online.html"; +AString online_html_filename = "online.html"_s; static int online_sorting_option = 0; // sorting option to display online players in online files static int online_refresh_html = 20; // refresh time (in sec) of the html file in the explorer static -int online_gm_display_min_level = 20; // minimum GM level to display 'GM' when we want to display it +GmLevel online_gm_display_min_level = GmLevel::from(20_u32); // minimum GM level to display 'GM' when we want to display it static std::vector<Session *> online_chars; // same size of char_keys, and id value of current server (or -1) @@ -185,6 +205,14 @@ TimeT update_online; // to update online files when we receiving infor static pid_t pid = 0; // For forked DB writes + +auto iter_map_sessions() -> decltype(filter_iterator<Session *>(std::declval<Array<Session *, MAX_MAP_SERVERS> *>())) +{ + return filter_iterator<Session *>(&server_session); +} + + + static void create_online_files(void); @@ -192,7 +220,7 @@ static void delete_tologin(Session *sess) { assert (sess == login_session); - PRINTF("Char-server can't connect to login-server (connection #%d).\n", + PRINTF("Char-server can't connect to login-server (connection #%d).\n"_fmt, sess); login_session = nullptr; } @@ -207,7 +235,7 @@ void delete_frommap(Session *sess) auto it = std::find(server_session.begin(), server_session.end(), sess); assert (it != server_session.end()); int id = it - server_session.begin(); - PRINTF("Map-server %d (session #%d) has disconnected.\n", id, + PRINTF("Map-server %d (session #%d) has disconnected.\n"_fmt, id, sess); server[id] = mmo_map_server{}; server_session[id] = nullptr; @@ -233,12 +261,12 @@ void char_log(XString line) // and returns its level (or 0 if it isn't a GM account or if not found) //---------------------------------------------------------------------- static -int isGM(int account_id) +GmLevel isGM(AccountId account_id) { for (GM_Account& gma : gm_accounts) if (gma.account_id == account_id) return gma.level; - return 0; + return default_gm_level; } //---------------------------------------------- @@ -264,7 +292,7 @@ const CharPair *search_character(CharName character_name) return nullptr; } -const CharPair *search_character_id(int char_id) +const CharPair *search_character_id(CharId char_id) { for (const CharPair& cd : char_keys) { @@ -317,7 +345,7 @@ AString mmo_char_tostr(struct CharPair *cp) "%d,%d,%d\t" "%d,%d,%d,%d,%d\t" "%s,%d,%d\t" - "%s,%d,%d,%d\t", + "%s,%d,%d,%d\t"_fmt, k->char_id, k->account_id, k->char_num, k->name, @@ -336,11 +364,12 @@ AString mmo_char_tostr(struct CharPair *cp) // memos were here (no longer supported) str_p += '\t'; - for (int i = 0; i < MAX_INVENTORY; i++) + for (IOff0 i : IOff0::iter()) + { if (p->inventory[i].nameid) { - str_p += STRPRINTF("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", - p->inventory[i].id, + str_p += STRPRINTF("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d "_fmt, + 0 /*id*/, p->inventory[i].nameid, p->inventory[i].amount, p->inventory[i].equip, @@ -353,33 +382,40 @@ AString mmo_char_tostr(struct CharPair *cp) 0 /*card[3]*/, 0 /*broken*/); } + } str_p += '\t'; // cart was here (no longer supported) str_p += '\t'; for (SkillID i : erange(SkillID(), MAX_SKILL)) + { if (p->skill[i].lv) { - str_p += STRPRINTF("%d,%d ", + str_p += STRPRINTF("%d,%d "_fmt, i, - p->skill[i].lv | (uint16_t(p->skill[i].flags) << 16)); + p->skill[i].lv | (static_cast<uint16_t>(p->skill[i].flags) << 16)); } + } str_p += '\t'; assert (p->global_reg_num < GLOBAL_REG_NUM); for (int i = 0; i < p->global_reg_num; i++) + { if (p->global_reg[i].str) - str_p += STRPRINTF("%s,%d ", + { + str_p += STRPRINTF("%s,%d "_fmt, p->global_reg[i].str, p->global_reg[i].value); + } + } str_p += '\t'; return AString(str_p); } static -bool extract(XString str, struct point *p) +bool extract(XString str, Point *p) { return extract(str, record<','>(&p->map_, &p->x, &p->y)); } @@ -414,10 +450,11 @@ bool extract(XString str, CharPair *cp) uint32_t unused_guild_id, unused_pet_id; XString unused_memos; - std::vector<struct item> inventory; + std::vector<Item> inventory; XString unused_cart; std::vector<struct skill_loader> skills; - std::vector<struct global_reg> vars; + std::vector<GlobalReg> vars; + XString hair_style; if (!extract(str, record<'\t'>( &k->char_id, @@ -430,7 +467,7 @@ bool extract(XString str, CharPair *cp) record<','>(&p->status_point, &p->skill_point), record<','>(&p->option, &p->karma, &p->manner), record<','>(&p->party_id, &unused_guild_id, &unused_pet_id), - record<','>(&p->hair, &p->hair_color, &p->clothes_color), + record<','>(&hair_style, &p->hair_color, &p->clothes_color), record<','>(&p->weapon, &p->shield, &p->head_top, &p->head_mid, &p->head_bottom), &p->last_point, // somebody was silly and stuck partner id as a field @@ -444,11 +481,19 @@ bool extract(XString str, CharPair *cp) vrec<' '>(&vars)))) return false; + // leftover corruption from Platinum + if (hair_style == "-1"_s) + { + p->hair = 0; + } + else if (!extract(hair_style, &p->hair)) + return false; + if (wisp_server_name == k->name) return false; // TODO replace *every* lookup with a map lookup - static std::set<int> seen_ids; + static std::set<CharId> seen_ids; static std::set<CharName> seen_names; // we don't have to worry about deleted characters, // this is only called during startup @@ -499,10 +544,10 @@ int mmo_char_init(void) io::ReadFile in(char_txt); if (!in.is_open()) { - PRINTF("Characters file not found: %s.\n", char_txt); - CHAR_LOG("Characters file not found: %s.\n", char_txt); - CHAR_LOG("Id for the next created character: %d.\n", - char_id_count); + PRINTF("Characters file not found: %s.\n"_fmt, char_txt); + CHAR_LOG("Characters file not found: %s.\n"_fmt, char_txt); + CHAR_LOG("Id for the next created character: %d.\n"_fmt, + char_id_count); return 0; } @@ -516,8 +561,8 @@ int mmo_char_init(void) continue; { - int i, j = 0; - if (SSCANF(line, "%d\t%%newid%%%n", &i, &j) == 1 && j > 0) + CharId i; + if (extract(line, record<'\t'>(&i, "%newid%"_s))) { if (char_id_count < i) char_id_count = i; @@ -528,21 +573,21 @@ int mmo_char_init(void) CharPair cd; if (!extract(line, &cd)) { - CHAR_LOG("Char skipped\n%s", line); + CHAR_LOG("Char skipped\n%s"_fmt, line); continue; } - if (cd.key.char_id >= char_id_count) - char_id_count = cd.key.char_id + 1; + if (char_id_count < next(cd.key.char_id)) + char_id_count = next(cd.key.char_id); char_keys.push_back(std::move(cd)); online_chars.push_back(nullptr); } - PRINTF("mmo_char_init: %zu characters read in %s.\n", + PRINTF("mmo_char_init: %zu characters read in %s.\n"_fmt, char_keys.size(), char_txt); - CHAR_LOG("mmo_char_init: %zu characters read in %s.\n", + CHAR_LOG("mmo_char_init: %zu characters read in %s.\n"_fmt, char_keys.size(), char_txt); - CHAR_LOG("Id for the next created character: %d.\n", + CHAR_LOG("Id for the next created character: %d.\n"_fmt, char_id_count); return 0; @@ -557,8 +602,8 @@ void mmo_char_sync(void) io::WriteLock fp(char_txt); if (!fp.is_open()) { - PRINTF("WARNING: Server can't not save characters.\n"); - CHAR_LOG("WARNING: Server can't not save characters.\n"); + PRINTF("WARNING: Server can't not save characters.\n"_fmt); + CHAR_LOG("WARNING: Server can't not save characters.\n"_fmt); return; } { @@ -568,7 +613,7 @@ void mmo_char_sync(void) AString line = mmo_char_tostr(&cd); fp.put_line(line); } - FPRINTF(fp, "%d\t%%newid%%\n", char_id_count); + FPRINTF(fp, "%d\t%%newid%%\n"_fmt, char_id_count); } } @@ -612,7 +657,7 @@ void mmo_char_sync_timer(TimerData *, tick_t) // Function to create a new character //----------------------------------- static -CharPair *make_new_char(Session *s, CharName name, const uint8_t (&stats)[6], uint8_t slot, uint16_t hair_color, uint16_t hair_style) +CharPair *make_new_char(Session *s, CharName name, const Stats6& stats, uint8_t slot, uint16_t hair_color, uint16_t hair_style) { // ugh char_session_data *sd = static_cast<char_session_data *>(s->session_data.get()); @@ -620,24 +665,24 @@ CharPair *make_new_char(Session *s, CharName name, const uint8_t (&stats)[6], ui // remove control characters from the name if (!name.to__actual().is_print()) { - CHAR_LOG("Make new char error (control char received in the name): (connection #%d, account: %d).\n", - s, sd->account_id); + CHAR_LOG("Make new char error (control char received in the name): (connection #%d, account: %d).\n"_fmt, + s, sd->account_id); return nullptr; } // Eliminate whitespace if (name.to__actual() != name.to__actual().strip()) { - CHAR_LOG("Make new char error (leading/trailing whitespace): (connection #%d, account: %d, name: '%s'.\n", - s, sd->account_id, name); + CHAR_LOG("Make new char error (leading/trailing whitespace): (connection #%d, account: %d, name: '%s'.\n"_fmt, + s, sd->account_id, name); return nullptr; } // check lenght of character name if (name.to__actual().size() < 4) { - CHAR_LOG("Make new char error (character name too small): (connection #%d, account: %d, name: '%s').\n", - s, sd->account_id, name); + CHAR_LOG("Make new char error (character name too small): (connection #%d, account: %d, name: '%s').\n"_fmt, + s, sd->account_id, name); return nullptr; } @@ -648,7 +693,7 @@ CharPair *make_new_char(Session *s, CharName name, const uint8_t (&stats)[6], ui for (uint8_t c : name.to__actual()) if (!char_name_letters[c]) { - CHAR_LOG("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n", + CHAR_LOG("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n"_fmt, s, sd->account_id, name, c); return nullptr; } @@ -659,36 +704,38 @@ CharPair *make_new_char(Session *s, CharName name, const uint8_t (&stats)[6], ui for (uint8_t c : name.to__actual()) if (char_name_letters[c]) { - CHAR_LOG("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n", + CHAR_LOG("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n"_fmt, s, sd->account_id, name, c); return nullptr; } } // else, all letters/symbols are authorised (except control char removed before) + // TODO this comment is obsolete // this is why it needs to be unsigned - if (stats[0] + stats[1] + stats[2] + stats[3] + stats[4] + stats[5] != 5 * 6 || // stats + if (stats.str + stats.agi + stats.vit + stats.int_ + stats.dex + stats.luk != 5 * 6 || // stats slot >= 9 || hair_style >= 20 || hair_color >= 12) { - CHAR_LOG("Make new char error (invalid values): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d\n", - s, sd->account_id, slot, name, - stats[0], stats[1], stats[2], stats[3], stats[4], stats[5], - stats[0] + stats[1] + stats[2] + stats[3] + stats[4] + stats[5], - hair_style, hair_color); + CHAR_LOG("Make new char error (invalid values): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d\n"_fmt, + s, sd->account_id, slot, name, + stats.str, stats.agi, stats.vit, stats.int_, stats.dex, stats.luk, + stats.str + stats.agi + stats.vit + stats.int_ + stats.dex + stats.luk, + hair_style, hair_color); return nullptr; } // check individual stat value for (int i = 0; i < 6; i++) { - if (stats[i] < 1 || stats[i] > 9) + uint8_t statsi = reinterpret_cast<const uint8_t *>(&stats)[i]; + if (statsi < 1 || statsi > 9) { - CHAR_LOG("Make new char error (invalid stat value: not between 1 to 9): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d\n", - s, sd->account_id, slot, name, - stats[0], stats[1], stats[2], stats[3], stats[4], stats[5], - stats[0] + stats[1] + stats[2] + stats[3] + stats[4] + stats[5], - hair_style, hair_color); + CHAR_LOG("Make new char error (invalid stat value: not between 1 to 9): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d\n"_fmt, + s, sd->account_id, slot, name, + stats.str, stats.agi, stats.vit, stats.int_, stats.dex, stats.luk, + stats.str + stats.agi + stats.vit + stats.int_ + stats.dex + stats.luk, + hair_style, hair_color); return nullptr; } } @@ -697,63 +744,63 @@ CharPair *make_new_char(Session *s, CharName name, const uint8_t (&stats)[6], ui { if (cd.key.name == name) { - CHAR_LOG("Make new char error (name already exists): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", - s, sd->account_id, slot, name, cd.key.name, - stats[0], stats[1], stats[2], stats[3], stats[4], stats[5], - stats[0] + stats[1] + stats[2] + stats[3] + stats[4] + stats[5], - hair_style, hair_color); + CHAR_LOG("Make new char error (name already exists): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n"_fmt, + s, sd->account_id, slot, name, cd.key.name, + stats.str, stats.agi, stats.vit, stats.int_, stats.dex, stats.luk, + stats.str + stats.agi + stats.vit + stats.int_ + stats.dex + stats.luk, + hair_style, hair_color); return nullptr; } if (cd.key.account_id == sd->account_id && cd.key.char_num == slot) { - CHAR_LOG("Make new char error (slot already used): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", - s, sd->account_id, slot, name, cd.key.name, - stats[0], stats[1], stats[2], stats[3], stats[4], stats[5], - stats[0] + stats[1] + stats[2] + stats[3] + stats[4] + stats[5], - hair_style, hair_color); + CHAR_LOG("Make new char error (slot already used): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n"_fmt, + s, sd->account_id, slot, name, cd.key.name, + stats.str, stats.agi, stats.vit, stats.int_, stats.dex, stats.luk, + stats.str + stats.agi + stats.vit + stats.int_ + stats.dex + stats.luk, + hair_style, hair_color); return nullptr; } } if (wisp_server_name == name) { - CHAR_LOG("Make new char error (name used is wisp name for server): (connection #%d, account: %d) slot %d, name: %s (actual name whisper server: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", - s, sd->account_id, slot, name, wisp_server_name, - stats[0], stats[1], stats[2], stats[3], stats[4], stats[5], - stats[0] + stats[1] + stats[2] + stats[3] + stats[4] + stats[5], - hair_style, hair_color); + CHAR_LOG("Make new char error (name used is wisp name for server): (connection #%d, account: %d) slot %d, name: %s (actual name whisper server: %s), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n"_fmt, + s, sd->account_id, slot, name, wisp_server_name, + stats.str, stats.agi, stats.vit, stats.int_, stats.dex, stats.luk, + stats.str + stats.agi + stats.vit + stats.int_ + stats.dex + stats.luk, + hair_style, hair_color); return nullptr; } IP4Address ip = s->client_ip; - CHAR_LOG("Creation of New Character: (connection #%d, account: %d) slot %d, character Name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d. [%s]\n", - s, sd->account_id, slot, name, - stats[0], stats[1], stats[2], stats[3], stats[4], stats[5], - stats[0] + stats[1] + stats[2] + stats[3] + stats[4] + stats[5], - hair_style, hair_color, ip); + CHAR_LOG("Creation of New Character: (connection #%d, account: %d) slot %d, character Name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d. [%s]\n"_fmt, + s, sd->account_id, slot, name, + stats.str, stats.agi, stats.vit, stats.int_, stats.dex, stats.luk, + stats.str + stats.agi + stats.vit + stats.int_ + stats.dex + stats.luk, + hair_style, hair_color, ip); CharPair cp; CharKey& ck = cp.key; CharData& cd = *cp.data; - ck.char_id = char_id_count++; + ck.char_id = char_id_count; char_id_count = next(char_id_count); ck.account_id = sd->account_id; ck.char_num = slot; ck.name = name; - cd.species = 0; + cd.species = Species(); cd.base_level = 1; cd.job_level = 1; cd.base_exp = 0; cd.job_exp = 0; cd.zeny = 0; - cd.attrs[ATTR::STR] = stats[0]; - cd.attrs[ATTR::AGI] = stats[1]; - cd.attrs[ATTR::VIT] = stats[2]; - cd.attrs[ATTR::INT] = stats[3]; - cd.attrs[ATTR::DEX] = stats[4]; - cd.attrs[ATTR::LUK] = stats[5]; + cd.attrs[ATTR::STR] = stats.str; + cd.attrs[ATTR::AGI] = stats.agi; + cd.attrs[ATTR::VIT] = stats.vit; + cd.attrs[ATTR::INT] = stats.int_; + cd.attrs[ATTR::DEX] = stats.dex; + cd.attrs[ATTR::LUK] = stats.luk; cd.max_hp = 40 * (100 + cd.attrs[ATTR::VIT]) / 100; cd.max_sp = 11 * (100 + cd.attrs[ATTR::INT]) / 100; cd.hp = cd.max_hp; @@ -763,17 +810,17 @@ CharPair *make_new_char(Session *s, CharName name, const uint8_t (&stats)[6], ui cd.option = static_cast<Option>(0x0000); // Option is only declared cd.karma = 0; cd.manner = 0; - cd.party_id = 0; + cd.party_id = PartyId(); //cd.guild_id = 0; cd.hair = hair_style; cd.hair_color = hair_color; cd.clothes_color = 0; // removed initial armor/weapon - unused and problematic cd.weapon = ItemLook::NONE; - cd.shield = 0; - cd.head_top = 0; - cd.head_mid = 0; - cd.head_bottom = 0; + cd.shield = ItemNameId(); + cd.head_top = ItemNameId(); + cd.head_mid = ItemNameId(); + cd.head_bottom = ItemNameId(); cd.last_point = start_point; cd.save_point = start_point; char_keys.push_back(std::move(cp)); @@ -799,17 +846,17 @@ void create_online_files(void) timestamp_seconds_buffer timetemp; stamp_time(timetemp); // write heading - FPRINTF(fp2, "<HTML>\n"); - FPRINTF(fp2, " <META http-equiv=\"Refresh\" content=\"%d\">\n", online_refresh_html); // update on client explorer every x seconds - FPRINTF(fp2, " <HEAD>\n"); - FPRINTF(fp2, " <TITLE>Online Players on %s</TITLE>\n", - server_name); - FPRINTF(fp2, " </HEAD>\n"); - FPRINTF(fp2, " <BODY>\n"); - FPRINTF(fp2, " <H3>Online Players on %s (%s):</H3>\n", - server_name, timetemp); - FPRINTF(fp, "Online Players on %s (%s):\n", server_name, timetemp); - FPRINTF(fp, "\n"); + FPRINTF(fp2, "<HTML>\n"_fmt); + FPRINTF(fp2, " <META http-equiv=\"Refresh\" content=\"%d\">\n"_fmt, online_refresh_html); // update on client explorer every x seconds + FPRINTF(fp2, " <HEAD>\n"_fmt); + FPRINTF(fp2, " <TITLE>Online Players on %s</TITLE>\n"_fmt, + server_name); + FPRINTF(fp2, " </HEAD>\n"_fmt); + FPRINTF(fp2, " <BODY>\n"_fmt); + FPRINTF(fp2, " <H3>Online Players on %s (%s):</H3>\n"_fmt, + server_name, timetemp); + FPRINTF(fp, "Online Players on %s (%s):\n"_fmt, server_name, timetemp); + FPRINTF(fp, "\n"_fmt); int players = 0; // This used to be conditional on having any players, @@ -817,20 +864,20 @@ void create_online_files(void) //if (players > 0) { int j = 0; // count the number of characters for the txt version and to set the separate line - FPRINTF(fp2, " <table border=\"1\" cellspacing=\"1\">\n"); - FPRINTF(fp2, " <tr>\n"); + FPRINTF(fp2, " <table border=\"1\" cellspacing=\"1\">\n"_fmt); + FPRINTF(fp2, " <tr>\n"_fmt); { - FPRINTF(fp2, " <th>Name</th>\n"); + FPRINTF(fp2, " <th>Name</th>\n"_fmt); { - FPRINTF(fp, "Name "); // 30 + FPRINTF(fp, "Name "_fmt); // 30 j += 30; } } - FPRINTF(fp2, " </tr>\n"); - FPRINTF(fp, "\n"); + FPRINTF(fp2, " </tr>\n"_fmt); + FPRINTF(fp, "\n"_fmt); for (int k = 0; k < j; k++) - FPRINTF(fp, "-"); - FPRINTF(fp, "\n"); + FPRINTF(fp, "-"_fmt); + FPRINTF(fp, "\n"_fmt); // display each player. for (CharPair& cd : char_keys) @@ -838,55 +885,55 @@ void create_online_files(void) if (!server_for(&cd)) continue; players++; - FPRINTF(fp2, " <tr>\n"); + FPRINTF(fp2, " <tr>\n"_fmt); // displaying the character name { // without/with 'GM' display - int gml = isGM(cd.key.account_id); + GmLevel gml = isGM(cd.key.account_id); { - if (gml >= online_gm_display_min_level) - FPRINTF(fp, "%-24s (GM) ", cd.key.name); + if (gml.satisfies(online_gm_display_min_level)) + FPRINTF(fp, "%-24s (GM) "_fmt, cd.key.name); else - FPRINTF(fp, "%-24s ", cd.key.name); + FPRINTF(fp, "%-24s "_fmt, cd.key.name); } // name of the character in the html (no < >, because that create problem in html code) - FPRINTF(fp2, " <td>"); - if (gml >= online_gm_display_min_level) - FPRINTF(fp2, "<b>"); + FPRINTF(fp2, " <td>"_fmt); + if (gml.satisfies(online_gm_display_min_level)) + FPRINTF(fp2, "<b>"_fmt); for (char c : cd.key.name.to__actual()) { switch (c) { case '&': - FPRINTF(fp2, "&"); + FPRINTF(fp2, "&"_fmt); break; case '<': - FPRINTF(fp2, "<"); + FPRINTF(fp2, "<"_fmt); break; case '>': - FPRINTF(fp2, ">"); + FPRINTF(fp2, ">"_fmt); break; default: - FPRINTF(fp2, "%c", c); + FPRINTF(fp2, "%c"_fmt, c); break; }; } - if (gml >= online_gm_display_min_level) - FPRINTF(fp2, "</b> (GM)"); - FPRINTF(fp2, "</td>\n"); + if (gml.satisfies(online_gm_display_min_level)) + FPRINTF(fp2, "</b> (GM)"_fmt); + FPRINTF(fp2, "</td>\n"_fmt); } - FPRINTF(fp, "\n"); - FPRINTF(fp2, " </tr>\n"); + FPRINTF(fp, "\n"_fmt); + FPRINTF(fp2, " </tr>\n"_fmt); } - FPRINTF(fp2, " </table>\n"); - FPRINTF(fp, "\n"); + FPRINTF(fp2, " </table>\n"_fmt); + FPRINTF(fp, "\n"_fmt); } // Displaying number of online players if (players == 0) { - FPRINTF(fp2, " <p>No user is online.</p>\n"); - FPRINTF(fp, "No user is online.\n"); + FPRINTF(fp2, " <p>No user is online.</p>\n"_fmt); + FPRINTF(fp, "No user is online.\n"_fmt); } else if (players == 1) { @@ -894,11 +941,11 @@ void create_online_files(void) } else { - FPRINTF(fp2, " <p>%d users are online.</p>\n", players); - FPRINTF(fp, "%d users are online.\n", players); + FPRINTF(fp2, " <p>%d users are online.</p>\n"_fmt, players); + FPRINTF(fp, "%d users are online.\n"_fmt, players); } - FPRINTF(fp2, " </BODY>\n"); - FPRINTF(fp2, "</HTML>\n"); + FPRINTF(fp2, " </BODY>\n"_fmt); + FPRINTF(fp2, "</HTML>\n"_fmt); } } @@ -925,14 +972,18 @@ int count_users(void) // [Fate] Find inventory item based on equipment mask, return view. ID must match view ID (!). //---------------------------------------- static -int find_equip_view(const CharPair *cp, EPOS equipmask) +ItemNameId find_equip_view(const CharPair *cp, EPOS equipmask) { CharData *p = cp->data.get(); - for (int i = 0; i < MAX_INVENTORY; i++) + for (IOff0 i : IOff0::iter()) + { if (p->inventory[i].nameid && p->inventory[i].amount && bool(p->inventory[i].equip & equipmask)) + { return p->inventory[i].nameid; - return 0; + } + } + return ItemNameId(); } //---------------------------------------- @@ -954,72 +1005,75 @@ int mmo_char_send006b(Session *s, struct char_session_data *sd) } } - const int offset = 24; - WFIFO_ZERO(s, 0, offset + found_num * 106); - WFIFOW(s, 0) = 0x6b; - WFIFOW(s, 2) = offset + found_num * 106; + Packet_Head<0x006b> head_6b; + std::vector<Packet_Repeat<0x006b>> repeat_6b; + + head_6b.unused = {}; for (int i = 0; i < found_num; i++) { const CharPair *cp = found_char[i]; - int j = offset + (i * 106); const CharKey *k = &cp->key; const CharData *p = cp->data.get(); - WFIFOL(s, j) = k->char_id; - WFIFOL(s, j + 4) = p->base_exp; - WFIFOL(s, j + 8) = p->zeny; - WFIFOL(s, j + 12) = p->job_exp; - WFIFOL(s, j + 16) = p->job_level; - - WFIFOW(s, j + 20) = find_equip_view(cp, EPOS::SHOES); - WFIFOW(s, j + 22) = find_equip_view(cp, EPOS::GLOVES); - WFIFOW(s, j + 24) = find_equip_view(cp, EPOS::CAPE); - WFIFOW(s, j + 26) = find_equip_view(cp, EPOS::MISC1); - WFIFOL(s, j + 28) = static_cast<uint16_t>(p->option); - - WFIFOL(s, j + 32) = p->karma; - WFIFOL(s, j + 36) = p->manner; - - WFIFOW(s, j + 40) = p->status_point; - WFIFOW(s, j + 42) = (p->hp > 0x7fff) ? 0x7fff : p->hp; - WFIFOW(s, j + 44) = (p->max_hp > 0x7fff) ? 0x7fff : p->max_hp; - WFIFOW(s, j + 46) = (p->sp > 0x7fff) ? 0x7fff : p->sp; - WFIFOW(s, j + 48) = (p->max_sp > 0x7fff) ? 0x7fff : p->max_sp; - WFIFOW(s, j + 50) = static_cast<uint16_t>(DEFAULT_WALK_SPEED.count()); // p->speed; - WFIFOW(s, j + 52) = p->species; - WFIFOW(s, j + 54) = p->hair; -// WFIFOW(s,j+56) = p->weapon; // dont send weapon since TMW does not support it - WFIFOW(s, j + 56) = 0; - WFIFOW(s, j + 58) = p->base_level; - WFIFOW(s, j + 60) = p->skill_point; - WFIFOW(s, j + 62) = p->head_bottom; - WFIFOW(s, j + 64) = p->shield; - WFIFOW(s, j + 66) = p->head_top; - WFIFOW(s, j + 68) = p->head_mid; - WFIFOW(s, j + 70) = p->hair_color; - WFIFOW(s, j + 72) = find_equip_view(cp, EPOS::MISC2); -// WFIFOW(s,j+72) = p->clothes_color; - - WFIFO_STRING(s, j + 74, k->name.to__actual(), 24); - - WFIFOB(s, j + 98) = min(p->attrs[ATTR::STR], 255); - WFIFOB(s, j + 99) = min(p->attrs[ATTR::AGI], 255); - WFIFOB(s, j + 100) = min(p->attrs[ATTR::VIT], 255); - WFIFOB(s, j + 101) = min(p->attrs[ATTR::INT], 255); - WFIFOB(s, j + 102) = min(p->attrs[ATTR::DEX], 255); - WFIFOB(s, j + 103) = min(p->attrs[ATTR::LUK], 255); - WFIFOB(s, j + 104) = k->char_num; + Packet_Repeat<0x006b> info; + CharSelect& sel = info.char_select; + + sel.char_id = k->char_id; + sel.base_exp = p->base_exp; + sel.zeny = p->zeny; + sel.job_exp = p->job_exp; + sel.job_level = p->job_level; + + sel.shoes = find_equip_view(cp, EPOS::SHOES); + sel.gloves = find_equip_view(cp, EPOS::GLOVES); + sel.cape = find_equip_view(cp, EPOS::CAPE); + sel.misc1 = find_equip_view(cp, EPOS::MISC1); + sel.option = p->option; + + sel.karma = p->karma; + sel.manner = p->manner; + + sel.status_point = p->status_point; + sel.hp = std::min(p->hp, 0x7fff); + sel.max_hp = std::min(p->max_hp, 0x7fff); + sel.sp = std::min(p->sp, 0x7fff); + sel.max_sp = std::min(p->max_sp, 0x7fff); + sel.speed = static_cast<uint16_t>(DEFAULT_WALK_SPEED.count()); // p->speed; + sel.species = p->species; + sel.hair_style = p->hair; + sel.weapon = 0; // p->weapon; // dont send weapon since TMW does not support it + sel.base_level = p->base_level; + sel.skill_point = p->skill_point; + sel.head_bottom = p->head_bottom; + sel.shield = p->shield; + sel.head_top = p->head_top; + sel.head_mid = p->head_mid; + sel.hair_color = p->hair_color; + sel.misc2 = find_equip_view(cp, EPOS::MISC2); // = p->clothes_color; + + sel.char_name = k->name; + + sel.stats.str = saturate<uint8_t>(p->attrs[ATTR::STR]); + sel.stats.agi = saturate<uint8_t>(p->attrs[ATTR::AGI]); + sel.stats.vit = saturate<uint8_t>(p->attrs[ATTR::VIT]); + sel.stats.int_ = saturate<uint8_t>(p->attrs[ATTR::INT]); + sel.stats.dex = saturate<uint8_t>(p->attrs[ATTR::DEX]); + sel.stats.luk = saturate<uint8_t>(p->attrs[ATTR::LUK]); + sel.char_num = k->char_num; + sel.unused = 0; + + repeat_6b.push_back(info); } - WFIFOSET(s, WFIFOW(s, 2)); + send_vpacket<0x006b, 24, 106>(s, head_6b, repeat_6b); return 0; } static -int set_account_reg2(int acc, Slice<global_reg> reg) +int set_account_reg2(AccountId acc, Slice<GlobalReg> reg) { size_t num = reg.size(); assert (num < ACCOUNT_REG2_NUM); @@ -1032,7 +1086,7 @@ int set_account_reg2(int acc, Slice<global_reg> reg) cd.data->account_reg2[i] = reg[i]; cd.data->account_reg2_num = num; for (int i = num; i < ACCOUNT_REG2_NUM; ++i) - cd.data->account_reg2[i] = global_reg{}; + cd.data->account_reg2[i] = GlobalReg{}; c++; } } @@ -1043,52 +1097,65 @@ int set_account_reg2(int acc, Slice<global_reg> reg) static int char_divorce(CharPair *cp) { - uint8_t buf[10]; - - if (cp == NULL) + if (cp == nullptr) return 0; CharKey *ck = &cp->key; CharData *cs = cp->data.get(); - if (cs->partner_id <= 0) + if (!cs->partner_id) { - WBUFW(buf, 0) = 0x2b12; - WBUFL(buf, 2) = ck->char_id; - WBUFL(buf, 6) = 0; // partner id 0 means failure - mapif_sendall(buf, 10); + Packet_Fixed<0x2b12> fixed_12; + fixed_12.char_id = ck->char_id; + // partner id 0 means failure + fixed_12.partner_id = cs->partner_id; + for (Session *ss : iter_map_sessions()) + { + send_fpacket<0x2b12, 10>(ss, fixed_12); + } return 0; } - WBUFW(buf, 0) = 0x2b12; - WBUFL(buf, 2) = ck->char_id; + Packet_Fixed<0x2b12> fixed_12; + fixed_12.char_id = ck->char_id; for (CharPair& cd : char_keys) { if (cd.key.char_id == cs->partner_id && cd.data->partner_id == ck->char_id) { - WBUFL(buf, 6) = cs->partner_id; - mapif_sendall(buf, 10); - cs->partner_id = 0; - cd.data->partner_id = 0; + fixed_12.partner_id = cs->partner_id; + for (Session *ss : iter_map_sessions()) + { + send_fpacket<0x2b12, 10>(ss, fixed_12); + } + + cs->partner_id = CharId(); + cd.data->partner_id = CharId(); return 0; } // The other char doesn't have us as their partner, so just clear our partner // Don't worry about this, as the map server should verify itself that the other doesn't have us as a partner, and so won't mess with their marriage else if (cd.key.char_id == cs->partner_id) { - WBUFL(buf, 6) = cs->partner_id; - mapif_sendall(buf, 10); - cs->partner_id = 0; + fixed_12.partner_id = cs->partner_id; + for (Session *ss : iter_map_sessions()) + { + send_fpacket<0x2b12, 10>(ss, fixed_12); + } + + cs->partner_id = CharId(); return 0; } } // Our partner wasn't found, so just clear our marriage - WBUFL(buf, 6) = cs->partner_id; - cs->partner_id = 0; - mapif_sendall(buf, 10); + fixed_12.partner_id = cs->partner_id; + cs->partner_id = CharId(); + for (Session *ss : iter_map_sessions()) + { + send_fpacket<0x2b12, 10>(ss, fixed_12); + } return 0; } @@ -1097,25 +1164,24 @@ int char_divorce(CharPair *cp) // Force disconnection of an online player (with account value) by [Yor] //---------------------------------------------------------------------- static -int disconnect_player(int accound_id) +void disconnect_player(AccountId accound_id) { // disconnect player if online on char-server for (io::FD i : iter_fds()) { - if (!get_session(i)) + Session *s = get_session(i); + if (!s) continue; - struct char_session_data *sd = static_cast<char_session_data *>(get_session(i)->session_data.get()); + struct char_session_data *sd = static_cast<char_session_data *>(s->session_data.get()); if (sd) { if (sd->account_id == accound_id) { - get_session(i)->set_eof(); - return 1; + s->set_eof(); + return; } } } - - return 0; } // キャラ削除に伴うデータ削除 @@ -1134,10 +1200,12 @@ int char_delete(CharPair *cp) // Force the character (and all on the same account) to leave all map servers { - unsigned char buf[6]; - WBUFW(buf, 0) = 0x2afe; - WBUFL(buf, 2) = ck->account_id; - mapif_sendall(buf, 6); + Packet_Fixed<0x2afe> fixed_fe; + fixed_fe.account_id = ck->account_id; + for (Session *ss : iter_map_sessions()) + { + send_fpacket<0x2afe, 6>(ss, fixed_fe); + } } return 0; @@ -1146,143 +1214,156 @@ int char_delete(CharPair *cp) static void parse_tologin(Session *ls) { - // only login-server can have an access to here. - // so, if it isn't the login-server, we disconnect the session (fd != login_fd). - if (ls != login_session) - { - ls->set_eof(); - return; - } + assert (ls == login_session); char_session_data *sd = static_cast<char_session_data *>(ls->session_data.get()); - while (RFIFOREST(ls) >= 2) + RecvResult rv = RecvResult::Complete; + uint16_t packet_id; + while (rv == RecvResult::Complete && packet_peek_id(ls, &packet_id)) { -// PRINTF("parse_tologin: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); - - switch (RFIFOW(ls, 0)) + switch (packet_id) { case 0x2711: - if (RFIFOREST(ls) < 3) - return; - if (RFIFOB(ls, 2)) + { + Packet_Fixed<0x2711> fixed; + rv = recv_fpacket<0x2711, 3>(ls, fixed); + if (rv != RecvResult::Complete) + break; + + if (fixed.code) { -// PRINTF("connect login server error : %d\n", RFIFOB(fd,2)); - PRINTF("Can not connect to login-server.\n"); - PRINTF("The server communication passwords (default s1/p1) is probably invalid.\n"); - PRINTF("Also, please make sure your accounts file (default: accounts.txt) has those values present.\n"); - PRINTF("If you changed the communication passwords, change them back at map_athena.conf and char_athena.conf\n"); + PRINTF("Can not connect to login-server.\n"_fmt); + PRINTF("The server communication passwords (default s1/p1) is probably invalid.\n"_fmt); + PRINTF("Also, please make sure your accounts file (default: accounts.txt) has those values present.\n"_fmt); + PRINTF("If you changed the communication passwords, change them back at map_athena.conf and char_athena.conf\n"_fmt); exit(1); } else { - PRINTF("Connected to login-server (connection #%d).\n", + PRINTF("Connected to login-server (connection #%d).\n"_fmt, ls); // if no map-server already connected, display a message... int i; for (i = 0; i < MAX_MAP_SERVERS; i++) + { if (server_session[i] && server[i].maps[0]) // if map-server online and at least 1 map break; + } if (i == MAX_MAP_SERVERS) - PRINTF("Awaiting maps from map-server.\n"); + PRINTF("Awaiting maps from map-server.\n"_fmt); } - RFIFOSKIP(ls, 3); break; + } case 0x2713: - if (RFIFOREST(ls) < 51) - return; -// PRINTF("parse_tologin 2713 : %d\n", RFIFOB(fd,6)); + { + Packet_Fixed<0x2713> fixed; + rv = recv_fpacket<0x2713, 51>(ls, fixed); + if (rv != RecvResult::Complete) + break; + for (io::FD i : iter_fds()) { + AccountId acc = fixed.account_id; Session *s2 = get_session(i); if (!s2) continue; sd = static_cast<char_session_data *>(s2->session_data.get()); - if (sd && sd->account_id == RFIFOL(ls, 2)) + if (sd && sd->account_id == acc) { - if (RFIFOB(ls, 6) != 0) + if (fixed.invalid != 0) { - WFIFOW(s2, 0) = 0x6c; - WFIFOB(s2, 2) = 0x42; - WFIFOSET(s2, 3); + Packet_Fixed<0x006c> fixed_6c; + fixed_6c.code = 0x42; + send_fpacket<0x006c, 3>(s2, fixed_6c); } else if (max_connect_user == 0 || count_users() < max_connect_user) { -// if (max_connect_user == 0) -// PRINTF("max_connect_user (unlimited) -> accepted.\n"); -// else -// PRINTF("count_users(): %d < max_connect_user (%d) -> accepted.\n", count_users(), max_connect_user); - sd->email = stringish<AccountEmail>(RFIFO_STRING<40>(ls, 7)); + sd->email = stringish<AccountEmail>(fixed.email); if (!e_mail_check(sd->email)) sd->email = DEFAULT_EMAIL; - sd->connect_until_time = static_cast<time_t>(RFIFOL(ls, 47)); + sd->connect_until_time = fixed.connect_until; // send characters to player mmo_char_send006b(s2, sd); } else { // refuse connection: too much online players -// PRINTF("count_users(): %d < max_connect_use (%d) -> fail...\n", count_users(), max_connect_user); - WFIFOW(s2, 0) = 0x6c; - WFIFOB(s2, 2) = 0; - WFIFOSET(s2, 3); + Packet_Fixed<0x006c> fixed_6c; + fixed_6c.code = 0; + send_fpacket<0x006c, 3>(s2, fixed_6c); } break; } } - RFIFOSKIP(ls, 51); break; + } // Receiving of an e-mail/time limit from the login-server (answer of a request because a player comes back from map-server to char-server) by [Yor] case 0x2717: - if (RFIFOREST(ls) < 50) - return; + { + Packet_Fixed<0x2717> fixed; + rv = recv_fpacket<0x2717, 50>(ls, fixed); + if (rv != RecvResult::Complete) + break; + for (io::FD i : iter_fds()) { + AccountId acc = fixed.account_id; Session *s2 = get_session(i); if (!s2) continue; sd = static_cast<char_session_data *>(s2->session_data.get()); if (sd) { - if (sd->account_id == RFIFOL(ls, 2)) + if (sd->account_id == acc) { - sd->email = stringish<AccountEmail>(RFIFO_STRING<40>(ls, 6)); + sd->email = fixed.email; if (!e_mail_check(sd->email)) sd->email = DEFAULT_EMAIL; - sd->connect_until_time = static_cast<time_t>(RFIFOL(ls, 46)); + sd->connect_until_time = fixed.connect_until; break; } } } - RFIFOSKIP(ls, 50); break; + } case 0x2721: // gm reply - if (RFIFOREST(ls) < 10) - return; + { + Packet_Fixed<0x2721> fixed; + rv = recv_fpacket<0x2721, 10>(ls, fixed); + if (rv != RecvResult::Complete) + break; + { - unsigned char buf[10]; - WBUFW(buf, 0) = 0x2b0b; - WBUFL(buf, 2) = RFIFOL(ls, 2); // account - WBUFL(buf, 6) = RFIFOL(ls, 6); // GM level - mapif_sendall(buf, 10); -// PRINTF("parse_tologin: To become GM answer: char -> map.\n"); + AccountId acc = fixed.account_id; + GmLevel gml = fixed.gm_level; + + Packet_Fixed<0x2b0b> fixed_2b; + fixed_2b.account_id = acc; + fixed_2b.gm_level = gml; + for (Session *ss : iter_map_sessions()) + { + send_fpacket<0x2b0b, 10>(ss, fixed_2b); + } } - RFIFOSKIP(ls, 10); break; + } case 0x2723: // changesex reply (modified by [Yor]) - if (RFIFOREST(ls) < 7) - return; + { + Packet_Fixed<0x2723> fixed; + rv = recv_fpacket<0x2723, 7>(ls, fixed); + if (rv != RecvResult::Complete) + break; + { - unsigned char buf[7]; - int acc = RFIFOL(ls, 2); - SEX sex = static_cast<SEX>(RFIFOB(ls, 6)); - RFIFOSKIP(ls, 7); - if (acc > 0) + AccountId acc = fixed.account_id; + SEX sex = fixed.sex; + if (acc) { for (CharPair& cp : char_keys) { @@ -1293,35 +1374,43 @@ void parse_tologin(Session *ls) cd.sex = sex; // auth_fifo[i].sex = sex; // to avoid any problem with equipment and invalid sex, equipment is unequiped. - for (int j = 0; j < MAX_INVENTORY; j++) + for (IOff0 j : IOff0::iter()) { if (cd.inventory[j].nameid && bool(cd.inventory[j].equip)) cd.inventory[j].equip = EPOS::ZERO; } cd.weapon = ItemLook::NONE; - cd.shield = 0; - cd.head_top = 0; - cd.head_mid = 0; - cd.head_bottom = 0; + cd.shield = ItemNameId(); + cd.head_top = ItemNameId(); + cd.head_mid = ItemNameId(); + cd.head_bottom = ItemNameId(); } } // disconnect player if online on char-server disconnect_player(acc); } - WBUFW(buf, 0) = 0x2b0d; - WBUFL(buf, 2) = acc; - WBUFB(buf, 6) = static_cast<uint8_t>(sex); - mapif_sendall(buf, 7); + Packet_Fixed<0x2b0d> fixed_0d; + fixed_0d.account_id = acc; + fixed_0d.sex = sex; + for (Session *ss : iter_map_sessions()) + { + send_fpacket<0x2b0d, 7>(ss, fixed_0d); + } } break; + } case 0x2726: // Request to send a broadcast message (no answer) - if (RFIFOREST(ls) < 8 - || RFIFOREST(ls) < (8 + RFIFOL(ls, 4))) - return; - if (RFIFOL(ls, 4) < 1) - CHAR_LOG("Receiving a message for broadcast, but message is void.\n"); + { + Packet_Head<0x2726> head; + AString repeat; + rv = recv_vpacket<0x2726, 8, 1>(ls, head, repeat); + if (rv != RecvResult::Complete) + break; + + if (!repeat) + CHAR_LOG("Receiving a message for broadcast, but message is void.\n"_fmt); else { int i; @@ -1330,82 +1419,100 @@ void parse_tologin(Session *ls) if (server_session[i]) break; if (i == MAX_MAP_SERVERS) - CHAR_LOG("'ladmin': Receiving a message for broadcast, but no map-server is online.\n"); + CHAR_LOG("'ladmin': Receiving a message for broadcast, but no map-server is online.\n"_fmt); else { - size_t len = RFIFOL(ls, 4); - AString message = RFIFO_STRING(ls, 8, len).to_print().lstrip(); + AString message = repeat.to_print().lstrip(); // if message is only composed of spaces if (!message) - CHAR_LOG("Receiving a message for broadcast, but message is only a lot of spaces.\n"); + CHAR_LOG("Receiving a message for broadcast, but message is only a lot of spaces.\n"_fmt); // else send message to all map-servers else { - CHAR_LOG("'ladmin': Receiving a message for broadcast (message (in yellow): %s)\n", + CHAR_LOG("'ladmin': Receiving a message for broadcast (message (in yellow): %s)\n"_fmt, message); // send broadcast to all map-servers - uint8_t buf[4 + len]; - WBUFW(buf, 0) = 0x3800; - WBUFW(buf, 2) = 4 + len; - WBUF_STRING(buf, 4, message, len); - mapif_sendall(buf, WBUFW(buf, 2)); + for (Session *ss : iter_map_sessions()) + { + send_packet_repeatonly<0x3800, 4, 1>(ss, message); + } } } } - RFIFOSKIP(ls, 8 + RFIFOL(ls, 4)); break; + } // account_reg2変更通知 case 0x2729: - if (RFIFOREST(ls) < 4 || RFIFOREST(ls) < RFIFOW(ls, 2)) - return; + { + Packet_Head<0x2729> head; + std::vector<Packet_Repeat<0x2729>> repeat; + rv = recv_vpacket<0x2729, 8, 36>(ls, head, repeat); + if (rv != RecvResult::Complete) + break; + { - Array<struct global_reg, ACCOUNT_REG2_NUM> reg; - int j, p, acc; - acc = RFIFOL(ls, 4); - for (p = 8, j = 0; - p < RFIFOW(ls, 2) && j < ACCOUNT_REG2_NUM; - p += 36, j++) + Array<GlobalReg, ACCOUNT_REG2_NUM> reg; + int j = 0; + AccountId acc = head.account_id; + for (const auto& info : repeat) + { + reg[j].str = info.name; + reg[j].value = info.value; + ++j; + if (j == ACCOUNT_REG2_NUM) + break; + } + set_account_reg2(acc, Slice<GlobalReg>(reg.begin(), j)); + + Packet_Head<0x2b11> head_11; + head_11.account_id = head.account_id; + std::vector<Packet_Repeat<0x2b11>> repeat_11(repeat.size()); + for (size_t k = 0; k < repeat.size(); ++k) + { + repeat_11[k].name = repeat[k].name; + repeat_11[k].value = repeat[k].value; + } + for (Session *ss : iter_map_sessions()) { - reg[j].str = stringish<VarName>(RFIFO_STRING<32>(ls, p)); - reg[j].value = RFIFOL(ls, p + 32); + send_vpacket<0x2b11, 8, 36>(ss, head_11, repeat_11); } - set_account_reg2(acc, Slice<struct global_reg>(reg.begin(), j)); - - size_t len = RFIFOW(ls, 2); - uint8_t buf[len]; - RFIFO_BUF_CLONE(ls, buf, len); - WBUFW(buf, 0) = 0x2b11; - mapif_sendall(buf, len); -// PRINTF("char: save_account_reg_reply\n"); } - RFIFOSKIP(ls, RFIFOW(ls, 2)); break; + } case 0x7924: { // [Fate] Itemfrob package: forwarded from login-server - if (RFIFOREST(ls) < 10) - return; - int source_id = RFIFOL(ls, 2); - int dest_id = RFIFOL(ls, 6); - unsigned char buf[10]; + Packet_Fixed<0x7924> fixed; + rv = recv_fpacket<0x7924, 10>(ls, fixed); + if (rv != RecvResult::Complete) + break; + + ItemNameId source_id = fixed.source_item_id; + ItemNameId dest_id = fixed.dest_item_id; + + Packet_Fixed<0x2afa> fixed_fa; + fixed_fa.source_item_id = source_id; + fixed_fa.dest_item_id = dest_id; - WBUFW(buf, 0) = 0x2afa; - WBUFL(buf, 2) = source_id; - WBUFL(buf, 6) = dest_id; + // forward package to map servers + for (Session *ss : iter_map_sessions()) + { + send_fpacket<0x2afa, 10>(ss, fixed_fa); + } - mapif_sendall(buf, 10); // forward package to map servers for (CharPair& cp : char_keys) { CharKey *k = &cp.key; CharData& cd = *cp.data.get(); CharData *c = &cd; - struct storage *s = account2storage(k->account_id); + Storage *s = account2storage(k->account_id); int changes = 0; - int j; #define FIX(v) if (v == source_id) {v = dest_id; ++changes; } - for (j = 0; j < MAX_INVENTORY; j++) + for (IOff0 j : IOff0::iter()) + { FIX(c->inventory[j].nameid); + } // used to FIX cart, but it's no longer supported // FIX(c->weapon); FIX(c->shield); @@ -1414,26 +1521,35 @@ void parse_tologin(Session *ls) FIX(c->head_bottom); if (s) - for (j = 0; j < s->storage_amount; j++) + { + for (SOff0 j : SOff0::iter()) + { FIX(s->storage_[j].nameid); + } + } #undef FIX if (changes) - CHAR_LOG("itemfrob(%d -> %d): `%s'(%d, account %d): changed %d times\n", - source_id, dest_id, k->name, k->char_id, - k->account_id, changes); + CHAR_LOG("itemfrob(%d -> %d): `%s'(%d, account %d): changed %d times\n"_fmt, + source_id, dest_id, k->name, k->char_id, + k->account_id, changes); } mmo_char_sync(); inter_storage_save(); - RFIFOSKIP(ls, 10); break; } // Account deletion notification (from login-server) case 0x2730: - if (RFIFOREST(ls) < 6) - return; + { + Packet_Fixed<0x2730> fixed; + rv = recv_fpacket<0x2730, 6>(ls, fixed); + if (rv != RecvResult::Complete) + break; + + AccountId aid = fixed.account_id; + // Deletion of all characters of the account //#warning "This comment is a lie, but it's still true." // needs to use index because they may move during resize @@ -1441,7 +1557,7 @@ void parse_tologin(Session *ls) { CharPair& cp = char_keys[idx]; CharKey& ck = cp.key; - if (ck.account_id == RFIFOL(ls, 2)) + if (ck.account_id == aid) { char_delete(&cp); if (&cp != &char_keys.back()) @@ -1450,7 +1566,7 @@ void parse_tologin(Session *ls) // if moved character owns to deleted account, check again it's character // YES this is the newly swapped one // we could avoid this by working backwards - if (ck.account_id == RFIFOL(ls, 2)) + if (ck.account_id == aid) { idx--; // Correct moved character reference in the character's owner by [Yor] @@ -1460,69 +1576,91 @@ void parse_tologin(Session *ls) } } // Deletion of the storage - inter_storage_delete(RFIFOL(ls, 2)); + inter_storage_delete(aid); // send to all map-servers to disconnect the player { - unsigned char buf[6]; - WBUFW(buf, 0) = 0x2b13; - WBUFL(buf, 2) = RFIFOL(ls, 2); - mapif_sendall(buf, 6); + Packet_Fixed<0x2b13> fixed_13; + fixed_13.account_id = aid; + for (Session *ss : iter_map_sessions()) + { + send_fpacket<0x2b13, 6>(ss, fixed_13); + } } // disconnect player if online on char-server - disconnect_player(RFIFOL(ls, 2)); - RFIFOSKIP(ls, 6); + disconnect_player(aid); break; + } // State change of account/ban notification (from login-server) by [Yor] case 0x2731: - if (RFIFOREST(ls) < 11) - return; + { + Packet_Fixed<0x2731> fixed; + rv = recv_fpacket<0x2731, 11>(ls, fixed); + if (rv != RecvResult::Complete) + break; + + AccountId aid = fixed.account_id; // send to all map-servers to disconnect the player { - unsigned char buf[11]; - WBUFW(buf, 0) = 0x2b14; - WBUFL(buf, 2) = RFIFOL(ls, 2); - WBUFB(buf, 6) = RFIFOB(ls, 6); // 0: change of statut, 1: ban - WBUFL(buf, 7) = RFIFOL(ls, 7); // status or final date of a banishment - mapif_sendall(buf, 11); + Packet_Fixed<0x2b14> fixed_14; + fixed_14.account_id = aid; + fixed_14.ban_not_status = fixed.ban_not_status; // 0: change of statut, 1: ban + fixed_14.status_or_ban_until = fixed.status_or_ban_until; // status or final date of a banishment + for (Session *ss : iter_map_sessions()) + { + send_fpacket<0x2b14, 11>(ss, fixed_14); + } } // disconnect player if online on char-server - disconnect_player(RFIFOL(ls, 2)); - RFIFOSKIP(ls, 11); + disconnect_player(aid); break; + } // Receiving GM acounts info from login-server (by [Yor]) case 0x2732: - if (RFIFOREST(ls) < 4 || RFIFOREST(ls) < RFIFOW(ls, 2)) - return; + { + std::vector<Packet_Repeat<0x2732>> repeat; + rv = recv_packet_repeatonly<0x2732, 4, 5>(ls, repeat); + if (rv != RecvResult::Complete) + break; + { - size_t len = RFIFOW(ls, 2); - uint8_t buf[len]; - gm_accounts.clear(); - gm_accounts.reserve((len - 4) / 5); - for (int i = 4; i < len; i += 5) + gm_accounts.resize(repeat.size()); + for (size_t k = 0; k < repeat.size(); ++k) { - gm_accounts.push_back({static_cast<int>(RFIFOL(ls, i)), RFIFOB(ls, i + 4)}); + gm_accounts[k].account_id = repeat[k].account_id; + gm_accounts[k].level = repeat[k].gm_level; } - PRINTF("From login-server: receiving of %zu GM accounts information.\n", - gm_accounts.size()); - CHAR_LOG("From login-server: receiving of %zu GM accounts information.\n", - gm_accounts.size()); + PRINTF("From login-server: receiving of %zu GM accounts information.\n"_fmt, + gm_accounts.size()); + CHAR_LOG("From login-server: receiving of %zu GM accounts information.\n"_fmt, + gm_accounts.size()); create_online_files(); // update online players files (perhaps some online players change of GM level) // send new gm acccounts level to map-servers - RFIFO_BUF_CLONE(ls, buf, len); - WBUFW(buf, 0) = 0x2b15; - mapif_sendall(buf, len); + std::vector<Packet_Repeat<0x2b15>> repeat_15(repeat.size()); + for (size_t k = 0; k < repeat.size(); ++k) + { + repeat_15[k].account_id = repeat[k].account_id; + repeat_15[k].gm_level = repeat[k].gm_level; + } + for (Session *ss : iter_map_sessions()) + { + send_packet_repeatonly<0x2b15, 4, 5>(ss, repeat_15); + } } - RFIFOSKIP(ls, RFIFOW(ls, 2)); break; + } case 0x2741: // change password reply - if (RFIFOREST(ls) < 7) - return; + { + Packet_Fixed<0x2741> fixed; + rv = recv_fpacket<0x2741, 7>(ls, fixed); + if (rv != RecvResult::Complete) + break; + { - int acc = RFIFOL(ls, 2); - int status = RFIFOB(ls, 6); + AccountId acc = fixed.account_id; + int status = fixed.status; for (io::FD i : iter_fds()) { @@ -1534,22 +1672,26 @@ void parse_tologin(Session *ls) { if (sd->account_id == acc) { - WFIFOW(s2, 0) = 0x62; - WFIFOB(s2, 2) = status; - WFIFOSET(s2, 3); + Packet_Fixed<0x0062> fixed_62; + fixed_62.status = status; + send_fpacket<0x0062, 3>(s2, fixed_62); break; } } } } - RFIFOSKIP(ls, 7); break; + } default: + { ls->set_eof(); return; + } } } + if (rv == RecvResult::Error) + ls->set_eof(); } //-------------------------------- @@ -1560,18 +1702,16 @@ void map_anti_freeze_system(TimerData *, tick_t) { int i; - //PRINTF("Entering in map_anti_freeze_system function to check freeze of servers.\n"); for (i = 0; i < MAX_MAP_SERVERS; i++) { if (server_session[i]) { // if map-server is online - //PRINTF("map_anti_freeze_system: server #%d, flag: %d.\n", i, server_freezeflag[i]); if (server_freezeflag[i]-- < 1) { // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed - PRINTF("Map-server anti-freeze system: char-server #%d is freezed -> disconnection.\n", - i); - CHAR_LOG("Map-server anti-freeze system: char-server #%d is freezed -> disconnection.\n", - i); + PRINTF("Map-server anti-freeze system: char-server #%d is freezed -> disconnection.\n"_fmt, + i); + CHAR_LOG("Map-server anti-freeze system: char-server #%d is freezed -> disconnection.\n"_fmt, + i); server_session[i]->set_eof(); } } @@ -1591,102 +1731,125 @@ void parse_frommap(Session *ms) return; } - while (RFIFOREST(ms) >= 2) + RecvResult rv = RecvResult::Complete; + uint16_t packet_id; + while (rv == RecvResult::Complete && packet_peek_id(ms, &packet_id)) { -// PRINTF("parse_frommap: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); - - switch (RFIFOW(ms, 0)) + switch (packet_id) { // request from map-server to reload GM accounts. Transmission to login-server (by Yor) case 0x2af7: + { + Packet_Fixed<0x2af7> fixed; + rv = recv_fpacket<0x2af7, 2>(ms, fixed); + if (rv != RecvResult::Complete) + break; + if (login_session) { // don't send request if no login-server - WFIFOW(login_session, 0) = 0x2709; - WFIFOSET(login_session, 2); + Packet_Fixed<0x2709> fixed_09; + send_fpacket<0x2709, 2>(login_session, fixed_09); } - RFIFOSKIP(ms, 2); break; + } // Receiving map names list from the map-server case 0x2afa: - if (RFIFOREST(ms) < 4 || RFIFOREST(ms) < RFIFOW(ms, 2)) - return; { + std::vector<Packet_Repeat<0x2afa>> repeat; + rv = recv_packet_repeatonly<0x2afa, 4, 16>(ms, repeat); + if (rv != RecvResult::Complete) + break; + for (MapName &foo : server[id].maps) foo = MapName(); - int j = 0; - for (int i = 4; i < RFIFOW(ms, 2); i += 16) + + for (size_t j = 0; j < repeat.size(); ++j) { - server[id].maps[j] = RFIFO_STRING<16>(ms, i); - j++; + server[id].maps[j] = repeat[j].map_name; } + const size_t j = repeat.size(); + { - PRINTF("Map-Server %d connected: %d maps, from IP %s port %d.\n", - id, j, server[id].ip, server[id].port); - PRINTF("Map-server %d loading complete.\n", id); - CHAR_LOG("Map-Server %d connected: %d maps, from IP %s port %d. Map-server %d loading complete.\n", - id, j, server[id].ip, - server[id].port, id); + PRINTF("Map-Server %d connected: %zu maps, from IP %s port %d.\n"_fmt, + id, j, server[id].ip, server[id].port); + PRINTF("Map-server %d loading complete.\n"_fmt, id); + CHAR_LOG("Map-Server %d connected: %zu maps, from IP %s port %d. Map-server %d loading complete.\n"_fmt, + id, j, server[id].ip, + server[id].port, id); } - WFIFOW(ms, 0) = 0x2afb; - WFIFOB(ms, 2) = 0; - WFIFO_STRING(ms, 3, wisp_server_name.to__actual(), 24); - WFIFOSET(ms, 27); + + Packet_Fixed<0x2afb> fixed_fb; + fixed_fb.unknown = 0; + fixed_fb.whisper_name = wisp_server_name; + send_fpacket<0x2afb, 27>(ms, fixed_fb); + { - unsigned char buf[16384]; if (j == 0) { - PRINTF("WARNING: Map-Server %d have NO map.\n", id); - CHAR_LOG("WARNING: Map-Server %d have NO map.\n", - id); + PRINTF("WARNING: Map-Server %d have NO map.\n"_fmt, id); + CHAR_LOG("WARNING: Map-Server %d have NO map.\n"_fmt, + id); // Transmitting maps information to the other map-servers } else { - WBUFW(buf, 0) = 0x2b04; - WBUFW(buf, 2) = j * 16 + 10; - WBUFIP(buf, 4) = server[id].ip; - WBUFW(buf, 8) = server[id].port; - // server[id].maps[i] = RFIFO_STRING(fd, 4 + i * 16) + Packet_Head<0x2b04> head_04; + head_04.ip = server[id].ip; + head_04.port = server[id].port; + std::vector<Packet_Repeat<0x2b04>> repeat_04(j); for (int i = 0; i < j; ++i) - WBUF_STRING(buf, 10, server[id].maps[i], 16); - mapif_sendallwos(ms, buf, WBUFW(buf, 2)); + { + repeat_04[i].map_name = server[id].maps[i]; + } + for (Session *ss : iter_map_sessions()) + { + if (ss == ms) + continue; + send_vpacket<0x2b04, 10, 16>(ss, head_04, repeat_04); + } } // Transmitting the maps of the other map-servers to the new map-server for (int x = 0; x < MAX_MAP_SERVERS; x++) { if (server_session[x] && x != id) { - WFIFOW(ms, 0) = 0x2b04; - WFIFOIP(ms, 4) = server[x].ip; - WFIFOW(ms, 8) = server[x].port; - j = 0; + Packet_Head<0x2b04> head_04; + head_04.ip = server[x].ip; + head_04.port = server[x].port; + std::vector<Packet_Repeat<0x2b04>> repeat_04; for (int i = 0; i < MAX_MAP_PER_SERVER; i++) + { if (server[x].maps[i]) - WFIFO_STRING(ms, 10 + (j++) * 16, server[x].maps[i], 16); - if (j > 0) + { + Packet_Repeat<0x2b04> info; + info.map_name = server[x].maps[i]; + repeat_04.push_back(info); + } + } + if (repeat.size()) { - WFIFOW(ms, 2) = j * 16 + 10; - WFIFOSET(ms, WFIFOW(ms, 2)); + send_vpacket<0x2b04, 10, 16>(ms, head_04, repeat_04); } } } } - } - RFIFOSKIP(ms, RFIFOW(ms, 2)); break; + } // 認証要求 case 0x2afc: - if (RFIFOREST(ms) < 22) - return; { - int account_id = RFIFOL(ms, 2); - int char_id = RFIFOL(ms, 6); - int login_id1 = RFIFOL(ms, 10); - int login_id2 = RFIFOL(ms, 14); - IP4Address ip = RFIFOIP(ms, 18); - //PRINTF("auth_fifo search: account: %d, char: %d, secure: %08x-%08x\n", RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14)); + Packet_Fixed<0x2afc> fixed; + rv = recv_fpacket<0x2afc, 22>(ms, fixed); + if (rv != RecvResult::Complete) + break; + + AccountId account_id = fixed.account_id; + CharId char_id = fixed.char_id; + int login_id1 = fixed.login_id1; + int login_id2 = fixed.login_id2; + IP4Address ip = fixed.ip; for (AuthFifoEntry& afi : auth_fifo) { if (afi.account_id == account_id && @@ -1706,46 +1869,49 @@ void parse_frommap(Session *ms) break; } } - assert (cp && "uh-oh - deleted while in queue?"); + assert (cp && "uh-oh - deleted while in queue?"_s); CharKey *ck = &cp->key; CharData *cd = cp->data.get(); afi.delflag = 1; - WFIFOW(ms, 0) = 0x2afd; - WFIFOW(ms, 2) = 18 + sizeof(*ck) + sizeof(*cd); - WFIFOL(ms, 4) = account_id; - WFIFOL(ms, 8) = afi.login_id2; - WFIFOL(ms, 12) = static_cast<time_t>(afi.connect_until_time); + Packet_Payload<0x2afd> payload_fd; // not file descriptor + payload_fd.account_id = account_id; + payload_fd.login_id2 = afi.login_id2; + payload_fd.connect_until = afi.connect_until_time; cd->sex = afi.sex; - WFIFOW(ms, 16) = afi.packet_tmw_version; + payload_fd.packet_tmw_version = afi.packet_tmw_version; FPRINTF(stderr, - "From queue index %zd: recalling packet version %d\n", - (&afi - &auth_fifo.front()), afi.packet_tmw_version); - WFIFO_STRUCT(ms, 18, *ck); - WFIFO_STRUCT(ms, 18 + sizeof(*ck), *cd); - WFIFOSET(ms, WFIFOW(ms, 2)); - //PRINTF("auth_fifo search success (auth #%d, account %d, character: %d).\n", i, RFIFOL(fd,2), RFIFOL(fd,6)); + "From queue index %zd: recalling packet version %d\n"_fmt, + (&afi - &auth_fifo.front()), afi.packet_tmw_version); + payload_fd.char_key = *ck; + payload_fd.char_data = *cd; + send_ppacket<0x2afd>(ms, payload_fd); goto x2afc_out; } } { - WFIFOW(ms, 0) = 0x2afe; - WFIFOL(ms, 2) = account_id; - WFIFOSET(ms, 6); - PRINTF("auth_fifo search error! account %d not authentified.\n", + Packet_Fixed<0x2afe> fixed_fe; + fixed_fe.account_id = account_id; + send_fpacket<0x2afe, 6>(ms, fixed_fe); + PRINTF("auth_fifo search error! account %d not authentified.\n"_fmt, account_id); } - } x2afc_out: - RFIFOSKIP(ms, 22); break; + } // MAPサーバー上のユーザー数受信 case 0x2aff: - if (RFIFOREST(ms) < 6 || RFIFOREST(ms) < RFIFOW(ms, 2)) - return; - server[id].users = RFIFOW(ms, 4); + { + Packet_Head<0x2aff> head; + std::vector<Packet_Repeat<0x2aff>> repeat; + rv = recv_vpacket<0x2aff, 6, 4>(ms, head, repeat); + if (rv != RecvResult::Complete) + break; + + server[id].users = head.users; + assert (head.users == repeat.size()); if (anti_freeze_enable) server_freezeflag[id] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed // remove all previously online players of the server @@ -1758,7 +1924,7 @@ void parse_frommap(Session *ms) // add online players in the list by [Yor] for (int i = 0; i < server[id].users; i++) { - int char_id = RFIFOL(ms, 6 + i * 4); + CharId char_id = repeat[i].char_id; for (const CharPair& cd : char_keys) { if (cd.key.char_id == char_id) @@ -1776,306 +1942,374 @@ void parse_frommap(Session *ms) // only every 8 sec. (normally, 1 server send users every 5 sec.) Don't update every time, because that takes time, but only every 2 connection. // it set to 8 sec because is more than 5 (sec) and if we have more than 1 map-server, informations can be received in shifted. } - RFIFOSKIP(ms, RFIFOW(ms, 2)); break; + } // キャラデータ保存 case 0x2b01: - if (RFIFOREST(ms) < 4 || RFIFOREST(ms) < RFIFOW(ms, 2)) - return; + { + Packet_Payload<0x2b01> payload; + rv = recv_ppacket<0x2b01>(ms, payload); + if (rv != RecvResult::Complete) + break; + + AccountId aid = payload.account_id; + CharId cid = payload.char_id; for (CharPair& cd : char_keys) { - if (cd.key.account_id == RFIFOL(ms, 4) && - cd.key.char_id == RFIFOL(ms, 8)) + if (cd.key.account_id == aid && + cd.key.char_id == cid) { - RFIFO_STRUCT(ms, 12, cd.key); - RFIFO_STRUCT(ms, 12 + sizeof(cd.key), *cd.data); + cd.key = payload.char_key; + *cd.data = payload.char_data; break; } } - RFIFOSKIP(ms, RFIFOW(ms, 2)); break; + } // キャラセレ要求 case 0x2b02: - if (RFIFOREST(ms) < 18) - return; { - int account_id = RFIFOL(ms, 2); + Packet_Fixed<0x2b02> fixed; + rv = recv_fpacket<0x2b02, 18>(ms, fixed); + if (rv != RecvResult::Complete) + break; + + AccountId account_id = fixed.account_id; if (auth_fifo_iter == auth_fifo.end()) auth_fifo_iter = auth_fifo.begin(); auth_fifo_iter->account_id = account_id; - auth_fifo_iter->char_id = 0; - auth_fifo_iter->login_id1 = RFIFOL(ms, 6); - auth_fifo_iter->login_id2 = RFIFOL(ms, 10); + auth_fifo_iter->char_id = CharId(); + auth_fifo_iter->login_id1 = fixed.login_id1; + auth_fifo_iter->login_id2 = fixed.login_id2; auth_fifo_iter->delflag = 2; auth_fifo_iter->connect_until_time = TimeT(); // unlimited/unknown time by default (not display in map-server) - auth_fifo_iter->ip = RFIFOIP(ms, 14); + auth_fifo_iter->ip = fixed.ip; auth_fifo_iter++; - WFIFOW(ms, 0) = 0x2b03; - WFIFOL(ms, 2) = account_id; - WFIFOB(ms, 6) = 0; - WFIFOSET(ms, 7); - } - RFIFOSKIP(ms, 18); + + Packet_Fixed<0x2b03> fixed_03; + fixed_03.account_id = account_id; + fixed_03.unknown = 0; + send_fpacket<0x2b03, 7>(ms, fixed_03); break; + } // マップサーバー間移動要求 case 0x2b05: - if (RFIFOREST(ms) < 49) - return; + { + Packet_Fixed<0x2b05> fixed; + rv = recv_fpacket<0x2b05, 49>(ms, fixed); + if (rv != RecvResult::Complete) + break; + if (auth_fifo_iter == auth_fifo.end()) auth_fifo_iter = auth_fifo.begin(); - RFIFO_WFIFO_CLONE(ms, ms, 44); - // overwrite - WFIFOW(ms, 0) = 0x2b06; - auth_fifo_iter->account_id = RFIFOL(ms, 2); - auth_fifo_iter->char_id = RFIFOL(ms, 14); - auth_fifo_iter->login_id1 = RFIFOL(ms, 6); - auth_fifo_iter->login_id2 = RFIFOL(ms, 10); + Packet_Fixed<0x2b06> fixed_06; + fixed_06.account_id = fixed.account_id; + //fixed_06.error = fixed.login_id1; + //fixed_06.unknown = fixed.login_id2; + fixed_06.char_id = fixed.char_id; + fixed_06.map_name = fixed.map_name; + fixed_06.x = fixed.x; + fixed_06.y = fixed.y; + fixed_06.map_ip = fixed.map_ip; + fixed_06.map_port = fixed.map_port; + + auth_fifo_iter->account_id = fixed.account_id; + auth_fifo_iter->login_id1 = fixed.login_id1; + auth_fifo_iter->login_id2 = fixed.login_id2; + auth_fifo_iter->char_id = fixed.char_id; auth_fifo_iter->delflag = 0; - auth_fifo_iter->sex = static_cast<SEX>(RFIFOB(ms, 44)); + auth_fifo_iter->sex = fixed.sex; auth_fifo_iter->connect_until_time = TimeT(); // unlimited/unknown time by default (not display in map-server) - auth_fifo_iter->ip = RFIFOIP(ms, 45); + auth_fifo_iter->ip = fixed.client_ip; // default, if not found in the loop - WFIFOW(ms, 6) = 1; + fixed_06.error = 1; for (const CharPair& cd : char_keys) - if (cd.key.account_id == RFIFOL(ms, 2) && - cd.key.char_id == RFIFOL(ms, 14)) + { + AccountId aid = fixed.account_id; + CharId cid = fixed.char_id; + if (cd.key.account_id == aid && + cd.key.char_id == cid) { auth_fifo_iter++; - WFIFOL(ms, 6) = 0; + fixed_06.error = 0; break; } - WFIFOSET(ms, 44); - RFIFOSKIP(ms, 49); + } + send_fpacket<0x2b06, 44>(ms, fixed_06); break; + } // it is a request to become GM case 0x2b0a: - if (RFIFOREST(ms) < 4 || RFIFOREST(ms) < RFIFOW(ms, 2)) - return; { - int account_id = RFIFOL(ms, 4); + Packet_Head<0x2b0a> head; + AString repeat; + rv = recv_vpacket<0x2b0a, 8, 1>(ms, head, repeat); + if (rv != RecvResult::Complete) + break; + + AccountId account_id = head.account_id; if (login_session) { // don't send request if no login-server - size_t len = RFIFOW(ms, 2); - RFIFO_WFIFO_CLONE(ms, login_session, len); - WFIFOW(login_session, 0) = 0x2720; - WFIFOSET(login_session, len); + Packet_Head<0x2720> head_20; + head_20.account_id = account_id; + AString& repeat_20 = repeat; + send_vpacket<0x2720, 8, 1>(login_session, head_20, repeat_20); } else { - WFIFOW(ms, 0) = 0x2b0b; - WFIFOL(ms, 2) = account_id; - WFIFOL(ms, 6) = 0; - WFIFOSET(ms, 10); + Packet_Fixed<0x2b0b> fixed_0b; + fixed_0b.account_id = account_id; + fixed_0b.gm_level = GmLevel(); + send_fpacket<0x2b0b, 10>(ms, fixed_0b); } - } - RFIFOSKIP(ms, RFIFOW(ms, 2)); break; + } // Map server send information to change an email of an account -> login-server case 0x2b0c: - if (RFIFOREST(ms) < 86) - return; + { + Packet_Fixed<0x2b0c> fixed; + rv = recv_fpacket<0x2b0c, 86>(ms, fixed); + if (rv != RecvResult::Complete) + break; + if (login_session) { // don't send request if no login-server - RFIFO_WFIFO_CLONE(ms, login_session, 86); // 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B - WFIFOW(login_session, 0) = 0x2722; - WFIFOSET(login_session, 86); + Packet_Fixed<0x2722> fixed_22; + fixed_22.account_id = fixed.account_id; + fixed_22.old_email = fixed.old_email; + fixed_22.new_email = fixed.new_email; + send_fpacket<0x2722, 86>(login_session, fixed_22); } - RFIFOSKIP(ms, 86); break; + } // Map server ask char-server about a character name to do some operations (all operations are transmitted to login-server) case 0x2b0e: - if (RFIFOREST(ms) < 44) - return; + { + Packet_Fixed<0x2b0e> fixed; + rv = recv_fpacket<0x2b0e, 44>(ms, fixed); + if (rv != RecvResult::Complete) + break; + { - int acc = RFIFOL(ms, 2); // account_id of who ask (-1 if nobody) - CharName character_name = stringish<CharName>(RFIFO_STRING<24>(ms, 6)); - int operation = RFIFOW(ms, 30); + AccountId acc = fixed.account_id; + CharName character_name = fixed.char_name; + int operation = fixed.operation; // prepare answer - WFIFOW(ms, 0) = 0x2b0f; // answer - WFIFOL(ms, 2) = acc; // who want do operation - WFIFOW(ms, 30) = operation; // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5-changesex + Packet_Fixed<0x2b0f> fixed_0f; + fixed_0f.account_id = acc; + fixed_0f.operation = operation; // search character const CharPair *cd = search_character(character_name); + // TODO invert the below logic to shrink this code + // Current logic: + // 1. if no character, return error 1 + // 2. else if gm level too low, return error 2 + // 3. else if login server offline, return error 3 + // 4. else return error 0 and maybe do other stuff if (cd) { const CharKey *ck = &cd->key; - WFIFO_STRING(ms, 6, ck->name.to__actual(), 24); // put correct name if found - WFIFOW(ms, 32) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - switch (RFIFOW(ms, 30)) + fixed_0f.char_name = ck->name; + fixed_0f.error = 0; + switch (operation) { case 1: // block - if (acc == -1 - || isGM(acc) >= isGM(ck->account_id)) + { + if (!acc + || isGM(acc).overwhelms(isGM(ck->account_id))) { if (login_session) { // don't send request if no login-server - WFIFOW(login_session, 0) = 0x2724; - WFIFOL(login_session, 2) = ck->account_id; // account value - WFIFOL(login_session, 6) = 5; // status of the account - WFIFOSET(login_session, 10); + Packet_Fixed<0x2724> fixed_24; + fixed_24.account_id = ck->account_id; + fixed_24.status = 5; + send_fpacket<0x2724, 10>(login_session, fixed_24); } else - WFIFOW(ms, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + fixed_0f.error = 3; } else - WFIFOW(ms, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + fixed_0f.error = 2; break; + } case 2: // ban - if (acc == -1 - || isGM(acc) >= isGM(ck->account_id)) + { + if (!acc + || isGM(acc).overwhelms(isGM(ck->account_id))) { if (login_session) { // don't send request if no login-server - WFIFOW(login_session, 0) = 0x2725; - WFIFOL(login_session, 2) = ck->account_id; // account value - HumanTimeDiff ban_change; - RFIFO_STRUCT(ms, 32, ban_change); - WFIFO_STRUCT(login_session, 6, ban_change); - WFIFOSET(login_session, 18); + Packet_Fixed<0x2725> fixed_25; + fixed_25.account_id = ck->account_id; + HumanTimeDiff ban_change = fixed.ban_add; + fixed_25.ban_add = ban_change; + send_fpacket<0x2725, 18>(login_session, fixed_25); } else - WFIFOW(ms, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + fixed_0f.error = 3; } else - WFIFOW(ms, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + fixed_0f.error = 2; break; + } case 3: // unblock - if (acc == -1 - || isGM(acc) >= isGM(ck->account_id)) + { + if (!acc + || isGM(acc).overwhelms(isGM(ck->account_id))) { if (login_session) { // don't send request if no login-server - WFIFOW(login_session, 0) = 0x2724; - WFIFOL(login_session, 2) = ck->account_id; // account value - WFIFOL(login_session, 6) = 0; // status of the account - WFIFOSET(login_session, 10); + Packet_Fixed<0x2724> fixed_24; + fixed_24.account_id = ck->account_id; + fixed_24.status = 0; + send_fpacket<0x2724, 10>(login_session, fixed_24); } else - WFIFOW(ms, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + fixed_0f.error = 3; } else - WFIFOW(ms, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + fixed_0f.error = 2; break; + } case 4: // unban - if (acc == -1 - || isGM(acc) >= isGM(ck->account_id)) + { + if (!acc + || isGM(acc).overwhelms(isGM(ck->account_id))) { if (login_session) { // don't send request if no login-server - WFIFOW(login_session, 0) = 0x272a; - WFIFOL(login_session, 2) = ck->account_id; // account value - WFIFOSET(login_session, 6); + Packet_Fixed<0x272a> fixed_2a; + fixed_2a.account_id = ck->account_id; + send_fpacket<0x272a, 6>(login_session, fixed_2a); } else - WFIFOW(ms, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + fixed_0f.error = 3; } else - WFIFOW(ms, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + fixed_0f.error = 2; break; + } case 5: // changesex - if (acc == -1 - || isGM(acc) >= isGM(ck->account_id)) + { + if (!acc + || isGM(acc).overwhelms(isGM(ck->account_id))) { if (login_session) { // don't send request if no login-server - WFIFOW(login_session, 0) = 0x2727; - WFIFOL(login_session, 2) = ck->account_id; // account value - WFIFOSET(login_session, 6); + Packet_Fixed<0x2727> fixed_27; + fixed_27.account_id = ck->account_id; + send_fpacket<0x2727, 6>(login_session, fixed_27); } else - WFIFOW(ms, 32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + fixed_0f.error = 3; } else - WFIFOW(ms, 32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + fixed_0f.error = 2; break; + } } } else { // character name not found - WFIFO_STRING(ms, 6, character_name.to__actual(), 24); - WFIFOW(ms, 32) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + fixed_0f.char_name = character_name; + fixed_0f.error = 1; } // send answer if a player ask, not if the server ask - if (acc != -1) + if (acc) { - WFIFOSET(ms, 34); + send_fpacket<0x2b0f, 34>(ms, fixed_0f); } - RFIFOSKIP(ms, 44); break; } - -// case 0x2b0f: not more used (available for futur usage) + } // account_reg保存要求 case 0x2b10: - if (RFIFOREST(ms) < 4 || RFIFOREST(ms) < RFIFOW(ms, 2)) - return; + { + Packet_Head<0x2b10> head; + std::vector<Packet_Repeat<0x2b10>> repeat; + rv = recv_vpacket<0x2b10, 8, 36>(ms, head, repeat); + if (rv != RecvResult::Complete) + break; + { - Array<struct global_reg, ACCOUNT_REG2_NUM> reg; - int p, j; - int acc = RFIFOL(ms, 4); - for (p = 8, j = 0; - p < RFIFOW(ms, 2) && j < ACCOUNT_REG2_NUM; - p += 36, j++) + Array<GlobalReg, ACCOUNT_REG2_NUM> reg; + AccountId acc = head.account_id; + auto jlim = std::min(repeat.size(), ACCOUNT_REG2_NUM); + for (size_t j = 0; j < jlim; ++j) { - reg[j].str = stringish<VarName>(RFIFO_STRING<32>(ms, p)); - reg[j].value = RFIFOL(ms, p + 32); + reg[j].str = repeat[j].name; + reg[j].value = repeat[j].value; } - set_account_reg2(acc, Slice<struct global_reg>(reg.begin(), j)); + set_account_reg2(acc, Slice<GlobalReg>(reg.begin(), jlim)); // loginサーバーへ送る if (login_session) { // don't send request if no login-server - RFIFO_WFIFO_CLONE(ms, login_session, RFIFOW(ms, 2)); - WFIFOW(login_session, 0) = 0x2728; - WFIFOSET(login_session, WFIFOW(login_session, 2)); + Packet_Head<0x2728> head_28; + std::vector<Packet_Repeat<0x2728>> repeat_28(repeat.size()); + for (size_t j = 0; j < repeat.size(); ++j) + { + repeat_28[j].name = repeat[j].name; + repeat_28[j].value = repeat[j].value; + } + send_vpacket<0x2728, 8, 36>(login_session, head_28, repeat_28); } - RFIFOSKIP(ms, RFIFOW(ms, 2)); break; } + } // Map server is requesting a divorce case 0x2b16: - if (RFIFOREST(ms) < 4) - return; + { + Packet_Fixed<0x2b16> fixed; + rv = recv_fpacket<0x2b16, 6>(ms, fixed); + if (rv != RecvResult::Complete) + break; + { + CharId cid = fixed.char_id; for (CharPair& cd : char_keys) - if (cd.key.char_id == RFIFOL(ms, 2)) + { + if (cd.key.char_id == cid) { char_divorce(&cd); break; } + } - RFIFOSKIP(ms, 6); break; } + } default: // inter server処理に渡す { - int r = inter_parse_frommap(ms); - if (r == 1) // 処理できた + RecvResult r = inter_parse_frommap(ms, packet_id); + if (r == RecvResult::Complete) break; - if (r == 2) // パケット長が足りない + if (r == RecvResult::Incomplete) return; - } // inter server処理でもない場合は切断 - PRINTF("char: unknown packet 0x%04x (%zu bytes to read in buffer)! (from map).\n", - RFIFOW(ms, 0), RFIFOREST(ms)); + PRINTF("char: unknown packet 0x%04x (%zu bytes to read in buffer)! (from map).\n"_fmt, + packet_id, packet_avail(ms)); ms->set_eof(); return; + } } } + if (rv == RecvResult::Error) + ms->set_eof(); } static @@ -2104,8 +2338,8 @@ int lan_ip_check(IP4Address addr) { bool lancheck = lan_subnet.covers(addr); - PRINTF("LAN test (result): %s.\n", - (lancheck) ? SGR_BOLD SGR_CYAN "LAN source" SGR_RESET : SGR_BOLD SGR_GREEN "WAN source" SGR_RESET); + PRINTF("LAN test (result): %s.\n"_fmt, + (lancheck) ? SGR_BOLD SGR_CYAN "LAN source" SGR_RESET ""_s : SGR_BOLD SGR_GREEN "WAN source" SGR_RESET ""_s); return lancheck; } @@ -2127,9 +2361,9 @@ void handle_x0066(Session *s, struct char_session_data *sd, uint8_t rfifob_2, IP CharKey *ck = &cp->key; CharData *cd = cp->data.get(); - CHAR_LOG("Character Selected, Account ID: %d, Character Slot: %d, Character Name: %s [%s]\n", - sd->account_id, rfifob_2, - ck->name, ip); + CHAR_LOG("Character Selected, Account ID: %d, Character Slot: %d, Character Name: %s [%s]\n"_fmt, + sd->account_id, rfifob_2, + ck->name, ip); // searching map server int i = search_mapserver(cd->last_point.map_); // if map is not found, we check major cities @@ -2144,33 +2378,35 @@ void handle_x0066(Session *s, struct char_session_data *sd, uint8_t rfifob_2, IP { // change save point to one of map found on the server (the first) i = j; cd->last_point.map_ = server[j].maps[0]; - PRINTF("Map-server #%d found with a map: '%s'.\n", - j, server[j].maps[0]); + PRINTF("Map-server #%d found with a map: '%s'.\n"_fmt, + j, server[j].maps[0]); // coordonates are unknown break; } // if no map-server is connected, we send: server closed if (j == MAX_MAP_SERVERS) { - WFIFOW(s, 0) = 0x81; - WFIFOB(s, 2) = 1; // 01 = Server closed - WFIFOSET(s, 3); + Packet_Fixed<0x0081> fixed_81; + fixed_81.error_code = 1; + send_fpacket<0x0081, 3>(s, fixed_81); return; } } - WFIFOW(s, 0) = 0x71; - WFIFOL(s, 2) = ck->char_id; - WFIFO_STRING(s, 6, cd->last_point.map_, 16); - PRINTF("Character selection '%s' (account: %d, slot: %d) [%s]\n", - ck->name, - sd->account_id, ck->char_num, ip); - PRINTF("--Send IP of map-server. "); + + Packet_Fixed<0x0071> fixed_71; + fixed_71.char_id = ck->char_id; + fixed_71.map_name = cd->last_point.map_; + PRINTF("Character selection '%s' (account: %d, slot: %d) [%s]\n"_fmt, + ck->name, + sd->account_id, ck->char_num, ip); + PRINTF("--Send IP of map-server. "_fmt); if (lan_ip_check(ip)) - WFIFOIP(s, 22) = lan_map_ip; + fixed_71.ip = lan_map_ip; else - WFIFOIP(s, 22) = server[i].ip; - WFIFOW(s, 26) = server[i].port; - WFIFOSET(s, 28); + fixed_71.ip = server[i].ip; + fixed_71.port = server[i].port; + send_fpacket<0x0071, 28>(s, fixed_71); + if (auth_fifo_iter == auth_fifo.end()) auth_fifo_iter = auth_fifo.begin(); auth_fifo_iter->account_id = sd->account_id; @@ -2192,73 +2428,75 @@ void parse_char(Session *s) { IP4Address ip = s->client_ip; + assert (s != login_session); + if (!login_session) { s->set_eof(); - - // I sure *hope* this doesn't happen ... - if (s == login_session) - login_session = nullptr; return; } char_session_data *sd = static_cast<char_session_data *>(s->session_data.get()); - while (RFIFOREST(s) >= 2) + RecvResult rv = RecvResult::Complete; + uint16_t packet_id; + while (rv == RecvResult::Complete && packet_peek_id(s, &packet_id)) { -// if (RFIFOW(fd,0) < 30000) -// PRINTF("parse_char: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); - - switch (RFIFOW(s, 0)) + switch (packet_id) { - case 0x20b: //20040622暗号化ragexe対応 - if (RFIFOREST(s) < 19) - return; - RFIFOSKIP(s, 19); - break; - case 0x61: // change password request - if (RFIFOREST(s) < 50) - return; + { + Packet_Fixed<0x0061> fixed; + rv = recv_fpacket<0x0061, 50>(s, fixed); + if (rv != RecvResult::Complete) + break; + { - WFIFOW(login_session, 0) = 0x2740; - WFIFOL(login_session, 2) = sd->account_id; - AccountPass old_pass = stringish<AccountPass>(RFIFO_STRING<24>(s, 2)); - WFIFO_STRING(login_session, 6, old_pass, 24); - AccountPass new_pass = stringish<AccountPass>(RFIFO_STRING<24>(s, 26)); - WFIFO_STRING(login_session, 30, new_pass, 24); - WFIFOSET(login_session, 54); + Packet_Fixed<0x2740> fixed_40; + fixed_40.account_id = sd->account_id; + AccountPass old_pass = fixed.old_pass; + fixed_40.old_pass = old_pass; + AccountPass new_pass = fixed.new_pass; + fixed_40.new_pass = new_pass; + send_fpacket<0x2740, 54>(login_session, fixed_40); } - RFIFOSKIP(s, 50); break; + } case 0x65: // 接続要求 - if (RFIFOREST(s) < 17) - return; + { + Packet_Fixed<0x0065> fixed; + rv = recv_fpacket<0x0065, 17>(s, fixed); + if (rv != RecvResult::Complete) + break; + { - int account_id = RFIFOL(s, 2); - int GM_value = isGM(account_id); + AccountId account_id = fixed.account_id; + GmLevel GM_value = isGM(account_id); if (GM_value) - PRINTF("Account Logged On; Account ID: %d (GM level %d).\n", + PRINTF("Account Logged On; Account ID: %d (GM level %d).\n"_fmt, account_id, GM_value); else - PRINTF("Account Logged On; Account ID: %d.\n", + PRINTF("Account Logged On; Account ID: %d.\n"_fmt, account_id); - if (sd == NULL) + if (sd == nullptr) { s->session_data = make_unique<char_session_data, SessionDeleter>(); sd = static_cast<char_session_data *>(s->session_data.get()); - sd->email = stringish<AccountEmail>("no mail"); // put here a mail without '@' to refuse deletion if we don't receive the e-mail + sd->email = stringish<AccountEmail>("no mail"_s); // put here a mail without '@' to refuse deletion if we don't receive the e-mail sd->connect_until_time = TimeT(); // unknow or illimited (not displaying on map-server) } sd->account_id = account_id; - sd->login_id1 = RFIFOL(s, 6); - sd->login_id2 = RFIFOL(s, 10); - sd->packet_tmw_version = RFIFOW(s, 14); - sd->sex = static_cast<SEX>(RFIFOB(s, 16)); - // send back account_id - WFIFOL(s, 0) = account_id; - WFIFOSET(s, 4); + sd->login_id1 = fixed.login_id1; + sd->login_id2 = fixed.login_id2; + sd->packet_tmw_version = fixed.packet_tmw_version; + sd->sex = fixed.sex; + + // formerly: send back account_id + Packet_Payload<0x8000> special; + special.magic_packet_length = 4; + send_ppacket<0x8000>(s, special); + // search authentification for (AuthFifoEntry& afi : auth_fifo) { @@ -2273,12 +2511,12 @@ void parse_char(Session *s) if (max_connect_user == 0 || count_users() < max_connect_user) { - if (login_session) - { // don't send request if no login-server + { + // there is always a login server // request to login-server to obtain e-mail/time limit - WFIFOW(login_session, 0) = 0x2716; - WFIFOL(login_session, 2) = sd->account_id; - WFIFOSET(login_session, 6); + Packet_Fixed<0x2716> fixed_16; + fixed_16.account_id = sd->account_id; + send_fpacket<0x2716, 6>(login_session, fixed_16); } // Record client version afi.packet_tmw_version = @@ -2289,126 +2527,153 @@ void parse_char(Session *s) else { // refuse connection (over populated) - WFIFOW(s, 0) = 0x6c; - WFIFOB(s, 2) = 0; - WFIFOSET(s, 3); + Packet_Fixed<0x006c> fixed_6c; + fixed_6c.code = 0; + send_fpacket<0x006c, 3>(s, fixed_6c); } goto x65_out; } } // authentification not found { - if (login_session) { - // don't send request if no login-server - WFIFOW(login_session, 0) = 0x2712; // ask login-server to authentify an account - WFIFOL(login_session, 2) = sd->account_id; - WFIFOL(login_session, 6) = sd->login_id1; - WFIFOL(login_session, 10) = sd->login_id2; // relate to the versions higher than 18 - WFIFOB(login_session, 14) = static_cast<uint8_t>(sd->sex); - WFIFOIP(login_session, 15) = s->client_ip; - WFIFOSET(login_session, 19); - } - else - { // if no login-server, we must refuse connection - WFIFOW(s, 0) = 0x6c; - WFIFOB(s, 2) = 0; - WFIFOSET(s, 3); + // there is always a login-server + Packet_Fixed<0x2712> fixed_12; + fixed_12.account_id = sd->account_id; + fixed_12.login_id1 = sd->login_id1; + fixed_12.login_id2 = sd->login_id2; // relate to the versions higher than 18 + fixed_12.sex = sd->sex; + fixed_12.ip = s->client_ip; + send_fpacket<0x2712, 19>(login_session, fixed_12); } } } x65_out: - RFIFOSKIP(s, 17); break; + } case 0x66: // キャラ選択 - if (!sd || RFIFOREST(s) < 3) + { + Packet_Fixed<0x0066> fixed; + rv = recv_fpacket<0x0066, 3>(s, fixed); + if (rv != RecvResult::Complete) + break; + + if (!sd) + { + s->set_eof(); return; - handle_x0066(s, sd, RFIFOB(s, 2), ip); - RFIFOSKIP(s, 3); + } + handle_x0066(s, sd, fixed.code, ip); break; + } case 0x67: // 作成 - if (!sd || RFIFOREST(s) < 37) - return; { - CharName name = stringish<CharName>(RFIFO_STRING<24>(s, 2)); - uint8_t stats[6]; - for (int i = 0; i < 6; ++i) - stats[i] = RFIFOB(s, 26 + i); - uint8_t slot = RFIFOB(s, 32); - uint16_t hair_color = RFIFOW(s, 33); - uint16_t hair_style = RFIFOW(s, 35); + Packet_Fixed<0x0067> fixed; + rv = recv_fpacket<0x0067, 37>(s, fixed); + if (rv != RecvResult::Complete) + break; + + if (!sd) + { + s->set_eof(); + return; + } + CharName name = fixed.char_name; + Stats6 stats = fixed.stats; + uint8_t slot = fixed.slot; + uint16_t hair_color = fixed.hair_color; + uint16_t hair_style = fixed.hair_style; const CharPair *cp = make_new_char(s, name, stats, slot, hair_color, hair_style); if (!cp) { - WFIFOW(s, 0) = 0x6e; - WFIFOB(s, 2) = 0x00; - WFIFOSET(s, 3); - RFIFOSKIP(s, 37); + Packet_Fixed<0x006e> fixed_6e; + fixed_6e.code = 0x00; + send_fpacket<0x006e, 3>(s, fixed_6e); break; } const CharKey *ck = &cp->key; const CharData *cd = cp->data.get(); - WFIFOW(s, 0) = 0x6d; - WFIFO_ZERO(s, 2, 106); - - WFIFOL(s, 2) = ck->char_id; - WFIFOL(s, 2 + 4) = cd->base_exp; - WFIFOL(s, 2 + 8) = cd->zeny; - WFIFOL(s, 2 + 12) = cd->job_exp; - WFIFOL(s, 2 + 16) = cd->job_level; - - WFIFOL(s, 2 + 28) = cd->karma; - WFIFOL(s, 2 + 32) = cd->manner; - - WFIFOW(s, 2 + 40) = 0x30; - WFIFOW(s, 2 + 42) = min(cd->hp, 0x7fff); - WFIFOW(s, 2 + 44) = min(cd->max_hp, 0x7fff); - WFIFOW(s, 2 + 46) = min(cd->sp, 0x7fff); - WFIFOW(s, 2 + 48) = min(cd->max_sp, 0x7fff); - WFIFOW(s, 2 + 50) = static_cast<uint16_t>(DEFAULT_WALK_SPEED.count()); // cd->speed; - WFIFOW(s, 2 + 52) = cd->species; - WFIFOW(s, 2 + 54) = cd->hair; - - WFIFOW(s, 2 + 58) = cd->base_level; - WFIFOW(s, 2 + 60) = cd->skill_point; - - WFIFOW(s, 2 + 64) = cd->shield; - WFIFOW(s, 2 + 66) = cd->head_top; - WFIFOW(s, 2 + 68) = cd->head_mid; - WFIFOW(s, 2 + 70) = cd->hair_color; - - WFIFO_STRING(s, 2 + 74, ck->name.to__actual(), 24); - - WFIFOB(s, 2 + 98) = min(cd->attrs[ATTR::STR], 255); - WFIFOB(s, 2 + 99) = min(cd->attrs[ATTR::AGI], 255); - WFIFOB(s, 2 + 100) = min(cd->attrs[ATTR::VIT], 255); - WFIFOB(s, 2 + 101) = min(cd->attrs[ATTR::INT], 255); - WFIFOB(s, 2 + 102) = min(cd->attrs[ATTR::DEX], 255); - WFIFOB(s, 2 + 103) = min(cd->attrs[ATTR::LUK], 255); - WFIFOB(s, 2 + 104) = ck->char_num; - - WFIFOSET(s, 108); - } - RFIFOSKIP(s, 37); + Packet_Fixed<0x006d> fixed_6d; + + fixed_6d.char_select.char_id = ck->char_id; + fixed_6d.char_select.base_exp = cd->base_exp; + fixed_6d.char_select.zeny = cd->zeny; + fixed_6d.char_select.job_exp = cd->job_exp; + fixed_6d.char_select.job_level = cd->job_level; + + fixed_6d.char_select.shoes = ItemNameId(); + fixed_6d.char_select.gloves = ItemNameId(); + fixed_6d.char_select.cape = ItemNameId(); + fixed_6d.char_select.misc1 = ItemNameId(); + fixed_6d.char_select.option = Option(); + fixed_6d.char_select.unused = 0; + + // this was buggy until the protocol became generated + // but the client ignores it anyway + fixed_6d.char_select.karma = cd->karma; + fixed_6d.char_select.manner = cd->manner; + + fixed_6d.char_select.status_point = 0x30; + fixed_6d.char_select.hp = saturate<int16_t>(cd->hp); + fixed_6d.char_select.max_hp = saturate<int16_t>(cd->max_hp); + fixed_6d.char_select.sp = saturate<int16_t>(cd->sp); + fixed_6d.char_select.max_sp = saturate<int16_t>(cd->max_sp); + fixed_6d.char_select.speed = static_cast<uint16_t>(DEFAULT_WALK_SPEED.count()); // cd->speed; + fixed_6d.char_select.species = cd->species; + fixed_6d.char_select.hair_style = cd->hair; + fixed_6d.char_select.weapon = 0; + + fixed_6d.char_select.base_level = cd->base_level; + fixed_6d.char_select.skill_point = cd->skill_point; + + fixed_6d.char_select.head_bottom = ItemNameId(); + fixed_6d.char_select.shield = cd->shield; + fixed_6d.char_select.head_top = cd->head_top; + fixed_6d.char_select.head_mid = cd->head_mid; + fixed_6d.char_select.hair_color = cd->hair_color; + fixed_6d.char_select.misc2 = ItemNameId(); + + fixed_6d.char_select.char_name = ck->name; + + fixed_6d.char_select.stats.str = saturate<uint8_t>(cd->attrs[ATTR::STR]); + fixed_6d.char_select.stats.agi = saturate<uint8_t>(cd->attrs[ATTR::AGI]); + fixed_6d.char_select.stats.vit = saturate<uint8_t>(cd->attrs[ATTR::VIT]); + fixed_6d.char_select.stats.int_ = saturate<uint8_t>(cd->attrs[ATTR::INT]); + fixed_6d.char_select.stats.dex = saturate<uint8_t>(cd->attrs[ATTR::DEX]); + fixed_6d.char_select.stats.luk = saturate<uint8_t>(cd->attrs[ATTR::LUK]); + fixed_6d.char_select.char_num = ck->char_num; + fixed_6d.char_select.unused2 = 0; + + send_fpacket<0x006d, 108>(s, fixed_6d); break; + } case 0x68: // delete char //Yor's Fix - if (!sd || RFIFOREST(s) < 46) - return; { - AccountEmail email = stringish<AccountEmail>(RFIFO_STRING<40>(s, 6)); + Packet_Fixed<0x0068> fixed; + rv = recv_fpacket<0x0068, 46>(s, fixed); + if (rv != RecvResult::Complete) + break; + + if (!sd) + { + s->set_eof(); + return; + } + AccountEmail email = fixed.email; if (!e_mail_check(email)) email = DEFAULT_EMAIL; { { + CharId cid = fixed.char_id; CharPair *cs = nullptr; for (CharPair& cd : char_keys) { - if (cd.key.char_id == RFIFOL(s, 2)) + if (cd.key.char_id == cid) { if (cd.key.account_id == sd->account_id) cs = &cd; @@ -2426,176 +2691,129 @@ void parse_char(Session *s) } char_keys.pop_back(); - WFIFOW(s, 0) = 0x6f; - WFIFOSET(s, 2); + Packet_Fixed<0x006f> fixed_6f; + send_fpacket<0x006f, 2>(s, fixed_6f); goto x68_out; } } { - WFIFOW(s, 0) = 0x70; - WFIFOB(s, 2) = 0; - WFIFOSET(s, 3); + Packet_Fixed<0x0070> fixed_70; + fixed_70.code = 0; + send_fpacket<0x0070, 3>(s, fixed_70); } } - } x68_out: - RFIFOSKIP(s, 46); break; + } case 0x2af8: // マップサーバーログイン - if (RFIFOREST(s) < 60) - return; { + Packet_Fixed<0x2af8> fixed; + rv = recv_fpacket<0x2af8, 60>(s, fixed); + if (rv != RecvResult::Complete) + break; + int i; - WFIFOW(s, 0) = 0x2af9; + Packet_Fixed<0x2af9> fixed_f9; for (i = 0; i < MAX_MAP_SERVERS; i++) { if (!server_session[i]) break; } - AccountName userid_ = stringish<AccountName>(RFIFO_STRING<24>(s, 2)); - AccountPass passwd_ = stringish<AccountPass>(RFIFO_STRING<24>(s, 26)); + AccountName userid_ = fixed.account_name; + AccountPass passwd_ = fixed.account_pass; if (i == MAX_MAP_SERVERS || userid_ != userid || passwd_ != passwd) { - WFIFOB(s, 2) = 3; - WFIFOSET(s, 3); - RFIFOSKIP(s, 60); + fixed_f9.code = 3; + send_fpacket<0x2af9, 3>(s, fixed_f9); } else { - int len; - WFIFOB(s, 2) = 0; - s->set_parsers(SessionParsers{func_parse: parse_frommap, func_delete: delete_frommap}); + fixed_f9.code = 0; + s->set_parsers(SessionParsers{.func_parse= parse_frommap, .func_delete= delete_frommap}); server_session[i] = s; if (anti_freeze_enable) server_freezeflag[i] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed - // ignore RFIFOL(fd, 50) - server[i].ip = RFIFOIP(s, 54); - server[i].port = RFIFOW(s, 58); + // ignore fixed.unknown + server[i].ip = fixed.ip; + server[i].port = fixed.port; server[i].users = 0; for (MapName& mapi : server[i].maps) mapi = MapName(); - WFIFOSET(s, 3); - RFIFOSKIP(s, 60); + send_fpacket<0x2af9, 3>(s, fixed_f9); realloc_fifo(s, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); // send gm acccounts level to map-servers - len = 4; - WFIFOW(s, 0) = 0x2b15; + std::vector<Packet_Repeat<0x2b15>> repeat_15(gm_accounts.size()); + auto it = repeat_15.begin(); for (const GM_Account& gma : gm_accounts) { - WFIFOL(s, len) = gma.account_id; - WFIFOB(s, len + 4) = gma.level; - len += 5; + it->account_id = gma.account_id; + it->gm_level = gma.level; + ++it; } - WFIFOW(s, 2) = len; - WFIFOSET(s, len); + send_packet_repeatonly<0x2b15, 4, 5>(s, repeat_15); + // justification: we switched the session parsers + parse_frommap(s); return; } - } - break; - - case 0x187: // Alive信号? - if (RFIFOREST(s) < 6) - return; - RFIFOSKIP(s, 6); break; + } case 0x7530: // Athena情報所得 - WFIFOW(s, 0) = 0x7531; - WFIFO_STRUCT(s, 2, CURRENT_CHAR_SERVER_VERSION); - WFIFOSET(s, 10); - RFIFOSKIP(s, 2); - return; + { + Packet_Fixed<0x7530> fixed; + rv = recv_fpacket<0x7530, 2>(s, fixed); + if (rv != RecvResult::Complete) + break; + + Packet_Fixed<0x7531> fixed_31; + fixed_31.version = CURRENT_CHAR_SERVER_VERSION; + send_fpacket<0x7531, 10>(s, fixed_31); + break; + } case 0x7532: // 接続の切断(defaultと処理は一緒だが明示的にするため) + { + Packet_Fixed<0x7532> fixed; + rv = recv_fpacket<0x7532, 2>(s, fixed); + if (rv != RecvResult::Complete) + break; + s->set_eof(); return; + } default: s->set_eof(); return; } } -} - -// 全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) -int mapif_sendall(const uint8_t *buf, unsigned int len) -{ - int i, c; - - c = 0; - for (i = 0; i < MAX_MAP_SERVERS; i++) - { - Session *s = server_session[i]; - if (s) - { - WFIFO_BUF_CLONE(s, buf, len); - WFIFOSET(s, len); - c++; - } - } - return c; -} - -// 自分以外の全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) -int mapif_sendallwos(Session *ss, const uint8_t *buf, unsigned int len) -{ - int i, c; - - c = 0; - for (i = 0; i < MAX_MAP_SERVERS; i++) - { - Session *s = server_session[i]; - if (s && s != ss) - { - WFIFO_BUF_CLONE(s, buf, len); - WFIFOSET(s, len); - c++; - } - } - return c; -} - -// MAPサーバーにデータ送信(map鯖生存確認有り) -int mapif_send(Session *s, const uint8_t *buf, unsigned int len) -{ - int i; - - if (s) - { - for (i = 0; i < MAX_MAP_SERVERS; i++) - { - if (s == server_session[i]) - { - WFIFO_BUF_CLONE(s, buf, len); - WFIFOSET(s, len); - return 1; - } - } - } - return 0; + if (rv == RecvResult::Error) + s->set_eof(); } static void send_users_tologin(TimerData *, tick_t) { int users = count_users(); - uint8_t buf[16]; if (login_session) { // send number of user to login server - WFIFOW(login_session, 0) = 0x2714; - WFIFOL(login_session, 2) = users; - WFIFOSET(login_session, 6); + Packet_Fixed<0x2714> fixed_14; + fixed_14.users = users; + send_fpacket<0x2714, 6>(login_session, fixed_14); } // send number of players to all map-servers - WBUFW(buf, 0) = 0x2b00; - WBUFL(buf, 2) = users; - mapif_sendall(buf, 6); + Packet_Fixed<0x2b00> fixed_00; + fixed_00.users = users; + for (Session *ss : iter_map_sessions()) + { + send_fpacket<0x2b00, 6>(ss, fixed_00); + } } static @@ -2603,24 +2821,24 @@ void check_connect_login_server(TimerData *, tick_t) { if (!login_session) { - PRINTF("Attempt to connect to login-server...\n"); + PRINTF("Attempt to connect to login-server...\n"_fmt); login_session = make_connection(login_ip, login_port, - SessionParsers{func_parse: parse_tologin, func_delete: delete_tologin}); + SessionParsers{.func_parse= parse_tologin, .func_delete= delete_tologin}); if (!login_session) return; realloc_fifo(login_session, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); - WFIFOW(login_session, 0) = 0x2710; - WFIFO_ZERO(login_session, 2, 24); - WFIFO_STRING(login_session, 2, userid, 24); - WFIFO_STRING(login_session, 26, passwd, 24); - WFIFOL(login_session, 50) = 0; - WFIFOIP(login_session, 54) = char_ip; - WFIFOL(login_session, 58) = char_port; - WFIFO_STRING(login_session, 60, server_name, 20); - WFIFOW(login_session, 80) = 0; - WFIFOW(login_session, 82) = 0; //char_maintenance; - WFIFOW(login_session, 84) = 0; //char_new; - WFIFOSET(login_session, 86); + + Packet_Fixed<0x2710> fixed_10; + fixed_10.account_name = userid; + fixed_10.account_pass = passwd; + fixed_10.unknown = 0; + fixed_10.ip = char_ip; + fixed_10.port = char_port; + fixed_10.server_name = server_name; + fixed_10.unknown2 = 0; + fixed_10.maintenance = 0; + fixed_10.is_new = 0; + send_fpacket<0x2710, 86>(login_session, fixed_10); } } @@ -2630,14 +2848,14 @@ void check_connect_login_server(TimerData *, tick_t) static bool char_lan_config(XString w1, ZString w2) { - struct hostent *h = NULL; + struct hostent *h = nullptr; { - if (w1 == "lan_map_ip") + if (w1 == "lan_map_ip"_s) { // Read map-server Lan IP Address h = gethostbyname(w2.c_str()); - if (h != NULL) + if (h != nullptr) { lan_map_ip = IP4Address({ static_cast<uint8_t>(h->h_addr[0]), @@ -2648,20 +2866,20 @@ bool char_lan_config(XString w1, ZString w2) } else { - PRINTF("Bad IP value: %s\n", w2); + PRINTF("Bad IP value: %s\n"_fmt, w2); return false; } - PRINTF("LAN IP of map-server: %s.\n", lan_map_ip); + PRINTF("LAN IP of map-server: %s.\n"_fmt, lan_map_ip); } - else if (w1 == "subnet" /*backward compatibility*/ - || w1 == "lan_subnet") + else if (w1 == "subnet"_s /*backward compatibility*/ + || w1 == "lan_subnet"_s) { if (!extract(w2, &lan_subnet)) { - PRINTF("Bad IP mask: %s\n", w2); + PRINTF("Bad IP mask: %s\n"_fmt, w2); return false; } - PRINTF("Sub-network of the map-server: %s.\n", + PRINTF("Sub-network of the map-server: %s.\n"_fmt, lan_subnet); } else @@ -2677,10 +2895,10 @@ bool lan_check() { // sub-network check of the map-server { - PRINTF("LAN test of LAN IP of the map-server: "); + PRINTF("LAN test of LAN IP of the map-server: "_fmt); if (!lan_ip_check(lan_map_ip)) { - PRINTF(SGR_BOLD SGR_RED "***ERROR: LAN IP of the map-server doesn't belong to the specified Sub-network." SGR_RESET "\n"); + PRINTF(SGR_BOLD SGR_RED "***ERROR: LAN IP of the map-server doesn't belong to the specified Sub-network." SGR_RESET "\n"_fmt); return false; } } @@ -2691,27 +2909,27 @@ bool lan_check() static bool char_config(XString w1, ZString w2) { - struct hostent *h = NULL; + struct hostent *h = nullptr; { - if (w1 == "userid") + if (w1 == "userid"_s) userid = stringish<AccountName>(w2); - else if (w1 == "passwd") + else if (w1 == "passwd"_s) passwd = stringish<AccountPass>(w2); - else if (w1 == "server_name") + else if (w1 == "server_name"_s) { server_name = stringish<ServerName>(w2); - PRINTF("%s server has been intialized\n", w2); + PRINTF("%s server has been intialized\n"_fmt, w2); } - else if (w1 == "wisp_server_name") + else if (w1 == "wisp_server_name"_s) { if (w2.size() >= 4) wisp_server_name = stringish<CharName>(w2); } - else if (w1 == "login_ip") + else if (w1 == "login_ip"_s) { h = gethostbyname(w2.c_str()); - if (h != NULL) + if (h != nullptr) { login_ip = IP4Address({ static_cast<uint8_t>(h->h_addr[0]), @@ -2719,23 +2937,23 @@ bool char_config(XString w1, ZString w2) static_cast<uint8_t>(h->h_addr[2]), static_cast<uint8_t>(h->h_addr[3]), }); - PRINTF("Login server IP address : %s -> %s\n", + PRINTF("Login server IP address : %s -> %s\n"_fmt, w2, login_ip); } else { - PRINTF("Bad IP value: %s\n", w2); + PRINTF("Bad IP value: %s\n"_fmt, w2); return false; } } - else if (w1 == "login_port") + else if (w1 == "login_port"_s) { login_port = atoi(w2.c_str()); } - else if (w1 == "char_ip") + else if (w1 == "char_ip"_s) { h = gethostbyname(w2.c_str()); - if (h != NULL) + if (h != nullptr) { char_ip = IP4Address({ static_cast<uint8_t>(h->h_addr[0]), @@ -2743,56 +2961,56 @@ bool char_config(XString w1, ZString w2) static_cast<uint8_t>(h->h_addr[2]), static_cast<uint8_t>(h->h_addr[3]), }); - PRINTF("Character server IP address : %s -> %s\n", + PRINTF("Character server IP address : %s -> %s\n"_fmt, w2, char_ip); } else { - PRINTF("Bad IP value: %s\n", w2); + PRINTF("Bad IP value: %s\n"_fmt, w2); return false; } } - else if (w1 == "char_port") + else if (w1 == "char_port"_s) { char_port = atoi(w2.c_str()); } - else if (w1 == "char_txt") + else if (w1 == "char_txt"_s) { char_txt = w2; } - else if (w1 == "max_connect_user") + else if (w1 == "max_connect_user"_s) { max_connect_user = atoi(w2.c_str()); if (max_connect_user < 0) max_connect_user = 0; // unlimited online players } - else if (w1 == "check_ip_flag") + else if (w1 == "check_ip_flag"_s) { check_ip_flag = config_switch(w2); } - else if (w1 == "autosave_time") + else if (w1 == "autosave_time"_s) { autosave_time = std::chrono::seconds(atoi(w2.c_str())); if (autosave_time <= std::chrono::seconds::zero()) autosave_time = DEFAULT_AUTOSAVE_INTERVAL; } - else if (w1 == "start_point") + else if (w1 == "start_point"_s) { extract(w2, &start_point); } - else if (w1 == "unknown_char_name") + else if (w1 == "unknown_char_name"_s) { unknown_char_name = stringish<CharName>(w2); } - else if (w1 == "char_log_filename") + else if (w1 == "char_log_filename"_s) { char_log_filename = w2; } - else if (w1 == "char_name_option") + else if (w1 == "char_name_option"_s) { char_name_option = atoi(w2.c_str()); } - else if (w1 == "char_name_letters") + else if (w1 == "char_name_letters"_s) { if (!w2) char_name_letters.reset(); @@ -2800,39 +3018,38 @@ bool char_config(XString w1, ZString w2) for (uint8_t c : w2) char_name_letters[c] = true; } - else if (w1 == "online_txt_filename") + else if (w1 == "online_txt_filename"_s) { online_txt_filename = w2; } - else if (w1 == "online_html_filename") + else if (w1 == "online_html_filename"_s) { online_html_filename = w2; } - else if (w1 == "online_sorting_option") + else if (w1 == "online_sorting_option"_s) { online_sorting_option = atoi(w2.c_str()); } - else if (w1 == "online_gm_display_min_level") - { // minimum GM level to display 'GM' when we want to display it - online_gm_display_min_level = atoi(w2.c_str()); - if (online_gm_display_min_level < 5) // send online file every 5 seconds to player is enough - online_gm_display_min_level = 5; + else if (w1 == "online_gm_display_min_level"_s) + { + // minimum GM level to display 'GM' when we want to display it + return extract(w2, &online_gm_display_min_level); } - else if (w1 == "online_refresh_html") + else if (w1 == "online_refresh_html"_s) { online_refresh_html = atoi(w2.c_str()); if (online_refresh_html < 1) online_refresh_html = 1; } - else if (w1 == "anti_freeze_enable") + else if (w1 == "anti_freeze_enable"_s) { anti_freeze_enable = config_switch(w2); } - else if (w1 == "anti_freeze_interval") + else if (w1 == "anti_freeze_interval"_s) { anti_freeze_interval = std::max( std::chrono::seconds(atoi(w2.c_str())), - std::chrono::seconds(5)); + 5_s); } else { @@ -2859,7 +3076,7 @@ void term_func(void) delete_session(login_session); delete_session(char_session); - CHAR_LOG("----End of char-server (normal end with closing of all files).\n"); + CHAR_LOG("----End of char-server (normal end with closing of all files).\n"_fmt); } static @@ -2884,20 +3101,20 @@ int do_init(Slice<ZString> argv) ZString argvi = argv.pop_front(); if (argvi.startswith('-')) { - if (argvi == "--help") + if (argvi == "--help"_s) { - PRINTF("Usage: %s [--help] [--version] [files...]\n", + PRINTF("Usage: %s [--help] [--version] [files...]\n"_fmt, argv0); exit(0); } - else if (argvi == "--version") + else if (argvi == "--version"_s) { - PRINTF("%s\n", CURRENT_VERSION_STRING); + PRINTF("%s\n"_fmt, CURRENT_VERSION_STRING); exit(0); } else { - FPRINTF(stderr, "Unknown argument: %s\n", argvi); + FPRINTF(stderr, "Unknown argument: %s\n"_fmt, argvi); runflag = false; } } @@ -2909,11 +3126,11 @@ int do_init(Slice<ZString> argv) } if (!loaded_config_yet) - runflag &= load_config_file("conf/tmwa-char.conf", char_confs); + runflag &= load_config_file("conf/tmwa-char.conf"_s, char_confs); // a newline in the log... - CHAR_LOG(""); - CHAR_LOG("The char-server starting...\n"); + CHAR_LOG(""_fmt); + CHAR_LOG("The char-server starting...\n"_fmt); runflag &= lan_check(); @@ -2925,13 +3142,13 @@ int do_init(Slice<ZString> argv) char_session = make_listen_port(char_port, SessionParsers{parse_char, delete_char}); - Timer(gettick() + std::chrono::seconds(1), + Timer(gettick() + 1_s, check_connect_login_server, - std::chrono::seconds(10) + 10_s ).detach(); - Timer(gettick() + std::chrono::seconds(1), + Timer(gettick() + 1_s, send_users_tologin, - std::chrono::seconds(5) + 5_s ).detach(); Timer(gettick() + autosave_time, mmo_char_sync_timer, @@ -2940,17 +3157,18 @@ int do_init(Slice<ZString> argv) if (anti_freeze_enable > 0) { - Timer(gettick() + std::chrono::seconds(1), + Timer(gettick() + 1_s, map_anti_freeze_system, anti_freeze_interval ).detach(); } - CHAR_LOG("The char-server is ready (Server is listening on the port %d).\n", - char_port); + CHAR_LOG("The char-server is ready (Server is listening on the port %d).\n"_fmt, + char_port); - PRINTF("The char-server is " SGR_BOLD SGR_GREEN "ready" SGR_RESET " (Server is listening on the port %d).\n\n", - char_port); + PRINTF("The char-server is " SGR_BOLD SGR_GREEN "ready" SGR_RESET " (Server is listening on the port %d).\n\n"_fmt, + char_port); return 0; } +} // namespace tmwa |