From c87b1dc338eadc68accac02563a487d7d8e1c9a0 Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Mon, 12 May 2014 17:24:51 -0700 Subject: Split net/ from mmo/ --- src/admin/ladmin.cpp | 5 +- src/char/char.cpp | 5 +- src/char/char.hpp | 3 +- src/char/int_party.cpp | 3 +- src/char/int_party.hpp | 2 + src/char/int_storage.cpp | 3 +- src/char/int_storage.hpp | 2 + src/char/inter.cpp | 3 +- src/char/inter.hpp | 2 +- src/compat/time_t.cpp | 21 ++ src/compat/time_t.hpp | 27 ++ src/generic/dumb_ptr.cpp | 21 ++ src/generic/dumb_ptr.hpp | 271 ++++++++++++++++++++ src/generic/fwd.hpp | 3 + src/login/login.cpp | 7 +- src/map/atcommand.cpp | 5 +- src/map/atcommand.hpp | 5 +- src/map/battle.hpp | 5 +- src/map/chrif.cpp | 7 +- src/map/chrif.hpp | 5 +- src/map/clif.cpp | 7 +- src/map/clif.hpp | 6 +- src/map/intif.cpp | 3 +- src/map/intif.hpp | 5 +- src/map/magic-expr.cpp | 3 +- src/map/magic-expr.hpp | 2 +- src/map/magic-interpreter-base.cpp | 2 +- src/map/magic-interpreter-base.hpp | 4 +- src/map/magic-interpreter.hpp | 6 +- src/map/magic-stmt.cpp | 2 +- src/map/magic-stmt.hpp | 4 +- src/map/magic-v2.cpp | 4 +- src/map/magic.cpp | 4 +- src/map/magic.hpp | 2 +- src/map/map.cpp | 5 +- src/map/map.hpp | 6 +- src/map/mob.cpp | 5 +- src/map/mob.hpp | 5 +- src/map/npc.cpp | 3 +- src/map/npc.hpp | 4 +- src/map/party.cpp | 3 +- src/map/party.hpp | 3 +- src/map/pc.cpp | 5 +- src/map/pc.hpp | 2 + src/map/script.cpp | 5 +- src/map/script.hpp | 3 +- src/map/skill.cpp | 3 +- src/map/skill.hpp | 1 + src/map/storage.hpp | 3 +- src/map/tmw.hpp | 2 +- src/map/trade.hpp | 2 +- src/mmo/core.cpp | 4 +- src/mmo/dumb_ptr.cpp | 21 -- src/mmo/dumb_ptr.hpp | 271 -------------------- src/mmo/fwd.hpp | 5 - src/mmo/ip.cpp | 116 --------- src/mmo/ip.hpp | 165 ------------ src/mmo/ip.py | 14 - src/mmo/ip_test.cpp | 355 -------------------------- src/mmo/md5more.cpp | 3 +- src/mmo/md5more.hpp | 2 + src/mmo/mmo.hpp | 3 +- src/mmo/socket.cpp | 509 ------------------------------------ src/mmo/socket.hpp | 424 ------------------------------ src/mmo/timer.cpp | 217 ---------------- src/mmo/timer.hpp | 50 ---- src/mmo/timer.t.hpp | 88 ------- src/net/fwd.hpp | 30 +++ src/net/ip.cpp | 116 +++++++++ src/net/ip.hpp | 165 ++++++++++++ src/net/ip.py | 14 + src/net/ip_test.cpp | 355 ++++++++++++++++++++++++++ src/net/socket.cpp | 511 +++++++++++++++++++++++++++++++++++++ src/net/socket.hpp | 425 ++++++++++++++++++++++++++++++ src/net/timer.cpp | 217 ++++++++++++++++ src/net/timer.hpp | 50 ++++ src/net/timer.t.hpp | 88 +++++++ 77 files changed, 2434 insertions(+), 2303 deletions(-) create mode 100644 src/compat/time_t.cpp create mode 100644 src/compat/time_t.hpp create mode 100644 src/generic/dumb_ptr.cpp create mode 100644 src/generic/dumb_ptr.hpp delete mode 100644 src/mmo/dumb_ptr.cpp delete mode 100644 src/mmo/dumb_ptr.hpp delete mode 100644 src/mmo/ip.cpp delete mode 100644 src/mmo/ip.hpp delete mode 100644 src/mmo/ip.py delete mode 100644 src/mmo/ip_test.cpp delete mode 100644 src/mmo/socket.cpp delete mode 100644 src/mmo/socket.hpp delete mode 100644 src/mmo/timer.cpp delete mode 100644 src/mmo/timer.hpp delete mode 100644 src/mmo/timer.t.hpp create mode 100644 src/net/fwd.hpp create mode 100644 src/net/ip.cpp create mode 100644 src/net/ip.hpp create mode 100644 src/net/ip.py create mode 100644 src/net/ip_test.cpp create mode 100644 src/net/socket.cpp create mode 100644 src/net/socket.hpp create mode 100644 src/net/timer.cpp create mode 100644 src/net/timer.hpp create mode 100644 src/net/timer.t.hpp diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp index cf5735b..a4c0a36 100644 --- a/src/admin/ladmin.cpp +++ b/src/admin/ladmin.cpp @@ -39,12 +39,13 @@ #include "../io/tty.hpp" #include "../io/write.hpp" +#include "../net/ip.hpp" +#include "../net/socket.hpp" + #include "../mmo/config_parse.hpp" #include "../mmo/core.hpp" #include "../mmo/human_time_diff.hpp" -#include "../mmo/ip.hpp" #include "../mmo/mmo.hpp" -#include "../mmo/socket.hpp" #include "../mmo/utils.hpp" #include "../mmo/version.hpp" diff --git a/src/char/char.cpp b/src/char/char.cpp index ae7a10a..e84de03 100644 --- a/src/char/char.cpp +++ b/src/char/char.cpp @@ -54,13 +54,14 @@ #include "../io/tty.hpp" #include "../io/write.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" + #include "../mmo/config_parse.hpp" #include "../mmo/core.hpp" #include "../mmo/extract.hpp" #include "../mmo/human_time_diff.hpp" #include "../mmo/mmo.hpp" -#include "../mmo/socket.hpp" -#include "../mmo/timer.hpp" #include "../mmo/utils.hpp" #include "../mmo/version.hpp" diff --git a/src/char/char.hpp b/src/char/char.hpp index 64d4254..3ba455f 100644 --- a/src/char/char.hpp +++ b/src/char/char.hpp @@ -27,7 +27,8 @@ # include "../generic/array.hpp" -# include "../mmo/ip.hpp" +# include "../net/ip.hpp" + # include "../mmo/mmo.hpp" constexpr int MAX_MAP_SERVERS = 30; diff --git a/src/char/int_party.cpp b/src/char/int_party.cpp index 1084bed..472e493 100644 --- a/src/char/int_party.cpp +++ b/src/char/int_party.cpp @@ -33,10 +33,11 @@ #include "../io/read.hpp" #include "../io/write.hpp" +#include "../net/socket.hpp" + #include "../mmo/extract.hpp" #include "../mmo/ids.hpp" #include "../mmo/mmo.hpp" -#include "../mmo/socket.hpp" #include "char.hpp" #include "inter.hpp" diff --git a/src/char/int_party.hpp b/src/char/int_party.hpp index 3c448b0..ab42501 100644 --- a/src/char/int_party.hpp +++ b/src/char/int_party.hpp @@ -25,6 +25,8 @@ # include "../strings/fwd.hpp" +# include "../net/fwd.hpp" + # include "../mmo/fwd.hpp" void inter_party_init(void); diff --git a/src/char/int_storage.cpp b/src/char/int_storage.cpp index 850d99e..82aa07f 100644 --- a/src/char/int_storage.cpp +++ b/src/char/int_storage.cpp @@ -32,9 +32,10 @@ #include "../io/read.hpp" #include "../io/write.hpp" +#include "../net/socket.hpp" + #include "../mmo/extract.hpp" #include "../mmo/mmo.hpp" -#include "../mmo/socket.hpp" #include "../poison.hpp" diff --git a/src/char/int_storage.hpp b/src/char/int_storage.hpp index 9f241e3..83981b6 100644 --- a/src/char/int_storage.hpp +++ b/src/char/int_storage.hpp @@ -25,6 +25,8 @@ # include "../strings/fwd.hpp" +# include "../net/fwd.hpp" + # include "../mmo/fwd.hpp" void inter_storage_init(void); diff --git a/src/char/inter.cpp b/src/char/inter.cpp index 621134f..a7617c9 100644 --- a/src/char/inter.cpp +++ b/src/char/inter.cpp @@ -38,9 +38,10 @@ #include "../io/read.hpp" #include "../io/write.hpp" +#include "../net/socket.hpp" + #include "../mmo/extract.hpp" #include "../mmo/mmo.hpp" -#include "../mmo/socket.hpp" #include "char.hpp" #include "int_party.hpp" diff --git a/src/char/inter.hpp b/src/char/inter.hpp index 3e48460..bb3c9da 100644 --- a/src/char/inter.hpp +++ b/src/char/inter.hpp @@ -25,7 +25,7 @@ # include "../strings/fwd.hpp" -# include "../mmo/fwd.hpp" +# include "../net/fwd.hpp" bool inter_config(XString key, ZString value); void inter_init2(); diff --git a/src/compat/time_t.cpp b/src/compat/time_t.cpp new file mode 100644 index 0000000..b1a3fb8 --- /dev/null +++ b/src/compat/time_t.cpp @@ -0,0 +1,21 @@ +#include "time_t.hpp" +// time_t.cpp - time_t with a reliable representation +// +// Copyright © 2013-2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../poison.hpp" diff --git a/src/compat/time_t.hpp b/src/compat/time_t.hpp new file mode 100644 index 0000000..4e0ffb6 --- /dev/null +++ b/src/compat/time_t.hpp @@ -0,0 +1,27 @@ +#ifndef TMWA_COMPAT_TIMET_HPP +#define TMWA_COMPAT_TIMET_HPP +// time_t.hpp - time_t with a reliable representation +// +// Copyright © 2013-2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +# include "fwd.hpp" + +// TODO fix this ordering violation by promoting TimeT here +# include "../mmo/utils.hpp" + +#endif // TMWA_COMPAT_TIMET_HPP diff --git a/src/generic/dumb_ptr.cpp b/src/generic/dumb_ptr.cpp new file mode 100644 index 0000000..77e3080 --- /dev/null +++ b/src/generic/dumb_ptr.cpp @@ -0,0 +1,21 @@ +#include "dumb_ptr.hpp" +// dumb_ptr.cpp - dummy file to make Make dependencies work +// +// Copyright © 2013 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../poison.hpp" diff --git a/src/generic/dumb_ptr.hpp b/src/generic/dumb_ptr.hpp new file mode 100644 index 0000000..ed18317 --- /dev/null +++ b/src/generic/dumb_ptr.hpp @@ -0,0 +1,271 @@ +#ifndef TMWA_GENERIC_DUMB_PTR_HPP +#define TMWA_GENERIC_DUMB_PTR_HPP +// dumb_ptr.hpp - temporary new/delete wrappers +// +// Copyright © 2013 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +# include "fwd.hpp" + +# include + +# include +# include + +# include "../strings/astring.hpp" +# include "../strings/zstring.hpp" +# include "../strings/xstring.hpp" + +// unmanaged new/delete-able pointer +// should be replaced by std::unique_ptr +template +class dumb_ptr +{ + template + friend class dumb_ptr; + T *impl; +public: + explicit + dumb_ptr(T *p=nullptr) + : impl(p) + {} + template + dumb_ptr(dumb_ptr p) + : impl(p.impl) + {} + dumb_ptr(std::nullptr_t) + : impl(nullptr) + {} + + void delete_() + { + delete impl; + *this = nullptr; + } + template + void new_(A&&... a) + { + impl = new T(std::forward(a)...); + } + template + static + dumb_ptr make(A&&... a) + { + return dumb_ptr(new T(std::forward(a)...)); + } + dumb_ptr& operator = (std::nullptr_t) + { + impl = nullptr; + return *this; + } + + T& operator *() const + { + return *impl; + } + T *operator->() const + { + return impl; + } + + explicit + operator bool() const + { + return impl; + } + bool operator !() const + { + return !impl; + } + + friend bool operator == (dumb_ptr l, dumb_ptr r) + { + return l.impl == r.impl; + } + friend bool operator != (dumb_ptr l, dumb_ptr r) + { + return !(l == r); + } +}; + +// unmanaged new/delete-able pointer +// should be replaced by std::unique_ptr or std::vector +template +class dumb_ptr +{ + T *impl; + size_t sz; +public: + dumb_ptr() : impl(), sz() {} + dumb_ptr(std::nullptr_t) + : impl(nullptr), sz(0) {} + dumb_ptr(T *p, size_t z) + : impl(p) + , sz(z) + {} + + void delete_() + { + delete[] impl; + *this = nullptr; + } + void new_(size_t z) + { + impl = new T[z](); + sz = z; + } + static + dumb_ptr make(size_t z) + { + return dumb_ptr(new T[z](), z); + } + dumb_ptr& operator = (std::nullptr_t) + { + impl = nullptr; + sz = 0; + return *this; + } + + size_t size() const + { + return sz; + } + void resize(size_t z) + { + if (z == sz) + return; + T *np = new T[z](); + // not exception-safe, but we don't have a dtor anyway + size_t i = std::min(z, sz); + while (i-->0) + np[i] = std::move(impl[i]); + delete[] impl; + impl = np; + sz = z; + } + + T& operator[](size_t i) const + { + return impl[i]; + } + + explicit + operator bool() const + { + return impl; + } + bool operator !() const + { + return !impl; + } + + friend bool operator == (dumb_ptr l, dumb_ptr r) + { + return l.impl == r.impl; + } + friend bool operator != (dumb_ptr l, dumb_ptr r) + { + return !(l == r); + } +}; + +struct dumb_string +{ + dumb_ptr impl; + + dumb_string() + : impl() + {} + dumb_string(char *) = delete; + // copy ctor, copy assign, and dtor are all default + + static dumb_string copy(const char *b, const char *e) + { + dumb_string rv; + rv.impl.new_((e - b) + 1); + std::copy(b, e, &rv.impl[0]); + return rv; + } + static dumb_string copys(XString s) + { + return dumb_string::copy(&*s.begin(), &*s.end()); + } + static +# ifndef __clang__ + __attribute__((warning("shouldn't use this - slice instead"))) +# endif + dumb_string copyn(const char *sn, size_t n) + { + return dumb_string::copy(sn, sn + strnlen(sn, n)); + } + + static + dumb_string fake(ZString p) + { + dumb_string rv; + size_t len = p.size(); + rv.impl = dumb_ptr(const_cast(p.c_str()), len); + return rv; + } + + dumb_string dup() const + { + return dumb_string::copy(&impl[0], &impl[0] + impl.size()); + } + void delete_() + { + impl.delete_(); + } + + const char *c_str() const + { + return &impl[0]; + } + + operator ZString() const + { + return ZString(strings::really_construct_from_a_pointer, c_str(), nullptr); + } + + AString str() const + { + return ZString(*this); + } + + char& operator[](size_t i) const + { + return impl[i]; + } + + explicit + operator bool() const + { + return bool(impl); + } + bool operator !() const + { + return !impl; + } +}; + +inline +const char *convert_for_printf(dumb_string ds) +{ + return ds.c_str(); +} + +#endif // TMWA_GENERIC_DUMB_PTR_HPP diff --git a/src/generic/fwd.hpp b/src/generic/fwd.hpp index df8485f..fcb1fab 100644 --- a/src/generic/fwd.hpp +++ b/src/generic/fwd.hpp @@ -22,5 +22,8 @@ # include "../sanity.hpp" // meh, add more when I feel like it +template +class dumb_ptr; +class dumb_string; #endif // TMWA_GENERIC_FWD_HPP diff --git a/src/login/login.cpp b/src/login/login.cpp index 6b503af..96cd416 100644 --- a/src/login/login.cpp +++ b/src/login/login.cpp @@ -50,6 +50,9 @@ #include "../io/tty.hpp" #include "../io/write.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" + #include "../mmo/config_parse.hpp" #include "../mmo/core.hpp" #include "../mmo/extract.hpp" @@ -57,10 +60,8 @@ #include "../mmo/ids.hpp" #include "../mmo/md5more.hpp" #include "../mmo/mmo.hpp" -#include "../mmo/socket.hpp" -#include "../mmo/timer.hpp" -#include "../mmo/version.hpp" #include "../mmo/utils.hpp" +#include "../mmo/version.hpp" #include "../poison.hpp" diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index 2f08627..733050c 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -42,14 +42,15 @@ #include "../io/read.hpp" #include "../io/write.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" + #include "../mmo/config_parse.hpp" #include "../mmo/core.hpp" #include "../mmo/extract.hpp" #include "../mmo/human_time_diff.hpp" #include "../mmo/ids.hpp" #include "../mmo/mmo.hpp" -#include "../mmo/socket.hpp" -#include "../mmo/timer.hpp" #include "../mmo/utils.hpp" #include "../mmo/version.hpp" diff --git a/src/map/atcommand.hpp b/src/map/atcommand.hpp index 0a24163..a2aaf5b 100644 --- a/src/map/atcommand.hpp +++ b/src/map/atcommand.hpp @@ -25,8 +25,11 @@ # include "../strings/fwd.hpp" +# include "../generic/fwd.hpp" + +# include "../net/fwd.hpp" + # include "../mmo/fwd.hpp" -# include "../mmo/dumb_ptr.hpp" bool is_atcommand(Session *s, dumb_ptr sd, ZString message, GmLevel gmlvl); diff --git a/src/map/battle.hpp b/src/map/battle.hpp index 6e73e70..b448073 100644 --- a/src/map/battle.hpp +++ b/src/map/battle.hpp @@ -27,8 +27,9 @@ # include "../strings/fwd.hpp" -# include "../mmo/dumb_ptr.hpp" -# include "../mmo/timer.t.hpp" +# include "../generic/fwd.hpp" + +# include "../net/timer.t.hpp" # include "magic-interpreter.t.hpp" # include "map.t.hpp" diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp index 50c62a8..b5c5bf6 100644 --- a/src/map/chrif.cpp +++ b/src/map/chrif.cpp @@ -28,11 +28,12 @@ #include "../io/cxxstdio.hpp" +#include "../net/ip.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" + #include "../mmo/human_time_diff.hpp" -#include "../mmo/ip.hpp" #include "../mmo/mmo.hpp" -#include "../mmo/socket.hpp" -#include "../mmo/timer.hpp" #include "../mmo/utils.hpp" #include "battle.hpp" diff --git a/src/map/chrif.hpp b/src/map/chrif.hpp index 7ac16e5..321b3bd 100644 --- a/src/map/chrif.hpp +++ b/src/map/chrif.hpp @@ -25,8 +25,11 @@ # include "../strings/fwd.hpp" +# include "../generic/fwd.hpp" + +# include "../net/fwd.hpp" + # include "../mmo/fwd.hpp" -# include "../mmo/dumb_ptr.hpp" void chrif_setuserid(AccountName); void chrif_setpasswd(AccountPass); diff --git a/src/map/clif.cpp b/src/map/clif.cpp index 0a6406b..ce328f6 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -38,10 +38,11 @@ #include "../io/cxxstdio.hpp" #include "../io/write.hpp" -#include "../mmo/ip.hpp" +#include "../net/ip.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" + #include "../mmo/md5more.hpp" -#include "../mmo/socket.hpp" -#include "../mmo/timer.hpp" #include "../mmo/utils.hpp" #include "../mmo/version.hpp" diff --git a/src/map/clif.hpp b/src/map/clif.hpp index 19d5fd3..b2a5747 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -29,10 +29,12 @@ # include "../strings/fwd.hpp" +# include "../generic/fwd.hpp" + +# include "../net/timer.t.hpp" + # include "../mmo/fwd.hpp" -# include "../mmo/dumb_ptr.hpp" # include "../mmo/mmo.hpp" -# include "../mmo/timer.t.hpp" # include "battle.t.hpp" # include "map.t.hpp" diff --git a/src/map/intif.cpp b/src/map/intif.cpp index d0b1ea0..57c6048 100644 --- a/src/map/intif.cpp +++ b/src/map/intif.cpp @@ -29,8 +29,9 @@ #include "../io/cxxstdio.hpp" +#include "../net/socket.hpp" + #include "../mmo/mmo.hpp" -#include "../mmo/socket.hpp" #include "battle.hpp" #include "chrif.hpp" diff --git a/src/map/intif.hpp b/src/map/intif.hpp index e05d00f..c60e3c1 100644 --- a/src/map/intif.hpp +++ b/src/map/intif.hpp @@ -25,8 +25,11 @@ # include "../strings/fwd.hpp" +# include "../generic/fwd.hpp" + +# include "../net/fwd.hpp" + # include "../mmo/fwd.hpp" -# include "../mmo/dumb_ptr.hpp" int intif_parse(Session *); diff --git a/src/map/magic-expr.cpp b/src/map/magic-expr.cpp index ee422c1..2ae9b20 100644 --- a/src/map/magic-expr.cpp +++ b/src/map/magic-expr.cpp @@ -29,12 +29,11 @@ #include "../strings/vstring.hpp" #include "../strings/literal.hpp" +#include "../generic/dumb_ptr.hpp" #include "../generic/random.hpp" #include "../io/cxxstdio.hpp" -#include "../mmo/dumb_ptr.hpp" - #include "battle.hpp" #include "itemdb.hpp" #include "magic-expr-eval.hpp" diff --git a/src/map/magic-expr.hpp b/src/map/magic-expr.hpp index 7276fd2..1c4d00e 100644 --- a/src/map/magic-expr.hpp +++ b/src/map/magic-expr.hpp @@ -22,7 +22,7 @@ # include "fwd.hpp" -# include "../mmo/dumb_ptr.hpp" +# include "../generic/fwd.hpp" # include "../range/fwd.hpp" diff --git a/src/map/magic-interpreter-base.cpp b/src/map/magic-interpreter-base.cpp index baec6fb..b2ed0a5 100644 --- a/src/map/magic-interpreter-base.cpp +++ b/src/map/magic-interpreter-base.cpp @@ -26,7 +26,7 @@ #include "../io/cxxstdio.hpp" -#include "../mmo/timer.hpp" +#include "../net/timer.hpp" #include "magic.hpp" #include "magic-expr.hpp" diff --git a/src/map/magic-interpreter-base.hpp b/src/map/magic-interpreter-base.hpp index 1baefc6..7d75049 100644 --- a/src/map/magic-interpreter-base.hpp +++ b/src/map/magic-interpreter-base.hpp @@ -24,7 +24,9 @@ # include "../strings/fwd.hpp" -# include "../mmo/dumb_ptr.hpp" +# include "../generic/fwd.hpp" + +# include "../mmo/fwd.hpp" extern magic_conf_t magic_conf; /* Global magic conf */ extern env_t magic_default_env; /* Fake default environment */ diff --git a/src/map/magic-interpreter.hpp b/src/map/magic-interpreter.hpp index 8861e92..cf0991e 100644 --- a/src/map/magic-interpreter.hpp +++ b/src/map/magic-interpreter.hpp @@ -31,9 +31,11 @@ # include "../strings/fwd.hpp" # include "../strings/rstring.hpp" -# include "../mmo/dumb_ptr.hpp" +# include "../generic/fwd.hpp" + +# include "../net/timer.t.hpp" + # include "../mmo/ids.hpp" -# include "../mmo/timer.t.hpp" # include "../mmo/utils.hpp" # include "map.hpp" diff --git a/src/map/magic-stmt.cpp b/src/map/magic-stmt.cpp index 98cdb7e..f87eabf 100644 --- a/src/map/magic-stmt.cpp +++ b/src/map/magic-stmt.cpp @@ -30,7 +30,7 @@ #include "../io/cxxstdio.hpp" -#include "../mmo/timer.hpp" +#include "../net/timer.hpp" #include "battle.hpp" #include "clif.hpp" diff --git a/src/map/magic-stmt.hpp b/src/map/magic-stmt.hpp index 08c2b28..97fdf1f 100644 --- a/src/map/magic-stmt.hpp +++ b/src/map/magic-stmt.hpp @@ -24,7 +24,9 @@ # include "../range/fwd.hpp" -# include "../mmo/dumb_ptr.hpp" +# include "../strings/zstring.hpp" + +# include "../generic/fwd.hpp" # include "skill.t.hpp" diff --git a/src/map/magic-v2.cpp b/src/map/magic-v2.cpp index 510c619..1e2a330 100644 --- a/src/map/magic-v2.cpp +++ b/src/map/magic-v2.cpp @@ -27,13 +27,13 @@ #include "../strings/rstring.hpp" #include "../strings/literal.hpp" +#include "../generic/dumb_ptr.hpp" + #include "../io/cxxstdio.hpp" #include "../io/line.hpp" #include "../sexpr/parser.hpp" -#include "../mmo/dumb_ptr.hpp" - #include "itemdb.hpp" #include "magic-expr.hpp" #include "magic-interpreter.hpp" diff --git a/src/map/magic.cpp b/src/map/magic.cpp index 88ee1f1..b502d3e 100644 --- a/src/map/magic.cpp +++ b/src/map/magic.cpp @@ -24,9 +24,9 @@ #include "../strings/xstring.hpp" -#include "../io/cxxstdio.hpp" +#include "../generic/dumb_ptr.hpp" -#include "../mmo/dumb_ptr.hpp" +#include "../io/cxxstdio.hpp" #include "magic-expr.hpp" #include "magic-interpreter.hpp" diff --git a/src/map/magic.hpp b/src/map/magic.hpp index cd0ff99..e54661b 100644 --- a/src/map/magic.hpp +++ b/src/map/magic.hpp @@ -24,7 +24,7 @@ # include "../strings/fwd.hpp" -# include "../mmo/dumb_ptr.hpp" +# include "../generic/fwd.hpp" # include "map.t.hpp" # include "skill.t.hpp" diff --git a/src/map/map.cpp b/src/map/map.cpp index b845abc..dde617a 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -49,11 +49,12 @@ #include "../io/tty.hpp" #include "../io/write.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" + #include "../mmo/config_parse.hpp" #include "../mmo/core.hpp" #include "../mmo/extract.hpp" -#include "../mmo/socket.hpp" -#include "../mmo/timer.hpp" #include "../mmo/utils.hpp" #include "../mmo/version.hpp" diff --git a/src/map/map.hpp b/src/map/map.hpp index 2bc8e45..78d12f8 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -37,10 +37,12 @@ # include "../strings/vstring.hpp" # include "../generic/db.hpp" +# include "../generic/dumb_ptr.hpp" # include "../generic/matrix.hpp" -# include "../mmo/socket.hpp" -# include "../mmo/timer.t.hpp" +# include "../net/socket.hpp" +# include "../net/timer.t.hpp" + # include "../mmo/utils.hpp" # include "battle.t.hpp" diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 923b369..731448b 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -38,10 +38,11 @@ #include "../io/cxxstdio.hpp" #include "../io/read.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" + #include "../mmo/config_parse.hpp" #include "../mmo/extract.hpp" -#include "../mmo/socket.hpp" -#include "../mmo/timer.hpp" #include "battle.hpp" #include "clif.hpp" diff --git a/src/map/mob.hpp b/src/map/mob.hpp index 48d7df5..8860ab5 100644 --- a/src/map/mob.hpp +++ b/src/map/mob.hpp @@ -25,10 +25,11 @@ # include "mob.t.hpp" -# include "../generic/random.t.hpp" +# include "../generic/fwd.hpp" # include "../generic/enum.hpp" +# include "../generic/random.t.hpp" -# include "../mmo/timer.t.hpp" +# include "../net/timer.t.hpp" # include "battle.t.hpp" # include "clif.t.hpp" diff --git a/src/map/npc.cpp b/src/map/npc.cpp index 32711d0..aee3d03 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -40,9 +40,10 @@ #include "../io/cxxstdio.hpp" #include "../io/read.hpp" +#include "../net/timer.hpp" + #include "../mmo/config_parse.hpp" #include "../mmo/extract.hpp" -#include "../mmo/timer.hpp" #include "../mmo/utils.hpp" #include "battle.hpp" diff --git a/src/map/npc.hpp b/src/map/npc.hpp index d8930c8..011ea50 100644 --- a/src/map/npc.hpp +++ b/src/map/npc.hpp @@ -27,7 +27,9 @@ # include "../strings/fwd.hpp" -# include "../mmo/timer.t.hpp" +# include "../generic/fwd.hpp" + +# include "../net/timer.t.hpp" # include "map.hpp" diff --git a/src/map/party.cpp b/src/map/party.cpp index 66dc2fa..f40b991 100644 --- a/src/map/party.cpp +++ b/src/map/party.cpp @@ -29,9 +29,10 @@ #include "../io/cxxstdio.hpp" +#include "../net/timer.hpp" + #include "../mmo/ids.hpp" #include "../mmo/mmo.hpp" -#include "../mmo/timer.hpp" #include "battle.hpp" #include "clif.hpp" diff --git a/src/map/party.hpp b/src/map/party.hpp index 4eaffb0..4670aee 100644 --- a/src/map/party.hpp +++ b/src/map/party.hpp @@ -27,8 +27,9 @@ # include "../strings/fwd.hpp" +# include "../generic/fwd.hpp" + # include "../mmo/fwd.hpp" -# include "../mmo/dumb_ptr.hpp" void do_init_party(void); struct party *party_search(PartyId party_id); diff --git a/src/map/pc.cpp b/src/map/pc.cpp index c9e67ea..9a1493f 100644 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -39,8 +39,9 @@ #include "../io/cxxstdio.hpp" #include "../io/read.hpp" -#include "../mmo/socket.hpp" -#include "../mmo/timer.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" + #include "../mmo/utils.hpp" #include "atcommand.hpp" diff --git a/src/map/pc.hpp b/src/map/pc.hpp index 412953d..900ce97 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -27,6 +27,8 @@ # include "../strings/fwd.hpp" +# include "../generic/dumb_ptr.hpp" + # include "../mmo/utils.hpp" # include "clif.t.hpp" diff --git a/src/map/script.cpp b/src/map/script.cpp index 158a832..9cc17aa 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -48,11 +48,12 @@ #include "../io/read.hpp" #include "../io/write.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" + #include "../mmo/core.hpp" #include "../mmo/extract.hpp" #include "../mmo/human_time_diff.hpp" -#include "../mmo/socket.hpp" -#include "../mmo/timer.hpp" #include "../mmo/utils.hpp" #include "atcommand.hpp" diff --git a/src/map/script.hpp b/src/map/script.hpp index 11a9289..d42da2c 100644 --- a/src/map/script.hpp +++ b/src/map/script.hpp @@ -32,8 +32,7 @@ # include "../strings/zstring.hpp" # include "../generic/db.hpp" - -# include "../mmo/dumb_ptr.hpp" +# include "../generic/dumb_ptr.hpp" # include "map.t.hpp" diff --git a/src/map/skill.cpp b/src/map/skill.cpp index 8e67065..ad8e2e7 100644 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -41,8 +41,9 @@ #include "../io/cxxstdio.hpp" #include "../io/read.hpp" +#include "../net/timer.hpp" + #include "../mmo/extract.hpp" -#include "../mmo/timer.hpp" #include "battle.hpp" #include "clif.hpp" diff --git a/src/map/skill.hpp b/src/map/skill.hpp index 3c162d5..0e566c6 100644 --- a/src/map/skill.hpp +++ b/src/map/skill.hpp @@ -30,6 +30,7 @@ # include "../strings/rstring.hpp" # include "../strings/literal.hpp" +# include "../generic/fwd.hpp" # include "../generic/array.hpp" # include "map.hpp" diff --git a/src/map/storage.hpp b/src/map/storage.hpp index 4101893..4de931f 100644 --- a/src/map/storage.hpp +++ b/src/map/storage.hpp @@ -23,8 +23,9 @@ # include "fwd.hpp" +# include "../generic/fwd.hpp" + # include "../mmo/fwd.hpp" -# include "../mmo/dumb_ptr.hpp" int storage_storageopen(dumb_ptr sd); int storage_storageadd(dumb_ptr sd, int index, int amount); diff --git a/src/map/tmw.hpp b/src/map/tmw.hpp index 14bf8cc..dae6e69 100644 --- a/src/map/tmw.hpp +++ b/src/map/tmw.hpp @@ -24,7 +24,7 @@ # include "../strings/fwd.hpp" -# include "../mmo/dumb_ptr.hpp" +# include "../generic/fwd.hpp" int tmw_CheckChatSpam(dumb_ptr sd, XString message); void tmw_GmHackMsg(ZString line); diff --git a/src/map/trade.hpp b/src/map/trade.hpp index 1514999..9d542a4 100644 --- a/src/map/trade.hpp +++ b/src/map/trade.hpp @@ -23,7 +23,7 @@ # include "fwd.hpp" -# include "../mmo/dumb_ptr.hpp" +# include "../generic/fwd.hpp" void trade_traderequest(dumb_ptr sd, BlockId target_id); void trade_tradeack(dumb_ptr sd, int type); diff --git a/src/mmo/core.cpp b/src/mmo/core.cpp index 68b5863..21aa5f7 100644 --- a/src/mmo/core.cpp +++ b/src/mmo/core.cpp @@ -32,8 +32,8 @@ #include "../io/cxxstdio.hpp" -#include "socket.hpp" -#include "timer.hpp" +#include "../net/socket.hpp" +#include "../net/timer.hpp" #include "../poison.hpp" diff --git a/src/mmo/dumb_ptr.cpp b/src/mmo/dumb_ptr.cpp deleted file mode 100644 index 77e3080..0000000 --- a/src/mmo/dumb_ptr.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "dumb_ptr.hpp" -// dumb_ptr.cpp - dummy file to make Make dependencies work -// -// Copyright © 2013 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "../poison.hpp" diff --git a/src/mmo/dumb_ptr.hpp b/src/mmo/dumb_ptr.hpp deleted file mode 100644 index 3472d8b..0000000 --- a/src/mmo/dumb_ptr.hpp +++ /dev/null @@ -1,271 +0,0 @@ -#ifndef TMWA_MMO_DUMB_PTR_HPP -#define TMWA_MMO_DUMB_PTR_HPP -// dumb_ptr.hpp - temporary new/delete wrappers -// -// Copyright © 2013 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -# include "fwd.hpp" - -# include - -# include -# include - -# include "../strings/astring.hpp" -# include "../strings/zstring.hpp" -# include "../strings/xstring.hpp" - -// unmanaged new/delete-able pointer -// should be replaced by std::unique_ptr -template -class dumb_ptr -{ - template - friend class dumb_ptr; - T *impl; -public: - explicit - dumb_ptr(T *p=nullptr) - : impl(p) - {} - template - dumb_ptr(dumb_ptr p) - : impl(p.impl) - {} - dumb_ptr(std::nullptr_t) - : impl(nullptr) - {} - - void delete_() - { - delete impl; - *this = nullptr; - } - template - void new_(A&&... a) - { - impl = new T(std::forward(a)...); - } - template - static - dumb_ptr make(A&&... a) - { - return dumb_ptr(new T(std::forward(a)...)); - } - dumb_ptr& operator = (std::nullptr_t) - { - impl = nullptr; - return *this; - } - - T& operator *() const - { - return *impl; - } - T *operator->() const - { - return impl; - } - - explicit - operator bool() const - { - return impl; - } - bool operator !() const - { - return !impl; - } - - friend bool operator == (dumb_ptr l, dumb_ptr r) - { - return l.impl == r.impl; - } - friend bool operator != (dumb_ptr l, dumb_ptr r) - { - return !(l == r); - } -}; - -// unmanaged new/delete-able pointer -// should be replaced by std::unique_ptr or std::vector -template -class dumb_ptr -{ - T *impl; - size_t sz; -public: - dumb_ptr() : impl(), sz() {} - dumb_ptr(std::nullptr_t) - : impl(nullptr), sz(0) {} - dumb_ptr(T *p, size_t z) - : impl(p) - , sz(z) - {} - - void delete_() - { - delete[] impl; - *this = nullptr; - } - void new_(size_t z) - { - impl = new T[z](); - sz = z; - } - static - dumb_ptr make(size_t z) - { - return dumb_ptr(new T[z](), z); - } - dumb_ptr& operator = (std::nullptr_t) - { - impl = nullptr; - sz = 0; - return *this; - } - - size_t size() const - { - return sz; - } - void resize(size_t z) - { - if (z == sz) - return; - T *np = new T[z](); - // not exception-safe, but we don't have a dtor anyway - size_t i = std::min(z, sz); - while (i-->0) - np[i] = std::move(impl[i]); - delete[] impl; - impl = np; - sz = z; - } - - T& operator[](size_t i) const - { - return impl[i]; - } - - explicit - operator bool() const - { - return impl; - } - bool operator !() const - { - return !impl; - } - - friend bool operator == (dumb_ptr l, dumb_ptr r) - { - return l.impl == r.impl; - } - friend bool operator != (dumb_ptr l, dumb_ptr r) - { - return !(l == r); - } -}; - -struct dumb_string -{ - dumb_ptr impl; - - dumb_string() - : impl() - {} - dumb_string(char *) = delete; - // copy ctor, copy assign, and dtor are all default - - static dumb_string copy(const char *b, const char *e) - { - dumb_string rv; - rv.impl.new_((e - b) + 1); - std::copy(b, e, &rv.impl[0]); - return rv; - } - static dumb_string copys(XString s) - { - return dumb_string::copy(&*s.begin(), &*s.end()); - } - static -# ifndef __clang__ - __attribute__((warning("shouldn't use this - slice instead"))) -# endif - dumb_string copyn(const char *sn, size_t n) - { - return dumb_string::copy(sn, sn + strnlen(sn, n)); - } - - static - dumb_string fake(ZString p) - { - dumb_string rv; - size_t len = p.size(); - rv.impl = dumb_ptr(const_cast(p.c_str()), len); - return rv; - } - - dumb_string dup() const - { - return dumb_string::copy(&impl[0], &impl[0] + impl.size()); - } - void delete_() - { - impl.delete_(); - } - - const char *c_str() const - { - return &impl[0]; - } - - operator ZString() const - { - return ZString(strings::really_construct_from_a_pointer, c_str(), nullptr); - } - - AString str() const - { - return ZString(*this); - } - - char& operator[](size_t i) const - { - return impl[i]; - } - - explicit - operator bool() const - { - return bool(impl); - } - bool operator !() const - { - return !impl; - } -}; - -inline -const char *convert_for_printf(dumb_string ds) -{ - return ds.c_str(); -} - -#endif // TMWA_MMO_DUMB_PTR_HPP diff --git a/src/mmo/fwd.hpp b/src/mmo/fwd.hpp index 65e8dfe..eb8bc05 100644 --- a/src/mmo/fwd.hpp +++ b/src/mmo/fwd.hpp @@ -26,10 +26,7 @@ class MapName; class CharName; class CharPair; -class Session; - class HumanTimeDiff; -class IP4Address; class AccountId; class CharId; @@ -48,6 +45,4 @@ class VarName; class MapName; class CharName; -class TimerData; - #endif // TMWA_MMO_FWD_HPP diff --git a/src/mmo/ip.cpp b/src/mmo/ip.cpp deleted file mode 100644 index 7c243ea..0000000 --- a/src/mmo/ip.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "ip.hpp" -// ip.cpp - Implementation of IP address functions. -// -// Copyright © 2013 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "../strings/xstring.hpp" -#include "../strings/vstring.hpp" - -#include "../io/cxxstdio.hpp" - -#include "extract.hpp" - -#include "../poison.hpp" - -bool extract(XString str, IP4Address *rv) -{ - if (str.endswith('.')) - return false; - uint8_t buf[4]; - if (extract(str, record<'.'>(&buf[0], &buf[1], &buf[2], &buf[3]))) - { - *rv = IP4Address(buf); - return true; - } - return false; -} - -bool extract(XString str, IP4Mask *rv) -{ - IP4Address a, m; - unsigned b; - XString l, r; - if (str.endswith('/')) - return false; - if (extract(str, record<'/'>(&l, &r))) - { - // a.b.c.d/e.f.g.h or a.b.c.d/n - if (!extract(l, &a)) - return false; - if (extract(r, &m)) - { - *rv = IP4Mask(a, m); - return true; - } - if (!extract(r, &b) || b > 32) - return false; - } - else - { - // a.b.c.d or a.b.c.d. or a.b.c. or a.b. or a. - if (extract(str, &a)) - { - *rv = IP4Mask(a, IP4_BROADCAST); - return true; - } - if (!str.endswith('.')) - return false; - uint8_t d[4] {}; - if (extract(str, record<'.'>(&d[0], &d[1], &d[2], &d[3]))) - b = 32; - else if (extract(str, record<'.'>(&d[0], &d[1], &d[2]))) - b = 24; - else if (extract(str, record<'.'>(&d[0], &d[1]))) - b = 16; - else if (extract(str, record<'.'>(&d[0]))) - b = 8; - else - return false; - a = IP4Address(d); - } - // a is set; need to construct m from b - if (b == 0) - m = IP4Address(); - else if (b == 32) - m = IP4_BROADCAST; - else - { - uint32_t s = -1; - s <<= (32 - b); - m = IP4Address({ - static_cast(s >> 24), - static_cast(s >> 16), - static_cast(s >> 8), - static_cast(s >> 0), - }); - } - *rv = IP4Mask(a, m); - return true; -} - -VString<15> convert_for_printf(IP4Address a_) -{ - const uint8_t *a = a_.bytes(); - return STRNPRINTF(16, "%hhu.%hhu.%hhu.%hhu"_fmt, a[0], a[1], a[2], a[3]); -} - -VString<31> convert_for_printf(IP4Mask a) -{ - return STRNPRINTF(32, "%s/%s"_fmt, - a.addr(), a.mask()); -} diff --git a/src/mmo/ip.hpp b/src/mmo/ip.hpp deleted file mode 100644 index 88ad965..0000000 --- a/src/mmo/ip.hpp +++ /dev/null @@ -1,165 +0,0 @@ -#ifndef TMWA_MMO_IP_HPP -#define TMWA_MMO_IP_HPP -// ip.hpp - classes to deal with IP addresses. -// -// Copyright © 2013 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -# include "fwd.hpp" - -# include - -# include -# include - -# include "../strings/fwd.hpp" - -// TODO - in the long run ports belong here also -// and of course, IPv6 stuff. -// But what about unix socket addresses? - -/// Helper function -template -constexpr -bool _ce_a_lt(T (&a)[n], T (&b)[n], size_t i=0) -{ - return (i != n - && (a[i] < b[i] - || (a[i] == b[i] - && _ce_a_lt(a, b, i + 1)))); -} - -/// A 32-bit Ipv4 address. Does not include a port. -/// Guaranteed to be laid out like the network wants. -class IP4Address -{ - uint8_t _addr[4]; -public: - constexpr - IP4Address() - : _addr{} - {} - constexpr explicit - IP4Address(const uint8_t (&a)[4]) - : _addr{a[0], a[1], a[2], a[3]} - {} - explicit - IP4Address(struct in_addr addr) - { - static_assert(sizeof(addr) == sizeof(_addr), "4 bytes"); - *this = IP4Address(reinterpret_cast(addr)); - } - explicit - operator struct in_addr() const - { - return reinterpret_cast(_addr); - } - - constexpr friend - IP4Address operator & (IP4Address l, IP4Address r) - { - return IP4Address({ - static_cast(l._addr[0] & r._addr[0]), - static_cast(l._addr[1] & r._addr[1]), - static_cast(l._addr[2] & r._addr[2]), - static_cast(l._addr[3] & r._addr[3]), - }); - } - - IP4Address& operator &= (IP4Address m) - { return *this = *this & m; } - - const uint8_t *bytes() const - { return _addr; } - - constexpr friend - bool operator < (IP4Address l, IP4Address r) - { - return _ce_a_lt(l._addr, r._addr); - } - - constexpr friend - bool operator > (IP4Address l, IP4Address r) - { - return _ce_a_lt(r._addr, l._addr); - } - - constexpr friend - bool operator >= (IP4Address l, IP4Address r) - { - return !_ce_a_lt(l._addr, r._addr); - } - - constexpr friend - bool operator <= (IP4Address l, IP4Address r) - { - return !_ce_a_lt(r._addr, l._addr); - } - - constexpr friend - bool operator == (IP4Address l, IP4Address r) - { - return !(l < r || r < l); - } - - constexpr friend - bool operator != (IP4Address l, IP4Address r) - { - return (l < r || r < l); - } -}; - -class IP4Mask -{ - IP4Address _addr, _mask; -public: - constexpr - IP4Mask() : _addr(), _mask() - {} - constexpr - IP4Mask(IP4Address a, IP4Address m) : _addr(a & m), _mask(m) - {} - - constexpr - IP4Address addr() const - { return _addr; } - constexpr - IP4Address mask() const - { return _mask; } - - constexpr - bool covers(IP4Address a) const - { - return (a & _mask) == _addr; - } -}; - - -constexpr -IP4Address IP4_LOCALHOST({127, 0, 0, 1}); -constexpr -IP4Address IP4_BROADCAST({255, 255, 255, 255}); - - -VString<15> convert_for_printf(IP4Address a); -VString<31> convert_for_printf(IP4Mask m); - -bool extract(XString str, IP4Address *iv); - -bool extract(XString str, IP4Mask *iv); - -#endif // TMWA_MMO_IP_HPP diff --git a/src/mmo/ip.py b/src/mmo/ip.py deleted file mode 100644 index e6a8183..0000000 --- a/src/mmo/ip.py +++ /dev/null @@ -1,14 +0,0 @@ -class IP4Address(object): - ''' print an IP4Address - ''' - __slots__ = ('_value') - name = 'IP4Address' - enabled = True - - def __init__(self, value): - self._value = value - - def to_string(self): - addr = self._value['_addr'] - addr = tuple(int(addr[i]) for i in range(4)) - return '%d.%d.%d.%d' % addr diff --git a/src/mmo/ip_test.cpp b/src/mmo/ip_test.cpp deleted file mode 100644 index 3c4d2ef..0000000 --- a/src/mmo/ip_test.cpp +++ /dev/null @@ -1,355 +0,0 @@ -#include "ip.hpp" -// ip_test.cpp - Testsuite for implementation of IP address functions. -// -// Copyright © 2013 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include - -#include "../strings/vstring.hpp" -#include "../strings/literal.hpp" - -#include "../io/cxxstdio.hpp" - -#include "../poison.hpp" - -#define CB(X) (std::integral_constant::value) -TEST(ip4addr, cmp) -{ - constexpr static - IP4Address a = IP4_LOCALHOST; - constexpr static - IP4Address b = IP4_BROADCAST; - - EXPECT_FALSE(CB(a < a)); - EXPECT_TRUE (CB(a < b)); - EXPECT_FALSE(CB(b < a)); - EXPECT_FALSE(CB(b < b)); - - EXPECT_FALSE(CB(a > a)); - EXPECT_FALSE(CB(a > b)); - EXPECT_TRUE (CB(b > a)); - EXPECT_FALSE(CB(b > b)); - - EXPECT_TRUE (CB(a <= a)); - EXPECT_TRUE (CB(a <= b)); - EXPECT_FALSE(CB(b <= a)); - EXPECT_TRUE (CB(b <= b)); - - EXPECT_TRUE (CB(a >= a)); - EXPECT_FALSE(CB(a >= b)); - EXPECT_TRUE (CB(b >= a)); - EXPECT_TRUE (CB(b >= b)); - - EXPECT_TRUE (CB(a == a)); - EXPECT_FALSE(CB(a == b)); - EXPECT_FALSE(CB(b == a)); - EXPECT_TRUE (CB(b == b)); - - EXPECT_FALSE(CB(a != a)); - EXPECT_TRUE (CB(a != b)); - EXPECT_TRUE (CB(b != a)); - EXPECT_FALSE(CB(b != b)); -} - -TEST(ip4addr, str) -{ - IP4Address a; - EXPECT_EQ("0.0.0.0"_s, STRNPRINTF(17, "%s"_fmt, a)); - EXPECT_EQ("127.0.0.1"_s, STRNPRINTF(17, "%s"_fmt, IP4_LOCALHOST)); - EXPECT_EQ("255.255.255.255"_s, STRNPRINTF(17, "%s"_fmt, IP4_BROADCAST)); -} - -TEST(ip4addr, extract) -{ - IP4Address a; - EXPECT_TRUE(extract("0.0.0.0"_s, &a)); - EXPECT_EQ("0.0.0.0"_s, STRNPRINTF(16, "%s"_fmt, a)); - EXPECT_TRUE(extract("127.0.0.1"_s, &a)); - EXPECT_EQ("127.0.0.1"_s, STRNPRINTF(16, "%s"_fmt, a)); - EXPECT_TRUE(extract("255.255.255.255"_s, &a)); - EXPECT_EQ("255.255.255.255"_s, STRNPRINTF(16, "%s"_fmt, a)); - EXPECT_TRUE(extract("1.2.3.4"_s, &a)); - EXPECT_EQ("1.2.3.4"_s, STRNPRINTF(16, "%s"_fmt, a)); - - EXPECT_FALSE(extract("1.2.3.4.5"_s, &a)); - EXPECT_FALSE(extract("1.2.3.4."_s, &a)); - EXPECT_FALSE(extract("1.2.3."_s, &a)); - EXPECT_FALSE(extract("1.2.3"_s, &a)); - EXPECT_FALSE(extract("1.2."_s, &a)); - EXPECT_FALSE(extract("1.2"_s, &a)); - EXPECT_FALSE(extract("1."_s, &a)); - EXPECT_FALSE(extract("1"_s, &a)); - EXPECT_FALSE(extract(""_s, &a)); -} - - -TEST(ip4mask, body) -{ - IP4Mask m; - EXPECT_EQ(IP4Address(), m.addr()); - EXPECT_EQ(IP4Address(), m.mask()); - m = IP4Mask(IP4_LOCALHOST, IP4_BROADCAST); - EXPECT_EQ(IP4_LOCALHOST, m.addr()); - EXPECT_EQ(IP4_BROADCAST, m.mask()); -} - -TEST(ip4mask, str) -{ - IP4Mask m; - EXPECT_EQ("0.0.0.0/0.0.0.0"_s, STRNPRINTF(33, "%s"_fmt, m)); - m = IP4Mask(IP4_LOCALHOST, IP4_BROADCAST); - EXPECT_EQ("127.0.0.1/255.255.255.255"_s, STRNPRINTF(33, "%s"_fmt, m)); -} - -TEST(ip4mask, extract) -{ - IP4Mask m; - EXPECT_FALSE(extract("9.8.7.6/33"_s, &m)); - EXPECT_FALSE(extract("9.8.7.6.5"_s, &m)); - EXPECT_FALSE(extract("9.8.7.6/"_s, &m)); - EXPECT_FALSE(extract("9.8.7"_s, &m)); - EXPECT_FALSE(extract("9.8"_s, &m)); - EXPECT_FALSE(extract("9"_s, &m)); - - EXPECT_TRUE(extract("127.0.0.1"_s, &m)); - EXPECT_EQ("127.0.0.1/255.255.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("127.0.0.1."_s, &m)); - EXPECT_EQ("127.0.0.1/255.255.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("127.0.0."_s, &m)); - EXPECT_EQ("127.0.0.0/255.255.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("127.0."_s, &m)); - EXPECT_EQ("127.0.0.0/255.255.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("127."_s, &m)); - EXPECT_EQ("127.0.0.0/255.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - - EXPECT_TRUE(extract("1.2.3.4/255.255.255.255"_s, &m)); - EXPECT_EQ("1.2.3.4/255.255.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("1.2.3.0/255.255.255.0"_s, &m)); - EXPECT_EQ("1.2.3.0/255.255.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("1.2.0.4/255.255.0.255"_s, &m)); - EXPECT_EQ("1.2.0.4/255.255.0.255"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("1.2.0.0/255.255.0.0"_s, &m)); - EXPECT_EQ("1.2.0.0/255.255.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("1.0.3.4/255.0.255.255"_s, &m)); - EXPECT_EQ("1.0.3.4/255.0.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("1.0.3.0/255.0.255.0"_s, &m)); - EXPECT_EQ("1.0.3.0/255.0.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("1.0.0.4/255.0.0.255"_s, &m)); - EXPECT_EQ("1.0.0.4/255.0.0.255"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("1.0.0.0/255.0.0.0"_s, &m)); - EXPECT_EQ("1.0.0.0/255.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.2.3.4/0.255.255.255"_s, &m)); - EXPECT_EQ("0.2.3.4/0.255.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.2.3.0/0.255.255.0"_s, &m)); - EXPECT_EQ("0.2.3.0/0.255.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.2.0.4/0.255.0.255"_s, &m)); - EXPECT_EQ("0.2.0.4/0.255.0.255"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.2.0.0/0.255.0.0"_s, &m)); - EXPECT_EQ("0.2.0.0/0.255.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.3.4/0.0.255.255"_s, &m)); - EXPECT_EQ("0.0.3.4/0.0.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.3.0/0.0.255.0"_s, &m)); - EXPECT_EQ("0.0.3.0/0.0.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.4/0.0.0.255"_s, &m)); - EXPECT_EQ("0.0.0.4/0.0.0.255"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/0.0.0.0"_s, &m)); - EXPECT_EQ("0.0.0.0/0.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - - // please don't do this - EXPECT_TRUE(extract("120.248.200.217/89.57.126.5"_s, &m)); - EXPECT_EQ("88.56.72.1/89.57.126.5"_s, STRNPRINTF(32, "%s"_fmt, m)); - - EXPECT_TRUE(extract("0.0.0.0/32"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); - - EXPECT_TRUE(extract("0.0.0.0/31"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.255.254"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/30"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.255.252"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/29"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.255.248"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/28"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.255.240"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/27"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.255.224"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/26"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.255.192"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/25"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.255.128"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/24"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - - EXPECT_TRUE(extract("0.0.0.0/23"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.254.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/22"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.252.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/21"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.248.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/20"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.240.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/19"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.224.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/18"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.192.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/17"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.128.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/16"_s, &m)); - EXPECT_EQ("0.0.0.0/255.255.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - - EXPECT_TRUE(extract("0.0.0.0/15"_s, &m)); - EXPECT_EQ("0.0.0.0/255.254.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/14"_s, &m)); - EXPECT_EQ("0.0.0.0/255.252.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/13"_s, &m)); - EXPECT_EQ("0.0.0.0/255.248.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/12"_s, &m)); - EXPECT_EQ("0.0.0.0/255.240.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/11"_s, &m)); - EXPECT_EQ("0.0.0.0/255.224.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/10"_s, &m)); - EXPECT_EQ("0.0.0.0/255.192.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/9"_s, &m)); - EXPECT_EQ("0.0.0.0/255.128.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/8"_s, &m)); - EXPECT_EQ("0.0.0.0/255.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - - EXPECT_TRUE(extract("0.0.0.0/7"_s, &m)); - EXPECT_EQ("0.0.0.0/254.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/6"_s, &m)); - EXPECT_EQ("0.0.0.0/252.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/5"_s, &m)); - EXPECT_EQ("0.0.0.0/248.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/4"_s, &m)); - EXPECT_EQ("0.0.0.0/240.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/3"_s, &m)); - EXPECT_EQ("0.0.0.0/224.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/2"_s, &m)); - EXPECT_EQ("0.0.0.0/192.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/1"_s, &m)); - EXPECT_EQ("0.0.0.0/128.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); - EXPECT_TRUE(extract("0.0.0.0/0"_s, &m)); - EXPECT_EQ("0.0.0.0/0.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); -} - -TEST(ip4mask, cover) -{ - IP4Address a; - IP4Address b = IP4_BROADCAST; - IP4Address l = IP4_LOCALHOST; - IP4Address h({127, 255, 255, 255}); - IP4Address p24l({10, 0, 0, 0}); - IP4Address p24h({10, 255, 255, 255}); - IP4Address p20l({172, 16, 0, 0}); - IP4Address p20h({172, 31, 255, 255}); - IP4Address p16l({192, 168, 0, 0}); - IP4Address p16h({192, 168, 255, 255}); - IP4Mask m; - EXPECT_TRUE(m.covers(a)); - EXPECT_TRUE(m.covers(b)); - EXPECT_TRUE(m.covers(l)); - EXPECT_TRUE(m.covers(h)); - EXPECT_TRUE(m.covers(p24l)); - EXPECT_TRUE(m.covers(p24h)); - EXPECT_TRUE(m.covers(p20l)); - EXPECT_TRUE(m.covers(p20h)); - EXPECT_TRUE(m.covers(p16l)); - EXPECT_TRUE(m.covers(p16h)); - m = IP4Mask(l, a); - EXPECT_TRUE(m.covers(a)); - EXPECT_TRUE(m.covers(b)); - EXPECT_TRUE(m.covers(l)); - EXPECT_TRUE(m.covers(h)); - EXPECT_TRUE(m.covers(p24l)); - EXPECT_TRUE(m.covers(p24h)); - EXPECT_TRUE(m.covers(p20l)); - EXPECT_TRUE(m.covers(p20h)); - EXPECT_TRUE(m.covers(p16l)); - EXPECT_TRUE(m.covers(p16h)); - m = IP4Mask(l, b); - EXPECT_FALSE(m.covers(a)); - EXPECT_FALSE(m.covers(b)); - EXPECT_TRUE(m.covers(l)); - EXPECT_FALSE(m.covers(h)); - EXPECT_FALSE(m.covers(p24l)); - EXPECT_FALSE(m.covers(p24h)); - EXPECT_FALSE(m.covers(p20l)); - EXPECT_FALSE(m.covers(p20h)); - EXPECT_FALSE(m.covers(p16l)); - EXPECT_FALSE(m.covers(p16h)); - - // but the really useful ones are with partial masks - m = IP4Mask(IP4Address({10, 0, 0, 0}), IP4Address({255, 0, 0, 0})); - EXPECT_FALSE(m.covers(a)); - EXPECT_FALSE(m.covers(b)); - EXPECT_FALSE(m.covers(l)); - EXPECT_FALSE(m.covers(h)); - EXPECT_TRUE(m.covers(p24l)); - EXPECT_TRUE(m.covers(p24h)); - EXPECT_FALSE(m.covers(p20l)); - EXPECT_FALSE(m.covers(p20h)); - EXPECT_FALSE(m.covers(p16l)); - EXPECT_FALSE(m.covers(p16h)); - EXPECT_FALSE(m.covers(IP4Address({9, 255, 255, 255}))); - EXPECT_FALSE(m.covers(IP4Address({11, 0, 0, 0}))); - m = IP4Mask(IP4Address({127, 0, 0, 0}), IP4Address({255, 0, 0, 0})); - EXPECT_FALSE(m.covers(a)); - EXPECT_FALSE(m.covers(b)); - EXPECT_TRUE(m.covers(l)); - EXPECT_TRUE(m.covers(h)); - EXPECT_FALSE(m.covers(p24l)); - EXPECT_FALSE(m.covers(p24h)); - EXPECT_FALSE(m.covers(p20l)); - EXPECT_FALSE(m.covers(p20h)); - EXPECT_FALSE(m.covers(p16l)); - EXPECT_FALSE(m.covers(p16h)); - EXPECT_FALSE(m.covers(IP4Address({126, 255, 255, 255}))); - EXPECT_FALSE(m.covers(IP4Address({128, 0, 0, 0}))); - m = IP4Mask(IP4Address({172, 16, 0, 0}), IP4Address({255, 240, 0, 0})); - EXPECT_FALSE(m.covers(a)); - EXPECT_FALSE(m.covers(b)); - EXPECT_FALSE(m.covers(l)); - EXPECT_FALSE(m.covers(h)); - EXPECT_FALSE(m.covers(p24l)); - EXPECT_FALSE(m.covers(p24h)); - EXPECT_TRUE(m.covers(p20l)); - EXPECT_TRUE(m.covers(p20h)); - EXPECT_FALSE(m.covers(p16l)); - EXPECT_FALSE(m.covers(p16h)); - EXPECT_FALSE(m.covers(IP4Address({172, 15, 255, 255}))); - EXPECT_FALSE(m.covers(IP4Address({172, 32, 0, 0}))); - m = IP4Mask(IP4Address({192, 168, 0, 0}), IP4Address({255, 255, 0, 0})); - EXPECT_FALSE(m.covers(a)); - EXPECT_FALSE(m.covers(b)); - EXPECT_FALSE(m.covers(l)); - EXPECT_FALSE(m.covers(h)); - EXPECT_FALSE(m.covers(p24l)); - EXPECT_FALSE(m.covers(p24h)); - EXPECT_FALSE(m.covers(p20l)); - EXPECT_FALSE(m.covers(p20h)); - EXPECT_TRUE(m.covers(p16l)); - EXPECT_TRUE(m.covers(p16h)); - EXPECT_FALSE(m.covers(IP4Address({192, 167, 255, 255}))); - EXPECT_FALSE(m.covers(IP4Address({192, 169, 0, 0}))); - - // OTOH this is crazy - EXPECT_TRUE(extract("120.248.200.217/89.57.126.5"_s, &m)); - EXPECT_TRUE(m.covers(IP4Address({120, 248, 200, 217}))); - EXPECT_TRUE(m.covers(IP4Address({88, 56, 72, 1}))); - EXPECT_FALSE(m.covers(IP4Address({88, 56, 72, 0}))); - EXPECT_FALSE(m.covers(IP4Address({88, 56, 72, 255}))); -} diff --git a/src/mmo/md5more.cpp b/src/mmo/md5more.cpp index c311583..96b12a7 100644 --- a/src/mmo/md5more.cpp +++ b/src/mmo/md5more.cpp @@ -29,7 +29,8 @@ #include "../io/cxxstdio.hpp" #include "../io/read.hpp" -#include "../mmo/ip.hpp" +#include "../net/ip.hpp" + #include "../mmo/mmo.hpp" #include "../poison.hpp" diff --git a/src/mmo/md5more.hpp b/src/mmo/md5more.hpp index 4b9895d..f3ab363 100644 --- a/src/mmo/md5more.hpp +++ b/src/mmo/md5more.hpp @@ -27,6 +27,8 @@ # include "../io/fwd.hpp" +# include "../net/fwd.hpp" + MD5_state MD5_from_FILE(io::ReadFile& in); // whoever wrote this fails basic understanding of diff --git a/src/mmo/mmo.hpp b/src/mmo/mmo.hpp index 15c3fa5..c72d815 100644 --- a/src/mmo/mmo.hpp +++ b/src/mmo/mmo.hpp @@ -32,8 +32,9 @@ # include "../generic/array.hpp" # include "../generic/enum.hpp" +# include "../net/timer.t.hpp" + # include "ids.hpp" -# include "timer.t.hpp" // affects CharName # define NAME_IGNORING_CASE 1 diff --git a/src/mmo/socket.cpp b/src/mmo/socket.cpp deleted file mode 100644 index 1d6094c..0000000 --- a/src/mmo/socket.cpp +++ /dev/null @@ -1,509 +0,0 @@ -#include "socket.hpp" -// socket.cpp - Network event system. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// Copyright © 2013 MadCamel -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include -#include - -#include - -#include - -#include - -#include "../compat/memory.hpp" - -#include "../io/cxxstdio.hpp" - -#include "../mmo/utils.hpp" - -#include "core.hpp" -#include "timer.hpp" - -#include "../poison.hpp" - -static -io::FD_Set readfds; -static -int fd_max; - -static -const uint32_t RFIFO_SIZE = 65536; -static -const uint32_t WFIFO_SIZE = 65536; - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -static -std::array, FD_SETSIZE> session; -#pragma GCC diagnostic pop - -Session::Session(SessionIO io, SessionParsers p) -: created() -, connected() -, eof() -, timed_close() -, rdata(), wdata() -, max_rdata(), max_wdata() -, rdata_size(), wdata_size() -, rdata_pos() -, client_ip() -, func_recv() -, func_send() -, func_parse() -, func_delete() -, for_inferior() -, session_data() -, fd() -{ - set_io(io); - set_parsers(p); -} -void Session::set_io(SessionIO io) -{ - func_send = io.func_send; - func_recv = io.func_recv; -} -void Session::set_parsers(SessionParsers p) -{ - func_parse = p.func_parse; - func_delete = p.func_delete; -} - - -void set_session(io::FD fd, std::unique_ptr sess) -{ - int f = fd.uncast_dammit(); - assert (0 <= f && f < FD_SETSIZE); - session[f] = std::move(sess); -} -Session *get_session(io::FD fd) -{ - int f = fd.uncast_dammit(); - if (0 <= f && f < FD_SETSIZE) - return session[f].get(); - return nullptr; -} -void reset_session(io::FD fd) -{ - int f = fd.uncast_dammit(); - assert (0 <= f && f < FD_SETSIZE); - session[f] = nullptr; -} -int get_fd_max() { return fd_max; } -IteratorPair> iter_fds() -{ - return {io::FD::cast_dammit(0), io::FD::cast_dammit(fd_max)}; -} - -/// clean up by discarding handled bytes -inline -void RFIFOFLUSH(Session *s) -{ - really_memmove(&s->rdata[0], &s->rdata[s->rdata_pos], RFIFOREST(s)); - s->rdata_size = RFIFOREST(s); - s->rdata_pos = 0; -} - -/// how much room there is to read more data -inline -size_t RFIFOSPACE(Session *s) -{ - return s->max_rdata - s->rdata_size; -} - - -/// Read from socket to the queue -static -void recv_to_fifo(Session *s) -{ - ssize_t len = s->fd.read(&s->rdata[s->rdata_size], - RFIFOSPACE(s)); - - if (len > 0) - { - s->rdata_size += len; - s->connected = 1; - } - else - { - s->set_eof(); - } -} - -static -void send_from_fifo(Session *s) -{ - ssize_t len = s->fd.write(&s->wdata[0], s->wdata_size); - - if (len > 0) - { - s->wdata_size -= len; - if (s->wdata_size) - { - really_memmove(&s->wdata[0], &s->wdata[len], - s->wdata_size); - } - s->connected = 1; - } - else - { - s->set_eof(); - } -} - -static -void nothing_delete(Session *s) -{ - (void)s; -} - -static -void connect_client(Session *ls) -{ - struct sockaddr_in client_address; - socklen_t len = sizeof(client_address); - - io::FD fd = ls->fd.accept(reinterpret_cast(&client_address), &len); - if (fd == io::FD()) - { - perror("accept"); - return; - } - if (fd.uncast_dammit() >= SOFT_LIMIT) - { - FPRINTF(stderr, "softlimit reached, disconnecting : %d\n"_fmt, fd.uncast_dammit()); - fd.shutdown(SHUT_RDWR); - fd.close(); - return; - } - if (fd_max <= fd.uncast_dammit()) - { - fd_max = fd.uncast_dammit() + 1; - } - - const int yes = 1; - /// Allow to bind() again after the server restarts. - // Since the socket is still in the TIME_WAIT, there's a possibility - // that formerly lost packets might be delivered and confuse the server. - fd.setsockopt(SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - /// Send packets as soon as possible - /// even if the kernel thinks there is too little for it to be worth it! - /// Testing shows this is indeed a good idea. - fd.setsockopt(IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); - - // Linux-ism: Set socket options to optimize for thin streams - // See http://lwn.net/Articles/308919/ and - // Documentation/networking/tcp-thin.txt .. Kernel 3.2+ -#ifdef TCP_THIN_LINEAR_TIMEOUTS - fd.setsockopt(IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, &yes, sizeof yes); -#endif -#ifdef TCP_THIN_DUPACK - fd.setsockopt(IPPROTO_TCP, TCP_THIN_DUPACK, &yes, sizeof yes); -#endif - - readfds.set(fd); - - fd.fcntl(F_SETFL, O_NONBLOCK); - - set_session(fd, make_unique( - SessionIO{.func_recv= recv_to_fifo, .func_send= send_from_fifo}, - ls->for_inferior)); - Session *s = get_session(fd); - s->fd = fd; - s->rdata.new_(RFIFO_SIZE); - s->wdata.new_(WFIFO_SIZE); - s->max_rdata = RFIFO_SIZE; - s->max_wdata = WFIFO_SIZE; - s->client_ip = IP4Address(client_address.sin_addr); - s->created = TimeT::now(); - s->connected = 0; -} - -Session *make_listen_port(uint16_t port, SessionParsers inferior) -{ - struct sockaddr_in server_address; - io::FD fd = io::FD::socket(AF_INET, SOCK_STREAM, 0); - if (fd == io::FD()) - { - perror("socket"); - return nullptr; - } - if (fd_max <= fd.uncast_dammit()) - fd_max = fd.uncast_dammit() + 1; - - fd.fcntl(F_SETFL, O_NONBLOCK); - - const int yes = 1; - /// Allow to bind() again after the server restarts. - // Since the socket is still in the TIME_WAIT, there's a possibility - // that formerly lost packets might be delivered and confuse the server. - fd.setsockopt(SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - /// Send packets as soon as possible - /// even if the kernel thinks there is too little for it to be worth it! - // I'm not convinced this is a good idea; although in minimizes the - // latency for an individual write, it increases traffic in general. - fd.setsockopt(IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); - - server_address.sin_family = AF_INET; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#if __GNUC__ > 4 || __GNUC_MINOR__ >= 8 -# pragma GCC diagnostic ignored "-Wuseless-cast" -#endif - server_address.sin_addr.s_addr = htonl(INADDR_ANY); - server_address.sin_port = htons(port); -#pragma GCC diagnostic pop - - if (fd.bind(reinterpret_cast(&server_address), - sizeof(server_address)) == -1) - { - perror("bind"); - exit(1); - } - if (fd.listen(5) == -1) - { /* error */ - perror("listen"); - exit(1); - } - - readfds.set(fd); - - set_session(fd, make_unique( - SessionIO{.func_recv= connect_client, .func_send= nullptr}, - SessionParsers{.func_parse= nullptr, .func_delete= nothing_delete})); - Session *s = get_session(fd); - s->for_inferior = inferior; - s->fd = fd; - - s->created = TimeT::now(); - s->connected = 1; - - return s; -} - -Session *make_connection(IP4Address ip, uint16_t port, SessionParsers parsers) -{ - struct sockaddr_in server_address; - io::FD fd = io::FD::socket(AF_INET, SOCK_STREAM, 0); - if (fd == io::FD()) - { - perror("socket"); - return nullptr; - } - if (fd_max <= fd.uncast_dammit()) - fd_max = fd.uncast_dammit() + 1; - - const int yes = 1; - /// Allow to bind() again after the server restarts. - // Since the socket is still in the TIME_WAIT, there's a possibility - // that formerly lost packets might be delivered and confuse the server. - fd.setsockopt(SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - /// Send packets as soon as possible - /// even if the kernel thinks there is too little for it to be worth it! - // I'm not convinced this is a good idea; although in minimizes the - // latency for an individual write, it increases traffic in general. - fd.setsockopt(IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); - - server_address.sin_family = AF_INET; - server_address.sin_addr = in_addr(ip); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#if __GNUC__ > 4 || __GNUC_MINOR__ >= 8 -# pragma GCC diagnostic ignored "-Wuseless-cast" -#endif - server_address.sin_port = htons(port); -#pragma GCC diagnostic pop - - fd.fcntl(F_SETFL, O_NONBLOCK); - - /// Errors not caught - we must not block - /// Let the main select() loop detect when we know the state - fd.connect(reinterpret_cast(&server_address), - sizeof(struct sockaddr_in)); - - readfds.set(fd); - - set_session(fd, make_unique( - SessionIO{.func_recv= recv_to_fifo, .func_send= send_from_fifo}, - parsers)); - Session *s = get_session(fd); - s->fd = fd; - s->rdata.new_(RFIFO_SIZE); - s->wdata.new_(WFIFO_SIZE); - - s->max_rdata = RFIFO_SIZE; - s->max_wdata = WFIFO_SIZE; - s->created = TimeT::now(); - s->connected = 1; - - return s; -} - -void delete_session(Session *s) -{ - if (!s) - return; - // this needs to be before the fd_max-- - s->func_delete(s); - - io::FD fd = s->fd; - // If this was the highest fd, decrease it - // We could add a loop to decrement fd_max further for every null session, - // but this is cheap and good enough for the typical case - if (fd.uncast_dammit() == fd_max - 1) - fd_max--; - readfds.clr(fd); - { - s->rdata.delete_(); - s->wdata.delete_(); - s->session_data.reset(); - reset_session(fd); - } - - // just close() would try to keep sending buffers - fd.shutdown(SHUT_RDWR); - fd.close(); -} - -void realloc_fifo(Session *s, size_t rfifo_size, size_t wfifo_size) -{ - if (s->max_rdata != rfifo_size && s->rdata_size < rfifo_size) - { - s->rdata.resize(rfifo_size); - s->max_rdata = rfifo_size; - } - if (s->max_wdata != wfifo_size && s->wdata_size < wfifo_size) - { - s->wdata.resize(wfifo_size); - s->max_wdata = wfifo_size; - } -} - -void WFIFOSET(Session *s, size_t len) -{ - if (s->wdata_size + len + 16384 > s->max_wdata) - { - realloc_fifo(s, s->max_rdata, s->max_wdata << 1); - PRINTF("socket: %d wdata expanded to %zu bytes.\n"_fmt, s, s->max_wdata); - } - if (s->wdata_size + len + 2048 < s->max_wdata) - s->wdata_size += len; - else - FPRINTF(stderr, "socket: %d wdata lost !!\n"_fmt, s), abort(); -} - -void do_sendrecv(interval_t next_ms) -{ - bool any = false; - io::FD_Set rfd = readfds, wfd; - for (io::FD i : iter_fds()) - { - Session *s = get_session(i); - if (s) - { - any = true; - if (s->wdata_size) - wfd.set(i); - } - } - if (!any) - { - if (!has_timers()) - { - PRINTF("Shutting down - nothing to do\n"_fmt); - runflag = false; - } - return; - } - struct timeval timeout; - { - std::chrono::seconds next_s = std::chrono::duration_cast(next_ms); - std::chrono::microseconds next_us = next_ms - next_s; - timeout.tv_sec = next_s.count(); - timeout.tv_usec = next_us.count(); - } - if (io::FD_Set::select(fd_max, &rfd, &wfd, NULL, &timeout) <= 0) - return; - for (io::FD i : iter_fds()) - { - Session *s = get_session(i); - if (!s) - continue; - if (wfd.isset(i) && !s->eof) - { - if (s->func_send) - //send_from_fifo(i); - s->func_send(s); - } - if (rfd.isset(i) && !s->eof) - { - if (s->func_recv) - //recv_to_fifo(i); - //or connect_client(i); - s->func_recv(s); - } - } -} - -void do_parsepacket(void) -{ - for (io::FD i : iter_fds()) - { - Session *s = get_session(i); - if (!s) - continue; - if (!s->connected - && static_cast(TimeT::now()) - static_cast(s->created) > CONNECT_TIMEOUT) - { - PRINTF("Session #%d timed out\n"_fmt, s); - s->set_eof(); - } - if (s->rdata_size && !s->eof && s->func_parse) - { - s->func_parse(s); - /// some func_parse may call delete_session - // (that's kind of evil) - s = get_session(i); - if (!s) - continue; - } - if (s->eof) - { - delete_session(s); - continue; - } - /// Reclaim buffer space for what was read - RFIFOFLUSH(s); - } -} - -void RFIFOSKIP(Session *s, size_t len) -{ - s->rdata_pos += len; - - if (s->rdata_size < s->rdata_pos) - { - FPRINTF(stderr, "too many skip\n"_fmt); - abort(); - } -} diff --git a/src/mmo/socket.hpp b/src/mmo/socket.hpp deleted file mode 100644 index ff10c64..0000000 --- a/src/mmo/socket.hpp +++ /dev/null @@ -1,424 +0,0 @@ -#ifndef TMWA_MMO_SOCKET_HPP -#define TMWA_MMO_SOCKET_HPP -// socket.hpp - Network event system. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -# include "fwd.hpp" - -# include - -# include - -# include - -# include "../compat/iter.hpp" -# include "../compat/rawmem.hpp" - -# include "../strings/astring.hpp" -# include "../strings/vstring.hpp" -# include "../strings/xstring.hpp" - -# include "../io/fd.hpp" - -# include "dumb_ptr.hpp" -# include "ip.hpp" -# include "utils.hpp" -# include "timer.t.hpp" - -struct SessionData -{ -}; -struct SessionDeleter -{ - // defined per-server - void operator()(SessionData *sd); -}; - -struct SessionIO -{ - void (*func_recv)(Session *); - void (*func_send)(Session *); -}; - -struct SessionParsers -{ - void (*func_parse)(Session *); - void (*func_delete)(Session *); -}; - -struct Session -{ - Session(SessionIO, SessionParsers); - Session(Session&&) = delete; - Session& operator = (Session&&) = delete; - - void set_io(SessionIO); - void set_parsers(SessionParsers); - - /// Checks whether a newly-connected socket actually does anything - TimeT created; - bool connected; - -private: - /// Flag needed since structure must be freed in a server-dependent manner - bool eof; -public: - void set_eof() { eof = true; } - - /// Currently used by clif_setwaitclose - Timer timed_close; - - /// Since this is a single-threaded application, it can't block - /// These are the read/write queues - dumb_ptr rdata, wdata; - size_t max_rdata, max_wdata; - /// How much is actually in the queue - size_t rdata_size, wdata_size; - /// How much has already been read from the queue - /// Note that there is no need for a wdata_pos - size_t rdata_pos; - - IP4Address client_ip; - -private: - /// Send or recieve - /// Only called when select() indicates the socket is ready - /// If, after that, nothing is read, it sets eof - // These could probably be hard-coded with a little work - void (*func_recv)(Session *); - void (*func_send)(Session *); - /// This is the important one - /// Set to different functions depending on whether the connection - /// is a player or a server/ladmin - void (*func_parse)(Session *); - /// Cleanup function since we're not fully RAII yet - void (*func_delete)(Session *); - -public: - // this really ought to be part of session_data, once that gets sane - SessionParsers for_inferior; - - /// Server-specific data type - // (this really should include the deleter, but ...) - std::unique_ptr session_data; - - io::FD fd; - - friend void do_sendrecv(interval_t next); - friend void do_parsepacket(void); - friend void delete_session(Session *); -}; - -inline -int convert_for_printf(Session *s) -{ - return s->fd.uncast_dammit(); -} - -// save file descriptors for important stuff -constexpr int SOFT_LIMIT = FD_SETSIZE - 50; - -// socket timeout to establish a full connection in seconds -constexpr int CONNECT_TIMEOUT = 15; - - -void set_session(io::FD fd, std::unique_ptr sess); -Session *get_session(io::FD fd); -void reset_session(io::FD fd); -int get_fd_max(); - -class IncrFD -{ -public: - static - io::FD inced(io::FD v) - { - return io::FD::cast_dammit(v.uncast_dammit() + 1); - } -}; -IteratorPair> iter_fds(); - - -/// open a socket, bind, and listen. Return an fd, or -1 if socket() fails, -/// but exit if bind() or listen() fails -Session *make_listen_port(uint16_t port, SessionParsers inferior); -/// Connect to an address, return a connected socket or -1 -// FIXME - this is IPv4 only! -Session *make_connection(IP4Address ip, uint16_t port, SessionParsers); -/// free() the structure and close() the fd -void delete_session(Session *); -/// Make a the internal queues bigger -void realloc_fifo(Session *s, size_t rfifo_size, size_t wfifo_size); -/// Update all sockets that can be read/written from the queues -void do_sendrecv(interval_t next); -/// Call the parser function for every socket that has read data -void do_parsepacket(void); - -template -uint8_t *pod_addressof_m(T& structure) -{ - static_assert(is_trivially_copyable::value, "Can only byte-copy POD-ish structs"); - return &reinterpret_cast(structure); -} - -template -const uint8_t *pod_addressof_c(const T& structure) -{ - static_assert(is_trivially_copyable::value, "Can only byte-copy POD-ish structs"); - return &reinterpret_cast(structure); -} - - -/// Check how much can be read -inline -size_t RFIFOREST(Session *s) -{ - return s->rdata_size - s->rdata_pos; -} -/// Read from the queue -inline -const void *RFIFOP(Session *s, size_t pos) -{ - return &s->rdata[s->rdata_pos + pos]; -} -inline -uint8_t RFIFOB(Session *s, size_t pos) -{ - return *static_cast(RFIFOP(s, pos)); -} -inline -uint16_t RFIFOW(Session *s, size_t pos) -{ - return *static_cast(RFIFOP(s, pos)); -} -inline -uint32_t RFIFOL(Session *s, size_t pos) -{ - return *static_cast(RFIFOP(s, pos)); -} -template -void RFIFO_STRUCT(Session *s, size_t pos, T& structure) -{ - really_memcpy(pod_addressof_m(structure), static_cast(RFIFOP(s, pos)), sizeof(T)); -} -inline -IP4Address RFIFOIP(Session *s, size_t pos) -{ - IP4Address o; - RFIFO_STRUCT(s, pos, o); - return o; -} -template -inline -VString RFIFO_STRING(Session *s, size_t pos) -{ - const char *const begin = static_cast(RFIFOP(s, pos)); - const char *const end = begin + len-1; - const char *const mid = std::find(begin, end, '\0'); - return XString(begin, mid, nullptr); -} -inline -AString RFIFO_STRING(Session *s, size_t pos, size_t len) -{ - const char *const begin = static_cast(RFIFOP(s, pos)); - const char *const end = begin + len; - const char *const mid = std::find(begin, end, '\0'); - return XString(begin, mid, nullptr); -} -inline -void RFIFO_BUF_CLONE(Session *s, uint8_t *buf, size_t len) -{ - really_memcpy(buf, static_cast(RFIFOP(s, 0)), len); -} - -/// Done reading -void RFIFOSKIP(Session *s, size_t len); - -/// Read from an arbitrary buffer -inline -const void *RBUFP(const uint8_t *p, size_t pos) -{ - return p + pos; -} -inline -uint8_t RBUFB(const uint8_t *p, size_t pos) -{ - return *static_cast(RBUFP(p, pos)); -} -inline -uint16_t RBUFW(const uint8_t *p, size_t pos) -{ - return *static_cast(RBUFP(p, pos)); -} -inline -uint32_t RBUFL(const uint8_t *p, size_t pos) -{ - return *static_cast(RBUFP(p, pos)); -} -template -void RBUF_STRUCT(const uint8_t *p, size_t pos, T& structure) -{ - really_memcpy(pod_addressof_m(structure), p + pos, sizeof(T)); -} -inline -IP4Address RBUFIP(const uint8_t *p, size_t pos) -{ - IP4Address o; - RBUF_STRUCT(p, pos, o); - return o; -} -template -inline -VString RBUF_STRING(const uint8_t *p, size_t pos) -{ - const char *const begin = static_cast(RBUFP(p, pos)); - const char *const end = begin + len-1; - const char *const mid = std::find(begin, end, '\0'); - return XString(begin, mid, nullptr); -} -inline -AString RBUF_STRING(const uint8_t *p, size_t pos, size_t len) -{ - const char *const begin = static_cast(RBUFP(p, pos)); - const char *const end = begin + len; - const char *const mid = std::find(begin, end, '\0'); - return XString(begin, mid, nullptr); -} - - -/// Unused - check how much data can be written -// the existence of this seems scary -inline -size_t WFIFOSPACE(Session *s) -{ - return s->max_wdata - s->wdata_size; -} -/// Write to the queue -inline -void *WFIFOP(Session *s, size_t pos) -{ - return &s->wdata[s->wdata_size + pos]; -} -inline -uint8_t& WFIFOB(Session *s, size_t pos) -{ - return *static_cast(WFIFOP(s, pos)); -} -inline -uint16_t& WFIFOW(Session *s, size_t pos) -{ - return *static_cast(WFIFOP(s, pos)); -} -inline -uint32_t& WFIFOL(Session *s, size_t pos) -{ - return *static_cast(WFIFOP(s, pos)); -} -template -void WFIFO_STRUCT(Session *s, size_t pos, T& structure) -{ - really_memcpy(static_cast(WFIFOP(s, pos)), pod_addressof_c(structure), sizeof(T)); -} -inline -IP4Address& WFIFOIP(Session *s, size_t pos) -{ - static_assert(is_trivially_copyable::value, "That was the whole point"); - return *static_cast(WFIFOP(s, pos)); -} -inline -void WFIFO_STRING(Session *s, size_t pos, XString str, size_t len) -{ - char *const begin = static_cast(WFIFOP(s, pos)); - char *const end = begin + len; - char *const mid = std::copy(str.begin(), str.end(), begin); - std::fill(mid, end, '\0'); -} -inline -void WFIFO_ZERO(Session *s, size_t pos, size_t len) -{ - uint8_t *b = static_cast(WFIFOP(s, pos)); - uint8_t *e = b + len; - std::fill(b, e, '\0'); -} -inline -void WFIFO_BUF_CLONE(Session *s, const uint8_t *buf, size_t len) -{ - really_memcpy(static_cast(WFIFOP(s, 0)), buf, len); -} - -/// Finish writing -void WFIFOSET(Session *s, size_t len); - -/// Write to an arbitrary buffer -inline -void *WBUFP(uint8_t *p, size_t pos) -{ - return p + pos; -} -inline -uint8_t& WBUFB(uint8_t *p, size_t pos) -{ - return *static_cast(WBUFP(p, pos)); -} -inline -uint16_t& WBUFW(uint8_t *p, size_t pos) -{ - return *static_cast(WBUFP(p, pos)); -} -inline -uint32_t& WBUFL(uint8_t *p, size_t pos) -{ - return *static_cast(WBUFP(p, pos)); -} -template -void WBUF_STRUCT(uint8_t *p, size_t pos, T& structure) -{ - really_memcpy(p + pos, pod_addressof_c(structure), sizeof(T)); -} -inline -IP4Address& WBUFIP(uint8_t *p, size_t pos) -{ - return *static_cast(WBUFP(p, pos)); -} -inline -void WBUF_STRING(uint8_t *p, size_t pos, XString s, size_t len) -{ - char *const begin = static_cast(WBUFP(p, pos)); - char *const end = begin + len; - char *const mid = std::copy(s.begin(), s.end(), begin); - std::fill(mid, end, '\0'); -} -inline -void WBUF_ZERO(uint8_t *p, size_t pos, size_t len) -{ - uint8_t *b = static_cast(WBUFP(p, pos)); - uint8_t *e = b + len; - std::fill(b, e, '\0'); -} - -inline -void RFIFO_WFIFO_CLONE(Session *rs, Session *ws, size_t len) -{ - really_memcpy(static_cast(WFIFOP(ws, 0)), - static_cast(RFIFOP(rs, 0)), len); -} - -#endif // TMWA_MMO_SOCKET_HPP diff --git a/src/mmo/timer.cpp b/src/mmo/timer.cpp deleted file mode 100644 index a85b8be..0000000 --- a/src/mmo/timer.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include "timer.hpp" -// timer.cpp - Future event scheduler. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include -#include - -#include - -#include -#include - -#include "../strings/zstring.hpp" - -#include "../poison.hpp" - -struct TimerData -{ - /// This will be reset on call, to avoid problems. - Timer *owner; - - /// When it will be triggered - tick_t tick; - /// What will be done - timer_func func; - /// Repeat rate - 0 for oneshot - interval_t interval; - - TimerData(Timer *o, tick_t t, timer_func f, interval_t i) - : owner(o) - , tick(t) - , func(std::move(f)) - , interval(i) - {} -}; - -struct TimerCompare -{ - /// implement "less than" - bool operator() (dumb_ptr l, dumb_ptr r) - { - // C++ provides a max-heap, but we want - // the smallest tick to be the head (a min-heap). - return l->tick > r->tick; - } -}; - -static -std::priority_queue, std::vector>, TimerCompare> timer_heap; - - -tick_t gettick_cache; - -tick_t milli_clock::now(void) noexcept -{ - struct timeval tval; - // BUG: This will cause strange behavior if the system clock is changed! - // it should be reimplemented in terms of clock_gettime(CLOCK_MONOTONIC, ) - gettimeofday(&tval, NULL); - return gettick_cache = tick_t(std::chrono::seconds(tval.tv_sec) - + std::chrono::duration_cast( - std::chrono::microseconds(tval.tv_usec))); -} - -static -void do_nothing(TimerData *, tick_t) -{ -} - -void Timer::cancel() -{ - if (!td) - return; - - assert (this == td->owner); - td->owner = nullptr; - td->func = do_nothing; - td->interval = interval_t::zero(); - td = nullptr; -} - -void Timer::detach() -{ - assert (this == td->owner); - td->owner = nullptr; - td = nullptr; -} - -static -void push_timer_heap(dumb_ptr td) -{ - timer_heap.push(td); -} - -static -dumb_ptr top_timer_heap(void) -{ - if (timer_heap.empty()) - return dumb_ptr(); - return timer_heap.top(); -} - -static -void pop_timer_heap(void) -{ - timer_heap.pop(); -} - -Timer::Timer(tick_t tick, timer_func func, interval_t interval) -: td(dumb_ptr::make(this, tick, std::move(func), interval)) -{ - assert (interval >= interval_t::zero()); - - push_timer_heap(td); -} - -Timer::Timer(Timer&& t) -: td(t.td) -{ - t.td = nullptr; - if (td) - { - assert (td->owner == &t); - td->owner = this; - } -} - -Timer& Timer::operator = (Timer&& t) -{ - std::swap(td, t.td); - if (td) - { - assert (td->owner == &t); - td->owner = this; - } - if (t.td) - { - assert (t.td->owner == this); - t.td->owner = &t; - } - return *this; -} - -interval_t do_timer(tick_t tick) -{ - /// Number of milliseconds until it calls this again - // this says to wait 1 sec if all timers get popped - interval_t nextmin = std::chrono::seconds(1); - - while (dumb_ptr td = top_timer_heap()) - { - // while the heap is not empty and - if (td->tick > tick) - { - /// Return the time until the next timer needs to goes off - nextmin = td->tick - tick; - break; - } - pop_timer_heap(); - - // Prevent destroying the object we're in. - // Note: this would be surprising in an interval timer, - // but all interval timers do an immediate explicit detach(). - if (td->owner) - td->owner->detach(); - // If we are too far past the requested tick, call with - // the current tick instead to fix reregistration problems - if (td->tick + std::chrono::seconds(1) < tick) - td->func(td.operator->(), tick); - else - td->func(td.operator->(), td->tick); - - if (td->interval == interval_t::zero()) - { - td.delete_(); - continue; - } - if (td->tick + std::chrono::seconds(1) < tick) - td->tick = tick + td->interval; - else - td->tick += td->interval; - push_timer_heap(td); - } - - return std::max(nextmin, std::chrono::milliseconds(10)); -} - -tick_t file_modified(ZString name) -{ - struct stat buf; - if (stat(name.c_str(), &buf)) - return tick_t(); - return tick_t(std::chrono::seconds(buf.st_mtime)); -} - -bool has_timers() -{ - return !timer_heap.empty(); -} diff --git a/src/mmo/timer.hpp b/src/mmo/timer.hpp deleted file mode 100644 index ddb559f..0000000 --- a/src/mmo/timer.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef TMWA_MMO_TIMER_HPP -#define TMWA_MMO_TIMER_HPP -// timer.hpp - Future event scheduler. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -# include "timer.t.hpp" - -# include "fwd.hpp" - -# include "../strings/fwd.hpp" - -// updated automatically when using milli_clock::now() -// which is done only by core.cpp -extern tick_t gettick_cache; - -inline -tick_t gettick(void) -{ - return gettick_cache; -} - -/// Do all timers scheduled before tick, and return the number of -/// milliseconds until the next timer happens -interval_t do_timer(tick_t tick); - -/// Stat a file, and return its modification time, truncated to seconds. -tick_t file_modified(ZString name); - -/// Check if there are any events at all scheduled. -bool has_timers(); - -#endif // TMWA_MMO_TIMER_HPP diff --git a/src/mmo/timer.t.hpp b/src/mmo/timer.t.hpp deleted file mode 100644 index 549959a..0000000 --- a/src/mmo/timer.t.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef TMWA_MMO_TIMER_T_HPP -#define TMWA_MMO_TIMER_T_HPP -// timer.t.hpp - Future event scheduler. -// -// Copyright © ????-2004 Athena Dev Teams -// Copyright © 2004-2011 The Mana World Development Team -// Copyright © 2011-2014 Ben Longbons -// -// This file is part of The Mana World (Athena server) -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -# include "fwd.hpp" - -# include -# include - -# include "dumb_ptr.hpp" - -/// An implementation of the C++ "clock" concept, exposing -/// durations in milliseconds. -class milli_clock -{ -public: - typedef std::chrono::milliseconds duration; - typedef duration::rep rep; - typedef duration::period period; - typedef std::chrono::time_point time_point; - static const bool is_steady = true; // assumed - not necessarily true - - static time_point now() noexcept; -}; - -/// A point in time. -typedef milli_clock::time_point tick_t; -/// The difference between two points in time. -typedef milli_clock::duration interval_t; -/// (to get additional arguments, use std::bind or a lambda). -typedef std::function timer_func; - -class Timer -{ - friend struct TimerData; - dumb_ptr td; - - Timer(const Timer&) = delete; - Timer& operator = (const Timer&) = delete; -public: - /// Don't own anything yet. - Timer() = default; - /// Schedule a timer for the given tick. - /// If you do not wish to keep track of it, call disconnect(). - /// Otherwise, you may cancel() or replace (operator =) it later. - /// - /// If the interval argument is given, the timer will reschedule - /// itself again forever. Otherwise, it will disconnect() itself - /// just BEFORE it is called. - Timer(tick_t tick, timer_func func, interval_t interval=interval_t::zero()); - - Timer(Timer&& t); - Timer& operator = (Timer&& t); - ~Timer() { cancel(); } - - /// Cancel the delivery of this timer's function, and make it falsy. - /// Implementation note: this doesn't actually remove it, just sets - /// the functor to do_nothing, and waits for the tick before removing. - void cancel(); - /// Make it falsy without cancelling the timer, - void detach(); - - /// Check if there is a timer connected. - explicit operator bool() { return bool(td); } - /// Check if there is no connected timer. - bool operator !() { return !td; } -}; - -#endif // TMWA_MMO_TIMER_T_HPP diff --git a/src/net/fwd.hpp b/src/net/fwd.hpp new file mode 100644 index 0000000..ca54e8d --- /dev/null +++ b/src/net/fwd.hpp @@ -0,0 +1,30 @@ +#ifndef TMWA_NET_FWD_HPP +#define TMWA_NET_FWD_HPP +// net/fwd.hpp - list of type names for net lib +// +// Copyright © 2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +# include "../sanity.hpp" + +class Session; + +class IP4Address; + +class TimerData; + +#endif // TMWA_NET_FWD_HPP diff --git a/src/net/ip.cpp b/src/net/ip.cpp new file mode 100644 index 0000000..591faab --- /dev/null +++ b/src/net/ip.cpp @@ -0,0 +1,116 @@ +#include "ip.hpp" +// ip.cpp - Implementation of IP address functions. +// +// Copyright © 2013 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../strings/xstring.hpp" +#include "../strings/vstring.hpp" + +#include "../io/cxxstdio.hpp" + +#include "../mmo/extract.hpp" + +#include "../poison.hpp" + +bool extract(XString str, IP4Address *rv) +{ + if (str.endswith('.')) + return false; + uint8_t buf[4]; + if (extract(str, record<'.'>(&buf[0], &buf[1], &buf[2], &buf[3]))) + { + *rv = IP4Address(buf); + return true; + } + return false; +} + +bool extract(XString str, IP4Mask *rv) +{ + IP4Address a, m; + unsigned b; + XString l, r; + if (str.endswith('/')) + return false; + if (extract(str, record<'/'>(&l, &r))) + { + // a.b.c.d/e.f.g.h or a.b.c.d/n + if (!extract(l, &a)) + return false; + if (extract(r, &m)) + { + *rv = IP4Mask(a, m); + return true; + } + if (!extract(r, &b) || b > 32) + return false; + } + else + { + // a.b.c.d or a.b.c.d. or a.b.c. or a.b. or a. + if (extract(str, &a)) + { + *rv = IP4Mask(a, IP4_BROADCAST); + return true; + } + if (!str.endswith('.')) + return false; + uint8_t d[4] {}; + if (extract(str, record<'.'>(&d[0], &d[1], &d[2], &d[3]))) + b = 32; + else if (extract(str, record<'.'>(&d[0], &d[1], &d[2]))) + b = 24; + else if (extract(str, record<'.'>(&d[0], &d[1]))) + b = 16; + else if (extract(str, record<'.'>(&d[0]))) + b = 8; + else + return false; + a = IP4Address(d); + } + // a is set; need to construct m from b + if (b == 0) + m = IP4Address(); + else if (b == 32) + m = IP4_BROADCAST; + else + { + uint32_t s = -1; + s <<= (32 - b); + m = IP4Address({ + static_cast(s >> 24), + static_cast(s >> 16), + static_cast(s >> 8), + static_cast(s >> 0), + }); + } + *rv = IP4Mask(a, m); + return true; +} + +VString<15> convert_for_printf(IP4Address a_) +{ + const uint8_t *a = a_.bytes(); + return STRNPRINTF(16, "%hhu.%hhu.%hhu.%hhu"_fmt, a[0], a[1], a[2], a[3]); +} + +VString<31> convert_for_printf(IP4Mask a) +{ + return STRNPRINTF(32, "%s/%s"_fmt, + a.addr(), a.mask()); +} diff --git a/src/net/ip.hpp b/src/net/ip.hpp new file mode 100644 index 0000000..42a4689 --- /dev/null +++ b/src/net/ip.hpp @@ -0,0 +1,165 @@ +#ifndef TMWA_NET_IP_HPP +#define TMWA_NET_IP_HPP +// ip.hpp - classes to deal with IP addresses. +// +// Copyright © 2013 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +# include "fwd.hpp" + +# include + +# include +# include + +# include "../strings/fwd.hpp" + +// TODO - in the long run ports belong here also +// and of course, IPv6 stuff. +// But what about unix socket addresses? + +/// Helper function +template +constexpr +bool _ce_a_lt(T (&a)[n], T (&b)[n], size_t i=0) +{ + return (i != n + && (a[i] < b[i] + || (a[i] == b[i] + && _ce_a_lt(a, b, i + 1)))); +} + +/// A 32-bit Ipv4 address. Does not include a port. +/// Guaranteed to be laid out like the network wants. +class IP4Address +{ + uint8_t _addr[4]; +public: + constexpr + IP4Address() + : _addr{} + {} + constexpr explicit + IP4Address(const uint8_t (&a)[4]) + : _addr{a[0], a[1], a[2], a[3]} + {} + explicit + IP4Address(struct in_addr addr) + { + static_assert(sizeof(addr) == sizeof(_addr), "4 bytes"); + *this = IP4Address(reinterpret_cast(addr)); + } + explicit + operator struct in_addr() const + { + return reinterpret_cast(_addr); + } + + constexpr friend + IP4Address operator & (IP4Address l, IP4Address r) + { + return IP4Address({ + static_cast(l._addr[0] & r._addr[0]), + static_cast(l._addr[1] & r._addr[1]), + static_cast(l._addr[2] & r._addr[2]), + static_cast(l._addr[3] & r._addr[3]), + }); + } + + IP4Address& operator &= (IP4Address m) + { return *this = *this & m; } + + const uint8_t *bytes() const + { return _addr; } + + constexpr friend + bool operator < (IP4Address l, IP4Address r) + { + return _ce_a_lt(l._addr, r._addr); + } + + constexpr friend + bool operator > (IP4Address l, IP4Address r) + { + return _ce_a_lt(r._addr, l._addr); + } + + constexpr friend + bool operator >= (IP4Address l, IP4Address r) + { + return !_ce_a_lt(l._addr, r._addr); + } + + constexpr friend + bool operator <= (IP4Address l, IP4Address r) + { + return !_ce_a_lt(r._addr, l._addr); + } + + constexpr friend + bool operator == (IP4Address l, IP4Address r) + { + return !(l < r || r < l); + } + + constexpr friend + bool operator != (IP4Address l, IP4Address r) + { + return (l < r || r < l); + } +}; + +class IP4Mask +{ + IP4Address _addr, _mask; +public: + constexpr + IP4Mask() : _addr(), _mask() + {} + constexpr + IP4Mask(IP4Address a, IP4Address m) : _addr(a & m), _mask(m) + {} + + constexpr + IP4Address addr() const + { return _addr; } + constexpr + IP4Address mask() const + { return _mask; } + + constexpr + bool covers(IP4Address a) const + { + return (a & _mask) == _addr; + } +}; + + +constexpr +IP4Address IP4_LOCALHOST({127, 0, 0, 1}); +constexpr +IP4Address IP4_BROADCAST({255, 255, 255, 255}); + + +VString<15> convert_for_printf(IP4Address a); +VString<31> convert_for_printf(IP4Mask m); + +bool extract(XString str, IP4Address *iv); + +bool extract(XString str, IP4Mask *iv); + +#endif // TMWA_NET_IP_HPP diff --git a/src/net/ip.py b/src/net/ip.py new file mode 100644 index 0000000..e6a8183 --- /dev/null +++ b/src/net/ip.py @@ -0,0 +1,14 @@ +class IP4Address(object): + ''' print an IP4Address + ''' + __slots__ = ('_value') + name = 'IP4Address' + enabled = True + + def __init__(self, value): + self._value = value + + def to_string(self): + addr = self._value['_addr'] + addr = tuple(int(addr[i]) for i in range(4)) + return '%d.%d.%d.%d' % addr diff --git a/src/net/ip_test.cpp b/src/net/ip_test.cpp new file mode 100644 index 0000000..3c4d2ef --- /dev/null +++ b/src/net/ip_test.cpp @@ -0,0 +1,355 @@ +#include "ip.hpp" +// ip_test.cpp - Testsuite for implementation of IP address functions. +// +// Copyright © 2013 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "../strings/vstring.hpp" +#include "../strings/literal.hpp" + +#include "../io/cxxstdio.hpp" + +#include "../poison.hpp" + +#define CB(X) (std::integral_constant::value) +TEST(ip4addr, cmp) +{ + constexpr static + IP4Address a = IP4_LOCALHOST; + constexpr static + IP4Address b = IP4_BROADCAST; + + EXPECT_FALSE(CB(a < a)); + EXPECT_TRUE (CB(a < b)); + EXPECT_FALSE(CB(b < a)); + EXPECT_FALSE(CB(b < b)); + + EXPECT_FALSE(CB(a > a)); + EXPECT_FALSE(CB(a > b)); + EXPECT_TRUE (CB(b > a)); + EXPECT_FALSE(CB(b > b)); + + EXPECT_TRUE (CB(a <= a)); + EXPECT_TRUE (CB(a <= b)); + EXPECT_FALSE(CB(b <= a)); + EXPECT_TRUE (CB(b <= b)); + + EXPECT_TRUE (CB(a >= a)); + EXPECT_FALSE(CB(a >= b)); + EXPECT_TRUE (CB(b >= a)); + EXPECT_TRUE (CB(b >= b)); + + EXPECT_TRUE (CB(a == a)); + EXPECT_FALSE(CB(a == b)); + EXPECT_FALSE(CB(b == a)); + EXPECT_TRUE (CB(b == b)); + + EXPECT_FALSE(CB(a != a)); + EXPECT_TRUE (CB(a != b)); + EXPECT_TRUE (CB(b != a)); + EXPECT_FALSE(CB(b != b)); +} + +TEST(ip4addr, str) +{ + IP4Address a; + EXPECT_EQ("0.0.0.0"_s, STRNPRINTF(17, "%s"_fmt, a)); + EXPECT_EQ("127.0.0.1"_s, STRNPRINTF(17, "%s"_fmt, IP4_LOCALHOST)); + EXPECT_EQ("255.255.255.255"_s, STRNPRINTF(17, "%s"_fmt, IP4_BROADCAST)); +} + +TEST(ip4addr, extract) +{ + IP4Address a; + EXPECT_TRUE(extract("0.0.0.0"_s, &a)); + EXPECT_EQ("0.0.0.0"_s, STRNPRINTF(16, "%s"_fmt, a)); + EXPECT_TRUE(extract("127.0.0.1"_s, &a)); + EXPECT_EQ("127.0.0.1"_s, STRNPRINTF(16, "%s"_fmt, a)); + EXPECT_TRUE(extract("255.255.255.255"_s, &a)); + EXPECT_EQ("255.255.255.255"_s, STRNPRINTF(16, "%s"_fmt, a)); + EXPECT_TRUE(extract("1.2.3.4"_s, &a)); + EXPECT_EQ("1.2.3.4"_s, STRNPRINTF(16, "%s"_fmt, a)); + + EXPECT_FALSE(extract("1.2.3.4.5"_s, &a)); + EXPECT_FALSE(extract("1.2.3.4."_s, &a)); + EXPECT_FALSE(extract("1.2.3."_s, &a)); + EXPECT_FALSE(extract("1.2.3"_s, &a)); + EXPECT_FALSE(extract("1.2."_s, &a)); + EXPECT_FALSE(extract("1.2"_s, &a)); + EXPECT_FALSE(extract("1."_s, &a)); + EXPECT_FALSE(extract("1"_s, &a)); + EXPECT_FALSE(extract(""_s, &a)); +} + + +TEST(ip4mask, body) +{ + IP4Mask m; + EXPECT_EQ(IP4Address(), m.addr()); + EXPECT_EQ(IP4Address(), m.mask()); + m = IP4Mask(IP4_LOCALHOST, IP4_BROADCAST); + EXPECT_EQ(IP4_LOCALHOST, m.addr()); + EXPECT_EQ(IP4_BROADCAST, m.mask()); +} + +TEST(ip4mask, str) +{ + IP4Mask m; + EXPECT_EQ("0.0.0.0/0.0.0.0"_s, STRNPRINTF(33, "%s"_fmt, m)); + m = IP4Mask(IP4_LOCALHOST, IP4_BROADCAST); + EXPECT_EQ("127.0.0.1/255.255.255.255"_s, STRNPRINTF(33, "%s"_fmt, m)); +} + +TEST(ip4mask, extract) +{ + IP4Mask m; + EXPECT_FALSE(extract("9.8.7.6/33"_s, &m)); + EXPECT_FALSE(extract("9.8.7.6.5"_s, &m)); + EXPECT_FALSE(extract("9.8.7.6/"_s, &m)); + EXPECT_FALSE(extract("9.8.7"_s, &m)); + EXPECT_FALSE(extract("9.8"_s, &m)); + EXPECT_FALSE(extract("9"_s, &m)); + + EXPECT_TRUE(extract("127.0.0.1"_s, &m)); + EXPECT_EQ("127.0.0.1/255.255.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("127.0.0.1."_s, &m)); + EXPECT_EQ("127.0.0.1/255.255.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("127.0.0."_s, &m)); + EXPECT_EQ("127.0.0.0/255.255.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("127.0."_s, &m)); + EXPECT_EQ("127.0.0.0/255.255.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("127."_s, &m)); + EXPECT_EQ("127.0.0.0/255.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + + EXPECT_TRUE(extract("1.2.3.4/255.255.255.255"_s, &m)); + EXPECT_EQ("1.2.3.4/255.255.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("1.2.3.0/255.255.255.0"_s, &m)); + EXPECT_EQ("1.2.3.0/255.255.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("1.2.0.4/255.255.0.255"_s, &m)); + EXPECT_EQ("1.2.0.4/255.255.0.255"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("1.2.0.0/255.255.0.0"_s, &m)); + EXPECT_EQ("1.2.0.0/255.255.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("1.0.3.4/255.0.255.255"_s, &m)); + EXPECT_EQ("1.0.3.4/255.0.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("1.0.3.0/255.0.255.0"_s, &m)); + EXPECT_EQ("1.0.3.0/255.0.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("1.0.0.4/255.0.0.255"_s, &m)); + EXPECT_EQ("1.0.0.4/255.0.0.255"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("1.0.0.0/255.0.0.0"_s, &m)); + EXPECT_EQ("1.0.0.0/255.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.2.3.4/0.255.255.255"_s, &m)); + EXPECT_EQ("0.2.3.4/0.255.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.2.3.0/0.255.255.0"_s, &m)); + EXPECT_EQ("0.2.3.0/0.255.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.2.0.4/0.255.0.255"_s, &m)); + EXPECT_EQ("0.2.0.4/0.255.0.255"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.2.0.0/0.255.0.0"_s, &m)); + EXPECT_EQ("0.2.0.0/0.255.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.3.4/0.0.255.255"_s, &m)); + EXPECT_EQ("0.0.3.4/0.0.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.3.0/0.0.255.0"_s, &m)); + EXPECT_EQ("0.0.3.0/0.0.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.4/0.0.0.255"_s, &m)); + EXPECT_EQ("0.0.0.4/0.0.0.255"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/0.0.0.0"_s, &m)); + EXPECT_EQ("0.0.0.0/0.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + + // please don't do this + EXPECT_TRUE(extract("120.248.200.217/89.57.126.5"_s, &m)); + EXPECT_EQ("88.56.72.1/89.57.126.5"_s, STRNPRINTF(32, "%s"_fmt, m)); + + EXPECT_TRUE(extract("0.0.0.0/32"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.255.255"_s, STRNPRINTF(32, "%s"_fmt, m)); + + EXPECT_TRUE(extract("0.0.0.0/31"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.255.254"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/30"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.255.252"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/29"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.255.248"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/28"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.255.240"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/27"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.255.224"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/26"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.255.192"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/25"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.255.128"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/24"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.255.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + + EXPECT_TRUE(extract("0.0.0.0/23"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.254.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/22"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.252.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/21"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.248.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/20"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.240.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/19"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.224.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/18"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.192.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/17"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.128.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/16"_s, &m)); + EXPECT_EQ("0.0.0.0/255.255.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + + EXPECT_TRUE(extract("0.0.0.0/15"_s, &m)); + EXPECT_EQ("0.0.0.0/255.254.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/14"_s, &m)); + EXPECT_EQ("0.0.0.0/255.252.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/13"_s, &m)); + EXPECT_EQ("0.0.0.0/255.248.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/12"_s, &m)); + EXPECT_EQ("0.0.0.0/255.240.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/11"_s, &m)); + EXPECT_EQ("0.0.0.0/255.224.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/10"_s, &m)); + EXPECT_EQ("0.0.0.0/255.192.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/9"_s, &m)); + EXPECT_EQ("0.0.0.0/255.128.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/8"_s, &m)); + EXPECT_EQ("0.0.0.0/255.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + + EXPECT_TRUE(extract("0.0.0.0/7"_s, &m)); + EXPECT_EQ("0.0.0.0/254.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/6"_s, &m)); + EXPECT_EQ("0.0.0.0/252.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/5"_s, &m)); + EXPECT_EQ("0.0.0.0/248.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/4"_s, &m)); + EXPECT_EQ("0.0.0.0/240.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/3"_s, &m)); + EXPECT_EQ("0.0.0.0/224.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/2"_s, &m)); + EXPECT_EQ("0.0.0.0/192.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/1"_s, &m)); + EXPECT_EQ("0.0.0.0/128.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); + EXPECT_TRUE(extract("0.0.0.0/0"_s, &m)); + EXPECT_EQ("0.0.0.0/0.0.0.0"_s, STRNPRINTF(32, "%s"_fmt, m)); +} + +TEST(ip4mask, cover) +{ + IP4Address a; + IP4Address b = IP4_BROADCAST; + IP4Address l = IP4_LOCALHOST; + IP4Address h({127, 255, 255, 255}); + IP4Address p24l({10, 0, 0, 0}); + IP4Address p24h({10, 255, 255, 255}); + IP4Address p20l({172, 16, 0, 0}); + IP4Address p20h({172, 31, 255, 255}); + IP4Address p16l({192, 168, 0, 0}); + IP4Address p16h({192, 168, 255, 255}); + IP4Mask m; + EXPECT_TRUE(m.covers(a)); + EXPECT_TRUE(m.covers(b)); + EXPECT_TRUE(m.covers(l)); + EXPECT_TRUE(m.covers(h)); + EXPECT_TRUE(m.covers(p24l)); + EXPECT_TRUE(m.covers(p24h)); + EXPECT_TRUE(m.covers(p20l)); + EXPECT_TRUE(m.covers(p20h)); + EXPECT_TRUE(m.covers(p16l)); + EXPECT_TRUE(m.covers(p16h)); + m = IP4Mask(l, a); + EXPECT_TRUE(m.covers(a)); + EXPECT_TRUE(m.covers(b)); + EXPECT_TRUE(m.covers(l)); + EXPECT_TRUE(m.covers(h)); + EXPECT_TRUE(m.covers(p24l)); + EXPECT_TRUE(m.covers(p24h)); + EXPECT_TRUE(m.covers(p20l)); + EXPECT_TRUE(m.covers(p20h)); + EXPECT_TRUE(m.covers(p16l)); + EXPECT_TRUE(m.covers(p16h)); + m = IP4Mask(l, b); + EXPECT_FALSE(m.covers(a)); + EXPECT_FALSE(m.covers(b)); + EXPECT_TRUE(m.covers(l)); + EXPECT_FALSE(m.covers(h)); + EXPECT_FALSE(m.covers(p24l)); + EXPECT_FALSE(m.covers(p24h)); + EXPECT_FALSE(m.covers(p20l)); + EXPECT_FALSE(m.covers(p20h)); + EXPECT_FALSE(m.covers(p16l)); + EXPECT_FALSE(m.covers(p16h)); + + // but the really useful ones are with partial masks + m = IP4Mask(IP4Address({10, 0, 0, 0}), IP4Address({255, 0, 0, 0})); + EXPECT_FALSE(m.covers(a)); + EXPECT_FALSE(m.covers(b)); + EXPECT_FALSE(m.covers(l)); + EXPECT_FALSE(m.covers(h)); + EXPECT_TRUE(m.covers(p24l)); + EXPECT_TRUE(m.covers(p24h)); + EXPECT_FALSE(m.covers(p20l)); + EXPECT_FALSE(m.covers(p20h)); + EXPECT_FALSE(m.covers(p16l)); + EXPECT_FALSE(m.covers(p16h)); + EXPECT_FALSE(m.covers(IP4Address({9, 255, 255, 255}))); + EXPECT_FALSE(m.covers(IP4Address({11, 0, 0, 0}))); + m = IP4Mask(IP4Address({127, 0, 0, 0}), IP4Address({255, 0, 0, 0})); + EXPECT_FALSE(m.covers(a)); + EXPECT_FALSE(m.covers(b)); + EXPECT_TRUE(m.covers(l)); + EXPECT_TRUE(m.covers(h)); + EXPECT_FALSE(m.covers(p24l)); + EXPECT_FALSE(m.covers(p24h)); + EXPECT_FALSE(m.covers(p20l)); + EXPECT_FALSE(m.covers(p20h)); + EXPECT_FALSE(m.covers(p16l)); + EXPECT_FALSE(m.covers(p16h)); + EXPECT_FALSE(m.covers(IP4Address({126, 255, 255, 255}))); + EXPECT_FALSE(m.covers(IP4Address({128, 0, 0, 0}))); + m = IP4Mask(IP4Address({172, 16, 0, 0}), IP4Address({255, 240, 0, 0})); + EXPECT_FALSE(m.covers(a)); + EXPECT_FALSE(m.covers(b)); + EXPECT_FALSE(m.covers(l)); + EXPECT_FALSE(m.covers(h)); + EXPECT_FALSE(m.covers(p24l)); + EXPECT_FALSE(m.covers(p24h)); + EXPECT_TRUE(m.covers(p20l)); + EXPECT_TRUE(m.covers(p20h)); + EXPECT_FALSE(m.covers(p16l)); + EXPECT_FALSE(m.covers(p16h)); + EXPECT_FALSE(m.covers(IP4Address({172, 15, 255, 255}))); + EXPECT_FALSE(m.covers(IP4Address({172, 32, 0, 0}))); + m = IP4Mask(IP4Address({192, 168, 0, 0}), IP4Address({255, 255, 0, 0})); + EXPECT_FALSE(m.covers(a)); + EXPECT_FALSE(m.covers(b)); + EXPECT_FALSE(m.covers(l)); + EXPECT_FALSE(m.covers(h)); + EXPECT_FALSE(m.covers(p24l)); + EXPECT_FALSE(m.covers(p24h)); + EXPECT_FALSE(m.covers(p20l)); + EXPECT_FALSE(m.covers(p20h)); + EXPECT_TRUE(m.covers(p16l)); + EXPECT_TRUE(m.covers(p16h)); + EXPECT_FALSE(m.covers(IP4Address({192, 167, 255, 255}))); + EXPECT_FALSE(m.covers(IP4Address({192, 169, 0, 0}))); + + // OTOH this is crazy + EXPECT_TRUE(extract("120.248.200.217/89.57.126.5"_s, &m)); + EXPECT_TRUE(m.covers(IP4Address({120, 248, 200, 217}))); + EXPECT_TRUE(m.covers(IP4Address({88, 56, 72, 1}))); + EXPECT_FALSE(m.covers(IP4Address({88, 56, 72, 0}))); + EXPECT_FALSE(m.covers(IP4Address({88, 56, 72, 255}))); +} diff --git a/src/net/socket.cpp b/src/net/socket.cpp new file mode 100644 index 0000000..6880bfa --- /dev/null +++ b/src/net/socket.cpp @@ -0,0 +1,511 @@ +#include "socket.hpp" +// socket.cpp - Network event system. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons +// Copyright © 2013 MadCamel +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include + +#include + +#include + +#include + +#include "../compat/memory.hpp" + +#include "../io/cxxstdio.hpp" + +// TODO get rid of ordering violations +#include "../mmo/utils.hpp" +#include "../mmo/core.hpp" + +#include "timer.hpp" + +#include "../poison.hpp" + +static +io::FD_Set readfds; +static +int fd_max; + +static +const uint32_t RFIFO_SIZE = 65536; +static +const uint32_t WFIFO_SIZE = 65536; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +static +std::array, FD_SETSIZE> session; +#pragma GCC diagnostic pop + +Session::Session(SessionIO io, SessionParsers p) +: created() +, connected() +, eof() +, timed_close() +, rdata(), wdata() +, max_rdata(), max_wdata() +, rdata_size(), wdata_size() +, rdata_pos() +, client_ip() +, func_recv() +, func_send() +, func_parse() +, func_delete() +, for_inferior() +, session_data() +, fd() +{ + set_io(io); + set_parsers(p); +} +void Session::set_io(SessionIO io) +{ + func_send = io.func_send; + func_recv = io.func_recv; +} +void Session::set_parsers(SessionParsers p) +{ + func_parse = p.func_parse; + func_delete = p.func_delete; +} + + +void set_session(io::FD fd, std::unique_ptr sess) +{ + int f = fd.uncast_dammit(); + assert (0 <= f && f < FD_SETSIZE); + session[f] = std::move(sess); +} +Session *get_session(io::FD fd) +{ + int f = fd.uncast_dammit(); + if (0 <= f && f < FD_SETSIZE) + return session[f].get(); + return nullptr; +} +void reset_session(io::FD fd) +{ + int f = fd.uncast_dammit(); + assert (0 <= f && f < FD_SETSIZE); + session[f] = nullptr; +} +int get_fd_max() { return fd_max; } +IteratorPair> iter_fds() +{ + return {io::FD::cast_dammit(0), io::FD::cast_dammit(fd_max)}; +} + +/// clean up by discarding handled bytes +inline +void RFIFOFLUSH(Session *s) +{ + really_memmove(&s->rdata[0], &s->rdata[s->rdata_pos], RFIFOREST(s)); + s->rdata_size = RFIFOREST(s); + s->rdata_pos = 0; +} + +/// how much room there is to read more data +inline +size_t RFIFOSPACE(Session *s) +{ + return s->max_rdata - s->rdata_size; +} + + +/// Read from socket to the queue +static +void recv_to_fifo(Session *s) +{ + ssize_t len = s->fd.read(&s->rdata[s->rdata_size], + RFIFOSPACE(s)); + + if (len > 0) + { + s->rdata_size += len; + s->connected = 1; + } + else + { + s->set_eof(); + } +} + +static +void send_from_fifo(Session *s) +{ + ssize_t len = s->fd.write(&s->wdata[0], s->wdata_size); + + if (len > 0) + { + s->wdata_size -= len; + if (s->wdata_size) + { + really_memmove(&s->wdata[0], &s->wdata[len], + s->wdata_size); + } + s->connected = 1; + } + else + { + s->set_eof(); + } +} + +static +void nothing_delete(Session *s) +{ + (void)s; +} + +static +void connect_client(Session *ls) +{ + struct sockaddr_in client_address; + socklen_t len = sizeof(client_address); + + io::FD fd = ls->fd.accept(reinterpret_cast(&client_address), &len); + if (fd == io::FD()) + { + perror("accept"); + return; + } + if (fd.uncast_dammit() >= SOFT_LIMIT) + { + FPRINTF(stderr, "softlimit reached, disconnecting : %d\n"_fmt, fd.uncast_dammit()); + fd.shutdown(SHUT_RDWR); + fd.close(); + return; + } + if (fd_max <= fd.uncast_dammit()) + { + fd_max = fd.uncast_dammit() + 1; + } + + const int yes = 1; + /// Allow to bind() again after the server restarts. + // Since the socket is still in the TIME_WAIT, there's a possibility + // that formerly lost packets might be delivered and confuse the server. + fd.setsockopt(SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + /// Send packets as soon as possible + /// even if the kernel thinks there is too little for it to be worth it! + /// Testing shows this is indeed a good idea. + fd.setsockopt(IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); + + // Linux-ism: Set socket options to optimize for thin streams + // See http://lwn.net/Articles/308919/ and + // Documentation/networking/tcp-thin.txt .. Kernel 3.2+ +#ifdef TCP_THIN_LINEAR_TIMEOUTS + fd.setsockopt(IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, &yes, sizeof yes); +#endif +#ifdef TCP_THIN_DUPACK + fd.setsockopt(IPPROTO_TCP, TCP_THIN_DUPACK, &yes, sizeof yes); +#endif + + readfds.set(fd); + + fd.fcntl(F_SETFL, O_NONBLOCK); + + set_session(fd, make_unique( + SessionIO{.func_recv= recv_to_fifo, .func_send= send_from_fifo}, + ls->for_inferior)); + Session *s = get_session(fd); + s->fd = fd; + s->rdata.new_(RFIFO_SIZE); + s->wdata.new_(WFIFO_SIZE); + s->max_rdata = RFIFO_SIZE; + s->max_wdata = WFIFO_SIZE; + s->client_ip = IP4Address(client_address.sin_addr); + s->created = TimeT::now(); + s->connected = 0; +} + +Session *make_listen_port(uint16_t port, SessionParsers inferior) +{ + struct sockaddr_in server_address; + io::FD fd = io::FD::socket(AF_INET, SOCK_STREAM, 0); + if (fd == io::FD()) + { + perror("socket"); + return nullptr; + } + if (fd_max <= fd.uncast_dammit()) + fd_max = fd.uncast_dammit() + 1; + + fd.fcntl(F_SETFL, O_NONBLOCK); + + const int yes = 1; + /// Allow to bind() again after the server restarts. + // Since the socket is still in the TIME_WAIT, there's a possibility + // that formerly lost packets might be delivered and confuse the server. + fd.setsockopt(SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + /// Send packets as soon as possible + /// even if the kernel thinks there is too little for it to be worth it! + // I'm not convinced this is a good idea; although in minimizes the + // latency for an individual write, it increases traffic in general. + fd.setsockopt(IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); + + server_address.sin_family = AF_INET; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#if __GNUC__ > 4 || __GNUC_MINOR__ >= 8 +# pragma GCC diagnostic ignored "-Wuseless-cast" +#endif + server_address.sin_addr.s_addr = htonl(INADDR_ANY); + server_address.sin_port = htons(port); +#pragma GCC diagnostic pop + + if (fd.bind(reinterpret_cast(&server_address), + sizeof(server_address)) == -1) + { + perror("bind"); + exit(1); + } + if (fd.listen(5) == -1) + { /* error */ + perror("listen"); + exit(1); + } + + readfds.set(fd); + + set_session(fd, make_unique( + SessionIO{.func_recv= connect_client, .func_send= nullptr}, + SessionParsers{.func_parse= nullptr, .func_delete= nothing_delete})); + Session *s = get_session(fd); + s->for_inferior = inferior; + s->fd = fd; + + s->created = TimeT::now(); + s->connected = 1; + + return s; +} + +Session *make_connection(IP4Address ip, uint16_t port, SessionParsers parsers) +{ + struct sockaddr_in server_address; + io::FD fd = io::FD::socket(AF_INET, SOCK_STREAM, 0); + if (fd == io::FD()) + { + perror("socket"); + return nullptr; + } + if (fd_max <= fd.uncast_dammit()) + fd_max = fd.uncast_dammit() + 1; + + const int yes = 1; + /// Allow to bind() again after the server restarts. + // Since the socket is still in the TIME_WAIT, there's a possibility + // that formerly lost packets might be delivered and confuse the server. + fd.setsockopt(SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + /// Send packets as soon as possible + /// even if the kernel thinks there is too little for it to be worth it! + // I'm not convinced this is a good idea; although in minimizes the + // latency for an individual write, it increases traffic in general. + fd.setsockopt(IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); + + server_address.sin_family = AF_INET; + server_address.sin_addr = in_addr(ip); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#if __GNUC__ > 4 || __GNUC_MINOR__ >= 8 +# pragma GCC diagnostic ignored "-Wuseless-cast" +#endif + server_address.sin_port = htons(port); +#pragma GCC diagnostic pop + + fd.fcntl(F_SETFL, O_NONBLOCK); + + /// Errors not caught - we must not block + /// Let the main select() loop detect when we know the state + fd.connect(reinterpret_cast(&server_address), + sizeof(struct sockaddr_in)); + + readfds.set(fd); + + set_session(fd, make_unique( + SessionIO{.func_recv= recv_to_fifo, .func_send= send_from_fifo}, + parsers)); + Session *s = get_session(fd); + s->fd = fd; + s->rdata.new_(RFIFO_SIZE); + s->wdata.new_(WFIFO_SIZE); + + s->max_rdata = RFIFO_SIZE; + s->max_wdata = WFIFO_SIZE; + s->created = TimeT::now(); + s->connected = 1; + + return s; +} + +void delete_session(Session *s) +{ + if (!s) + return; + // this needs to be before the fd_max-- + s->func_delete(s); + + io::FD fd = s->fd; + // If this was the highest fd, decrease it + // We could add a loop to decrement fd_max further for every null session, + // but this is cheap and good enough for the typical case + if (fd.uncast_dammit() == fd_max - 1) + fd_max--; + readfds.clr(fd); + { + s->rdata.delete_(); + s->wdata.delete_(); + s->session_data.reset(); + reset_session(fd); + } + + // just close() would try to keep sending buffers + fd.shutdown(SHUT_RDWR); + fd.close(); +} + +void realloc_fifo(Session *s, size_t rfifo_size, size_t wfifo_size) +{ + if (s->max_rdata != rfifo_size && s->rdata_size < rfifo_size) + { + s->rdata.resize(rfifo_size); + s->max_rdata = rfifo_size; + } + if (s->max_wdata != wfifo_size && s->wdata_size < wfifo_size) + { + s->wdata.resize(wfifo_size); + s->max_wdata = wfifo_size; + } +} + +void WFIFOSET(Session *s, size_t len) +{ + if (s->wdata_size + len + 16384 > s->max_wdata) + { + realloc_fifo(s, s->max_rdata, s->max_wdata << 1); + PRINTF("socket: %d wdata expanded to %zu bytes.\n"_fmt, s, s->max_wdata); + } + if (s->wdata_size + len + 2048 < s->max_wdata) + s->wdata_size += len; + else + FPRINTF(stderr, "socket: %d wdata lost !!\n"_fmt, s), abort(); +} + +void do_sendrecv(interval_t next_ms) +{ + bool any = false; + io::FD_Set rfd = readfds, wfd; + for (io::FD i : iter_fds()) + { + Session *s = get_session(i); + if (s) + { + any = true; + if (s->wdata_size) + wfd.set(i); + } + } + if (!any) + { + if (!has_timers()) + { + PRINTF("Shutting down - nothing to do\n"_fmt); + // TODO hoist this + runflag = false; + } + return; + } + struct timeval timeout; + { + std::chrono::seconds next_s = std::chrono::duration_cast(next_ms); + std::chrono::microseconds next_us = next_ms - next_s; + timeout.tv_sec = next_s.count(); + timeout.tv_usec = next_us.count(); + } + if (io::FD_Set::select(fd_max, &rfd, &wfd, NULL, &timeout) <= 0) + return; + for (io::FD i : iter_fds()) + { + Session *s = get_session(i); + if (!s) + continue; + if (wfd.isset(i) && !s->eof) + { + if (s->func_send) + //send_from_fifo(i); + s->func_send(s); + } + if (rfd.isset(i) && !s->eof) + { + if (s->func_recv) + //recv_to_fifo(i); + //or connect_client(i); + s->func_recv(s); + } + } +} + +void do_parsepacket(void) +{ + for (io::FD i : iter_fds()) + { + Session *s = get_session(i); + if (!s) + continue; + if (!s->connected + && static_cast(TimeT::now()) - static_cast(s->created) > CONNECT_TIMEOUT) + { + PRINTF("Session #%d timed out\n"_fmt, s); + s->set_eof(); + } + if (s->rdata_size && !s->eof && s->func_parse) + { + s->func_parse(s); + /// some func_parse may call delete_session + // (that's kind of evil) + s = get_session(i); + if (!s) + continue; + } + if (s->eof) + { + delete_session(s); + continue; + } + /// Reclaim buffer space for what was read + RFIFOFLUSH(s); + } +} + +void RFIFOSKIP(Session *s, size_t len) +{ + s->rdata_pos += len; + + if (s->rdata_size < s->rdata_pos) + { + FPRINTF(stderr, "too many skip\n"_fmt); + abort(); + } +} diff --git a/src/net/socket.hpp b/src/net/socket.hpp new file mode 100644 index 0000000..94198f6 --- /dev/null +++ b/src/net/socket.hpp @@ -0,0 +1,425 @@ +#ifndef TMWA_NET_SOCKET_HPP +#define TMWA_NET_SOCKET_HPP +// socket.hpp - Network event system. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +# include "fwd.hpp" + +# include + +# include + +# include + +# include "../compat/iter.hpp" +# include "../compat/rawmem.hpp" +# include "../compat/time_t.hpp" + +# include "../strings/astring.hpp" +# include "../strings/vstring.hpp" +# include "../strings/xstring.hpp" + +# include "../generic/dumb_ptr.hpp" + +# include "../io/fd.hpp" + +# include "ip.hpp" +# include "timer.t.hpp" + +struct SessionData +{ +}; +struct SessionDeleter +{ + // defined per-server + void operator()(SessionData *sd); +}; + +struct SessionIO +{ + void (*func_recv)(Session *); + void (*func_send)(Session *); +}; + +struct SessionParsers +{ + void (*func_parse)(Session *); + void (*func_delete)(Session *); +}; + +struct Session +{ + Session(SessionIO, SessionParsers); + Session(Session&&) = delete; + Session& operator = (Session&&) = delete; + + void set_io(SessionIO); + void set_parsers(SessionParsers); + + /// Checks whether a newly-connected socket actually does anything + TimeT created; + bool connected; + +private: + /// Flag needed since structure must be freed in a server-dependent manner + bool eof; +public: + void set_eof() { eof = true; } + + /// Currently used by clif_setwaitclose + Timer timed_close; + + /// Since this is a single-threaded application, it can't block + /// These are the read/write queues + dumb_ptr rdata, wdata; + size_t max_rdata, max_wdata; + /// How much is actually in the queue + size_t rdata_size, wdata_size; + /// How much has already been read from the queue + /// Note that there is no need for a wdata_pos + size_t rdata_pos; + + IP4Address client_ip; + +private: + /// Send or recieve + /// Only called when select() indicates the socket is ready + /// If, after that, nothing is read, it sets eof + // These could probably be hard-coded with a little work + void (*func_recv)(Session *); + void (*func_send)(Session *); + /// This is the important one + /// Set to different functions depending on whether the connection + /// is a player or a server/ladmin + void (*func_parse)(Session *); + /// Cleanup function since we're not fully RAII yet + void (*func_delete)(Session *); + +public: + // this really ought to be part of session_data, once that gets sane + SessionParsers for_inferior; + + /// Server-specific data type + // (this really should include the deleter, but ...) + std::unique_ptr session_data; + + io::FD fd; + + friend void do_sendrecv(interval_t next); + friend void do_parsepacket(void); + friend void delete_session(Session *); +}; + +inline +int convert_for_printf(Session *s) +{ + return s->fd.uncast_dammit(); +} + +// save file descriptors for important stuff +constexpr int SOFT_LIMIT = FD_SETSIZE - 50; + +// socket timeout to establish a full connection in seconds +constexpr int CONNECT_TIMEOUT = 15; + + +void set_session(io::FD fd, std::unique_ptr sess); +Session *get_session(io::FD fd); +void reset_session(io::FD fd); +int get_fd_max(); + +class IncrFD +{ +public: + static + io::FD inced(io::FD v) + { + return io::FD::cast_dammit(v.uncast_dammit() + 1); + } +}; +IteratorPair> iter_fds(); + + +/// open a socket, bind, and listen. Return an fd, or -1 if socket() fails, +/// but exit if bind() or listen() fails +Session *make_listen_port(uint16_t port, SessionParsers inferior); +/// Connect to an address, return a connected socket or -1 +// FIXME - this is IPv4 only! +Session *make_connection(IP4Address ip, uint16_t port, SessionParsers); +/// free() the structure and close() the fd +void delete_session(Session *); +/// Make a the internal queues bigger +void realloc_fifo(Session *s, size_t rfifo_size, size_t wfifo_size); +/// Update all sockets that can be read/written from the queues +void do_sendrecv(interval_t next); +/// Call the parser function for every socket that has read data +void do_parsepacket(void); + +template +uint8_t *pod_addressof_m(T& structure) +{ + static_assert(is_trivially_copyable::value, "Can only byte-copy POD-ish structs"); + return &reinterpret_cast(structure); +} + +template +const uint8_t *pod_addressof_c(const T& structure) +{ + static_assert(is_trivially_copyable::value, "Can only byte-copy POD-ish structs"); + return &reinterpret_cast(structure); +} + + +/// Check how much can be read +inline +size_t RFIFOREST(Session *s) +{ + return s->rdata_size - s->rdata_pos; +} +/// Read from the queue +inline +const void *RFIFOP(Session *s, size_t pos) +{ + return &s->rdata[s->rdata_pos + pos]; +} +inline +uint8_t RFIFOB(Session *s, size_t pos) +{ + return *static_cast(RFIFOP(s, pos)); +} +inline +uint16_t RFIFOW(Session *s, size_t pos) +{ + return *static_cast(RFIFOP(s, pos)); +} +inline +uint32_t RFIFOL(Session *s, size_t pos) +{ + return *static_cast(RFIFOP(s, pos)); +} +template +void RFIFO_STRUCT(Session *s, size_t pos, T& structure) +{ + really_memcpy(pod_addressof_m(structure), static_cast(RFIFOP(s, pos)), sizeof(T)); +} +inline +IP4Address RFIFOIP(Session *s, size_t pos) +{ + IP4Address o; + RFIFO_STRUCT(s, pos, o); + return o; +} +template +inline +VString RFIFO_STRING(Session *s, size_t pos) +{ + const char *const begin = static_cast(RFIFOP(s, pos)); + const char *const end = begin + len-1; + const char *const mid = std::find(begin, end, '\0'); + return XString(begin, mid, nullptr); +} +inline +AString RFIFO_STRING(Session *s, size_t pos, size_t len) +{ + const char *const begin = static_cast(RFIFOP(s, pos)); + const char *const end = begin + len; + const char *const mid = std::find(begin, end, '\0'); + return XString(begin, mid, nullptr); +} +inline +void RFIFO_BUF_CLONE(Session *s, uint8_t *buf, size_t len) +{ + really_memcpy(buf, static_cast(RFIFOP(s, 0)), len); +} + +/// Done reading +void RFIFOSKIP(Session *s, size_t len); + +/// Read from an arbitrary buffer +inline +const void *RBUFP(const uint8_t *p, size_t pos) +{ + return p + pos; +} +inline +uint8_t RBUFB(const uint8_t *p, size_t pos) +{ + return *static_cast(RBUFP(p, pos)); +} +inline +uint16_t RBUFW(const uint8_t *p, size_t pos) +{ + return *static_cast(RBUFP(p, pos)); +} +inline +uint32_t RBUFL(const uint8_t *p, size_t pos) +{ + return *static_cast(RBUFP(p, pos)); +} +template +void RBUF_STRUCT(const uint8_t *p, size_t pos, T& structure) +{ + really_memcpy(pod_addressof_m(structure), p + pos, sizeof(T)); +} +inline +IP4Address RBUFIP(const uint8_t *p, size_t pos) +{ + IP4Address o; + RBUF_STRUCT(p, pos, o); + return o; +} +template +inline +VString RBUF_STRING(const uint8_t *p, size_t pos) +{ + const char *const begin = static_cast(RBUFP(p, pos)); + const char *const end = begin + len-1; + const char *const mid = std::find(begin, end, '\0'); + return XString(begin, mid, nullptr); +} +inline +AString RBUF_STRING(const uint8_t *p, size_t pos, size_t len) +{ + const char *const begin = static_cast(RBUFP(p, pos)); + const char *const end = begin + len; + const char *const mid = std::find(begin, end, '\0'); + return XString(begin, mid, nullptr); +} + + +/// Unused - check how much data can be written +// the existence of this seems scary +inline +size_t WFIFOSPACE(Session *s) +{ + return s->max_wdata - s->wdata_size; +} +/// Write to the queue +inline +void *WFIFOP(Session *s, size_t pos) +{ + return &s->wdata[s->wdata_size + pos]; +} +inline +uint8_t& WFIFOB(Session *s, size_t pos) +{ + return *static_cast(WFIFOP(s, pos)); +} +inline +uint16_t& WFIFOW(Session *s, size_t pos) +{ + return *static_cast(WFIFOP(s, pos)); +} +inline +uint32_t& WFIFOL(Session *s, size_t pos) +{ + return *static_cast(WFIFOP(s, pos)); +} +template +void WFIFO_STRUCT(Session *s, size_t pos, T& structure) +{ + really_memcpy(static_cast(WFIFOP(s, pos)), pod_addressof_c(structure), sizeof(T)); +} +inline +IP4Address& WFIFOIP(Session *s, size_t pos) +{ + static_assert(is_trivially_copyable::value, "That was the whole point"); + return *static_cast(WFIFOP(s, pos)); +} +inline +void WFIFO_STRING(Session *s, size_t pos, XString str, size_t len) +{ + char *const begin = static_cast(WFIFOP(s, pos)); + char *const end = begin + len; + char *const mid = std::copy(str.begin(), str.end(), begin); + std::fill(mid, end, '\0'); +} +inline +void WFIFO_ZERO(Session *s, size_t pos, size_t len) +{ + uint8_t *b = static_cast(WFIFOP(s, pos)); + uint8_t *e = b + len; + std::fill(b, e, '\0'); +} +inline +void WFIFO_BUF_CLONE(Session *s, const uint8_t *buf, size_t len) +{ + really_memcpy(static_cast(WFIFOP(s, 0)), buf, len); +} + +/// Finish writing +void WFIFOSET(Session *s, size_t len); + +/// Write to an arbitrary buffer +inline +void *WBUFP(uint8_t *p, size_t pos) +{ + return p + pos; +} +inline +uint8_t& WBUFB(uint8_t *p, size_t pos) +{ + return *static_cast(WBUFP(p, pos)); +} +inline +uint16_t& WBUFW(uint8_t *p, size_t pos) +{ + return *static_cast(WBUFP(p, pos)); +} +inline +uint32_t& WBUFL(uint8_t *p, size_t pos) +{ + return *static_cast(WBUFP(p, pos)); +} +template +void WBUF_STRUCT(uint8_t *p, size_t pos, T& structure) +{ + really_memcpy(p + pos, pod_addressof_c(structure), sizeof(T)); +} +inline +IP4Address& WBUFIP(uint8_t *p, size_t pos) +{ + return *static_cast(WBUFP(p, pos)); +} +inline +void WBUF_STRING(uint8_t *p, size_t pos, XString s, size_t len) +{ + char *const begin = static_cast(WBUFP(p, pos)); + char *const end = begin + len; + char *const mid = std::copy(s.begin(), s.end(), begin); + std::fill(mid, end, '\0'); +} +inline +void WBUF_ZERO(uint8_t *p, size_t pos, size_t len) +{ + uint8_t *b = static_cast(WBUFP(p, pos)); + uint8_t *e = b + len; + std::fill(b, e, '\0'); +} + +inline +void RFIFO_WFIFO_CLONE(Session *rs, Session *ws, size_t len) +{ + really_memcpy(static_cast(WFIFOP(ws, 0)), + static_cast(RFIFOP(rs, 0)), len); +} + +#endif // TMWA_NET_SOCKET_HPP diff --git a/src/net/timer.cpp b/src/net/timer.cpp new file mode 100644 index 0000000..a85b8be --- /dev/null +++ b/src/net/timer.cpp @@ -0,0 +1,217 @@ +#include "timer.hpp" +// timer.cpp - Future event scheduler. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include + +#include + +#include +#include + +#include "../strings/zstring.hpp" + +#include "../poison.hpp" + +struct TimerData +{ + /// This will be reset on call, to avoid problems. + Timer *owner; + + /// When it will be triggered + tick_t tick; + /// What will be done + timer_func func; + /// Repeat rate - 0 for oneshot + interval_t interval; + + TimerData(Timer *o, tick_t t, timer_func f, interval_t i) + : owner(o) + , tick(t) + , func(std::move(f)) + , interval(i) + {} +}; + +struct TimerCompare +{ + /// implement "less than" + bool operator() (dumb_ptr l, dumb_ptr r) + { + // C++ provides a max-heap, but we want + // the smallest tick to be the head (a min-heap). + return l->tick > r->tick; + } +}; + +static +std::priority_queue, std::vector>, TimerCompare> timer_heap; + + +tick_t gettick_cache; + +tick_t milli_clock::now(void) noexcept +{ + struct timeval tval; + // BUG: This will cause strange behavior if the system clock is changed! + // it should be reimplemented in terms of clock_gettime(CLOCK_MONOTONIC, ) + gettimeofday(&tval, NULL); + return gettick_cache = tick_t(std::chrono::seconds(tval.tv_sec) + + std::chrono::duration_cast( + std::chrono::microseconds(tval.tv_usec))); +} + +static +void do_nothing(TimerData *, tick_t) +{ +} + +void Timer::cancel() +{ + if (!td) + return; + + assert (this == td->owner); + td->owner = nullptr; + td->func = do_nothing; + td->interval = interval_t::zero(); + td = nullptr; +} + +void Timer::detach() +{ + assert (this == td->owner); + td->owner = nullptr; + td = nullptr; +} + +static +void push_timer_heap(dumb_ptr td) +{ + timer_heap.push(td); +} + +static +dumb_ptr top_timer_heap(void) +{ + if (timer_heap.empty()) + return dumb_ptr(); + return timer_heap.top(); +} + +static +void pop_timer_heap(void) +{ + timer_heap.pop(); +} + +Timer::Timer(tick_t tick, timer_func func, interval_t interval) +: td(dumb_ptr::make(this, tick, std::move(func), interval)) +{ + assert (interval >= interval_t::zero()); + + push_timer_heap(td); +} + +Timer::Timer(Timer&& t) +: td(t.td) +{ + t.td = nullptr; + if (td) + { + assert (td->owner == &t); + td->owner = this; + } +} + +Timer& Timer::operator = (Timer&& t) +{ + std::swap(td, t.td); + if (td) + { + assert (td->owner == &t); + td->owner = this; + } + if (t.td) + { + assert (t.td->owner == this); + t.td->owner = &t; + } + return *this; +} + +interval_t do_timer(tick_t tick) +{ + /// Number of milliseconds until it calls this again + // this says to wait 1 sec if all timers get popped + interval_t nextmin = std::chrono::seconds(1); + + while (dumb_ptr td = top_timer_heap()) + { + // while the heap is not empty and + if (td->tick > tick) + { + /// Return the time until the next timer needs to goes off + nextmin = td->tick - tick; + break; + } + pop_timer_heap(); + + // Prevent destroying the object we're in. + // Note: this would be surprising in an interval timer, + // but all interval timers do an immediate explicit detach(). + if (td->owner) + td->owner->detach(); + // If we are too far past the requested tick, call with + // the current tick instead to fix reregistration problems + if (td->tick + std::chrono::seconds(1) < tick) + td->func(td.operator->(), tick); + else + td->func(td.operator->(), td->tick); + + if (td->interval == interval_t::zero()) + { + td.delete_(); + continue; + } + if (td->tick + std::chrono::seconds(1) < tick) + td->tick = tick + td->interval; + else + td->tick += td->interval; + push_timer_heap(td); + } + + return std::max(nextmin, std::chrono::milliseconds(10)); +} + +tick_t file_modified(ZString name) +{ + struct stat buf; + if (stat(name.c_str(), &buf)) + return tick_t(); + return tick_t(std::chrono::seconds(buf.st_mtime)); +} + +bool has_timers() +{ + return !timer_heap.empty(); +} diff --git a/src/net/timer.hpp b/src/net/timer.hpp new file mode 100644 index 0000000..8fd383d --- /dev/null +++ b/src/net/timer.hpp @@ -0,0 +1,50 @@ +#ifndef TMWA_NET_TIMER_HPP +#define TMWA_NET_TIMER_HPP +// timer.hpp - Future event scheduler. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +# include "timer.t.hpp" + +# include "fwd.hpp" + +# include "../strings/fwd.hpp" + +// updated automatically when using milli_clock::now() +// which is done only by core.cpp +extern tick_t gettick_cache; + +inline +tick_t gettick(void) +{ + return gettick_cache; +} + +/// Do all timers scheduled before tick, and return the number of +/// milliseconds until the next timer happens +interval_t do_timer(tick_t tick); + +/// Stat a file, and return its modification time, truncated to seconds. +tick_t file_modified(ZString name); + +/// Check if there are any events at all scheduled. +bool has_timers(); + +#endif // TMWA_NET_TIMER_HPP diff --git a/src/net/timer.t.hpp b/src/net/timer.t.hpp new file mode 100644 index 0000000..91d4b74 --- /dev/null +++ b/src/net/timer.t.hpp @@ -0,0 +1,88 @@ +#ifndef TMWA_NET_TIMER_T_HPP +#define TMWA_NET_TIMER_T_HPP +// timer.t.hpp - Future event scheduler. +// +// Copyright © ????-2004 Athena Dev Teams +// Copyright © 2004-2011 The Mana World Development Team +// Copyright © 2011-2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +# include "fwd.hpp" + +# include +# include + +# include "../generic/dumb_ptr.hpp" + +/// An implementation of the C++ "clock" concept, exposing +/// durations in milliseconds. +class milli_clock +{ +public: + typedef std::chrono::milliseconds duration; + typedef duration::rep rep; + typedef duration::period period; + typedef std::chrono::time_point time_point; + static const bool is_steady = true; // assumed - not necessarily true + + static time_point now() noexcept; +}; + +/// A point in time. +typedef milli_clock::time_point tick_t; +/// The difference between two points in time. +typedef milli_clock::duration interval_t; +/// (to get additional arguments, use std::bind or a lambda). +typedef std::function timer_func; + +class Timer +{ + friend struct TimerData; + dumb_ptr td; + + Timer(const Timer&) = delete; + Timer& operator = (const Timer&) = delete; +public: + /// Don't own anything yet. + Timer() = default; + /// Schedule a timer for the given tick. + /// If you do not wish to keep track of it, call disconnect(). + /// Otherwise, you may cancel() or replace (operator =) it later. + /// + /// If the interval argument is given, the timer will reschedule + /// itself again forever. Otherwise, it will disconnect() itself + /// just BEFORE it is called. + Timer(tick_t tick, timer_func func, interval_t interval=interval_t::zero()); + + Timer(Timer&& t); + Timer& operator = (Timer&& t); + ~Timer() { cancel(); } + + /// Cancel the delivery of this timer's function, and make it falsy. + /// Implementation note: this doesn't actually remove it, just sets + /// the functor to do_nothing, and waits for the tick before removing. + void cancel(); + /// Make it falsy without cancelling the timer, + void detach(); + + /// Check if there is a timer connected. + explicit operator bool() { return bool(td); } + /// Check if there is no connected timer. + bool operator !() { return !td; } +}; + +#endif // TMWA_NET_TIMER_T_HPP -- cgit v1.2.3-70-g09d2