diff options
Diffstat (limited to 'src/mmo')
37 files changed, 1221 insertions, 2955 deletions
diff --git a/src/mmo/config_parse.cpp b/src/mmo/config_parse.cpp index b954e8b..8362810 100644 --- a/src/mmo/config_parse.cpp +++ b/src/mmo/config_parse.cpp @@ -18,6 +18,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. +#include <algorithm> + #include "../strings/xstring.hpp" #include "../strings/zstring.hpp" @@ -28,9 +30,12 @@ #include "../poison.hpp" + +namespace tmwa +{ bool is_comment(XString line) { - return not line or line.startswith("//"); + return not line or line.startswith("//"_s); } template<class ZS> @@ -72,7 +77,7 @@ bool load_config_file(ZString filename, ConfigItemParser slave) io::LineReader in(filename); if (!in.is_open()) { - PRINTF("Unable to open file: %s\n", filename); + PRINTF("Unable to open file: %s\n"_fmt, filename); return false; } io::Line line; @@ -85,16 +90,16 @@ bool load_config_file(ZString filename, ConfigItemParser slave) ZString value; if (!config_split(line.text, &key, &value)) { - line.error("Bad config line"); + line.error("Bad config line"_s); rv = false; continue; } - if (key == "import") + if (key == "import"_s) { rv &= load_config_file(value, slave); continue; } - else if (key == "version-lt") + else if (key == "version-lt"_s) { Version vers; if (!extract(value, &vers)) @@ -106,7 +111,7 @@ bool load_config_file(ZString filename, ConfigItemParser slave) continue; break; } - else if (key == "version-le") + else if (key == "version-le"_s) { Version vers; if (!extract(value, &vers)) @@ -118,7 +123,7 @@ bool load_config_file(ZString filename, ConfigItemParser slave) continue; break; } - else if (key == "version-gt") + else if (key == "version-gt"_s) { Version vers; if (!extract(value, &vers)) @@ -130,7 +135,7 @@ bool load_config_file(ZString filename, ConfigItemParser slave) continue; break; } - else if (key == "version-ge") + else if (key == "version-ge"_s) { Version vers; if (!extract(value, &vers)) @@ -144,7 +149,7 @@ bool load_config_file(ZString filename, ConfigItemParser slave) } else if (!slave(key, value)) { - line.error("Bad config key or value"); + line.error("Bad config key or value"_s); rv = false; continue; } @@ -152,3 +157,4 @@ bool load_config_file(ZString filename, ConfigItemParser slave) } return rv; } +} // namespace tmwa diff --git a/src/mmo/config_parse.hpp b/src/mmo/config_parse.hpp index dd1b79e..50a115e 100644 --- a/src/mmo/config_parse.hpp +++ b/src/mmo/config_parse.hpp @@ -1,5 +1,4 @@ -#ifndef TMWA_MMO_CONFIG_PARSE_HPP -#define TMWA_MMO_CONFIG_PARSE_HPP +#pragma once // config_parse.hpp - Framework for per-server config parsers. // // Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> @@ -19,10 +18,13 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -# include "../sanity.hpp" +#include "fwd.hpp" -# include "../strings/fwd.hpp" +#include "../strings/fwd.hpp" + +namespace tmwa +{ typedef bool (*ConfigItemParser)(XString key, ZString value); bool is_comment(XString line); @@ -32,5 +34,4 @@ bool config_split(XString line, XString *key, XString *value); /// Master config parser. This handles 'import' and 'version-ge' etc. /// Then it defers to the inferior parser for a line it does not understand. bool load_config_file(ZString filename, ConfigItemParser slave); - -#endif // TMWA_MMO_CONFIG_PARSE_HPP +} // namespace tmwa diff --git a/src/mmo/consts.cpp b/src/mmo/consts.cpp new file mode 100644 index 0000000..e49cdf5 --- /dev/null +++ b/src/mmo/consts.cpp @@ -0,0 +1,26 @@ +#include "consts.hpp" +// consts.cpp - empty mess of constants +// +// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +#include "../poison.hpp" + + +namespace tmwa +{ +} // namespace tmwa diff --git a/src/mmo/consts.hpp b/src/mmo/consts.hpp new file mode 100644 index 0000000..c1a7eb6 --- /dev/null +++ b/src/mmo/consts.hpp @@ -0,0 +1,66 @@ +#pragma once +// consts.hpp - Huge mess of constants. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +#include "../net/timer.t.hpp" + +#include "ids.hpp" +#include "strs.hpp" + + +namespace tmwa +{ +constexpr int FIFOSIZE_SERVERLINK = 256 * 1024; + +constexpr int MAX_MAP_PER_SERVER = 512; +constexpr int MAX_INVENTORY = 100; +constexpr int MAX_AMOUNT = 30000; +constexpr int MAX_ZENY = 1000000000; // 1G zeny +constexpr int TRADE_MAX = 10; + +constexpr int GLOBAL_REG_NUM = 96; +constexpr size_t ACCOUNT_REG_NUM = 16; +constexpr size_t ACCOUNT_REG2_NUM = 16; +constexpr interval_t DEFAULT_WALK_SPEED = 150_ms; +constexpr interval_t MIN_WALK_SPEED = interval_t::zero(); +constexpr interval_t MAX_WALK_SPEED = 1_s; +constexpr int MAX_STORAGE = 300; +constexpr int MAX_PARTY = 12; + +#define MIN_HAIR_STYLE battle_config.min_hair_style +#define MAX_HAIR_STYLE battle_config.max_hair_style +#define MIN_HAIR_COLOR battle_config.min_hair_color +#define MAX_HAIR_COLOR battle_config.max_hair_color +#define MIN_CLOTH_COLOR battle_config.min_cloth_color +#define MAX_CLOTH_COLOR battle_config.max_cloth_color + +// WTF is this doing here? +struct PartyMember +{ + AccountId account_id; + CharName name; + MapName map; + int leader, online, lv; + struct map_session_data *sd; +}; +} // namespace tmwa diff --git a/src/mmo/core.cpp b/src/mmo/core.cpp index 68b7823..f1a8d07 100644 --- a/src/mmo/core.cpp +++ b/src/mmo/core.cpp @@ -22,23 +22,26 @@ #include <sys/wait.h> -#include <unistd.h> +#include <alloca.h> #include <csignal> #include <cstdlib> -#include <ctime> -#include "../strings/zstring.hpp" +#include <tmwa/shared.hpp> -#include "../generic/random.hpp" +#include "../strings/zstring.hpp" +#include "../strings/literal.hpp" #include "../io/cxxstdio.hpp" -#include "socket.hpp" -#include "timer.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" #include "../poison.hpp" + +namespace tmwa +{ // Added by Gabuzomeu // // This is an implementation of signal() using sigaction() for portability. @@ -61,10 +64,12 @@ sigfunc compat_signal(int signo, sigfunc func) sact.sa_flags = 0; if (sigaction(signo, &sact, &oact) < 0) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" + { + DIAG_PUSH(); + DIAG_I(old_style_cast); return SIG_ERR; -#pragma GCC diagnostic pop + DIAG_POP(); + } return oact.sa_handler; } @@ -75,16 +80,18 @@ bool runflag = true; static void chld_proc(int) { - wait(NULL); + wait(nullptr); } static void sig_proc(int) { for (int i = 1; i < 31; ++i) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" + { + DIAG_PUSH(); + DIAG_I(old_style_cast); compat_signal(i, SIG_IGN); -#pragma GCC diagnostic pop + DIAG_POP(); + } runflag = false; } @@ -97,8 +104,14 @@ void sig_proc(int) Unless you use SA_SIGINFO and *carefully* check the origin, that means they must be SIG_DFL. */ +} // namespace tmwa + int main(int argc, char **argv) { + using namespace tmwa; + + check_paths(); + // ZString args[argc]; is (deliberately!) not supported by clang yet ZString *args = static_cast<ZString *>(alloca(argc * sizeof(ZString))); for (int i = 0; i < argc; ++i) @@ -107,29 +120,30 @@ int main(int argc, char **argv) if (!runflag) { - PRINTF("Fatal error during startup; exiting\n"); + PRINTF("Fatal error during startup; exiting\n"_fmt); return 1; } // set up exit handlers *after* the initialization has happened. // This is because term_func is likely to depend on successful init. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" + DIAG_PUSH(); + DIAG_I(old_style_cast); compat_signal(SIGPIPE, SIG_IGN); -#pragma GCC diagnostic pop + DIAG_POP(); compat_signal(SIGTERM, sig_proc); compat_signal(SIGINT, sig_proc); compat_signal(SIGCHLD, chld_proc); // Signal to create coredumps by system when necessary (crash) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" + DIAG_PUSH(); + DIAG_I(old_style_cast); + DIAG_I(zero_as_null_pointer_constant); compat_signal(SIGSEGV, SIG_DFL); compat_signal(SIGBUS, SIG_DFL); compat_signal(SIGTRAP, SIG_DFL); compat_signal(SIGILL, SIG_DFL); compat_signal(SIGFPE, SIG_DFL); -#pragma GCC diagnostic pop + DIAG_POP(); atexit(term_func); diff --git a/src/mmo/core.hpp b/src/mmo/core.hpp index 5699045..5b2dbbb 100644 --- a/src/mmo/core.hpp +++ b/src/mmo/core.hpp @@ -1,5 +1,4 @@ -#ifndef TMWA_MMO_CORE_HPP -#define TMWA_MMO_CORE_HPP +#pragma once // core.hpp - The main loop. // // Copyright © ????-2004 Athena Dev Teams @@ -21,12 +20,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -# include "../sanity.hpp" +#include "fwd.hpp" -# include "../range/slice.hpp" +#include "../range/slice.hpp" -# include "../strings/fwd.hpp" +#include "../strings/fwd.hpp" + +namespace tmwa +{ /// core.c contains a server-independent main() function /// and then runs a do_sendrecv loop @@ -40,5 +42,4 @@ extern int do_init(Slice<ZString>); /// Cleanup function called whenever a signal kills us /// or when if we manage to exit() gracefully. extern void term_func(void); - -#endif // TMWA_MMO_CORE_HPP +} // namespace tmwa diff --git a/src/mmo/dumb_ptr.hpp b/src/mmo/dumb_ptr.hpp deleted file mode 100644 index 9632945..0000000 --- a/src/mmo/dumb_ptr.hpp +++ /dev/null @@ -1,274 +0,0 @@ -#ifndef TMWA_MMO_DUMB_PTR_HPP -#define TMWA_MMO_DUMB_PTR_HPP -// ptr.hpp - temporary new/delete wrappers -// -// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com> -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -# include "../sanity.hpp" - -# include <cstring> - -# include <algorithm> - -# include "../strings/astring.hpp" -# include "../strings/zstring.hpp" -# include "../strings/xstring.hpp" - -// unmanaged new/delete-able pointer -// should be replaced by std::unique_ptr<T> -template<class T> -class dumb_ptr -{ - template<class U> - friend class dumb_ptr; - T *impl; -public: - explicit - dumb_ptr(T *p=nullptr) - : impl(p) - {} - template<class U> - dumb_ptr(dumb_ptr<U> p) - : impl(p.impl) - {} - dumb_ptr(std::nullptr_t) - : impl(nullptr) - {} - - void delete_() - { - delete impl; - *this = nullptr; - } - template<class... A> - void new_(A&&... a) - { - impl = new T(std::forward<A>(a)...); - } - template<class... A> - static - dumb_ptr<T> make(A&&... a) - { - return dumb_ptr<T>(new T(std::forward<A>(a)...)); - } - dumb_ptr& operator = (std::nullptr_t) - { - impl = nullptr; - return *this; - } - - T& operator *() const - { - return *impl; - } - T *operator->() const - { - return impl; - } - - explicit - operator bool() const - { - return impl; - } - bool operator !() const - { - return !impl; - } - - friend bool operator == (dumb_ptr l, dumb_ptr r) - { - return l.impl == r.impl; - } - friend bool operator != (dumb_ptr l, dumb_ptr r) - { - return !(l == r); - } -}; - -// unmanaged new/delete-able pointer -// should be replaced by std::unique_ptr<T[]> or std::vector<T> -template<class T> -class dumb_ptr<T[]> -{ - T *impl; - size_t sz; -public: - dumb_ptr() : impl(), sz() {} - dumb_ptr(std::nullptr_t) - : impl(nullptr), sz(0) {} - dumb_ptr(T *p, size_t z) - : impl(p) - , sz(z) - {} - - void delete_() - { - delete[] impl; - *this = nullptr; - } - void new_(size_t z) - { - impl = new T[z](); - sz = z; - } - static - dumb_ptr<T[]> make(size_t z) - { - return dumb_ptr<T[]>(new T[z](), z); - } - dumb_ptr& operator = (std::nullptr_t) - { - impl = nullptr; - sz = 0; - return *this; - } - - size_t size() const - { - return sz; - } - void resize(size_t z) - { - if (z == sz) - return; - T *np = new T[z](); - // not exception-safe, but we don't have a dtor anyway - size_t i = std::min(z, sz); - while (i-->0) - np[i] = std::move(impl[i]); - delete[] impl; - impl = np; - sz = z; - } - - T& operator[](size_t i) const - { - return impl[i]; - } - - explicit - operator bool() const - { - return impl; - } - bool operator !() const - { - return !impl; - } - - friend bool operator == (dumb_ptr l, dumb_ptr r) - { - return l.impl == r.impl; - } - friend bool operator != (dumb_ptr l, dumb_ptr r) - { - return !(l == r); - } -}; - -struct dumb_string -{ - dumb_ptr<char[]> impl; - - dumb_string() - : impl() - {} - dumb_string(char *) = delete; - // copy ctor, copy assign, and dtor are all default - - static dumb_string copy(const char *b, const char *e) - { - dumb_string rv; - rv.impl.new_((e - b) + 1); - std::copy(b, e, &rv.impl[0]); - return rv; - } - static dumb_string copy(const char *sz) - { - return dumb_string::copy(sz, sz + strlen(sz)); - } - static dumb_string copys(XString s) - { - return dumb_string::copy(&*s.begin(), &*s.end()); - } - static -# ifndef __clang__ - __attribute__((warning("shouldn't use this - slice instead"))) -# endif - dumb_string copyn(const char *sn, size_t n) - { - return dumb_string::copy(sn, sn + strnlen(sn, n)); - } - - static - dumb_string fake(ZString p) - { - dumb_string rv; - size_t len = p.size(); - rv.impl = dumb_ptr<char[]>(const_cast<char *>(p.c_str()), len); - return rv; - } - - dumb_string dup() const - { - return dumb_string::copy(&impl[0]); - } - void delete_() - { - impl.delete_(); - } - - const char *c_str() const - { - return &impl[0]; - } - - operator ZString() const - { - return ZString(strings::really_construct_from_a_pointer, c_str(), nullptr); - } - - AString str() const - { - return ZString(*this); - } - - char& operator[](size_t i) const - { - return impl[i]; - } - - explicit - operator bool() const - { - return bool(impl); - } - bool operator !() const - { - return !impl; - } -}; - -inline -const char *convert_for_printf(dumb_string ds) -{ - return ds.c_str(); -} - -#endif // TMWA_MMO_DUMB_PTR_HPP diff --git a/src/mmo/enums.cpp b/src/mmo/enums.cpp new file mode 100644 index 0000000..d05be91 --- /dev/null +++ b/src/mmo/enums.cpp @@ -0,0 +1,26 @@ +#include "enums.hpp" +// enums.cpp - Common enumerated types +// +// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +#include "../poison.hpp" + + +namespace tmwa +{ +} // namespace tmwa diff --git a/src/mmo/enums.hpp b/src/mmo/enums.hpp new file mode 100644 index 0000000..bf8a75c --- /dev/null +++ b/src/mmo/enums.hpp @@ -0,0 +1,162 @@ +#pragma once +// enums.hpp - Common enumerated types +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +#include <cstdint> + +#include "../generic/enum.hpp" + + +namespace tmwa +{ +enum class SkillID : uint16_t; +constexpr SkillID MAX_SKILL = SkillID(474); // not 450 +constexpr SkillID get_enum_min_value(SkillID) { return SkillID(); } +constexpr SkillID get_enum_max_value(SkillID) { return MAX_SKILL; } + +namespace e +{ +enum class EPOS : uint16_t +{ + ZERO = 0x0000, + + LEGS = 0x0001, + WEAPON = 0x0002, + GLOVES = 0x0004, + CAPE = 0x0008, + MISC1 = 0x0010, + SHIELD = 0x0020, + SHOES = 0x0040, + MISC2 = 0x0080, + HAT = 0x0100, + TORSO = 0x0200, + + ARROW = 0x8000, +}; +ENUM_BITWISE_OPERATORS(EPOS) + +constexpr EPOS get_enum_min_value(EPOS) { return EPOS(0x0000); } +constexpr EPOS get_enum_max_value(EPOS) { return EPOS(0xffff); } +} +using e::EPOS; + +namespace e +{ +enum class SkillFlags : uint16_t; +} +using e::SkillFlags; + +// Option and Opt1..3 in map.hpp +namespace e +{ +enum class Option : uint16_t; +constexpr Option get_enum_min_value(Option) { return Option(0x0000); } +constexpr Option get_enum_max_value(Option) { return Option(0xffff); } +} +using e::Option; + +enum class ATTR +{ + STR = 0, + AGI = 1, + VIT = 2, + INT = 3, + DEX = 4, + LUK = 5, + + COUNT = 6, +}; + +constexpr ATTR ATTRs[6] = +{ + ATTR::STR, + ATTR::AGI, + ATTR::VIT, + ATTR::INT, + ATTR::DEX, + ATTR::LUK, +}; + +enum class ItemLook : uint16_t +{ + NONE = 0, + BLADE = 1, // or some other common weapons + _2, + SETZER_AND_SCYTHE = 3, + _6, + STAFF = 10, + BOW = 11, + _13 = 13, + _14 = 14, + _16 = 16, + SINGLE_HANDED_COUNT = 17, + + DUAL_BLADE = 0x11, + DUAL_2 = 0x12, + DUAL_6 = 0x13, + DUAL_12 = 0x14, + DUAL_16 = 0x15, + DUAL_26 = 0x16, +}; + +enum class SEX : uint8_t +{ + FEMALE = 0, + MALE = 1, + // For items. This is also used as error, sometime. + NEUTRAL = 2, +}; +inline +char sex_to_char(SEX sex) +{ + switch (sex) + { + case SEX::FEMALE: return 'F'; + case SEX::MALE: return 'M'; + default: return '\0'; + } +} +inline +SEX sex_from_char(char c) +{ + switch (c) + { + case 'F': return SEX::FEMALE; + case 'M': return SEX::MALE; + default: return SEX::NEUTRAL; + } +} + +inline +bool native_to_network(char *network, SEX native) +{ + *network = sex_to_char(native); + return true; +} +inline +bool network_to_native(SEX *native, char network) +{ + *native = sex_from_char(network); + return true; +} +} // namespace tmwa diff --git a/src/mmo/extract.cpp b/src/mmo/extract.cpp index 378986d..d486ed5 100644 --- a/src/mmo/extract.cpp +++ b/src/mmo/extract.cpp @@ -18,12 +18,19 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. +#include <algorithm> + #include "../strings/astring.hpp" #include "../strings/xstring.hpp" #include "../strings/vstring.hpp" +#include "mmo.hpp" + #include "../poison.hpp" + +namespace tmwa +{ bool extract(XString str, XString *rv) { *rv = str; @@ -36,20 +43,21 @@ bool extract(XString str, AString *rv) return true; } -bool extract(XString str, struct global_reg *var) +bool extract(XString str, GlobalReg *var) { return extract(str, record<','>(&var->str, &var->value)); } -bool extract(XString str, struct item *it) +bool extract(XString str, Item *it) { XString ignored; - return extract(str, + XString corruption_hack_amount; + bool rv = extract(str, record<',', 11>( - &it->id, + &ignored, &it->nameid, - &it->amount, + &corruption_hack_amount, &it->equip, &ignored, &ignored, @@ -59,4 +67,34 @@ bool extract(XString str, struct item *it) &ignored, &ignored, &ignored)); + if (rv) + { + if (corruption_hack_amount == "-1"_s) + it->amount = 0; + else + rv = extract(corruption_hack_amount, &it->amount); + } + return rv; +} + +bool extract(XString str, MapName *m) +{ + XString::iterator it = std::find(str.begin(), str.end(), '.'); + str = str.xislice_h(it); + VString<15> tmp; + bool rv = extract(str, &tmp); + *m = tmp; + return rv; +} + +bool extract(XString str, CharName *out) +{ + VString<23> tmp; + if (extract(str, &tmp)) + { + *out = CharName(tmp); + return true; + } + return false; } +} // namespace tmwa diff --git a/src/mmo/extract.hpp b/src/mmo/extract.hpp index 622281b..355e2da 100644 --- a/src/mmo/extract.hpp +++ b/src/mmo/extract.hpp @@ -1,5 +1,4 @@ -#ifndef TMWA_MMO_EXTRACT_HPP -#define TMWA_MMO_EXTRACT_HPP +#pragma once // extract.hpp - a simple, hierarchical, tokenizer // // Copyright © 2012-2013 Ben Longbons <b.r.longbons@gmail.com> @@ -19,14 +18,27 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -# include "../sanity.hpp" +#include "fwd.hpp" -# include <algorithm> +#include <cerrno> +#include <cstdlib> -# include "../strings/xstring.hpp" +#include <algorithm> +#include <vector> -# include "mmo.hpp" -# include "utils.hpp" +#include "../ints/wrap.hpp" + +#include "../strings/xstring.hpp" + +#include "../generic/enum.hpp" + +#include "utils.hpp" + + +namespace tmwa +{ +template<class T> +bool do_extract(XString str, T t); template<class T, typename=typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value && !std::is_same<T, bool>::value>::type> bool extract(XString str, T *iv) @@ -94,6 +106,12 @@ bool extract(XString str, VString<N> *out) return true; } +inline +bool extract(XString str, LString exact) +{ + return str == exact; +} + template<class T> class LStripper { @@ -192,31 +210,23 @@ bool extract(XString str, VRecord<split, T> rec) && extract(str.xislice_t(s + 1), rec); } -bool extract(XString str, struct global_reg *var); +bool extract(XString str, GlobalReg *var); -bool extract(XString str, struct item *it); +bool extract(XString str, Item *it); -inline -bool extract(XString str, MapName *m) -{ - XString::iterator it = std::find(str.begin(), str.end(), '.'); - str = str.xislice_h(it); - VString<15> tmp; - bool rv = extract(str, &tmp); - *m = tmp; - return rv; -} +bool extract(XString str, MapName *m); -inline -bool extract(XString str, CharName *out) +bool extract(XString str, CharName *out); + +template<class T> +bool do_extract(XString str, T t) { - VString<23> tmp; - if (extract(str, &tmp)) - { - *out = CharName(tmp); - return true; - } - return false; + return extract(str, t); } -#endif // TMWA_MMO_EXTRACT_HPP +template<class R> +bool extract(XString str, Wrapped<R> *w) +{ + return extract(str, &w->_value); +} +} // namespace tmwa diff --git a/src/mmo/extract_test.cpp b/src/mmo/extract_test.cpp index 60ab49e..e6dc7b2 100644 --- a/src/mmo/extract_test.cpp +++ b/src/mmo/extract_test.cpp @@ -22,160 +22,165 @@ #include "../strings/xstring.hpp" +#include "mmo.hpp" + #include "../poison.hpp" + +namespace tmwa +{ TEST(extract, record_int) { int x, y, z; x = y = z = 0; - EXPECT_FALSE(extract("1 2 3 4 ", record<' '>(&x, &y, &z))); + EXPECT_FALSE(extract("1 2 3 4 "_s, record<' '>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_FALSE(extract("1 2 3 4", record<' '>(&x, &y, &z))); + EXPECT_FALSE(extract("1 2 3 4"_s, record<' '>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_TRUE(extract("1 2 3 ", record<' '>(&x, &y, &z))); + EXPECT_TRUE(extract("1 2 3 "_s, record<' '>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_TRUE(extract("1 2 3", record<' '>(&x, &y, &z))); + EXPECT_TRUE(extract("1 2 3"_s, record<' '>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_FALSE(extract("1 2 ", record<' '>(&x, &y, &z))); + EXPECT_FALSE(extract("1 2 "_s, record<' '>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract("1 2", record<' '>(&x, &y, &z))); + EXPECT_FALSE(extract("1 2"_s, record<' '>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract("1 ", record<' '>(&x, &y, &z))); + EXPECT_FALSE(extract("1 "_s, record<' '>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract("1", record<' '>(&x, &y, &z))); + EXPECT_FALSE(extract("1"_s, record<' '>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract(" ", record<' '>(&x, &y, &z))); + EXPECT_FALSE(extract(" "_s, record<' '>(&x, &y, &z))); EXPECT_EQ(0, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract("", record<' '>(&x, &y, &z))); + EXPECT_FALSE(extract(""_s, record<' '>(&x, &y, &z))); EXPECT_EQ(0, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 2>(&x, &y, &z))); + EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 2>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_FALSE(extract("1 2 3 4", record<' ', 2>(&x, &y, &z))); + EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 2>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_TRUE(extract("1 2 3 ", record<' ', 2>(&x, &y, &z))); + EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 2>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_TRUE(extract("1 2 3", record<' ', 2>(&x, &y, &z))); + EXPECT_TRUE(extract("1 2 3"_s, record<' ', 2>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_TRUE(extract("1 2 ", record<' ', 2>(&x, &y, &z))); + EXPECT_TRUE(extract("1 2 "_s, record<' ', 2>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_TRUE(extract("1 2", record<' ', 2>(&x, &y, &z))); + EXPECT_TRUE(extract("1 2"_s, record<' ', 2>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract("1 ", record<' ', 2>(&x, &y, &z))); + EXPECT_FALSE(extract("1 "_s, record<' ', 2>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract("1", record<' ', 2>(&x, &y, &z))); + EXPECT_FALSE(extract("1"_s, record<' ', 2>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract(" ", record<' ', 2>(&x, &y, &z))); + EXPECT_FALSE(extract(" "_s, record<' ', 2>(&x, &y, &z))); EXPECT_EQ(0, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract("", record<' ', 2>(&x, &y, &z))); + EXPECT_FALSE(extract(""_s, record<' ', 2>(&x, &y, &z))); EXPECT_EQ(0, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 1>(&x, &y, &z))); + EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 1>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_FALSE(extract("1 2 3 4", record<' ', 1>(&x, &y, &z))); + EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 1>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_TRUE(extract("1 2 3 ", record<' ', 1>(&x, &y, &z))); + EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 1>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_TRUE(extract("1 2 3", record<' ', 1>(&x, &y, &z))); + EXPECT_TRUE(extract("1 2 3"_s, record<' ', 1>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(3, z); x = y = z = 0; - EXPECT_TRUE(extract("1 2 ", record<' ', 1>(&x, &y, &z))); + EXPECT_TRUE(extract("1 2 "_s, record<' ', 1>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_TRUE(extract("1 2", record<' ', 1>(&x, &y, &z))); + EXPECT_TRUE(extract("1 2"_s, record<' ', 1>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(2, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_TRUE(extract("1 ", record<' ', 1>(&x, &y, &z))); + EXPECT_TRUE(extract("1 "_s, record<' ', 1>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_TRUE(extract("1", record<' ', 1>(&x, &y, &z))); + EXPECT_TRUE(extract("1"_s, record<' ', 1>(&x, &y, &z))); EXPECT_EQ(1, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract(" ", record<' ', 1>(&x, &y, &z))); + EXPECT_FALSE(extract(" "_s, record<' ', 1>(&x, &y, &z))); EXPECT_EQ(0, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); x = y = z = 0; - EXPECT_FALSE(extract("", record<' ', 1>(&x, &y, &z))); + EXPECT_FALSE(extract(""_s, record<' ', 1>(&x, &y, &z))); EXPECT_EQ(0, x); EXPECT_EQ(0, y); EXPECT_EQ(0, z); @@ -185,170 +190,171 @@ TEST(extract, record_int) TEST(extract, record_str) { XString x, y, z; - x = y = z = ""; - EXPECT_FALSE(extract("1 2 3 4 ", record<' '>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_FALSE(extract("1 2 3 4", record<' '>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 2 3 ", record<' '>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 2 3", record<' '>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 2 ", record<' '>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_FALSE(extract("1 2", record<' '>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_FALSE(extract("1 ", record<' '>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_FALSE(extract("1", record<' '>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_FALSE(extract(" ", record<' '>(&x, &y, &z))); - EXPECT_EQ("", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_FALSE(extract("", record<' '>(&x, &y, &z))); - EXPECT_EQ("", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; + x = y = z = ""_s; + EXPECT_FALSE(extract("1 2 3 4 "_s, record<' '>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_FALSE(extract("1 2 3 4"_s, record<' '>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 2 3 "_s, record<' '>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 2 3"_s, record<' '>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 2 "_s, record<' '>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_FALSE(extract("1 2"_s, record<' '>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_FALSE(extract("1 "_s, record<' '>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_FALSE(extract("1"_s, record<' '>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_FALSE(extract(" "_s, record<' '>(&x, &y, &z))); + EXPECT_EQ(""_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_FALSE(extract(""_s, record<' '>(&x, &y, &z))); + EXPECT_EQ(""_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; - EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 2>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_FALSE(extract("1 2 3 4", record<' ', 2>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 2 3 ", record<' ', 2>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 2 3", record<' ', 2>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 2 ", record<' ', 2>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 2", record<' ', 2>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 ", record<' ', 2>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_FALSE(extract("1", record<' ', 2>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_TRUE(extract(" ", record<' ', 2>(&x, &y, &z))); - EXPECT_EQ("", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_FALSE(extract("", record<' ', 2>(&x, &y, &z))); - EXPECT_EQ("", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; + EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 2 3"_s, record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 2 "_s, record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 2"_s, record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 "_s, record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_FALSE(extract("1"_s, record<' ', 2>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract(" "_s, record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(""_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_FALSE(extract(""_s, record<' ', 2>(&x, &y, &z))); + EXPECT_EQ(""_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; - EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 1>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_FALSE(extract("1 2 3 4", record<' ', 1>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 2 3 ", record<' ', 1>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 2 3", record<' ', 1>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("3", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 2 ", record<' ', 1>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 2", record<' ', 1>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("2", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_TRUE(extract("1 ", record<' ', 1>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_TRUE(extract("1", record<' ', 1>(&x, &y, &z))); - EXPECT_EQ("1", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_TRUE(extract(" ", record<' ', 1>(&x, &y, &z))); - EXPECT_EQ("", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; - EXPECT_TRUE(extract("", record<' ', 1>(&x, &y, &z))); - EXPECT_EQ("", x); - EXPECT_EQ("", y); - EXPECT_EQ("", z); - x = y = z = ""; + EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 2 3"_s, record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ("3"_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 2 "_s, record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 2"_s, record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ("2"_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1 "_s, record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract("1"_s, record<' ', 1>(&x, &y, &z))); + EXPECT_EQ("1"_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract(" "_s, record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(""_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; + EXPECT_TRUE(extract(""_s, record<' ', 1>(&x, &y, &z))); + EXPECT_EQ(""_s, x); + EXPECT_EQ(""_s, y); + EXPECT_EQ(""_s, z); + x = y = z = ""_s; } TEST(extract, mapname) { MapName map; - EXPECT_TRUE(extract("abc", &map)); - EXPECT_EQ(map, "abc"); - EXPECT_TRUE(extract("abc.gat", &map)); - EXPECT_EQ(map, "abc"); - EXPECT_TRUE(extract("abcdefghijklmno", &map)); - EXPECT_EQ(map, "abcdefghijklmno"); - EXPECT_TRUE(extract("abcdefghijklmno.gat", &map)); - EXPECT_EQ(map, "abcdefghijklmno"); + EXPECT_TRUE(extract("abc"_s, &map)); + EXPECT_EQ(map, "abc"_s); + EXPECT_TRUE(extract("abc.gat"_s, &map)); + EXPECT_EQ(map, "abc"_s); + EXPECT_TRUE(extract("abcdefghijklmno"_s, &map)); + EXPECT_EQ(map, "abcdefghijklmno"_s); + EXPECT_TRUE(extract("abcdefghijklmno.gat"_s, &map)); + EXPECT_EQ(map, "abcdefghijklmno"_s); } +} // namespace tmwa diff --git a/src/mmo/fwd.hpp b/src/mmo/fwd.hpp new file mode 100644 index 0000000..3b56bfb --- /dev/null +++ b/src/mmo/fwd.hpp @@ -0,0 +1,68 @@ +#pragma once +// mmo/fwd.hpp - list of type names for mmo lib +// +// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +#include "../sanity.hpp" + + +namespace tmwa +{ +// meh, add more when I feel like it +class MapName; +class CharName; +class CharPair; + +class HumanTimeDiff; + +class AccountId; +class CharId; +class PartyId; +class ItemUnkId; +class ItemNameId; +class GmLevel; + +class AccountName; +class AccountPass; +class AccountCrypt; +class AccountEmail; +class ServerName; +class PartyName; +class VarName; +class MapName; +class CharName; + +class Item; +#if 0 +class Point; +class SkillValue; +#endif +class GlobalReg; +#if 0 +class CharKey; +class CharData; +class CharPair; +#endif +class Storage; +#if 0 +class GM_Account; +class PartyMember; +#endif +class PartyMost; +class PartyPair; +} // namespace tmwa diff --git a/src/mmo/human_time_diff.cpp b/src/mmo/human_time_diff.cpp index f2f720e..49a7664 100644 --- a/src/mmo/human_time_diff.cpp +++ b/src/mmo/human_time_diff.cpp @@ -19,3 +19,8 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "../poison.hpp" + + +namespace tmwa +{ +} // namespace tmwa diff --git a/src/mmo/human_time_diff.hpp b/src/mmo/human_time_diff.hpp index 689b8d9..b5c19fb 100644 --- a/src/mmo/human_time_diff.hpp +++ b/src/mmo/human_time_diff.hpp @@ -1,5 +1,4 @@ -#ifndef TMWA_MMO_HUMAN_TIME_DIFF_HPP -#define TMWA_MMO_HUMAN_TIME_DIFF_HPP +#pragma once // human_time_diff.hpp - broken deltas // // Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com> @@ -19,12 +18,17 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -# include "../sanity.hpp" +#include "fwd.hpp" -# include "../strings/xstring.hpp" +#include <algorithm> -# include "extract.hpp" +#include "../strings/xstring.hpp" +#include "extract.hpp" + + +namespace tmwa +{ struct HumanTimeDiff { short year, month, day, hour, minute, second; @@ -61,26 +65,25 @@ bool extract(XString str, HumanTimeDiff *iv) str = str.xislice_t(it2); short *ptr = nullptr; - if (suffix == "y" || suffix == "a") + if (suffix == "y"_s || suffix == "a"_s) ptr = &iv->year; - else if (suffix == "m") + else if (suffix == "m"_s) ptr = &iv->month; - else if (suffix == "j" || suffix == "d") + else if (suffix == "j"_s || suffix == "d"_s) ptr = &iv->day; - else if (suffix == "h") + else if (suffix == "h"_s) ptr = &iv->hour; - else if (suffix == "mn") + else if (suffix == "mn"_s) ptr = &iv->minute; - else if (suffix == "s") + else if (suffix == "s"_s) ptr = &iv->second; else return false; - if (number.startswith('+') && !number.startswith("+-")) + if (number.startswith('+') && !number.startswith("+-"_s)) number = number.xslice_t(1); if (*ptr || !extract(number, ptr)) return false; } return true; } - -#endif // TMWA_MMO_HUMAN_TIME_DIFF_HPP +} // namespace tmwa diff --git a/src/mmo/human_time_diff_test.cpp b/src/mmo/human_time_diff_test.cpp index 138849b..c18599d 100644 --- a/src/mmo/human_time_diff_test.cpp +++ b/src/mmo/human_time_diff_test.cpp @@ -1,5 +1,5 @@ #include "human_time_diff.hpp" -// human_time_diff.hpp - Testwuite for broken deltas +// human_time_diff_test.cpp - Testwuite for broken deltas // // Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com> // @@ -22,13 +22,16 @@ #include "../poison.hpp" + +namespace tmwa +{ // a sequence of [-+]?[0-9]+([ay]|m|[jd]|h|mn|s) TEST(humantimediff, single) { HumanTimeDiff diff; - EXPECT_TRUE(extract("42y", &diff)); + EXPECT_TRUE(extract("42y"_s, &diff)); EXPECT_EQ(42, diff.year); EXPECT_EQ(0, diff.month); EXPECT_EQ(0, diff.day); @@ -36,7 +39,7 @@ TEST(humantimediff, single) EXPECT_EQ(0, diff.minute); EXPECT_EQ(0, diff.second); - EXPECT_TRUE(extract("42m", &diff)); + EXPECT_TRUE(extract("42m"_s, &diff)); EXPECT_EQ(0, diff.year); EXPECT_EQ(42, diff.month); EXPECT_EQ(0, diff.day); @@ -44,7 +47,7 @@ TEST(humantimediff, single) EXPECT_EQ(0, diff.minute); EXPECT_EQ(0, diff.second); - EXPECT_TRUE(extract("42d", &diff)); + EXPECT_TRUE(extract("42d"_s, &diff)); EXPECT_EQ(0, diff.year); EXPECT_EQ(0, diff.month); EXPECT_EQ(42, diff.day); @@ -52,7 +55,7 @@ TEST(humantimediff, single) EXPECT_EQ(0, diff.minute); EXPECT_EQ(0, diff.second); - EXPECT_TRUE(extract("42h", &diff)); + EXPECT_TRUE(extract("42h"_s, &diff)); EXPECT_EQ(0, diff.year); EXPECT_EQ(0, diff.month); EXPECT_EQ(0, diff.day); @@ -60,7 +63,7 @@ TEST(humantimediff, single) EXPECT_EQ(0, diff.minute); EXPECT_EQ(0, diff.second); - EXPECT_TRUE(extract("42mn", &diff)); + EXPECT_TRUE(extract("42mn"_s, &diff)); EXPECT_EQ(0, diff.year); EXPECT_EQ(0, diff.month); EXPECT_EQ(0, diff.day); @@ -68,7 +71,7 @@ TEST(humantimediff, single) EXPECT_EQ(42, diff.minute); EXPECT_EQ(0, diff.second); - EXPECT_TRUE(extract("42s", &diff)); + EXPECT_TRUE(extract("42s"_s, &diff)); EXPECT_EQ(0, diff.year); EXPECT_EQ(0, diff.month); EXPECT_EQ(0, diff.day); @@ -76,28 +79,29 @@ TEST(humantimediff, single) EXPECT_EQ(0, diff.minute); EXPECT_EQ(42, diff.second); - EXPECT_TRUE(extract("+42y", &diff)); + EXPECT_TRUE(extract("+42y"_s, &diff)); EXPECT_EQ(42, diff.year); - EXPECT_TRUE(extract("-42y", &diff)); + EXPECT_TRUE(extract("-42y"_s, &diff)); EXPECT_EQ(-42, diff.year); - EXPECT_FALSE(extract("++42y", &diff)); - EXPECT_FALSE(extract("+-42y", &diff)); - EXPECT_FALSE(extract("-+42y", &diff)); - EXPECT_FALSE(extract("--42y", &diff)); - EXPECT_FALSE(extract("4+2y", &diff)); - EXPECT_FALSE(extract("42z", &diff)); + EXPECT_FALSE(extract("++42y"_s, &diff)); + EXPECT_FALSE(extract("+-42y"_s, &diff)); + EXPECT_FALSE(extract("-+42y"_s, &diff)); + EXPECT_FALSE(extract("--42y"_s, &diff)); + EXPECT_FALSE(extract("4+2y"_s, &diff)); + EXPECT_FALSE(extract("42z"_s, &diff)); } TEST(humantimediff, multiple) { HumanTimeDiff diff; - EXPECT_TRUE(extract("42y23m-2d", &diff)); + EXPECT_TRUE(extract("42y23m-2d"_s, &diff)); EXPECT_EQ(42, diff.year); EXPECT_EQ(23, diff.month); EXPECT_EQ(-2, diff.day); EXPECT_EQ(0, diff.hour); EXPECT_EQ(0, diff.minute); EXPECT_EQ(0, diff.second); - EXPECT_FALSE(extract("1y2y", &diff)); + EXPECT_FALSE(extract("1y2y"_s, &diff)); } +} // namespace tmwa diff --git a/src/mmo/ids.cpp b/src/mmo/ids.cpp new file mode 100644 index 0000000..d40d5c3 --- /dev/null +++ b/src/mmo/ids.cpp @@ -0,0 +1,26 @@ +#include "ids.hpp" +// ids.cpp - special integer classes for various object IDs +// +// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +#include "../poison.hpp" + + +namespace tmwa +{ +} // namespace tmwa diff --git a/src/mmo/ids.hpp b/src/mmo/ids.hpp new file mode 100644 index 0000000..4e2b97c --- /dev/null +++ b/src/mmo/ids.hpp @@ -0,0 +1,168 @@ +#pragma once +// ids.hpp - special integer classes for various object IDs +// +// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +#include "../ints/little.hpp" +#include "../ints/wrap.hpp" + +#include "extract.hpp" + + +namespace tmwa +{ +class Species : public Wrapped<uint16_t> { public: explicit operator bool() const = delete; bool operator !() const = delete; constexpr Species() : Wrapped<uint16_t>() {} protected: constexpr explicit Species(uint16_t a) : Wrapped<uint16_t>(a) {} }; + +constexpr Species NEGATIVE_SPECIES = Species(); + +inline +bool extract(XString str, Species *w) +{ + // lots of data files use this + if (str == "-1"_s) + { + *w = NEGATIVE_SPECIES; + return true; + } + return extract(str, &w->_value); +} + + +class AccountId : public Wrapped<uint32_t> { public: constexpr AccountId() : Wrapped<uint32_t>() {} protected: constexpr explicit AccountId(uint32_t a) : Wrapped<uint32_t>(a) {} }; +class CharId : public Wrapped<uint32_t> { public: constexpr CharId() : Wrapped<uint32_t>() {} protected: constexpr explicit CharId(uint32_t a) : Wrapped<uint32_t>(a) {} }; +// important note: slave mobs synthesize PartyId as -BlockId of master +class PartyId : public Wrapped<uint32_t> { public: constexpr PartyId() : Wrapped<uint32_t>() {} protected: constexpr explicit PartyId(uint32_t a) : Wrapped<uint32_t>(a) {} }; +class ItemNameId : public Wrapped<uint16_t> { public: constexpr ItemNameId() : Wrapped<uint16_t>() {} protected: constexpr explicit ItemNameId(uint16_t a) : Wrapped<uint16_t>(a) {} }; + +class BlockId : public Wrapped<uint32_t> { public: constexpr BlockId() : Wrapped<uint32_t>() {} protected: constexpr explicit BlockId(uint32_t a) : Wrapped<uint32_t>(a) {} }; + +class GmLevel +{ + uint32_t bits; + + friend bool extract(XString str, GmLevel *lvl) { return extract(str, &lvl->bits); } + constexpr explicit + GmLevel(uint32_t b) : bits(b) {} + constexpr explicit + operator uint32_t() const { return bits; } + + template<class T> + explicit + GmLevel(T) = delete; + template<class T, typename=typename std::enable_if<!std::is_same<T, uint32_t>::value && !std::is_same<T, bool>::value>::type> + explicit + operator T() = delete; +public: + constexpr + GmLevel() : bits() {} + constexpr static + GmLevel from(uint32_t bits) { return GmLevel(bits); } + template<class T> + constexpr static + GmLevel from(T) = delete; + + constexpr explicit + operator bool() const { return bits; } + constexpr + bool operator !() const { return !bits; } + + // the argument is the level of a command + constexpr + bool satisfies(GmLevel perm) const { return bits >= perm.bits; } + // the argument is another player's gm level, for info commands + constexpr + bool detects(GmLevel other) const { return bits >= other.bits; } + // the argument is another player's gm level, for aggressive commands + constexpr + bool overwhelms(GmLevel other) const { return bits >= other.bits; } + // the argument is another potential permission level + constexpr + bool obsoletes(GmLevel plvl) const { return bits >= plvl.bits; } + + constexpr + uint16_t get_public_word() const + { + return (bits == 60 || bits == 99) ? 0x0080 : 0; + } + + constexpr + uint32_t get_all_bits() const + { + return bits; + } + + friend constexpr + bool operator == (GmLevel l, GmLevel r) + { + return l.bits == r.bits; + } + friend constexpr + bool operator != (GmLevel l, GmLevel r) + { + return l.bits != r.bits; + } + + friend + bool native_to_network(Byte *network, GmLevel native) + { + network->value = native.bits; + return true; // LIES. But this code is going away soon anyway + } + friend + bool network_to_native(GmLevel *native, Byte network) + { + native->bits = network.value; + return true; // LIES. But this code is going away soon anyway + } + + // TODO kill this code too + friend + bool native_to_network(Little16 *network, GmLevel native) + { + uint16_t tmp = native.bits; + return native_to_network(network, tmp); + } + friend + bool network_to_native(GmLevel *native, Little16 network) + { + uint16_t tmp; + bool rv = network_to_native(&tmp, network); + native->bits = tmp; + return rv; + } + + friend + bool native_to_network(Little32 *network, GmLevel native) + { + return native_to_network(network, native.bits); + } + friend + bool network_to_native(GmLevel *native, Little32 network) + { + return network_to_native(&native->bits, network); + } +}; + +inline +uint32_t convert_for_printf(GmLevel g) +{ + return g.get_all_bits(); +} +} // namespace tmwa diff --git a/src/mmo/ip.cpp b/src/mmo/ip.cpp deleted file mode 100644 index 146734a..0000000 --- a/src/mmo/ip.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "ip.hpp" -// ip.cpp - Implementation of IP address functions. -// -// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com> -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -#include "../strings/xstring.hpp" -#include "../strings/vstring.hpp" - -#include "../io/cxxstdio.hpp" - -#include "../poison.hpp" - -bool extract(XString str, IP4Address *rv) -{ - if (str.endswith('.')) - return false; - uint8_t buf[4]; - if (extract(str, record<'.'>(&buf[0], &buf[1], &buf[2], &buf[3]))) - { - *rv = IP4Address(buf); - return true; - } - return false; -} - -bool extract(XString str, IP4Mask *rv) -{ - IP4Address a, m; - unsigned b; - XString l, r; - if (str.endswith('/')) - return false; - if (extract(str, record<'/'>(&l, &r))) - { - // a.b.c.d/e.f.g.h or a.b.c.d/n - if (!extract(l, &a)) - return false; - if (extract(r, &m)) - { - *rv = IP4Mask(a, m); - return true; - } - if (!extract(r, &b) || b > 32) - return false; - } - else - { - // a.b.c.d or a.b.c.d. or a.b.c. or a.b. or a. - if (extract(str, &a)) - { - *rv = IP4Mask(a, IP4_BROADCAST); - return true; - } - if (!str.endswith('.')) - return false; - uint8_t d[4] {}; - if (extract(str, record<'.'>(&d[0], &d[1], &d[2], &d[3]))) - b = 32; - else if (extract(str, record<'.'>(&d[0], &d[1], &d[2]))) - b = 24; - else if (extract(str, record<'.'>(&d[0], &d[1]))) - b = 16; - else if (extract(str, record<'.'>(&d[0]))) - b = 8; - else - return false; - a = IP4Address(d); - } - // a is set; need to construct m from b - if (b == 0) - m = IP4Address(); - else if (b == 32) - m = IP4_BROADCAST; - else - { - uint32_t s = -1; - s <<= (32 - b); - m = IP4Address({ - static_cast<uint8_t>(s >> 24), - static_cast<uint8_t>(s >> 16), - static_cast<uint8_t>(s >> 8), - static_cast<uint8_t>(s >> 0), - }); - } - *rv = IP4Mask(a, m); - return true; -} - -VString<15> convert_for_printf(IP4Address a_) -{ - const uint8_t *a = a_.bytes(); - return STRNPRINTF(16, "%hhu.%hhu.%hhu.%hhu", a[0], a[1], a[2], a[3]); -} - -VString<31> convert_for_printf(IP4Mask a) -{ - return STRNPRINTF(32, "%s/%s", - a.addr(), a.mask()); -} diff --git a/src/mmo/ip.hpp b/src/mmo/ip.hpp deleted file mode 100644 index a425710..0000000 --- a/src/mmo/ip.hpp +++ /dev/null @@ -1,164 +0,0 @@ -#ifndef TMWA_MMO_IP_HPP -#define TMWA_MMO_IP_HPP -// ip.hpp - classes to deal with IP addresses. -// -// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com> -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -# include "../sanity.hpp" - -# include <netinet/in.h> - -# include "../strings/fwd.hpp" - -# include "extract.hpp" - -// TODO - in the long run ports belong here also -// and of course, IPv6 stuff. -// But what about unix socket addresses? - -/// Helper function -template<class T, size_t n> -constexpr -bool _ce_a_lt(T (&a)[n], T (&b)[n], size_t i=0) -{ - return (i != n - && (a[i] < b[i] - || (a[i] == b[i] - && _ce_a_lt(a, b, i + 1)))); -} - -/// A 32-bit Ipv4 address. Does not include a port. -/// Guaranteed to be laid out like the network wants. -class IP4Address -{ - uint8_t _addr[4]; -public: - constexpr - IP4Address() - : _addr{} - {} - constexpr explicit - IP4Address(const uint8_t (&a)[4]) - : _addr{a[0], a[1], a[2], a[3]} - {} - explicit - IP4Address(struct in_addr addr) - { - static_assert(sizeof(addr) == sizeof(_addr), "4 bytes"); - *this = IP4Address(reinterpret_cast<const uint8_t (&)[4]>(addr)); - } - explicit - operator struct in_addr() const - { - return reinterpret_cast<const struct in_addr&>(_addr); - } - - constexpr friend - IP4Address operator & (IP4Address l, IP4Address r) - { - return IP4Address({ - static_cast<uint8_t>(l._addr[0] & r._addr[0]), - static_cast<uint8_t>(l._addr[1] & r._addr[1]), - static_cast<uint8_t>(l._addr[2] & r._addr[2]), - static_cast<uint8_t>(l._addr[3] & r._addr[3]), - }); - } - - IP4Address& operator &= (IP4Address m) - { return *this = *this & m; } - - const uint8_t *bytes() const - { return _addr; } - - constexpr friend - bool operator < (IP4Address l, IP4Address r) - { - return _ce_a_lt(l._addr, r._addr); - } - - constexpr friend - bool operator > (IP4Address l, IP4Address r) - { - return _ce_a_lt(r._addr, l._addr); - } - - constexpr friend - bool operator >= (IP4Address l, IP4Address r) - { - return !_ce_a_lt(l._addr, r._addr); - } - - constexpr friend - bool operator <= (IP4Address l, IP4Address r) - { - return !_ce_a_lt(r._addr, l._addr); - } - - constexpr friend - bool operator == (IP4Address l, IP4Address r) - { - return !(l < r || r < l); - } - - constexpr friend - bool operator != (IP4Address l, IP4Address r) - { - return (l < r || r < l); - } -}; - -class IP4Mask -{ - IP4Address _addr, _mask; -public: - constexpr - IP4Mask() : _addr(), _mask() - {} - constexpr - IP4Mask(IP4Address a, IP4Address m) : _addr(a & m), _mask(m) - {} - - constexpr - IP4Address addr() const - { return _addr; } - constexpr - IP4Address mask() const - { return _mask; } - - constexpr - bool covers(IP4Address a) const - { - return (a & _mask) == _addr; - } -}; - - -constexpr -IP4Address IP4_LOCALHOST({127, 0, 0, 1}); -constexpr -IP4Address IP4_BROADCAST({255, 255, 255, 255}); - - -VString<15> convert_for_printf(IP4Address a); -VString<31> convert_for_printf(IP4Mask m); - -bool extract(XString str, IP4Address *iv); - -bool extract(XString str, IP4Mask *iv); - -#endif // TMWA_MMO_IP_HPP diff --git a/src/mmo/ip.py b/src/mmo/ip.py deleted file mode 100644 index e6a8183..0000000 --- a/src/mmo/ip.py +++ /dev/null @@ -1,14 +0,0 @@ -class IP4Address(object): - ''' print an IP4Address - ''' - __slots__ = ('_value') - name = 'IP4Address' - enabled = True - - def __init__(self, value): - self._value = value - - def to_string(self): - addr = self._value['_addr'] - addr = tuple(int(addr[i]) for i in range(4)) - return '%d.%d.%d.%d' % addr diff --git a/src/mmo/ip_test.cpp b/src/mmo/ip_test.cpp deleted file mode 100644 index 8e50453..0000000 --- a/src/mmo/ip_test.cpp +++ /dev/null @@ -1,352 +0,0 @@ -#include "ip.hpp" -// ip_test.cpp - Testsuite for implementation of IP address functions. -// -// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com> -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -#include <gtest/gtest.h> - -#include "../io/cxxstdio.hpp" - -#include "../poison.hpp" - -#define CB(X) (std::integral_constant<bool, (X)>::value) -TEST(ip4addr, cmp) -{ - constexpr static - IP4Address a = IP4_LOCALHOST; - constexpr static - IP4Address b = IP4_BROADCAST; - - EXPECT_FALSE(CB(a < a)); - EXPECT_TRUE (CB(a < b)); - EXPECT_FALSE(CB(b < a)); - EXPECT_FALSE(CB(b < b)); - - EXPECT_FALSE(CB(a > a)); - EXPECT_FALSE(CB(a > b)); - EXPECT_TRUE (CB(b > a)); - EXPECT_FALSE(CB(b > b)); - - EXPECT_TRUE (CB(a <= a)); - EXPECT_TRUE (CB(a <= b)); - EXPECT_FALSE(CB(b <= a)); - EXPECT_TRUE (CB(b <= b)); - - EXPECT_TRUE (CB(a >= a)); - EXPECT_FALSE(CB(a >= b)); - EXPECT_TRUE (CB(b >= a)); - EXPECT_TRUE (CB(b >= b)); - - EXPECT_TRUE (CB(a == a)); - EXPECT_FALSE(CB(a == b)); - EXPECT_FALSE(CB(b == a)); - EXPECT_TRUE (CB(b == b)); - - EXPECT_FALSE(CB(a != a)); - EXPECT_TRUE (CB(a != b)); - EXPECT_TRUE (CB(b != a)); - EXPECT_FALSE(CB(b != b)); -} - -TEST(ip4addr, str) -{ - IP4Address a; - EXPECT_EQ("0.0.0.0", STRNPRINTF(17, "%s", a)); - EXPECT_EQ("127.0.0.1", STRNPRINTF(17, "%s", IP4_LOCALHOST)); - EXPECT_EQ("255.255.255.255", STRNPRINTF(17, "%s", IP4_BROADCAST)); -} - -TEST(ip4addr, extract) -{ - IP4Address a; - EXPECT_TRUE(extract("0.0.0.0", &a)); - EXPECT_EQ("0.0.0.0", STRNPRINTF(16, "%s", a)); - EXPECT_TRUE(extract("127.0.0.1", &a)); - EXPECT_EQ("127.0.0.1", STRNPRINTF(16, "%s", a)); - EXPECT_TRUE(extract("255.255.255.255", &a)); - EXPECT_EQ("255.255.255.255", STRNPRINTF(16, "%s", a)); - EXPECT_TRUE(extract("1.2.3.4", &a)); - EXPECT_EQ("1.2.3.4", STRNPRINTF(16, "%s", a)); - - EXPECT_FALSE(extract("1.2.3.4.5", &a)); - EXPECT_FALSE(extract("1.2.3.4.", &a)); - EXPECT_FALSE(extract("1.2.3.", &a)); - EXPECT_FALSE(extract("1.2.3", &a)); - EXPECT_FALSE(extract("1.2.", &a)); - EXPECT_FALSE(extract("1.2", &a)); - EXPECT_FALSE(extract("1.", &a)); - EXPECT_FALSE(extract("1", &a)); - EXPECT_FALSE(extract("", &a)); -} - - -TEST(ip4mask, body) -{ - IP4Mask m; - EXPECT_EQ(IP4Address(), m.addr()); - EXPECT_EQ(IP4Address(), m.mask()); - m = IP4Mask(IP4_LOCALHOST, IP4_BROADCAST); - EXPECT_EQ(IP4_LOCALHOST, m.addr()); - EXPECT_EQ(IP4_BROADCAST, m.mask()); -} - -TEST(ip4mask, str) -{ - IP4Mask m; - EXPECT_EQ("0.0.0.0/0.0.0.0", STRNPRINTF(33, "%s", m)); - m = IP4Mask(IP4_LOCALHOST, IP4_BROADCAST); - EXPECT_EQ("127.0.0.1/255.255.255.255", STRNPRINTF(33, "%s", m)); -} - -TEST(ip4mask, extract) -{ - IP4Mask m; - EXPECT_FALSE(extract("9.8.7.6/33", &m)); - EXPECT_FALSE(extract("9.8.7.6.5", &m)); - EXPECT_FALSE(extract("9.8.7.6/", &m)); - EXPECT_FALSE(extract("9.8.7", &m)); - EXPECT_FALSE(extract("9.8", &m)); - EXPECT_FALSE(extract("9", &m)); - - EXPECT_TRUE(extract("127.0.0.1", &m)); - EXPECT_EQ("127.0.0.1/255.255.255.255", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("127.0.0.1.", &m)); - EXPECT_EQ("127.0.0.1/255.255.255.255", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("127.0.0.", &m)); - EXPECT_EQ("127.0.0.0/255.255.255.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("127.0.", &m)); - EXPECT_EQ("127.0.0.0/255.255.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("127.", &m)); - EXPECT_EQ("127.0.0.0/255.0.0.0", STRNPRINTF(32, "%s", m)); - - EXPECT_TRUE(extract("1.2.3.4/255.255.255.255", &m)); - EXPECT_EQ("1.2.3.4/255.255.255.255", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("1.2.3.0/255.255.255.0", &m)); - EXPECT_EQ("1.2.3.0/255.255.255.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("1.2.0.4/255.255.0.255", &m)); - EXPECT_EQ("1.2.0.4/255.255.0.255", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("1.2.0.0/255.255.0.0", &m)); - EXPECT_EQ("1.2.0.0/255.255.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("1.0.3.4/255.0.255.255", &m)); - EXPECT_EQ("1.0.3.4/255.0.255.255", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("1.0.3.0/255.0.255.0", &m)); - EXPECT_EQ("1.0.3.0/255.0.255.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("1.0.0.4/255.0.0.255", &m)); - EXPECT_EQ("1.0.0.4/255.0.0.255", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("1.0.0.0/255.0.0.0", &m)); - EXPECT_EQ("1.0.0.0/255.0.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.2.3.4/0.255.255.255", &m)); - EXPECT_EQ("0.2.3.4/0.255.255.255", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.2.3.0/0.255.255.0", &m)); - EXPECT_EQ("0.2.3.0/0.255.255.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.2.0.4/0.255.0.255", &m)); - EXPECT_EQ("0.2.0.4/0.255.0.255", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.2.0.0/0.255.0.0", &m)); - EXPECT_EQ("0.2.0.0/0.255.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.3.4/0.0.255.255", &m)); - EXPECT_EQ("0.0.3.4/0.0.255.255", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.3.0/0.0.255.0", &m)); - EXPECT_EQ("0.0.3.0/0.0.255.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.4/0.0.0.255", &m)); - EXPECT_EQ("0.0.0.4/0.0.0.255", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/0.0.0.0", &m)); - EXPECT_EQ("0.0.0.0/0.0.0.0", STRNPRINTF(32, "%s", m)); - - // please don't do this - EXPECT_TRUE(extract("120.248.200.217/89.57.126.5", &m)); - EXPECT_EQ("88.56.72.1/89.57.126.5", STRNPRINTF(32, "%s", m)); - - EXPECT_TRUE(extract("0.0.0.0/32", &m)); - EXPECT_EQ("0.0.0.0/255.255.255.255", STRNPRINTF(32, "%s", m)); - - EXPECT_TRUE(extract("0.0.0.0/31", &m)); - EXPECT_EQ("0.0.0.0/255.255.255.254", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/30", &m)); - EXPECT_EQ("0.0.0.0/255.255.255.252", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/29", &m)); - EXPECT_EQ("0.0.0.0/255.255.255.248", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/28", &m)); - EXPECT_EQ("0.0.0.0/255.255.255.240", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/27", &m)); - EXPECT_EQ("0.0.0.0/255.255.255.224", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/26", &m)); - EXPECT_EQ("0.0.0.0/255.255.255.192", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/25", &m)); - EXPECT_EQ("0.0.0.0/255.255.255.128", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/24", &m)); - EXPECT_EQ("0.0.0.0/255.255.255.0", STRNPRINTF(32, "%s", m)); - - EXPECT_TRUE(extract("0.0.0.0/23", &m)); - EXPECT_EQ("0.0.0.0/255.255.254.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/22", &m)); - EXPECT_EQ("0.0.0.0/255.255.252.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/21", &m)); - EXPECT_EQ("0.0.0.0/255.255.248.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/20", &m)); - EXPECT_EQ("0.0.0.0/255.255.240.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/19", &m)); - EXPECT_EQ("0.0.0.0/255.255.224.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/18", &m)); - EXPECT_EQ("0.0.0.0/255.255.192.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/17", &m)); - EXPECT_EQ("0.0.0.0/255.255.128.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/16", &m)); - EXPECT_EQ("0.0.0.0/255.255.0.0", STRNPRINTF(32, "%s", m)); - - EXPECT_TRUE(extract("0.0.0.0/15", &m)); - EXPECT_EQ("0.0.0.0/255.254.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/14", &m)); - EXPECT_EQ("0.0.0.0/255.252.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/13", &m)); - EXPECT_EQ("0.0.0.0/255.248.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/12", &m)); - EXPECT_EQ("0.0.0.0/255.240.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/11", &m)); - EXPECT_EQ("0.0.0.0/255.224.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/10", &m)); - EXPECT_EQ("0.0.0.0/255.192.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/9", &m)); - EXPECT_EQ("0.0.0.0/255.128.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/8", &m)); - EXPECT_EQ("0.0.0.0/255.0.0.0", STRNPRINTF(32, "%s", m)); - - EXPECT_TRUE(extract("0.0.0.0/7", &m)); - EXPECT_EQ("0.0.0.0/254.0.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/6", &m)); - EXPECT_EQ("0.0.0.0/252.0.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/5", &m)); - EXPECT_EQ("0.0.0.0/248.0.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/4", &m)); - EXPECT_EQ("0.0.0.0/240.0.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/3", &m)); - EXPECT_EQ("0.0.0.0/224.0.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/2", &m)); - EXPECT_EQ("0.0.0.0/192.0.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/1", &m)); - EXPECT_EQ("0.0.0.0/128.0.0.0", STRNPRINTF(32, "%s", m)); - EXPECT_TRUE(extract("0.0.0.0/0", &m)); - EXPECT_EQ("0.0.0.0/0.0.0.0", STRNPRINTF(32, "%s", m)); -} - -TEST(ip4mask, cover) -{ - IP4Address a; - IP4Address b = IP4_BROADCAST; - IP4Address l = IP4_LOCALHOST; - IP4Address h({127, 255, 255, 255}); - IP4Address p24l({10, 0, 0, 0}); - IP4Address p24h({10, 255, 255, 255}); - IP4Address p20l({172, 16, 0, 0}); - IP4Address p20h({172, 31, 255, 255}); - IP4Address p16l({192, 168, 0, 0}); - IP4Address p16h({192, 168, 255, 255}); - IP4Mask m; - EXPECT_TRUE(m.covers(a)); - EXPECT_TRUE(m.covers(b)); - EXPECT_TRUE(m.covers(l)); - EXPECT_TRUE(m.covers(h)); - EXPECT_TRUE(m.covers(p24l)); - EXPECT_TRUE(m.covers(p24h)); - EXPECT_TRUE(m.covers(p20l)); - EXPECT_TRUE(m.covers(p20h)); - EXPECT_TRUE(m.covers(p16l)); - EXPECT_TRUE(m.covers(p16h)); - m = IP4Mask(l, a); - EXPECT_TRUE(m.covers(a)); - EXPECT_TRUE(m.covers(b)); - EXPECT_TRUE(m.covers(l)); - EXPECT_TRUE(m.covers(h)); - EXPECT_TRUE(m.covers(p24l)); - EXPECT_TRUE(m.covers(p24h)); - EXPECT_TRUE(m.covers(p20l)); - EXPECT_TRUE(m.covers(p20h)); - EXPECT_TRUE(m.covers(p16l)); - EXPECT_TRUE(m.covers(p16h)); - m = IP4Mask(l, b); - EXPECT_FALSE(m.covers(a)); - EXPECT_FALSE(m.covers(b)); - EXPECT_TRUE(m.covers(l)); - EXPECT_FALSE(m.covers(h)); - EXPECT_FALSE(m.covers(p24l)); - EXPECT_FALSE(m.covers(p24h)); - EXPECT_FALSE(m.covers(p20l)); - EXPECT_FALSE(m.covers(p20h)); - EXPECT_FALSE(m.covers(p16l)); - EXPECT_FALSE(m.covers(p16h)); - - // but the really useful ones are with partial masks - m = IP4Mask(IP4Address({10, 0, 0, 0}), IP4Address({255, 0, 0, 0})); - EXPECT_FALSE(m.covers(a)); - EXPECT_FALSE(m.covers(b)); - EXPECT_FALSE(m.covers(l)); - EXPECT_FALSE(m.covers(h)); - EXPECT_TRUE(m.covers(p24l)); - EXPECT_TRUE(m.covers(p24h)); - EXPECT_FALSE(m.covers(p20l)); - EXPECT_FALSE(m.covers(p20h)); - EXPECT_FALSE(m.covers(p16l)); - EXPECT_FALSE(m.covers(p16h)); - EXPECT_FALSE(m.covers(IP4Address({9, 255, 255, 255}))); - EXPECT_FALSE(m.covers(IP4Address({11, 0, 0, 0}))); - m = IP4Mask(IP4Address({127, 0, 0, 0}), IP4Address({255, 0, 0, 0})); - EXPECT_FALSE(m.covers(a)); - EXPECT_FALSE(m.covers(b)); - EXPECT_TRUE(m.covers(l)); - EXPECT_TRUE(m.covers(h)); - EXPECT_FALSE(m.covers(p24l)); - EXPECT_FALSE(m.covers(p24h)); - EXPECT_FALSE(m.covers(p20l)); - EXPECT_FALSE(m.covers(p20h)); - EXPECT_FALSE(m.covers(p16l)); - EXPECT_FALSE(m.covers(p16h)); - EXPECT_FALSE(m.covers(IP4Address({126, 255, 255, 255}))); - EXPECT_FALSE(m.covers(IP4Address({128, 0, 0, 0}))); - m = IP4Mask(IP4Address({172, 16, 0, 0}), IP4Address({255, 240, 0, 0})); - EXPECT_FALSE(m.covers(a)); - EXPECT_FALSE(m.covers(b)); - EXPECT_FALSE(m.covers(l)); - EXPECT_FALSE(m.covers(h)); - EXPECT_FALSE(m.covers(p24l)); - EXPECT_FALSE(m.covers(p24h)); - EXPECT_TRUE(m.covers(p20l)); - EXPECT_TRUE(m.covers(p20h)); - EXPECT_FALSE(m.covers(p16l)); - EXPECT_FALSE(m.covers(p16h)); - EXPECT_FALSE(m.covers(IP4Address({172, 15, 255, 255}))); - EXPECT_FALSE(m.covers(IP4Address({172, 32, 0, 0}))); - m = IP4Mask(IP4Address({192, 168, 0, 0}), IP4Address({255, 255, 0, 0})); - EXPECT_FALSE(m.covers(a)); - EXPECT_FALSE(m.covers(b)); - EXPECT_FALSE(m.covers(l)); - EXPECT_FALSE(m.covers(h)); - EXPECT_FALSE(m.covers(p24l)); - EXPECT_FALSE(m.covers(p24h)); - EXPECT_FALSE(m.covers(p20l)); - EXPECT_FALSE(m.covers(p20h)); - EXPECT_TRUE(m.covers(p16l)); - EXPECT_TRUE(m.covers(p16h)); - EXPECT_FALSE(m.covers(IP4Address({192, 167, 255, 255}))); - EXPECT_FALSE(m.covers(IP4Address({192, 169, 0, 0}))); - - // OTOH this is crazy - EXPECT_TRUE(extract("120.248.200.217/89.57.126.5", &m)); - EXPECT_TRUE(m.covers(IP4Address({120, 248, 200, 217}))); - EXPECT_TRUE(m.covers(IP4Address({88, 56, 72, 1}))); - EXPECT_FALSE(m.covers(IP4Address({88, 56, 72, 0}))); - EXPECT_FALSE(m.covers(IP4Address({88, 56, 72, 255}))); -} diff --git a/src/mmo/md5more.cpp b/src/mmo/md5more.cpp index 3fce5c7..4e5d2da 100644 --- a/src/mmo/md5more.cpp +++ b/src/mmo/md5more.cpp @@ -20,14 +20,24 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. +#include <algorithm> + #include "../compat/rawmem.hpp" #include "../generic/random.hpp" #include "../io/cxxstdio.hpp" +#include "../io/read.hpp" + +#include "../net/ip.hpp" + +#include "../mmo/mmo.hpp" #include "../poison.hpp" + +namespace tmwa +{ #define X block.data // TODO - refactor MD5 into a stream, and merge the implementations @@ -103,7 +113,7 @@ AccountCrypt MD5_saltcrypt(AccountPass key, SaltString salt) VString<31> obuf; // This truncates the string, but we have to keep it like that for compatibility - SNPRINTF(obuf, 32, "!%s$%s", salt, tbuf3); + SNPRINTF(obuf, 32, "!%s$%s"_fmt, salt, tbuf3); return stringish<AccountCrypt>(obuf); } @@ -134,7 +144,7 @@ IP4Address MD5_ip(IP4Address ip) // MD5sum a secret + the IP address VString<31> ipbuf; - SNPRINTF(ipbuf, 32, "%s %s", ip, secret); + SNPRINTF(ipbuf, 32, "%s %s"_fmt, ip, secret); md5_binary obuf; MD5_to_bin(MD5_from_string(ipbuf), obuf); @@ -146,3 +156,4 @@ IP4Address MD5_ip(IP4Address ip) static_cast<uint8_t>(obuf[6] ^ obuf[7] ^ obuf[14] ^ obuf[15]), }); } +} // namespace tmwa diff --git a/src/mmo/md5more.hpp b/src/mmo/md5more.hpp index b1da783..7d50713 100644 --- a/src/mmo/md5more.hpp +++ b/src/mmo/md5more.hpp @@ -1,5 +1,4 @@ -#ifndef TMWA_MMO_MD5MORE_HPP -#define TMWA_MMO_MD5MORE_HPP +#pragma once // md5more.hpp - Non-basic MD5 functions. // // Copyright © ????-2004 Athena Dev Teams @@ -21,15 +20,17 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -# include "../sanity.hpp" +#include "fwd.hpp" -# include "../generic/md5.hpp" +#include "../generic/md5.hpp" -# include "../io/read.hpp" +#include "../io/fwd.hpp" -# include "ip.hpp" -# include "mmo.hpp" +#include "../net/fwd.hpp" + +namespace tmwa +{ MD5_state MD5_from_FILE(io::ReadFile& in); // whoever wrote this fails basic understanding of @@ -44,5 +45,4 @@ bool pass_ok(AccountPass password, AccountCrypt crypted); /// This returns an IP4Address because it is configurable whether it gets called at all IP4Address MD5_ip(IP4Address ip); - -#endif // TMWA_MMO_MD5MORE_HPP +} // namespace tmwa diff --git a/src/mmo/mmo.cpp b/src/mmo/mmo.cpp index 8bf7edf..aafa431 100644 --- a/src/mmo/mmo.cpp +++ b/src/mmo/mmo.cpp @@ -19,3 +19,8 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "../poison.hpp" + + +namespace tmwa +{ +} // namespace tmwa diff --git a/src/mmo/mmo.hpp b/src/mmo/mmo.hpp index c9d62ca..cfa278d 100644 --- a/src/mmo/mmo.hpp +++ b/src/mmo/mmo.hpp @@ -1,6 +1,5 @@ -#ifndef TMWA_MMO_MMO_HPP -#define TMWA_MMO_MMO_HPP -// mmo.hpp - Huge mess of structures and constants. +#pragma once +// mmo.hpp - Huge mess of structures. // // Copyright © ????-2004 Athena Dev Teams // Copyright © 2004-2011 The Mana World Development Team @@ -21,347 +20,25 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -# include "../sanity.hpp" +#include "fwd.hpp" -# include "../compat/memory.hpp" +#include "../compat/memory.hpp" -# include "../strings/vstring.hpp" +#include "../proto2/types.hpp" -# include "../generic/enum.hpp" -# include "timer.t.hpp" - -// affects CharName -# define NAME_IGNORING_CASE 1 - -constexpr int FIFOSIZE_SERVERLINK = 256 * 1024; - -constexpr int MAX_MAP_PER_SERVER = 512; -constexpr int MAX_INVENTORY = 100; -constexpr int MAX_AMOUNT = 30000; -constexpr int MAX_ZENY = 1000000000; // 1G zeny -constexpr int TRADE_MAX = 10; - -enum class SkillID : uint16_t; -constexpr SkillID MAX_SKILL = SkillID(474); // not 450 -constexpr SkillID get_enum_min_value(SkillID) { return SkillID(); } -constexpr SkillID get_enum_max_value(SkillID) { return MAX_SKILL; } - -constexpr int GLOBAL_REG_NUM = 96; -constexpr int ACCOUNT_REG_NUM = 16; -constexpr int ACCOUNT_REG2_NUM = 16; -constexpr interval_t DEFAULT_WALK_SPEED = std::chrono::milliseconds(150); -constexpr interval_t MIN_WALK_SPEED = interval_t::zero(); -constexpr interval_t MAX_WALK_SPEED = std::chrono::seconds(1); -constexpr int MAX_STORAGE = 300; -constexpr int MAX_PARTY = 12; - -# define MIN_HAIR_STYLE battle_config.min_hair_style -# define MAX_HAIR_STYLE battle_config.max_hair_style -# define MIN_HAIR_COLOR battle_config.min_hair_color -# define MAX_HAIR_COLOR battle_config.max_hair_color -# define MIN_CLOTH_COLOR battle_config.min_cloth_color -# define MAX_CLOTH_COLOR battle_config.max_cloth_color - -template<class T, size_t n> -struct Array +namespace tmwa { - T data[n]; -public: - T& operator [](size_t i) { assert (i < n); return data[i]; } - const T& operator [](size_t i) const { assert (i < n); return data[i]; } - - T *begin() { return data + 0; } - T *end() { return data + n; } - const T *begin() const { return data + 0; } - const T *end() const { return data + n; } -}; - -struct AccountName : VString<23> {}; -struct AccountPass : VString<23> {}; -struct AccountCrypt : VString<39> {}; -struct AccountEmail : VString<39> {}; -struct ServerName : VString<19> {}; -struct PartyName : VString<23> {}; -struct VarName : VString<31> {}; - -# define DEFAULT_EMAIL stringish<AccountEmail>("a@a.com") - -// It is decreed: a mapname shall not contain an extension -class MapName : public strings::_crtp_string<MapName, MapName, strings::ZPair> -{ - VString<15> _impl; -public: - MapName() = default; - MapName(VString<15> v) : _impl(v.xislice_h(std::find(v.begin(), v.end(), '.'))) {} - - iterator begin() const { return &*_impl.begin(); } - iterator end() const { return &*_impl.end(); } - const char *c_str() const { return _impl.c_str(); } - - operator RString() const { return _impl; } - operator AString() const { return _impl; } - operator TString() const { return _impl; } - operator SString() const { return _impl; } - operator ZString() const { return _impl; } - operator XString() const { return _impl; } -}; -template<> inline -MapName stringish<MapName>(VString<15> iv) +bool operator == (const SkillValue& l, const SkillValue& r) { - return iv; + return l.lv == r.lv && l.flags == r.flags; } inline -const char *decay_for_printf(const MapName& vs) { return vs.c_str(); } - -// It is decreed: a charname is sometimes case sensitive -struct CharName +bool operator != (const SkillValue& l, const SkillValue& r) { -private: - VString<23> _impl; -public: - CharName() = default; - explicit CharName(VString<23> name) - : _impl(name) - {} - - VString<23> to__actual() const - { - return _impl; - } - VString<23> to__lower() const - { - return _impl.to_lower(); - } - VString<23> to__upper() const - { - return _impl.to_upper(); - } - VString<23> to__canonical() const - { -# if NAME_IGNORING_CASE == 0 - return to__actual(); -# endif -# if NAME_IGNORING_CASE == 1 - return to__lower(); -# endif - } - - friend bool operator == (const CharName& l, const CharName& r) - { return l.to__canonical() == r.to__canonical(); } - friend bool operator != (const CharName& l, const CharName& r) - { return l.to__canonical() != r.to__canonical(); } - friend bool operator < (const CharName& l, const CharName& r) - { return l.to__canonical() < r.to__canonical(); } - friend bool operator <= (const CharName& l, const CharName& r) - { return l.to__canonical() <= r.to__canonical(); } - friend bool operator > (const CharName& l, const CharName& r) - { return l.to__canonical() > r.to__canonical(); } - friend bool operator >= (const CharName& l, const CharName& r) - { return l.to__canonical() >= r.to__canonical(); } - - friend - VString<23> convert_for_printf(const CharName& vs) { return vs.to__actual(); } -}; -template<> -inline -CharName stringish<CharName>(VString<23> iv) -{ - return CharName(iv); -} - -namespace e -{ -enum class EPOS : uint16_t -{ - ZERO = 0x0000, - - LEGS = 0x0001, - WEAPON = 0x0002, - GLOVES = 0x0004, - CAPE = 0x0008, - MISC1 = 0x0010, - SHIELD = 0x0020, - SHOES = 0x0040, - MISC2 = 0x0080, - HAT = 0x0100, - TORSO = 0x0200, - - ARROW = 0x8000, -}; -ENUM_BITWISE_OPERATORS(EPOS) - -constexpr EPOS get_enum_min_value(EPOS) { return EPOS(0x0000); } -constexpr EPOS get_enum_max_value(EPOS) { return EPOS(0xffff); } -} -using e::EPOS; - -struct item -{ - int id; - short nameid; - short amount; - EPOS equip; -}; - -struct point -{ - MapName map_; - short x, y; -}; - -namespace e -{ -enum class SkillFlags : uint16_t; + return !(l == r); } -using e::SkillFlags; - -struct skill_value -{ - unsigned short lv; - SkillFlags flags; - - friend bool operator == (const skill_value& l, const skill_value& r) - { - return l.lv == r.lv && l.flags == r.flags; - } - friend bool operator != (const skill_value& l, const skill_value& r) - { - return !(l == r); - } -}; - -struct global_reg -{ - VarName str; - int value; -}; - -// Option and Opt1..3 in map.hpp -namespace e -{ -enum class Option : uint16_t; -constexpr Option get_enum_min_value(Option) { return Option(0x0000); } -constexpr Option get_enum_max_value(Option) { return Option(0xffff); } -} -using e::Option; - -enum class ATTR -{ - STR = 0, - AGI = 1, - VIT = 2, - INT = 3, - DEX = 4, - LUK = 5, - - COUNT = 6, -}; - -constexpr ATTR ATTRs[6] = -{ - ATTR::STR, - ATTR::AGI, - ATTR::VIT, - ATTR::INT, - ATTR::DEX, - ATTR::LUK, -}; - -enum class ItemLook : uint16_t -{ - NONE = 0, - BLADE = 1, // or some other common weapons - _2, - SETZER_AND_SCYTHE = 3, - _6, - STAFF = 10, - BOW = 11, - _13 = 13, - _14 = 14, - _16 = 16, - SINGLE_HANDED_COUNT = 17, - - DUAL_BLADE = 0x11, - DUAL_2 = 0x12, - DUAL_6 = 0x13, - DUAL_12 = 0x14, - DUAL_16 = 0x15, - DUAL_26 = 0x16, -}; - -enum class SEX : uint8_t -{ - FEMALE = 0, - MALE = 1, - // For items. This is also used as error, sometime. - NEUTRAL = 2, -}; -inline -char sex_to_char(SEX sex) -{ - switch (sex) - { - case SEX::FEMALE: return 'F'; - case SEX::MALE: return 'M'; - default: return '\0'; - } -} -inline -SEX sex_from_char(char c) -{ - switch (c) - { - case 'F': return SEX::FEMALE; - case 'M': return SEX::MALE; - default: return SEX::NEUTRAL; - } -} - -struct CharKey -{ - CharName name; - int account_id; - int char_id; - unsigned char char_num; -}; - -struct CharData -{ - int partner_id; - - int base_exp, job_exp, zeny; - - short species; - short status_point, skill_point; - int hp, max_hp, sp, max_sp; - Option option; - short karma, manner; - short hair, hair_color, clothes_color; - int party_id; - - ItemLook weapon; - short shield; - short head_top, head_mid, head_bottom; - - unsigned char base_level, job_level; - earray<short, ATTR, ATTR::COUNT> attrs; - SEX sex; - - unsigned long mapip; - unsigned int mapport; - - struct point last_point, save_point; - Array<struct item, MAX_INVENTORY> inventory; - earray<skill_value, SkillID, MAX_SKILL> skill; - int global_reg_num; - Array<struct global_reg, GLOBAL_REG_NUM> global_reg; - int account_reg_num; - Array<struct global_reg, ACCOUNT_REG_NUM> account_reg; - int account_reg2_num; - Array<struct global_reg, ACCOUNT_REG2_NUM> account_reg2; -}; struct CharPair { @@ -373,37 +50,20 @@ struct CharPair {} }; -struct storage -{ - int dirty; - int account_id; - short storage_status; - short storage_amount; - Array<struct item, MAX_STORAGE> storage_; -}; - struct GM_Account { - int account_id; - uint8_t level; + AccountId account_id; + GmLevel level; }; -struct party_member +struct PartyPair { - int account_id; - CharName name; - MapName map; - int leader, online, lv; - struct map_session_data *sd; -}; + PartyId party_id = {}; + PartyMost *party_most = {}; -struct party -{ - int party_id; - PartyName name; - int exp; - int item; - Array<struct party_member, MAX_PARTY> member; + explicit + operator bool() const { return party_most; } + bool operator !() const { return !party_most; } + PartyMost *operator->() const { return party_most; } }; - -#endif // TMWA_MMO_MMO_HPP +} // namespace tmwa diff --git a/src/mmo/socket.cpp b/src/mmo/socket.cpp deleted file mode 100644 index 1e294bd..0000000 --- a/src/mmo/socket.cpp +++ /dev/null @@ -1,508 +0,0 @@ -#include "socket.hpp" -// socket.cpp - Network event system. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> -// Copyright © 2013 MadCamel -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -#include <arpa/inet.h> -#include <netinet/tcp.h> -#include <sys/socket.h> -//#include <sys/types.h> - -#include <fcntl.h> -#include <unistd.h> - -#include <cstdlib> -#include <cstring> -#include <ctime> - -#include "../io/cxxstdio.hpp" -#include "core.hpp" -#include "timer.hpp" -#include "utils.hpp" - -#include "../poison.hpp" - -static -io::FD_Set readfds; -static -int fd_max; - -static -const uint32_t RFIFO_SIZE = 65536; -static -const uint32_t WFIFO_SIZE = 65536; - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -static -std::array<std::unique_ptr<Session>, FD_SETSIZE> session; -#pragma GCC diagnostic pop - -Session::Session(SessionIO io, SessionParsers p) -: created() -, connected() -, eof() -, timed_close() -, rdata(), wdata() -, max_rdata(), max_wdata() -, rdata_size(), wdata_size() -, rdata_pos() -, client_ip() -, func_recv() -, func_send() -, func_parse() -, func_delete() -, for_inferior() -, session_data() -, fd() -{ - set_io(io); - set_parsers(p); -} -void Session::set_io(SessionIO io) -{ - func_send = io.func_send; - func_recv = io.func_recv; -} -void Session::set_parsers(SessionParsers p) -{ - func_parse = p.func_parse; - func_delete = p.func_delete; -} - - -void set_session(io::FD fd, std::unique_ptr<Session> sess) -{ - int f = fd.uncast_dammit(); - assert (0 <= f && f < FD_SETSIZE); - session[f] = std::move(sess); -} -Session *get_session(io::FD fd) -{ - int f = fd.uncast_dammit(); - if (0 <= f && f < FD_SETSIZE) - return session[f].get(); - return nullptr; -} -void reset_session(io::FD fd) -{ - int f = fd.uncast_dammit(); - assert (0 <= f && f < FD_SETSIZE); - session[f] = nullptr; -} -int get_fd_max() { return fd_max; } -IteratorPair<ValueIterator<io::FD, IncrFD>> iter_fds() -{ - return {io::FD::cast_dammit(0), io::FD::cast_dammit(fd_max)}; -} - -/// clean up by discarding handled bytes -inline -void RFIFOFLUSH(Session *s) -{ - really_memmove(&s->rdata[0], &s->rdata[s->rdata_pos], RFIFOREST(s)); - s->rdata_size = RFIFOREST(s); - s->rdata_pos = 0; -} - -/// how much room there is to read more data -inline -size_t RFIFOSPACE(Session *s) -{ - return s->max_rdata - s->rdata_size; -} - - -/// Read from socket to the queue -static -void recv_to_fifo(Session *s) -{ - ssize_t len = s->fd.read(&s->rdata[s->rdata_size], - RFIFOSPACE(s)); - - if (len > 0) - { - s->rdata_size += len; - s->connected = 1; - } - else - { - s->set_eof(); - } -} - -static -void send_from_fifo(Session *s) -{ - ssize_t len = s->fd.write(&s->wdata[0], s->wdata_size); - - if (len > 0) - { - s->wdata_size -= len; - if (s->wdata_size) - { - really_memmove(&s->wdata[0], &s->wdata[len], - s->wdata_size); - } - s->connected = 1; - } - else - { - s->set_eof(); - } -} - -static -void nothing_delete(Session *s) -{ - (void)s; -} - -static -void connect_client(Session *ls) -{ - struct sockaddr_in client_address; - socklen_t len = sizeof(client_address); - - io::FD fd = ls->fd.accept(reinterpret_cast<struct sockaddr *>(&client_address), &len); - if (fd == io::FD()) - { - perror("accept"); - return; - } - if (fd.uncast_dammit() >= SOFT_LIMIT) - { - FPRINTF(stderr, "softlimit reached, disconnecting : %d\n", fd.uncast_dammit()); - fd.shutdown(SHUT_RDWR); - fd.close(); - return; - } - if (fd_max <= fd.uncast_dammit()) - { - fd_max = fd.uncast_dammit() + 1; - } - - const int yes = 1; - /// Allow to bind() again after the server restarts. - // Since the socket is still in the TIME_WAIT, there's a possibility - // that formerly lost packets might be delivered and confuse the server. - fd.setsockopt(SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - /// Send packets as soon as possible - /// even if the kernel thinks there is too little for it to be worth it! - /// Testing shows this is indeed a good idea. - fd.setsockopt(IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); - - // Linux-ism: Set socket options to optimize for thin streams - // See http://lwn.net/Articles/308919/ and - // Documentation/networking/tcp-thin.txt .. Kernel 3.2+ -#ifdef TCP_THIN_LINEAR_TIMEOUTS - fd.setsockopt(IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, &yes, sizeof yes); -#endif -#ifdef TCP_THIN_DUPACK - fd.setsockopt(IPPROTO_TCP, TCP_THIN_DUPACK, &yes, sizeof yes); -#endif - - readfds.set(fd); - - fd.fcntl(F_SETFL, O_NONBLOCK); - - set_session(fd, make_unique<Session>( - SessionIO{func_recv: recv_to_fifo, func_send: send_from_fifo}, - ls->for_inferior)); - Session *s = get_session(fd); - s->fd = fd; - s->rdata.new_(RFIFO_SIZE); - s->wdata.new_(WFIFO_SIZE); - s->max_rdata = RFIFO_SIZE; - s->max_wdata = WFIFO_SIZE; - s->client_ip = IP4Address(client_address.sin_addr); - s->created = TimeT::now(); - s->connected = 0; -} - -Session *make_listen_port(uint16_t port, SessionParsers inferior) -{ - struct sockaddr_in server_address; - io::FD fd = io::FD::socket(AF_INET, SOCK_STREAM, 0); - if (fd == io::FD()) - { - perror("socket"); - return nullptr; - } - if (fd_max <= fd.uncast_dammit()) - fd_max = fd.uncast_dammit() + 1; - - fd.fcntl(F_SETFL, O_NONBLOCK); - - const int yes = 1; - /// Allow to bind() again after the server restarts. - // Since the socket is still in the TIME_WAIT, there's a possibility - // that formerly lost packets might be delivered and confuse the server. - fd.setsockopt(SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - /// Send packets as soon as possible - /// even if the kernel thinks there is too little for it to be worth it! - // I'm not convinced this is a good idea; although in minimizes the - // latency for an individual write, it increases traffic in general. - fd.setsockopt(IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); - - server_address.sin_family = AF_INET; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#if __GNUC__ > 4 || __GNUC_MINOR__ >= 8 -# pragma GCC diagnostic ignored "-Wuseless-cast" -#endif - server_address.sin_addr.s_addr = htonl(INADDR_ANY); - server_address.sin_port = htons(port); -#pragma GCC diagnostic pop - - if (fd.bind(reinterpret_cast<struct sockaddr *>(&server_address), - sizeof(server_address)) == -1) - { - perror("bind"); - exit(1); - } - if (fd.listen(5) == -1) - { /* error */ - perror("listen"); - exit(1); - } - - readfds.set(fd); - - set_session(fd, make_unique<Session>( - SessionIO{func_recv: connect_client, func_send: nullptr}, - SessionParsers{func_parse: nullptr, func_delete: nothing_delete})); - Session *s = get_session(fd); - s->for_inferior = inferior; - s->fd = fd; - - s->created = TimeT::now(); - s->connected = 1; - - return s; -} - -Session *make_connection(IP4Address ip, uint16_t port, SessionParsers parsers) -{ - struct sockaddr_in server_address; - io::FD fd = io::FD::socket(AF_INET, SOCK_STREAM, 0); - if (fd == io::FD()) - { - perror("socket"); - return nullptr; - } - if (fd_max <= fd.uncast_dammit()) - fd_max = fd.uncast_dammit() + 1; - - const int yes = 1; - /// Allow to bind() again after the server restarts. - // Since the socket is still in the TIME_WAIT, there's a possibility - // that formerly lost packets might be delivered and confuse the server. - fd.setsockopt(SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - /// Send packets as soon as possible - /// even if the kernel thinks there is too little for it to be worth it! - // I'm not convinced this is a good idea; although in minimizes the - // latency for an individual write, it increases traffic in general. - fd.setsockopt(IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); - - server_address.sin_family = AF_INET; - server_address.sin_addr = in_addr(ip); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#if __GNUC__ > 4 || __GNUC_MINOR__ >= 8 -# pragma GCC diagnostic ignored "-Wuseless-cast" -#endif - server_address.sin_port = htons(port); -#pragma GCC diagnostic pop - - fd.fcntl(F_SETFL, O_NONBLOCK); - - /// Errors not caught - we must not block - /// Let the main select() loop detect when we know the state - fd.connect(reinterpret_cast<struct sockaddr *>(&server_address), - sizeof(struct sockaddr_in)); - - readfds.set(fd); - - set_session(fd, make_unique<Session>( - SessionIO{func_recv: recv_to_fifo, func_send: send_from_fifo}, - parsers)); - Session *s = get_session(fd); - s->fd = fd; - s->rdata.new_(RFIFO_SIZE); - s->wdata.new_(WFIFO_SIZE); - - s->max_rdata = RFIFO_SIZE; - s->max_wdata = WFIFO_SIZE; - s->created = TimeT::now(); - s->connected = 1; - - return s; -} - -void delete_session(Session *s) -{ - if (!s) - return; - // this needs to be before the fd_max-- - s->func_delete(s); - - io::FD fd = s->fd; - // If this was the highest fd, decrease it - // We could add a loop to decrement fd_max further for every null session, - // but this is cheap and good enough for the typical case - if (fd.uncast_dammit() == fd_max - 1) - fd_max--; - readfds.clr(fd); - { - s->rdata.delete_(); - s->wdata.delete_(); - s->session_data.reset(); - reset_session(fd); - } - - // just close() would try to keep sending buffers - fd.shutdown(SHUT_RDWR); - fd.close(); -} - -void realloc_fifo(Session *s, size_t rfifo_size, size_t wfifo_size) -{ - if (s->max_rdata != rfifo_size && s->rdata_size < rfifo_size) - { - s->rdata.resize(rfifo_size); - s->max_rdata = rfifo_size; - } - if (s->max_wdata != wfifo_size && s->wdata_size < wfifo_size) - { - s->wdata.resize(wfifo_size); - s->max_wdata = wfifo_size; - } -} - -void WFIFOSET(Session *s, size_t len) -{ - if (s->wdata_size + len + 16384 > s->max_wdata) - { - realloc_fifo(s, s->max_rdata, s->max_wdata << 1); - PRINTF("socket: %d wdata expanded to %zu bytes.\n", s, s->max_wdata); - } - if (s->wdata_size + len + 2048 < s->max_wdata) - s->wdata_size += len; - else - FPRINTF(stderr, "socket: %d wdata lost !!\n", s), abort(); -} - -void do_sendrecv(interval_t next_ms) -{ - bool any = false; - io::FD_Set rfd = readfds, wfd; - for (io::FD i : iter_fds()) - { - Session *s = get_session(i); - if (s) - { - any = true; - if (s->wdata_size) - wfd.set(i); - } - } - if (!any) - { - if (!has_timers()) - { - PRINTF("Shutting down - nothing to do\n"); - runflag = false; - } - return; - } - struct timeval timeout; - { - std::chrono::seconds next_s = std::chrono::duration_cast<std::chrono::seconds>(next_ms); - std::chrono::microseconds next_us = next_ms - next_s; - timeout.tv_sec = next_s.count(); - timeout.tv_usec = next_us.count(); - } - if (io::FD_Set::select(fd_max, &rfd, &wfd, NULL, &timeout) <= 0) - return; - for (io::FD i : iter_fds()) - { - Session *s = get_session(i); - if (!s) - continue; - if (wfd.isset(i) && !s->eof) - { - if (s->func_send) - //send_from_fifo(i); - s->func_send(s); - } - if (rfd.isset(i) && !s->eof) - { - if (s->func_recv) - //recv_to_fifo(i); - //or connect_client(i); - s->func_recv(s); - } - } -} - -void do_parsepacket(void) -{ - for (io::FD i : iter_fds()) - { - Session *s = get_session(i); - if (!s) - continue; - if (!s->connected - && static_cast<time_t>(TimeT::now()) - static_cast<time_t>(s->created) > CONNECT_TIMEOUT) - { - PRINTF("Session #%d timed out\n", s); - s->set_eof(); - } - if (s->rdata_size && !s->eof && s->func_parse) - { - s->func_parse(s); - /// some func_parse may call delete_session - // (that's kind of evil) - s = get_session(i); - if (!s) - continue; - } - if (s->eof) - { - delete_session(s); - continue; - } - /// Reclaim buffer space for what was read - RFIFOFLUSH(s); - } -} - -void RFIFOSKIP(Session *s, size_t len) -{ - s->rdata_pos += len; - - if (s->rdata_size < s->rdata_pos) - { - FPRINTF(stderr, "too many skip\n"); - abort(); - } -} diff --git a/src/mmo/socket.hpp b/src/mmo/socket.hpp deleted file mode 100644 index c166794..0000000 --- a/src/mmo/socket.hpp +++ /dev/null @@ -1,424 +0,0 @@ -#ifndef TMWA_MMO_SOCKET_HPP -#define TMWA_MMO_SOCKET_HPP -// socket.hpp - Network event system. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -# include "../sanity.hpp" - -# include <netinet/in.h> - -# include <cstdio> - -# include <array> - -# include "../compat/rawmem.hpp" - -# include "../strings/astring.hpp" -# include "../strings/vstring.hpp" -# include "../strings/xstring.hpp" - -# include "../io/fd.hpp" - -# include "dumb_ptr.hpp" -# include "ip.hpp" -# include "utils.hpp" -# include "timer.t.hpp" - -struct SessionData -{ -}; -struct SessionDeleter -{ - // defined per-server - void operator()(SessionData *sd); -}; - -struct Session; -struct SessionIO -{ - void (*func_recv)(Session *); - void (*func_send)(Session *); -}; - -struct SessionParsers -{ - void (*func_parse)(Session *); - void (*func_delete)(Session *); -}; - -struct Session -{ - Session(SessionIO, SessionParsers); - Session(Session&&) = delete; - Session& operator = (Session&&) = delete; - - void set_io(SessionIO); - void set_parsers(SessionParsers); - - /// Checks whether a newly-connected socket actually does anything - TimeT created; - bool connected; - -private: - /// Flag needed since structure must be freed in a server-dependent manner - bool eof; -public: - void set_eof() { eof = true; } - - /// Currently used by clif_setwaitclose - Timer timed_close; - - /// Since this is a single-threaded application, it can't block - /// These are the read/write queues - dumb_ptr<uint8_t[]> rdata, wdata; - size_t max_rdata, max_wdata; - /// How much is actually in the queue - size_t rdata_size, wdata_size; - /// How much has already been read from the queue - /// Note that there is no need for a wdata_pos - size_t rdata_pos; - - IP4Address client_ip; - -private: - /// Send or recieve - /// Only called when select() indicates the socket is ready - /// If, after that, nothing is read, it sets eof - // These could probably be hard-coded with a little work - void (*func_recv)(Session *); - void (*func_send)(Session *); - /// This is the important one - /// Set to different functions depending on whether the connection - /// is a player or a server/ladmin - void (*func_parse)(Session *); - /// Cleanup function since we're not fully RAII yet - void (*func_delete)(Session *); - -public: - // this really ought to be part of session_data, once that gets sane - SessionParsers for_inferior; - - /// Server-specific data type - // (this really should include the deleter, but ...) - std::unique_ptr<SessionData, SessionDeleter> session_data; - - io::FD fd; - - friend void do_sendrecv(interval_t next); - friend void do_parsepacket(void); - friend void delete_session(Session *); -}; - -inline -int convert_for_printf(Session *s) -{ - return s->fd.uncast_dammit(); -} - -// save file descriptors for important stuff -constexpr int SOFT_LIMIT = FD_SETSIZE - 50; - -// socket timeout to establish a full connection in seconds -constexpr int CONNECT_TIMEOUT = 15; - - -void set_session(io::FD fd, std::unique_ptr<Session> sess); -Session *get_session(io::FD fd); -void reset_session(io::FD fd); -int get_fd_max(); - -class IncrFD -{ -public: - static - io::FD inced(io::FD v) - { - return io::FD::cast_dammit(v.uncast_dammit() + 1); - } -}; -IteratorPair<ValueIterator<io::FD, IncrFD>> iter_fds(); - - -/// open a socket, bind, and listen. Return an fd, or -1 if socket() fails, -/// but exit if bind() or listen() fails -Session *make_listen_port(uint16_t port, SessionParsers inferior); -/// Connect to an address, return a connected socket or -1 -// FIXME - this is IPv4 only! -Session *make_connection(IP4Address ip, uint16_t port, SessionParsers); -/// free() the structure and close() the fd -void delete_session(Session *); -/// Make a the internal queues bigger -void realloc_fifo(Session *s, size_t rfifo_size, size_t wfifo_size); -/// Update all sockets that can be read/written from the queues -void do_sendrecv(interval_t next); -/// Call the parser function for every socket that has read data -void do_parsepacket(void); - -template<class T> -uint8_t *pod_addressof_m(T& structure) -{ - static_assert(is_trivially_copyable<T>::value, "Can only byte-copy POD-ish structs"); - return &reinterpret_cast<uint8_t&>(structure); -} - -template<class T> -const uint8_t *pod_addressof_c(const T& structure) -{ - static_assert(is_trivially_copyable<T>::value, "Can only byte-copy POD-ish structs"); - return &reinterpret_cast<const uint8_t&>(structure); -} - - -/// Check how much can be read -inline -size_t RFIFOREST(Session *s) -{ - return s->rdata_size - s->rdata_pos; -} -/// Read from the queue -inline -const void *RFIFOP(Session *s, size_t pos) -{ - return &s->rdata[s->rdata_pos + pos]; -} -inline -uint8_t RFIFOB(Session *s, size_t pos) -{ - return *static_cast<const uint8_t *>(RFIFOP(s, pos)); -} -inline -uint16_t RFIFOW(Session *s, size_t pos) -{ - return *static_cast<const uint16_t *>(RFIFOP(s, pos)); -} -inline -uint32_t RFIFOL(Session *s, size_t pos) -{ - return *static_cast<const uint32_t *>(RFIFOP(s, pos)); -} -template<class T> -void RFIFO_STRUCT(Session *s, size_t pos, T& structure) -{ - really_memcpy(pod_addressof_m(structure), static_cast<const uint8_t *>(RFIFOP(s, pos)), sizeof(T)); -} -inline -IP4Address RFIFOIP(Session *s, size_t pos) -{ - IP4Address o; - RFIFO_STRUCT(s, pos, o); - return o; -} -template<uint8_t len> -inline -VString<len-1> RFIFO_STRING(Session *s, size_t pos) -{ - const char *const begin = static_cast<const char *>(RFIFOP(s, pos)); - const char *const end = begin + len-1; - const char *const mid = std::find(begin, end, '\0'); - return XString(begin, mid, nullptr); -} -inline -AString RFIFO_STRING(Session *s, size_t pos, size_t len) -{ - const char *const begin = static_cast<const char *>(RFIFOP(s, pos)); - const char *const end = begin + len; - const char *const mid = std::find(begin, end, '\0'); - return XString(begin, mid, nullptr); -} -inline -void RFIFO_BUF_CLONE(Session *s, uint8_t *buf, size_t len) -{ - really_memcpy(buf, static_cast<const uint8_t *>(RFIFOP(s, 0)), len); -} - -/// Done reading -void RFIFOSKIP(Session *s, size_t len); - -/// Read from an arbitrary buffer -inline -const void *RBUFP(const uint8_t *p, size_t pos) -{ - return p + pos; -} -inline -uint8_t RBUFB(const uint8_t *p, size_t pos) -{ - return *static_cast<const uint8_t *>(RBUFP(p, pos)); -} -inline -uint16_t RBUFW(const uint8_t *p, size_t pos) -{ - return *static_cast<const uint16_t *>(RBUFP(p, pos)); -} -inline -uint32_t RBUFL(const uint8_t *p, size_t pos) -{ - return *static_cast<const uint32_t *>(RBUFP(p, pos)); -} -template<class T> -void RBUF_STRUCT(const uint8_t *p, size_t pos, T& structure) -{ - really_memcpy(pod_addressof_m(structure), p + pos, sizeof(T)); -} -inline -IP4Address RBUFIP(const uint8_t *p, size_t pos) -{ - IP4Address o; - RBUF_STRUCT(p, pos, o); - return o; -} -template<uint8_t len> -inline -VString<len-1> RBUF_STRING(const uint8_t *p, size_t pos) -{ - const char *const begin = static_cast<const char *>(RBUFP(p, pos)); - const char *const end = begin + len-1; - const char *const mid = std::find(begin, end, '\0'); - return XString(begin, mid, nullptr); -} -inline -AString RBUF_STRING(const uint8_t *p, size_t pos, size_t len) -{ - const char *const begin = static_cast<const char *>(RBUFP(p, pos)); - const char *const end = begin + len; - const char *const mid = std::find(begin, end, '\0'); - return XString(begin, mid, nullptr); -} - - -/// Unused - check how much data can be written -// the existence of this seems scary -inline -size_t WFIFOSPACE(Session *s) -{ - return s->max_wdata - s->wdata_size; -} -/// Write to the queue -inline -void *WFIFOP(Session *s, size_t pos) -{ - return &s->wdata[s->wdata_size + pos]; -} -inline -uint8_t& WFIFOB(Session *s, size_t pos) -{ - return *static_cast<uint8_t *>(WFIFOP(s, pos)); -} -inline -uint16_t& WFIFOW(Session *s, size_t pos) -{ - return *static_cast<uint16_t *>(WFIFOP(s, pos)); -} -inline -uint32_t& WFIFOL(Session *s, size_t pos) -{ - return *static_cast<uint32_t *>(WFIFOP(s, pos)); -} -template<class T> -void WFIFO_STRUCT(Session *s, size_t pos, T& structure) -{ - really_memcpy(static_cast<uint8_t *>(WFIFOP(s, pos)), pod_addressof_c(structure), sizeof(T)); -} -inline -IP4Address& WFIFOIP(Session *s, size_t pos) -{ - static_assert(is_trivially_copyable<IP4Address>::value, "That was the whole point"); - return *static_cast<IP4Address *>(WFIFOP(s, pos)); -} -inline -void WFIFO_STRING(Session *s, size_t pos, XString str, size_t len) -{ - char *const begin = static_cast<char *>(WFIFOP(s, pos)); - char *const end = begin + len; - char *const mid = std::copy(str.begin(), str.end(), begin); - std::fill(mid, end, '\0'); -} -inline -void WFIFO_ZERO(Session *s, size_t pos, size_t len) -{ - uint8_t *b = static_cast<uint8_t *>(WFIFOP(s, pos)); - uint8_t *e = b + len; - std::fill(b, e, '\0'); -} -inline -void WFIFO_BUF_CLONE(Session *s, const uint8_t *buf, size_t len) -{ - really_memcpy(static_cast<uint8_t *>(WFIFOP(s, 0)), buf, len); -} - -/// Finish writing -void WFIFOSET(Session *s, size_t len); - -/// Write to an arbitrary buffer -inline -void *WBUFP(uint8_t *p, size_t pos) -{ - return p + pos; -} -inline -uint8_t& WBUFB(uint8_t *p, size_t pos) -{ - return *static_cast<uint8_t *>(WBUFP(p, pos)); -} -inline -uint16_t& WBUFW(uint8_t *p, size_t pos) -{ - return *static_cast<uint16_t *>(WBUFP(p, pos)); -} -inline -uint32_t& WBUFL(uint8_t *p, size_t pos) -{ - return *static_cast<uint32_t *>(WBUFP(p, pos)); -} -template<class T> -void WBUF_STRUCT(uint8_t *p, size_t pos, T& structure) -{ - really_memcpy(p + pos, pod_addressof_c(structure), sizeof(T)); -} -inline -IP4Address& WBUFIP(uint8_t *p, size_t pos) -{ - return *static_cast<IP4Address *>(WBUFP(p, pos)); -} -inline -void WBUF_STRING(uint8_t *p, size_t pos, XString s, size_t len) -{ - char *const begin = static_cast<char *>(WBUFP(p, pos)); - char *const end = begin + len; - char *const mid = std::copy(s.begin(), s.end(), begin); - std::fill(mid, end, '\0'); -} -inline -void WBUF_ZERO(uint8_t *p, size_t pos, size_t len) -{ - uint8_t *b = static_cast<uint8_t *>(WBUFP(p, pos)); - uint8_t *e = b + len; - std::fill(b, e, '\0'); -} - -inline -void RFIFO_WFIFO_CLONE(Session *rs, Session *ws, size_t len) -{ - really_memcpy(static_cast<uint8_t *>(WFIFOP(ws, 0)), - static_cast<const uint8_t *>(RFIFOP(rs, 0)), len); -} - -#endif // TMWA_MMO_SOCKET_HPP diff --git a/src/mmo/dumb_ptr.cpp b/src/mmo/strs.cpp index 77e3080..71dceec 100644 --- a/src/mmo/dumb_ptr.cpp +++ b/src/mmo/strs.cpp @@ -1,7 +1,7 @@ -#include "dumb_ptr.hpp" -// dumb_ptr.cpp - dummy file to make Make dependencies work +#include "strs.hpp" +// strs.cpp - common string types // -// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com> +// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> // // This file is part of The Mana World (Athena server) // @@ -19,3 +19,8 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "../poison.hpp" + + +namespace tmwa +{ +} // namespace tmwa diff --git a/src/mmo/strs.hpp b/src/mmo/strs.hpp new file mode 100644 index 0000000..fea0c98 --- /dev/null +++ b/src/mmo/strs.hpp @@ -0,0 +1,126 @@ +#pragma once +// strs.hpp - common string types +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +#include "fwd.hpp" + +#include "../strings/vstring.hpp" + + +namespace tmwa +{ +// affects CharName +#define NAME_IGNORING_CASE 1 + +struct AccountName : VString<23> {}; +struct AccountPass : VString<23> {}; +struct AccountCrypt : VString<39> {}; +struct AccountEmail : VString<39> {}; +struct ServerName : VString<19> {}; +struct PartyName : VString<23> {}; +struct VarName : VString<31> {}; + +#define DEFAULT_EMAIL stringish<AccountEmail>("a@a.com"_s) + +// It is decreed: a mapname shall not contain an extension +class MapName : public strings::_crtp_string<MapName, MapName, strings::ZPair> +{ + VString<15> _impl; +public: + MapName() = default; + MapName(VString<15> v) : _impl(v.xislice_h(std::find(v.begin(), v.end(), '.'))) {} + + iterator begin() const { return &*_impl.begin(); } + iterator end() const { return &*_impl.end(); } + const char *c_str() const { return _impl.c_str(); } + + operator RString() const { return _impl; } + operator AString() const { return _impl; } + operator TString() const { return _impl; } + operator SString() const { return _impl; } + operator ZString() const { return _impl; } + operator XString() const { return _impl; } +}; +template<> +inline +MapName stringish<MapName>(VString<15> iv) +{ + return iv; +} +inline +const char *decay_for_printf(const MapName& vs) { return vs.c_str(); } + +// It is decreed: a charname is sometimes case sensitive +struct CharName +{ +private: + VString<23> _impl; +public: + CharName() = default; + explicit CharName(VString<23> name) + : _impl(name) + {} + + VString<23> to__actual() const + { + return _impl; + } + VString<23> to__lower() const + { + return _impl.to_lower(); + } + VString<23> to__upper() const + { + return _impl.to_upper(); + } + VString<23> to__canonical() const + { +#if NAME_IGNORING_CASE == 0 + return to__actual(); +#endif +#if NAME_IGNORING_CASE == 1 + return to__lower(); +#endif + } + + friend bool operator == (const CharName& l, const CharName& r) + { return l.to__canonical() == r.to__canonical(); } + friend bool operator != (const CharName& l, const CharName& r) + { return l.to__canonical() != r.to__canonical(); } + friend bool operator < (const CharName& l, const CharName& r) + { return l.to__canonical() < r.to__canonical(); } + friend bool operator <= (const CharName& l, const CharName& r) + { return l.to__canonical() <= r.to__canonical(); } + friend bool operator > (const CharName& l, const CharName& r) + { return l.to__canonical() > r.to__canonical(); } + friend bool operator >= (const CharName& l, const CharName& r) + { return l.to__canonical() >= r.to__canonical(); } + + friend + VString<23> convert_for_printf(const CharName& vs) { return vs.to__actual(); } +}; +template<> +inline +CharName stringish<CharName>(VString<23> iv) +{ + return CharName(iv); +} +} // namespace tmwa diff --git a/src/mmo/timer.cpp b/src/mmo/timer.cpp deleted file mode 100644 index 6e28a12..0000000 --- a/src/mmo/timer.cpp +++ /dev/null @@ -1,221 +0,0 @@ -#include "timer.hpp" -// timer.cpp - Future event scheduler. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -#include <sys/stat.h> -#include <sys/time.h> - -#include <cassert> -#include <cstring> - -#include <queue> - -#include "../strings/zstring.hpp" - -#include "../io/cxxstdio.hpp" - -#include "utils.hpp" - -#include "../poison.hpp" - -struct TimerData -{ - /// This will be reset on call, to avoid problems. - Timer *owner; - - /// When it will be triggered - tick_t tick; - /// What will be done - timer_func func; - /// Repeat rate - 0 for oneshot - interval_t interval; - - TimerData(Timer *o, tick_t t, timer_func f, interval_t i) - : owner(o) - , tick(t) - , func(std::move(f)) - , interval(i) - {} -}; - -struct TimerCompare -{ - /// implement "less than" - bool operator() (dumb_ptr<TimerData> l, dumb_ptr<TimerData> r) - { - // C++ provides a max-heap, but we want - // the smallest tick to be the head (a min-heap). - return l->tick > r->tick; - } -}; - -static -std::priority_queue<dumb_ptr<TimerData>, std::vector<dumb_ptr<TimerData>>, TimerCompare> timer_heap; - - -tick_t gettick_cache; - -tick_t milli_clock::now(void) noexcept -{ - struct timeval tval; - // BUG: This will cause strange behavior if the system clock is changed! - // it should be reimplemented in terms of clock_gettime(CLOCK_MONOTONIC, ) - gettimeofday(&tval, NULL); - return gettick_cache = tick_t(std::chrono::seconds(tval.tv_sec) - + std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::microseconds(tval.tv_usec))); -} - -static -void do_nothing(TimerData *, tick_t) -{ -} - -void Timer::cancel() -{ - if (!td) - return; - - assert (this == td->owner); - td->owner = nullptr; - td->func = do_nothing; - td->interval = interval_t::zero(); - td = nullptr; -} - -void Timer::detach() -{ - assert (this == td->owner); - td->owner = nullptr; - td = nullptr; -} - -static -void push_timer_heap(dumb_ptr<TimerData> td) -{ - timer_heap.push(td); -} - -static -dumb_ptr<TimerData> top_timer_heap(void) -{ - if (timer_heap.empty()) - return dumb_ptr<TimerData>(); - return timer_heap.top(); -} - -static -void pop_timer_heap(void) -{ - timer_heap.pop(); -} - -Timer::Timer(tick_t tick, timer_func func, interval_t interval) -: td(dumb_ptr<TimerData>::make(this, tick, std::move(func), interval)) -{ - assert (interval >= interval_t::zero()); - - push_timer_heap(td); -} - -Timer::Timer(Timer&& t) -: td(t.td) -{ - t.td = nullptr; - if (td) - { - assert (td->owner == &t); - td->owner = this; - } -} - -Timer& Timer::operator = (Timer&& t) -{ - std::swap(td, t.td); - if (td) - { - assert (td->owner == &t); - td->owner = this; - } - if (t.td) - { - assert (t.td->owner == this); - t.td->owner = &t; - } - return *this; -} - -interval_t do_timer(tick_t tick) -{ - /// Number of milliseconds until it calls this again - // this says to wait 1 sec if all timers get popped - interval_t nextmin = std::chrono::seconds(1); - - while (dumb_ptr<TimerData> td = top_timer_heap()) - { - // while the heap is not empty and - if (td->tick > tick) - { - /// Return the time until the next timer needs to goes off - nextmin = td->tick - tick; - break; - } - pop_timer_heap(); - - // Prevent destroying the object we're in. - // Note: this would be surprising in an interval timer, - // but all interval timers do an immediate explicit detach(). - if (td->owner) - td->owner->detach(); - // If we are too far past the requested tick, call with - // the current tick instead to fix reregistration problems - if (td->tick + std::chrono::seconds(1) < tick) - td->func(td.operator->(), tick); - else - td->func(td.operator->(), td->tick); - - if (td->interval == interval_t::zero()) - { - td.delete_(); - continue; - } - if (td->tick + std::chrono::seconds(1) < tick) - td->tick = tick + td->interval; - else - td->tick += td->interval; - push_timer_heap(td); - } - - return std::max(nextmin, std::chrono::milliseconds(10)); -} - -tick_t file_modified(ZString name) -{ - struct stat buf; - if (stat(name.c_str(), &buf)) - return tick_t(); - return tick_t(std::chrono::seconds(buf.st_mtime)); -} - -bool has_timers() -{ - return !timer_heap.empty(); -} diff --git a/src/mmo/timer.hpp b/src/mmo/timer.hpp deleted file mode 100644 index 363cf17..0000000 --- a/src/mmo/timer.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef TMWA_MMO_TIMER_HPP -#define TMWA_MMO_TIMER_HPP -// timer.hpp - Future event scheduler. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -# include "timer.t.hpp" - -# include "../sanity.hpp" - -# include "../strings/fwd.hpp" - -// updated automatically when using milli_clock::now() -// which is done only by core.cpp -extern tick_t gettick_cache; - -inline -tick_t gettick(void) -{ - return gettick_cache; -} - -/// Do all timers scheduled before tick, and return the number of -/// milliseconds until the next timer happens -interval_t do_timer(tick_t tick); - -/// Stat a file, and return its modification time, truncated to seconds. -tick_t file_modified(ZString name); - -/// Check if there are any events at all scheduled. -bool has_timers(); - -#endif // TMWA_MMO_TIMER_HPP diff --git a/src/mmo/timer.t.hpp b/src/mmo/timer.t.hpp deleted file mode 100644 index 6066e7c..0000000 --- a/src/mmo/timer.t.hpp +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef TMWA_MMO_TIMER_T_HPP -#define TMWA_MMO_TIMER_T_HPP -// timer.t.hpp - Future event scheduler. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com> -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -# include "../sanity.hpp" - -# include <chrono> -# include <functional> - -# include "dumb_ptr.hpp" - -struct TimerData; - -/// An implementation of the C++ "clock" concept, exposing -/// durations in milliseconds. -class milli_clock -{ -public: - typedef std::chrono::milliseconds duration; - typedef duration::rep rep; - typedef duration::period period; - typedef std::chrono::time_point<milli_clock, duration> time_point; - static const bool is_steady = true; // assumed - not necessarily true - - static time_point now() noexcept; -}; - -/// A point in time. -typedef milli_clock::time_point tick_t; -/// The difference between two points in time. -typedef milli_clock::duration interval_t; -/// (to get additional arguments, use std::bind or a lambda). -typedef std::function<void (TimerData *, tick_t)> timer_func; - -class Timer -{ - friend struct TimerData; - dumb_ptr<TimerData> td; - - Timer(const Timer&) = delete; - Timer& operator = (const Timer&) = delete; -public: - /// Don't own anything yet. - Timer() = default; - /// Schedule a timer for the given tick. - /// If you do not wish to keep track of it, call disconnect(). - /// Otherwise, you may cancel() or replace (operator =) it later. - /// - /// If the interval argument is given, the timer will reschedule - /// itself again forever. Otherwise, it will disconnect() itself - /// just BEFORE it is called. - Timer(tick_t tick, timer_func func, interval_t interval=interval_t::zero()); - - Timer(Timer&& t); - Timer& operator = (Timer&& t); - ~Timer() { cancel(); } - - /// Cancel the delivery of this timer's function, and make it falsy. - /// Implementation note: this doesn't actually remove it, just sets - /// the functor to do_nothing, and waits for the tick before removing. - void cancel(); - /// Make it falsy without cancelling the timer, - void detach(); - - /// Check if there is a timer connected. - explicit operator bool() { return bool(td); } - /// Check if there is no connected timer. - bool operator !() { return !td; } -}; - -#endif // TMWA_MMO_TIMER_T_HPP diff --git a/src/mmo/utils.cpp b/src/mmo/utils.cpp index a1316d1..f8aff2e 100644 --- a/src/mmo/utils.cpp +++ b/src/mmo/utils.cpp @@ -20,12 +20,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -#include <netinet/in.h> #include <sys/time.h> #include <algorithm> -#include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" @@ -36,6 +34,9 @@ #include "../poison.hpp" + +namespace tmwa +{ //--------------------------------------------------- // E-mail check: return 0 (not correct) or 1 (valid). //--------------------------------------------------- @@ -57,9 +58,9 @@ bool e_mail_check(XString email) return 0; if (hostname.front() == '.' || hostname.back() == '.') return 0; - if (hostname.contains_seq("..")) + if (hostname.contains_seq(".."_s)) return 0; - if (email.contains_any(" ;")) + if (email.contains_any(" ;"_s)) return 0; return email.is_print(); } @@ -70,18 +71,18 @@ bool e_mail_check(XString email) //------------------------------------------------- int config_switch(ZString str) { - if (str == "true" || str == "on" || str == "yes" - || str == "oui" || str == "ja" - || str == "si") + if (str == "true"_s || str == "on"_s || str == "yes"_s + || str == "oui"_s || str == "ja"_s + || str == "si"_s) return 1; - if (str == "false" || str == "off" || str == "no" - || str == "non" || str == "nein") + if (str == "false"_s || str == "off"_s || str == "no"_s + || str == "non"_s || str == "nein"_s) return 0; int rv; if (extract(str, &rv)) return rv; - FPRINTF(stderr, "Fatal: bad option value %s", str); + FPRINTF(stderr, "Fatal: bad option value %s"_fmt, str); abort(); } @@ -93,17 +94,17 @@ void stamp_time(timestamp_seconds_buffer& out, const TimeT *t) struct tm when = t ? *t : TimeT::now(); char buf[20]; strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &when); - out = stringish<timestamp_seconds_buffer>(const_(buf)); + out = stringish<timestamp_seconds_buffer>(VString<19>(strings::really_construct_from_a_pointer, buf)); } void stamp_time(timestamp_milliseconds_buffer& out) { struct timeval tv; - gettimeofday(&tv, NULL); + gettimeofday(&tv, nullptr); struct tm when = TimeT(tv.tv_sec); char buf[24]; strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &when); sprintf(buf + 19, ".%03d", static_cast<int>(tv.tv_usec / 1000)); - out = stringish<timestamp_milliseconds_buffer>(const_(buf)); + out = stringish<timestamp_milliseconds_buffer>(VString<23>(strings::really_construct_from_a_pointer, buf)); } void log_with_timestamp(io::WriteFile& out, XString line) @@ -119,3 +120,4 @@ void log_with_timestamp(io::WriteFile& out, XString line) out.really_put(": ", 2); out.put_line(line); } +} // namespace tmwa diff --git a/src/mmo/utils.hpp b/src/mmo/utils.hpp index d59f7ac..fc3ea74 100644 --- a/src/mmo/utils.hpp +++ b/src/mmo/utils.hpp @@ -1,5 +1,4 @@ -#ifndef TMWA_MMO_UTILS_HPP -#define TMWA_MMO_UTILS_HPP +#pragma once // utils.hpp - Useful stuff that hasn't been categorized. // // Copyright © ????-2004 Athena Dev Teams @@ -21,19 +20,25 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -# include "../sanity.hpp" +#include "fwd.hpp" -# include <cstdio> +#include <cstring> +#include <ctime> -# include <type_traits> +#include <type_traits> -# include "../strings/fwd.hpp" -# include "../strings/vstring.hpp" +#include "../ints/little.hpp" -# include "../generic/operators.hpp" +#include "../strings/fwd.hpp" +#include "../strings/vstring.hpp" -# include "../io/fwd.hpp" +#include "../generic/operators.hpp" +#include "../io/fwd.hpp" + + +namespace tmwa +{ template<class T> struct is_trivially_copyable : std::integral_constant<bool, @@ -82,7 +87,7 @@ struct TimeT : Comparable TimeT now() { // poisoned, but this is still in header-land - return time(NULL); + return time(nullptr); } bool error() const @@ -101,12 +106,40 @@ long long convert_for_printf(TimeT t) return t.value; } -inline -long long& convert_for_scanf(TimeT& t) +// 2038 problem +inline __attribute__((warn_unused_result)) +bool native_to_network(Little32 *net, TimeT nat) { - return t.value; + time_t tmp = nat; + return native_to_network(net, static_cast<uint32_t>(tmp)); +} + +inline __attribute__((warn_unused_result)) +bool network_to_native(TimeT *nat, Little32 net) +{ + uint32_t tmp; + bool rv = network_to_native(&tmp, net); + *nat = static_cast<time_t>(tmp); + return rv; } +inline __attribute__((warn_unused_result)) +bool native_to_network(Little64 *net, TimeT nat) +{ + time_t tmp = nat; + return native_to_network(net, static_cast<uint64_t>(tmp)); +} + +inline __attribute__((warn_unused_result)) +bool network_to_native(TimeT *nat, Little64 net) +{ + uint64_t tmp; + bool rv = network_to_native(&tmp, net); + *nat = static_cast<time_t>(tmp); + return rv; +} + + struct timestamp_seconds_buffer : VString<19> {}; struct timestamp_milliseconds_buffer : VString<23> {}; void stamp_time(timestamp_seconds_buffer&, const TimeT *t=nullptr); @@ -115,21 +148,20 @@ void stamp_time(timestamp_milliseconds_buffer&); void log_with_timestamp(io::WriteFile& out, XString line); // TODO VString? -# define TIMESTAMP_DUMMY "YYYY-MM-DD HH:MM:SS" +#define TIMESTAMP_DUMMY "YYYY-MM-DD HH:MM:SS" static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer), "timestamp size"); -# define WITH_TIMESTAMP(str) str TIMESTAMP_DUMMY +#define WITH_TIMESTAMP(str) str TIMESTAMP_DUMMY // str: prefix: YYYY-MM-DD HH:MM:SS // sizeof: 01234567890123456789012345678 // str + sizeof: ^ // -1: ^ // there's probably a better way to do this now -# define REPLACE_TIMESTAMP(str, t) \ +#define REPLACE_TIMESTAMP(str, t) \ stamp_time( \ reinterpret_cast<timestamp_seconds_buffer *>( \ str + sizeof(str) \ )[-1], \ &t \ ) - -#endif // TMWA_MMO_UTILS_HPP +} // namespace tmwa diff --git a/src/mmo/version.cpp b/src/mmo/version.cpp index 54b1709..2e337c1 100644 --- a/src/mmo/version.cpp +++ b/src/mmo/version.cpp @@ -28,6 +28,9 @@ #include "../poison.hpp" + +namespace tmwa +{ Version CURRENT_VERSION = { VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, @@ -61,7 +64,7 @@ Version CURRENT_MAP_SERVER_VERSION = VENDOR_POINT, }; -const char CURRENT_VERSION_STRING[] = VERSION_STRING; +LString CURRENT_VERSION_STRING = VERSION_STRING; bool extract(XString str, Version *vers) { @@ -70,3 +73,4 @@ bool extract(XString str, Version *vers) // It would've been useful during the magic migration. return extract(str, record<'.'>(&vers->major, &vers->minor, &vers->patch)); } +} // namespace tmwa diff --git a/src/mmo/version.hpp b/src/mmo/version.hpp index 11bab39..440dce6 100644 --- a/src/mmo/version.hpp +++ b/src/mmo/version.hpp @@ -1,5 +1,4 @@ -#ifndef TMWA_MMO_VERSION_HPP -#define TMWA_MMO_VERSION_HPP +#pragma once // version.hpp - Prevent mass rebuild when conf/version.hpp changes. // // Copyright © ????-2004 Athena Dev Teams @@ -21,20 +20,24 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. -# include "../sanity.hpp" +#include "fwd.hpp" -# include <cstdint> +#include <cstdint> -# include "../strings/fwd.hpp" +#include "../strings/fwd.hpp" + +namespace tmwa +{ // TODO make these bitwise enums -# define TMWA_FLAG_REGISTRATION 0x01 +#define TMWA_FLAG_REGISTRATION 0x01 -# define TMWA_SERVER_LOGIN 0x01 -# define TMWA_SERVER_CHAR 0x02 -# define TMWA_SERVER_INTER 0x04 -# define TMWA_SERVER_MAP 0x08 +#define TMWA_SERVER_LOGIN 0x01 +#define TMWA_SERVER_CHAR 0x02 +#define TMWA_SERVER_INTER 0x04 +#define TMWA_SERVER_MAP 0x08 +// TODO now that I generate the protocol, split 'flags' out of the struct struct Version { uint8_t major; @@ -46,6 +49,35 @@ struct Version uint8_t which; uint16_t vend; // can't add vendor name yet + + constexpr friend + bool operator < (Version l, Version r) + { + return (l.major < r.major + || (l.major == r.major + && (l.minor < r.minor + || (l.minor == r.minor + && (l.patch < r.patch + || (l.patch == r.patch + && (l.devel < r.devel + || (l.devel == r.devel + && l.vend < r.vend)))))))); + } + constexpr friend + bool operator > (Version l, Version r) + { + return r < l; + } + constexpr friend + bool operator <= (Version l, Version r) + { + return !(r < l); + } + constexpr friend + bool operator >= (Version l, Version r) + { + return !(l < r); + } }; static_assert(sizeof(Version) == 8, "this is sent over the network, can't change"); @@ -55,37 +87,7 @@ extern Version CURRENT_LOGIN_SERVER_VERSION; extern Version CURRENT_CHAR_SERVER_VERSION; extern Version CURRENT_MAP_SERVER_VERSION; -extern const char CURRENT_VERSION_STRING[]; +extern LString CURRENT_VERSION_STRING; bool extract(XString str, Version *vers); - -constexpr -bool operator < (Version l, Version r) -{ - return (l.major < r.major - || (l.major == r.major - && (l.minor < r.minor - || (l.minor == r.minor - && (l.patch < r.patch - || (l.patch == r.patch - && (l.devel < r.devel - || (l.devel == r.devel - && l.vend < r.vend)))))))); -} -constexpr -bool operator > (Version l, Version r) -{ - return r < l; -} -constexpr -bool operator <= (Version l, Version r) -{ - return !(r < l); -} -constexpr -bool operator >= (Version l, Version r) -{ - return !(l < r); -} - -#endif // TMWA_MMO_VERSION_HPP +} // namespace tmwa |