summaryrefslogtreecommitdiff
path: root/src/mmo
diff options
context:
space:
mode:
Diffstat (limited to 'src/mmo')
-rw-r--r--src/mmo/clif.t.hpp717
-rw-r--r--src/mmo/config_parse.cpp211
-rw-r--r--src/mmo/config_parse.hpp7
-rw-r--r--src/mmo/config_parse_test.cpp60
-rw-r--r--src/mmo/consts.cpp26
-rw-r--r--src/mmo/consts.hpp7
-rw-r--r--src/mmo/core.cpp161
-rw-r--r--src/mmo/core.hpp49
-rw-r--r--src/mmo/cxxstdio_enums.hpp86
-rw-r--r--src/mmo/enums.cpp26
-rw-r--r--src/mmo/enums.hpp25
-rw-r--r--src/mmo/extract.cpp101
-rw-r--r--src/mmo/extract.hpp231
-rw-r--r--src/mmo/extract_enums.cpp33
-rw-r--r--src/mmo/extract_enums.hpp50
-rw-r--r--src/mmo/extract_test.cpp360
-rw-r--r--src/mmo/fwd.hpp36
-rw-r--r--src/mmo/human_time_diff.cpp42
-rw-r--r--src/mmo/human_time_diff.hpp45
-rw-r--r--src/mmo/human_time_diff_test.cpp2
-rw-r--r--src/mmo/ids.cpp16
-rw-r--r--src/mmo/ids.hpp18
-rw-r--r--src/mmo/ids.py1
-rw-r--r--src/mmo/login.t.hpp (renamed from src/mmo/md5more.hpp)34
-rw-r--r--src/mmo/md5more.cpp159
-rw-r--r--src/mmo/mmo.cpp26
-rw-r--r--src/mmo/mmo.hpp69
-rw-r--r--src/mmo/skill.t.hpp139
-rw-r--r--src/mmo/strs.cpp6
-rw-r--r--src/mmo/strs.hpp33
-rw-r--r--src/mmo/utils.cpp123
-rw-r--r--src/mmo/utils.hpp167
-rw-r--r--src/mmo/version.cpp33
-rw-r--r--src/mmo/version.hpp9
34 files changed, 1429 insertions, 1679 deletions
diff --git a/src/mmo/clif.t.hpp b/src/mmo/clif.t.hpp
new file mode 100644
index 0000000..0a51523
--- /dev/null
+++ b/src/mmo/clif.t.hpp
@@ -0,0 +1,717 @@
+#pragma once
+// clif.t.hpp - Network interface to the client.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include <cstdint>
+
+#include "../ints/little.hpp"
+
+#include "../compat/iter.hpp"
+
+#include "../generic/enum.hpp"
+
+#include "consts.hpp"
+#include "enums.hpp"
+
+
+namespace tmwa
+{
+namespace e
+{
+// [Fate] status.option properties. These are persistent status changes.
+// IDs that are not listed are not used in the code (to the best of my knowledge)
+enum class Opt0 : uint16_t
+{
+ ZERO = 0x0000,
+
+ // [Fate] This is the GM `@hide' flag
+ HIDE = 0x0040,
+ // [Fate] Complete invisibility to other clients
+ INVISIBILITY = 0x1000,
+
+ // ?
+ REAL_ANY_HIDE = HIDE,
+};
+enum class Opt1 : uint16_t
+{
+ ZERO = 0,
+ _stone1 = 1,
+ _freeze = 2,
+ _stan = 3,
+ _sleep = 4,
+ _stone6 = 6,
+};
+enum class Opt2 : uint16_t
+{
+ ZERO = 0x0000,
+ _poison = 0x0001,
+ _curse = 0x0002,
+ _silence = 0x0004,
+ BLIND = 0x0010,
+ _speedpotion0 = 0x0020,
+ _signumcrucis = 0x0040,
+ _atkpot = 0x0080,
+ _heal = 0x0100,
+ _slowpoison = 0x0200,
+};
+enum class Opt3 : uint16_t
+{
+ ZERO = 0x0000,
+ _concentration = 0x0001,
+ _overthrust = 0x0002,
+ _energycoat = 0x0004,
+ _explosionspirits = 0x0008,
+ _steelbody = 0x0010,
+ _berserk = 0x0080,
+
+ _marionette = 0x0400,
+ _assumptio = 0x0800,
+};
+
+ENUM_BITWISE_OPERATORS(Opt0)
+ENUM_BITWISE_OPERATORS(Opt2)
+ENUM_BITWISE_OPERATORS(Opt3)
+}
+using e::Opt0;
+using e::Opt1;
+using e::Opt2;
+using e::Opt3;
+
+
+enum class ItemType : uint8_t
+{
+ USE = 0, // in eA, healing only
+ _1 = 1, // unused
+ _2 = 2, // in eA, other usable items
+ JUNK = 3, // "useless" items (e.g. quests)
+ WEAPON = 4, // all weapons
+ ARMOR = 5, // all other equipment
+ _6 = 6, // in eA, card
+ _7 = 7, // in eA, pet egg
+ _8 = 8, // in eA, pet equipment
+ _9 = 9, // unused
+ ARROW = 10, // ammo
+ _11 = 11, // in eA, delayed use (special script)
+};
+
+enum class BeingRemoveWhy : uint8_t
+{
+ // general disappearance
+ GONE = 0,
+ // only case handled specially in client
+ DEAD = 1,
+ QUIT = 2,
+ WARPED = 3,
+ // handled specially in clif_clearchar - sent as 0 over network
+ DISGUISE = 9,
+
+ // handled speciall in mob_warp - not actually sent over network
+ NEGATIVE1 = 0xff,
+};
+
+enum class PickupFail : uint8_t
+{
+ OKAY = 0,
+ BAD_ITEM = 1,
+ TOO_HEAVY = 2,
+ TOO_FAR = 3,
+ INV_FULL = 4,
+ STACK_FULL = 5,
+ DROP_STEAL = 6,
+};
+
+// this is used for both input and output
+// different values are valid in 0x0089 vs 0x008a
+enum class DamageType : uint8_t
+{
+ NORMAL = 0x00,
+ TAKEITEM = 0x01,
+ SIT = 0x02,
+ STAND = 0x03,
+ RETURNED = 0x04,
+ CONTINUOUS = 0x07,
+ DOUBLED = 0x08,
+ CRITICAL = 0x0a,
+ FLEE2 = 0x0b,
+};
+
+enum class LOOK : uint8_t
+{
+ BASE = 0,
+ HAIR = 1,
+ WEAPON = 2,
+ HEAD_BOTTOM = 3,
+ HEAD_TOP = 4,
+ HEAD_MID = 5,
+ HAIR_COLOR = 6,
+ CLOTHES_COLOR = 7,
+ SHIELD = 8,
+ SHOES = 9,
+ GLOVES = 10,
+ CAPE = 11,
+ MISC1 = 12,
+ MISC2 = 13,
+
+ COUNT,
+};
+
+// Note: there is also a typedef by this name in <dirent.h>
+// but we should be fine since we never include it.
+// (in the long term we should still rename this though)
+enum class DIR : uint8_t
+{
+ S = 0,
+ SW = 1,
+ W = 2,
+ NW = 3,
+ N = 4,
+ NE = 5,
+ E = 6,
+ SE = 7,
+
+ COUNT,
+};
+
+constexpr
+earray<int, DIR, DIR::COUNT> dirx //=
+{{
+ 0, -1, -1, -1, 0, 1, 1, 1,
+}}, diry //=
+{{
+ 1, 1, 0, -1, -1, -1, 0, 1,
+}};
+
+constexpr
+bool dir_is_diagonal(DIR d)
+{
+ return static_cast<uint8_t>(d) & 1;
+}
+
+
+enum class SP : uint16_t
+{
+ // sent to client
+ SPEED = 0,
+
+ // when used as "no stat"
+ ZERO = 0,
+
+ // sent to client
+ BASEEXP = 1,
+ // sent to client
+ JOBEXP = 2,
+#if 0
+ KARMA = 3,
+#endif
+
+ // sent to client
+ HP = 5,
+ // sent to client
+ MAXHP = 6,
+ // sent to client
+ SP = 7,
+ // sent to client
+ MAXSP = 8,
+ // sent to client
+ STATUSPOINT = 9,
+
+ // sent to client
+ BASELEVEL = 11,
+ // sent to client
+ SKILLPOINT = 12,
+ // sent to client
+ STR = 13,
+ // sent to client
+ AGI = 14,
+ // sent to client
+ VIT = 15,
+ // sent to client
+ INT = 16,
+ // sent to client
+ DEX = 17,
+ // sent to client
+ LUK = 18,
+ CLASS = 19,
+ // sent to client
+ ZENY = 20,
+ SEX = 21,
+ // sent to client
+ NEXTBASEEXP = 22,
+ // sent to client
+ NEXTJOBEXP = 23,
+ // sent to client
+ WEIGHT = 24,
+ // sent to client
+ MAXWEIGHT = 25,
+
+ // sent to client
+ USTR = 32,
+ // sent to client
+ UAGI = 33,
+ // sent to client
+ UVIT = 34,
+ // sent to client
+ UINT = 35,
+ // sent to client
+ UDEX = 36,
+ // sent to client
+ ULUK = 37,
+
+ // sent to client
+ ATK1 = 41,
+ // sent to client
+ ATK2 = 42,
+ // sent to client
+ MATK1 = 43,
+ // sent to client
+ MATK2 = 44,
+ // sent to client
+ DEF1 = 45,
+ // sent to client
+ DEF2 = 46,
+ // sent to client
+ MDEF1 = 47,
+ // sent to client
+ MDEF2 = 48,
+ // sent to client
+ HIT = 49,
+ // sent to client
+ FLEE1 = 50,
+ // sent to client
+ FLEE2 = 51,
+ // sent to client
+ CRITICAL = 52,
+ // sent to client
+ ASPD = 53,
+
+ // sent to client
+ JOBLEVEL = 55,
+
+#if 0
+ PARTNER = 57,
+ CART = 58,
+ FAME = 59,
+ UNBREAKABLE = 60,
+#endif
+
+ DEAF = 70,
+
+ // sent to client
+ GM = 500,
+
+ // sent to client
+ ATTACKRANGE = 1000,
+#if 0
+ ATKELE = 1001,
+#endif
+#if 0
+ DEFELE = 1002,
+#endif
+#if 0
+ CASTRATE = 1003,
+#endif
+ MAXHPRATE = 1004,
+#if 0
+ MAXSPRATE = 1005,
+#endif
+#if 0
+ SPRATE = 1006,
+#endif
+
+#if 0
+ ADDEFF = 1012,
+#endif
+#if 0
+ RESEFF = 1013,
+#endif
+ BASE_ATK = 1014,
+ ASPD_RATE = 1015,
+ HP_RECOV_RATE = 1016,
+#if 0
+ SP_RECOV_RATE = 1017,
+#endif
+#if 0
+ SPEED_RATE = 1018,
+#endif
+ CRITICAL_DEF = 1019,
+#if 0
+ NEAR_ATK_DEF = 1020,
+#endif
+#if 0
+ LONG_ATK_DEF = 1021,
+#endif
+#if 0
+ DOUBLE_RATE = 1022,
+#endif
+ DOUBLE_ADD_RATE = 1023,
+#if 0
+ MATK = 1024,
+#endif
+#if 0
+ MATK_RATE = 1025,
+#endif
+#if 0
+ IGNORE_DEF_ELE = 1026,
+#endif
+#if 0
+ IGNORE_DEF_RACE = 1027,
+#endif
+#if 0
+ ATK_RATE = 1028,
+#endif
+ SPEED_ADDRATE = 1029,
+#if 0
+ ASPD_ADDRATE = 1030,
+#endif
+#if 0
+ MAGIC_ATK_DEF = 1031,
+#endif
+#if 0
+ MISC_ATK_DEF = 1032,
+#endif
+#if 0
+ IGNORE_MDEF_ELE = 1033,
+#endif
+#if 0
+ IGNORE_MDEF_RACE = 1034,
+#endif
+
+#if 0
+ PERFECT_HIT_RATE = 1038,
+#endif
+#if 0
+ PERFECT_HIT_ADD_RATE = 1039,
+#endif
+#if 0
+ CRITICAL_RATE = 1040,
+#endif
+#if 0
+ GET_ZENY_NUM = 1041,
+#endif
+#if 0
+ ADD_GET_ZENY_NUM = 1042,
+#endif
+
+#if 0
+ ADD_MONSTER_DROP_ITEM = 1047,
+#endif
+#if 0
+ DEF_RATIO_ATK_ELE = 1048,
+#endif
+#if 0
+ DEF_RATIO_ATK_RACE = 1049,
+#endif
+#if 0
+ ADD_SPEED = 1050,
+#endif
+#if 0
+ HIT_RATE = 1051,
+#endif
+#if 0
+ FLEE_RATE = 1052,
+#endif
+#if 0
+ FLEE2_RATE = 1053,
+#endif
+ DEF_RATE = 1054,
+ DEF2_RATE = 1055,
+#if 0
+ MDEF_RATE = 1056,
+#endif
+#if 0
+ MDEF2_RATE = 1057,
+#endif
+#if 0
+ SPLASH_RANGE = 1058,
+#endif
+#if 0
+ SPLASH_ADD_RANGE = 1059,
+#endif
+
+ HP_DRAIN_RATE = 1061,
+#if 0
+ SP_DRAIN_RATE = 1062,
+#endif
+#if 0
+ SHORT_WEAPON_DAMAGE_RETURN = 1063,
+#endif
+#if 0
+ LONG_WEAPON_DAMAGE_RETURN = 1064,
+#endif
+
+#if 0
+ ADDEFF2 = 1067,
+#endif
+ BREAK_WEAPON_RATE = 1068,
+ BREAK_ARMOR_RATE = 1069,
+ ADD_STEAL_RATE = 1070,
+ MAGIC_DAMAGE_RETURN = 1071,
+#if 0
+ RANDOM_ATTACK_INCREASE = 1072,
+#endif
+};
+
+constexpr
+SP attr_to_sp(ATTR attr)
+{
+ return static_cast<SP>(static_cast<uint16_t>(attr) + static_cast<uint16_t>(SP::STR));
+}
+
+constexpr
+ATTR sp_to_attr(SP sp)
+{
+ return static_cast<ATTR>(static_cast<uint16_t>(sp) - static_cast<uint16_t>(SP::STR));
+}
+
+constexpr
+SP attr_to_usp(ATTR attr)
+{
+ return static_cast<SP>(static_cast<uint16_t>(attr) + static_cast<uint16_t>(SP::USTR));
+}
+
+constexpr
+ATTR usp_to_attr(SP sp)
+{
+ return static_cast<ATTR>(static_cast<uint16_t>(sp) - static_cast<uint16_t>(SP::USTR));
+}
+
+constexpr
+SP sp_to_usp(SP sp)
+{
+ return attr_to_usp(sp_to_attr(sp));
+}
+
+constexpr
+SP usp_to_sp(SP sp)
+{
+ return attr_to_sp(usp_to_attr(sp));
+}
+
+
+// xxxx xxxx xxyy yyyy yyyy dddd
+struct NetPosition1
+{
+ Byte data[3];
+};
+
+struct Position1
+{
+ uint16_t x, y;
+ DIR dir;
+};
+
+inline
+bool native_to_network(NetPosition1 *network, Position1 native)
+{
+ uint16_t x = native.x;
+ uint16_t y = native.y;
+ uint8_t d = static_cast<uint8_t>(native.dir);
+
+ uint8_t *p = reinterpret_cast<uint8_t *>(network);
+ p[0] = x >> 2;
+ p[1] = (x << 6) | ((y >> 4) & 0x3f);
+ p[2] = y << 4 | d;
+
+ return x < 1024 && y < 1024 && d < 16;
+}
+
+inline
+bool network_to_native(Position1 *native, NetPosition1 network)
+{
+ const uint8_t *p = reinterpret_cast<const uint8_t *>(&network);
+ native->x = (p[0] & (0x3ff >> 2)) << 2 | p[1] >> (8 - 2);
+ native->y = (p[1] & (0x3ff >> 4)) << 4 | p[2] >> (8 - 4);
+ uint8_t d = p[2] & 0x0f;
+ native->dir = static_cast<DIR>(d);
+ return d < 8;
+}
+
+// x0xx xxxx xxy0 yyyy yyyy x1xx xxxx xxy1 yyyy yyyy
+struct NetPosition2
+{
+ Byte data[5];
+};
+
+struct Position2
+{
+ uint16_t x0, y0;
+ uint16_t x1, y1;
+};
+
+inline
+bool native_to_network(NetPosition2 *network, Position2 native)
+{
+ uint16_t x0 = native.x0;
+ uint16_t y0 = native.y0;
+ uint16_t x1 = native.x1;
+ uint16_t y1 = native.y1;
+
+ uint8_t *p = reinterpret_cast<uint8_t *>(network);
+ p[0] = x0 >> 2;
+ p[1] = (x0 << 6) | ((y0 >> 4) & 0x3f);
+ p[2] = (y0 << 4) | ((x1 >> 6) & 0x0f);
+ p[3] = (x1 << 2) | ((y1 >> 8) & 0x03);
+ p[4] = y1;
+
+ return x0 < 1024 && y0 < 1024 && x1 < 1024 && y1 < 1024;
+}
+
+inline
+bool network_to_native(Position2 *native, NetPosition2 network)
+{
+ const uint8_t *p = reinterpret_cast<const uint8_t *>(&network);
+ native->x0 = (p[0] & (0x3ff >> 2)) << 2 | p[1] >> (8 - 2);
+ native->y0 = (p[1] & (0x3ff >> 4)) << 4 | p[2] >> (8 - 4);
+ native->x1 = (p[2] & (0x3ff >> 6)) << 6 | p[3] >> (8 - 6);
+ native->y1 = (p[3] & (0x3ff >> 8)) << 8 | p[4] >> (8 - 8);
+ return true;
+}
+
+struct IOff2;
+struct SOff1;
+
+struct IOff0
+{
+ uint16_t index;
+
+ bool ok() const
+ { return get0() < MAX_INVENTORY; }
+ uint16_t get0() const
+ { return index; }
+ static IOff0 from(uint16_t i)
+ { return IOff0{i}; }
+ static IteratorPair<ValueIterator<IOff0>> iter()
+ { return {IOff0::from(0), IOff0::from(MAX_INVENTORY)}; }
+ friend uint16_t convert_for_printf(IOff0 i0) { return i0.index; }
+
+ IOff0& operator ++() { ++index; return *this; }
+ friend bool operator == (IOff0 l, IOff0 r) { return l.index == r.index; }
+ friend bool operator != (IOff0 l, IOff0 r) { return !(l == r); }
+ IOff2 shift() const;
+
+ IOff0() : index(0) {}
+private:
+ explicit IOff0(uint16_t i) : index(i) {}
+};
+
+struct SOff0
+{
+ uint16_t index;
+
+ bool ok() const
+ { return get0() < MAX_STORAGE; }
+ uint16_t get0() const
+ { return index; }
+ static SOff0 from(uint16_t i)
+ { return SOff0{i}; }
+ static IteratorPair<ValueIterator<SOff0>> iter()
+ { return {SOff0::from(0), SOff0::from(MAX_STORAGE)}; }
+ friend uint16_t convert_for_printf(SOff0 s0) { return s0.index; }
+
+ SOff0& operator ++() { ++index; return *this; }
+ friend bool operator == (SOff0 l, SOff0 r) { return l.index == r.index; }
+ friend bool operator != (SOff0 l, SOff0 r) { return !(l == r); }
+ SOff1 shift() const;
+
+ SOff0() : index(0) {}
+private:
+ explicit SOff0(uint16_t i) : index(i) {}
+};
+
+struct IOff2
+{
+ uint16_t index;
+
+ bool ok() const
+ { return get2() < MAX_INVENTORY; }
+ uint16_t get2() const
+ { return index - 2; }
+ static IOff2 from(uint16_t i)
+ { return IOff2{static_cast<uint16_t>(i + 2)}; }
+ static IteratorPair<ValueIterator<IOff2>> iter()
+ { return {IOff2::from(0), IOff2::from(MAX_INVENTORY)}; }
+
+ IOff2& operator ++() { ++index; return *this; }
+ friend bool operator == (IOff2 l, IOff2 r) { return l.index == r.index; }
+ friend bool operator != (IOff2 l, IOff2 r) { return !(l == r); }
+ IOff0 unshift() const
+ { return IOff0::from(get2()); }
+
+ IOff2() : index(0) {}
+private:
+ explicit IOff2(uint16_t i) : index(i) {}
+};
+
+struct SOff1
+{
+ uint16_t index;
+
+ bool ok() const
+ { return get1() < MAX_STORAGE; }
+ uint16_t get1() const
+ { return index - 1; }
+ static SOff1 from(uint16_t i)
+ { return SOff1{static_cast<uint16_t>(i + 1)}; }
+ static IteratorPair<ValueIterator<SOff1>> iter()
+ { return {SOff1::from(0), SOff1::from(MAX_STORAGE)}; }
+
+ SOff1& operator ++() { ++index; return *this; }
+ friend bool operator == (SOff1 l, SOff1 r) { return l.index == r.index; }
+ friend bool operator != (SOff1 l, SOff1 r) { return !(l == r); }
+ SOff0 unshift() const
+ { return SOff0::from(get1()); }
+
+ SOff1() : index(0) {}
+private:
+ explicit SOff1(uint16_t i) : index(i) {}
+};
+
+inline IOff2 IOff0::shift() const
+{ return IOff2::from(get0()); }
+inline SOff1 SOff0::shift() const
+{ return SOff1::from(get0()); }
+
+inline
+bool native_to_network(Little16 *network, IOff2 native)
+{
+ return native_to_network(network, native.index);
+}
+
+inline
+bool network_to_native(IOff2 *native, Little16 network)
+{
+ return network_to_native(&native->index, network);
+}
+
+inline
+bool native_to_network(Little16 *network, SOff1 native)
+{
+ return native_to_network(network, native.index);
+}
+
+inline
+bool network_to_native(SOff1 *native, Little16 network)
+{
+ return network_to_native(&native->index, network);
+}
+} // namespace tmwa
diff --git a/src/mmo/config_parse.cpp b/src/mmo/config_parse.cpp
index 8362810..6e43471 100644
--- a/src/mmo/config_parse.cpp
+++ b/src/mmo/config_parse.cpp
@@ -23,7 +23,10 @@
#include "../strings/xstring.hpp"
#include "../strings/zstring.hpp"
+#include "../compat/borrow.hpp"
+
#include "../io/cxxstdio.hpp"
+#include "../io/extract.hpp"
#include "../io/line.hpp"
#include "version.hpp"
@@ -39,39 +42,138 @@ bool is_comment(XString line)
}
template<class ZS>
+static
+bool do_split(io::Spanned<ZS> line, io::Spanned<XString> *key, io::Spanned<ZS> *value)
+{
+ typename ZS::iterator colon = std::find(line.data.begin(), line.data.end(), ':');
+ if (colon == line.data.end())
+ return false;
+ key->data = line.data.xislice_h(colon);
+ key->span = line.span;
+ key->span.end.column = key->span.begin.column + key->data.size() - 1;
+ ++colon;
+ value->data = line.data.xislice_t(colon);
+ value->span = line.span;
+ value->span.begin.column = value->span.end.column - value->data.size() + 1;
+ return true;
+}
+
+template<class ZS>
+static
+io::Spanned<ZS> do_lstrip(io::Spanned<ZS> value)
+{
+ io::Spanned<ZS> rv;
+ rv.data = value.data.lstrip();
+ rv.span = value.span;
+ rv.span.begin.column += (value.data.size() - rv.data.size());
+ return rv;
+}
+
+static
+io::Spanned<XString> do_rstrip(io::Spanned<XString> value)
+{
+ io::Spanned<XString> rv;
+ rv.data = value.data.rstrip();
+ rv.span = value.span;
+ rv.span.end.column -= (value.data.size() - rv.data.size());
+ return rv;
+}
+
+static
+io::Spanned<XString> do_strip(io::Spanned<XString> value)
+{
+ return do_lstrip(do_rstrip(value));
+}
+
+template<class ZS>
inline
-bool config_split_impl(ZS line, XString *key, ZS *value)
+bool config_split_impl(io::Spanned<ZS> line, io::Spanned<XString> *key, io::Spanned<ZS> *value)
{
// unconditionally fail if line contains control characters
- if (std::find_if(line.begin(), line.end(),
+ if (std::find_if(line.data.begin(), line.data.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())
+ ) != line.data.end())
return false;
- *key = line.xislice_h(colon).strip();
- // move past the colon and any spaces
- ++colon;
- *value = line.xislice_t(colon).lstrip();
+ if (!do_split(line, key, value))
+ return false;
+ *key = do_strip(*key);
+ *value = do_lstrip(*value);
return true;
}
// eventually this should go away
-bool config_split(ZString line, XString *key, ZString *value)
+// currently the only real offenders are io::FD::open and *PRINTF
+bool config_split(io::Spanned<ZString> line, io::Spanned<XString> *key, io::Spanned<ZString> *value)
{
return config_split_impl(line, key, value);
}
-// and use this instead
-bool config_split(XString line, XString *key, XString *value)
+
+static
+bool check_version(io::Spanned<XString> avers, Borrowed<bool> valid)
{
- return config_split_impl(line, key, value);
+ enum
+ {
+ GE, LE, GT, LT
+ } cmp;
+
+ if (avers.data.startswith(">="_s))
+ {
+ cmp = GE;
+ avers.data = avers.data.xslice_t(2);
+ avers.span.begin.column += 2;
+ }
+ else if (avers.data.startswith('>'))
+ {
+ cmp = GT;
+ avers.data = avers.data.xslice_t(1);
+ avers.span.begin.column += 1;
+ }
+ else if (avers.data.startswith("<="_s))
+ {
+ cmp = LE;
+ avers.data = avers.data.xslice_t(2);
+ avers.span.begin.column += 2;
+ }
+ else if (avers.data.startswith('<'))
+ {
+ cmp = LT;
+ avers.data = avers.data.xslice_t(1);
+ avers.span.begin.column += 1;
+ }
+ else
+ {
+ avers.span.error("Version check must begin with one of: '>=', '>', '<=', '<'"_s);
+ *valid = false;
+ return false;
+ }
+
+ Version vers;
+ if (!extract(avers.data, &vers))
+ {
+ avers.span.error("Bad value"_s);
+ *valid = false;
+ return false;
+ }
+ switch (cmp)
+ {
+ case GE:
+ return CURRENT_VERSION >= vers;
+ case GT:
+ return CURRENT_VERSION > vers;
+ case LE:
+ return CURRENT_VERSION <= vers;
+ case LT:
+ return CURRENT_VERSION < vers;
+ }
+ abort();
}
/// Master config parser. This handles 'import' and 'version-ge' etc.
/// Then it defers to the inferior parser for a line it does not understand.
+///
+/// Note: old-style 'version-ge: 1.2.3' etc apply to the rest of the file, but
+/// new-style 'version: >= 1.2.3' apply only up to the next 'version:'
bool load_config_file(ZString filename, ConfigItemParser slave)
{
io::LineReader in(filename);
@@ -80,30 +182,66 @@ bool load_config_file(ZString filename, ConfigItemParser slave)
PRINTF("Unable to open file: %s\n"_fmt, filename);
return false;
}
- io::Line line;
+ io::Line line_;
bool rv = true;
- while (in.read_line(line))
+ bool good_version = true;
+ while (in.read_line(line_))
{
- if (is_comment(line.text))
+ if (is_comment(line_.text))
continue;
- XString key;
- ZString value;
- if (!config_split(line.text, &key, &value))
+ auto line = io::respan(line_.to_span(), ZString(line_.text));
+ io::Spanned<XString> key;
+ io::Spanned<ZString> value;
+ if (!config_split(line, &key, &value))
{
- line.error("Bad config line"_s);
+ line.span.error("Bad config line"_s);
rv = false;
continue;
}
- if (key == "import"_s)
+ if (key.data == "version"_s)
+ {
+ if (value.data == "all"_s)
+ {
+ good_version = true;
+ }
+ else
+ {
+ good_version = true;
+ while (good_version && value.data)
+ {
+ ZString::iterator it = std::find(value.data.begin(), value.data.end(), ' ');
+ io::Spanned<XString> value_head;
+ value_head.data = value.data.xislice_h(it);
+ value_head.span = value.span;
+ value.data = value.data.xislice_t(it).lstrip();
+
+ value_head.span.end.column = value_head.span.begin.column + value_head.data.size() - 1;
+ value.span.begin.column = value.span.end.column - value.data.size() + 1;
+
+ good_version &= check_version(value_head, borrow(rv));
+ }
+ }
+ continue;
+ }
+ if (!good_version)
{
- rv &= load_config_file(value, slave);
continue;
}
- else if (key == "version-lt"_s)
+ if (key.data == "import"_s)
+ {
+ if (!load_config_file(value.data, slave))
+ {
+ value.span.error("Failed to include file"_s);
+ rv = false;
+ }
+ continue;
+ }
+ else if (key.data == "version-lt"_s)
{
Version vers;
- if (!extract(value, &vers))
+ if (!extract(value.data, &vers))
{
+ value.span.error("Bad value"_s);
rv = false;
continue;
}
@@ -111,47 +249,48 @@ bool load_config_file(ZString filename, ConfigItemParser slave)
continue;
break;
}
- else if (key == "version-le"_s)
+ else if (key.data == "version-le"_s)
{
Version vers;
- if (!extract(value, &vers))
+ if (!extract(value.data, &vers))
{
rv = false;
+ value.span.error("Bad value"_s);
continue;
}
if (CURRENT_VERSION <= vers)
continue;
break;
}
- else if (key == "version-gt"_s)
+ else if (key.data == "version-gt"_s)
{
Version vers;
- if (!extract(value, &vers))
+ if (!extract(value.data, &vers))
{
rv = false;
+ value.span.error("Bad value"_s);
continue;
}
if (CURRENT_VERSION > vers)
continue;
break;
}
- else if (key == "version-ge"_s)
+ else if (key.data == "version-ge"_s)
{
Version vers;
- if (!extract(value, &vers))
+ if (!extract(value.data, &vers))
{
rv = false;
+ value.span.error("Bad value"_s);
continue;
}
if (CURRENT_VERSION >= vers)
continue;
break;
}
- else if (!slave(key, value))
+ else
{
- line.error("Bad config key or value"_s);
- rv = false;
- continue;
+ rv &= slave(key, value);
}
// nothing to see here, move along
}
diff --git a/src/mmo/config_parse.hpp b/src/mmo/config_parse.hpp
index 50a115e..06432ba 100644
--- a/src/mmo/config_parse.hpp
+++ b/src/mmo/config_parse.hpp
@@ -20,16 +20,13 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
namespace tmwa
{
-typedef bool (*ConfigItemParser)(XString key, ZString value);
+using ConfigItemParser = bool(io::Spanned<XString> key, io::Spanned<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);
+bool config_split(io::Spanned<ZString> line, io::Spanned<XString> *key, io::Spanned<ZString> *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.
diff --git a/src/mmo/config_parse_test.cpp b/src/mmo/config_parse_test.cpp
new file mode 100644
index 0000000..e1170cb
--- /dev/null
+++ b/src/mmo/config_parse_test.cpp
@@ -0,0 +1,60 @@
+#include "config_parse.hpp"
+// config_parse_test.cpp - Testsuite for config parsers
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <gtest/gtest.h>
+
+#include "../strings/literal.hpp"
+#include "../strings/rstring.hpp"
+
+#include "../io/span.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+#define EXPECT_SPAN(span, bl,bc, el,ec) \
+ ({ \
+ EXPECT_EQ((span).begin.line, bl); \
+ EXPECT_EQ((span).begin.column, bc); \
+ EXPECT_EQ((span).end.line, el); \
+ EXPECT_EQ((span).end.column, ec); \
+ })
+TEST(configparse, keyvalue)
+{
+ // 0 1 2 3
+ // 123456789012345678901234567890
+ RString data = " key : value "_s;
+
+ io::Spanned<ZString> input, value;
+ io::Spanned<XString> key;
+ input.data = data;
+ input.span.begin.text = data;
+ input.span.begin.filename = "<config parse key/value test>"_s;
+ input.span.begin.line = 1;
+ input.span.begin.column = 1;
+ input.span.end = input.span.begin;
+ input.span.end.column = data.size();
+ EXPECT_EQ(data.size(), 30);
+ ASSERT_TRUE(config_split(input, &key, &value));
+ EXPECT_SPAN(key.span, 1,3, 1,5);
+ EXPECT_SPAN(value.span, 1,18, 1,30);
+}
+} // namespace tmwa
diff --git a/src/mmo/consts.cpp b/src/mmo/consts.cpp
deleted file mode 100644
index e49cdf5..0000000
--- a/src/mmo/consts.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "consts.hpp"
-// consts.cpp - empty mess of constants
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/mmo/consts.hpp b/src/mmo/consts.hpp
index c1a7eb6..5533446 100644
--- a/src/mmo/consts.hpp
+++ b/src/mmo/consts.hpp
@@ -54,6 +54,11 @@ constexpr int MAX_PARTY = 12;
#define MIN_CLOTH_COLOR battle_config.min_cloth_color
#define MAX_CLOTH_COLOR battle_config.max_cloth_color
+namespace map
+{
+ struct map_session_data;
+}
+
// WTF is this doing here?
struct PartyMember
{
@@ -61,6 +66,6 @@ struct PartyMember
CharName name;
MapName map;
int leader, online, lv;
- struct map_session_data *sd;
+ map::map_session_data *sd;
};
} // namespace tmwa
diff --git a/src/mmo/core.cpp b/src/mmo/core.cpp
deleted file mode 100644
index ff12f17..0000000
--- a/src/mmo/core.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-#include "core.hpp"
-// core.cpp - The main loop.
-//
-// Copyright © ????-2004 Athena Dev Teams
-// Copyright © 2004-2011 The Mana World Development Team
-// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include <sys/wait.h>
-
-#include <alloca.h>
-
-#include <csignal>
-#include <cstdlib>
-
-#include <tmwa/shared.hpp>
-
-#include "../strings/zstring.hpp"
-#include "../strings/literal.hpp"
-
-#include "../io/cxxstdio.hpp"
-
-#include "../net/socket.hpp"
-#include "../net/timer.hpp"
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-// Added by Gabuzomeu
-//
-// This is an implementation of signal() using sigaction() for portability.
-// (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)
- {
- DIAG_PUSH();
- DIAG_I(old_style_cast);
- return SIG_ERR;
- DIAG_POP();
- }
-
- return oact.sa_handler;
-}
-
-volatile
-bool runflag = true;
-
-static
-void chld_proc(int)
-{
- wait(nullptr);
-}
-static
-void sig_proc(int)
-{
- for (int i = 1; i < 31; ++i)
- {
- DIAG_PUSH();
- DIAG_I(old_style_cast);
- compat_signal(i, SIG_IGN);
- DIAG_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.
- */
-} // namespace tmwa
-
-int tmwa_main(int argc, char **argv)
-{
- using namespace tmwa;
-
- check_paths();
-
- // ZString args[argc]; is (deliberately!) not supported by clang yet
- ZString *args = static_cast<ZString *>(alloca(argc * sizeof(ZString)));
- for (int i = 0; i < argc; ++i)
- args[i] = ZString(strings::really_construct_from_a_pointer, argv[i], nullptr);
- do_init(Slice<ZString>(args, argc));
-
- if (!runflag)
- {
- PRINTF("Fatal error during startup; exiting\n"_fmt);
- return 1;
- }
- // set up exit handlers *after* the initialization has happened.
- // This is because term_func is likely to depend on successful init.
-
- DIAG_PUSH();
- DIAG_I(old_style_cast);
- compat_signal(SIGPIPE, SIG_IGN);
- DIAG_POP();
- compat_signal(SIGTERM, sig_proc);
- compat_signal(SIGINT, sig_proc);
- compat_signal(SIGCHLD, chld_proc);
-
- // Signal to create coredumps by system when necessary (crash)
- DIAG_PUSH();
- DIAG_I(old_style_cast);
- DIAG_I(zero_as_null_pointer_constant);
- compat_signal(SIGSEGV, SIG_DFL);
- compat_signal(SIGBUS, SIG_DFL);
- compat_signal(SIGTRAP, SIG_DFL);
- compat_signal(SIGILL, SIG_DFL);
- compat_signal(SIGFPE, SIG_DFL);
- DIAG_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();
- }
-
- return 0;
-}
diff --git a/src/mmo/core.hpp b/src/mmo/core.hpp
deleted file mode 100644
index 259dd90..0000000
--- a/src/mmo/core.hpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-// core.hpp - The main loop.
-//
-// Copyright © ????-2004 Athena Dev Teams
-// Copyright © 2004-2011 The Mana World Development Team
-// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "fwd.hpp"
-
-#include "../range/slice.hpp"
-
-#include "../strings/fwd.hpp"
-
-
-namespace tmwa
-{
-/// 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(Slice<ZString>);
-
-/// Cleanup function called whenever a signal kills us
-/// or when if we manage to exit() gracefully.
-extern void term_func(void);
-} // namespace tmwa
-
-/// grumble grumble stupid intertwined includes mumble mumble
-__attribute__((warn_unused_result))
-extern int tmwa_main(int argc, char **argv);
diff --git a/src/mmo/cxxstdio_enums.hpp b/src/mmo/cxxstdio_enums.hpp
new file mode 100644
index 0000000..28a8a14
--- /dev/null
+++ b/src/mmo/cxxstdio_enums.hpp
@@ -0,0 +1,86 @@
+#pragma once
+// cxxstdio_enums.hpp - Opt-in integer formatting support for enums.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include <cstdint>
+
+#include "../generic/enum.hpp"
+
+
+namespace tmwa
+{
+namespace e
+{
+enum class EPOS : uint16_t;
+enum class Opt0 : uint16_t;
+
+inline
+auto decay_for_printf(EPOS v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+inline
+auto decay_for_printf(Opt0 v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+} // namespace e
+
+enum class ItemLook : uint16_t;
+enum class SP : uint16_t;
+enum class SkillID : uint16_t;
+enum class StatusChange : uint16_t;
+
+inline
+auto decay_for_printf(ItemLook v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+inline
+auto decay_for_printf(SP v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+inline
+auto decay_for_printf(SkillID v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+inline
+auto decay_for_printf(StatusChange v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+
+namespace map
+{
+namespace e
+{
+enum class BF : uint16_t;
+enum class MapCell : uint8_t;
+
+inline
+auto decay_for_printf(BF v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+inline
+auto decay_for_printf(MapCell v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+} // namespace map::e
+namespace magic
+{
+enum class SPELLARG : uint8_t;
+
+inline
+auto decay_for_printf(SPELLARG v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+} // namespace map::magic
+
+enum class BL : uint8_t;
+enum class ByteCode : uint8_t;
+enum class MS : uint8_t;
+
+inline
+auto decay_for_printf(BL v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+inline
+auto decay_for_printf(ByteCode v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+inline
+auto decay_for_printf(MS v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
+} // namespace map
+} // namespace tmwa
diff --git a/src/mmo/enums.cpp b/src/mmo/enums.cpp
deleted file mode 100644
index d05be91..0000000
--- a/src/mmo/enums.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "enums.hpp"
-// enums.cpp - Common enumerated types
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/mmo/enums.hpp b/src/mmo/enums.hpp
index bf8a75c..caecc13 100644
--- a/src/mmo/enums.hpp
+++ b/src/mmo/enums.hpp
@@ -66,14 +66,14 @@ enum class SkillFlags : uint16_t;
}
using e::SkillFlags;
-// Option and Opt1..3 in map.hpp
+// Opt0 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); }
+enum class Opt0 : uint16_t;
+constexpr Opt0 get_enum_min_value(Opt0) { return Opt0(0x0000); }
+constexpr Opt0 get_enum_max_value(Opt0) { return Opt0(0xffff); }
}
-using e::Option;
+using e::Opt0;
enum class ATTR
{
@@ -101,22 +101,10 @@ 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,
+ COUNT = 17,
};
enum class SEX : uint8_t
@@ -124,6 +112,7 @@ enum class SEX : uint8_t
FEMALE = 0,
MALE = 1,
// For items. This is also used as error, sometime.
+ // TODO switch to Option<SEX> where appropriate.
NEUTRAL = 2,
};
inline
diff --git a/src/mmo/extract.cpp b/src/mmo/extract.cpp
deleted file mode 100644
index a480984..0000000
--- a/src/mmo/extract.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-#include "extract.hpp"
-// extract.cpp - a simple, hierarchical, tokenizer
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include <algorithm>
-
-#include "../strings/astring.hpp"
-#include "../strings/xstring.hpp"
-#include "../strings/vstring.hpp"
-
-#include "extract_enums.hpp"
-#include "mmo.hpp"
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-bool extract(XString str, XString *rv)
-{
- *rv = str;
- return true;
-}
-
-bool extract(XString str, AString *rv)
-{
- *rv = str;
- return true;
-}
-
-bool extract(XString str, GlobalReg *var)
-{
- return extract(str,
- record<','>(&var->str, &var->value));
-}
-
-bool extract(XString str, Item *it)
-{
- XString ignored;
- XString corruption_hack_amount;
- bool rv = extract(str,
- record<',', 11>(
- &ignored,
- &it->nameid,
- &corruption_hack_amount,
- &it->equip,
- &ignored,
- &ignored,
- &ignored,
- &ignored,
- &ignored,
- &ignored,
- &ignored,
- &ignored));
- if (rv)
- {
- if (corruption_hack_amount == "-1"_s)
- it->amount = 0;
- else
- rv = extract(corruption_hack_amount, &it->amount);
- }
- return rv;
-}
-
-bool extract(XString str, MapName *m)
-{
- XString::iterator it = std::find(str.begin(), str.end(), '.');
- str = str.xislice_h(it);
- VString<15> tmp;
- bool rv = extract(str, &tmp);
- *m = tmp;
- return rv;
-}
-
-bool extract(XString str, CharName *out)
-{
- VString<23> tmp;
- if (extract(str, &tmp))
- {
- *out = CharName(tmp);
- return true;
- }
- return false;
-}
-} // namespace tmwa
diff --git a/src/mmo/extract.hpp b/src/mmo/extract.hpp
deleted file mode 100644
index ed2eb78..0000000
--- a/src/mmo/extract.hpp
+++ /dev/null
@@ -1,231 +0,0 @@
-#pragma once
-// extract.hpp - a simple, hierarchical, tokenizer
-//
-// Copyright © 2012-2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "fwd.hpp"
-
-#include <cerrno>
-#include <cstdlib>
-
-#include <algorithm>
-#include <vector>
-
-#include "../ints/wrap.hpp"
-
-#include "../strings/xstring.hpp"
-
-#include "../generic/enum.hpp"
-
-#include "utils.hpp"
-
-
-namespace tmwa
-{
-template<class T>
-bool do_extract(XString str, T t);
-
-template<class T, typename=typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value && !std::is_same<T, bool>::value>::type>
-bool extract(XString str, T *iv)
-{
- if (!str || str.size() > 20)
- return false;
- if (!((str.front() == '-' && std::is_signed<T>::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<T>::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);
-}
-
-template<class T, typename=typename std::enable_if<std::is_enum<T>::value>::type>
-bool extract_as_int(XString str, T *iv)
-{
- typedef typename underlying_type<T>::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<T>(v);
- return true;
-}
-
-bool extract(XString str, XString *rv);
-
-bool extract(XString str, AString *rv);
-
-template<uint8_t N>
-bool extract(XString str, VString<N> *out)
-{
- if (str.size() > N)
- return false;
- *out = str;
- return true;
-}
-
-inline
-bool extract(XString str, LString exact)
-{
- return str == exact;
-}
-
-template<class T>
-class LStripper
-{
-public:
- T impl;
-};
-
-template<class T>
-LStripper<T> lstripping(T v)
-{
- return {v};
-}
-
-template<class T>
-bool extract(XString str, LStripper<T> out)
-{
- return extract(str.lstrip(), out.impl);
-}
-
-// basically just a std::tuple
-// but it provides its data members publically
-template<char split, int n, class... T>
-class Record;
-template<char split, int n>
-class Record<split, n>
-{
-};
-template<char split, int n, class F, class... R>
-class Record<split, n, F, R...>
-{
-public:
- F frist;
- Record<split, n - 1, R...> rest;
-public:
- Record(F f, R... r)
- : frist(f), rest(r...)
- {}
-};
-template<char split, class... T>
-Record<split, sizeof...(T), T...> record(T... t)
-{
- return Record<split, sizeof...(T), T...>(t...);
-}
-template<char split, int n, class... T>
-Record<split, n, T...> record(T... t)
-{
- static_assert(0 < n && n < sizeof...(T), "don't be silly");
- return Record<split, n, T...>(t...);
-}
-
-template<char split, int n>
-bool extract(XString str, Record<split, n>)
-{
- return !str;
-}
-
-template<char split, int n, class F, class... R>
-bool extract(XString str, Record<split, n, F, R...> 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<char split, class T>
-struct VRecord
-{
- std::vector<T> *arr;
-};
-
-template<char split, class T>
-VRecord<split, T> vrec(std::vector<T> *arr)
-{
- return {arr};
-}
-
-template<char split, class T>
-bool extract(XString str, VRecord<split, T> 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, GlobalReg *var);
-
-bool extract(XString str, Item *it);
-
-bool extract(XString str, MapName *m);
-
-bool extract(XString str, CharName *out);
-
-template<class T>
-bool do_extract(XString str, T t)
-{
- return extract(str, t);
-}
-
-template<class R>
-bool extract(XString str, Wrapped<R> *w)
-{
- return extract(str, &w->_value);
-}
-} // namespace tmwa
diff --git a/src/mmo/extract_enums.cpp b/src/mmo/extract_enums.cpp
index f906179..efafa39 100644
--- a/src/mmo/extract_enums.cpp
+++ b/src/mmo/extract_enums.cpp
@@ -23,4 +23,37 @@
namespace tmwa
{
+bool impl_extract(XString str, DIR *d)
+{
+ unsigned di;
+ if (extract(str, &di) && di < 8)
+ {
+ *d = static_cast<DIR>(di);
+ return true;
+ }
+ const struct
+ {
+ LString str;
+ DIR d;
+ } dirs[] =
+ {
+ {"S"_s, DIR::S},
+ {"SW"_s, DIR::SW},
+ {"W"_s, DIR::W},
+ {"NW"_s, DIR::NW},
+ {"N"_s, DIR::N},
+ {"NE"_s, DIR::NE},
+ {"E"_s, DIR::E},
+ {"SE"_s, DIR::SE},
+ };
+ for (auto& pair : dirs)
+ {
+ if (str == pair.str)
+ {
+ *d = pair.d;
+ return true;
+ }
+ }
+ return false;
+}
} // namespace tmwa
diff --git a/src/mmo/extract_enums.hpp b/src/mmo/extract_enums.hpp
index 613fae9..0e8ac4c 100644
--- a/src/mmo/extract_enums.hpp
+++ b/src/mmo/extract_enums.hpp
@@ -22,7 +22,9 @@
#include <cstdint>
-#include "extract.hpp"
+#include "../io/extract.hpp"
+
+#include "clif.t.hpp"
namespace tmwa
@@ -30,40 +32,54 @@ namespace tmwa
namespace e
{
enum class EPOS : uint16_t;
-enum class MobMode : uint16_t;
enum class Opt1 : uint16_t;
enum class Opt2 : uint16_t;
-enum class Option : uint16_t;
+enum class Opt0 : uint16_t;
inline
-bool extract(XString str, EPOS *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, EPOS *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, MobMode *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, Opt1 *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, Opt1 *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, Opt2 *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, Opt2 *iv) { return extract_as_int(str, iv); }
-inline
-bool extract(XString str, Option *iv) { return extract_as_int(str, iv); }
-}
+bool impl_extract(XString str, Opt0 *iv) { return extract_as_int(str, iv); }
+} // namespace e
enum class ItemLook : uint16_t;
enum class ItemType : uint8_t;
-enum class Race : uint8_t;
enum class SEX : uint8_t;
enum class SkillID : uint16_t;
enum class StatusChange : uint16_t;
inline
-bool extract(XString str, ItemLook *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, ItemLook *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, ItemType *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, ItemType *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, Race *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, SEX *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, SEX *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, SkillID *iv) { return extract_as_int(str, iv); }
+inline
+bool impl_extract(XString str, StatusChange *iv) { return extract_as_int(str, iv); }
+
+bool impl_extract(XString, DIR *);
+
+namespace map
+{
+namespace e
+{
+enum class MobMode : uint16_t;
+
+inline
+bool impl_extract(XString str, MobMode *iv) { return extract_as_int(str, iv); }
+} // namespace map::e
+enum class Race : uint8_t;
+enum class ATK;
+
inline
-bool extract(XString str, SkillID *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, Race *iv) { return extract_as_int(str, iv); }
inline
-bool extract(XString str, StatusChange *iv) { return extract_as_int(str, iv); }
+bool impl_extract(XString str, ATK *iv) { return extract_as_int(str, iv); }
+} // namespace map
} // namespace tmwa
diff --git a/src/mmo/extract_test.cpp b/src/mmo/extract_test.cpp
deleted file mode 100644
index e6dc7b2..0000000
--- a/src/mmo/extract_test.cpp
+++ /dev/null
@@ -1,360 +0,0 @@
-#include "extract.hpp"
-// extract_test.cpp - Testsuite for a simple, hierarchical, tokenizer
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include <gtest/gtest.h>
-
-#include "../strings/xstring.hpp"
-
-#include "mmo.hpp"
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-TEST(extract, record_int)
-{
- int x, y, z;
- x = y = z = 0;
- EXPECT_FALSE(extract("1 2 3 4 "_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_FALSE(extract("1 2 3 4"_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1 2 3 "_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1 2 3"_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_FALSE(extract("1 2 "_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_FALSE(extract("1 2"_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_FALSE(extract("1 "_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(0, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_FALSE(extract("1"_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(0, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_FALSE(extract(" "_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(0, x);
- EXPECT_EQ(0, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_FALSE(extract(""_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(0, x);
- EXPECT_EQ(0, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
-
- EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1 2 3"_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1 2 "_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1 2"_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_FALSE(extract("1 "_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(0, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_FALSE(extract("1"_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(0, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_FALSE(extract(" "_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(0, x);
- EXPECT_EQ(0, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_FALSE(extract(""_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(0, x);
- EXPECT_EQ(0, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
-
- EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1 2 3"_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(3, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1 2 "_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1 2"_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(2, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1 "_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(0, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_TRUE(extract("1"_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ(1, x);
- EXPECT_EQ(0, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_FALSE(extract(" "_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ(0, x);
- EXPECT_EQ(0, y);
- EXPECT_EQ(0, z);
- x = y = z = 0;
- EXPECT_FALSE(extract(""_s, 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 = ""_s;
- EXPECT_FALSE(extract("1 2 3 4 "_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_FALSE(extract("1 2 3 4"_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 2 3 "_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 2 3"_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 2 "_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_FALSE(extract("1 2"_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_FALSE(extract("1 "_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_FALSE(extract("1"_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_FALSE(extract(" "_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(""_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_FALSE(extract(""_s, record<' '>(&x, &y, &z)));
- EXPECT_EQ(""_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
-
- EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 2 3"_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 2 "_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 2"_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 "_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_FALSE(extract("1"_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract(" "_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(""_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_FALSE(extract(""_s, record<' ', 2>(&x, &y, &z)));
- EXPECT_EQ(""_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
-
- EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 2 3"_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ("3"_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 2 "_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 2"_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ("2"_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1 "_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract("1"_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ("1"_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract(" "_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ(""_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
- EXPECT_TRUE(extract(""_s, record<' ', 1>(&x, &y, &z)));
- EXPECT_EQ(""_s, x);
- EXPECT_EQ(""_s, y);
- EXPECT_EQ(""_s, z);
- x = y = z = ""_s;
-}
-
-TEST(extract, mapname)
-{
- MapName map;
- EXPECT_TRUE(extract("abc"_s, &map));
- EXPECT_EQ(map, "abc"_s);
- EXPECT_TRUE(extract("abc.gat"_s, &map));
- EXPECT_EQ(map, "abc"_s);
- EXPECT_TRUE(extract("abcdefghijklmno"_s, &map));
- EXPECT_EQ(map, "abcdefghijklmno"_s);
- EXPECT_TRUE(extract("abcdefghijklmno.gat"_s, &map));
- EXPECT_EQ(map, "abcdefghijklmno"_s);
-}
-} // namespace tmwa
diff --git a/src/mmo/fwd.hpp b/src/mmo/fwd.hpp
index 3b56bfb..434885e 100644
--- a/src/mmo/fwd.hpp
+++ b/src/mmo/fwd.hpp
@@ -20,21 +20,29 @@
#include "../sanity.hpp"
+#include "../ints/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp" // rank 2
+#include "../generic/fwd.hpp" // rank 3
+#include "../io/fwd.hpp" // rank 4
+#include "../net/fwd.hpp" // rank 5
+// mmo/fwd.hpp is rank 6
+
namespace tmwa
{
// meh, add more when I feel like it
class MapName;
class CharName;
-class CharPair;
class HumanTimeDiff;
+class Species;
class AccountId;
class CharId;
class PartyId;
-class ItemUnkId;
class ItemNameId;
+class BlockId;
class GmLevel;
class AccountName;
@@ -43,26 +51,14 @@ class AccountCrypt;
class AccountEmail;
class ServerName;
class PartyName;
+class QuestId;
class VarName;
class MapName;
class CharName;
-class Item;
-#if 0
-class Point;
-class SkillValue;
-#endif
-class GlobalReg;
-#if 0
-class CharKey;
-class CharData;
-class CharPair;
-#endif
-class Storage;
-#if 0
-class GM_Account;
-class PartyMember;
-#endif
-class PartyMost;
-class PartyPair;
+struct MobName;
+struct NpcName;
+struct ScriptLabel;
+struct ItemName;
+struct NpcEvent;
} // namespace tmwa
diff --git a/src/mmo/human_time_diff.cpp b/src/mmo/human_time_diff.cpp
index 49a7664..aaa7928 100644
--- a/src/mmo/human_time_diff.cpp
+++ b/src/mmo/human_time_diff.cpp
@@ -18,9 +18,51 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
+#include "../io/extract.hpp"
+
#include "../poison.hpp"
namespace tmwa
{
+bool impl_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"_s || suffix == "a"_s)
+ ptr = &iv->year;
+ else if (suffix == "m"_s)
+ ptr = &iv->month;
+ else if (suffix == "j"_s || suffix == "d"_s)
+ ptr = &iv->day;
+ else if (suffix == "h"_s)
+ ptr = &iv->hour;
+ else if (suffix == "mn"_s)
+ ptr = &iv->minute;
+ else if (suffix == "s"_s)
+ ptr = &iv->second;
+ else
+ return false;
+ if (number.startswith('+') && !number.startswith("+-"_s))
+ number = number.xslice_t(1);
+ if (*ptr || !extract(number, ptr))
+ return false;
+ }
+ return true;
+}
} // namespace tmwa
diff --git a/src/mmo/human_time_diff.hpp b/src/mmo/human_time_diff.hpp
index b5c19fb..92b3288 100644
--- a/src/mmo/human_time_diff.hpp
+++ b/src/mmo/human_time_diff.hpp
@@ -24,8 +24,6 @@
#include "../strings/xstring.hpp"
-#include "extract.hpp"
-
namespace tmwa
{
@@ -44,46 +42,5 @@ struct HumanTimeDiff
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"_s || suffix == "a"_s)
- ptr = &iv->year;
- else if (suffix == "m"_s)
- ptr = &iv->month;
- else if (suffix == "j"_s || suffix == "d"_s)
- ptr = &iv->day;
- else if (suffix == "h"_s)
- ptr = &iv->hour;
- else if (suffix == "mn"_s)
- ptr = &iv->minute;
- else if (suffix == "s"_s)
- ptr = &iv->second;
- else
- return false;
- if (number.startswith('+') && !number.startswith("+-"_s))
- number = number.xslice_t(1);
- if (*ptr || !extract(number, ptr))
- return false;
- }
- return true;
-}
+bool impl_extract(XString str, HumanTimeDiff *iv);
} // namespace tmwa
diff --git a/src/mmo/human_time_diff_test.cpp b/src/mmo/human_time_diff_test.cpp
index c18599d..08a75bf 100644
--- a/src/mmo/human_time_diff_test.cpp
+++ b/src/mmo/human_time_diff_test.cpp
@@ -20,6 +20,8 @@
#include <gtest/gtest.h>
+#include "../io/extract.hpp"
+
#include "../poison.hpp"
diff --git a/src/mmo/ids.cpp b/src/mmo/ids.cpp
index d40d5c3..65c470b 100644
--- a/src/mmo/ids.cpp
+++ b/src/mmo/ids.cpp
@@ -18,9 +18,25 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
+#include "../io/extract.hpp"
+
#include "../poison.hpp"
namespace tmwa
{
+bool impl_extract(XString str, Species *w)
+{
+ // lots of data files use this
+ if (str == "-1"_s)
+ {
+ *w = NEGATIVE_SPECIES;
+ return true;
+ }
+ return extract(str, &w->_value);
+}
+bool impl_extract(XString str, GmLevel *lvl)
+{
+ return extract(str, &lvl->bits);
+}
} // namespace tmwa
diff --git a/src/mmo/ids.hpp b/src/mmo/ids.hpp
index 4e2b97c..28b146a 100644
--- a/src/mmo/ids.hpp
+++ b/src/mmo/ids.hpp
@@ -23,8 +23,6 @@
#include "../ints/little.hpp"
#include "../ints/wrap.hpp"
-#include "extract.hpp"
-
namespace tmwa
{
@@ -32,17 +30,7 @@ class Species : public Wrapped<uint16_t> { public: explicit operator bool() cons
constexpr Species NEGATIVE_SPECIES = Species();
-inline
-bool extract(XString str, Species *w)
-{
- // lots of data files use this
- if (str == "-1"_s)
- {
- *w = NEGATIVE_SPECIES;
- return true;
- }
- return extract(str, &w->_value);
-}
+bool impl_extract(XString str, Species *w);
class AccountId : public Wrapped<uint32_t> { public: constexpr AccountId() : Wrapped<uint32_t>() {} protected: constexpr explicit AccountId(uint32_t a) : Wrapped<uint32_t>(a) {} };
@@ -52,12 +40,14 @@ class PartyId : public Wrapped<uint32_t> { public: constexpr PartyId() : Wrapped
class ItemNameId : public Wrapped<uint16_t> { public: constexpr ItemNameId() : Wrapped<uint16_t>() {} protected: constexpr explicit ItemNameId(uint16_t a) : Wrapped<uint16_t>(a) {} };
class BlockId : public Wrapped<uint32_t> { public: constexpr BlockId() : Wrapped<uint32_t>() {} protected: constexpr explicit BlockId(uint32_t a) : Wrapped<uint32_t>(a) {} };
+class QuestId : public Wrapped<uint16_t> { public: constexpr QuestId() : Wrapped<uint16_t>() {} protected: constexpr explicit QuestId(uint16_t a) : Wrapped<uint16_t>(a) {} };
+bool impl_extract(XString str, GmLevel *lvl);
class GmLevel
{
uint32_t bits;
- friend bool extract(XString str, GmLevel *lvl) { return extract(str, &lvl->bits); }
+ friend bool impl_extract(XString str, GmLevel *lvl);
constexpr explicit
GmLevel(uint32_t b) : bits(b) {}
constexpr explicit
diff --git a/src/mmo/ids.py b/src/mmo/ids.py
index 89392b1..a98920f 100644
--- a/src/mmo/ids.py
+++ b/src/mmo/ids.py
@@ -5,6 +5,7 @@ for s in [
'PartyId',
'ItemNameId',
'BlockId',
+ 'QuestId',
]:
class OtherId(object):
__slots__ = ('_value')
diff --git a/src/mmo/md5more.hpp b/src/mmo/login.t.hpp
index 7d50713..f2c775a 100644
--- a/src/mmo/md5more.hpp
+++ b/src/mmo/login.t.hpp
@@ -1,5 +1,5 @@
#pragma once
-// md5more.hpp - Non-basic MD5 functions.
+// login.t.hpp - externally useful types from login
//
// Copyright © ????-2004 Athena Dev Teams
// Copyright © 2004-2011 The Mana World Development Team
@@ -22,27 +22,23 @@
#include "fwd.hpp"
-#include "../generic/md5.hpp"
+#include <cstdint>
-#include "../io/fwd.hpp"
-
-#include "../net/fwd.hpp"
+#include "../generic/enum.hpp"
namespace tmwa
{
-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);
+namespace e
+{
+enum class VERSION_2 : uint8_t
+{
+ /// client supports updatehost
+ UPDATEHOST = 0x01,
+ /// send servers in forward order
+ SERVERORDER = 0x02,
+};
+ENUM_BITWISE_OPERATORS(VERSION_2)
+}
+using e::VERSION_2;
} // namespace tmwa
diff --git a/src/mmo/md5more.cpp b/src/mmo/md5more.cpp
deleted file mode 100644
index 4e5d2da..0000000
--- a/src/mmo/md5more.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-#include "md5more.hpp"
-// md5more.cpp - Non-basic MD5 functions.
-//
-// Copyright © ????-2004 Athena Dev Teams
-// Copyright © 2004-2011 The Mana World Development Team
-// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include <algorithm>
-
-#include "../compat/rawmem.hpp"
-
-#include "../generic/random.hpp"
-
-#include "../io/cxxstdio.hpp"
-#include "../io/read.hpp"
-
-#include "../net/ip.hpp"
-
-#include "../mmo/mmo.hpp"
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-#define X block.data
-
-// TODO - refactor MD5 into a stream, and merge the implementations
-// 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<char *>(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"_fmt, salt, tbuf3);
- return stringish<AccountCrypt>(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<SaltString>(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<SaltString>(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"_fmt, 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<uint8_t>(obuf[0] ^ obuf[1] ^ obuf[8] ^ obuf[9]),
- static_cast<uint8_t>(obuf[2] ^ obuf[3] ^ obuf[10] ^ obuf[11]),
- static_cast<uint8_t>(obuf[4] ^ obuf[5] ^ obuf[12] ^ obuf[13]),
- static_cast<uint8_t>(obuf[6] ^ obuf[7] ^ obuf[14] ^ obuf[15]),
- });
-}
-} // namespace tmwa
diff --git a/src/mmo/mmo.cpp b/src/mmo/mmo.cpp
deleted file mode 100644
index aafa431..0000000
--- a/src/mmo/mmo.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "mmo.hpp"
-// mmo.cpp - dummy file to make Make dependencies work
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/mmo/mmo.hpp b/src/mmo/mmo.hpp
deleted file mode 100644
index cfa278d..0000000
--- a/src/mmo/mmo.hpp
+++ /dev/null
@@ -1,69 +0,0 @@
-#pragma once
-// mmo.hpp - Huge mess of structures.
-//
-// Copyright © ????-2004 Athena Dev Teams
-// Copyright © 2004-2011 The Mana World Development Team
-// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "fwd.hpp"
-
-#include "../compat/memory.hpp"
-
-#include "../proto2/types.hpp"
-
-
-namespace tmwa
-{
-inline
-bool operator == (const SkillValue& l, const SkillValue& r)
-{
- return l.lv == r.lv && l.flags == r.flags;
-}
-inline
-bool operator != (const SkillValue& l, const SkillValue& r)
-{
- return !(l == r);
-}
-
-struct CharPair
-{
- CharKey key;
- std::unique_ptr<CharData> data;
-
- CharPair()
- : key{}, data(make_unique<CharData>())
- {}
-};
-
-struct GM_Account
-{
- AccountId account_id;
- GmLevel level;
-};
-
-struct PartyPair
-{
- PartyId party_id = {};
- PartyMost *party_most = {};
-
- explicit
- operator bool() const { return party_most; }
- bool operator !() const { return !party_most; }
- PartyMost *operator->() const { return party_most; }
-};
-} // namespace tmwa
diff --git a/src/mmo/skill.t.hpp b/src/mmo/skill.t.hpp
new file mode 100644
index 0000000..21e4059
--- /dev/null
+++ b/src/mmo/skill.t.hpp
@@ -0,0 +1,139 @@
+#pragma once
+// skill.t.hpp - Old-style skills.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include <cstdint>
+
+#include "../generic/enum.hpp"
+
+
+namespace tmwa
+{
+// TODO remove most of these as their corresponding SkillIDs get deleted.
+enum class StatusChange : uint16_t
+{
+ // indices into (map_session_data).status_change
+ SC_SENDMAX = 256,
+
+ // sometimes means "none", sometimes not
+ NEGATIVE1 = 0xffff,
+
+ // these ones are used by clif_status_change,
+ // e.g. by the magic system
+ ZERO = 0,
+ ATTACK_ICON_GENERIC = 2000,
+ ATTACK_ICON_SHEARING = 2001,
+ CART = 0x0c,
+ CLIF_OPTION_SC_INVISIBILITY = 0x1000,
+ CLIF_OPTION_SC_SCRIBE = 0x1001,
+
+ // the rest are the normal effects
+ SC_SLOWPOISON = 14, // item script
+
+ SC_WEIGHT50 = 35, // ? sort of used
+ SC_WEIGHT90 = 36, // definitely used
+ SC_SPEEDPOTION0 = 37, // item script
+
+ SC_HEALING = 70, // item script
+
+ SC_POISON = 132, // bad; actually used
+
+ SC_ATKPOT = 185, // item script
+ SC_MATKPOT = 186, // unused, but kept for parallel
+
+// Added for Fate's spells
+ SC_HIDE = 194, // Hide from `detect' magic (PCs only)
+ SC_SHEARED = 194, // Has been sheared (mobs only)
+ SC_HALT_REGENERATE = 195, // Suspend regeneration
+ SC_FLYING_BACKPACK = 196, // Flying backpack
+ SC_MBARRIER = 197, // Magical barrier, magic resistance (val1 : power (%))
+ SC_HASTE = 198, // `Haste' spell (val1 : power)
+ SC_PHYS_SHIELD = 199, // `Protect' spell, reduce damage (val1: power)
+ MAX_STATUSCHANGE = 200,
+};
+
+enum class SkillID : uint16_t
+{
+ // TODO: Remove these!
+ NEGATIVE = 0xffff,
+ ZERO = 0x0000,
+ // this is probably the remains of the 'basic' skill,
+ // which has since been partially split into emote, trade, and party,
+ // but the confusion is caused by the fact that it also covered attacks.
+ ONE = 0x0001,
+
+ // Basic skills.
+ // These should probably be made unconditional.
+ NV_EMOTE = 1, //
+ NV_TRADE = 2, //
+ NV_PARTY = 3, //
+
+ AC_OWL = 45, // Mallard's Eye
+
+ NPC_SELFDESTRUCTION = 175, //
+
+ NPC_POISON = 178, //
+
+ NPC_SUMMONSLAVE = 198, //
+ NPC_EMOTION = 199, //
+
+ TMW_SKILLPOOL = 339, // skill pool size
+
+ // magic skills
+ TMW_MAGIC = 340, //
+ TMW_MAGIC_LIFE = 341, //
+ TMW_MAGIC_WAR = 342, //
+ TMW_MAGIC_TRANSMUTE = 343, //
+ TMW_MAGIC_NATURE = 344, //
+ TMW_MAGIC_ETHER = 345, //
+ TMW_MAGIC_DARK = 346, //
+ TMW_MAGIC_LIGHT = 347, //
+
+ // focusable skills
+ TMW_BRAWLING = 350, //
+ TMW_LUCKY_COUNTER = 351, //
+ TMW_SPEED = 352, //
+ TMW_RESIST_POISON = 353, //
+ TMW_ASTRAL_SOUL = 354, //
+ TMW_RAGING = 355, //
+
+ // Note: this value is also hard-coded in common/mmo.hpp
+ MAX_SKILL_DB = 474, // not 450
+};
+
+namespace e
+{
+enum class SkillFlags : uint16_t
+{
+ ZERO = 0x00,
+ // is a pool skill
+ POOL_FLAG = 0x01,
+ // is an active pool skill
+ POOL_ACTIVE = 0x02,
+ // pool skill has been activated (used for clif)
+ POOL_ACTIVATED = 0x04,
+};
+ENUM_BITWISE_OPERATORS(SkillFlags)
+}
+using e::SkillFlags;
+} // namespace tmwa
diff --git a/src/mmo/strs.cpp b/src/mmo/strs.cpp
index 71dceec..d780702 100644
--- a/src/mmo/strs.cpp
+++ b/src/mmo/strs.cpp
@@ -18,9 +18,15 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
+#include "../io/cxxstdio.hpp"
+
#include "../poison.hpp"
namespace tmwa
{
+VString<49> convert_for_printf(NpcEvent ev)
+{
+ return STRNPRINTF(50, "%s::%s"_fmt, ev.npc, ev.label);
+}
} // namespace tmwa
diff --git a/src/mmo/strs.hpp b/src/mmo/strs.hpp
index fea0c98..6a132c2 100644
--- a/src/mmo/strs.hpp
+++ b/src/mmo/strs.hpp
@@ -123,4 +123,37 @@ CharName stringish<CharName>(VString<23> iv)
{
return CharName(iv);
}
+
+struct MobName : VString<23> {};
+struct NpcName : VString<23> {};
+struct ScriptLabel : VString<23> {};
+struct ItemName : VString<23> {};
+
+// formerly VString<49>, as name::label
+struct NpcEvent
+{
+ NpcName npc;
+ ScriptLabel label;
+
+ explicit operator bool()
+ {
+ return npc || label;
+ }
+ bool operator !()
+ {
+ return !bool(*this);
+ }
+
+ friend bool operator == (const NpcEvent& l, const NpcEvent& r)
+ {
+ return l.npc == r.npc && l.label == r.label;
+ }
+
+ friend bool operator < (const NpcEvent& l, const NpcEvent& r)
+ {
+ return l.npc < r.npc || (l.npc == r.npc && l.label < r.label);
+ }
+
+ friend VString<49> convert_for_printf(NpcEvent ev);
+};
} // namespace tmwa
diff --git a/src/mmo/utils.cpp b/src/mmo/utils.cpp
deleted file mode 100644
index f8aff2e..0000000
--- a/src/mmo/utils.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-#include "utils.hpp"
-// utils.cpp - Useful stuff that hasn't been categorized.
-//
-// Copyright © ????-2004 Athena Dev Teams
-// Copyright © 2004-2011 The Mana World Development Team
-// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include <sys/time.h>
-
-#include <algorithm>
-
-#include "../strings/zstring.hpp"
-#include "../strings/xstring.hpp"
-
-#include "../io/cxxstdio.hpp"
-#include "../io/write.hpp"
-
-#include "extract.hpp"
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-//---------------------------------------------------
-// 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(".."_s))
- return 0;
- if (email.contains_any(" ;"_s))
- 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"_s || str == "on"_s || str == "yes"_s
- || str == "oui"_s || str == "ja"_s
- || str == "si"_s)
- return 1;
- if (str == "false"_s || str == "off"_s || str == "no"_s
- || str == "non"_s || str == "nein"_s)
- return 0;
-
- int rv;
- if (extract(str, &rv))
- return rv;
- FPRINTF(stderr, "Fatal: bad option value %s"_fmt, 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<timestamp_seconds_buffer>(VString<19>(strings::really_construct_from_a_pointer, buf));
-}
-void stamp_time(timestamp_milliseconds_buffer& out)
-{
- struct timeval tv;
- gettimeofday(&tv, nullptr);
- struct tm when = TimeT(tv.tv_sec);
- char buf[24];
- strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &when);
- sprintf(buf + 19, ".%03d", static_cast<int>(tv.tv_usec / 1000));
- out = stringish<timestamp_milliseconds_buffer>(VString<23>(strings::really_construct_from_a_pointer, 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);
-}
-} // namespace tmwa
diff --git a/src/mmo/utils.hpp b/src/mmo/utils.hpp
deleted file mode 100644
index fc3ea74..0000000
--- a/src/mmo/utils.hpp
+++ /dev/null
@@ -1,167 +0,0 @@
-#pragma once
-// utils.hpp - Useful stuff that hasn't been categorized.
-//
-// Copyright © ????-2004 Athena Dev Teams
-// Copyright © 2004-2011 The Mana World Development Team
-// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "fwd.hpp"
-
-#include <cstring>
-#include <ctime>
-
-#include <type_traits>
-
-#include "../ints/little.hpp"
-
-#include "../strings/fwd.hpp"
-#include "../strings/vstring.hpp"
-
-#include "../generic/operators.hpp"
-
-#include "../io/fwd.hpp"
-
-
-namespace tmwa
-{
-template<class T>
-struct is_trivially_copyable
-: std::integral_constant<bool,
- // come back when GCC actually implements the public traits properly
- __has_trivial_copy(T)
- && __has_trivial_assign(T)
- && __has_trivial_destructor(T)>
-{};
-
-bool e_mail_check(XString email);
-int config_switch (ZString str);
-
-template<class T>
-void really_memzero_this(T *v)
-{
- static_assert(is_trivially_copyable<T>::value, "only for mostly-pod types");
- static_assert(std::is_class<T>::value || std::is_union<T>::value, "Only for user-defined structures (for now)");
- memset(v, '\0', sizeof(*v));
-}
-template<class T, size_t n>
-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<class T>
- TimeT(T) = delete;
- template<class T>
- operator T() const = delete;
-
- static
- TimeT now()
- {
- // poisoned, but this is still in header-land
- return time(nullptr);
- }
-
- bool error() const
- {
- return value == -1;
- }
- bool okay() const
- {
- return !error();
- }
-};
-
-inline
-long long convert_for_printf(TimeT t)
-{
- return t.value;
-}
-
-// 2038 problem
-inline __attribute__((warn_unused_result))
-bool native_to_network(Little32 *net, TimeT nat)
-{
- time_t tmp = nat;
- return native_to_network(net, static_cast<uint32_t>(tmp));
-}
-
-inline __attribute__((warn_unused_result))
-bool network_to_native(TimeT *nat, Little32 net)
-{
- uint32_t tmp;
- bool rv = network_to_native(&tmp, net);
- *nat = static_cast<time_t>(tmp);
- return rv;
-}
-
-inline __attribute__((warn_unused_result))
-bool native_to_network(Little64 *net, TimeT nat)
-{
- time_t tmp = nat;
- return native_to_network(net, static_cast<uint64_t>(tmp));
-}
-
-inline __attribute__((warn_unused_result))
-bool network_to_native(TimeT *nat, Little64 net)
-{
- uint64_t tmp;
- bool rv = network_to_native(&tmp, net);
- *nat = static_cast<time_t>(tmp);
- return rv;
-}
-
-
-struct timestamp_seconds_buffer : VString<19> {};
-struct timestamp_milliseconds_buffer : VString<23> {};
-void stamp_time(timestamp_seconds_buffer&, const TimeT *t=nullptr);
-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<timestamp_seconds_buffer *>( \
- str + sizeof(str) \
- )[-1], \
- &t \
- )
-} // namespace tmwa
diff --git a/src/mmo/version.cpp b/src/mmo/version.cpp
index 2e337c1..f91b748 100644
--- a/src/mmo/version.cpp
+++ b/src/mmo/version.cpp
@@ -24,7 +24,7 @@
#include "../strings/xstring.hpp"
-#include "extract.hpp"
+#include "../io/extract.hpp"
#include "../poison.hpp"
@@ -66,11 +66,36 @@ Version CURRENT_MAP_SERVER_VERSION =
LString CURRENT_VERSION_STRING = VERSION_STRING;
-bool extract(XString str, Version *vers)
+bool impl_extract(XString str, Version *vers)
{
*vers = {};
- // TODO should I try to extract dev and vend also?
- // It would've been useful during the magic migration.
+ // versions look like:
+ // 1.2.3 (release)
+ // 1.2.3+5 (vendor patches)
+ // 1.2.3-4 (dev patches)
+ // 1.2.3-4+5 (dev patches + vendor patches)
+ XString a, b;
+ if (extract(str, record<'+'>(&a, &b)))
+ {
+ if (!extract(b, &vers->vend))
+ {
+ return false;
+ }
+ str = a;
+ }
+ if (extract(str, record<'-'>(&a, &b)))
+ {
+ if (!extract(b, &vers->devel))
+ {
+ return false;
+ }
+ str = a;
+ }
return extract(str, record<'.'>(&vers->major, &vers->minor, &vers->patch));
}
+
+LString VERSION_INFO_HEADER = "This server code consists of Free Software under GPL3&AGPL3"_s;
+LString VERSION_INFO_COMMIT = "This is commit " VERSION_HASH ", also known as " VERSION_FULL ""_s;
+LString VERSION_INFO_NUMBER = "The version is " VERSION_STRING ""_s;
+LString VERSION_INFO_URL = "For source, see [@@" VENDOR_SOURCE "|" VENDOR_SOURCE "@@]"_s;
} // namespace tmwa
diff --git a/src/mmo/version.hpp b/src/mmo/version.hpp
index 440dce6..a09953f 100644
--- a/src/mmo/version.hpp
+++ b/src/mmo/version.hpp
@@ -24,8 +24,6 @@
#include <cstdint>
-#include "../strings/fwd.hpp"
-
namespace tmwa
{
@@ -89,5 +87,10 @@ extern Version CURRENT_MAP_SERVER_VERSION;
extern LString CURRENT_VERSION_STRING;
-bool extract(XString str, Version *vers);
+bool impl_extract(XString str, Version *vers);
+
+extern LString VERSION_INFO_HEADER;
+extern LString VERSION_INFO_COMMIT;
+extern LString VERSION_INFO_NUMBER;
+extern LString VERSION_INFO_URL;
} // namespace tmwa