From c812c92d1a1835f0bda783e709481188c8d92225 Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Sat, 15 Mar 2014 19:34:59 -0700 Subject: Clean up header organization --- src/admin/GNUmakefile | 7 - src/admin/ladmin.cpp | 17 +- src/admin/main.cpp | 2 + src/char/GNUmakefile | 7 - src/char/char.cpp | 19 +- src/char/char.hpp | 13 +- src/char/int_party.cpp | 9 +- src/char/int_party.hpp | 6 +- src/char/int_storage.cpp | 9 +- src/char/int_storage.hpp | 6 +- src/char/inter.cpp | 13 +- src/char/inter.hpp | 6 +- src/char/main.cpp | 2 + src/common/GNUmakefile | 7 - src/common/config_parse.cpp | 154 ------------ src/common/config_parse.hpp | 36 --- src/common/const_array.hpp | 132 ---------- src/common/core.cpp | 124 --------- src/common/core.hpp | 22 -- src/common/db.cpp | 3 - src/common/db.hpp | 180 ------------- src/common/dumb_ptr.hpp | 276 -------------------- src/common/extract.cpp | 62 ----- src/common/extract.hpp | 223 ---------------- src/common/extract_test.cpp | 334 ------------------------ src/common/human_time_diff.hpp | 86 ------- src/common/human_time_diff_test.cpp | 83 ------ src/common/intern-pool.hpp | 41 --- src/common/intern-pool_test.cpp | 20 -- src/common/ip.cpp | 114 --------- src/common/ip.hpp | 164 ------------ src/common/ip.py | 14 -- src/common/ip_test.cpp | 332 ------------------------ src/common/iter.hpp | 97 ------- src/common/iter_test.cpp | 82 ------ src/common/matrix.hpp | 50 ---- src/common/md5calc.cpp | 352 -------------------------- src/common/md5calc.hpp | 64 ----- src/common/md5calc_test.cpp | 30 --- src/common/mmo.hpp | 380 ---------------------------- src/common/nullpo.cpp | 28 --- src/common/nullpo.hpp | 41 --- src/common/operators.hpp | 47 ---- src/common/random.cpp | 8 - src/common/random.hpp | 69 ----- src/common/random.t.hpp | 23 -- src/common/random2.hpp | 74 ------ src/common/socket.cpp | 474 ----------------------------------- src/common/socket.hpp | 371 --------------------------- src/common/timer.cpp | 201 --------------- src/common/timer.hpp | 30 --- src/common/timer.t.hpp | 68 ----- src/common/utils.cpp | 101 -------- src/common/utils.hpp | 140 ----------- src/common/utils2.hpp | 256 ------------------- src/common/version.cpp | 53 ---- src/common/version.hpp | 69 ----- src/compat/alg.cpp | 3 + src/compat/alg.hpp | 21 ++ src/compat/attr.hpp | 13 + src/compat/cast.cpp | 3 + src/compat/cast.hpp | 54 ++++ src/compat/fun.hpp | 11 + src/compat/iter.cpp | 3 + src/compat/iter.hpp | 97 +++++++ src/compat/iter_test.cpp | 82 ++++++ src/compat/memory.cpp | 3 + src/compat/memory.hpp | 27 ++ src/compat/nullpo.cpp | 28 +++ src/compat/nullpo.hpp | 41 +++ src/compat/rawmem.cpp | 3 + src/compat/rawmem.hpp | 30 +++ src/conf/version.hpp | 6 +- src/generic/const_array.cpp | 3 + src/generic/const_array.hpp | 132 ++++++++++ src/generic/db.cpp | 3 + src/generic/db.hpp | 180 +++++++++++++ src/generic/enum.cpp | 3 + src/generic/enum.hpp | 170 +++++++++++++ src/generic/intern-pool.cpp | 3 + src/generic/intern-pool.hpp | 41 +++ src/generic/intern-pool_test.cpp | 20 ++ src/generic/matrix.cpp | 3 + src/generic/matrix.hpp | 54 ++++ src/generic/md5.cpp | 234 +++++++++++++++++ src/generic/md5.hpp | 44 ++++ src/generic/md5_test.cpp | 28 +++ src/generic/operators.cpp | 3 + src/generic/operators.hpp | 47 ++++ src/generic/random.cpp | 8 + src/generic/random.hpp | 69 +++++ src/generic/random.t.hpp | 23 ++ src/generic/random2.hpp | 75 ++++++ src/io/cxxstdio.cpp | 2 + src/io/cxxstdio.hpp | 61 +++-- src/io/fd.cpp | 1 + src/io/fd.hpp | 2 +- src/io/fwd.hpp | 2 +- src/io/line.hpp | 2 +- src/io/read.hpp | 2 +- src/io/tty.cpp | 2 + src/io/tty.hpp | 2 +- src/io/write.hpp | 2 +- src/login/GNUmakefile | 7 - src/login/login.cpp | 25 +- src/login/main.cpp | 2 + src/map/GNUmakefile | 7 - src/map/atcommand.cpp | 24 +- src/map/atcommand.hpp | 8 +- src/map/battle.cpp | 9 +- src/map/battle.hpp | 8 +- src/map/battle.t.hpp | 8 +- src/map/chrif.cpp | 10 +- src/map/chrif.hpp | 12 +- src/map/clif.cpp | 17 +- src/map/clif.hpp | 13 +- src/map/clif.t.hpp | 6 +- src/map/grfio.cpp | 2 +- src/map/grfio.hpp | 8 +- src/map/intif.cpp | 5 +- src/map/intif.hpp | 8 +- src/map/itemdb.cpp | 14 +- src/map/itemdb.hpp | 8 +- src/map/magic-expr-eval.hpp | 8 +- src/map/magic-expr.cpp | 6 +- src/map/magic-expr.hpp | 6 +- src/map/magic-interpreter-aux.hpp | 6 +- src/map/magic-interpreter-base.cpp | 2 +- src/map/magic-interpreter-parser.ypp | 4 +- src/map/magic-interpreter.hpp | 6 +- src/map/magic-interpreter.t.hpp | 8 +- src/map/magic-stmt.cpp | 8 +- src/map/magic.hpp | 8 +- src/map/main.cpp | 2 + src/map/map.cpp | 21 +- src/map/map.hpp | 15 +- src/map/map.t.hpp | 9 +- src/map/mapflag.hpp | 2 +- src/map/mob.cpp | 15 +- src/map/mob.hpp | 13 +- src/map/mob.t.hpp | 6 +- src/map/npc.cpp | 15 +- src/map/npc.hpp | 8 +- src/map/party.cpp | 10 +- src/map/party.hpp | 6 +- src/map/path.cpp | 7 +- src/map/path.hpp | 6 +- src/map/pc.cpp | 12 +- src/map/pc.hpp | 6 +- src/map/pc.t.hpp | 6 +- src/map/script.cpp | 21 +- src/map/script.hpp | 13 +- src/map/script.py | 3 + src/map/skill.cpp | 14 +- src/map/skill.hpp | 6 +- src/map/skill.t.hpp | 8 +- src/map/storage.cpp | 8 +- src/map/storage.hpp | 8 +- src/map/tmw.cpp | 4 +- src/map/tmw.hpp | 11 +- src/map/trade.cpp | 4 +- src/map/trade.hpp | 6 +- src/mmo/config_parse.cpp | 154 ++++++++++++ src/mmo/config_parse.hpp | 36 +++ src/mmo/core.cpp | 125 +++++++++ src/mmo/core.hpp | 22 ++ src/mmo/dumb_ptr.cpp | 3 + src/mmo/dumb_ptr.hpp | 276 ++++++++++++++++++++ src/mmo/extract.cpp | 62 +++++ src/mmo/extract.hpp | 224 +++++++++++++++++ src/mmo/extract_test.cpp | 334 ++++++++++++++++++++++++ src/mmo/human_time_diff.cpp | 3 + src/mmo/human_time_diff.hpp | 86 +++++++ src/mmo/human_time_diff_test.cpp | 83 ++++++ src/mmo/ip.cpp | 114 +++++++++ src/mmo/ip.hpp | 164 ++++++++++++ src/mmo/ip.py | 14 ++ src/mmo/ip_test.cpp | 332 ++++++++++++++++++++++++ src/mmo/md5more.cpp | 128 ++++++++++ src/mmo/md5more.hpp | 26 ++ src/mmo/mmo.cpp | 3 + src/mmo/mmo.hpp | 377 ++++++++++++++++++++++++++++ src/mmo/socket.cpp | 474 +++++++++++++++++++++++++++++++++++ src/mmo/socket.hpp | 373 +++++++++++++++++++++++++++ src/mmo/timer.cpp | 201 +++++++++++++++ src/mmo/timer.hpp | 30 +++ src/mmo/timer.t.hpp | 68 +++++ src/mmo/utils.cpp | 101 ++++++++ src/mmo/utils.hpp | 116 +++++++++ src/mmo/version.cpp | 55 ++++ src/mmo/version.hpp | 69 +++++ src/monitor/GNUmakefile | 7 - src/monitor/main.cpp | 4 +- src/sanity.hpp | 5 - src/strings/astring.cpp | 2 + src/strings/base.hpp | 2 + src/strings/fwd.hpp | 2 + src/strings/mstring.cpp | 2 + src/strings/rstring.cpp | 2 + src/strings/sstring.cpp | 2 + src/strings/tstring.cpp | 2 + src/strings/vstring.cpp | 2 + src/strings/vstring.hpp | 8 + src/strings/vstring.tcc | 2 +- src/strings/xstring.cpp | 2 + src/strings/zstring.cpp | 2 + 206 files changed, 6030 insertions(+), 5874 deletions(-) delete mode 100644 src/admin/GNUmakefile delete mode 100644 src/char/GNUmakefile delete mode 100644 src/common/GNUmakefile delete mode 100644 src/common/config_parse.cpp delete mode 100644 src/common/config_parse.hpp delete mode 100644 src/common/const_array.hpp delete mode 100644 src/common/core.cpp delete mode 100644 src/common/core.hpp delete mode 100644 src/common/db.cpp delete mode 100644 src/common/db.hpp delete mode 100644 src/common/dumb_ptr.hpp delete mode 100644 src/common/extract.cpp delete mode 100644 src/common/extract.hpp delete mode 100644 src/common/extract_test.cpp delete mode 100644 src/common/human_time_diff.hpp delete mode 100644 src/common/human_time_diff_test.cpp delete mode 100644 src/common/intern-pool.hpp delete mode 100644 src/common/intern-pool_test.cpp delete mode 100644 src/common/ip.cpp delete mode 100644 src/common/ip.hpp delete mode 100644 src/common/ip.py delete mode 100644 src/common/ip_test.cpp delete mode 100644 src/common/iter.hpp delete mode 100644 src/common/iter_test.cpp delete mode 100644 src/common/matrix.hpp delete mode 100644 src/common/md5calc.cpp delete mode 100644 src/common/md5calc.hpp delete mode 100644 src/common/md5calc_test.cpp delete mode 100644 src/common/mmo.hpp delete mode 100644 src/common/nullpo.cpp delete mode 100644 src/common/nullpo.hpp delete mode 100644 src/common/operators.hpp delete mode 100644 src/common/random.cpp delete mode 100644 src/common/random.hpp delete mode 100644 src/common/random.t.hpp delete mode 100644 src/common/random2.hpp delete mode 100644 src/common/socket.cpp delete mode 100644 src/common/socket.hpp delete mode 100644 src/common/timer.cpp delete mode 100644 src/common/timer.hpp delete mode 100644 src/common/timer.t.hpp delete mode 100644 src/common/utils.cpp delete mode 100644 src/common/utils.hpp delete mode 100644 src/common/utils2.hpp delete mode 100644 src/common/version.cpp delete mode 100644 src/common/version.hpp create mode 100644 src/compat/alg.cpp create mode 100644 src/compat/alg.hpp create mode 100644 src/compat/attr.hpp create mode 100644 src/compat/cast.cpp create mode 100644 src/compat/cast.hpp create mode 100644 src/compat/fun.hpp create mode 100644 src/compat/iter.cpp create mode 100644 src/compat/iter.hpp create mode 100644 src/compat/iter_test.cpp create mode 100644 src/compat/memory.cpp create mode 100644 src/compat/memory.hpp create mode 100644 src/compat/nullpo.cpp create mode 100644 src/compat/nullpo.hpp create mode 100644 src/compat/rawmem.cpp create mode 100644 src/compat/rawmem.hpp create mode 100644 src/generic/const_array.cpp create mode 100644 src/generic/const_array.hpp create mode 100644 src/generic/db.cpp create mode 100644 src/generic/db.hpp create mode 100644 src/generic/enum.cpp create mode 100644 src/generic/enum.hpp create mode 100644 src/generic/intern-pool.cpp create mode 100644 src/generic/intern-pool.hpp create mode 100644 src/generic/intern-pool_test.cpp create mode 100644 src/generic/matrix.cpp create mode 100644 src/generic/matrix.hpp create mode 100644 src/generic/md5.cpp create mode 100644 src/generic/md5.hpp create mode 100644 src/generic/md5_test.cpp create mode 100644 src/generic/operators.cpp create mode 100644 src/generic/operators.hpp create mode 100644 src/generic/random.cpp create mode 100644 src/generic/random.hpp create mode 100644 src/generic/random.t.hpp create mode 100644 src/generic/random2.hpp delete mode 100644 src/login/GNUmakefile delete mode 100644 src/map/GNUmakefile create mode 100644 src/mmo/config_parse.cpp create mode 100644 src/mmo/config_parse.hpp create mode 100644 src/mmo/core.cpp create mode 100644 src/mmo/core.hpp create mode 100644 src/mmo/dumb_ptr.cpp create mode 100644 src/mmo/dumb_ptr.hpp create mode 100644 src/mmo/extract.cpp create mode 100644 src/mmo/extract.hpp create mode 100644 src/mmo/extract_test.cpp create mode 100644 src/mmo/human_time_diff.cpp create mode 100644 src/mmo/human_time_diff.hpp create mode 100644 src/mmo/human_time_diff_test.cpp create mode 100644 src/mmo/ip.cpp create mode 100644 src/mmo/ip.hpp create mode 100644 src/mmo/ip.py create mode 100644 src/mmo/ip_test.cpp create mode 100644 src/mmo/md5more.cpp create mode 100644 src/mmo/md5more.hpp create mode 100644 src/mmo/mmo.cpp create mode 100644 src/mmo/mmo.hpp create mode 100644 src/mmo/socket.cpp create mode 100644 src/mmo/socket.hpp create mode 100644 src/mmo/timer.cpp create mode 100644 src/mmo/timer.hpp create mode 100644 src/mmo/timer.t.hpp create mode 100644 src/mmo/utils.cpp create mode 100644 src/mmo/utils.hpp create mode 100644 src/mmo/version.cpp create mode 100644 src/mmo/version.hpp delete mode 100644 src/monitor/GNUmakefile (limited to 'src') diff --git a/src/admin/GNUmakefile b/src/admin/GNUmakefile deleted file mode 100644 index a34b76b..0000000 --- a/src/admin/GNUmakefile +++ /dev/null @@ -1,7 +0,0 @@ -.SUFFIXES: -bin/tmwa-admin: - ${MAKE} -C ../.. bin/tmwa-admin -clean: - rm -r ../../obj/admin/ -%:: - ${MAKE} -C ../.. obj/admin/$@ diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp index 38f0c4b..f889b19 100644 --- a/src/admin/ladmin.cpp +++ b/src/admin/ladmin.cpp @@ -11,19 +11,20 @@ #include "../strings/xstring.hpp" #include "../strings/vstring.hpp" +#include "../generic/md5.hpp" + #include "../io/cxxstdio.hpp" #include "../io/read.hpp" #include "../io/tty.hpp" #include "../io/write.hpp" -#include "../common/config_parse.hpp" -#include "../common/core.hpp" -#include "../common/human_time_diff.hpp" -#include "../common/md5calc.hpp" -#include "../common/mmo.hpp" -#include "../common/socket.hpp" -#include "../common/utils.hpp" -#include "../common/version.hpp" +#include "../mmo/config_parse.hpp" +#include "../mmo/core.hpp" +#include "../mmo/human_time_diff.hpp" +#include "../mmo/mmo.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/utils.hpp" +#include "../mmo/version.hpp" #include "../poison.hpp" diff --git a/src/admin/main.cpp b/src/admin/main.cpp index 2900720..bed1539 100644 --- a/src/admin/main.cpp +++ b/src/admin/main.cpp @@ -1,2 +1,4 @@ // dummy file to make Make dependencies work #include "ladmin.hpp" + +#include "../poison.hpp" diff --git a/src/char/GNUmakefile b/src/char/GNUmakefile deleted file mode 100644 index 48b4c0a..0000000 --- a/src/char/GNUmakefile +++ /dev/null @@ -1,7 +0,0 @@ -.SUFFIXES: -bin/tmwa-char: - ${MAKE} -C ../.. bin/tmwa-char -clean: - rm -r ../../obj/char/ -%:: - ${MAKE} -C ../.. obj/char/$@ diff --git a/src/char/char.cpp b/src/char/char.cpp index f6fd492..b74df46 100644 --- a/src/char/char.cpp +++ b/src/char/char.cpp @@ -17,24 +17,27 @@ #include #include +#include "../compat/alg.hpp" + #include "../strings/mstring.hpp" #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" +#include "../generic/db.hpp" + #include "../io/cxxstdio.hpp" #include "../io/lock.hpp" #include "../io/read.hpp" #include "../io/tty.hpp" -#include "../common/config_parse.hpp" -#include "../common/core.hpp" -#include "../common/db.hpp" -#include "../common/extract.hpp" -#include "../common/human_time_diff.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" -#include "../common/version.hpp" +#include "../mmo/config_parse.hpp" +#include "../mmo/core.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/human_time_diff.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" +#include "../mmo/version.hpp" #include "inter.hpp" #include "int_party.hpp" diff --git a/src/char/char.hpp b/src/char/char.hpp index 57cacee..6c47c1a 100644 --- a/src/char/char.hpp +++ b/src/char/char.hpp @@ -1,11 +1,12 @@ -#ifndef CHAR_HPP -#define CHAR_HPP +#ifndef TMWA_CHAR_CHAR_HPP +#define TMWA_CHAR_CHAR_HPP # include "../strings/fwd.hpp" -# include "../common/const_array.hpp" -# include "../common/ip.hpp" -# include "../common/mmo.hpp" +# include "../generic/const_array.hpp" + +# include "../mmo/ip.hpp" +# include "../mmo/mmo.hpp" class Session; @@ -32,4 +33,4 @@ void char_log(XString line); # define CHAR_LOG(fmt, ...) \ char_log(STRPRINTF(fmt, ## __VA_ARGS__)) -#endif // CHAR_HPP +#endif // TMWA_CHAR_CHAR_HPP diff --git a/src/char/int_party.cpp b/src/char/int_party.cpp index 85af96c..c0bedde 100644 --- a/src/char/int_party.cpp +++ b/src/char/int_party.cpp @@ -7,14 +7,15 @@ #include "../strings/astring.hpp" #include "../strings/xstring.hpp" +#include "../generic/db.hpp" + #include "../io/cxxstdio.hpp" #include "../io/lock.hpp" #include "../io/read.hpp" -#include "../common/db.hpp" -#include "../common/extract.hpp" -#include "../common/mmo.hpp" -#include "../common/socket.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/mmo.hpp" +#include "../mmo/socket.hpp" #include "char.hpp" #include "inter.hpp" diff --git a/src/char/int_party.hpp b/src/char/int_party.hpp index e4dedd1..73020a9 100644 --- a/src/char/int_party.hpp +++ b/src/char/int_party.hpp @@ -1,5 +1,5 @@ -#ifndef INT_PARTY_HPP -#define INT_PARTY_HPP +#ifndef TMWA_CHAR_INT_PARTY_HPP +#define TMWA_CHAR_INT_PARTY_HPP # include "../strings/fwd.hpp" @@ -14,4 +14,4 @@ void inter_party_leave(int party_id, int account_id); extern AString party_txt; -#endif // INT_PARTY_HPP +#endif // TMWA_CHAR_INT_PARTY_HPP diff --git a/src/char/int_storage.cpp b/src/char/int_storage.cpp index 91151ec..0794c8f 100644 --- a/src/char/int_storage.cpp +++ b/src/char/int_storage.cpp @@ -9,14 +9,15 @@ #include "../strings/astring.hpp" #include "../strings/xstring.hpp" +#include "../generic/db.hpp" + #include "../io/cxxstdio.hpp" #include "../io/lock.hpp" #include "../io/read.hpp" -#include "../common/db.hpp" -#include "../common/extract.hpp" -#include "../common/mmo.hpp" -#include "../common/socket.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/mmo.hpp" +#include "../mmo/socket.hpp" #include "../poison.hpp" diff --git a/src/char/int_storage.hpp b/src/char/int_storage.hpp index 7081f30..356d924 100644 --- a/src/char/int_storage.hpp +++ b/src/char/int_storage.hpp @@ -1,5 +1,5 @@ -#ifndef INT_STORAGE_HPP -#define INT_STORAGE_HPP +#ifndef TMWA_CHAR_INT_STORAGE_HPP +#define TMWA_CHAR_INT_STORAGE_HPP # include "../strings/fwd.hpp" @@ -14,4 +14,4 @@ int inter_storage_parse_frommap(Session *ms); extern AString storage_txt; -#endif // INT_STORAGE_HPP +#endif // TMWA_CHAR_INT_STORAGE_HPP diff --git a/src/char/inter.cpp b/src/char/inter.cpp index 4fea6be..c287844 100644 --- a/src/char/inter.cpp +++ b/src/char/inter.cpp @@ -11,16 +11,17 @@ #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" +#include "../generic/db.hpp" + #include "../io/cxxstdio.hpp" #include "../io/lock.hpp" #include "../io/read.hpp" -#include "../common/config_parse.hpp" -#include "../common/db.hpp" -#include "../common/extract.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" -#include "../common/utils.hpp" +#include "../mmo/config_parse.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" +#include "../mmo/utils.hpp" #include "char.hpp" #include "int_party.hpp" diff --git a/src/char/inter.hpp b/src/char/inter.hpp index b566575..6dd907d 100644 --- a/src/char/inter.hpp +++ b/src/char/inter.hpp @@ -1,5 +1,5 @@ -#ifndef INTER_HPP -#define INTER_HPP +#ifndef TMWA_CHAR_INTER_HPP +#define TMWA_CHAR_INTER_HPP # include "../strings/fwd.hpp" @@ -14,4 +14,4 @@ int inter_check_length(Session *ms, int length); extern int party_share_level; -#endif // INTER_HPP +#endif // TMWA_CHAR_INTER_HPP diff --git a/src/char/main.cpp b/src/char/main.cpp index 9f68e0f..de51ae3 100644 --- a/src/char/main.cpp +++ b/src/char/main.cpp @@ -1,2 +1,4 @@ // dummy file to make Make dependencies work #include "char.hpp" + +#include "../poison.hpp" diff --git a/src/common/GNUmakefile b/src/common/GNUmakefile deleted file mode 100644 index 917ce0e..0000000 --- a/src/common/GNUmakefile +++ /dev/null @@ -1,7 +0,0 @@ -.SUFFIXES: -common: - ${MAKE} -C ../.. common -clean: - rm -r ../../obj/common/ -%:: - ${MAKE} -C ../.. obj/common/$@ diff --git a/src/common/config_parse.cpp b/src/common/config_parse.cpp deleted file mode 100644 index d677d8e..0000000 --- a/src/common/config_parse.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include "config_parse.hpp" -// config_parse.hpp - Framework for per-server config parsers. -// -// Copyright © 2014 Ben Longbons -// -// 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 . - -#include "../strings/xstring.hpp" -#include "../strings/zstring.hpp" - -#include "../io/cxxstdio.hpp" -#include "../io/line.hpp" - -#include "version.hpp" - -#include "../poison.hpp" - -bool is_comment(XString line) -{ - return not line or line.startswith("//"); -} - -template -inline -bool config_split_impl(ZS line, XString *key, ZS *value) -{ - // unconditionally fail if line contains control characters - if (std::find_if(line.begin(), line.end(), - [](unsigned char c) { return c < ' '; } - ) != line.end()) - return false; - // try to find a colon, fail if not found - typename ZS::iterator colon = std::find(line.begin(), line.end(), ':'); - if (colon == line.end()) - return false; - - *key = line.xislice_h(colon).strip(); - // move past the colon and any spaces - ++colon; - *value = line.xislice_t(colon).lstrip(); - return true; -} - -// eventually this should go away -bool config_split(ZString line, XString *key, ZString *value) -{ - return config_split_impl(line, key, value); -} -// and use this instead -bool config_split(XString line, XString *key, XString *value) -{ - return config_split_impl(line, key, 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) -{ - io::LineReader in(filename); - if (!in.is_open()) - { - PRINTF("Unable to open file: %s\n", filename); - return false; - } - io::Line line; - bool rv = true; - while (in.read_line(line)) - { - if (is_comment(line.text)) - continue; - XString key; - ZString value; - if (!config_split(line.text, &key, &value)) - { - line.error("Bad config line"); - rv = false; - continue; - } - if (key == "import") - { - rv &= load_config_file(value, slave); - continue; - } - else if (key == "version-lt") - { - Version vers; - if (!extract(value, &vers)) - { - rv = false; - continue; - } - if (CURRENT_VERSION < vers) - continue; - break; - } - else if (key == "version-le") - { - Version vers; - if (!extract(value, &vers)) - { - rv = false; - continue; - } - if (CURRENT_VERSION <= vers) - continue; - break; - } - else if (key == "version-gt") - { - Version vers; - if (!extract(value, &vers)) - { - rv = false; - continue; - } - if (CURRENT_VERSION > vers) - continue; - break; - } - else if (key == "version-ge") - { - Version vers; - if (!extract(value, &vers)) - { - rv = false; - continue; - } - if (CURRENT_VERSION >= vers) - continue; - break; - } - else if (!slave(key, value)) - { - line.error("Bad config key or value"); - rv = false; - continue; - } - // nothing to see here, move along - } - return rv; -} diff --git a/src/common/config_parse.hpp b/src/common/config_parse.hpp deleted file mode 100644 index 6a55174..0000000 --- a/src/common/config_parse.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef TMWA_COMMON_CONFIG_PARSE_HPP -#define TMWA_COMMON_CONFIG_PARSE_HPP -// config_parse.hpp - Framework for per-server config parsers. -// -// Copyright © 2014 Ben Longbons -// -// 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 . - -# include "../sanity.hpp" - -# include "../strings/fwd.hpp" - -typedef bool (*ConfigItemParser)(XString key, ZString value); - -bool is_comment(XString line); -bool config_split(ZString line, XString *key, ZString *value); -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_COMMON_CONFIG_PARSE_HPP diff --git a/src/common/const_array.hpp b/src/common/const_array.hpp deleted file mode 100644 index fe15728..0000000 --- a/src/common/const_array.hpp +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef CONST_ARRAY_HPP -#define CONST_ARRAY_HPP -// const_array.hpp - just a pointer-to-const and a length -// -// Copyright © 2011-2012 Ben Longbons -// -// 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 . - -# include "../sanity.hpp" - -# include - -# include -# include -# include - -# ifdef WORKAROUND_GCC46_COMPILER -// constexpr is buggy with templates in this version -// Is this still needed now that const_string is removed? -# define constexpr /* nothing */ -# endif - -// TODO see if I ever actually use this, and not the subclass -template -class const_array -{ - const T *d; - size_t n; -public: - typedef const T *iterator; - typedef std::reverse_iterator reverse_iterator; - - constexpr - const_array(std::nullptr_t) - : d(nullptr), n(0) - {} - - constexpr - const_array(const T *p, size_t z) - : d(p), n(z) - {} - - constexpr - const_array(const T *b, const T *e) - : d(b), n(e - b) - {} - - const_array(std::initializer_list list) - : d(list.begin()), n(list.size()) - {} - - // Implicit conversion from std::vector - const_array(const std::vector& v) - : d(v.data()), n(v.size()) - {} - - // but disallow conversion from a temporary - const_array(std::vector&&) = delete; - - // Oops. see src/warnings.hpp - constexpr - const T *data() const { return d; } - constexpr - size_t size() const { return n; } - constexpr - bool empty() const { return not n; } - constexpr explicit - operator bool() const { return n; } - - constexpr - std::pair cut(size_t o) const - { - return {const_array(d, o), const_array(d + o, n - o)}; - } - - constexpr - const_array first(size_t o) const - { - return cut(o).first; - } - - constexpr - const_array last(size_t l) const - { - return cut(size() - l).second; - } - - constexpr - const_array after(size_t o) const - { - return cut(o).second; - } - - constexpr - iterator begin() const { return d; } - constexpr - iterator end() const { return d + n; } - constexpr - reverse_iterator rbegin() const { return reverse_iterator(end()); } - constexpr - reverse_iterator rend() const { return reverse_iterator(begin()); } - - constexpr - const T& front() const { return *begin(); } - constexpr - const T& back() const { return *rbegin(); } - - // This probably shouldn't be used, but I'm adding it for porting. - T& operator[](size_t i) - { - return const_cast(d[i]); - } -}; - -# ifdef WORKAROUND_GCC46_COMPILER -# undef constexpr -# endif - -#endif // CONST_ARRAY_HPP diff --git a/src/common/core.cpp b/src/common/core.cpp deleted file mode 100644 index d57e970..0000000 --- a/src/common/core.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "core.hpp" - -#include - -#include - -#include -#include -#include - -#include "../strings/zstring.hpp" - -#include "../io/cxxstdio.hpp" - -#include "random.hpp" -#include "socket.hpp" -#include "timer.hpp" - -#include "../poison.hpp" - -// Added by Gabuzomeu -// -// This is an implementation of signal() using sigaction() for portability. -// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced -// Programming in the UNIX Environment_. -// -typedef void(*sigfunc)(int); -static -sigfunc compat_signal(int signo, sigfunc func) -{ - struct sigaction sact, oact; - - sact.sa_handler = func; - sigfillset(&sact.sa_mask); - sigdelset(&sact.sa_mask, SIGSEGV); - sigdelset(&sact.sa_mask, SIGBUS); - sigdelset(&sact.sa_mask, SIGTRAP); - sigdelset(&sact.sa_mask, SIGILL); - sigdelset(&sact.sa_mask, SIGFPE); - sact.sa_flags = 0; - - if (sigaction(signo, &sact, &oact) < 0) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" - return SIG_ERR; -#pragma GCC diagnostic pop - - return oact.sa_handler; -} - -volatile -bool runflag = true; - -static -void chld_proc(int) -{ - wait(NULL); -} -static -void sig_proc(int) -{ - for (int i = 1; i < 31; ++i) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" - compat_signal(i, SIG_IGN); -#pragma GCC diagnostic pop - runflag = false; -} - -/* - Note about fatal signals: - - Under certain circumstances, - the following signals MUST not be ignored: - SIGFPE, SIGSEGV, SIGILL - Unless you use SA_SIGINFO and *carefully* check the origin, - that means they must be SIG_DFL. - */ -int main(int argc, char **argv) -{ - // ZString args[argc]; is (deliberately!) not supported by clang yet - ZString *args = static_cast(alloca(argc * sizeof(ZString))); - for (int i = 0; i < argc; ++i) - args[i] = ZString(strings::really_construct_from_a_pointer, argv[i], nullptr); - do_init(argc, args); - - if (!runflag) - { - PRINTF("Fatal error during startup; exiting\n"); - 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" - compat_signal(SIGPIPE, SIG_IGN); -#pragma GCC diagnostic 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" - 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 - - atexit(term_func); - - while (runflag) - { - // TODO - if timers take a long time to run, this - // may wait too long in sendrecv - tick_t now = milli_clock::now(); - interval_t next = do_timer(now); - do_sendrecv(next); - do_parsepacket(); - } -} diff --git a/src/common/core.hpp b/src/common/core.hpp deleted file mode 100644 index 8141e9c..0000000 --- a/src/common/core.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef CORE_HPP -#define CORE_HPP - -# include "../sanity.hpp" - -# include "../strings/fwd.hpp" - -/// core.c contains a server-independent main() function -/// and then runs a do_sendrecv loop - -/// When this is cleared, the server exits gracefully. -extern volatile bool runflag; - -/// This is an external function defined by each server -/// This function must register stuff for the parse loop -extern int do_init(int, ZString *); - -/// Cleanup function called whenever a signal kills us -/// or when if we manage to exit() gracefully. -extern void term_func(void); - -#endif // CORE_HPP diff --git a/src/common/db.cpp b/src/common/db.cpp deleted file mode 100644 index cc58ad8..0000000 --- a/src/common/db.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "db.hpp" - -#include "../poison.hpp" diff --git a/src/common/db.hpp b/src/common/db.hpp deleted file mode 100644 index 0384f1c..0000000 --- a/src/common/db.hpp +++ /dev/null @@ -1,180 +0,0 @@ -#ifndef DB_HPP -#define DB_HPP -// db.hpp - convenience wrappers over std::map -// -// Copyright © 2013 Ben Longbons -// -// 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 . - -# include "../sanity.hpp" - -# include -# include - -template -class Map -{ - typedef std::map Impl; - - Impl impl; -public: - Map() = default; - Map(std::initializer_list> il) - : impl(il) - {} - typedef typename Impl::iterator iterator; - typedef typename Impl::const_iterator const_iterator; - - iterator begin() { return impl.begin(); } - iterator end() { return impl.end(); } - const_iterator begin() const { return impl.begin(); } - const_iterator end() const { return impl.end(); } - - V *search(const K& k) - { - iterator it = impl.find(k); - if (it == impl.end()) - return nullptr; - return &it->second; - } - const V *search(const K& k) const - { - const_iterator it = impl.find(k); - if (it == impl.end()) - return nullptr; - return &it->second; - } - void insert(const K& k, V v) - { - // As far as I can tell, this is the simplest way to - // implement move-only insert-with-replacement. - iterator it = impl.lower_bound(k); - // invariant: if it is valid, it->first >= k - if (it != impl.end() && it->first == k) - it->second = std::move(v); - else - it = impl.insert(std::pair(std::move(k), std::move(v))).first; - return (void)&it->second; - - } - V *init(const K& k) - { - return &impl[k]; - } - void erase(const K& k) - { - impl.erase(k); - } - void clear() - { - impl.clear(); - } - bool empty() const - { - return impl.empty(); - } - size_t size() const - { - return impl.size(); - } -}; - -template -class DMap -{ - typedef Map Impl; - - Impl impl; -public: - typedef typename Impl::iterator iterator; - typedef typename Impl::const_iterator const_iterator; - - iterator begin() { return impl.begin(); } - iterator end() { return impl.end(); } - const_iterator begin() const { return impl.begin(); } - const_iterator end() const { return impl.end(); } - - // const V& ? with a static default V? - V get(const K& k) - { - V *vp = impl.search(k); - return vp ? *vp : V(); - } - void put(const K& k, V v) - { - if (v == V()) - impl.erase(k); - else - impl.insert(k, std::move(v)); - } - void clear() - { - impl.clear(); - } - bool empty() const - { - return impl.empty(); - } - size_t size() const - { - return impl.size(); - } -}; - -template -class UPMap -{ - typedef std::unique_ptr U; - typedef Map Impl; - - Impl impl; -public: - typedef typename Impl::iterator iterator; - typedef typename Impl::const_iterator const_iterator; - - iterator begin() { return impl.begin(); } - iterator end() { return impl.end(); } - const_iterator begin() const { return impl.begin(); } - const_iterator end() const { return impl.end(); } - - // const V& ? with a static default V? - V *get(const K& k) - { - U *up = impl.search(k); - return up ? up->get() : nullptr; - } - void put(const K& k, U v) - { - if (!v) - impl.erase(k); - else - impl.insert(k, std::move(v)); - } - void clear() - { - impl.clear(); - } - bool empty() const - { - return impl.empty(); - } - size_t size() const - { - return impl.size(); - } -}; - -#endif // DB_HPP diff --git a/src/common/dumb_ptr.hpp b/src/common/dumb_ptr.hpp deleted file mode 100644 index bf97f22..0000000 --- a/src/common/dumb_ptr.hpp +++ /dev/null @@ -1,276 +0,0 @@ -#ifndef TMWA_COMMON_DUMB_PTR_HPP -#define TMWA_COMMON_DUMB_PTR_HPP -// ptr.hpp - temporary new/delete wrappers -// -// Copyright © 2013 Ben Longbons -// -// 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 . - -# include "../sanity.hpp" - -# include - -# include - -# include "../strings/astring.hpp" -# include "../strings/zstring.hpp" -# include "../strings/xstring.hpp" - -# include "const_array.hpp" - -// unmanaged new/delete-able pointer -// should be replaced by std::unique_ptr -template -class dumb_ptr -{ - template - friend class dumb_ptr; - T *impl; -public: - explicit - dumb_ptr(T *p=nullptr) - : impl(p) - {} - template - dumb_ptr(dumb_ptr p) - : impl(p.impl) - {} - dumb_ptr(std::nullptr_t) - : impl(nullptr) - {} - - void delete_() - { - delete impl; - *this = nullptr; - } - template - void new_(A&&... a) - { - impl = new T(std::forward(a)...); - } - template - static - dumb_ptr make(A&&... a) - { - return dumb_ptr(new T(std::forward(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 or std::vector -template -class dumb_ptr -{ - 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 make(size_t z) - { - return dumb_ptr(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 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(const_cast(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_COMMON_DUMB_PTR_HPP diff --git a/src/common/extract.cpp b/src/common/extract.cpp deleted file mode 100644 index 378986d..0000000 --- a/src/common/extract.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "extract.hpp" -// extract.cpp - a simple, hierarchical, tokenizer -// -// Copyright © 2013 Ben Longbons -// -// 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 . - -#include "../strings/astring.hpp" -#include "../strings/xstring.hpp" -#include "../strings/vstring.hpp" - -#include "../poison.hpp" - -bool extract(XString str, XString *rv) -{ - *rv = str; - return true; -} - -bool extract(XString str, AString *rv) -{ - *rv = str; - return true; -} - -bool extract(XString str, struct global_reg *var) -{ - return extract(str, - record<','>(&var->str, &var->value)); -} - -bool extract(XString str, struct item *it) -{ - XString ignored; - return extract(str, - record<',', 11>( - &it->id, - &it->nameid, - &it->amount, - &it->equip, - &ignored, - &ignored, - &ignored, - &ignored, - &ignored, - &ignored, - &ignored, - &ignored)); -} diff --git a/src/common/extract.hpp b/src/common/extract.hpp deleted file mode 100644 index 31cf111..0000000 --- a/src/common/extract.hpp +++ /dev/null @@ -1,223 +0,0 @@ -#ifndef EXTRACT_HPP -#define EXTRACT_HPP -// extract.hpp - a simple, hierarchical, tokenizer -// -// Copyright © 2012-2013 Ben Longbons -// -// 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 . - -# include "../sanity.hpp" - -# include - -# include "../strings/xstring.hpp" - -# include "const_array.hpp" -# include "mmo.hpp" -# include "utils.hpp" - -template::value && !std::is_same::value && !std::is_same::value>::type> -bool extract(XString str, T *iv) -{ - if (!str || str.size() > 20) - return false; - if (!((str.front() == '-' && std::is_signed::value) - || ('0' <= str.front() && str.front() <= '9'))) - return false; - // needs a NUL, but can't always be given one. TODO VString? - char buf[20 + 1]; - std::copy(str.begin(), str.end(), buf); - buf[str.size()] = '\0'; - - char *end; - errno = 0; - if (std::is_signed::value) - { - long long v = strtoll(buf, &end, 10); - if (errno || *end) - return false; - *iv = v; - return *iv == v; - } - else - { - unsigned long long v = strtoull(buf, &end, 10); - if (errno || *end) - return false; - *iv = v; - return *iv == v; - } -} - -inline -bool extract(XString str, TimeT *tv) -{ - return extract(str, &tv->value); -} - -// extra typename=void to workaround some duplicate overload rule -template::value>::type, typename=void> -bool extract(XString str, T *iv) -{ - typedef typename underlying_type::type U; - U v; - // defer to integer version - if (!extract(str, &v)) - return false; - // TODO check bounds using enum min/max as in SSCANF - *iv = static_cast(v); - return true; -} - -bool extract(XString str, XString *rv); - -bool extract(XString str, AString *rv); - -template -bool extract(XString str, VString *out) -{ - if (str.size() > N) - return false; - *out = str; - return true; -} - -template -class LStripper -{ -public: - T impl; -}; - -template -LStripper lstripping(T v) -{ - return {v}; -} - -template -bool extract(XString str, LStripper out) -{ - return extract(str.lstrip(), out.impl); -} - -// basically just a std::tuple -// but it provides its data members publically -template -class Record; -template -class Record -{ -}; -template -class Record -{ -public: - F frist; - Record rest; -public: - Record(F f, R... r) - : frist(f), rest(r...) - {} -}; -template -Record record(T... t) -{ - return Record(t...); -} -template -Record record(T... t) -{ - static_assert(0 < n && n < sizeof...(T), "don't be silly"); - return Record(t...); -} - -template -bool extract(XString str, Record) -{ - return !str; -} - -template -bool extract(XString str, Record rec) -{ - XString::iterator s = std::find(str.begin(), str.end(), split); - XString::iterator s2 = s; - if (s2 != str.end()) - ++s2; - XString head = str.xislice_h(s); - XString tail = str.xislice_t(s2); - if (s == str.end()) - return (extract(head, rec.frist) && n <= 1) - || (!head && n <= 0); - - return (extract(head, rec.frist) || n <= 0) - && extract(tail, rec.rest); -} - -template -struct VRecord -{ - std::vector *arr; -}; - -template -VRecord vrec(std::vector *arr) -{ - return {arr}; -} - -template -bool extract(XString str, VRecord rec) -{ - if (!str) - return true; - XString::iterator s = std::find(str.begin(), str.end(), split); - rec.arr->emplace_back(); - if (s == str.end()) - return extract(str, &rec.arr->back()); - return extract(str.xislice_h(s), &rec.arr->back()) - && extract(str.xislice_t(s + 1), rec); -} - -bool extract(XString str, struct global_reg *var); - -bool extract(XString str, struct 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; -} - -inline -bool extract(XString str, CharName *out) -{ - VString<23> tmp; - if (extract(str, &tmp)) - { - *out = CharName(tmp); - return true; - } - return false; -} - -#endif // EXTRACT_HPP diff --git a/src/common/extract_test.cpp b/src/common/extract_test.cpp deleted file mode 100644 index 3d0610e..0000000 --- a/src/common/extract_test.cpp +++ /dev/null @@ -1,334 +0,0 @@ -#include "extract.hpp" - -#include - -#include "../strings/xstring.hpp" - -TEST(extract, record_int) -{ - int x, y, z; - x = y = z = 0; - 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 = 0; - 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 = 0; - 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 = 0; - 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 = 0; - EXPECT_FALSE(extract("1 2 ", 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_EQ(1, x); - EXPECT_EQ(2, y); - EXPECT_EQ(0, z); - x = y = z = 0; - EXPECT_FALSE(extract("1 ", 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_EQ(1, x); - EXPECT_EQ(0, y); - EXPECT_EQ(0, z); - x = y = z = 0; - EXPECT_FALSE(extract(" ", 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_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_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_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_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_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_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_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_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_EQ(1, x); - EXPECT_EQ(0, y); - EXPECT_EQ(0, z); - x = y = z = 0; - EXPECT_FALSE(extract(" ", 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_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_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_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_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_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_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_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_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_EQ(1, x); - EXPECT_EQ(0, y); - EXPECT_EQ(0, z); - x = y = z = 0; - EXPECT_FALSE(extract(" ", 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_EQ(0, x); - EXPECT_EQ(0, y); - EXPECT_EQ(0, z); - x = y = z = 0; -} - -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 = ""; - - 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 ", 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 = ""; -} - -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"); -} diff --git a/src/common/human_time_diff.hpp b/src/common/human_time_diff.hpp deleted file mode 100644 index b8040b8..0000000 --- a/src/common/human_time_diff.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef TMWA_COMMON_HUMAN_TIME_DIFF_HPP -#define TMWA_COMMON_HUMAN_TIME_DIFF_HPP -// human_time_diff.hpp - broken deltas -// -// Copyright © 2013 Ben Longbons -// -// 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 . - -# include "../sanity.hpp" - -# include "../strings/xstring.hpp" - -# include "extract.hpp" - -struct HumanTimeDiff -{ - short year, month, day, hour, minute, second; - - explicit - operator bool() - { - return year || month || day || hour || minute || second; - } - - bool operator !() - { - return !bool(*this); - } -}; - -inline -bool extract(XString str, HumanTimeDiff *iv) -{ - // str is a sequence of [-+]?[0-9]+([ay]|m|[jd]|h|mn|s) - // there are NO spaces here - // parse by counting the number starts - auto is_num = [](char c) - { return c == '-' || c == '+' || ('0' <= c && c <= '9'); }; - if (!str || !is_num(str.front())) - return false; - *iv = HumanTimeDiff{}; - while (str) - { - auto it = std::find_if_not(str.begin(), str.end(), is_num); - auto it2 = std::find_if(it, str.end(), is_num); - XString number = str.xislice_h(it); - XString suffix = str.xislice(it, it2); - str = str.xislice_t(it2); - - short *ptr = nullptr; - if (suffix == "y" || suffix == "a") - ptr = &iv->year; - else if (suffix == "m") - ptr = &iv->month; - else if (suffix == "j" || suffix == "d") - ptr = &iv->day; - else if (suffix == "h") - ptr = &iv->hour; - else if (suffix == "mn") - ptr = &iv->minute; - else if (suffix == "s") - ptr = &iv->second; - else - return false; - if (number.startswith('+') && !number.startswith("+-")) - number = number.xslice_t(1); - if (*ptr || !extract(number, ptr)) - return false; - } - return true; -} - -#endif // TMWA_COMMON_HUMAN_TIME_DIFF_HPP diff --git a/src/common/human_time_diff_test.cpp b/src/common/human_time_diff_test.cpp deleted file mode 100644 index d3ddad1..0000000 --- a/src/common/human_time_diff_test.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "human_time_diff.hpp" - -#include - -// a sequence of [-+]?[0-9]+([ay]|m|[jd]|h|mn|s) - -TEST(humantimediff, single) -{ - HumanTimeDiff diff; - - EXPECT_TRUE(extract("42y", &diff)); - EXPECT_EQ(42, diff.year); - EXPECT_EQ(0, diff.month); - EXPECT_EQ(0, diff.day); - EXPECT_EQ(0, diff.hour); - EXPECT_EQ(0, diff.minute); - EXPECT_EQ(0, diff.second); - - EXPECT_TRUE(extract("42m", &diff)); - EXPECT_EQ(0, diff.year); - EXPECT_EQ(42, diff.month); - EXPECT_EQ(0, diff.day); - EXPECT_EQ(0, diff.hour); - EXPECT_EQ(0, diff.minute); - EXPECT_EQ(0, diff.second); - - EXPECT_TRUE(extract("42d", &diff)); - EXPECT_EQ(0, diff.year); - EXPECT_EQ(0, diff.month); - EXPECT_EQ(42, diff.day); - EXPECT_EQ(0, diff.hour); - EXPECT_EQ(0, diff.minute); - EXPECT_EQ(0, diff.second); - - EXPECT_TRUE(extract("42h", &diff)); - EXPECT_EQ(0, diff.year); - EXPECT_EQ(0, diff.month); - EXPECT_EQ(0, diff.day); - EXPECT_EQ(42, diff.hour); - EXPECT_EQ(0, diff.minute); - EXPECT_EQ(0, diff.second); - - EXPECT_TRUE(extract("42mn", &diff)); - EXPECT_EQ(0, diff.year); - EXPECT_EQ(0, diff.month); - EXPECT_EQ(0, diff.day); - EXPECT_EQ(0, diff.hour); - EXPECT_EQ(42, diff.minute); - EXPECT_EQ(0, diff.second); - - EXPECT_TRUE(extract("42s", &diff)); - EXPECT_EQ(0, diff.year); - EXPECT_EQ(0, diff.month); - EXPECT_EQ(0, diff.day); - EXPECT_EQ(0, diff.hour); - EXPECT_EQ(0, diff.minute); - EXPECT_EQ(42, diff.second); - - EXPECT_TRUE(extract("+42y", &diff)); - EXPECT_EQ(42, diff.year); - EXPECT_TRUE(extract("-42y", &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)); -} - -TEST(humantimediff, multiple) -{ - HumanTimeDiff diff; - - EXPECT_TRUE(extract("42y23m-2d", &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)); -} diff --git a/src/common/intern-pool.hpp b/src/common/intern-pool.hpp deleted file mode 100644 index e75a359..0000000 --- a/src/common/intern-pool.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef INTERN_POOL_HPP -#define INTERN_POOL_HPP - -# include - -# include -# include - -# include "../strings/rstring.hpp" -# include "../strings/zstring.hpp" -# include "../strings/xstring.hpp" - -class InternPool -{ - std::map known; - std::vector names; -public: - size_t intern(XString name_) - { - // TODO just look up the XString, the memory should not move by now - RString name = name_; - // hm, I could change this to do aliases - auto pair = known.insert({name, known.size()}); - if (pair.second) - names.push_back(name); - assert (known.size() == names.size()); - return pair.first->second; - } - - ZString outtern(size_t sz) const - { - return names[sz]; - } - - size_t size() const - { - return known.size(); - } -}; - -#endif //INTERN_POOL_HPP diff --git a/src/common/intern-pool_test.cpp b/src/common/intern-pool_test.cpp deleted file mode 100644 index bf17b67..0000000 --- a/src/common/intern-pool_test.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "intern-pool.hpp" - -#include - -#include "../strings/base.hpp" - -TEST(InternPool, whydoesthisalwaysneedasecondname) -{ - InternPool p; - EXPECT_EQ(0, p.size()); - EXPECT_EQ(0, p.intern("hello")); - EXPECT_EQ(0, p.intern("hello")); - EXPECT_EQ(1, p.size()); - EXPECT_EQ(1, p.intern("world")); - EXPECT_EQ(0, p.intern("hello")); - EXPECT_EQ(1, p.intern("world")); - EXPECT_EQ(2, p.size()); - EXPECT_EQ("hello", p.outtern(0)); - EXPECT_EQ("world", p.outtern(1)); -} diff --git a/src/common/ip.cpp b/src/common/ip.cpp deleted file mode 100644 index 146734a..0000000 --- a/src/common/ip.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "ip.hpp" -// ip.cpp - Implementation of IP address functions. -// -// Copyright © 2013 Ben Longbons -// -// 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 . - -#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(s >> 24), - static_cast(s >> 16), - static_cast(s >> 8), - static_cast(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/common/ip.hpp b/src/common/ip.hpp deleted file mode 100644 index d6e6f04..0000000 --- a/src/common/ip.hpp +++ /dev/null @@ -1,164 +0,0 @@ -#ifndef TMWA_COMMON_IP_HPP -#define TMWA_COMMON_IP_HPP -// ip.hpp - classes to deal with IP addresses. -// -// Copyright © 2013 Ben Longbons -// -// 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 . - -# include "../sanity.hpp" - -# include - -# 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 -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(addr)); - } - explicit - operator struct in_addr() const - { - return reinterpret_cast(_addr); - } - - constexpr friend - IP4Address operator & (IP4Address l, IP4Address r) - { - return IP4Address({ - static_cast(l._addr[0] & r._addr[0]), - static_cast(l._addr[1] & r._addr[1]), - static_cast(l._addr[2] & r._addr[2]), - static_cast(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_COMMON_IP_HPP diff --git a/src/common/ip.py b/src/common/ip.py deleted file mode 100644 index e6a8183..0000000 --- a/src/common/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/common/ip_test.cpp b/src/common/ip_test.cpp deleted file mode 100644 index 7ef1047..0000000 --- a/src/common/ip_test.cpp +++ /dev/null @@ -1,332 +0,0 @@ -#include "ip.hpp" - -#include - -#include "../io/cxxstdio.hpp" - -#define CB(X) (std::integral_constant::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/common/iter.hpp b/src/common/iter.hpp deleted file mode 100644 index 5b39588..0000000 --- a/src/common/iter.hpp +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef TMWA_COMMON_ITER_HPP -#define TMWA_COMMON_ITER_HPP -// iter.hpp - tools for dealing with iterators -// -// Copyright © 2012-2014 Ben Longbons -// -// 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 . - -# include "../sanity.hpp" - -# include - - -/// Simple class to use a pair of iterators with foreach -template -class IteratorPair -{ - It _b, _e; -public: - IteratorPair(It b, It e) - : _b(b), _e(e) - {} - - It begin() { return _b; } - It end() { return _e; } -}; - -template -IteratorPair iterator_pair(It b, It e) -{ - return {b, e}; -} - -template -class PassthroughMath -{ -public: - static - T inced(T v) { return ++v; } -}; - -// An iterator that just directly contains an integer-like value -// TODO port this once the new integer API happens -template> -class ValueIterator -{ - T value; -public: - typedef std::forward_iterator_tag iterator_category; - typedef void difference_type; - typedef T value_type; - typedef void reference; - typedef void pointer; -public: - ValueIterator(T v) - : value(v) - {} - - T operator *() - { - return value; - } - ValueIterator& operator++ () - { - value = Math::inced(value); - return *this; - } - friend bool operator == (ValueIterator l, ValueIterator r) - { - return l.value == r.value; - } - friend bool operator != (ValueIterator l, ValueIterator r) - { - return !(l == r); - } -}; - -template -IteratorPair> value_range(T b, T e) -{ - return {b, e}; -} - -#endif // TMWA_COMMON_ITER_HPP diff --git a/src/common/iter_test.cpp b/src/common/iter_test.cpp deleted file mode 100644 index 647ebf9..0000000 --- a/src/common/iter_test.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "iter.hpp" - -#include - -#include "../strings/xstring.hpp" - -TEST(iterpair, string) -{ - IteratorPair> pair = value_range('0', ':'); - const char *str = "0123456789"; - EXPECT_TRUE(std::equal(pair.begin(), pair.end(), str)); -} - -TEST(iterpair, signed8) -{ - IteratorPair> pair = value_range(int8_t(-128), int8_t(127)); - int8_t arr[255] = - { - -128, -127, -126, -125, -124, -123, -122, -121, -120, - -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, - -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, - -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, - -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, - -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, - -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, - -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, - -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, - -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, - -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, - -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, - -9, -8, -7, -6, -5, -4, -3, -2, -1, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, - 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, - 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, - }; - EXPECT_TRUE(std::equal(pair.begin(), pair.end(), arr + 0)); -} - -TEST(iterpair, unsigned8) -{ - IteratorPair> pair = value_range(uint8_t(0), uint8_t(255)); - uint8_t arr[255] = - { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, - 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, - 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, - 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, - 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, - 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, - 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, - 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, - 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, - 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, - 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, - 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, - 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, - 250, 251, 252, 253, 254, - }; - EXPECT_TRUE(std::equal(pair.begin(), pair.end(), arr)); -} diff --git a/src/common/matrix.hpp b/src/common/matrix.hpp deleted file mode 100644 index 8595191..0000000 --- a/src/common/matrix.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef TMWA_COMMON_MATRIX_HPP -#define TMWA_COMMON_MATRIX_HPP - -template -class Matrix -{ - std::unique_ptr _data; - size_t _xs, _ys; -public: - Matrix() - : _data() - , _xs() - , _ys() - {} - Matrix(size_t x, size_t y) - : _data(new T[x * y]()) - , _xs(x) - , _ys(y) - {} - // no copy-ctor or copy-assign - - void reset(size_t x, size_t y) - { - *this = Matrix(x, y); - } - void clear() - { - *this = Matrix(); - } - - T& ref(size_t x, size_t y) - { - return _data[x + y * _xs]; - } - const T& ref(size_t x, size_t y) const - { - return _data[x + y * _xs]; - } - - size_t xs() const - { - return _xs; - } - size_t ys() const - { - return _ys; - } -}; - -#endif // TMWA_COMMON_MATRIX_HPP diff --git a/src/common/md5calc.cpp b/src/common/md5calc.cpp deleted file mode 100644 index d23f8e3..0000000 --- a/src/common/md5calc.cpp +++ /dev/null @@ -1,352 +0,0 @@ -#include "md5calc.hpp" - -#include - -#include "../strings/xstring.hpp" -#include "../strings/vstring.hpp" - -#include "../io/cxxstdio.hpp" -#include "../io/read.hpp" - -#include "random.hpp" -#include "utils.hpp" - -#include "../poison.hpp" - -// auxilary data -/* -sin() constant table -#Reformatted output of: -echo 'scale=40; obase=16; for (i=1;i<=64;i++) print 2^32 * sin(i), "\n"' | -bc | sed 's/^-//;s/^/0x/;s/\..*$/,/' -*/ -static -const uint32_t T[64] = -{ - // used by round 1 - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0 - 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4 - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8 - 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12 - // used by round 2 - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16 - 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, //20 - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24 - 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28 - // used by round 3 - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32 - 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36 - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, //40 - 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44 - // used by round 4 - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48 - 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52 - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56 - 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, //60 -}; - -// auxilary functions -// note - the RFC defines these by non-CS conventions: or=v, and=(empty) -static -uint32_t rotate_left(uint32_t val, unsigned shift) -{ - return val << shift | val >> (32 - shift); -} - -static -uint32_t F(uint32_t X, uint32_t Y, uint32_t Z) -{ - return (X & Y) | (~X & Z); -} -static -uint32_t G(uint32_t X, uint32_t Y, uint32_t Z) -{ - return (X & Z) | (Y & ~Z); -} -static -uint32_t H(uint32_t X, uint32_t Y, uint32_t Z) -{ - return X ^ Y ^ Z; -} -static -uint32_t I(uint32_t X, uint32_t Y, uint32_t Z) -{ - return Y ^ (X | ~Z); -} - -static -const struct -{ - uint8_t k : 4; - uint8_t : 0; - uint8_t s : 5; -// uint8_t i : 6; just increments constantly, from 1 .. 64 over all rounds -} -MD5_round1[16] = -{ - { 0, 7}, { 1, 12}, { 2, 17}, { 3, 22}, - { 4, 7}, { 5, 12}, { 6, 17}, { 7, 22}, - { 8, 7}, { 9, 12}, {10, 17}, {11, 22}, - {12, 7}, {13, 12}, {14, 17}, {15, 22}, -}, -MD5_round2[16] = -{ - { 1, 5}, { 6, 9}, {11, 14}, { 0, 20}, - { 5, 5}, {10, 9}, {15, 14}, { 4, 20}, - { 9, 5}, {14, 9}, { 3, 14}, { 8, 20}, - {13, 5}, { 2, 9}, { 7, 14}, {12, 20}, -}, -MD5_round3[16] = -{ - { 5, 4}, { 8, 11}, {11, 16}, {14, 23}, - { 1, 4}, { 4, 11}, { 7, 16}, {10, 23}, - {13, 4}, { 0, 11}, { 3, 16}, { 6, 23}, - { 9, 4}, {12, 11}, {15, 16}, { 2, 23}, -}, -MD5_round4[16] = -{ - { 0, 6}, { 7, 10}, {14, 15}, { 5, 21}, - {12, 6}, { 3, 10}, {10, 15}, { 1, 21}, - { 8, 6}, {15, 10}, { 6, 15}, {13, 21}, - { 4, 6}, {11, 10}, { 2, 15}, { 9, 21}, -}; - - -void MD5_init(MD5_state* state) -{ - // in the RFC, these are specified as bytes, interpreted as little-endian - state->val[0] = 0x67452301; - state->val[1] = 0xEFCDAB89; - state->val[2] = 0x98BADCFE; - state->val[3] = 0x10325476; -} - -void MD5_do_block(MD5_state* state, MD5_block block) -{ -#define X block.data -#define a state->val[(16 - i) % 4] -#define b state->val[(17 - i) % 4] -#define c state->val[(18 - i) % 4] -#define d state->val[(19 - i) % 4] - // save the values - const MD5_state saved = *state; - // round 1 - for (int i = 0; i < 16; i++) - { -#define k MD5_round1[i].k -#define s MD5_round1[i].s - a = b + rotate_left(a + F(b, c, d) + X[k] + T[i + 0x0], s); -#undef k -#undef s - } - // round 2 - for (int i = 0; i < 16; i++) - { -#define k MD5_round2[i].k -#define s MD5_round2[i].s - a = b + rotate_left(a + G(b, c, d) + X[k] + T[i + 0x10], s); -#undef k -#undef s - } - // round 3 - for (int i = 0; i < 16; i++) - { -#define k MD5_round3[i].k -#define s MD5_round3[i].s - a = b + rotate_left(a + H(b, c, d) + X[k] + T[i + 0x20], s); -#undef k -#undef s - } - // round 4 - for (int i = 0; i < 16; i++) - { -#define k MD5_round4[i].k -#define s MD5_round4[i].s - a = b + rotate_left(a + I(b, c, d) + X[k] + T[i + 0x30], s); -#undef k -#undef s - } - // adjust state based on original - state->val[0] += saved.val[0]; - state->val[1] += saved.val[1]; - state->val[2] += saved.val[2]; - state->val[3] += saved.val[3]; -#undef a -#undef b -#undef c -#undef d -} - -void MD5_to_bin(MD5_state state, md5_binary& out) -{ - for (int i = 0; i < 0x10; i++) - out[i] = state.val[i / 4] >> 8 * (i % 4); -} - -static -const char hex[] = "0123456789abcdef"; - -void MD5_to_str(MD5_state state, md5_string& out_) -{ - md5_binary bin; - MD5_to_bin(state, bin); - char out[0x20]; - for (int i = 0; i < 0x10; i++) - out[2 * i] = hex[bin[i] >> 4], - out[2 * i + 1] = hex[bin[i] & 0xf]; - out_ = stringish(XString(out, out + 0x20, nullptr)); -} - -MD5_state MD5_from_string(XString msg) -{ - MD5_state state; - MD5_init(&state); - MD5_block block; - const uint64_t msg_full_len = msg.size(); - while (msg.size() >= 64) - { - for (int i = 0; i < 0x10; i++) - X[i] = msg[4 * i + 0] | msg[4 * i + 1] << 8 | msg[4 * i + 2] << 16 | msg[4 * i + 3] << 24; - MD5_do_block(&state, block); - msg = msg.xslice_t(64); - } - // now pad 1-512 bits + the 64-bit length - may be two blocks - uint8_t buf[0x40] = {}; - really_memcpy(buf, reinterpret_cast(msg.data()), msg.size()); - buf[msg.size()] = 0x80; // a single one bit - if (64 - msg.size() > 8) - { - for (int i = 0; i < 8; i++) - buf[0x38 + i] = (msg_full_len * 8) >> (i * 8); - } - for (int i = 0; i < 0x10; i++) - X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; - MD5_do_block(&state, block); - if (64 - msg.size() <= 8) - { - really_memset0(buf, 0x38); - for (int i = 0; i < 8; i++) - buf[0x38 + i] = (msg_full_len * 8) >> (i * 8); - for (int i = 0; i < 0x10; i++) - X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; - MD5_do_block(&state, block); - } - return state; -} - -// TODO - refactor MD5 into a stream, and merge the implementations -// I once implemented an ostream that does it ... -MD5_state MD5_from_FILE(io::ReadFile& in) -{ - uint64_t total_len = 0; - - uint8_t buf[0x40]; - uint8_t block_len = 0; - - MD5_state state; - MD5_init(&state); - - MD5_block block; - - while (true) - { - size_t rv = in.get(sign_cast(buf + block_len), 0x40 - block_len); - if (!rv) - break; - total_len += 8 * rv; // in bits - block_len += rv; - if (block_len != 0x40) - continue; - for (int i = 0; i < 0x10; i++) - X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; - MD5_do_block(&state, block); - block_len = 0; - } - // no more input, just pad and append the length - buf[block_len] = 0x80; - really_memset0(buf + block_len + 1, 0x40 - block_len - 1); - if (block_len < 0x38) - { - for (int i = 0; i < 8; i++) - buf[0x38 + i] = total_len >> i * 8; - } - for (int i = 0; i < 0x10; i++) - X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; - MD5_do_block(&state, block); - if (0x38 <= block_len) - { - really_memset0(buf, 0x38); - for (int i = 0; i < 8; i++) - buf[0x38 + i] = total_len >> i * 8; - for (int i = 0; i < 0x10; i++) - X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; - MD5_do_block(&state, block); - } - return state; -} - - -// Hash a password with a salt. -// Whoever wrote this FAILS programming -AccountCrypt MD5_saltcrypt(AccountPass key, SaltString salt) -{ - char cbuf[64] {}; - - // hash the key then the salt - // buf ends up as a 64-char NUL-terminated string - md5_string tbuf, tbuf2; - MD5_to_str(MD5_from_string(key), tbuf); - MD5_to_str(MD5_from_string(salt), tbuf2); - const auto it = std::copy(tbuf.begin(), tbuf.end(), std::begin(cbuf)); - auto it2 = std::copy(tbuf2.begin(), tbuf2.end(), it); - assert(it2 == std::end(cbuf)); - - md5_string tbuf3; - MD5_to_str(MD5_from_string(XString(std::begin(cbuf), it2, nullptr)), tbuf3); - - VString<31> obuf; - - // This truncates the string, but we have to keep it like that for compatibility - SNPRINTF(obuf, 32, "!%s$%s", salt, tbuf3); - return stringish(obuf); -} - -SaltString make_salt(void) -{ - char salt[5]; - for (int i = 0; i < 5; i++) - // 126 would probably actually be okay - salt[i] = random_::in(48, 125); - return stringish(XString(salt + 0, salt + 5, nullptr)); -} - -bool pass_ok(AccountPass password, AccountCrypt crypted) -{ - // crypted is like !salt$hash - auto begin = crypted.begin() + 1; - auto end = std::find(begin, crypted.end(), '$'); - SaltString salt = stringish(crypted.xislice(begin, end)); - - return crypted == MD5_saltcrypt(password, salt); -} - -// [M|h]ashes up an IP address and a secret key -// to return a hopefully unique masked IP. -IP4Address MD5_ip(IP4Address ip) -{ - static SaltString secret = make_salt(); - - // MD5sum a secret + the IP address - VString<31> ipbuf; - SNPRINTF(ipbuf, 32, "%s %s", ip, secret); - md5_binary obuf; - MD5_to_bin(MD5_from_string(ipbuf), obuf); - - // Fold the md5sum to 32 bits, pack the bytes to an in_addr - return IP4Address({ - static_cast(obuf[0] ^ obuf[1] ^ obuf[8] ^ obuf[9]), - static_cast(obuf[2] ^ obuf[3] ^ obuf[10] ^ obuf[11]), - static_cast(obuf[4] ^ obuf[5] ^ obuf[12] ^ obuf[13]), - static_cast(obuf[6] ^ obuf[7] ^ obuf[14] ^ obuf[15]), - }); -} diff --git a/src/common/md5calc.hpp b/src/common/md5calc.hpp deleted file mode 100644 index 45bec84..0000000 --- a/src/common/md5calc.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef MD5CALC_HPP -#define MD5CALC_HPP - -# include "../sanity.hpp" - -# include - -# include -# include -# include - -# include - -# include "../strings/fwd.hpp" -# include "../strings/vstring.hpp" - -# include "../io/fwd.hpp" - -# include "ip.hpp" -# include "mmo.hpp" - -/// The digest state - becomes the output -struct MD5_state -{ - // classically named {A,B,C,D} - // but use an so we can index - uint32_t val[4]; -}; -struct MD5_block -{ - uint32_t data[16]; -}; - -struct md5_binary : std::array {}; -struct md5_string : VString<0x20> {}; -struct SaltString : VString<5> {}; - -// Implementation -void MD5_init(MD5_state *state); -void MD5_do_block(MD5_state *state, MD5_block block); - -// Output formatting -void MD5_to_bin(MD5_state state, md5_binary& out); -void MD5_to_str(MD5_state state, md5_string& out); - -// Convenience -MD5_state MD5_from_string(XString msg); -MD5_state MD5_from_FILE(io::ReadFile& in); - - -// whoever wrote this fails basic understanding of -AccountCrypt MD5_saltcrypt(AccountPass key, SaltString salt); - -/// return some random characters (statically allocated) -// Currently, returns a 5-char string -SaltString make_salt(void); - -/// check plaintext password against saved saltcrypt -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 // MD5CALC_HPP diff --git a/src/common/md5calc_test.cpp b/src/common/md5calc_test.cpp deleted file mode 100644 index ab5f242..0000000 --- a/src/common/md5calc_test.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "md5calc.hpp" - -#include - -#include "../strings/xstring.hpp" -#include "../strings/vstring.hpp" - -#include "utils.hpp" - -// This should be made part of the main API, -// but is not yet to keep the diff small. -// Edit: hack to fix the new strict comparison. -static -VString<32> MD5(XString in) -{ - md5_string out; - MD5_to_str(MD5_from_string(in), out); - return out; -} - -TEST(md5calc, rfc1321) -{ - EXPECT_EQ("d41d8cd98f00b204e9800998ecf8427e", MD5("")); - EXPECT_EQ("0cc175b9c0f1b6a831c399e269772661", MD5("a")); - EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72", MD5("abc")); - EXPECT_EQ("f96b697d7cb7938d525a2f31aaf161d0", MD5("message digest")); - EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b", MD5("abcdefghijklmnopqrstuvwxyz")); - EXPECT_EQ("d174ab98d277d9f5a5611c2c9f419d9f", MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")); - EXPECT_EQ("57edf4a22be3c955ac49da2e2107b67a", MD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890")); -} diff --git a/src/common/mmo.hpp b/src/common/mmo.hpp deleted file mode 100644 index bb30682..0000000 --- a/src/common/mmo.hpp +++ /dev/null @@ -1,380 +0,0 @@ -/// Global structures and defines -#ifndef MMO_HPP -#define MMO_HPP - -# include "../sanity.hpp" - -# include "../strings/vstring.hpp" - -# include "timer.t.hpp" -# include "utils2.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 - -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 - -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> {}; -template -T stringish(VString iv) -{ - T rv; - static_cast&>(rv) = iv; - return rv; -} -# define DEFAULT_EMAIL stringish("a@a.com") - -// It is decreed: a mapname shall not contain an extension -class MapName : public strings::_crtp_string -{ - 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(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(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; -} -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 attrs; - SEX sex; - - unsigned long mapip; - unsigned int mapport; - - struct point last_point, save_point; - struct item inventory[MAX_INVENTORY]; - earray skill; - int global_reg_num; - struct global_reg global_reg[GLOBAL_REG_NUM]; - int account_reg_num; - struct global_reg account_reg[ACCOUNT_REG_NUM]; - int account_reg2_num; - struct global_reg account_reg2[ACCOUNT_REG2_NUM]; -}; - -struct CharPair -{ - CharKey key; - std::unique_ptr data; - - CharPair() - : key{}, data(make_unique()) - {} -}; - -struct storage -{ - int dirty; - int account_id; - short storage_status; - short storage_amount; - struct item storage_[MAX_STORAGE]; -}; - -struct map_session_data; - -struct GM_Account -{ - int account_id; - uint8_t level; -}; - -struct party_member -{ - int account_id; - CharName name; - MapName map; - int leader, online, lv; - struct map_session_data *sd; -}; - -struct party -{ - int party_id; - PartyName name; - int exp; - int item; - struct party_member member[MAX_PARTY]; -}; - -#endif // MMO_HPP diff --git a/src/common/nullpo.cpp b/src/common/nullpo.cpp deleted file mode 100644 index 423ed8c..0000000 --- a/src/common/nullpo.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "nullpo.hpp" - -#include - -#include "../poison.hpp" - -/// Actual output function -static -void nullpo_info(const char *file, int line, const char *func) -{ - if (!file) - file = "??"; - if (!func || !*func) - func = "unknown"; - - fprintf(stderr, "%s:%d: in func `%s': NULL pointer\n", - file, line, func); -} - -bool nullpo_chk(const char *file, int line, const char *func, - const void *target) -{ - if (target) - return 0; - - nullpo_info(file, line, func); - return 1; -} diff --git a/src/common/nullpo.hpp b/src/common/nullpo.hpp deleted file mode 100644 index 0eaa1b2..0000000 --- a/src/common/nullpo.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/// return wrappers for unexpected NULL pointers -#ifndef NULLPO_HPP -#define NULLPO_HPP -/// Uncomment this to live dangerously -/// (really exist to detect mostly-unused variables) -//# define BUG_FREE - -/// All functions print to standard error (was: standard output) -/// nullpo_ret(cond) - return 0 if given pointer is NULL -/// nullpo_retv(cond) - just return (function returns void) -/// nullpo_retr(rv, cond) - return given value instead - -# ifndef BUG_FREE -# define nullpo_retr(ret, t) \ - if (nullpo_chk(__FILE__, __LINE__, __PRETTY_FUNCTION__, t)) \ - return ret; -# else // BUG_FREE -# define nullpo_retr(ret, t) /*t*/ -# endif // BUG_FREE - -# define nullpo_ret(t) nullpo_retr(0, t) -# define nullpo_retv(t) nullpo_retr(, t) - -# include "../sanity.hpp" - -/// Used by macros in this header -bool nullpo_chk(const char *file, int line, const char *func, - const void *target); - -template -bool nullpo_chk(const char *file, int line, const char *func, T target) -{ - return nullpo_chk(file, line, func, target.operator->()); -} -template -bool nullpo_chk(const char *file, int line, const char *func, T *target) -{ - return nullpo_chk(file, line, func, static_cast(target)); -} - -#endif // NULLPO_HPP diff --git a/src/common/operators.hpp b/src/common/operators.hpp deleted file mode 100644 index 3d44b81..0000000 --- a/src/common/operators.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef OPERATORS_HPP -#define OPERATORS_HPP - -namespace _operators -{ - class Comparable {}; - - template - bool operator == (T l, T r) - { - return l.value == r.value; - } - - template - bool operator != (T l, T r) - { - return l.value != r.value; - } - - template - bool operator < (T l, T r) - { - return l.value < r.value; - } - - template - bool operator <= (T l, T r) - { - return l.value <= r.value; - } - - template - bool operator > (T l, T r) - { - return l.value > r.value; - } - - template - bool operator >= (T l, T r) - { - return l.value >= r.value; - } -} - -using _operators::Comparable; - -#endif // OPERATORS_HPP diff --git a/src/common/random.cpp b/src/common/random.cpp deleted file mode 100644 index 273dcec..0000000 --- a/src/common/random.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "random2.hpp" - -#include "../poison.hpp" - -namespace random_ -{ - std::mt19937 generate{std::random_device()()}; -} // namespace random_ diff --git a/src/common/random.hpp b/src/common/random.hpp deleted file mode 100644 index a694cce..0000000 --- a/src/common/random.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef RANDOM_HPP -#define RANDOM_HPP - -# include "random.t.hpp" - -# include "../sanity.hpp" - -# include - -// This is not namespace random since that collides with a C function, -// but this can be revisited when everything goes into namespace tmwa. -namespace random_ -{ - /// Get a random number from 0 .. 2**32 - 1 - extern std::mt19937 generate; - - /// Get a random number from 0 .. bound - 1 - inline - int to(int bound) - { - std::uniform_int_distribution dist(0, bound - 1); - return dist(generate); - } - - /// Get a random number from low .. high (inclusive!) - inline - int in(int low, int high) - { - std::uniform_int_distribution dist(low, high); - return dist(generate); - } - - inline - bool coin() - { - // sigh, can't specify directly ... - std::uniform_int_distribution dist(false, true); - return dist(generate); - } - - inline - bool chance(Fraction f) - { - if (f.num <= 0) - return false; - if (f.num >= f.den) - return true; - return random_::to(f.den) < f.num; - } - - // C is usually one of: - // std::vector - // std::initializer_list - // std::array - template - auto choice(C&& c) -> decltype(*c.begin()) - { - return *(c.begin() + random_::to(c.size())); - } - - // allow bare braces - template - T choice(std::initializer_list&& il) - { - return random_::choice(il); - } -} // namespace random_ - -#endif // RANDOM_HPP diff --git a/src/common/random.t.hpp b/src/common/random.t.hpp deleted file mode 100644 index 98a6c59..0000000 --- a/src/common/random.t.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef RANDOM_T_HPP -#define RANDOM_T_HPP - -namespace random_ -{ - struct Fraction - { - int num, den; - }; - - template - struct Fixed - { - T num; - - operator Fraction() - { - return {num, den}; - } - }; -} // namespace random_ - -#endif // RANDOM_T_HPP diff --git a/src/common/random2.hpp b/src/common/random2.hpp deleted file mode 100644 index 86deddf..0000000 --- a/src/common/random2.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef RANDOM2_HPP -#define RANDOM2_HPP - -# include "random.hpp" -# include "utils2.hpp" - -# include - -namespace random_ -{ - namespace detail - { - struct RandomIterator - { - int bound; - int current; - bool frist; - - void operator ++() - { - frist = false; - // TODO: reimplement in terms of LFSRs, so that certain - // blockage patterns don't favor adjacent cells. - current = current + 1; - if (current == bound) - current = 0; - } - int operator *() - { - return current; - } - }; - - inline - bool operator == (RandomIterator l, RandomIterator r) - { - return l.current == r.current && l.frist == r.frist; - } - - inline - bool operator != (RandomIterator l, RandomIterator r) - { - return !(l == r); - } - } - - /// Yield every cell from 0 .. bound - 1 in some order. - /// The starting position is random, but not the order itself. - /// - /// Expected usage: - /// for (int i : random_::iterator(vec.size())) - /// if (vec[i].okay()) - /// return frob(vec[i]); - inline - IteratorPair iterator(int bound) - { - int current = random_::to(bound); - return - { - detail::RandomIterator{bound, current, true}, - detail::RandomIterator{bound, current, false} - }; - } - - /// similar to std::random_shuffle(c.begin(), c.end()), but guaranteed - /// to use a good source of randomness. - template - void shuffle(C&& c) - { - std::random_shuffle(c.begin(), c.end(), random_::to); - } -} // namespace random_ - -#endif // RANDOM2_HPP diff --git a/src/common/socket.cpp b/src/common/socket.cpp deleted file mode 100644 index 73e32a4..0000000 --- a/src/common/socket.cpp +++ /dev/null @@ -1,474 +0,0 @@ -#include "socket.hpp" - -#include -#include -#include -//#include - -#include -#include - -#include -#include -#include - -#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, FD_SETSIZE> session; -#pragma GCC diagnostic pop - -void set_session(io::FD fd, std::unique_ptr 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> 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; -} - - -/// Discard all input -static -void null_parse(Session *s); -/// Default parser for new connections -static -void (*default_func_parse)(Session *) = null_parse; - -void set_defaultparse(void (*defaultparse)(Session *)) -{ - default_func_parse = defaultparse; -} - -/// Read from socket to the queue -static -void recv_to_fifo(Session *s) -{ - if (s->eof) - return; - - 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->eof = 1; - } -} - -static -void send_from_fifo(Session *s) -{ - if (s->eof) - return; - - 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->eof = 1; - } -} - -static -void null_parse(Session *s) -{ - PRINTF("null_parse : %d\n", s); - RFIFOSKIP(s, RFIFOREST(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(&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 *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->func_recv = recv_to_fifo; - s->func_send = send_from_fifo; - s->func_parse = default_func_parse; - s->client_ip = IP4Address(client_address.sin_addr); - s->created = TimeT::now(); - s->connected = 0; -} - -Session *make_listen_port(uint16_t port) -{ - 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(&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 *s = get_session(fd); - s->fd = fd; - - s->func_recv = connect_client; - s->created = TimeT::now(); - s->connected = 1; - - return s; -} - -Session *make_connection(IP4Address ip, uint16_t port) -{ - 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(&server_address), - sizeof(struct sockaddr_in)); - - readfds.set(fd); - - set_session(fd, make_unique()); - 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->func_recv = recv_to_fifo; - s->func_send = send_from_fifo; - s->func_parse = default_func_parse; - s->created = TimeT::now(); - s->connected = 1; - - return s; -} - -void delete_session(Session *s) -{ - if (!s) - return; - - 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(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)) - { - if (s->func_send) - //send_from_fifo(i); - s->func_send(s); - } - if (rfd.isset(i)) - { - 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(TimeT::now()) - static_cast(s->created) > CONNECT_TIMEOUT) - { - PRINTF("Session #%d timed out\n", s); - s->eof = 1; - } - if (!s->rdata_size && !s->eof) - continue; - if (s->func_parse) - { - s->func_parse(s); - /// some func_parse may call delete_session - s = get_session(i); - if (s && s->eof) - { - delete_session(s); - s = nullptr; - } - if (!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/common/socket.hpp b/src/common/socket.hpp deleted file mode 100644 index e0847ac..0000000 --- a/src/common/socket.hpp +++ /dev/null @@ -1,371 +0,0 @@ -#ifndef SOCKET_HPP -#define SOCKET_HPP - -# include "../sanity.hpp" - -# include - -# include - -# include - -# 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 declaration - -struct Session -{ - /// Checks whether a newly-connected socket actually does anything - TimeT created; - bool connected; - - /// Flag needed since structure must be freed in a server-dependent manner - bool eof; - - /// Since this is a single-threaded application, it can't block - /// These are the read/write queues - dumb_ptr 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; - - /// 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 - /// Can be set explicitly or via set_defaultparse - void (*func_parse)(Session *); - /// Server-specific data type - std::unique_ptr session_data; - - io::FD fd; -}; - -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 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> 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); -/// Connect to an address, return a connected socket or -1 -// FIXME - this is IPv4 only! -Session *make_connection(IP4Address ip, uint16_t port); -/// 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); - -/// Change the default parser for newly connected clients -// typically called once per server, but individual clients may identify -// themselves as servers -void set_defaultparse(void(*defaultparse)(Session *)); - -template -uint8_t *pod_addressof_m(T& structure) -{ - static_assert(is_trivially_copyable::value, "Can only byte-copy POD-ish structs"); - return &reinterpret_cast(structure); -} - -template -const uint8_t *pod_addressof_c(const T& structure) -{ - static_assert(is_trivially_copyable::value, "Can only byte-copy POD-ish structs"); - return &reinterpret_cast(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(RFIFOP(s, pos)); -} -inline -uint16_t RFIFOW(Session *s, size_t pos) -{ - return *static_cast(RFIFOP(s, pos)); -} -inline -uint32_t RFIFOL(Session *s, size_t pos) -{ - return *static_cast(RFIFOP(s, pos)); -} -template -void RFIFO_STRUCT(Session *s, size_t pos, T& structure) -{ - really_memcpy(pod_addressof_m(structure), static_cast(RFIFOP(s, pos)), sizeof(T)); -} -inline -IP4Address RFIFOIP(Session *s, size_t pos) -{ - IP4Address o; - RFIFO_STRUCT(s, pos, o); - return o; -} -template -inline -VString RFIFO_STRING(Session *s, size_t pos) -{ - const char *const begin = static_cast(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(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(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(RBUFP(p, pos)); -} -inline -uint16_t RBUFW(const uint8_t *p, size_t pos) -{ - return *static_cast(RBUFP(p, pos)); -} -inline -uint32_t RBUFL(const uint8_t *p, size_t pos) -{ - return *static_cast(RBUFP(p, pos)); -} -template -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 -inline -VString RBUF_STRING(const uint8_t *p, size_t pos) -{ - const char *const begin = static_cast(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(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(WFIFOP(s, pos)); -} -inline -uint16_t& WFIFOW(Session *s, size_t pos) -{ - return *static_cast(WFIFOP(s, pos)); -} -inline -uint32_t& WFIFOL(Session *s, size_t pos) -{ - return *static_cast(WFIFOP(s, pos)); -} -template -void WFIFO_STRUCT(Session *s, size_t pos, T& structure) -{ - really_memcpy(static_cast(WFIFOP(s, pos)), pod_addressof_c(structure), sizeof(T)); -} -inline -IP4Address& WFIFOIP(Session *s, size_t pos) -{ - static_assert(is_trivially_copyable::value, "That was the whole point"); - return *static_cast(WFIFOP(s, pos)); -} -inline -void WFIFO_STRING(Session *s, size_t pos, XString str, size_t len) -{ - char *const begin = static_cast(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(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(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(WBUFP(p, pos)); -} -inline -uint16_t& WBUFW(uint8_t *p, size_t pos) -{ - return *static_cast(WBUFP(p, pos)); -} -inline -uint32_t& WBUFL(uint8_t *p, size_t pos) -{ - return *static_cast(WBUFP(p, pos)); -} -template -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(WBUFP(p, pos)); -} -inline -void WBUF_STRING(uint8_t *p, size_t pos, XString s, size_t len) -{ - char *const begin = static_cast(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(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(WFIFOP(ws, 0)), - static_cast(RFIFOP(rs, 0)), len); -} - -#endif // SOCKET_HPP diff --git a/src/common/timer.cpp b/src/common/timer.cpp deleted file mode 100644 index b5a2a5e..0000000 --- a/src/common/timer.cpp +++ /dev/null @@ -1,201 +0,0 @@ -#include "timer.hpp" - -#include -#include - -#include -#include - -#include - -#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 l, dumb_ptr 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, std::vector>, 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::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 td) -{ - timer_heap.push(td); -} - -static -dumb_ptr top_timer_heap(void) -{ - if (timer_heap.empty()) - return dumb_ptr(); - 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::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 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/common/timer.hpp b/src/common/timer.hpp deleted file mode 100644 index 7e187a3..0000000 --- a/src/common/timer.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef TIMER_HPP -#define TIMER_HPP - -# 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 // TIMER_HPP diff --git a/src/common/timer.t.hpp b/src/common/timer.t.hpp deleted file mode 100644 index 1e3a87a..0000000 --- a/src/common/timer.t.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef TIMER_T_HPP -#define TIMER_T_HPP - -# include -# include - -# 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 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 timer_func; - -class Timer -{ - friend struct TimerData; - dumb_ptr 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 // TIMER_T_HPP diff --git a/src/common/utils.cpp b/src/common/utils.cpp deleted file mode 100644 index 0dbf145..0000000 --- a/src/common/utils.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "utils.hpp" - -#include -#include - -#include - -#include "../strings/astring.hpp" -#include "../strings/zstring.hpp" -#include "../strings/xstring.hpp" - -#include "../io/cxxstdio.hpp" -#include "../io/write.hpp" - -#include "extract.hpp" - -#include "../poison.hpp" - -//--------------------------------------------------- -// E-mail check: return 0 (not correct) or 1 (valid). -//--------------------------------------------------- -bool e_mail_check(XString email) -{ - // athena limits - if (email.size() < 3 || email.size() > 39) - return 0; - - // part of RFC limits (official reference of e-mail description) - XString::iterator at = std::find(email.begin(), email.end(), '@'); - if (at == email.end()) - return 0; - XString username = email.xislice_h(at); - XString hostname = email.xislice_t(at + 1); - if (!username || !hostname) - return 0; - if (hostname.contains('@')) - return 0; - if (hostname.front() == '.' || hostname.back() == '.') - return 0; - if (hostname.contains_seq("..")) - return 0; - if (email.contains_any(" ;")) - return 0; - return email.is_print(); -} - -//------------------------------------------------- -// Return numerical value of a switch configuration -// on/off, english, français, deutsch, español -//------------------------------------------------- -int config_switch(ZString str) -{ - if (str == "true" || str == "on" || str == "yes" - || str == "oui" || str == "ja" - || str == "si") - return 1; - if (str == "false" || str == "off" || str == "no" - || str == "non" || str == "nein") - return 0; - - int rv; - if (extract(str, &rv)) - return rv; - FPRINTF(stderr, "Fatal: bad option value %s", str); - abort(); -} - -static_assert(sizeof(timestamp_seconds_buffer) == 20, "seconds buffer"); -static_assert(sizeof(timestamp_milliseconds_buffer) == 24, "millis buffer"); - -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(const_(buf)); -} -void stamp_time(timestamp_milliseconds_buffer& out) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - 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(tv.tv_usec / 1000)); - out = stringish(const_(buf)); -} - -void log_with_timestamp(io::WriteFile& out, XString line) -{ - if (!line) - { - out.put('\n'); - return; - } - timestamp_milliseconds_buffer tmpstr; - stamp_time(tmpstr); - out.really_put(tmpstr.data(), tmpstr.size()); - out.really_put(": ", 2); - out.put_line(line); -} diff --git a/src/common/utils.hpp b/src/common/utils.hpp deleted file mode 100644 index 161cbd4..0000000 --- a/src/common/utils.hpp +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef UTILS_HPP -#define UTILS_HPP - -# include "../sanity.hpp" - -# include -# include - -# include - -# include "../strings/fwd.hpp" -# include "../strings/vstring.hpp" - -# include "../io/fwd.hpp" - -# include "const_array.hpp" -# include "operators.hpp" -# include "utils2.hpp" - -template -struct is_trivially_copyable -: std::integral_constant -{}; - -bool e_mail_check(XString email); -int config_switch (ZString str); - -inline -void really_memcpy(uint8_t *dest, const uint8_t *src, size_t n) -{ - memcpy(dest, src, n); -} - -inline -void really_memmove(uint8_t *dest, const uint8_t *src, size_t n) -{ - memmove(dest, src, n); -} -inline -bool really_memequal(const uint8_t *a, const uint8_t *b, size_t n) -{ - return memcmp(a, b, n) == 0; -} - -inline -void really_memset0(uint8_t *dest, size_t n) -{ - memset(dest, '\0', n); -} -template -void really_memzero_this(T *v) -{ - static_assert(is_trivially_copyable::value, "only for mostly-pod types"); - static_assert(std::is_class::value || std::is_union::value, "Only for user-defined structures (for now)"); - memset(v, '\0', sizeof(*v)); -} -template -void really_memzero_this(T (&)[n]) = delete; - -// Exists in place of time_t, to give it a predictable printf-format. -// (on x86 and amd64, time_t == long, but not on x32) -static_assert(sizeof(long long) >= sizeof(time_t), "long long >= time_t"); -struct TimeT : Comparable -{ - long long value; - - // conversion - TimeT(time_t t=0) : value(t) {} - TimeT(struct tm t) : value(timegm(&t)) {} - operator time_t() const { return value; } - operator struct tm() const { time_t v = value; return *gmtime(&v); } - - explicit operator bool() const { return value; } - bool operator !() const { return !value; } - - // prevent surprises - template - TimeT(T) = delete; - template - operator T() const = delete; - - static - TimeT now() - { - // poisoned, but this is still in header-land - return time(NULL); - } - - bool error() const - { - return value == -1; - } - bool okay() const - { - return !error(); - } -}; - -inline -long long convert_for_printf(TimeT t) -{ - return t.value; -} - -inline -long long& convert_for_scanf(TimeT& t) -{ - return t.value; -} - -struct timestamp_seconds_buffer : VString<19> {}; -struct timestamp_milliseconds_buffer : VString<23> {}; -void stamp_time(timestamp_seconds_buffer&, const TimeT *t=nullptr); -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" -static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer), - "timestamp size"); -# 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) \ - stamp_time( \ - reinterpret_cast( \ - str + sizeof(str) \ - )[-1], \ - &t \ - ) - -#endif //UTILS_HPP diff --git a/src/common/utils2.hpp b/src/common/utils2.hpp deleted file mode 100644 index 9af39e5..0000000 --- a/src/common/utils2.hpp +++ /dev/null @@ -1,256 +0,0 @@ -#ifndef UTILS2_HPP -#define UTILS2_HPP - -# include "../sanity.hpp" - -# include -# include -# include -# include -# include - -# include "iter.hpp" - -# ifdef __clang__ -# define FALLTHROUGH [[clang::fallthrough]] -# else -# define FALLTHROUGH /* fallthrough */ -# endif - -template -struct earray -{ - // no ctor/dtor and one public member variable for easy initialization - T _data[size_t(max)]; - - T& operator[](E v) - { - return _data[size_t(v)]; - } - - const T& operator[](E v) const - { - return _data[size_t(v)]; - } - - T *begin() - { - return _data; - } - - T *end() - { - return _data + size_t(max); - } - - const T *begin() const - { - return _data; - } - - const T *end() const - { - return _data + size_t(max); - } - - friend bool operator == (const earray& l, const earray& r) - { - return std::equal(l.begin(), l.end(), r.begin()); - } - - friend bool operator != (const earray& l, const earray& r) - { - return !(l == r); - } -}; - -template -class eptr -{ - T *_data; -public: - eptr(decltype(nullptr)=nullptr) - : _data(nullptr) - {} - - template - eptr(earray& arr) - : _data(arr._data) - {} - - T& operator [](E v) - { - return _data[size_t(v)]; - } - - explicit operator bool() - { - return _data; - } - - bool operator not() - { - return not _data; - } -}; - -// std::underlying_type isn't supported until gcc 4.7 -// this is a poor man's emulation -template -struct underlying_type -{ - static_assert(std::is_enum::value, "Only enums have underlying type!"); - typedef typename std::conditional< - std::is_signed::value, - typename std::make_signed::type, - typename std::make_unsigned::type - >::type type; -}; - -template::value> -struct remove_enum -{ - typedef E type; -}; -template -struct remove_enum -{ - typedef typename underlying_type::type type; -}; - - -# define ENUM_BITWISE_OPERATORS(E) \ -inline \ -E operator & (E l, E r) \ -{ \ - typedef underlying_type::type U; \ - return E(U(l) & U(r)); \ -} \ -inline \ -E operator | (E l, E r) \ -{ \ - typedef underlying_type::type U; \ - return E(U(l) | U(r)); \ -} \ -inline \ -E operator ^ (E l, E r) \ -{ \ - typedef underlying_type::type U; \ - return E(U(l) ^ U(r)); \ -} \ -inline \ -E& operator &= (E& l, E r) \ -{ \ - return l = l & r; \ -} \ -inline \ -E& operator |= (E& l, E r) \ -{ \ - return l = l | r; \ -} \ -inline \ -E& operator ^= (E& l, E r) \ -{ \ - return l = l ^ r; \ -} \ -inline \ -E operator ~ (E r) \ -{ \ - return E(-1) ^ r; \ -} - -template -class EnumMath -{ - typedef typename underlying_type::type U; -public: - static - E inced(E v) - { - return E(U(v) + 1); - } -}; - -template -IteratorPair>> erange(E b, E e) -{ - return {b, e}; -} - -namespace ph = std::placeholders; - -template -typename std::common_type::type min(A a, B b) -{ - return a < b ? a : b; -} - -template -typename std::common_type::type max(A a, B b) -{ - return b < a ? a : b; -} - -template -struct is_array_of_unknown_bound -: std::is_same::type[]> -{}; - -template, class... A> -typename std::enable_if::value, std::unique_ptr>::type make_unique(A&&... a) -{ - return std::unique_ptr(new T(std::forward(a)...)); -} - -template> -typename std::enable_if::value, std::unique_ptr>::type make_unique(size_t sz) -{ - typedef typename std::remove_extent::type E; - return std::unique_ptr(new E[sz]()); -} - -template -const T& const_(T& t) -{ - return t; -} - -template -T no_cast(U&& u) -{ - typedef typename std::remove_reference::type Ti; - typedef typename std::remove_reference::type Ui; - typedef typename std::remove_cv::type Tb; - typedef typename std::remove_cv::type Ub; - static_assert(std::is_same::value, "not no cast"); - return std::forward(u); -} - -template -T base_cast(U&& u) -{ - static_assert(std::is_reference::value, "must base cast with references"); - typedef typename std::remove_reference::type Ti; - typedef typename std::remove_reference::type Ui; - typedef typename std::remove_cv::type Tb; - typedef typename std::remove_cv::type Ub; - static_assert(std::is_base_of::value, "not base cast"); - return std::forward(u); -} - -// use this when e.g. U is an int of unknown size -template -T maybe_cast(U u) -{ - return u; -} - -template -typename std::remove_pointer::type *sign_cast(U *u) -{ - typedef typename std::remove_pointer::type T_; - static_assert(sizeof(T_) == sizeof(U), "sign cast must be same size"); - return reinterpret_cast(u); -} - -#endif // UTILS2_HPP diff --git a/src/common/version.cpp b/src/common/version.cpp deleted file mode 100644 index 811ffdf..0000000 --- a/src/common/version.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "version.hpp" - -#include "../conf/version.hpp" - -#include "../strings/xstring.hpp" - -#include "extract.hpp" - -Version CURRENT_VERSION = -{ - VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, - VERSION_DEVEL, - - 0, 0, - VENDOR_VERSION, -}; -Version CURRENT_LOGIN_SERVER_VERSION = -{ - VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, - VERSION_DEVEL, - - 0, TMWA_SERVER_LOGIN, - VENDOR_VERSION, -}; -Version CURRENT_CHAR_SERVER_VERSION = -{ - VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, - VERSION_DEVEL, - - 0, TMWA_SERVER_CHAR | TMWA_SERVER_INTER, - VENDOR_VERSION, -}; -Version CURRENT_MAP_SERVER_VERSION = -{ - VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, - VERSION_DEVEL, - - 0, TMWA_SERVER_MAP, - VENDOR_VERSION, -}; - -#define S2(a) #a -#define S(a) S2(a) - -const char CURRENT_VERSION_STRING[] = "TMWA " - S(VERSION_MAJOR) "." S(VERSION_MINOR) "." S(VERSION_PATCH) - " dev" S(VERSION_DEVEL) " (" VENDOR " " S(VENDOR_VERSION) ")"; - -bool extract(XString str, Version *vers) -{ - *vers = {}; - return extract(str, record<'.'>(&vers->major, &vers->minor, &vers->patch)); -} diff --git a/src/common/version.hpp b/src/common/version.hpp deleted file mode 100644 index a2c4e05..0000000 --- a/src/common/version.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef TMWA_COMMON_VERSION_HPP -#define TMWA_COMMON_VERSION_HPP - -# include - -# include "../strings/fwd.hpp" - -// TODO make these bitwise enums -# 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 - -struct Version -{ - uint8_t major; - uint8_t minor; // flavor1 - uint8_t patch; // flavor2 - uint8_t devel; // flavor3 - - uint8_t flags; - uint8_t which; - uint16_t vend; - // can't add vendor name yet -}; -static_assert(sizeof(Version) == 8, "this is sent over the network, can't change"); - -extern Version CURRENT_VERSION; - -extern Version CURRENT_LOGIN_SERVER_VERSION; -extern Version CURRENT_CHAR_SERVER_VERSION; -extern Version CURRENT_MAP_SERVER_VERSION; - -extern const char 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_COMMON_VERSION_HPP diff --git a/src/compat/alg.cpp b/src/compat/alg.cpp new file mode 100644 index 0000000..7800e79 --- /dev/null +++ b/src/compat/alg.cpp @@ -0,0 +1,3 @@ +#include "alg.hpp" + +#include "../poison.hpp" diff --git a/src/compat/alg.hpp b/src/compat/alg.hpp new file mode 100644 index 0000000..250c161 --- /dev/null +++ b/src/compat/alg.hpp @@ -0,0 +1,21 @@ +#ifndef TMWA_COMPAT_ALG_HPP +#define TMWA_COMPAT_ALG_HPP + +# include "../sanity.hpp" + +# include + + +template +typename std::common_type::type min(A a, B b) +{ + return a < b ? a : b; +} + +template +typename std::common_type::type max(A a, B b) +{ + return b < a ? a : b; +} + +#endif // TMWA_COMPAT_ALG_HPP diff --git a/src/compat/attr.hpp b/src/compat/attr.hpp new file mode 100644 index 0000000..ca9a7a2 --- /dev/null +++ b/src/compat/attr.hpp @@ -0,0 +1,13 @@ +#ifndef TMWA_COMPAT_ATTR_HPP +#define TMWA_COMPAT_ATTR_HPP + +# include "../sanity.hpp" + + +# ifdef __clang__ +# define FALLTHROUGH [[clang::fallthrough]] +# else +# define FALLTHROUGH /* fallthrough */ +# endif + +#endif // TMWA_COMPAT_ATTR_HPP diff --git a/src/compat/cast.cpp b/src/compat/cast.cpp new file mode 100644 index 0000000..015fd27 --- /dev/null +++ b/src/compat/cast.cpp @@ -0,0 +1,3 @@ +#include "cast.hpp" + +#include "../poison.hpp" diff --git a/src/compat/cast.hpp b/src/compat/cast.hpp new file mode 100644 index 0000000..c1b9bab --- /dev/null +++ b/src/compat/cast.hpp @@ -0,0 +1,54 @@ +#ifndef TMWA_COMPAT_CAST_HPP +#define TMWA_COMPAT_CAST_HPP + +# include "../sanity.hpp" + +# include +# include + + +template +const T& const_(T& t) +{ + return t; +} + +template +T no_cast(U&& u) +{ + typedef typename std::remove_reference::type Ti; + typedef typename std::remove_reference::type Ui; + typedef typename std::remove_cv::type Tb; + typedef typename std::remove_cv::type Ub; + static_assert(std::is_same::value, "not no cast"); + return std::forward(u); +} + +template +T base_cast(U&& u) +{ + static_assert(std::is_reference::value, "must base cast with references"); + typedef typename std::remove_reference::type Ti; + typedef typename std::remove_reference::type Ui; + typedef typename std::remove_cv::type Tb; + typedef typename std::remove_cv::type Ub; + static_assert(std::is_base_of::value, "not base cast"); + return std::forward(u); +} + +// use this when e.g. U is an int of unknown size +template +T maybe_cast(U u) +{ + return u; +} + +template +typename std::remove_pointer::type *sign_cast(U *u) +{ + typedef typename std::remove_pointer::type T_; + static_assert(sizeof(T_) == sizeof(U), "sign cast must be same size"); + return reinterpret_cast(u); +} + +#endif // TMWA_COMPAT_CAST_HPP diff --git a/src/compat/fun.hpp b/src/compat/fun.hpp new file mode 100644 index 0000000..536a113 --- /dev/null +++ b/src/compat/fun.hpp @@ -0,0 +1,11 @@ +#ifndef TMWA_COMPAT_FUN_HPP +#define TMWA_COMPAT_FUN_HPP + +# include "../sanity.hpp" + +# include + + +namespace ph = std::placeholders; + +#endif // TMWA_COMPAT_FUN_HPP diff --git a/src/compat/iter.cpp b/src/compat/iter.cpp new file mode 100644 index 0000000..2b1fb0c --- /dev/null +++ b/src/compat/iter.cpp @@ -0,0 +1,3 @@ +#include "iter.hpp" + +#include "../poison.hpp" diff --git a/src/compat/iter.hpp b/src/compat/iter.hpp new file mode 100644 index 0000000..7793d90 --- /dev/null +++ b/src/compat/iter.hpp @@ -0,0 +1,97 @@ +#ifndef TMWA_COMPAT_ITER_HPP +#define TMWA_COMPAT_ITER_HPP +// iter.hpp - tools for dealing with iterators +// +// Copyright © 2012-2014 Ben Longbons +// +// 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 . + +# include "../sanity.hpp" + +# include + + +/// Simple class to use a pair of iterators with foreach +template +class IteratorPair +{ + It _b, _e; +public: + IteratorPair(It b, It e) + : _b(b), _e(e) + {} + + It begin() { return _b; } + It end() { return _e; } +}; + +template +IteratorPair iterator_pair(It b, It e) +{ + return {b, e}; +} + +template +class PassthroughMath +{ +public: + static + T inced(T v) { return ++v; } +}; + +// An iterator that just directly contains an integer-like value +// TODO port this once the new integer API happens +template> +class ValueIterator +{ + T value; +public: + typedef std::forward_iterator_tag iterator_category; + typedef void difference_type; + typedef T value_type; + typedef void reference; + typedef void pointer; +public: + ValueIterator(T v) + : value(v) + {} + + T operator *() + { + return value; + } + ValueIterator& operator++ () + { + value = Math::inced(value); + return *this; + } + friend bool operator == (ValueIterator l, ValueIterator r) + { + return l.value == r.value; + } + friend bool operator != (ValueIterator l, ValueIterator r) + { + return !(l == r); + } +}; + +template +IteratorPair> value_range(T b, T e) +{ + return {b, e}; +} + +#endif // TMWA_COMPAT_ITER_HPP diff --git a/src/compat/iter_test.cpp b/src/compat/iter_test.cpp new file mode 100644 index 0000000..647ebf9 --- /dev/null +++ b/src/compat/iter_test.cpp @@ -0,0 +1,82 @@ +#include "iter.hpp" + +#include + +#include "../strings/xstring.hpp" + +TEST(iterpair, string) +{ + IteratorPair> pair = value_range('0', ':'); + const char *str = "0123456789"; + EXPECT_TRUE(std::equal(pair.begin(), pair.end(), str)); +} + +TEST(iterpair, signed8) +{ + IteratorPair> pair = value_range(int8_t(-128), int8_t(127)); + int8_t arr[255] = + { + -128, -127, -126, -125, -124, -123, -122, -121, -120, + -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, + -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, + -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, + -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, + -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, + -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, + -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, + -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, + -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, + -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, + -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, + -9, -8, -7, -6, -5, -4, -3, -2, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, + }; + EXPECT_TRUE(std::equal(pair.begin(), pair.end(), arr + 0)); +} + +TEST(iterpair, unsigned8) +{ + IteratorPair> pair = value_range(uint8_t(0), uint8_t(255)); + uint8_t arr[255] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, + }; + EXPECT_TRUE(std::equal(pair.begin(), pair.end(), arr)); +} diff --git a/src/compat/memory.cpp b/src/compat/memory.cpp new file mode 100644 index 0000000..6a5f526 --- /dev/null +++ b/src/compat/memory.cpp @@ -0,0 +1,3 @@ +#include "memory.hpp" + +#include "../poison.hpp" diff --git a/src/compat/memory.hpp b/src/compat/memory.hpp new file mode 100644 index 0000000..2839640 --- /dev/null +++ b/src/compat/memory.hpp @@ -0,0 +1,27 @@ +#ifndef TMWA_COMPAT_MEMORY_HPP +#define TMWA_COMPAT_MEMORY_HPP + +# include "../sanity.hpp" + +# include + + +template +struct is_array_of_unknown_bound +: std::is_same::type[]> +{}; + +template, class... A> +typename std::enable_if::value, std::unique_ptr>::type make_unique(A&&... a) +{ + return std::unique_ptr(new T(std::forward(a)...)); +} + +template> +typename std::enable_if::value, std::unique_ptr>::type make_unique(size_t sz) +{ + typedef typename std::remove_extent::type E; + return std::unique_ptr(new E[sz]()); +} + +#endif // TMWA_COMPAT_MEMORY_HPP diff --git a/src/compat/nullpo.cpp b/src/compat/nullpo.cpp new file mode 100644 index 0000000..423ed8c --- /dev/null +++ b/src/compat/nullpo.cpp @@ -0,0 +1,28 @@ +#include "nullpo.hpp" + +#include + +#include "../poison.hpp" + +/// Actual output function +static +void nullpo_info(const char *file, int line, const char *func) +{ + if (!file) + file = "??"; + if (!func || !*func) + func = "unknown"; + + fprintf(stderr, "%s:%d: in func `%s': NULL pointer\n", + file, line, func); +} + +bool nullpo_chk(const char *file, int line, const char *func, + const void *target) +{ + if (target) + return 0; + + nullpo_info(file, line, func); + return 1; +} diff --git a/src/compat/nullpo.hpp b/src/compat/nullpo.hpp new file mode 100644 index 0000000..6eca4b6 --- /dev/null +++ b/src/compat/nullpo.hpp @@ -0,0 +1,41 @@ +/// return wrappers for unexpected NULL pointers +#ifndef TMWA_COMPAT_NULLPO_HPP +#define TMWA_COMPAT_NULLPO_HPP +/// Uncomment this to live dangerously +/// (really exist to detect mostly-unused variables) +//# define BUG_FREE + +/// All functions print to standard error (was: standard output) +/// nullpo_ret(cond) - return 0 if given pointer is NULL +/// nullpo_retv(cond) - just return (function returns void) +/// nullpo_retr(rv, cond) - return given value instead + +# ifndef BUG_FREE +# define nullpo_retr(ret, t) \ + if (nullpo_chk(__FILE__, __LINE__, __PRETTY_FUNCTION__, t)) \ + return ret; +# else // BUG_FREE +# define nullpo_retr(ret, t) /*t*/ +# endif // BUG_FREE + +# define nullpo_ret(t) nullpo_retr(0, t) +# define nullpo_retv(t) nullpo_retr(, t) + +# include "../sanity.hpp" + +/// Used by macros in this header +bool nullpo_chk(const char *file, int line, const char *func, + const void *target); + +template +bool nullpo_chk(const char *file, int line, const char *func, T target) +{ + return nullpo_chk(file, line, func, target.operator->()); +} +template +bool nullpo_chk(const char *file, int line, const char *func, T *target) +{ + return nullpo_chk(file, line, func, static_cast(target)); +} + +#endif // TMWA_COMPAT_NULLPO_HPP diff --git a/src/compat/rawmem.cpp b/src/compat/rawmem.cpp new file mode 100644 index 0000000..9e353e8 --- /dev/null +++ b/src/compat/rawmem.cpp @@ -0,0 +1,3 @@ +#include "rawmem.hpp" + +#include "../poison.hpp" diff --git a/src/compat/rawmem.hpp b/src/compat/rawmem.hpp new file mode 100644 index 0000000..ac08964 --- /dev/null +++ b/src/compat/rawmem.hpp @@ -0,0 +1,30 @@ +#ifndef TMWA_COMPAT_RAWMEM_HPP +#define TMWA_COMPAT_RAWMEM_HPP + +# include +# include +# include + +inline +void really_memcpy(uint8_t *dest, const uint8_t *src, size_t n) +{ + memcpy(dest, src, n); +} + +inline +void really_memmove(uint8_t *dest, const uint8_t *src, size_t n) +{ + memmove(dest, src, n); +} +inline +bool really_memequal(const uint8_t *a, const uint8_t *b, size_t n) +{ + return memcmp(a, b, n) == 0; +} + +inline +void really_memset0(uint8_t *dest, size_t n) +{ + memset(dest, '\0', n); +} +#endif // TMWA_COMPAT_RAWMEM_HPP diff --git a/src/conf/version.hpp b/src/conf/version.hpp index a4ee931..ab7a3d5 100644 --- a/src/conf/version.hpp +++ b/src/conf/version.hpp @@ -1,5 +1,5 @@ -#ifndef CONF_VERSION_HPP -#define CONF_VERSION_HPP +#ifndef TMWA_CONF_VERSION_HPP +#define TMWA_CONF_VERSION_HPP # include "conf-raw/str-VERSION_FULL.h" # include "conf-raw/str-VERSION_HASH.h" @@ -12,4 +12,4 @@ # include "conf-raw/str-VENDOR.h" # include "conf-raw/int-VENDOR_VERSION.h" -#endif +#endif // TMWA_CONF_VERSION_HPP diff --git a/src/generic/const_array.cpp b/src/generic/const_array.cpp new file mode 100644 index 0000000..0c09333 --- /dev/null +++ b/src/generic/const_array.cpp @@ -0,0 +1,3 @@ +#include "const_array.hpp" + +#include "../poison.hpp" diff --git a/src/generic/const_array.hpp b/src/generic/const_array.hpp new file mode 100644 index 0000000..1c70f5d --- /dev/null +++ b/src/generic/const_array.hpp @@ -0,0 +1,132 @@ +#ifndef TMWA_GENERIC_CONST_ARRAY_HPP +#define TMWA_GENERIC_CONST_ARRAY_HPP +// const_array.hpp - just a pointer-to-const and a length +// +// Copyright © 2011-2012 Ben Longbons +// +// 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 . + +# include "../sanity.hpp" + +# include + +# include +# include +# include + +# ifdef WORKAROUND_GCC46_COMPILER +// constexpr is buggy with templates in this version +// Is this still needed now that const_string is removed? +# define constexpr /* nothing */ +# endif + +// TODO see if I ever actually use this, and not the subclass +template +class const_array +{ + const T *d; + size_t n; +public: + typedef const T *iterator; + typedef std::reverse_iterator reverse_iterator; + + constexpr + const_array(std::nullptr_t) + : d(nullptr), n(0) + {} + + constexpr + const_array(const T *p, size_t z) + : d(p), n(z) + {} + + constexpr + const_array(const T *b, const T *e) + : d(b), n(e - b) + {} + + const_array(std::initializer_list list) + : d(list.begin()), n(list.size()) + {} + + // Implicit conversion from std::vector + const_array(const std::vector& v) + : d(v.data()), n(v.size()) + {} + + // but disallow conversion from a temporary + const_array(std::vector&&) = delete; + + // Oops. see src/warnings.hpp + constexpr + const T *data() const { return d; } + constexpr + size_t size() const { return n; } + constexpr + bool empty() const { return not n; } + constexpr explicit + operator bool() const { return n; } + + constexpr + std::pair cut(size_t o) const + { + return {const_array(d, o), const_array(d + o, n - o)}; + } + + constexpr + const_array first(size_t o) const + { + return cut(o).first; + } + + constexpr + const_array last(size_t l) const + { + return cut(size() - l).second; + } + + constexpr + const_array after(size_t o) const + { + return cut(o).second; + } + + constexpr + iterator begin() const { return d; } + constexpr + iterator end() const { return d + n; } + constexpr + reverse_iterator rbegin() const { return reverse_iterator(end()); } + constexpr + reverse_iterator rend() const { return reverse_iterator(begin()); } + + constexpr + const T& front() const { return *begin(); } + constexpr + const T& back() const { return *rbegin(); } + + // This probably shouldn't be used, but I'm adding it for porting. + T& operator[](size_t i) + { + return const_cast(d[i]); + } +}; + +# ifdef WORKAROUND_GCC46_COMPILER +# undef constexpr +# endif + +#endif // TMWA_GENERIC_CONST_ARRAY_HPP diff --git a/src/generic/db.cpp b/src/generic/db.cpp new file mode 100644 index 0000000..cc58ad8 --- /dev/null +++ b/src/generic/db.cpp @@ -0,0 +1,3 @@ +#include "db.hpp" + +#include "../poison.hpp" diff --git a/src/generic/db.hpp b/src/generic/db.hpp new file mode 100644 index 0000000..314c449 --- /dev/null +++ b/src/generic/db.hpp @@ -0,0 +1,180 @@ +#ifndef TMWA_GENERIC_DB_HPP +#define TMWA_GENERIC_DB_HPP +// db.hpp - convenience wrappers over std::map +// +// Copyright © 2013 Ben Longbons +// +// 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 . + +# include "../sanity.hpp" + +# include +# include + +template +class Map +{ + typedef std::map Impl; + + Impl impl; +public: + Map() = default; + Map(std::initializer_list> il) + : impl(il) + {} + typedef typename Impl::iterator iterator; + typedef typename Impl::const_iterator const_iterator; + + iterator begin() { return impl.begin(); } + iterator end() { return impl.end(); } + const_iterator begin() const { return impl.begin(); } + const_iterator end() const { return impl.end(); } + + V *search(const K& k) + { + iterator it = impl.find(k); + if (it == impl.end()) + return nullptr; + return &it->second; + } + const V *search(const K& k) const + { + const_iterator it = impl.find(k); + if (it == impl.end()) + return nullptr; + return &it->second; + } + void insert(const K& k, V v) + { + // As far as I can tell, this is the simplest way to + // implement move-only insert-with-replacement. + iterator it = impl.lower_bound(k); + // invariant: if it is valid, it->first >= k + if (it != impl.end() && it->first == k) + it->second = std::move(v); + else + it = impl.insert(std::pair(std::move(k), std::move(v))).first; + return (void)&it->second; + + } + V *init(const K& k) + { + return &impl[k]; + } + void erase(const K& k) + { + impl.erase(k); + } + void clear() + { + impl.clear(); + } + bool empty() const + { + return impl.empty(); + } + size_t size() const + { + return impl.size(); + } +}; + +template +class DMap +{ + typedef Map Impl; + + Impl impl; +public: + typedef typename Impl::iterator iterator; + typedef typename Impl::const_iterator const_iterator; + + iterator begin() { return impl.begin(); } + iterator end() { return impl.end(); } + const_iterator begin() const { return impl.begin(); } + const_iterator end() const { return impl.end(); } + + // const V& ? with a static default V? + V get(const K& k) + { + V *vp = impl.search(k); + return vp ? *vp : V(); + } + void put(const K& k, V v) + { + if (v == V()) + impl.erase(k); + else + impl.insert(k, std::move(v)); + } + void clear() + { + impl.clear(); + } + bool empty() const + { + return impl.empty(); + } + size_t size() const + { + return impl.size(); + } +}; + +template +class UPMap +{ + typedef std::unique_ptr U; + typedef Map Impl; + + Impl impl; +public: + typedef typename Impl::iterator iterator; + typedef typename Impl::const_iterator const_iterator; + + iterator begin() { return impl.begin(); } + iterator end() { return impl.end(); } + const_iterator begin() const { return impl.begin(); } + const_iterator end() const { return impl.end(); } + + // const V& ? with a static default V? + V *get(const K& k) + { + U *up = impl.search(k); + return up ? up->get() : nullptr; + } + void put(const K& k, U v) + { + if (!v) + impl.erase(k); + else + impl.insert(k, std::move(v)); + } + void clear() + { + impl.clear(); + } + bool empty() const + { + return impl.empty(); + } + size_t size() const + { + return impl.size(); + } +}; + +#endif // TMWA_GENERIC_DB_HPP diff --git a/src/generic/enum.cpp b/src/generic/enum.cpp new file mode 100644 index 0000000..c5ab250 --- /dev/null +++ b/src/generic/enum.cpp @@ -0,0 +1,3 @@ +#include "enum.hpp" + +#include "../poison.hpp" diff --git a/src/generic/enum.hpp b/src/generic/enum.hpp new file mode 100644 index 0000000..6f29981 --- /dev/null +++ b/src/generic/enum.hpp @@ -0,0 +1,170 @@ +#ifndef TMWA_GENERIC_ENUM_HPP +#define TMWA_GENERIC_ENUM_HPP + +# include "../sanity.hpp" + +# include + +# include "../compat/iter.hpp" + +template +struct earray +{ + // no ctor/dtor and one public member variable for easy initialization + T _data[size_t(max)]; + + T& operator[](E v) + { + return _data[size_t(v)]; + } + + const T& operator[](E v) const + { + return _data[size_t(v)]; + } + + T *begin() + { + return _data; + } + + T *end() + { + return _data + size_t(max); + } + + const T *begin() const + { + return _data; + } + + const T *end() const + { + return _data + size_t(max); + } + + friend bool operator == (const earray& l, const earray& r) + { + return std::equal(l.begin(), l.end(), r.begin()); + } + + friend bool operator != (const earray& l, const earray& r) + { + return !(l == r); + } +}; + +template +class eptr +{ + T *_data; +public: + eptr(std::nullptr_t=nullptr) + : _data(nullptr) + {} + + template + eptr(earray& arr) + : _data(arr._data) + {} + + T& operator [](E v) + { + return _data[size_t(v)]; + } + + explicit operator bool() + { + return _data; + } + + bool operator not() + { + return not _data; + } +}; + +// std::underlying_type isn't supported until gcc 4.7 +// this is a poor man's emulation +template +struct underlying_type +{ + static_assert(std::is_enum::value, "Only enums have underlying type!"); + typedef typename std::conditional< + std::is_signed::value, + typename std::make_signed::type, + typename std::make_unsigned::type + >::type type; +}; + +template::value> +struct remove_enum +{ + typedef E type; +}; +template +struct remove_enum +{ + typedef typename underlying_type::type type; +}; + + +# define ENUM_BITWISE_OPERATORS(E) \ +inline \ +E operator & (E l, E r) \ +{ \ + typedef underlying_type::type U; \ + return E(U(l) & U(r)); \ +} \ +inline \ +E operator | (E l, E r) \ +{ \ + typedef underlying_type::type U; \ + return E(U(l) | U(r)); \ +} \ +inline \ +E operator ^ (E l, E r) \ +{ \ + typedef underlying_type::type U; \ + return E(U(l) ^ U(r)); \ +} \ +inline \ +E& operator &= (E& l, E r) \ +{ \ + return l = l & r; \ +} \ +inline \ +E& operator |= (E& l, E r) \ +{ \ + return l = l | r; \ +} \ +inline \ +E& operator ^= (E& l, E r) \ +{ \ + return l = l ^ r; \ +} \ +inline \ +E operator ~ (E r) \ +{ \ + return E(-1) ^ r; \ +} + +template +class EnumMath +{ + typedef typename underlying_type::type U; +public: + static + E inced(E v) + { + return E(U(v) + 1); + } +}; + +template +IteratorPair>> erange(E b, E e) +{ + return {b, e}; +} + +#endif // TMWA_GENERIC_ENUM_HPP diff --git a/src/generic/intern-pool.cpp b/src/generic/intern-pool.cpp new file mode 100644 index 0000000..33649f2 --- /dev/null +++ b/src/generic/intern-pool.cpp @@ -0,0 +1,3 @@ +#include "intern-pool.hpp" + +#include "../poison.hpp" diff --git a/src/generic/intern-pool.hpp b/src/generic/intern-pool.hpp new file mode 100644 index 0000000..69f20ef --- /dev/null +++ b/src/generic/intern-pool.hpp @@ -0,0 +1,41 @@ +#ifndef TMWA_GENERIC_INTERN_POOL_HPP +#define TMWA_GENERIC_INTERN_POOL_HPP + +# include + +# include +# include + +# include "../strings/rstring.hpp" +# include "../strings/zstring.hpp" +# include "../strings/xstring.hpp" + +class InternPool +{ + std::map known; + std::vector names; +public: + size_t intern(XString name_) + { + // TODO just look up the XString, the memory should not move by now + RString name = name_; + // hm, I could change this to do aliases + auto pair = known.insert({name, known.size()}); + if (pair.second) + names.push_back(name); + assert (known.size() == names.size()); + return pair.first->second; + } + + ZString outtern(size_t sz) const + { + return names[sz]; + } + + size_t size() const + { + return known.size(); + } +}; + +#endif // TMWA_GENERIC_INTERN_POOL_HPP diff --git a/src/generic/intern-pool_test.cpp b/src/generic/intern-pool_test.cpp new file mode 100644 index 0000000..bf17b67 --- /dev/null +++ b/src/generic/intern-pool_test.cpp @@ -0,0 +1,20 @@ +#include "intern-pool.hpp" + +#include + +#include "../strings/base.hpp" + +TEST(InternPool, whydoesthisalwaysneedasecondname) +{ + InternPool p; + EXPECT_EQ(0, p.size()); + EXPECT_EQ(0, p.intern("hello")); + EXPECT_EQ(0, p.intern("hello")); + EXPECT_EQ(1, p.size()); + EXPECT_EQ(1, p.intern("world")); + EXPECT_EQ(0, p.intern("hello")); + EXPECT_EQ(1, p.intern("world")); + EXPECT_EQ(2, p.size()); + EXPECT_EQ("hello", p.outtern(0)); + EXPECT_EQ("world", p.outtern(1)); +} diff --git a/src/generic/matrix.cpp b/src/generic/matrix.cpp new file mode 100644 index 0000000..9ee049e --- /dev/null +++ b/src/generic/matrix.cpp @@ -0,0 +1,3 @@ +#include "matrix.hpp" + +#include "../poison.hpp" diff --git a/src/generic/matrix.hpp b/src/generic/matrix.hpp new file mode 100644 index 0000000..3530ba7 --- /dev/null +++ b/src/generic/matrix.hpp @@ -0,0 +1,54 @@ +#ifndef TMWA_GENERIC_MATRIX_HPP +#define TMWA_GENERIC_MATRIX_HPP + +# include "../sanity.hpp" + +# include "../compat/memory.hpp" + +template +class Matrix +{ + std::unique_ptr _data; + size_t _xs, _ys; +public: + Matrix() + : _data() + , _xs() + , _ys() + {} + Matrix(size_t x, size_t y) + : _data(make_unique(x * y)) + , _xs(x) + , _ys(y) + {} + // no copy-ctor or copy-assign + + void reset(size_t x, size_t y) + { + *this = Matrix(x, y); + } + void clear() + { + *this = Matrix(); + } + + T& ref(size_t x, size_t y) + { + return _data[x + y * _xs]; + } + const T& ref(size_t x, size_t y) const + { + return _data[x + y * _xs]; + } + + size_t xs() const + { + return _xs; + } + size_t ys() const + { + return _ys; + } +}; + +#endif // TMWA_GENERIC_MATRIX_HPP diff --git a/src/generic/md5.cpp b/src/generic/md5.cpp new file mode 100644 index 0000000..a626dc5 --- /dev/null +++ b/src/generic/md5.cpp @@ -0,0 +1,234 @@ +#include "md5.hpp" + +#include + +#include "../compat/rawmem.hpp" + +#include "../strings/xstring.hpp" +#include "../strings/vstring.hpp" + +#include "random.hpp" + +#include "../poison.hpp" + +// auxilary data +/* +sin() constant table +#Reformatted output of: +echo 'scale=40; obase=16; for (i=1;i<=64;i++) print 2^32 * sin(i), "\n"' | +bc | sed 's/^-//;s/^/0x/;s/\..*$/,/' +*/ +static +const uint32_t T[64] = +{ + // used by round 1 + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0 + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4 + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8 + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12 + // used by round 2 + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16 + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, //20 + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24 + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28 + // used by round 3 + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32 + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36 + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, //40 + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44 + // used by round 4 + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48 + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52 + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56 + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, //60 +}; + +// auxilary functions +// note - the RFC defines these by non-CS conventions: or=v, and=(empty) +static +uint32_t rotate_left(uint32_t val, unsigned shift) +{ + return val << shift | val >> (32 - shift); +} + +static +uint32_t F(uint32_t X, uint32_t Y, uint32_t Z) +{ + return (X & Y) | (~X & Z); +} +static +uint32_t G(uint32_t X, uint32_t Y, uint32_t Z) +{ + return (X & Z) | (Y & ~Z); +} +static +uint32_t H(uint32_t X, uint32_t Y, uint32_t Z) +{ + return X ^ Y ^ Z; +} +static +uint32_t I(uint32_t X, uint32_t Y, uint32_t Z) +{ + return Y ^ (X | ~Z); +} + +static +const struct +{ + uint8_t k : 4; + uint8_t : 0; + uint8_t s : 5; +// uint8_t i : 6; just increments constantly, from 1 .. 64 over all rounds +} +MD5_round1[16] = +{ + { 0, 7}, { 1, 12}, { 2, 17}, { 3, 22}, + { 4, 7}, { 5, 12}, { 6, 17}, { 7, 22}, + { 8, 7}, { 9, 12}, {10, 17}, {11, 22}, + {12, 7}, {13, 12}, {14, 17}, {15, 22}, +}, +MD5_round2[16] = +{ + { 1, 5}, { 6, 9}, {11, 14}, { 0, 20}, + { 5, 5}, {10, 9}, {15, 14}, { 4, 20}, + { 9, 5}, {14, 9}, { 3, 14}, { 8, 20}, + {13, 5}, { 2, 9}, { 7, 14}, {12, 20}, +}, +MD5_round3[16] = +{ + { 5, 4}, { 8, 11}, {11, 16}, {14, 23}, + { 1, 4}, { 4, 11}, { 7, 16}, {10, 23}, + {13, 4}, { 0, 11}, { 3, 16}, { 6, 23}, + { 9, 4}, {12, 11}, {15, 16}, { 2, 23}, +}, +MD5_round4[16] = +{ + { 0, 6}, { 7, 10}, {14, 15}, { 5, 21}, + {12, 6}, { 3, 10}, {10, 15}, { 1, 21}, + { 8, 6}, {15, 10}, { 6, 15}, {13, 21}, + { 4, 6}, {11, 10}, { 2, 15}, { 9, 21}, +}; + + +void MD5_init(MD5_state* state) +{ + // in the RFC, these are specified as bytes, interpreted as little-endian + state->val[0] = 0x67452301; + state->val[1] = 0xEFCDAB89; + state->val[2] = 0x98BADCFE; + state->val[3] = 0x10325476; +} + +#define X block.data + +void MD5_do_block(MD5_state* state, MD5_block block) +{ +#define a state->val[(16 - i) % 4] +#define b state->val[(17 - i) % 4] +#define c state->val[(18 - i) % 4] +#define d state->val[(19 - i) % 4] + // save the values + const MD5_state saved = *state; + // round 1 + for (int i = 0; i < 16; i++) + { +#define k MD5_round1[i].k +#define s MD5_round1[i].s + a = b + rotate_left(a + F(b, c, d) + X[k] + T[i + 0x0], s); +#undef k +#undef s + } + // round 2 + for (int i = 0; i < 16; i++) + { +#define k MD5_round2[i].k +#define s MD5_round2[i].s + a = b + rotate_left(a + G(b, c, d) + X[k] + T[i + 0x10], s); +#undef k +#undef s + } + // round 3 + for (int i = 0; i < 16; i++) + { +#define k MD5_round3[i].k +#define s MD5_round3[i].s + a = b + rotate_left(a + H(b, c, d) + X[k] + T[i + 0x20], s); +#undef k +#undef s + } + // round 4 + for (int i = 0; i < 16; i++) + { +#define k MD5_round4[i].k +#define s MD5_round4[i].s + a = b + rotate_left(a + I(b, c, d) + X[k] + T[i + 0x30], s); +#undef k +#undef s + } + // adjust state based on original + state->val[0] += saved.val[0]; + state->val[1] += saved.val[1]; + state->val[2] += saved.val[2]; + state->val[3] += saved.val[3]; +#undef a +#undef b +#undef c +#undef d +} + +void MD5_to_bin(MD5_state state, md5_binary& out) +{ + for (int i = 0; i < 0x10; i++) + out[i] = state.val[i / 4] >> 8 * (i % 4); +} + +static +const char hex[] = "0123456789abcdef"; + +void MD5_to_str(MD5_state state, md5_string& out_) +{ + md5_binary bin; + MD5_to_bin(state, bin); + char out[0x20]; + for (int i = 0; i < 0x10; i++) + out[2 * i] = hex[bin[i] >> 4], + out[2 * i + 1] = hex[bin[i] & 0xf]; + out_ = stringish(XString(out, out + 0x20, nullptr)); +} + +MD5_state MD5_from_string(XString msg) +{ + MD5_state state; + MD5_init(&state); + MD5_block block; + const uint64_t msg_full_len = msg.size(); + while (msg.size() >= 64) + { + for (int i = 0; i < 0x10; i++) + X[i] = msg[4 * i + 0] | msg[4 * i + 1] << 8 | msg[4 * i + 2] << 16 | msg[4 * i + 3] << 24; + MD5_do_block(&state, block); + msg = msg.xslice_t(64); + } + // now pad 1-512 bits + the 64-bit length - may be two blocks + uint8_t buf[0x40] = {}; + really_memcpy(buf, reinterpret_cast(msg.data()), msg.size()); + buf[msg.size()] = 0x80; // a single one bit + if (64 - msg.size() > 8) + { + for (int i = 0; i < 8; i++) + buf[0x38 + i] = (msg_full_len * 8) >> (i * 8); + } + for (int i = 0; i < 0x10; i++) + X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; + MD5_do_block(&state, block); + if (64 - msg.size() <= 8) + { + really_memset0(buf, 0x38); + for (int i = 0; i < 8; i++) + buf[0x38 + i] = (msg_full_len * 8) >> (i * 8); + for (int i = 0; i < 0x10; i++) + X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; + MD5_do_block(&state, block); + } + return state; +} diff --git a/src/generic/md5.hpp b/src/generic/md5.hpp new file mode 100644 index 0000000..ba1212a --- /dev/null +++ b/src/generic/md5.hpp @@ -0,0 +1,44 @@ +#ifndef TMWA_GENERIC_MD5CALC_HPP +#define TMWA_GENERIC_MD5CALC_HPP + +# include "../sanity.hpp" + +# include + +# include +# include +# include + +# include + +# include "../strings/fwd.hpp" +# include "../strings/vstring.hpp" + +/// The digest state - becomes the output +struct MD5_state +{ + // classically named {A,B,C,D} + // but use an array so we can index + uint32_t val[4]; +}; +struct MD5_block +{ + uint32_t data[16]; +}; + +struct md5_binary : std::array {}; +struct md5_string : VString<0x20> {}; +struct SaltString : VString<5> {}; + +// Implementation +void MD5_init(MD5_state *state); +void MD5_do_block(MD5_state *state, MD5_block block); + +// Output formatting +void MD5_to_bin(MD5_state state, md5_binary& out); +void MD5_to_str(MD5_state state, md5_string& out); + +// Convenience +MD5_state MD5_from_string(XString msg); + +#endif // TMWA_GENERIC_MD5CALC_HPP diff --git a/src/generic/md5_test.cpp b/src/generic/md5_test.cpp new file mode 100644 index 0000000..7086c8c --- /dev/null +++ b/src/generic/md5_test.cpp @@ -0,0 +1,28 @@ +#include "md5.hpp" + +#include + +#include "../strings/xstring.hpp" +#include "../strings/vstring.hpp" + +// This should be made part of the main API, +// but is not yet to keep the diff small. +// Edit: hack to fix the new strict comparison. +static +VString<32> MD5(XString in) +{ + md5_string out; + MD5_to_str(MD5_from_string(in), out); + return out; +} + +TEST(md5calc, rfc1321) +{ + EXPECT_EQ("d41d8cd98f00b204e9800998ecf8427e", MD5("")); + EXPECT_EQ("0cc175b9c0f1b6a831c399e269772661", MD5("a")); + EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72", MD5("abc")); + EXPECT_EQ("f96b697d7cb7938d525a2f31aaf161d0", MD5("message digest")); + EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b", MD5("abcdefghijklmnopqrstuvwxyz")); + EXPECT_EQ("d174ab98d277d9f5a5611c2c9f419d9f", MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")); + EXPECT_EQ("57edf4a22be3c955ac49da2e2107b67a", MD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890")); +} diff --git a/src/generic/operators.cpp b/src/generic/operators.cpp new file mode 100644 index 0000000..877aec6 --- /dev/null +++ b/src/generic/operators.cpp @@ -0,0 +1,3 @@ +#include "operators.hpp" + +#include "../poison.hpp" diff --git a/src/generic/operators.hpp b/src/generic/operators.hpp new file mode 100644 index 0000000..3d3dccd --- /dev/null +++ b/src/generic/operators.hpp @@ -0,0 +1,47 @@ +#ifndef TMWA_GENERIC_OPERATORS_HPP +#define TMWA_GENERIC_OPERATORS_HPP + +namespace _operators +{ + class Comparable {}; + + template + bool operator == (T l, T r) + { + return l.value == r.value; + } + + template + bool operator != (T l, T r) + { + return l.value != r.value; + } + + template + bool operator < (T l, T r) + { + return l.value < r.value; + } + + template + bool operator <= (T l, T r) + { + return l.value <= r.value; + } + + template + bool operator > (T l, T r) + { + return l.value > r.value; + } + + template + bool operator >= (T l, T r) + { + return l.value >= r.value; + } +} + +using _operators::Comparable; + +#endif // TMWA_GENERIC_OPERATORS_HPP diff --git a/src/generic/random.cpp b/src/generic/random.cpp new file mode 100644 index 0000000..273dcec --- /dev/null +++ b/src/generic/random.cpp @@ -0,0 +1,8 @@ +#include "random2.hpp" + +#include "../poison.hpp" + +namespace random_ +{ + std::mt19937 generate{std::random_device()()}; +} // namespace random_ diff --git a/src/generic/random.hpp b/src/generic/random.hpp new file mode 100644 index 0000000..5d7a7af --- /dev/null +++ b/src/generic/random.hpp @@ -0,0 +1,69 @@ +#ifndef TMWA_GENERIC_RANDOM_HPP +#define TMWA_GENERIC_RANDOM_HPP + +# include "random.t.hpp" + +# include "../sanity.hpp" + +# include + +// This is not namespace random since that collides with a C function, +// but this can be revisited when everything goes into namespace tmwa. +namespace random_ +{ + /// Get a random number from 0 .. 2**32 - 1 + extern std::mt19937 generate; + + /// Get a random number from 0 .. bound - 1 + inline + int to(int bound) + { + std::uniform_int_distribution dist(0, bound - 1); + return dist(generate); + } + + /// Get a random number from low .. high (inclusive!) + inline + int in(int low, int high) + { + std::uniform_int_distribution dist(low, high); + return dist(generate); + } + + inline + bool coin() + { + // sigh, can't specify directly ... + std::uniform_int_distribution dist(false, true); + return dist(generate); + } + + inline + bool chance(Fraction f) + { + if (f.num <= 0) + return false; + if (f.num >= f.den) + return true; + return random_::to(f.den) < f.num; + } + + // C is usually one of: + // std::vector + // std::initializer_list + // std::array + template + auto choice(C&& c) -> decltype(*c.begin()) + { + return *(c.begin() + random_::to(c.size())); + } + + // allow bare braces + template + T choice(std::initializer_list&& il) + { + return random_::choice(il); + } +} // namespace random_ + +#endif // TMWA_GENERIC_RANDOM_HPP diff --git a/src/generic/random.t.hpp b/src/generic/random.t.hpp new file mode 100644 index 0000000..feea2b0 --- /dev/null +++ b/src/generic/random.t.hpp @@ -0,0 +1,23 @@ +#ifndef TMWA_GENERIC_RANDOM_T_HPP +#define TMWA_GENERIC_RANDOM_T_HPP + +namespace random_ +{ + struct Fraction + { + int num, den; + }; + + template + struct Fixed + { + T num; + + operator Fraction() + { + return {num, den}; + } + }; +} // namespace random_ + +#endif // TMWA_GENERIC_RANDOM_T_HPP diff --git a/src/generic/random2.hpp b/src/generic/random2.hpp new file mode 100644 index 0000000..4bdc72e --- /dev/null +++ b/src/generic/random2.hpp @@ -0,0 +1,75 @@ +#ifndef TMWA_GENERIC_RANDOM2_HPP +#define TMWA_GENERIC_RANDOM2_HPP + +# include "random.hpp" + +# include + +# include "../compat/iter.hpp" + +namespace random_ +{ + namespace detail + { + struct RandomIterator + { + int bound; + int current; + bool frist; + + void operator ++() + { + frist = false; + // TODO: reimplement in terms of LFSRs, so that certain + // blockage patterns don't favor adjacent cells. + current = current + 1; + if (current == bound) + current = 0; + } + int operator *() + { + return current; + } + }; + + inline + bool operator == (RandomIterator l, RandomIterator r) + { + return l.current == r.current && l.frist == r.frist; + } + + inline + bool operator != (RandomIterator l, RandomIterator r) + { + return !(l == r); + } + } + + /// Yield every cell from 0 .. bound - 1 in some order. + /// The starting position is random, but not the order itself. + /// + /// Expected usage: + /// for (int i : random_::iterator(vec.size())) + /// if (vec[i].okay()) + /// return frob(vec[i]); + inline + IteratorPair iterator(int bound) + { + int current = random_::to(bound); + return + { + detail::RandomIterator{bound, current, true}, + detail::RandomIterator{bound, current, false} + }; + } + + /// similar to std::random_shuffle(c.begin(), c.end()), but guaranteed + /// to use a good source of randomness. + template + void shuffle(C&& c) + { + std::random_shuffle(c.begin(), c.end(), random_::to); + } +} // namespace random_ + +#endif // TMWA_GENERIC_RANDOM2_HPP diff --git a/src/io/cxxstdio.cpp b/src/io/cxxstdio.cpp index 47617cb..aa603e4 100644 --- a/src/io/cxxstdio.cpp +++ b/src/io/cxxstdio.cpp @@ -1 +1,3 @@ #include "cxxstdio.hpp" + +#include "../poison.hpp" diff --git a/src/io/cxxstdio.hpp b/src/io/cxxstdio.hpp index 753a1fc..0324ebc 100644 --- a/src/io/cxxstdio.hpp +++ b/src/io/cxxstdio.hpp @@ -24,9 +24,10 @@ # include # include -// TODO get rid of these header order violations -# include "../common/const_array.hpp" -# include "../common/utils2.hpp" +# include "../compat/cast.hpp" + +# include "../generic/const_array.hpp" +# include "../generic/enum.hpp" # include "fwd.hpp" @@ -213,27 +214,25 @@ namespace cxxstdio } }; -# define XPRINTF(out, fmt, ...) \ - (/*[&]() -> int*/ \ - { \ - struct format_impl \ - { \ - constexpr static \ - const char *print_format() { return fmt; } \ - }; \ - /*return*/ cxxstdio::PrintFormatter::print(out, ## __VA_ARGS__); \ - }/*()*/) +# define XPRINTF(out, fmt, ...) \ + ({ \ + struct format_impl \ + { \ + constexpr static \ + const char *print_format() { return fmt; } \ + }; \ + cxxstdio::PrintFormatter::print(out, ## __VA_ARGS__); \ + }) -# define XSCANF(out, fmt, ...) \ - (/*[&]() -> int*/ \ - { \ - struct format_impl \ - { \ - constexpr static \ - const char *scan_format() { return fmt; } \ - }; \ - /*return*/ cxxstdio::ScanFormatter::scan(out, ## __VA_ARGS__); \ - }/*()*/) +# define XSCANF(out, fmt, ...) \ + ({ \ + struct format_impl \ + { \ + constexpr static \ + const char *scan_format() { return fmt; } \ + }; \ + cxxstdio::ScanFormatter::scan(out, ## __VA_ARGS__); \ + }) # define FPRINTF(file, fmt, ...) XPRINTF(/*no_cast*/(file), fmt, ## __VA_ARGS__) # define FSCANF(file, fmt, ...) XSCANF(no_cast(file), fmt, ## __VA_ARGS__) @@ -241,23 +240,21 @@ namespace cxxstdio # define SPRINTF(str, fmt, ...) XPRINTF(base_cast(str), fmt, ## __VA_ARGS__) # define SNPRINTF(str, n, fmt, ...) XPRINTF(base_cast&>(str), fmt, ## __VA_ARGS__) # define SCANF(fmt, ...) FSCANF(stdin, fmt, ## __VA_ARGS__) -# define SSCANF(str, fmt, ...) XSCANF(/*ZString or compatible*/str, fmt, ## __VA_ARGS__) +# define SSCANF(str, fmt, ...) XSCANF(maybe_cast(str), fmt, ## __VA_ARGS__) # define STRPRINTF(fmt, ...) \ - (/*[&]() -> AString*/ \ - { \ + ({ \ AString _out_impl; \ SPRINTF(_out_impl, fmt, ## __VA_ARGS__); \ - /*return*/ _out_impl; \ - }/*()*/) + _out_impl; \ + }) # define STRNPRINTF(n, fmt, ...) \ - (/*[&]() -> VString*/ \ - { \ + ({ \ VString _out_impl; \ SNPRINTF(_out_impl, n, fmt, ## __VA_ARGS__); \ - /*return*/ _out_impl; \ - }/*()*/) + _out_impl; \ + }) } // namespace cxxstdio diff --git a/src/io/fd.cpp b/src/io/fd.cpp index 87d3967..4fc33e9 100644 --- a/src/io/fd.cpp +++ b/src/io/fd.cpp @@ -23,6 +23,7 @@ #include "../strings/zstring.hpp" +#include "../poison.hpp" namespace io { diff --git a/src/io/fd.hpp b/src/io/fd.hpp index ca703d6..7afb40f 100644 --- a/src/io/fd.hpp +++ b/src/io/fd.hpp @@ -165,4 +165,4 @@ namespace io }; } // namespace io -#endif //TMWA_IO_FD_HPP +#endif // TMWA_IO_FD_HPP diff --git a/src/io/fwd.hpp b/src/io/fwd.hpp index 52e6d6d..1e5fa82 100644 --- a/src/io/fwd.hpp +++ b/src/io/fwd.hpp @@ -29,4 +29,4 @@ namespace io class AppendFile; } // namespace io -#endif //TMWA_IO_FWD_HPP +#endif // TMWA_IO_FWD_HPP diff --git a/src/io/line.hpp b/src/io/line.hpp index bdff1bb..321cdf7 100644 --- a/src/io/line.hpp +++ b/src/io/line.hpp @@ -94,4 +94,4 @@ namespace io }; } // namespace io -#endif //TMWA_IO_LINE_HPP +#endif // TMWA_IO_LINE_HPP diff --git a/src/io/read.hpp b/src/io/read.hpp index 2355e46..f99fb56 100644 --- a/src/io/read.hpp +++ b/src/io/read.hpp @@ -50,4 +50,4 @@ namespace io }; } // namespace io -#endif //TMWA_IO_READ_HPP +#endif // TMWA_IO_READ_HPP diff --git a/src/io/tty.cpp b/src/io/tty.cpp index d2e084e..e71ee44 100644 --- a/src/io/tty.cpp +++ b/src/io/tty.cpp @@ -18,4 +18,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include "../poison.hpp" + /* Nothing to see here, move along */ diff --git a/src/io/tty.hpp b/src/io/tty.hpp index 86616f1..97487b8 100644 --- a/src/io/tty.hpp +++ b/src/io/tty.hpp @@ -35,4 +35,4 @@ # define SGR_RESET "\e[0m" -#endif //TMWA_IO_TTY_HPP +#endif // TMWA_IO_TTY_HPP diff --git a/src/io/write.hpp b/src/io/write.hpp index 0a238c0..870ebb5 100644 --- a/src/io/write.hpp +++ b/src/io/write.hpp @@ -66,4 +66,4 @@ namespace io int do_vprint(WriteFile& out, const char *fmt, va_list ap); } // namespace io -#endif //TMWA_IO_WRITE_HPP +#endif // TMWA_IO_WRITE_HPP diff --git a/src/login/GNUmakefile b/src/login/GNUmakefile deleted file mode 100644 index eb983e8..0000000 --- a/src/login/GNUmakefile +++ /dev/null @@ -1,7 +0,0 @@ -.SUFFIXES: -bin/tmwa-login: - ${MAKE} -C ../.. bin/tmwa-login -clean: - rm -r ../../obj/login/ -%:: - ${MAKE} -C ../.. obj/login/$@ diff --git a/src/login/login.cpp b/src/login/login.cpp index b21b028..c9b285f 100644 --- a/src/login/login.cpp +++ b/src/login/login.cpp @@ -22,23 +22,24 @@ #include "../strings/xstring.hpp" #include "../strings/vstring.hpp" +#include "../generic/db.hpp" +#include "../generic/random.hpp" + #include "../io/cxxstdio.hpp" #include "../io/lock.hpp" #include "../io/read.hpp" #include "../io/tty.hpp" -#include "../common/config_parse.hpp" -#include "../common/core.hpp" -#include "../common/db.hpp" -#include "../common/extract.hpp" -#include "../common/human_time_diff.hpp" -#include "../common/md5calc.hpp" -#include "../common/mmo.hpp" -#include "../common/random.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" -#include "../common/version.hpp" -#include "../common/utils.hpp" +#include "../mmo/config_parse.hpp" +#include "../mmo/core.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/human_time_diff.hpp" +#include "../mmo/md5more.hpp" +#include "../mmo/mmo.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" +#include "../mmo/version.hpp" +#include "../mmo/utils.hpp" #include "../poison.hpp" diff --git a/src/login/main.cpp b/src/login/main.cpp index 2ef17a0..470bcd1 100644 --- a/src/login/main.cpp +++ b/src/login/main.cpp @@ -1,2 +1,4 @@ // dummy file to make Make dependencies work #include "login.hpp" + +#include "../poison.hpp" diff --git a/src/map/GNUmakefile b/src/map/GNUmakefile deleted file mode 100644 index 22f6daa..0000000 --- a/src/map/GNUmakefile +++ /dev/null @@ -1,7 +0,0 @@ -.SUFFIXES: -bin/tmwa-map: - ${MAKE} -C ../.. bin/tmwa-map -clean: - rm -r ../../obj/map/ -%:: - ${MAKE} -C ../.. obj/map/$@ diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index 39e81a9..29bf0b1 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -4,27 +4,29 @@ #include #include +#include "../compat/nullpo.hpp" +#include "../compat/fun.hpp" + #include "../strings/mstring.hpp" #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" #include "../strings/vstring.hpp" +#include "../generic/random.hpp" + #include "../io/cxxstdio.hpp" #include "../io/read.hpp" #include "../io/write.hpp" -#include "../common/config_parse.hpp" -#include "../common/core.hpp" -#include "../common/extract.hpp" -#include "../common/human_time_diff.hpp" -#include "../common/mmo.hpp" -#include "../common/nullpo.hpp" -#include "../common/random.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" -#include "../common/utils2.hpp" -#include "../common/version.hpp" +#include "../mmo/config_parse.hpp" +#include "../mmo/core.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/human_time_diff.hpp" +#include "../mmo/mmo.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" +#include "../mmo/version.hpp" #include "battle.hpp" #include "chrif.hpp" diff --git a/src/map/atcommand.hpp b/src/map/atcommand.hpp index c825976..16d1efc 100644 --- a/src/map/atcommand.hpp +++ b/src/map/atcommand.hpp @@ -1,9 +1,9 @@ -#ifndef ATCOMMAND_HPP -#define ATCOMMAND_HPP +#ifndef TMWA_MAP_ATCOMMAND_HPP +#define TMWA_MAP_ATCOMMAND_HPP # include "../strings/fwd.hpp" -# include "../common/const_array.hpp" +# include "../generic/const_array.hpp" # include "map.hpp" @@ -19,4 +19,4 @@ extern AString gm_log; void atcommand_config_write(ZString cfgName); -#endif // ATCOMMAND_HPP +#endif // TMWA_MAP_ATCOMMAND_HPP diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 59e798d..7e563b8 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -2,15 +2,18 @@ #include +#include "../compat/alg.hpp" +#include "../compat/nullpo.hpp" + #include "../strings/astring.hpp" #include "../strings/zstring.hpp" +#include "../generic/random.hpp" + #include "../io/cxxstdio.hpp" #include "../io/read.hpp" -#include "../common/config_parse.hpp" -#include "../common/nullpo.hpp" -#include "../common/random.hpp" +#include "../mmo/config_parse.hpp" #include "clif.hpp" #include "itemdb.hpp" diff --git a/src/map/battle.hpp b/src/map/battle.hpp index 243d7fb..fd452e8 100644 --- a/src/map/battle.hpp +++ b/src/map/battle.hpp @@ -1,11 +1,11 @@ -#ifndef BATTLE_HPP -#define BATTLE_HPP +#ifndef TMWA_MAP_BATTLE_HPP +#define TMWA_MAP_BATTLE_HPP # include "battle.t.hpp" # include "../strings/fwd.hpp" -# include "../common/timer.t.hpp" +# include "../mmo/timer.t.hpp" # include "magic-interpreter.t.hpp" # include "map.t.hpp" @@ -204,4 +204,4 @@ extern struct Battle_Config bool battle_config_read(ZString cfgName); void battle_config_check(); -#endif // BATTLE_HPP +#endif // TMWA_MAP_BATTLE_HPP diff --git a/src/map/battle.t.hpp b/src/map/battle.t.hpp index 1242c85..76ff571 100644 --- a/src/map/battle.t.hpp +++ b/src/map/battle.t.hpp @@ -1,7 +1,7 @@ -#ifndef BATTLE_T_HPP -#define BATTLE_T_HPP +#ifndef TMWA_MAP_BATTLE_T_HPP +#define TMWA_MAP_BATTLE_T_HPP -# include "../common/utils2.hpp" +# include "../generic/enum.hpp" namespace e { @@ -226,4 +226,4 @@ enum class DamageType : uint8_t FLEE2 = 0x0b, }; -#endif // BATTLE_T_HPP +#endif // TMWA_MAP_BATTLE_T_HPP diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp index 48cff81..ea74ca4 100644 --- a/src/map/chrif.cpp +++ b/src/map/chrif.cpp @@ -4,15 +4,17 @@ #include +#include "../compat/fun.hpp" +#include "../compat/nullpo.hpp" + #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../io/cxxstdio.hpp" -#include "../common/nullpo.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" -#include "../common/utils.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" +#include "../mmo/utils.hpp" #include "battle.hpp" #include "clif.hpp" diff --git a/src/map/chrif.hpp b/src/map/chrif.hpp index 734f1ac..2dfda43 100644 --- a/src/map/chrif.hpp +++ b/src/map/chrif.hpp @@ -1,11 +1,11 @@ -#ifndef CHRIF_HPP -#define CHRIF_HPP +#ifndef TMWA_MAP_CHRIF_HPP +#define TMWA_MAP_CHRIF_HPP # include "../strings/fwd.hpp" -# include "../common/dumb_ptr.hpp" -# include "../common/human_time_diff.hpp" -# include "../common/ip.hpp" +# include "../mmo/dumb_ptr.hpp" +# include "../mmo/human_time_diff.hpp" +# include "../mmo/ip.hpp" # include "map.hpp" @@ -39,4 +39,4 @@ void do_init_chrif(void); // only used by intif.cpp extern Session *char_session; -#endif // CHRIF_HPP +#endif // TMWA_MAP_CHRIF_HPP diff --git a/src/map/clif.cpp b/src/map/clif.cpp index 37646bf..3ec41ae 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -6,19 +6,24 @@ #include #include +#include "../compat/alg.hpp" +#include "../compat/attr.hpp" +#include "../compat/fun.hpp" +#include "../compat/nullpo.hpp" + #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" +#include "../generic/random.hpp" + #include "../io/cxxstdio.hpp" #include "../io/write.hpp" -#include "../common/md5calc.hpp" -#include "../common/random.hpp" -#include "../common/nullpo.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" -#include "../common/version.hpp" +#include "../mmo/md5more.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" +#include "../mmo/version.hpp" #include "atcommand.hpp" #include "battle.hpp" diff --git a/src/map/clif.hpp b/src/map/clif.hpp index e84adf3..c346f7d 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -1,5 +1,5 @@ -#ifndef CLIF_HPP -#define CLIF_HPP +#ifndef TMWA_MAP_CLIF_HPP +#define TMWA_MAP_CLIF_HPP # include "clif.t.hpp" @@ -7,9 +7,10 @@ # include "../strings/fwd.hpp" -# include "../common/const_array.hpp" -# include "../common/ip.hpp" -# include "../common/timer.t.hpp" +# include "../generic/const_array.hpp" + +# include "../mmo/ip.hpp" +# include "../mmo/timer.t.hpp" # include "battle.t.hpp" # include "map.hpp" @@ -163,4 +164,4 @@ int clif_foreachclient(std::function)>); void do_init_clif(void); -#endif // CLIF_HPP +#endif // TMWA_MAP_CLIF_HPP diff --git a/src/map/clif.t.hpp b/src/map/clif.t.hpp index 96b8a9c..a52b33a 100644 --- a/src/map/clif.t.hpp +++ b/src/map/clif.t.hpp @@ -1,5 +1,5 @@ -#ifndef CLIF_T_HPP -#define CLIF_T_HPP +#ifndef TMWA_MAP_CLIF_T_HPP +#define TMWA_MAP_CLIF_T_HPP # include @@ -18,4 +18,4 @@ enum class BeingRemoveWhy : uint8_t NEGATIVE1 = 0xff, }; -#endif // CLIF_T_HPP +#endif // TMWA_MAP_CLIF_T_HPP diff --git a/src/map/grfio.cpp b/src/map/grfio.cpp index d89fe21..bf55cf7 100644 --- a/src/map/grfio.cpp +++ b/src/map/grfio.cpp @@ -19,7 +19,7 @@ #include "../io/cxxstdio.hpp" #include "../io/read.hpp" -#include "../common/extract.hpp" +#include "../mmo/extract.hpp" #include "../poison.hpp" diff --git a/src/map/grfio.hpp b/src/map/grfio.hpp index 3fde76a..e0758fe 100644 --- a/src/map/grfio.hpp +++ b/src/map/grfio.hpp @@ -1,11 +1,11 @@ -#ifndef GRFIO_HPP -#define GRFIO_HPP +#ifndef TMWA_MAP_GRFIO_HPP +#define TMWA_MAP_GRFIO_HPP # include # include -# include "../common/mmo.hpp" +# include "../mmo/mmo.hpp" bool load_resnametable(ZString filename); @@ -14,4 +14,4 @@ bool load_resnametable(ZString filename); /// Currently there is exactly one .wlk per .gat, but multiples are fine. std::vector grfio_reads(MapName resourcename); -#endif // GRFIO_HPP +#endif // TMWA_MAP_GRFIO_HPP diff --git a/src/map/intif.cpp b/src/map/intif.cpp index 5c6c46a..7f7be73 100644 --- a/src/map/intif.cpp +++ b/src/map/intif.cpp @@ -3,14 +3,15 @@ #include #include +#include "../compat/nullpo.hpp" + #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" #include "../io/cxxstdio.hpp" -#include "../common/nullpo.hpp" -#include "../common/socket.hpp" +#include "../mmo/socket.hpp" #include "battle.hpp" #include "chrif.hpp" diff --git a/src/map/intif.hpp b/src/map/intif.hpp index 0fe7182..ac2740c 100644 --- a/src/map/intif.hpp +++ b/src/map/intif.hpp @@ -1,9 +1,9 @@ -#ifndef INTIF_HPP -#define INTIF_HPP +#ifndef TMWA_MAP_INTIF_HPP +#define TMWA_MAP_INTIF_HPP # include "../strings/fwd.hpp" -# include "../common/const_array.hpp" +# include "../generic/const_array.hpp" # include "map.hpp" @@ -30,4 +30,4 @@ void intif_party_changemap(dumb_ptr sd, int online); void intif_party_message(int party_id, int account_id, XString mes); void intif_party_checkconflict(int party_id, int account_id, CharName nick); -#endif // INTIF_HPP +#endif // TMWA_MAP_INTIF_HPP diff --git a/src/map/itemdb.cpp b/src/map/itemdb.cpp index 330bd8b..528c81f 100644 --- a/src/map/itemdb.cpp +++ b/src/map/itemdb.cpp @@ -3,19 +3,21 @@ #include #include +#include "../compat/nullpo.hpp" + #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" +#include "../generic/db.hpp" +#include "../generic/random.hpp" + #include "../io/cxxstdio.hpp" #include "../io/read.hpp" -#include "../common/config_parse.hpp" -#include "../common/db.hpp" -#include "../common/extract.hpp" -#include "../common/nullpo.hpp" -#include "../common/random.hpp" -#include "../common/socket.hpp" +#include "../mmo/config_parse.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/socket.hpp" #include "../poison.hpp" diff --git a/src/map/itemdb.hpp b/src/map/itemdb.hpp index f026f95..4c07303 100644 --- a/src/map/itemdb.hpp +++ b/src/map/itemdb.hpp @@ -1,7 +1,7 @@ -#ifndef ITEMDB_HPP -#define ITEMDB_HPP +#ifndef TMWA_MAP_ITEMDB_HPP +#define TMWA_MAP_ITEMDB_HPP -# include "../common/mmo.hpp" +# include "../mmo/mmo.hpp" # include "map.t.hpp" # include "script.hpp" @@ -77,4 +77,4 @@ void itemdb_reload(void); void do_final_itemdb(void); bool itemdb_readdb(ZString filename); -#endif // ITEMDB_HPP +#endif // TMWA_MAP_ITEMDB_HPP diff --git a/src/map/magic-expr-eval.hpp b/src/map/magic-expr-eval.hpp index 1569be8..0783dc8 100644 --- a/src/map/magic-expr-eval.hpp +++ b/src/map/magic-expr-eval.hpp @@ -1,10 +1,8 @@ -#ifndef MAGIC_EXPR_EVAL_HPP -#define MAGIC_EXPR_EVAL_HPP +#ifndef TMWA_MAP_MAGIC_EXPR_EVAL_HPP +#define TMWA_MAP_MAGIC_EXPR_EVAL_HPP # include "../strings/zstring.hpp" -# include "../common/utils2.hpp" - # include "magic-interpreter.hpp" /* Helper definitions for dealing with functions and operations */ @@ -42,4 +40,4 @@ void magic_area_rect(map_local **m, int *x, int *y, int *width, int *height, # define ARG_MAY_BE_AREA(x) (ARG_TYPE(x) == TYPE::AREA || ARG_TYPE(x) == TYPE::LOCATION) -#endif // MAGIC_EXPR_EVAL_HPP +#endif // TMWA_MAP_MAGIC_EXPR_EVAL_HPP diff --git a/src/map/magic-expr.cpp b/src/map/magic-expr.cpp index 1f9ef82..279ba56 100644 --- a/src/map/magic-expr.cpp +++ b/src/map/magic-expr.cpp @@ -5,14 +5,16 @@ #include #include +#include "../compat/alg.hpp" + #include "../strings/mstring.hpp" #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/vstring.hpp" -#include "../io/cxxstdio.hpp" +#include "../generic/random.hpp" -#include "../common/random.hpp" +#include "../io/cxxstdio.hpp" #include "battle.hpp" #include "npc.hpp" diff --git a/src/map/magic-expr.hpp b/src/map/magic-expr.hpp index 0fdc435..79fdc3f 100644 --- a/src/map/magic-expr.hpp +++ b/src/map/magic-expr.hpp @@ -1,5 +1,5 @@ -#ifndef MAGIC_EXPR_HPP -#define MAGIC_EXPR_HPP +#ifndef TMWA_MAP_MAGIC_EXPR_HPP +#define TMWA_MAP_MAGIC_EXPR_HPP # include "magic-interpreter.hpp" @@ -84,4 +84,4 @@ int magic_find_item(const_array args, int index, struct item *item, int * int magic_location_in_area(map_local *m, int x, int y, dumb_ptr area); -#endif // MAGIC_EXPR_HPP +#endif // TMWA_MAP_MAGIC_EXPR_HPP diff --git a/src/map/magic-interpreter-aux.hpp b/src/map/magic-interpreter-aux.hpp index 070c771..6f5ad79 100644 --- a/src/map/magic-interpreter-aux.hpp +++ b/src/map/magic-interpreter-aux.hpp @@ -1,5 +1,5 @@ -#ifndef MAGIC_INTERPRETER_AUX_HPP -#define MAGIC_INTERPRETER_AUX_HPP +#ifndef TMWA_MAP_MAGIC_INTERPRETER_AUX_HPP +#define TMWA_MAP_MAGIC_INTERPRETER_AUX_HPP # include "magic-interpreter.t.hpp" @@ -9,4 +9,4 @@ bool CHECK_TYPE(T *v, TYPE t) return v->ty == t; } -#endif // MAGIC_INTERPRETER_AUX_HPP +#endif // TMWA_MAP_MAGIC_INTERPRETER_AUX_HPP diff --git a/src/map/magic-interpreter-base.cpp b/src/map/magic-interpreter-base.cpp index f1878a0..40f186b 100644 --- a/src/map/magic-interpreter-base.cpp +++ b/src/map/magic-interpreter-base.cpp @@ -8,7 +8,7 @@ #include "../io/cxxstdio.hpp" -#include "../common/timer.hpp" +#include "../mmo/timer.hpp" #include "magic-expr.hpp" diff --git a/src/map/magic-interpreter-parser.ypp b/src/map/magic-interpreter-parser.ypp index 3ec1bdd..ef8b159 100644 --- a/src/map/magic-interpreter-parser.ypp +++ b/src/map/magic-interpreter-parser.ypp @@ -17,9 +17,9 @@ AString current_magic_filename; #include "../strings/astring.hpp" #include "../strings/zstring.hpp" -#include "../io/cxxstdio.hpp" +#include "../generic/const_array.hpp" -#include "../common/const_array.hpp" +#include "../io/cxxstdio.hpp" #include "itemdb.hpp" #include "magic-expr.hpp" diff --git a/src/map/magic-interpreter.hpp b/src/map/magic-interpreter.hpp index 7e42499..6864844 100644 --- a/src/map/magic-interpreter.hpp +++ b/src/map/magic-interpreter.hpp @@ -1,5 +1,5 @@ -#ifndef MAGIC_INTERPRETER_HPP -#define MAGIC_INTERPRETER_HPP +#ifndef TMWA_MAP_MAGIC_INTERPRETER_HPP +#define TMWA_MAP_MAGIC_INTERPRETER_HPP # include "magic-interpreter.t.hpp" @@ -450,4 +450,4 @@ bool magic_init0(); bool magic_init1(ZString filename); void spell_update_location(dumb_ptr invocation); -#endif // MAGIC_INTERPRETER_HPP +#endif // TMWA_MAP_MAGIC_INTERPRETER_HPP diff --git a/src/map/magic-interpreter.t.hpp b/src/map/magic-interpreter.t.hpp index fb7bb6d..e8d7e06 100644 --- a/src/map/magic-interpreter.t.hpp +++ b/src/map/magic-interpreter.t.hpp @@ -1,7 +1,7 @@ -#ifndef MAGIC_INTERPRETER_T_HPP -#define MAGIC_INTERPRETER_T_HPP +#ifndef TMWA_MAP_MAGIC_INTERPRETER_T_HPP +#define TMWA_MAP_MAGIC_INTERPRETER_T_HPP -# include "../common/utils2.hpp" +# include "../generic/enum.hpp" enum class SPELLARG : uint8_t { @@ -156,4 +156,4 @@ ENUM_BITWISE_OPERATORS(INVOCATION_FLAG) } using e::INVOCATION_FLAG; -#endif // MAGIC_INTERPRETER_T_HPP +#endif // TMWA_MAP_MAGIC_INTERPRETER_T_HPP diff --git a/src/map/magic-stmt.cpp b/src/map/magic-stmt.cpp index a71f43c..4063a68 100644 --- a/src/map/magic-stmt.cpp +++ b/src/map/magic-stmt.cpp @@ -2,12 +2,16 @@ #include +#include "../compat/attr.hpp" +#include "../compat/fun.hpp" + #include "../strings/zstring.hpp" +#include "../generic/random2.hpp" + #include "../io/cxxstdio.hpp" -#include "../common/random2.hpp" -#include "../common/timer.hpp" +#include "../mmo/timer.hpp" #include "magic-expr.hpp" #include "magic-expr-eval.hpp" diff --git a/src/map/magic.hpp b/src/map/magic.hpp index 1fabd6f..1562469 100644 --- a/src/map/magic.hpp +++ b/src/map/magic.hpp @@ -1,9 +1,9 @@ -#ifndef MAGIC_HPP -#define MAGIC_HPP +#ifndef TMWA_MAP_MAGIC_HPP +#define TMWA_MAP_MAGIC_HPP # include "../strings/fwd.hpp" -# include "../common/dumb_ptr.hpp" +# include "../mmo/dumb_ptr.hpp" # include "map.hpp" # include "skill.t.hpp" @@ -80,4 +80,4 @@ int spell_attack(int caster, int target); void spell_free_invocation(dumb_ptr invocation); -#endif // MAGIC_HPP +#endif // TMWA_MAP_MAGIC_HPP diff --git a/src/map/main.cpp b/src/map/main.cpp index c930630..f0e3517 100644 --- a/src/map/main.cpp +++ b/src/map/main.cpp @@ -1,2 +1,4 @@ // dummy file to make Make dependencies work #include "map.hpp" + +#include "../poison.hpp" diff --git a/src/map/map.cpp b/src/map/map.cpp index 132afac..d9dd9cc 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -10,25 +10,28 @@ #include #include +#include "../compat/nullpo.hpp" +#include "../compat/fun.hpp" + #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" #include "../strings/vstring.hpp" +#include "../generic/db.hpp" +#include "../generic/random2.hpp" + #include "../io/cxxstdio.hpp" #include "../io/read.hpp" #include "../io/tty.hpp" #include "../io/write.hpp" -#include "../common/config_parse.hpp" -#include "../common/core.hpp" -#include "../common/db.hpp" -#include "../common/extract.hpp" -#include "../common/random2.hpp" -#include "../common/nullpo.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" -#include "../common/version.hpp" +#include "../mmo/config_parse.hpp" +#include "../mmo/core.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" +#include "../mmo/version.hpp" #include "atcommand.hpp" #include "battle.hpp" diff --git a/src/map/map.hpp b/src/map/map.hpp index 1964a3c..d476dbc 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -1,5 +1,5 @@ -#ifndef MAP_HPP -#define MAP_HPP +#ifndef TMWA_MAP_MAP_HPP +#define TMWA_MAP_MAP_HPP # include "map.t.hpp" @@ -13,12 +13,13 @@ # include "../strings/astring.hpp" # include "../strings/vstring.hpp" +# include "../generic/db.hpp" +# include "../generic/matrix.hpp" + # include "../io/cxxstdio.hpp" -# include "../common/db.hpp" -# include "../common/matrix.hpp" -# include "../common/socket.hpp" -# include "../common/timer.t.hpp" +# include "../mmo/socket.hpp" +# include "../mmo/timer.t.hpp" # include "battle.t.hpp" # include "magic-interpreter.t.hpp" @@ -714,4 +715,4 @@ inline dumb_ptr npc_data::is_shop() { return npc_subtype == NpcSu inline dumb_ptr npc_data::is_warp() { return npc_subtype == NpcSubtype::WARP ? as_warp() : nullptr ; } inline dumb_ptr npc_data::is_message() { return npc_subtype == NpcSubtype::MESSAGE ? as_message() : nullptr ; } -#endif // MAP_HPP +#endif // TMWA_MAP_MAP_HPP diff --git a/src/map/map.t.hpp b/src/map/map.t.hpp index f4d916a..4a41ea8 100644 --- a/src/map/map.t.hpp +++ b/src/map/map.t.hpp @@ -1,10 +1,9 @@ -#ifndef MAP_T_HPP -#define MAP_T_HPP +#ifndef TMWA_MAP_MAP_T_HPP +#define TMWA_MAP_MAP_T_HPP # include "../strings/vstring.hpp" -# include "../common/mmo.hpp" -# include "../common/utils2.hpp" +# include "../mmo/mmo.hpp" namespace e { @@ -562,4 +561,4 @@ struct NpcName : VString<23> {}; struct ScriptLabel : VString<23> {}; struct ItemName : VString<23> {}; -#endif // MAP_T_HPP +#endif // TMWA_MAP_MAP_T_HPP diff --git a/src/map/mapflag.hpp b/src/map/mapflag.hpp index bf8229f..f3819ff 100644 --- a/src/map/mapflag.hpp +++ b/src/map/mapflag.hpp @@ -21,7 +21,7 @@ # include "../sanity.hpp" -# include "../common/extract.hpp" // TODO remove this (requires specializing the *other* half) +# include "../mmo/extract.hpp" // TODO remove this (requires specializing the *other* half) # include "../strings/xstring.hpp" diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 5a414be..61dcfb6 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -8,18 +8,21 @@ #include +#include "../compat/fun.hpp" +#include "../compat/nullpo.hpp" + #include "../strings/astring.hpp" #include "../strings/xstring.hpp" +#include "../generic/random.hpp" + #include "../io/cxxstdio.hpp" #include "../io/read.hpp" -#include "../common/config_parse.hpp" -#include "../common/extract.hpp" -#include "../common/nullpo.hpp" -#include "../common/random.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" +#include "../mmo/config_parse.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" #include "battle.hpp" #include "clif.hpp" diff --git a/src/map/mob.hpp b/src/map/mob.hpp index bc8350a..305cd16 100644 --- a/src/map/mob.hpp +++ b/src/map/mob.hpp @@ -1,11 +1,12 @@ -#ifndef MOB_HPP -#define MOB_HPP +#ifndef TMWA_MAP_MOB_HPP +#define TMWA_MAP_MOB_HPP # include "mob.t.hpp" -# include "../common/mmo.hpp" -# include "../common/timer.t.hpp" -# include "../common/random.t.hpp" +# include "../generic/random.t.hpp" + +# include "../mmo/mmo.hpp" +# include "../mmo/timer.t.hpp" # include "clif.t.hpp" # include "map.hpp" @@ -108,4 +109,4 @@ int mob_summonslave(dumb_ptr md2, int *value, int amount, int flag); void mob_reload(void); -#endif // MOB_HPP +#endif // TMWA_MAP_MOB_HPP diff --git a/src/map/mob.t.hpp b/src/map/mob.t.hpp index 54c326d..c8b1e7a 100644 --- a/src/map/mob.t.hpp +++ b/src/map/mob.t.hpp @@ -1,5 +1,5 @@ -#ifndef MOB_T_HPP -#define MOB_T_HPP +#ifndef TMWA_MAP_MOB_T_HPP +#define TMWA_MAP_MOB_T_HPP # include @@ -38,4 +38,4 @@ enum class MobSkillState : uint8_t MSS_CHASE, }; -#endif // MOB_T_HPP +#endif // TMWA_MAP_MOB_T_HPP diff --git a/src/map/npc.cpp b/src/map/npc.cpp index 8ecd7a3..1e164a5 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -7,20 +7,23 @@ #include +#include "../compat/fun.hpp" +#include "../compat/nullpo.hpp" + #include "../strings/mstring.hpp" #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" +#include "../generic/db.hpp" + #include "../io/cxxstdio.hpp" #include "../io/read.hpp" -#include "../common/config_parse.hpp" -#include "../common/db.hpp" -#include "../common/extract.hpp" -#include "../common/nullpo.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" +#include "../mmo/config_parse.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" #include "battle.hpp" #include "clif.hpp" diff --git a/src/map/npc.hpp b/src/map/npc.hpp index 4d4cef1..93f2030 100644 --- a/src/map/npc.hpp +++ b/src/map/npc.hpp @@ -1,12 +1,12 @@ -#ifndef NPC_HPP -#define NPC_HPP +#ifndef TMWA_MAP_NPC_HPP +#define TMWA_MAP_NPC_HPP # include # include # include "../strings/fwd.hpp" -# include "../common/timer.t.hpp" +# include "../mmo/timer.t.hpp" # include "map.hpp" @@ -70,4 +70,4 @@ interval_t npc_gettimerevent_tick(dumb_ptr nd); void npc_settimerevent_tick(dumb_ptr nd, interval_t newtimer); int npc_delete(dumb_ptr nd); -#endif // NPC_HPP +#endif // TMWA_MAP_NPC_HPP diff --git a/src/map/party.cpp b/src/map/party.cpp index f7b8396..eadf392 100644 --- a/src/map/party.cpp +++ b/src/map/party.cpp @@ -2,14 +2,16 @@ #include +#include "../compat/nullpo.hpp" + #include "../strings/xstring.hpp" +#include "../generic/db.hpp" + #include "../io/cxxstdio.hpp" -#include "../common/db.hpp" -#include "../common/nullpo.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" #include "battle.hpp" #include "clif.hpp" diff --git a/src/map/party.hpp b/src/map/party.hpp index 91dab1b..c2d4e9d 100644 --- a/src/map/party.hpp +++ b/src/map/party.hpp @@ -1,5 +1,5 @@ -#ifndef PARTY_HPP -#define PARTY_HPP +#ifndef TMWA_MAP_PARTY_HPP +#define TMWA_MAP_PARTY_HPP # include @@ -48,4 +48,4 @@ int party_exp_share(struct party *p, map_local *map, int base_exp, int job_exp); void party_foreachsamemap(std::function)> func, dumb_ptr sd, int type); -#endif // PARTY_HPP +#endif // TMWA_MAP_PARTY_HPP diff --git a/src/map/path.cpp b/src/map/path.cpp index d66640e..a237fa8 100644 --- a/src/map/path.cpp +++ b/src/map/path.cpp @@ -2,10 +2,11 @@ #include -#include "../io/cxxstdio.hpp" +#include "../compat/nullpo.hpp" + +#include "../generic/random.hpp" -#include "../common/random.hpp" -#include "../common/nullpo.hpp" +#include "../io/cxxstdio.hpp" #include "battle.hpp" diff --git a/src/map/path.hpp b/src/map/path.hpp index 4b5b3d8..69316e9 100644 --- a/src/map/path.hpp +++ b/src/map/path.hpp @@ -1,8 +1,8 @@ -#ifndef PATH_HPP -#define PATH_HPP +#ifndef TMWA_MAP_PATH_HPP +#define TMWA_MAP_PATH_HPP # include "map.hpp" int path_search(struct walkpath_data *, map_local *, int, int, int, int, int); -#endif // PATH_HPP +#endif // TMWA_MAP_PATH_HPP diff --git a/src/map/pc.cpp b/src/map/pc.cpp index 86280ed..e3dd67a 100644 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -4,17 +4,21 @@ #include #include +#include "../compat/alg.hpp" +#include "../compat/fun.hpp" +#include "../compat/nullpo.hpp" + #include "../strings/rstring.hpp" #include "../strings/astring.hpp" #include "../strings/zstring.hpp" +#include "../generic/random.hpp" + #include "../io/cxxstdio.hpp" #include "../io/read.hpp" -#include "../common/nullpo.hpp" -#include "../common/random.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" #include "atcommand.hpp" #include "battle.hpp" diff --git a/src/map/pc.hpp b/src/map/pc.hpp index df612d8..0226019 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -1,5 +1,5 @@ -#ifndef PC_HPP -#define PC_HPP +#ifndef TMWA_MAP_PC_HPP +#define TMWA_MAP_PC_HPP # include "pc.t.hpp" @@ -155,4 +155,4 @@ void pc_show_motd(dumb_ptr sd); void do_init_pc(void); -#endif // PC_HPP +#endif // TMWA_MAP_PC_HPP diff --git a/src/map/pc.t.hpp b/src/map/pc.t.hpp index 95a76a7..29c3515 100644 --- a/src/map/pc.t.hpp +++ b/src/map/pc.t.hpp @@ -1,5 +1,5 @@ -#ifndef PC_T_HPP -#define PC_T_HPP +#ifndef TMWA_MAP_PC_T_HPP +#define TMWA_MAP_PC_T_HPP # include @@ -41,4 +41,4 @@ enum class PickupFail : uint8_t DROP_STEAL = 6, }; -#endif // PC_T_HPP +#endif // TMWA_MAP_PC_T_HPP diff --git a/src/map/script.cpp b/src/map/script.cpp index e3db7c7..fe42e44 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -7,25 +7,28 @@ #include #include +#include "../compat/fun.hpp" + #include "../strings/mstring.hpp" #include "../strings/rstring.hpp" #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" +#include "../generic/db.hpp" +#include "../generic/intern-pool.hpp" +#include "../generic/random.hpp" + #include "../io/cxxstdio.hpp" #include "../io/lock.hpp" #include "../io/read.hpp" -#include "../common/config_parse.hpp" -#include "../common/core.hpp" -#include "../common/db.hpp" -#include "../common/extract.hpp" -#include "../common/intern-pool.hpp" -#include "../common/random.hpp" -#include "../common/socket.hpp" -#include "../common/utils.hpp" -#include "../common/timer.hpp" +#include "../mmo/config_parse.hpp" +#include "../mmo/core.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/utils.hpp" +#include "../mmo/timer.hpp" #include "atcommand.hpp" #include "battle.hpp" diff --git a/src/map/script.hpp b/src/map/script.hpp index 91ee6a5..6cf40b0 100644 --- a/src/map/script.hpp +++ b/src/map/script.hpp @@ -1,5 +1,5 @@ -#ifndef SCRIPT_HPP -#define SCRIPT_HPP +#ifndef TMWA_MAP_SCRIPT_HPP +#define TMWA_MAP_SCRIPT_HPP # include # include // for inlined get_str - TODO remove @@ -10,9 +10,10 @@ # include "../strings/astring.hpp" # include "../strings/zstring.hpp" -# include "../common/db.hpp" -# include "../common/dumb_ptr.hpp" -# include "../common/utils.hpp" +# include "../generic/db.hpp" + +# include "../mmo/dumb_ptr.hpp" +# include "../mmo/utils.hpp" # include "map.t.hpp" @@ -175,4 +176,4 @@ void set_script_var_s(dumb_ptr sd, VarName var, int e, XString int get_script_var_i(dumb_ptr sd, VarName var, int e); ZString get_script_var_s(dumb_ptr sd, VarName var, int e); -#endif // SCRIPT_HPP +#endif // TMWA_MAP_SCRIPT_HPP diff --git a/src/map/script.py b/src/map/script.py index ba0198c..068307a 100644 --- a/src/map/script.py +++ b/src/map/script.py @@ -54,3 +54,6 @@ class script_data(object): yield 'str', v['str'] else: yield 'numi', v['numi'] + + def to_string(self): + return None diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 077bf66..72d7f7a 100644 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -5,18 +5,22 @@ #include #include +#include "../compat/attr.hpp" +#include "../compat/fun.hpp" +#include "../compat/nullpo.hpp" + #include "../strings/mstring.hpp" #include "../strings/rstring.hpp" #include "../strings/xstring.hpp" +#include "../generic/random.hpp" + #include "../io/cxxstdio.hpp" #include "../io/read.hpp" -#include "../common/extract.hpp" -#include "../common/nullpo.hpp" -#include "../common/random.hpp" -#include "../common/socket.hpp" -#include "../common/timer.hpp" +#include "../mmo/extract.hpp" +#include "../mmo/socket.hpp" +#include "../mmo/timer.hpp" #include "battle.hpp" #include "clif.hpp" diff --git a/src/map/skill.hpp b/src/map/skill.hpp index e8f36ab..b612268 100644 --- a/src/map/skill.hpp +++ b/src/map/skill.hpp @@ -1,5 +1,5 @@ -#ifndef SKILL_HPP -#define SKILL_HPP +#ifndef TMWA_MAP_SKILL_HPP +#define TMWA_MAP_SKILL_HPP # include "skill.t.hpp" # include "skill-pools.hpp" @@ -143,4 +143,4 @@ int skill_power_bl(dumb_ptr bl, SkillID skill); // [Fate] Remember that a certain skill ID belongs to a pool skill void skill_pool_register(SkillID id); -#endif // SKILL_HPP +#endif // TMWA_MAP_SKILL_HPP diff --git a/src/map/skill.t.hpp b/src/map/skill.t.hpp index 1470d61..aedbb11 100644 --- a/src/map/skill.t.hpp +++ b/src/map/skill.t.hpp @@ -1,9 +1,9 @@ -#ifndef SKILL_T_HPP -#define SKILL_T_HPP +#ifndef TMWA_MAP_SKILL_T_HPP +#define TMWA_MAP_SKILL_T_HPP # include -# include "../common/utils2.hpp" +# include "../generic/enum.hpp" // TODO remove most of these as their corresponding SkillIDs get deleted. enum class StatusChange : uint16_t @@ -110,4 +110,4 @@ ENUM_BITWISE_OPERATORS(SkillFlags) } using e::SkillFlags; -#endif // SKILL_T_HPP +#endif // TMWA_MAP_SKILL_T_HPP diff --git a/src/map/storage.cpp b/src/map/storage.cpp index b753c0d..89357b9 100644 --- a/src/map/storage.cpp +++ b/src/map/storage.cpp @@ -1,13 +1,13 @@ +#include "storage.hpp" // Copyright (c) Athena Dev Teams - Licensed under GNU GPL // For more information, see COPYING in the main folder -#include "storage.hpp" - #include #include -#include "../common/db.hpp" -#include "../common/nullpo.hpp" +#include "../compat/nullpo.hpp" + +#include "../generic/db.hpp" #include "chrif.hpp" #include "clif.hpp" diff --git a/src/map/storage.hpp b/src/map/storage.hpp index 76be7d6..0e6d111 100644 --- a/src/map/storage.hpp +++ b/src/map/storage.hpp @@ -1,9 +1,9 @@ +#ifndef TMWA_MAP_STORAGE_HPP +#define TMWA_MAP_STORAGE_HPP + // Copyright (c) Athena Dev Teams - Licensed under GNU GPL // For more information, see COPYING in the main folder -#ifndef STORAGE_HPP -#define STORAGE_HPP - # include "map.hpp" int storage_storageopen(dumb_ptr sd); @@ -17,4 +17,4 @@ int storage_storage_quit(dumb_ptr sd); int storage_storage_save(int account_id, int final); int storage_storage_saved(int account_id); -#endif // STORAGE_HPP +#endif // TMWA_MAP_STORAGE_HPP diff --git a/src/map/tmw.cpp b/src/map/tmw.cpp index 16276fa..1c18bd5 100644 --- a/src/map/tmw.cpp +++ b/src/map/tmw.cpp @@ -3,14 +3,14 @@ #include #include +#include "../compat/nullpo.hpp" + #include "../strings/astring.hpp" #include "../strings/zstring.hpp" #include "../strings/xstring.hpp" #include "../io/cxxstdio.hpp" -#include "../common/nullpo.hpp" - #include "atcommand.hpp" #include "battle.hpp" #include "chrif.hpp" diff --git a/src/map/tmw.hpp b/src/map/tmw.hpp index e90143d..65e71fb 100644 --- a/src/map/tmw.hpp +++ b/src/map/tmw.hpp @@ -1,14 +1,15 @@ -#ifndef TMW_HPP -#define TMW_HPP +#ifndef TMWA_MAP_TMW_HPP +#define TMWA_MAP_TMW_HPP # include "../strings/fwd.hpp" -# include "../common/const_array.hpp" -# include "../common/dumb_ptr.hpp" +# include "../generic/const_array.hpp" + +# include "../mmo/dumb_ptr.hpp" # include "map.hpp" int tmw_CheckChatSpam(dumb_ptr sd, XString message); void tmw_GmHackMsg(ZString line); -#endif // TMW_HPP +#endif // TMWA_MAP_TMW_HPP diff --git a/src/map/trade.cpp b/src/map/trade.cpp index 9626191..e9c5fd4 100644 --- a/src/map/trade.cpp +++ b/src/map/trade.cpp @@ -1,8 +1,8 @@ #include "trade.hpp" -#include "../io/cxxstdio.hpp" +#include "../compat/nullpo.hpp" -#include "../common/nullpo.hpp" +#include "../io/cxxstdio.hpp" #include "battle.hpp" #include "clif.hpp" diff --git a/src/map/trade.hpp b/src/map/trade.hpp index e50d608..1fb30f4 100644 --- a/src/map/trade.hpp +++ b/src/map/trade.hpp @@ -1,5 +1,5 @@ -#ifndef TRADE_HPP -#define TRADE_HPP +#ifndef TMWA_MAP_TRADE_HPP +#define TMWA_MAP_TRADE_HPP # include "map.hpp" @@ -11,4 +11,4 @@ void trade_tradecancel(dumb_ptr sd); void trade_tradecommit(dumb_ptr sd); void trade_verifyzeny(dumb_ptr sd); -#endif // TRADE_HPP +#endif // TMWA_MAP_TRADE_HPP diff --git a/src/mmo/config_parse.cpp b/src/mmo/config_parse.cpp new file mode 100644 index 0000000..d677d8e --- /dev/null +++ b/src/mmo/config_parse.cpp @@ -0,0 +1,154 @@ +#include "config_parse.hpp" +// config_parse.hpp - Framework for per-server config parsers. +// +// Copyright © 2014 Ben Longbons +// +// 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 . + +#include "../strings/xstring.hpp" +#include "../strings/zstring.hpp" + +#include "../io/cxxstdio.hpp" +#include "../io/line.hpp" + +#include "version.hpp" + +#include "../poison.hpp" + +bool is_comment(XString line) +{ + return not line or line.startswith("//"); +} + +template +inline +bool config_split_impl(ZS line, XString *key, ZS *value) +{ + // unconditionally fail if line contains control characters + if (std::find_if(line.begin(), line.end(), + [](unsigned char c) { return c < ' '; } + ) != line.end()) + return false; + // try to find a colon, fail if not found + typename ZS::iterator colon = std::find(line.begin(), line.end(), ':'); + if (colon == line.end()) + return false; + + *key = line.xislice_h(colon).strip(); + // move past the colon and any spaces + ++colon; + *value = line.xislice_t(colon).lstrip(); + return true; +} + +// eventually this should go away +bool config_split(ZString line, XString *key, ZString *value) +{ + return config_split_impl(line, key, value); +} +// and use this instead +bool config_split(XString line, XString *key, XString *value) +{ + return config_split_impl(line, key, 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) +{ + io::LineReader in(filename); + if (!in.is_open()) + { + PRINTF("Unable to open file: %s\n", filename); + return false; + } + io::Line line; + bool rv = true; + while (in.read_line(line)) + { + if (is_comment(line.text)) + continue; + XString key; + ZString value; + if (!config_split(line.text, &key, &value)) + { + line.error("Bad config line"); + rv = false; + continue; + } + if (key == "import") + { + rv &= load_config_file(value, slave); + continue; + } + else if (key == "version-lt") + { + Version vers; + if (!extract(value, &vers)) + { + rv = false; + continue; + } + if (CURRENT_VERSION < vers) + continue; + break; + } + else if (key == "version-le") + { + Version vers; + if (!extract(value, &vers)) + { + rv = false; + continue; + } + if (CURRENT_VERSION <= vers) + continue; + break; + } + else if (key == "version-gt") + { + Version vers; + if (!extract(value, &vers)) + { + rv = false; + continue; + } + if (CURRENT_VERSION > vers) + continue; + break; + } + else if (key == "version-ge") + { + Version vers; + if (!extract(value, &vers)) + { + rv = false; + continue; + } + if (CURRENT_VERSION >= vers) + continue; + break; + } + else if (!slave(key, value)) + { + line.error("Bad config key or value"); + rv = false; + continue; + } + // nothing to see here, move along + } + return rv; +} diff --git a/src/mmo/config_parse.hpp b/src/mmo/config_parse.hpp new file mode 100644 index 0000000..dd1b79e --- /dev/null +++ b/src/mmo/config_parse.hpp @@ -0,0 +1,36 @@ +#ifndef TMWA_MMO_CONFIG_PARSE_HPP +#define TMWA_MMO_CONFIG_PARSE_HPP +// config_parse.hpp - Framework for per-server config parsers. +// +// Copyright © 2014 Ben Longbons +// +// 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 . + +# include "../sanity.hpp" + +# include "../strings/fwd.hpp" + +typedef bool (*ConfigItemParser)(XString key, ZString value); + +bool is_comment(XString line); +bool config_split(ZString line, XString *key, ZString *value); +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 diff --git a/src/mmo/core.cpp b/src/mmo/core.cpp new file mode 100644 index 0000000..1a9a52e --- /dev/null +++ b/src/mmo/core.cpp @@ -0,0 +1,125 @@ +#include "core.hpp" + +#include + +#include + +#include +#include +#include + +#include "../strings/zstring.hpp" + +#include "../generic/random.hpp" + +#include "../io/cxxstdio.hpp" + +#include "socket.hpp" +#include "timer.hpp" + +#include "../poison.hpp" + +// Added by Gabuzomeu +// +// This is an implementation of signal() using sigaction() for portability. +// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced +// Programming in the UNIX Environment_. +// +typedef void(*sigfunc)(int); +static +sigfunc compat_signal(int signo, sigfunc func) +{ + struct sigaction sact, oact; + + sact.sa_handler = func; + sigfillset(&sact.sa_mask); + sigdelset(&sact.sa_mask, SIGSEGV); + sigdelset(&sact.sa_mask, SIGBUS); + sigdelset(&sact.sa_mask, SIGTRAP); + sigdelset(&sact.sa_mask, SIGILL); + sigdelset(&sact.sa_mask, SIGFPE); + sact.sa_flags = 0; + + if (sigaction(signo, &sact, &oact) < 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" + return SIG_ERR; +#pragma GCC diagnostic pop + + return oact.sa_handler; +} + +volatile +bool runflag = true; + +static +void chld_proc(int) +{ + wait(NULL); +} +static +void sig_proc(int) +{ + for (int i = 1; i < 31; ++i) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" + compat_signal(i, SIG_IGN); +#pragma GCC diagnostic pop + runflag = false; +} + +/* + Note about fatal signals: + + Under certain circumstances, + the following signals MUST not be ignored: + SIGFPE, SIGSEGV, SIGILL + Unless you use SA_SIGINFO and *carefully* check the origin, + that means they must be SIG_DFL. + */ +int main(int argc, char **argv) +{ + // ZString args[argc]; is (deliberately!) not supported by clang yet + ZString *args = static_cast(alloca(argc * sizeof(ZString))); + for (int i = 0; i < argc; ++i) + args[i] = ZString(strings::really_construct_from_a_pointer, argv[i], nullptr); + do_init(argc, args); + + if (!runflag) + { + PRINTF("Fatal error during startup; exiting\n"); + 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" + compat_signal(SIGPIPE, SIG_IGN); +#pragma GCC diagnostic 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" + 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 + + atexit(term_func); + + while (runflag) + { + // TODO - if timers take a long time to run, this + // may wait too long in sendrecv + tick_t now = milli_clock::now(); + interval_t next = do_timer(now); + do_sendrecv(next); + do_parsepacket(); + } +} diff --git a/src/mmo/core.hpp b/src/mmo/core.hpp new file mode 100644 index 0000000..1788ece --- /dev/null +++ b/src/mmo/core.hpp @@ -0,0 +1,22 @@ +#ifndef TMWA_MMO_CORE_HPP +#define TMWA_MMO_CORE_HPP + +# include "../sanity.hpp" + +# include "../strings/fwd.hpp" + +/// core.c contains a server-independent main() function +/// and then runs a do_sendrecv loop + +/// When this is cleared, the server exits gracefully. +extern volatile bool runflag; + +/// This is an external function defined by each server +/// This function must register stuff for the parse loop +extern int do_init(int, 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 diff --git a/src/mmo/dumb_ptr.cpp b/src/mmo/dumb_ptr.cpp new file mode 100644 index 0000000..5f73d27 --- /dev/null +++ b/src/mmo/dumb_ptr.cpp @@ -0,0 +1,3 @@ +#include "dumb_ptr.hpp" + +#include "../poison.hpp" diff --git a/src/mmo/dumb_ptr.hpp b/src/mmo/dumb_ptr.hpp new file mode 100644 index 0000000..98c6308 --- /dev/null +++ b/src/mmo/dumb_ptr.hpp @@ -0,0 +1,276 @@ +#ifndef TMWA_MMO_DUMB_PTR_HPP +#define TMWA_MMO_DUMB_PTR_HPP +// ptr.hpp - temporary new/delete wrappers +// +// Copyright © 2013 Ben Longbons +// +// 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 . + +# include "../sanity.hpp" + +# include + +# include + +# include "../strings/astring.hpp" +# include "../strings/zstring.hpp" +# include "../strings/xstring.hpp" + +# include "../generic/const_array.hpp" + +// unmanaged new/delete-able pointer +// should be replaced by std::unique_ptr +template +class dumb_ptr +{ + template + friend class dumb_ptr; + T *impl; +public: + explicit + dumb_ptr(T *p=nullptr) + : impl(p) + {} + template + dumb_ptr(dumb_ptr p) + : impl(p.impl) + {} + dumb_ptr(std::nullptr_t) + : impl(nullptr) + {} + + void delete_() + { + delete impl; + *this = nullptr; + } + template + void new_(A&&... a) + { + impl = new T(std::forward(a)...); + } + template + static + dumb_ptr make(A&&... a) + { + return dumb_ptr(new T(std::forward(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 or std::vector +template +class dumb_ptr +{ + 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 make(size_t z) + { + return dumb_ptr(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 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(const_cast(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/extract.cpp b/src/mmo/extract.cpp new file mode 100644 index 0000000..378986d --- /dev/null +++ b/src/mmo/extract.cpp @@ -0,0 +1,62 @@ +#include "extract.hpp" +// extract.cpp - a simple, hierarchical, tokenizer +// +// Copyright © 2013 Ben Longbons +// +// 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 . + +#include "../strings/astring.hpp" +#include "../strings/xstring.hpp" +#include "../strings/vstring.hpp" + +#include "../poison.hpp" + +bool extract(XString str, XString *rv) +{ + *rv = str; + return true; +} + +bool extract(XString str, AString *rv) +{ + *rv = str; + return true; +} + +bool extract(XString str, struct global_reg *var) +{ + return extract(str, + record<','>(&var->str, &var->value)); +} + +bool extract(XString str, struct item *it) +{ + XString ignored; + return extract(str, + record<',', 11>( + &it->id, + &it->nameid, + &it->amount, + &it->equip, + &ignored, + &ignored, + &ignored, + &ignored, + &ignored, + &ignored, + &ignored, + &ignored)); +} diff --git a/src/mmo/extract.hpp b/src/mmo/extract.hpp new file mode 100644 index 0000000..0ea9eb9 --- /dev/null +++ b/src/mmo/extract.hpp @@ -0,0 +1,224 @@ +#ifndef TMWA_MMO_EXTRACT_HPP +#define TMWA_MMO_EXTRACT_HPP +// extract.hpp - a simple, hierarchical, tokenizer +// +// Copyright © 2012-2013 Ben Longbons +// +// 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 . + +# include "../sanity.hpp" + +# include + +# include "../strings/xstring.hpp" + +# include "../generic/const_array.hpp" + +# include "mmo.hpp" +# include "utils.hpp" + +template::value && !std::is_same::value && !std::is_same::value>::type> +bool extract(XString str, T *iv) +{ + if (!str || str.size() > 20) + return false; + if (!((str.front() == '-' && std::is_signed::value) + || ('0' <= str.front() && str.front() <= '9'))) + return false; + // needs a NUL, but can't always be given one. TODO VString? + char buf[20 + 1]; + std::copy(str.begin(), str.end(), buf); + buf[str.size()] = '\0'; + + char *end; + errno = 0; + if (std::is_signed::value) + { + long long v = strtoll(buf, &end, 10); + if (errno || *end) + return false; + *iv = v; + return *iv == v; + } + else + { + unsigned long long v = strtoull(buf, &end, 10); + if (errno || *end) + return false; + *iv = v; + return *iv == v; + } +} + +inline +bool extract(XString str, TimeT *tv) +{ + return extract(str, &tv->value); +} + +// extra typename=void to workaround some duplicate overload rule +template::value>::type, typename=void> +bool extract(XString str, T *iv) +{ + typedef typename underlying_type::type U; + U v; + // defer to integer version + if (!extract(str, &v)) + return false; + // TODO check bounds using enum min/max as in SSCANF + *iv = static_cast(v); + return true; +} + +bool extract(XString str, XString *rv); + +bool extract(XString str, AString *rv); + +template +bool extract(XString str, VString *out) +{ + if (str.size() > N) + return false; + *out = str; + return true; +} + +template +class LStripper +{ +public: + T impl; +}; + +template +LStripper lstripping(T v) +{ + return {v}; +} + +template +bool extract(XString str, LStripper out) +{ + return extract(str.lstrip(), out.impl); +} + +// basically just a std::tuple +// but it provides its data members publically +template +class Record; +template +class Record +{ +}; +template +class Record +{ +public: + F frist; + Record rest; +public: + Record(F f, R... r) + : frist(f), rest(r...) + {} +}; +template +Record record(T... t) +{ + return Record(t...); +} +template +Record record(T... t) +{ + static_assert(0 < n && n < sizeof...(T), "don't be silly"); + return Record(t...); +} + +template +bool extract(XString str, Record) +{ + return !str; +} + +template +bool extract(XString str, Record rec) +{ + XString::iterator s = std::find(str.begin(), str.end(), split); + XString::iterator s2 = s; + if (s2 != str.end()) + ++s2; + XString head = str.xislice_h(s); + XString tail = str.xislice_t(s2); + if (s == str.end()) + return (extract(head, rec.frist) && n <= 1) + || (!head && n <= 0); + + return (extract(head, rec.frist) || n <= 0) + && extract(tail, rec.rest); +} + +template +struct VRecord +{ + std::vector *arr; +}; + +template +VRecord vrec(std::vector *arr) +{ + return {arr}; +} + +template +bool extract(XString str, VRecord rec) +{ + if (!str) + return true; + XString::iterator s = std::find(str.begin(), str.end(), split); + rec.arr->emplace_back(); + if (s == str.end()) + return extract(str, &rec.arr->back()); + return extract(str.xislice_h(s), &rec.arr->back()) + && extract(str.xislice_t(s + 1), rec); +} + +bool extract(XString str, struct global_reg *var); + +bool extract(XString str, struct 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; +} + +inline +bool extract(XString str, CharName *out) +{ + VString<23> tmp; + if (extract(str, &tmp)) + { + *out = CharName(tmp); + return true; + } + return false; +} + +#endif // TMWA_MMO_EXTRACT_HPP diff --git a/src/mmo/extract_test.cpp b/src/mmo/extract_test.cpp new file mode 100644 index 0000000..3d0610e --- /dev/null +++ b/src/mmo/extract_test.cpp @@ -0,0 +1,334 @@ +#include "extract.hpp" + +#include + +#include "../strings/xstring.hpp" + +TEST(extract, record_int) +{ + int x, y, z; + x = y = z = 0; + 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 = 0; + 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 = 0; + 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 = 0; + 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 = 0; + EXPECT_FALSE(extract("1 2 ", 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_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract("1 ", 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_EQ(1, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract(" ", 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_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_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_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_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_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_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_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_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_EQ(1, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract(" ", 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_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_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_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_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_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_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_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_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_EQ(1, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; + EXPECT_FALSE(extract(" ", 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_EQ(0, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0, z); + x = y = z = 0; +} + +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 = ""; + + 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 ", 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 = ""; +} + +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"); +} diff --git a/src/mmo/human_time_diff.cpp b/src/mmo/human_time_diff.cpp new file mode 100644 index 0000000..93a6d52 --- /dev/null +++ b/src/mmo/human_time_diff.cpp @@ -0,0 +1,3 @@ +#include "human_time_diff.hpp" + +#include "../poison.hpp" diff --git a/src/mmo/human_time_diff.hpp b/src/mmo/human_time_diff.hpp new file mode 100644 index 0000000..689b8d9 --- /dev/null +++ b/src/mmo/human_time_diff.hpp @@ -0,0 +1,86 @@ +#ifndef TMWA_MMO_HUMAN_TIME_DIFF_HPP +#define TMWA_MMO_HUMAN_TIME_DIFF_HPP +// human_time_diff.hpp - broken deltas +// +// Copyright © 2013 Ben Longbons +// +// 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 . + +# include "../sanity.hpp" + +# include "../strings/xstring.hpp" + +# include "extract.hpp" + +struct HumanTimeDiff +{ + short year, month, day, hour, minute, second; + + explicit + operator bool() + { + return year || month || day || hour || minute || second; + } + + bool operator !() + { + return !bool(*this); + } +}; + +inline +bool extract(XString str, HumanTimeDiff *iv) +{ + // str is a sequence of [-+]?[0-9]+([ay]|m|[jd]|h|mn|s) + // there are NO spaces here + // parse by counting the number starts + auto is_num = [](char c) + { return c == '-' || c == '+' || ('0' <= c && c <= '9'); }; + if (!str || !is_num(str.front())) + return false; + *iv = HumanTimeDiff{}; + while (str) + { + auto it = std::find_if_not(str.begin(), str.end(), is_num); + auto it2 = std::find_if(it, str.end(), is_num); + XString number = str.xislice_h(it); + XString suffix = str.xislice(it, it2); + str = str.xislice_t(it2); + + short *ptr = nullptr; + if (suffix == "y" || suffix == "a") + ptr = &iv->year; + else if (suffix == "m") + ptr = &iv->month; + else if (suffix == "j" || suffix == "d") + ptr = &iv->day; + else if (suffix == "h") + ptr = &iv->hour; + else if (suffix == "mn") + ptr = &iv->minute; + else if (suffix == "s") + ptr = &iv->second; + else + return false; + if (number.startswith('+') && !number.startswith("+-")) + number = number.xslice_t(1); + if (*ptr || !extract(number, ptr)) + return false; + } + return true; +} + +#endif // TMWA_MMO_HUMAN_TIME_DIFF_HPP diff --git a/src/mmo/human_time_diff_test.cpp b/src/mmo/human_time_diff_test.cpp new file mode 100644 index 0000000..d3ddad1 --- /dev/null +++ b/src/mmo/human_time_diff_test.cpp @@ -0,0 +1,83 @@ +#include "human_time_diff.hpp" + +#include + +// a sequence of [-+]?[0-9]+([ay]|m|[jd]|h|mn|s) + +TEST(humantimediff, single) +{ + HumanTimeDiff diff; + + EXPECT_TRUE(extract("42y", &diff)); + EXPECT_EQ(42, diff.year); + EXPECT_EQ(0, diff.month); + EXPECT_EQ(0, diff.day); + EXPECT_EQ(0, diff.hour); + EXPECT_EQ(0, diff.minute); + EXPECT_EQ(0, diff.second); + + EXPECT_TRUE(extract("42m", &diff)); + EXPECT_EQ(0, diff.year); + EXPECT_EQ(42, diff.month); + EXPECT_EQ(0, diff.day); + EXPECT_EQ(0, diff.hour); + EXPECT_EQ(0, diff.minute); + EXPECT_EQ(0, diff.second); + + EXPECT_TRUE(extract("42d", &diff)); + EXPECT_EQ(0, diff.year); + EXPECT_EQ(0, diff.month); + EXPECT_EQ(42, diff.day); + EXPECT_EQ(0, diff.hour); + EXPECT_EQ(0, diff.minute); + EXPECT_EQ(0, diff.second); + + EXPECT_TRUE(extract("42h", &diff)); + EXPECT_EQ(0, diff.year); + EXPECT_EQ(0, diff.month); + EXPECT_EQ(0, diff.day); + EXPECT_EQ(42, diff.hour); + EXPECT_EQ(0, diff.minute); + EXPECT_EQ(0, diff.second); + + EXPECT_TRUE(extract("42mn", &diff)); + EXPECT_EQ(0, diff.year); + EXPECT_EQ(0, diff.month); + EXPECT_EQ(0, diff.day); + EXPECT_EQ(0, diff.hour); + EXPECT_EQ(42, diff.minute); + EXPECT_EQ(0, diff.second); + + EXPECT_TRUE(extract("42s", &diff)); + EXPECT_EQ(0, diff.year); + EXPECT_EQ(0, diff.month); + EXPECT_EQ(0, diff.day); + EXPECT_EQ(0, diff.hour); + EXPECT_EQ(0, diff.minute); + EXPECT_EQ(42, diff.second); + + EXPECT_TRUE(extract("+42y", &diff)); + EXPECT_EQ(42, diff.year); + EXPECT_TRUE(extract("-42y", &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)); +} + +TEST(humantimediff, multiple) +{ + HumanTimeDiff diff; + + EXPECT_TRUE(extract("42y23m-2d", &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)); +} diff --git a/src/mmo/ip.cpp b/src/mmo/ip.cpp new file mode 100644 index 0000000..146734a --- /dev/null +++ b/src/mmo/ip.cpp @@ -0,0 +1,114 @@ +#include "ip.hpp" +// ip.cpp - Implementation of IP address functions. +// +// Copyright © 2013 Ben Longbons +// +// 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 . + +#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(s >> 24), + static_cast(s >> 16), + static_cast(s >> 8), + static_cast(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 new file mode 100644 index 0000000..a425710 --- /dev/null +++ b/src/mmo/ip.hpp @@ -0,0 +1,164 @@ +#ifndef TMWA_MMO_IP_HPP +#define TMWA_MMO_IP_HPP +// ip.hpp - classes to deal with IP addresses. +// +// Copyright © 2013 Ben Longbons +// +// 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 . + +# include "../sanity.hpp" + +# include + +# 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 +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(addr)); + } + explicit + operator struct in_addr() const + { + return reinterpret_cast(_addr); + } + + constexpr friend + IP4Address operator & (IP4Address l, IP4Address r) + { + return IP4Address({ + static_cast(l._addr[0] & r._addr[0]), + static_cast(l._addr[1] & r._addr[1]), + static_cast(l._addr[2] & r._addr[2]), + static_cast(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 new file mode 100644 index 0000000..e6a8183 --- /dev/null +++ b/src/mmo/ip.py @@ -0,0 +1,14 @@ +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 new file mode 100644 index 0000000..7ef1047 --- /dev/null +++ b/src/mmo/ip_test.cpp @@ -0,0 +1,332 @@ +#include "ip.hpp" + +#include + +#include "../io/cxxstdio.hpp" + +#define CB(X) (std::integral_constant::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 new file mode 100644 index 0000000..51ff5c4 --- /dev/null +++ b/src/mmo/md5more.cpp @@ -0,0 +1,128 @@ +#include "md5more.hpp" + +#include "../compat/rawmem.hpp" + +#include "../generic/random.hpp" + +#include "../io/cxxstdio.hpp" + +#include "../poison.hpp" + +#define X block.data + +// TODO - refactor MD5 into a stream, and merge the implementations +// I once implemented an ostream that does it ... +MD5_state MD5_from_FILE(io::ReadFile& in) +{ + uint64_t total_len = 0; + + uint8_t buf[0x40]; + uint8_t block_len = 0; + + MD5_state state; + MD5_init(&state); + + MD5_block block; + + while (true) + { + size_t rv = in.get(sign_cast(buf + block_len), 0x40 - block_len); + if (!rv) + break; + total_len += 8 * rv; // in bits + block_len += rv; + if (block_len != 0x40) + continue; + for (int i = 0; i < 0x10; i++) + X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; + MD5_do_block(&state, block); + block_len = 0; + } + // no more input, just pad and append the length + buf[block_len] = 0x80; + really_memset0(buf + block_len + 1, 0x40 - block_len - 1); + if (block_len < 0x38) + { + for (int i = 0; i < 8; i++) + buf[0x38 + i] = total_len >> i * 8; + } + for (int i = 0; i < 0x10; i++) + X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; + MD5_do_block(&state, block); + if (0x38 <= block_len) + { + really_memset0(buf, 0x38); + for (int i = 0; i < 8; i++) + buf[0x38 + i] = total_len >> i * 8; + for (int i = 0; i < 0x10; i++) + X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24; + MD5_do_block(&state, block); + } + return state; +} + + +// Hash a password with a salt. +// Whoever wrote this FAILS programming +AccountCrypt MD5_saltcrypt(AccountPass key, SaltString salt) +{ + char cbuf[64] {}; + + // hash the key then the salt + // buf ends up as a 64-char NUL-terminated string + md5_string tbuf, tbuf2; + MD5_to_str(MD5_from_string(key), tbuf); + MD5_to_str(MD5_from_string(salt), tbuf2); + const auto it = std::copy(tbuf.begin(), tbuf.end(), std::begin(cbuf)); + auto it2 = std::copy(tbuf2.begin(), tbuf2.end(), it); + assert(it2 == std::end(cbuf)); + + md5_string tbuf3; + MD5_to_str(MD5_from_string(XString(std::begin(cbuf), it2, nullptr)), tbuf3); + + VString<31> obuf; + + // This truncates the string, but we have to keep it like that for compatibility + SNPRINTF(obuf, 32, "!%s$%s", salt, tbuf3); + return stringish(obuf); +} + +SaltString make_salt(void) +{ + char salt[5]; + for (int i = 0; i < 5; i++) + // 126 would probably actually be okay + salt[i] = random_::in(48, 125); + return stringish(XString(salt + 0, salt + 5, nullptr)); +} + +bool pass_ok(AccountPass password, AccountCrypt crypted) +{ + // crypted is like !salt$hash + auto begin = crypted.begin() + 1; + auto end = std::find(begin, crypted.end(), '$'); + SaltString salt = stringish(crypted.xislice(begin, end)); + + return crypted == MD5_saltcrypt(password, salt); +} + +// [M|h]ashes up an IP address and a secret key +// to return a hopefully unique masked IP. +IP4Address MD5_ip(IP4Address ip) +{ + static SaltString secret = make_salt(); + + // MD5sum a secret + the IP address + VString<31> ipbuf; + SNPRINTF(ipbuf, 32, "%s %s", ip, secret); + md5_binary obuf; + MD5_to_bin(MD5_from_string(ipbuf), obuf); + + // Fold the md5sum to 32 bits, pack the bytes to an in_addr + return IP4Address({ + static_cast(obuf[0] ^ obuf[1] ^ obuf[8] ^ obuf[9]), + static_cast(obuf[2] ^ obuf[3] ^ obuf[10] ^ obuf[11]), + static_cast(obuf[4] ^ obuf[5] ^ obuf[12] ^ obuf[13]), + static_cast(obuf[6] ^ obuf[7] ^ obuf[14] ^ obuf[15]), + }); +} diff --git a/src/mmo/md5more.hpp b/src/mmo/md5more.hpp new file mode 100644 index 0000000..0c50cca --- /dev/null +++ b/src/mmo/md5more.hpp @@ -0,0 +1,26 @@ +#ifndef TMWA_MMO_MD5MORE_HPP +#define TMWA_MMO_MD5MORE_HPP + +# include "../generic/md5.hpp" + +# include "../io/read.hpp" + +# include "ip.hpp" +# include "mmo.hpp" + +MD5_state MD5_from_FILE(io::ReadFile& in); + +// whoever wrote this fails basic understanding of +AccountCrypt MD5_saltcrypt(AccountPass key, SaltString salt); + +/// return some random characters +// Currently, returns a 5-char string +SaltString make_salt(void); + +/// check plaintext password against saved saltcrypt +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 diff --git a/src/mmo/mmo.cpp b/src/mmo/mmo.cpp new file mode 100644 index 0000000..e9893ee --- /dev/null +++ b/src/mmo/mmo.cpp @@ -0,0 +1,3 @@ +#include "mmo.hpp" + +#include "../poison.hpp" diff --git a/src/mmo/mmo.hpp b/src/mmo/mmo.hpp new file mode 100644 index 0000000..6b3cd53 --- /dev/null +++ b/src/mmo/mmo.hpp @@ -0,0 +1,377 @@ +/// Global structures and defines +#ifndef TMWA_MMO_MMO_HPP +#define TMWA_MMO_MMO_HPP + +# include "../sanity.hpp" + +# include "../compat/memory.hpp" + +# include "../strings/vstring.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 + +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 + +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("a@a.com") + +// It is decreed: a mapname shall not contain an extension +class MapName : public strings::_crtp_string +{ + 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(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(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; +} +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 attrs; + SEX sex; + + unsigned long mapip; + unsigned int mapport; + + struct point last_point, save_point; + struct item inventory[MAX_INVENTORY]; + earray skill; + int global_reg_num; + struct global_reg global_reg[GLOBAL_REG_NUM]; + int account_reg_num; + struct global_reg account_reg[ACCOUNT_REG_NUM]; + int account_reg2_num; + struct global_reg account_reg2[ACCOUNT_REG2_NUM]; +}; + +struct CharPair +{ + CharKey key; + std::unique_ptr data; + + CharPair() + : key{}, data(make_unique()) + {} +}; + +struct storage +{ + int dirty; + int account_id; + short storage_status; + short storage_amount; + struct item storage_[MAX_STORAGE]; +}; + +//struct map_session_data; + +struct GM_Account +{ + int account_id; + uint8_t level; +}; + +struct party_member +{ + int account_id; + CharName name; + MapName map; + int leader, online, lv; + struct map_session_data *sd; +}; + +struct party +{ + int party_id; + PartyName name; + int exp; + int item; + struct party_member member[MAX_PARTY]; +}; + +#endif // TMWA_MMO_MMO_HPP diff --git a/src/mmo/socket.cpp b/src/mmo/socket.cpp new file mode 100644 index 0000000..73e32a4 --- /dev/null +++ b/src/mmo/socket.cpp @@ -0,0 +1,474 @@ +#include "socket.hpp" + +#include +#include +#include +//#include + +#include +#include + +#include +#include +#include + +#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, FD_SETSIZE> session; +#pragma GCC diagnostic pop + +void set_session(io::FD fd, std::unique_ptr 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> 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; +} + + +/// Discard all input +static +void null_parse(Session *s); +/// Default parser for new connections +static +void (*default_func_parse)(Session *) = null_parse; + +void set_defaultparse(void (*defaultparse)(Session *)) +{ + default_func_parse = defaultparse; +} + +/// Read from socket to the queue +static +void recv_to_fifo(Session *s) +{ + if (s->eof) + return; + + 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->eof = 1; + } +} + +static +void send_from_fifo(Session *s) +{ + if (s->eof) + return; + + 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->eof = 1; + } +} + +static +void null_parse(Session *s) +{ + PRINTF("null_parse : %d\n", s); + RFIFOSKIP(s, RFIFOREST(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(&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 *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->func_recv = recv_to_fifo; + s->func_send = send_from_fifo; + s->func_parse = default_func_parse; + s->client_ip = IP4Address(client_address.sin_addr); + s->created = TimeT::now(); + s->connected = 0; +} + +Session *make_listen_port(uint16_t port) +{ + 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(&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 *s = get_session(fd); + s->fd = fd; + + s->func_recv = connect_client; + s->created = TimeT::now(); + s->connected = 1; + + return s; +} + +Session *make_connection(IP4Address ip, uint16_t port) +{ + 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(&server_address), + sizeof(struct sockaddr_in)); + + readfds.set(fd); + + set_session(fd, make_unique()); + 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->func_recv = recv_to_fifo; + s->func_send = send_from_fifo; + s->func_parse = default_func_parse; + s->created = TimeT::now(); + s->connected = 1; + + return s; +} + +void delete_session(Session *s) +{ + if (!s) + return; + + 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(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)) + { + if (s->func_send) + //send_from_fifo(i); + s->func_send(s); + } + if (rfd.isset(i)) + { + 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(TimeT::now()) - static_cast(s->created) > CONNECT_TIMEOUT) + { + PRINTF("Session #%d timed out\n", s); + s->eof = 1; + } + if (!s->rdata_size && !s->eof) + continue; + if (s->func_parse) + { + s->func_parse(s); + /// some func_parse may call delete_session + s = get_session(i); + if (s && s->eof) + { + delete_session(s); + s = nullptr; + } + if (!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 new file mode 100644 index 0000000..a77f512 --- /dev/null +++ b/src/mmo/socket.hpp @@ -0,0 +1,373 @@ +#ifndef TMWA_MMO_SOCKET_HPP +#define TMWA_MMO_SOCKET_HPP + +# include "../sanity.hpp" + +# include + +# include + +# include + +# 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 declaration + +struct Session +{ + /// Checks whether a newly-connected socket actually does anything + TimeT created; + bool connected; + + /// Flag needed since structure must be freed in a server-dependent manner + bool eof; + + /// Since this is a single-threaded application, it can't block + /// These are the read/write queues + dumb_ptr 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; + + /// 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 + /// Can be set explicitly or via set_defaultparse + void (*func_parse)(Session *); + /// Server-specific data type + std::unique_ptr session_data; + + io::FD fd; +}; + +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 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> 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); +/// Connect to an address, return a connected socket or -1 +// FIXME - this is IPv4 only! +Session *make_connection(IP4Address ip, uint16_t port); +/// 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); + +/// Change the default parser for newly connected clients +// typically called once per server, but individual clients may identify +// themselves as servers +void set_defaultparse(void(*defaultparse)(Session *)); + +template +uint8_t *pod_addressof_m(T& structure) +{ + static_assert(is_trivially_copyable::value, "Can only byte-copy POD-ish structs"); + return &reinterpret_cast(structure); +} + +template +const uint8_t *pod_addressof_c(const T& structure) +{ + static_assert(is_trivially_copyable::value, "Can only byte-copy POD-ish structs"); + return &reinterpret_cast(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(RFIFOP(s, pos)); +} +inline +uint16_t RFIFOW(Session *s, size_t pos) +{ + return *static_cast(RFIFOP(s, pos)); +} +inline +uint32_t RFIFOL(Session *s, size_t pos) +{ + return *static_cast(RFIFOP(s, pos)); +} +template +void RFIFO_STRUCT(Session *s, size_t pos, T& structure) +{ + really_memcpy(pod_addressof_m(structure), static_cast(RFIFOP(s, pos)), sizeof(T)); +} +inline +IP4Address RFIFOIP(Session *s, size_t pos) +{ + IP4Address o; + RFIFO_STRUCT(s, pos, o); + return o; +} +template +inline +VString RFIFO_STRING(Session *s, size_t pos) +{ + const char *const begin = static_cast(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(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(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(RBUFP(p, pos)); +} +inline +uint16_t RBUFW(const uint8_t *p, size_t pos) +{ + return *static_cast(RBUFP(p, pos)); +} +inline +uint32_t RBUFL(const uint8_t *p, size_t pos) +{ + return *static_cast(RBUFP(p, pos)); +} +template +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 +inline +VString RBUF_STRING(const uint8_t *p, size_t pos) +{ + const char *const begin = static_cast(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(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(WFIFOP(s, pos)); +} +inline +uint16_t& WFIFOW(Session *s, size_t pos) +{ + return *static_cast(WFIFOP(s, pos)); +} +inline +uint32_t& WFIFOL(Session *s, size_t pos) +{ + return *static_cast(WFIFOP(s, pos)); +} +template +void WFIFO_STRUCT(Session *s, size_t pos, T& structure) +{ + really_memcpy(static_cast(WFIFOP(s, pos)), pod_addressof_c(structure), sizeof(T)); +} +inline +IP4Address& WFIFOIP(Session *s, size_t pos) +{ + static_assert(is_trivially_copyable::value, "That was the whole point"); + return *static_cast(WFIFOP(s, pos)); +} +inline +void WFIFO_STRING(Session *s, size_t pos, XString str, size_t len) +{ + char *const begin = static_cast(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(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(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(WBUFP(p, pos)); +} +inline +uint16_t& WBUFW(uint8_t *p, size_t pos) +{ + return *static_cast(WBUFP(p, pos)); +} +inline +uint32_t& WBUFL(uint8_t *p, size_t pos) +{ + return *static_cast(WBUFP(p, pos)); +} +template +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(WBUFP(p, pos)); +} +inline +void WBUF_STRING(uint8_t *p, size_t pos, XString s, size_t len) +{ + char *const begin = static_cast(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(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(WFIFOP(ws, 0)), + static_cast(RFIFOP(rs, 0)), len); +} + +#endif // TMWA_MMO_SOCKET_HPP diff --git a/src/mmo/timer.cpp b/src/mmo/timer.cpp new file mode 100644 index 0000000..b5a2a5e --- /dev/null +++ b/src/mmo/timer.cpp @@ -0,0 +1,201 @@ +#include "timer.hpp" + +#include +#include + +#include +#include + +#include + +#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 l, dumb_ptr 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, std::vector>, 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::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 td) +{ + timer_heap.push(td); +} + +static +dumb_ptr top_timer_heap(void) +{ + if (timer_heap.empty()) + return dumb_ptr(); + 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::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 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 new file mode 100644 index 0000000..01b8623 --- /dev/null +++ b/src/mmo/timer.hpp @@ -0,0 +1,30 @@ +#ifndef TMWA_MMO_TIMER_HPP +#define TMWA_MMO_TIMER_HPP + +# 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 new file mode 100644 index 0000000..dcc88f8 --- /dev/null +++ b/src/mmo/timer.t.hpp @@ -0,0 +1,68 @@ +#ifndef TMWA_MMO_TIMER_T_HPP +#define TMWA_MMO_TIMER_T_HPP + +# include +# include + +# 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 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 timer_func; + +class Timer +{ + friend struct TimerData; + dumb_ptr 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 new file mode 100644 index 0000000..0dbf145 --- /dev/null +++ b/src/mmo/utils.cpp @@ -0,0 +1,101 @@ +#include "utils.hpp" + +#include +#include + +#include + +#include "../strings/astring.hpp" +#include "../strings/zstring.hpp" +#include "../strings/xstring.hpp" + +#include "../io/cxxstdio.hpp" +#include "../io/write.hpp" + +#include "extract.hpp" + +#include "../poison.hpp" + +//--------------------------------------------------- +// E-mail check: return 0 (not correct) or 1 (valid). +//--------------------------------------------------- +bool e_mail_check(XString email) +{ + // athena limits + if (email.size() < 3 || email.size() > 39) + return 0; + + // part of RFC limits (official reference of e-mail description) + XString::iterator at = std::find(email.begin(), email.end(), '@'); + if (at == email.end()) + return 0; + XString username = email.xislice_h(at); + XString hostname = email.xislice_t(at + 1); + if (!username || !hostname) + return 0; + if (hostname.contains('@')) + return 0; + if (hostname.front() == '.' || hostname.back() == '.') + return 0; + if (hostname.contains_seq("..")) + return 0; + if (email.contains_any(" ;")) + return 0; + return email.is_print(); +} + +//------------------------------------------------- +// Return numerical value of a switch configuration +// on/off, english, français, deutsch, español +//------------------------------------------------- +int config_switch(ZString str) +{ + if (str == "true" || str == "on" || str == "yes" + || str == "oui" || str == "ja" + || str == "si") + return 1; + if (str == "false" || str == "off" || str == "no" + || str == "non" || str == "nein") + return 0; + + int rv; + if (extract(str, &rv)) + return rv; + FPRINTF(stderr, "Fatal: bad option value %s", str); + abort(); +} + +static_assert(sizeof(timestamp_seconds_buffer) == 20, "seconds buffer"); +static_assert(sizeof(timestamp_milliseconds_buffer) == 24, "millis buffer"); + +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(const_(buf)); +} +void stamp_time(timestamp_milliseconds_buffer& out) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + 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(tv.tv_usec / 1000)); + out = stringish(const_(buf)); +} + +void log_with_timestamp(io::WriteFile& out, XString line) +{ + if (!line) + { + out.put('\n'); + return; + } + timestamp_milliseconds_buffer tmpstr; + stamp_time(tmpstr); + out.really_put(tmpstr.data(), tmpstr.size()); + out.really_put(": ", 2); + out.put_line(line); +} diff --git a/src/mmo/utils.hpp b/src/mmo/utils.hpp new file mode 100644 index 0000000..5e9de26 --- /dev/null +++ b/src/mmo/utils.hpp @@ -0,0 +1,116 @@ +#ifndef TMWA_MMO_UTILS_HPP +#define TMWA_MMO_UTILS_HPP + +# include "../sanity.hpp" + +# include + +# include + +# include "../strings/fwd.hpp" +# include "../strings/vstring.hpp" + +# include "../generic/const_array.hpp" +# include "../generic/operators.hpp" + +# include "../io/fwd.hpp" + +template +struct is_trivially_copyable +: std::integral_constant +{}; + +bool e_mail_check(XString email); +int config_switch (ZString str); + +template +void really_memzero_this(T *v) +{ + static_assert(is_trivially_copyable::value, "only for mostly-pod types"); + static_assert(std::is_class::value || std::is_union::value, "Only for user-defined structures (for now)"); + memset(v, '\0', sizeof(*v)); +} +template +void really_memzero_this(T (&)[n]) = delete; + +// Exists in place of time_t, to give it a predictable printf-format. +// (on x86 and amd64, time_t == long, but not on x32) +static_assert(sizeof(long long) >= sizeof(time_t), "long long >= time_t"); +struct TimeT : Comparable +{ + long long value; + + // conversion + TimeT(time_t t=0) : value(t) {} + TimeT(struct tm t) : value(timegm(&t)) {} + operator time_t() const { return value; } + operator struct tm() const { time_t v = value; return *gmtime(&v); } + + explicit operator bool() const { return value; } + bool operator !() const { return !value; } + + // prevent surprises + template + TimeT(T) = delete; + template + operator T() const = delete; + + static + TimeT now() + { + // poisoned, but this is still in header-land + return time(NULL); + } + + bool error() const + { + return value == -1; + } + bool okay() const + { + return !error(); + } +}; + +inline +long long convert_for_printf(TimeT t) +{ + return t.value; +} + +inline +long long& convert_for_scanf(TimeT& t) +{ + return t.value; +} + +struct timestamp_seconds_buffer : VString<19> {}; +struct timestamp_milliseconds_buffer : VString<23> {}; +void stamp_time(timestamp_seconds_buffer&, const TimeT *t=nullptr); +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" +static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer), + "timestamp size"); +# 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) \ + stamp_time( \ + reinterpret_cast( \ + str + sizeof(str) \ + )[-1], \ + &t \ + ) + +#endif // TMWA_MMO_UTILS_HPP diff --git a/src/mmo/version.cpp b/src/mmo/version.cpp new file mode 100644 index 0000000..dd18fe1 --- /dev/null +++ b/src/mmo/version.cpp @@ -0,0 +1,55 @@ +#include "version.hpp" + +#include "../conf/version.hpp" + +#include "../strings/xstring.hpp" + +#include "extract.hpp" + +#include "../poison.hpp" + +Version CURRENT_VERSION = +{ + VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, + VERSION_DEVEL, + + 0, 0, + VENDOR_VERSION, +}; +Version CURRENT_LOGIN_SERVER_VERSION = +{ + VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, + VERSION_DEVEL, + + 0, TMWA_SERVER_LOGIN, + VENDOR_VERSION, +}; +Version CURRENT_CHAR_SERVER_VERSION = +{ + VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, + VERSION_DEVEL, + + 0, TMWA_SERVER_CHAR | TMWA_SERVER_INTER, + VENDOR_VERSION, +}; +Version CURRENT_MAP_SERVER_VERSION = +{ + VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, + VERSION_DEVEL, + + 0, TMWA_SERVER_MAP, + VENDOR_VERSION, +}; + +#define S2(a) #a +#define S(a) S2(a) + +const char CURRENT_VERSION_STRING[] = "TMWA " + S(VERSION_MAJOR) "." S(VERSION_MINOR) "." S(VERSION_PATCH) + " dev" S(VERSION_DEVEL) " (" VENDOR " " S(VENDOR_VERSION) ")"; + +bool extract(XString str, Version *vers) +{ + *vers = {}; + return extract(str, record<'.'>(&vers->major, &vers->minor, &vers->patch)); +} diff --git a/src/mmo/version.hpp b/src/mmo/version.hpp new file mode 100644 index 0000000..420cbf9 --- /dev/null +++ b/src/mmo/version.hpp @@ -0,0 +1,69 @@ +#ifndef TMWA_MMO_VERSION_HPP +#define TMWA_MMO_VERSION_HPP + +# include + +# include "../strings/fwd.hpp" + +// TODO make these bitwise enums +# 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 + +struct Version +{ + uint8_t major; + uint8_t minor; // flavor1 + uint8_t patch; // flavor2 + uint8_t devel; // flavor3 + + uint8_t flags; + uint8_t which; + uint16_t vend; + // can't add vendor name yet +}; +static_assert(sizeof(Version) == 8, "this is sent over the network, can't change"); + +extern Version CURRENT_VERSION; + +extern Version CURRENT_LOGIN_SERVER_VERSION; +extern Version CURRENT_CHAR_SERVER_VERSION; +extern Version CURRENT_MAP_SERVER_VERSION; + +extern const char 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 diff --git a/src/monitor/GNUmakefile b/src/monitor/GNUmakefile deleted file mode 100644 index 42efa8b..0000000 --- a/src/monitor/GNUmakefile +++ /dev/null @@ -1,7 +0,0 @@ -.SUFFIXES: -bin/tmwa-monitor: - ${MAKE} -C ../.. bin/tmwa-monitor -clean: - rm -r ../../obj/monitor/ -%:: - ${MAKE} -C ../.. obj/monitor/$@ diff --git a/src/monitor/main.cpp b/src/monitor/main.cpp index e59be71..d2d22c4 100644 --- a/src/monitor/main.cpp +++ b/src/monitor/main.cpp @@ -23,8 +23,8 @@ #include "../io/fd.hpp" #include "../io/read.hpp" -#include "../common/config_parse.hpp" -#include "../common/utils.hpp" +#include "../mmo/config_parse.hpp" +#include "../mmo/utils.hpp" #include "../poison.hpp" diff --git a/src/sanity.hpp b/src/sanity.hpp index d4a8738..b048c36 100644 --- a/src/sanity.hpp +++ b/src/sanity.hpp @@ -10,11 +10,6 @@ # error "Your compiler is absolutely ancient. You have no chance ..." # endif // __GNUC__ < 4 -/// Convert type assumptions to use the standard types here -# include -/// size_t, NULL -# include - # if __GNUC__ == 4 // clang identifies as GCC 4.2, but is mostly okay. // Until a bug-free release of it happens, though, I won't recommend it. diff --git a/src/strings/astring.cpp b/src/strings/astring.cpp index f7cfa2e..f1e9030 100644 --- a/src/strings/astring.cpp +++ b/src/strings/astring.cpp @@ -25,6 +25,8 @@ #include "xstring.hpp" #include "vstring.hpp" +//#include "../poison.hpp" + namespace strings { static_assert(sizeof(AString) == 256, "AString"); diff --git a/src/strings/base.hpp b/src/strings/base.hpp index c081f70..cd8d46f 100644 --- a/src/strings/base.hpp +++ b/src/strings/base.hpp @@ -22,6 +22,8 @@ # include "fwd.hpp" # include "pair.hpp" +# include + # include // It is a common mistake to assume that one string class for everything. diff --git a/src/strings/fwd.hpp b/src/strings/fwd.hpp index f2b4037..4c58e03 100644 --- a/src/strings/fwd.hpp +++ b/src/strings/fwd.hpp @@ -21,6 +21,8 @@ # include "../sanity.hpp" +# include + // It is a common mistake to assume that one string class for everything. // Because C++ and TMWA have a C legacy, there are a few more here // than would probably be necessary in an ideal language. diff --git a/src/strings/mstring.cpp b/src/strings/mstring.cpp index 0d496a5..2a1ca62 100644 --- a/src/strings/mstring.cpp +++ b/src/strings/mstring.cpp @@ -20,6 +20,8 @@ #include "xstring.hpp" +#include "../poison.hpp" + namespace strings { MString::iterator MString::begin() diff --git a/src/strings/rstring.cpp b/src/strings/rstring.cpp index eb9d88a..c0e231e 100644 --- a/src/strings/rstring.cpp +++ b/src/strings/rstring.cpp @@ -25,6 +25,8 @@ #include "xstring.hpp" #include "vstring.hpp" +//#include "../poison.hpp" + namespace strings { static_assert(sizeof(RString) == sizeof(const char *), "RString"); diff --git a/src/strings/sstring.cpp b/src/strings/sstring.cpp index fee98f9..76f0994 100644 --- a/src/strings/sstring.cpp +++ b/src/strings/sstring.cpp @@ -22,6 +22,8 @@ #include "zstring.hpp" #include "xstring.hpp" +#include "../poison.hpp" + namespace strings { SString::SString() diff --git a/src/strings/tstring.cpp b/src/strings/tstring.cpp index 8be7112..5f463ca 100644 --- a/src/strings/tstring.cpp +++ b/src/strings/tstring.cpp @@ -22,6 +22,8 @@ #include "zstring.hpp" #include "xstring.hpp" +#include "../poison.hpp" + namespace strings { TString::TString() diff --git a/src/strings/vstring.cpp b/src/strings/vstring.cpp index 6a0416a..0ef8f3d 100644 --- a/src/strings/vstring.cpp +++ b/src/strings/vstring.cpp @@ -18,6 +18,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include "../poison.hpp" + namespace strings { } // namespace strings diff --git a/src/strings/vstring.hpp b/src/strings/vstring.hpp index 183e782..c467b64 100644 --- a/src/strings/vstring.hpp +++ b/src/strings/vstring.hpp @@ -65,6 +65,14 @@ namespace strings int do_vprint(VString& out, const char *fmt, va_list ap); } // namespace strings +template +T stringish(VString iv) +{ + T rv; + static_cast&>(rv) = iv; + return rv; +} + # include "vstring.tcc" #endif // TMWA_STRINGS_VSTRING_HPP diff --git a/src/strings/vstring.tcc b/src/strings/vstring.tcc index 69729d4..1aa163d 100644 --- a/src/strings/vstring.tcc +++ b/src/strings/vstring.tcc @@ -19,7 +19,7 @@ #include -#include "../common/utils2.hpp" +#include "../compat/cast.hpp" #include "rstring.hpp" #include "astring.hpp" diff --git a/src/strings/xstring.cpp b/src/strings/xstring.cpp index 8a604c7..0808104 100644 --- a/src/strings/xstring.cpp +++ b/src/strings/xstring.cpp @@ -18,6 +18,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include "../poison.hpp" + namespace strings { XString::XString() diff --git a/src/strings/zstring.cpp b/src/strings/zstring.cpp index bfc0c96..e2a763f 100644 --- a/src/strings/zstring.cpp +++ b/src/strings/zstring.cpp @@ -20,6 +20,8 @@ #include "xstring.hpp" +#include "../poison.hpp" + namespace strings { ZString::ZString() -- cgit v1.2.3-60-g2f50