From 0c0b5719babfa474185bf7aab273dae1fc614c9e Mon Sep 17 00:00:00 2001 From: Jesusaves Date: Wed, 31 Jan 2024 18:33:58 -0300 Subject: Transfer something from local/ to live repository so there are less conflicts --- src/map/pc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map/pc.cpp b/src/map/pc.cpp index c7c6acf..e6b65b7 100644 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -1566,9 +1566,9 @@ int pc_calcstatus(dumb_ptr sd, int first) if (sd->attack_spell_override || first & (int)CalcStatusKind::MAGIC_OVERRIDE) sd->aspd = sd->attack_spell_delay; - /* Red Threshold Calculation (TODO) */ + /* Red Threshold Calculation */ if (sd->aspd < 300_ms) { - sd->aspd = 300_ms + ((sd->aspd - 300_ms) * 20 / 20); + sd->aspd = 300_ms + ((sd->aspd - 300_ms) * 19 / 20); } sd->aspd = std::max(sd->aspd, battle_config.max_aspd); -- cgit v1.2.3-60-g2f50 From e9b49f5f827cbacc02ff9a54f158d363942d6fe5 Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Thu, 1 Feb 2024 16:55:31 +0100 Subject: Fix bug whereby stat updates were not sent to client after equipping +1 stat pt item when base stat is 1. What is funnier is that it sent updates for all other 5 (unchanged stats). Example, amethyst ring +1 dex: Sending update for stat 0: saved: 0+1, new: 0+0 (str?) Sending update for stat 1: saved: 0+1, new: 0+0 (agi?) Sending update for stat 2: saved: 0+1, new: 0+0 (vit?) Sending update for stat 3: saved: 0+1, new: 0+0 (int?) Sending update for stat 5: saved: 0+1, new: 0+0 (luk?) --- src/map/pc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/pc.cpp b/src/map/pc.cpp index e6b65b7..12af48f 100644 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -1135,7 +1135,7 @@ int pc_calcstatus(dumb_ptr sd, int first) b_weight = sd->weight; b_max_weight = sd->max_weight; earray b_paramb = sd->paramb; - earray b_parame = sd->paramc; + earray b_parame = sd->parame; earray b_skill = sd->status.skill; b_hit = sd->hit; b_flee = sd->flee; -- cgit v1.2.3-60-g2f50 From ad78e6a131143dbd18a47f03943f3be5eec00d01 Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Wed, 13 Dec 2023 23:14:07 +0100 Subject: Enable GitLab CI +Add meway's Ubuntu +Add python -> python2 symlink +Separate python/python2 into INSTALL_PACKAGES --- .gitlab-ci.yml | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..67559b8 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,52 @@ +# Copied in from Moubootaur Legends's Hercules .gitlab-ci.yml +stages: + - build + +variables: &base_vars + DEBIAN_COMMON_PACKAGES: make git gcc g++ + # Depth of clone. If no tag is made after this many commits, then + # the git describe call and version header generation will fail. + GIT_DEPTH: 100 # Will break again eventually. + +.prerequisites: &prerequisites + before_script: + - echo "Building TMW Athena $CI_BUILD_NAME" + - uname -a + - apt-get update + - apt-get install -y -qq $INSTALL_PACKAGES $DEBIAN_COMMON_PACKAGES + + +# Active server OS? +re:ubuntu1804:build: + <<: *prerequisites + stage: build + image: ubuntu:18.04 + variables: + <<: *base_vars + INSTALL_PACKAGES: python + script: + - git submodule update --init + - git fetch -t + - printf "Building TMW Athena version %s\n" "$(git describe --tags HEAD)" + - ./configure --user + - make + - whoami + - make install + +# Next server OS? +re:ubuntu2204:build: + <<: *prerequisites + stage: build + image: ubuntu:22.04 + variables: + <<: *base_vars + INSTALL_PACKAGES: python2 + script: + - ln -s /usr/bin/python2 /usr/bin/python + - git submodule update --init + - git fetch -t + - printf "Building TMW Athena version %s\n" "$(git describe --tags HEAD)" + - ./configure --user + - make + - whoami + - make install -- cgit v1.2.3-60-g2f50 From a2c628a405e6ecdf4cb4a10cc0b2dee9805a633a Mon Sep 17 00:00:00 2001 From: Thorbjørn Lindeijer Date: Tue, 20 Feb 2024 02:46:34 +0000 Subject: tools/protocol.py: Added generation of client code This helps with keeping the protocol in the Mana client up to date. **** --- tools/protocol.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/tools/protocol.py b/tools/protocol.py index 7419639..cf89a16 100755 --- a/tools/protocol.py +++ b/tools/protocol.py @@ -984,6 +984,18 @@ class BasePacket(object): d[p].post_set(d, n - 1 + (len(self.post) == 1), accum) return accum + def dump_client_enum(self, f): + id = self.id + define = self.define + name = self.name + if define and id: + f.write(' %s ' % define) + f.write(' ' * (30 - len(define))) + f.write('= 0x%04x,' % id) + if name: + f.write(" // " + name) + f.write('\n') + class FixedPacket(BasePacket): __slots__ = ('fixed_struct') @@ -1016,6 +1028,15 @@ class FixedPacket(BasePacket): self.fixed_struct.dump_convert(f) f.write('\n') + def dump_client_packet_info(self, f): + size = self.fixed_struct.size + define = self.define + if define and id: + f.write(' { %s, ' % define) + f.write(' ' * (30 - len(define))) + f.write('%d, "%s" },' % (size, define)) + f.write('\n') + class VarPacket(BasePacket): __slots__ = ('head_struct', 'repeat_struct') @@ -1053,6 +1074,14 @@ class VarPacket(BasePacket): self.repeat_struct.dump_convert(f) f.write('\n') + def dump_client_packet_info(self, f): + define = self.define + if define and id: + f.write(' { %s, ' % define) + f.write(' ' * (30 - len(define))) + f.write('VAR, "%s" },' % define) + f.write('\n') + def sanitize_line(line, n): if not line: return line @@ -1181,6 +1210,20 @@ class Channel(object): p.dump_convert(f) f.write('} // namespace tmwa\n') + def dump_client_enum(self, f): + if any(p.define and p.id for p in self.packets): + f.write(' // %s server messages\n' % self.server) + for p in self.packets: + p.dump_client_enum(f) + f.write('\n') + + def dump_client_packet_info(self, f): + if any(p.define and p.id for p in self.packets): + f.write(' // %s server messages\n' % self.server) + for p in self.packets: + p.dump_client_packet_info(f) + f.write('\n') + ident_translation = ''.join(chr(c) if chr(c).isalnum() else '_' for c in range(256)) @@ -1293,6 +1336,20 @@ class Context(object): ty.dump(f) f.write('} // namespace tmwa\n') + # for net/tmwa/protocol.h in Mana client + with OpenWrite(os.path.join(outdir, 'client-enum.hpp')) as f: + f.write('enum {\n') + for ch in self._channels: + ch.dump_client_enum(f) + f.write('};\n') + + # for net/tmwa/network.cpp in Mana client + with OpenWrite(os.path.join(outdir, 'client-packet-info.cpp')) as f: + f.write('static const PacketInfo packet_infos[] = {\n') + for ch in self._channels: + ch.dump_client_packet_info(f) + f.write('};\n') + for g in glob.glob(os.path.join(outdir, '*.old')): print('Obsolete: %s' % g) os.remove(g) @@ -3651,7 +3708,7 @@ def build_context(): ''', ) map_user.r(0x00f5, 'storage take', - define='CSMG_MOVE_FROM_STORAGE', + define='CMSG_MOVE_FROM_STORAGE', fixed=[ at(0, u16, 'packet id'), at(2, soff1, 'soff1'), @@ -6267,6 +6324,7 @@ def build_context(): # TOC_MISC # any client any_user.r(0x7530, 'version', + define='CMSG_SERVER_VERSION_REQUEST', fixed=[ at(0, u16, 'packet id'), ], @@ -6278,6 +6336,7 @@ def build_context(): ''', ) any_user.s(0x7531, 'version result', + define='SMSG_SERVER_VERSION_RESPONSE', fixed=[ at(0, u16, 'packet id'), at(2, version, 'version'), @@ -6290,6 +6349,7 @@ def build_context(): ''', ) any_user.r(0x7532, 'disconnect', + define='CMSG_CLIENT_DISCONNECT', fixed=[ at(0, u16, 'packet id'), ], @@ -6300,9 +6360,6 @@ def build_context(): Request from client or ladmin to disconnect. ''', ) - # 0x7530 define='CMSG_SERVER_VERSION_REQUEST', - # 0x7531 define='SMSG_SERVER_VERSION_RESPONSE', - # 0x7532 define='CMSG_CLIENT_DISCONNECT', # TOC_LOGINADMIN # login admin @@ -6937,7 +6994,7 @@ def build_context(): delete a login-stored ##register of an account. ''', ) - + # TOC_NEW ## new-style packets # notify packets, standalone, can occur at any time; always 'payload' -- cgit v1.2.3-60-g2f50 From b5acf91dfe7057b8effaf2f23afebb200448cd97 Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Tue, 16 Jan 2024 18:02:10 +0100 Subject: Update test cases for addition of Item Mode field See "Item Mode" commit from Apr 3 2023 --- src/ast/item_test.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/ast/item_test.cpp b/src/ast/item_test.cpp index 7e5f1a9..33ed9cb 100644 --- a/src/ast/item_test.cpp +++ b/src/ast/item_test.cpp @@ -87,11 +87,11 @@ namespace item QuietFd q; LString inputs[] = { - // 1 2 3 4 5 - //2345678901234567890123456789012345678901234567890123456789 - "1,abc , 3,4,5,6,7,8,9,10,xx,2,16,12,13,11, {end;}, {}"_s, - "1,abc , 3,4,5,6,7,8,9,10,xx,2,16,12,13,11, {end;}, {}\n"_s, - "1,abc , 3,4,5,6,7,8,9,10,xx,2,16,12,13,11, {end;}, {}\nabc"_s, + // 1 2 3 4 5 6 + //23456789012345678901234567890123456789012345678901234567890123456789 + "1,abc , 3,4,5,6,7,8,9,10,xx,2,16,12,13,11,1, {end;}, {}"_s, + "1,abc , 3,4,5,6,7,8,9,10,xx,2,16,12,13,11,1, {end;}, {}\n"_s, + "1,abc , 3,4,5,6,7,8,9,10,xx,2,16,12,13,11,1, {end;}, {}\nabc"_s, }; for (auto input : inputs) { @@ -99,7 +99,7 @@ namespace item auto res = TRY_UNWRAP(parse_item(lr), FAIL()); EXPECT_TRUE(res.get_success().is_some()); auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); - EXPECT_SPAN(top.span, 1,1, 1,58); + EXPECT_SPAN(top.span, 1,1, 1,60); auto p = top.get_if(); EXPECT_TRUE(p); if (p) @@ -136,9 +136,11 @@ namespace item EXPECT_EQ(p->elv.data, 13); EXPECT_SPAN(p->view.span, 1,45, 1,46); EXPECT_EQ(p->view.data, ItemLook::W_BOW); - EXPECT_SPAN(p->use_script.span, 1,49, 1,54); + EXPECT_SPAN(p->mode.span, 1,48, 1,48); + EXPECT_EQ(p->mode.data, ItemMode::NO_DROP); + EXPECT_SPAN(p->use_script.span, 1,51, 1,56); EXPECT_EQ(p->use_script.braced_body, "{end;}"_s); - EXPECT_SPAN(p->equip_script.span, 1,57, 1,58); + EXPECT_SPAN(p->equip_script.span, 1,59, 1,60); EXPECT_EQ(p->equip_script.braced_body, "{}"_s); } } -- cgit v1.2.3-60-g2f50 From 790c57e165648bcf11d01960d0f8e50ebf1c3074 Mon Sep 17 00:00:00 2001 From: Thorbjørn Lindeijer Date: Wed, 6 Mar 2024 09:33:00 +0100 Subject: Restored README.md visibility * Removed the
tag used to hide the main contents. * Removed deprecation notice, since we're still running this server and will need to maintain it until it has been replaced. * Deleted the section about tmwa-monitor, since the tool is not just deprecated but actually was deleted in 53a91a3fbf0929a99abcdfea23126f978c3ce71a. This partly reverts da50c517000391c83305ff704e3059c900f28d5b. --- README.md | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 2b61743..95a5903 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,10 @@ ![The Mana World logo](share/tmwa/TheManaWorldLogo.png) -This is TMWA, an MMORPG server used by The Mana World Legacy that uses a -protocol based on one of many projects that foolishly chose the name "Athena". +This is TMWA, an MMORPG server used by The Mana World that uses a protocol +based on one of many projects that foolishly chose the name "Athena". Specifically, it was forked from eAthena, a Ragnarok Online clone, in 2004. -# TMWA IS NO LONGER SUPPORTED! don't use this - -

- -
-click _here_ for the old readme - -
- Take a look at the [wiki](http://wiki.themanaworld.org/index.php/How_to_Develop) for user instructions. **Important note:** building from a github-generated tarball does not work! @@ -154,22 +145,6 @@ current working directory; this was the only thing that makes sense since the files are dependent on the server-data. However, a migration to installed files has begun. - -#### tmwa-monitor: -<DEPRECATED> -Formerly known as `eathena-monitor`. - -An unmaintained tool whose job was to keep restarting the servers -every time they crashed. It still builds in case anyone was using it, -but it proved inflexible and has't really been kept up-to-date with our -(TMW's) server-data, and besides, the server doesn't crash much now. -There are also a number of other Open Source programs that monitor -services already. - -There is a `run-all` script in the server-data repo that starts the -appropriate server for that config. On the main server, we instead -start the servers (and bots) individually in a tmux. - #### tmwa-admin: Formerly known as `ladmin` ("local"). @@ -275,4 +250,3 @@ The following skills are good to know required for various tasks: tasks. Particularly, reread the bit about review.) - C++11 (Not a low entry barrier. I'm not really expecting help with this, and since this is conflict heavy, please do the other tasks first). -
-- cgit v1.2.3-60-g2f50 From e1aa301c395afe399898d84c13297c4c2557ed52 Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Sat, 9 Dec 2023 20:00:13 +0100 Subject: Correct issue with GTEST_DIR not being passed properly. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note how gcc uses /usr/include/gtest (default search path) as the -I for GTEST_DIR is not on the gcc command line: System-wide gtest is 1.14 and requires C++14 while provided one is 1.8.1. GTEST_DIR="$PWD/deps/googletest/googletest" ./configure --user make[1]: Leaving directory '/data/users/tmwa/proj/tmw/classic/tmwa' find: ‘doc-gen/’: No such file or directory g++ -std=c++0x -I . -I ./include -DGENERATING_DEPENDENCIES -O2 -g -fstack-protector -fno-strict-aliasing -fvisibility=hidden -fvisibility=hidden -MG -MM \ -MT 'ast/item_test := ' \ -MF obj/ast/item_test.d src/ast/item_test.cpp In file included from /usr/include/gtest/gtest-message.h:57, from /usr/include/gtest/gtest-assertion-result.h:46, from /usr/include/gtest/gtest.h:64, from src/ast/item_test.cpp:21: /usr/include/gtest/internal/gtest-port.h:270:2: error: #error C++ versions less than C++14 are not supported. 270 | #error C++ versions less than C++14 are not supported. | ^~~~~ Makefile:331: obj/ast/item_test.d: No such file or directory --- Makefile.in | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index 610e7f6..10f909c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -492,8 +492,15 @@ lib/%.a: ${GTEST_BINARIES}: obj/gtest_main.pdc.o obj/gtest-all.pdc.o -# This isn't perfect. -$(filter %_test.pdc.o,${PDC_OBJECTS}) obj/gtest_main.pdc.o obj/gtest-all.pdc.o: override CPPFLAGS += -DGTEST_HAS_PTHREAD=0 -I${GTEST_DIR} +# Need gtest include for both compile (*_test.pdc.o) and dependency-generation (*_test.d) steps. +GTEST_NEED_INCLUDE := $(filter %_test.pdc.o,${PDC_OBJECTS}) $(filter %_test.d,${DEPENDS}) obj/gtest_main.pdc.o obj/gtest-all.pdc.o +${GTEST_NEED_INCLUDE}: override CXXFLAGS += -DGTEST_HAS_PTHREAD=0 -I${GTEST_DIR}/include + + +# Special rule for src/gtest-all.cc, it includes "src/gtest.cc" +# and thus additionally needs toplevel as include +obj/gtest-all.pdc.o: override CXXFLAGS += -DGTEST_HAS_PTHREAD=0 -I${GTEST_DIR} + obj/gtest-all.pdc.o obj/gtest_main.pdc.o: override WARNINGS := obj/gtest%.pdc.o: ${GTEST_DIR}/src/gtest%.cc $(MKDIR_FIRST) -- cgit v1.2.3-60-g2f50 From cc086e08c128763762b3fa67c6d2771b88d84d9d Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Sun, 10 Dec 2023 22:10:58 +0100 Subject: Add googletest-1.8.1 as a submodule - gtest-1.14 shipped by my distro requires C++14. --- .gitmodules | 3 +++ configure | 8 +++++++- deps/googletest | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) create mode 160000 deps/googletest diff --git a/.gitmodules b/.gitmodules index 931035d..8197e60 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "deps/attoconf"] path = deps/attoconf url = https://github.com/o11c/attoconf.git +[submodule "deps/googletest"] + path = deps/googletest + url = https://github.com/google/googletest.git diff --git a/configure b/configure index 67bcf53..aa1d0fd 100755 --- a/configure +++ b/configure @@ -84,7 +84,13 @@ class Configuration(Cxx, Install, ConfigHash, Templates): def vars(self): super(Configuration, self).vars() - self.add_option('GTEST_DIR', init='/usr/src/gtest', + # Why submodule gtest? + # 1) make test requires gtest-all.cc. This file is shipped by Ubuntu, + # but not by Gentoo; + # 2) Modern distros ship gtest-1.13+. It requires C++14+, while + # TMWA is currently a C++0x codebase. + self.add_option('GTEST_DIR', + init=os.path.join(os.getcwd(), 'deps/googletest/googletest'), # http://code.google.com/p/googletest/wiki/FAQ#Why_is_it_not_recommended_to_install_a_pre-compiled_copy_of_Goog type=filepath, check=lambda build, GTEST_DIR: None, help='Location of Google Test sources, must contain src/gtest-all.cc (linking to a precompiled library is NOT supported)', hidden=False) diff --git a/deps/googletest b/deps/googletest new file mode 160000 index 0000000..2fe3bd9 --- /dev/null +++ b/deps/googletest @@ -0,0 +1 @@ +Subproject commit 2fe3bd994b3189899d93f1d5a881e725e046fdc2 -- cgit v1.2.3-60-g2f50 From 581490f804832b479abda46a52cefd4218cbf910 Mon Sep 17 00:00:00 2001 From: Thorbjørn Lindeijer Date: Mon, 11 Mar 2024 14:40:08 +0000 Subject: Several updates to the README.md It's no longer maintained by o11c since many years now, so an update was long due. Also updated some outdated links. Finally, who can say for sure that The Mana World will never run on Manaserv? --- README.md | 68 +++++++++++++++++---------------------------------------------- 1 file changed, 18 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 95a5903..b2309f5 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Specifically, it was forked from eAthena, a Ragnarok Online clone, in 2004. Take a look at the [wiki](http://wiki.themanaworld.org/index.php/How_to_Develop) for user instructions. -**Important note:** building from a github-generated tarball does not work! +**Important note:** building from a GitHub-generated tarball does not work! You must either build from a git checkout or from a 'make dist' tarball. @@ -17,24 +17,14 @@ The rest of this file contains information relevant only to: 1. Distributors. 2. Contributors. - -TMWA has been maintained by o11c (Ben Longbons) since early 2011 or so. -Before that, it never really had a proper maintainer, since everyone -thought that ManaServ was going to be the thing. But it won't ever be, -at least not for TMW. - -TMWA has a [bugtracker](https://github.com/themanaworld/tmwa/issues). +TMWA has a [bugtracker](https://git.themanaworld.org/legacy/tmwa/-/issues). But it's probably worth getting on IRC first: -* Use an IRC client: irc://chat.freenode.net/tmwa -* Or just use the [webchat](https://webchat.freenode.net/?channels=#tmwa). - -Note that this channel is *only* for technical discussion of TMWA (and -attoconf), not general chat or TMW content development. +* Use an IRC client: irc://irc.libera.chat/themanaworld-dev +* Or just use the [webchat](https://web.libera.chat/?channel=#themanaworld-dev). -I'm active in the Pacific timezone, but I might not have internet access -all the time. I'm usually never AFK longer than 48 hours; when there is an -exception, I always tell the content devs who also idle there. +Note that this channel is not specific to technical discussion of TMWA (and +attoconf), but for any discussions related to TMW development. ## 1. Distributors. ### Important notes: @@ -196,7 +186,7 @@ developers get annoyed when wushin makes us work straight from his client-data repo. Currently, there is only *one* set of server data that is known to be -compatible with TMWA: https://github.com/themanaworld/tmwa-server-data +compatible with TMWA: https://git.themanaworld.org/legacy/serverdata The only recommended way of using this is by following the instructions in the [How to Develop](https://wiki.themanaworld.org/index.php/Dev:How_to_Develop) article. These instructions are only designed @@ -217,36 +207,14 @@ Note also that The Mana World has not investigated the copyright status of other sets of server data. ## 2. Contributors. -The most important thing if you want to help improve TMWA is *talk* to me. -No, wait, that's the second most important thing. - -The real most important thing if you want to help improve TMWA is that it's -*work*. You can't just stop by and chat for a few hours and help at all. -If you're going to work on TMWA, you have to be work months in the future. - -TMWA was terrible when I got it, and I've only fixed enough to make it -sane, not pretty. Even a minimal change is likely to touch the whole tree, -so merge conflicts are a constant problem. - -That said, there *are* several tasks that I could use help with. Several -essential tasks have been left undone just because they don't conflict with -the main body of my work. - -But I do not want someone who will just work for a few hours, go to bed, -then never return. I have wasted far too many hours answering their -questions. If you're going to help, you have to actually *help*. - -The following skills are good to know required for various tasks: - - - ability to read - - ability to write - - ability to notice error messages - - ability to solve your own problems - - willingness to accept review of your changes. It's not personal if I - say your work is wrong, I'm just seeing more than you do, and tiny - details are often incredibly important. - - familiarity with gdb - - Python (A low entry barrier, but Python alone is not enough for the - tasks. Particularly, reread the bit about review.) - - C++11 (Not a low entry barrier. I'm not really expecting help with this, - and since this is conflict heavy, please do the other tasks first). + +You're welcome to help maintain this server, but please make sure to +get in touch with the currently active maintainers first. + +Remember that this server is what currently keeps The Mana World alive, +so any changes should be made with extreme care. It is important that +each change be tested and reviewed before it goes live. + +Finally, if you got yourself somewhat familiar with this code, please +consider sticking around either to fix further issues as they come up +or to help reviewing changes by others. Thanks! -- cgit v1.2.3-60-g2f50 From 6071d37c4127aafef84ba1144ac1055972426116 Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Sun, 28 Jan 2024 13:52:38 +0100 Subject: Workaround "Function... not defined in.." (breakpoints not found) (GDB bug) Function "_Z13do_breakpointIN4tmwa3map11script_dataEEvRKT_PKc" not defined in "/builds/specing/tmwa/src/debug-debug/map-script-persist.cpp". Breakpoint 1 (/builds/specing/tmwa/src/debug-debug/map-script-persist.cpp:'_Z13do_breakpointIN4tmwa3map11script_dataEEvRKT_PKc') pending. void do_breakpoint(tmwa::map::script_data const&, char const*); Thanks to ssbssa@#gdb for pointing this out. --- tools/debug-debug.gdb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/debug-debug.gdb b/tools/debug-debug.gdb index 2e59437..b50a49b 100644 --- a/tools/debug-debug.gdb +++ b/tools/debug-debug.gdb @@ -42,8 +42,21 @@ set print frame-arguments none set python print-stack full set logging on -rbreak do_breakpoint +# Workaround "Function... not defined in.." (breakpoints not found) (GDB bug) +# https://sourceware.org/bugzilla/show_bug.cgi?id=15962 +# In some gdb versions rbreak works, in some break does. +# This code should work for any. +python +bpoint = gdb.Breakpoint("do_breakpoint") + +if bpoint.pending: + print("`break ...` found no breakpoints, trying `rbreak ...`") + bpoint.delete() + gdb.execute("rbreak do_breakpoint") + +end set logging off + commands silent python hit_breakpoint() -- cgit v1.2.3-60-g2f50 From ebf078b9fa81142834dc45e22ea661a61c50d17b Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Thu, 14 Dec 2023 23:22:52 +0100 Subject: Add CI test stage +Pass artifacts (well, the whole repo) to test stage +YAML syntax error. Since I saw a glimpse of the repo being reinitialised two tries ago, perhaps it saves everything tracked and just adding untracked files should be enough. --- .gitlab-ci.yml | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 67559b8..f7c750a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,7 @@ # Copied in from Moubootaur Legends's Hercules .gitlab-ci.yml stages: - build + - test variables: &base_vars DEBIAN_COMMON_PACKAGES: make git gcc g++ @@ -10,7 +11,6 @@ variables: &base_vars .prerequisites: &prerequisites before_script: - - echo "Building TMW Athena $CI_BUILD_NAME" - uname -a - apt-get update - apt-get install -y -qq $INSTALL_PACKAGES $DEBIAN_COMMON_PACKAGES @@ -25,6 +25,7 @@ re:ubuntu1804:build: <<: *base_vars INSTALL_PACKAGES: python script: + - echo "Building TMW Athena $CI_BUILD_NAME" - git submodule update --init - git fetch -t - printf "Building TMW Athena version %s\n" "$(git describe --tags HEAD)" @@ -32,6 +33,12 @@ re:ubuntu1804:build: - make - whoami - make install + artifacts: # required for test stage + untracked: true + expire_in: 30 mins + + + # Next server OS? re:ubuntu2204:build: @@ -50,3 +57,35 @@ re:ubuntu2204:build: - make - whoami - make install + artifacts: # required for test stage + untracked: true + expire_in: 30 mins + + + + +# Disabled. fails with: +# (1) GDB failing to resolve a type +# (2) /usr/bin/ld: Dwarf Error: Can't find .debug_ranges section. +.re:ubuntu1804:test: + <<: *prerequisites + stage: test + image: ubuntu:18.04 + variables: + <<: *base_vars + INSTALL_PACKAGES: python gdb + script: + - printf "Testing TMW Athena version %s\n" "$(git describe --tags HEAD)" + - make test + +re:ubuntu2204:test: + <<: *prerequisites + stage: test + image: ubuntu:22.04 + variables: + <<: *base_vars + INSTALL_PACKAGES: python2 gdb + script: + - ln -s /usr/bin/python2 /usr/bin/python + - printf "Testing TMW Athena version %s\n" "$(git describe --tags HEAD)" + - make test -- cgit v1.2.3-60-g2f50 From 01a27d4af7e54736a7922aec23f6f4e3e21f0965 Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Wed, 10 Apr 2024 16:00:04 +0200 Subject: Report (hardcoded) drop rates on same line as base/job exp, reword messages. --- src/map/atcommand.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index eaefd4f..d9ec555 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -1681,7 +1681,7 @@ ATCE atcommand_exprate(Session *s, dumb_ptr, } battle_config.base_exp_rate = rate; battle_config.job_exp_rate = rate; - AString output = STRPRINTF("All Xp at %d percent"_fmt, rate); + AString output = STRPRINTF("Base & job XP rates now at %d percent"_fmt, rate); clif_displaymessage(s, output); return ATCE::OKAY; } @@ -1690,9 +1690,9 @@ static ATCE atcommand_rates(Session *s, dumb_ptr, ZString message) { - AString output = STRPRINTF("Experience rates: Base %d%% / Job %d%%"_fmt, battle_config.base_exp_rate, battle_config.job_exp_rate); - clif_displaymessage(s, output); - output = STRPRINTF("Drop rate: 100%%"_fmt); + AString output = STRPRINTF( + "Experience rates: Base %d%% / Job %d%%. Drop rate: 100%%"_fmt, + battle_config.base_exp_rate, battle_config.job_exp_rate); clif_displaymessage(s, output); return ATCE::OKAY; } @@ -5599,10 +5599,10 @@ Map atcommand_info = "Enable PvP on your map"_s}}, {"exprate"_s, {""_s, 60, atcommand_exprate, - "Set base job/exp rate"_s}}, + "Set base and job exp rate"_s}}, {"rates"_s, {""_s, 0, atcommand_rates, - "Show base job/exp rate"_s}}, + "Show base and job exp rates"_s}}, {"pvpon"_s, {""_s, 60, atcommand_pvpon, "Disable PvP on your map"_s}}, -- cgit v1.2.3-60-g2f50 From 0fb2ce09aeec67550a41f7027db5b68a6a8b06d7 Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Wed, 10 Apr 2024 16:24:46 +0200 Subject: Split @exprate into @bexprate and @jexprate Preserve @exprate as a shortcut for both & because scripts in serverdata call it --- src/map/atcommand.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index d9ec555..6901437 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -1667,6 +1667,8 @@ ATCE atcommand_pvpoff(Session *s, dumb_ptr sd, return ATCE::OKAY; } +// Command not removed during bexprate/jexprate split +// because serverdata calls it. static ATCE atcommand_exprate(Session *s, dumb_ptr, ZString message) @@ -1686,6 +1688,42 @@ ATCE atcommand_exprate(Session *s, dumb_ptr, return ATCE::OKAY; } +static +ATCE atcommand_bexprate(Session *s, dumb_ptr, + ZString message) +{ + int rate; + + if (!extract(message, &rate) || !rate) + { + clif_displaymessage(s, + "Please, enter a rate adjustement (usage: @bexprate )."_s); + return ATCE::USAGE; + } + battle_config.base_exp_rate = rate; + AString output = STRPRINTF("Base XP rate now at %d percent"_fmt, rate); + clif_displaymessage(s, output); + return ATCE::OKAY; +} + +static +ATCE atcommand_jexprate(Session *s, dumb_ptr, + ZString message) +{ + int rate; + + if (!extract(message, &rate) || !rate) + { + clif_displaymessage(s, + "Please, enter a rate adjustement (usage: @jexprate )."_s); + return ATCE::USAGE; + } + battle_config.job_exp_rate = rate; + AString output = STRPRINTF("Job XP rate now at %d percent"_fmt, rate); + clif_displaymessage(s, output); + return ATCE::OKAY; +} + static ATCE atcommand_rates(Session *s, dumb_ptr, ZString message) @@ -5600,6 +5638,12 @@ Map atcommand_info = {"exprate"_s, {""_s, 60, atcommand_exprate, "Set base and job exp rate"_s}}, + {"bexprate"_s, {""_s, + 60, atcommand_bexprate, + "Set base exp rate"_s}}, + {"jexprate"_s, {""_s, + 60, atcommand_jexprate, + "Set job exp rate"_s}}, {"rates"_s, {""_s, 0, atcommand_rates, "Show base and job exp rates"_s}}, -- cgit v1.2.3-60-g2f50 From dc9c104ad231e6ba9f695072d36d586e2e3d3d4c Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Wed, 10 Apr 2024 16:43:21 +0200 Subject: Add server-wide drop rates modifier --- src/map/atcommand.cpp | 28 +++++++++++++++++++++++++--- src/map/mob.cpp | 3 +++ tools/config.py | 1 + 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index 6901437..20743dd 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -1724,13 +1724,32 @@ ATCE atcommand_jexprate(Session *s, dumb_ptr, return ATCE::OKAY; } +static +ATCE atcommand_droprate(Session *s, dumb_ptr, + ZString message) +{ + int rate; + + if (!extract(message, &rate) || !rate) + { + clif_displaymessage(s, + "Please, enter a rate adjustement (usage: @droprate )."_s); + return ATCE::USAGE; + } + battle_config.drop_rate = rate; + AString output = STRPRINTF("Drops rate now at %d percent"_fmt, rate); + clif_displaymessage(s, output); + return ATCE::OKAY; +} + static ATCE atcommand_rates(Session *s, dumb_ptr, ZString message) { AString output = STRPRINTF( - "Experience rates: Base %d%% / Job %d%%. Drop rate: 100%%"_fmt, - battle_config.base_exp_rate, battle_config.job_exp_rate); + "Experience rates: Base %d%% / Job %d%%. Drop rate: %d%%"_fmt, + battle_config.base_exp_rate, battle_config.job_exp_rate, + battle_config.drop_rate); clif_displaymessage(s, output); return ATCE::OKAY; } @@ -5644,9 +5663,12 @@ Map atcommand_info = {"jexprate"_s, {""_s, 60, atcommand_jexprate, "Set job exp rate"_s}}, + {"droprate"_s, {""_s, + 60, atcommand_droprate, + "Set drop rate"_s}}, {"rates"_s, {""_s, 0, atcommand_rates, - "Show base and job exp rates"_s}}, + "Show base and job exp and drop rates"_s}}, {"pvpon"_s, {""_s, 60, atcommand_pvpon, "Disable PvP on your map"_s}}, diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 4fd9d6d..f2f6815 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -2736,6 +2736,9 @@ int mob_damage(dumb_ptr src, dumb_ptr md, int damage, if (sd && md && battle_config.pk_mode == 1 && (get_mob_db(md->mob_class).lv - sd->status.base_level >= 20)) drop_rate.num *= 1.25; // pk_mode increase drops if 20 level difference [Valaris] + + // server-wide drop rate scaling + drop_rate.num = (drop_rate.num * battle_config.drop_rate) / 100; if (!random_::chance(drop_rate)) continue; diff --git a/tools/config.py b/tools/config.py index a187c06..49339bd 100755 --- a/tools/config.py +++ b/tools/config.py @@ -601,6 +601,7 @@ def build_config(): battle_conf.opt('item_third_get_time', milliseconds, '1_s') battle_conf.opt('base_exp_rate', percent, '100') battle_conf.opt('job_exp_rate', percent, '100') + battle_conf.opt('drop_rate', percent, '100') battle_conf.opt('death_penalty_type', i32, '0', min='0', max='2') battle_conf.opt('death_penalty_base', per10kd, '0') battle_conf.opt('death_penalty_job', per10kd, '0') -- cgit v1.2.3-60-g2f50 From 45d4a85fe4b9e6804a454daab19bd41638aec874 Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Fri, 12 Apr 2024 01:13:10 +0200 Subject: Add battle_config.max_rate limit (500). GMs cannot go above this Blame Ledmitz (: --- src/map/atcommand.cpp | 63 ++++++++++++++++++++++++++++----------------------- tools/config.py | 1 + 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index 20743dd..311ca76 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -1667,20 +1667,38 @@ ATCE atcommand_pvpoff(Session *s, dumb_ptr sd, return ATCE::OKAY; } + + +static int extract_rate(Session *s, ZString message) +{ + int rate; + AString output; + + if (!extract(message, &rate)) + { + clif_displaymessage(s, "Please enter the new rate"_s); + return -1; + } + if (rate <= 0 || rate > battle_config.max_rate) + { + output = STRPRINTF("Rate adjustment must be between 1 and %d%%"_fmt, + battle_config.max_rate); + clif_displaymessage(s, output); + return -1; + } + return rate; +} + // Command not removed during bexprate/jexprate split // because serverdata calls it. static ATCE atcommand_exprate(Session *s, dumb_ptr, ZString message) { - int rate; - - if (!extract(message, &rate) || !rate) - { - clif_displaymessage(s, - "Please, enter a rate adjustement (usage: @exprate )."_s); + int rate = extract_rate(s, message); + if (rate < 0) return ATCE::USAGE; - } + battle_config.base_exp_rate = rate; battle_config.job_exp_rate = rate; AString output = STRPRINTF("Base & job XP rates now at %d percent"_fmt, rate); @@ -1692,14 +1710,10 @@ static ATCE atcommand_bexprate(Session *s, dumb_ptr, ZString message) { - int rate; - - if (!extract(message, &rate) || !rate) - { - clif_displaymessage(s, - "Please, enter a rate adjustement (usage: @bexprate )."_s); + int rate = extract_rate(s, message); + if (rate < 0) return ATCE::USAGE; - } + battle_config.base_exp_rate = rate; AString output = STRPRINTF("Base XP rate now at %d percent"_fmt, rate); clif_displaymessage(s, output); @@ -1710,14 +1724,10 @@ static ATCE atcommand_jexprate(Session *s, dumb_ptr, ZString message) { - int rate; - - if (!extract(message, &rate) || !rate) - { - clif_displaymessage(s, - "Please, enter a rate adjustement (usage: @jexprate )."_s); + int rate = extract_rate(s, message); + if (rate < 0) return ATCE::USAGE; - } + battle_config.job_exp_rate = rate; AString output = STRPRINTF("Job XP rate now at %d percent"_fmt, rate); clif_displaymessage(s, output); @@ -1728,14 +1738,10 @@ static ATCE atcommand_droprate(Session *s, dumb_ptr, ZString message) { - int rate; - - if (!extract(message, &rate) || !rate) - { - clif_displaymessage(s, - "Please, enter a rate adjustement (usage: @droprate )."_s); + int rate = extract_rate(s, message); + if (rate < 0) return ATCE::USAGE; - } + battle_config.drop_rate = rate; AString output = STRPRINTF("Drops rate now at %d percent"_fmt, rate); clif_displaymessage(s, output); @@ -1754,6 +1760,7 @@ ATCE atcommand_rates(Session *s, dumb_ptr, return ATCE::OKAY; } + static ATCE atcommand_pvpon(Session *s, dumb_ptr sd, ZString) diff --git a/tools/config.py b/tools/config.py index 49339bd..2f0781d 100755 --- a/tools/config.py +++ b/tools/config.py @@ -602,6 +602,7 @@ def build_config(): battle_conf.opt('base_exp_rate', percent, '100') battle_conf.opt('job_exp_rate', percent, '100') battle_conf.opt('drop_rate', percent, '100') + battle_conf.opt('max_rate', percent, '500') battle_conf.opt('death_penalty_type', i32, '0', min='0', max='2') battle_conf.opt('death_penalty_base', per10kd, '0') battle_conf.opt('death_penalty_job', per10kd, '0') -- cgit v1.2.3-60-g2f50 From b301141dd36b0dc5d7289ffe504e17cf4c2921e1 Mon Sep 17 00:00:00 2001 From: Freeyorp Date: Sat, 20 Apr 2024 18:35:55 +0000 Subject: npc: Deregister events with `ev_db` on free In serverdata@c3b7fe59a, `magic-knuckles` made use of temporary copies of the spell NPC via `puppet`/`destroy` to provide additional functionality. This implementation was correct with respect to tmwa behaviour as documented. However, `puppet` and `destroy` doesn't quite return the server to a clean prior state. In `builtin_puppet`, events with the fresh new copy of NPC are registered into the global `ev_db` map to track named events, such as `OnDischarge`, which might be triggered later. However, these registrations are not reversed with `destroy`, leaving `ev_db` to retain references to now invalid and destroyed NPCs. serverdata@c3b7fe59a revealed this oversight through an intersection of rare conditions: - An NPC that is routinely cloned and destroyed - Having a variety of events - Including one that can be triggered during a search for the event over ALL NPCs when `#discharge` is cast. To reproduce, compile with `-fsanitize=address -fsanitize=undefined` to more reliably catch use of invalid memory, cast `magic-knuckles`, log out the character which cast `magic-knuckles` to destroy the NPC, log back in, then cast `#discharge`. The server will then crash. Special thanks to SystemError for testing out this theory. Currently lacking a test environment of my own, this would not have been possible without his patience and diligence. --- src/map/npc.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/map/npc.cpp b/src/map/npc.cpp index ee2f30c..efb2ba3 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -1050,6 +1050,18 @@ void npc_free_internal(dumb_ptr nd_) nd_->bl_m->npc[nd_->n] = nullptr; } + // Also clean up any events we registered to the global ev_db + if (auto nd = nd_->is_script()) + { + for (auto& pair : ev_db) + { + if (pair.second.nd == nd) + { + ev_db.erase(pair.first); + } + } + } + nd_.delete_(); } -- cgit v1.2.3-60-g2f50 From 3f214c06c13dfc4c4eb42a3f24553d7a1d30b26d Mon Sep 17 00:00:00 2001 From: HoraK-FDF Date: Thu, 18 Apr 2024 00:46:16 +0200 Subject: getmapmaxx getmapmaxy --- src/map/script-fun.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp index 8dc1989..2be0333 100644 --- a/src/map/script-fun.cpp +++ b/src/map/script-fun.cpp @@ -5471,6 +5471,30 @@ void builtin_getmap(ScriptState *st) push_str(st->stack, sd->bl_m->name_); } +/*========================================== + * Get the maximum x coordinate of a map + *------------------------------------------ + */ +static +void builtin_getmapmaxx(ScriptState *st) +{ + MapName mapname = stringish(ZString(conv_str(st, &AARG(0)))); + P m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + push_int(st->stack, m->xs-1); +} + +/*========================================== + * Get the maximum y coordinate of a map + *------------------------------------------ + */ +static +void builtin_getmapmaxy(ScriptState *st) +{ + MapName mapname = stringish(ZString(conv_str(st, &AARG(0)))); + P m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + push_int(st->stack, m->ys-1); +} + /*========================================== * Get the NPC's info *------------------------------------------ @@ -5732,6 +5756,8 @@ BuiltinFunction builtin_functions[] = BUILTIN(getnpcy, "?"_s, 'i'), BUILTIN(strnpcinfo, "i?"_s, 's'), BUILTIN(getmap, "?"_s, 's'), + BUILTIN(getmapmaxx, "M"_s, 'i'), + BUILTIN(getmapmaxy, "M"_s, 'i'), BUILTIN(mapexit, ""_s, '\0'), BUILTIN(freeloop, "i"_s, '\0'), BUILTIN(if_then_else, "iii"_s, 'v'), -- cgit v1.2.3-60-g2f50 From ae2556ec8fcbfed9e18d275a578f4bc47d26f721 Mon Sep 17 00:00:00 2001 From: Freeyorp Date: Sun, 21 Apr 2024 06:43:43 +0000 Subject: DB: Maybe fix concurrent modification --- src/generic/db.hpp | 4 ++++ src/map/npc.cpp | 10 +++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/generic/db.hpp b/src/generic/db.hpp index 04ead79..d610f32 100644 --- a/src/generic/db.hpp +++ b/src/generic/db.hpp @@ -78,6 +78,10 @@ public: { return borrow(impl[k]); } + void erase_if(std::function&)> predicate) + { + std::erase_if(impl, predicate); + } void erase(const K& k) { impl.erase(k); diff --git a/src/map/npc.cpp b/src/map/npc.cpp index efb2ba3..76f0e14 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -1053,13 +1053,9 @@ void npc_free_internal(dumb_ptr nd_) // Also clean up any events we registered to the global ev_db if (auto nd = nd_->is_script()) { - for (auto& pair : ev_db) - { - if (pair.second.nd == nd) - { - ev_db.erase(pair.first); - } - } + ev_db.erase_if([&](auto& pair) { + return pair.second.nd == nd; + }); } nd_.delete_(); -- cgit v1.2.3-60-g2f50 From f31f51ae0fa0ce8f34de747dec3ed57a3f5373cd Mon Sep 17 00:00:00 2001 From: Freeyorp Date: Sun, 21 Apr 2024 07:09:23 +0000 Subject: DB/npc_free_internal: Use ancient approach C++11 was a really long time ago, huh? --- src/generic/db.hpp | 4 ---- src/map/npc.cpp | 15 ++++++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/generic/db.hpp b/src/generic/db.hpp index d610f32..04ead79 100644 --- a/src/generic/db.hpp +++ b/src/generic/db.hpp @@ -78,10 +78,6 @@ public: { return borrow(impl[k]); } - void erase_if(std::function&)> predicate) - { - std::erase_if(impl, predicate); - } void erase(const K& k) { impl.erase(k); diff --git a/src/map/npc.cpp b/src/map/npc.cpp index 76f0e14..e8d6d4b 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -1053,9 +1053,18 @@ void npc_free_internal(dumb_ptr nd_) // Also clean up any events we registered to the global ev_db if (auto nd = nd_->is_script()) { - ev_db.erase_if([&](auto& pair) { - return pair.second.nd == nd; - }); + std::vector to_erase; + for (auto& pair : ev_db) + { + if (pair.second.nd == nd) + { + to_erase.push_back(pair.first); + } + } + for (auto& key : to_erase) + { + ev_db.erase(key); + } } nd_.delete_(); -- cgit v1.2.3-60-g2f50 From 18b88038af1750288fed389c4674a3a71786b5fd Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Fri, 12 Apr 2024 17:51:49 +0200 Subject: Fix -Wformat warnings --- src/admin/ladmin.cpp | 2 +- src/map/atcommand.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp index 6e8642b..f538069 100644 --- a/src/admin/ladmin.cpp +++ b/src/admin/ladmin.cpp @@ -2571,7 +2571,7 @@ void parse_fromlogin(Session *s) } else { - PRINTF("Variables %i of %i used.\n"_fmt, repeat.size(), ACCOUNT_REG2_NUM); + PRINTF("Variables %zi of %zi used.\n"_fmt, repeat.size(), ACCOUNT_REG2_NUM); auto jlim = std::min(repeat.size(), ACCOUNT_REG2_NUM); for (size_t j = 0; j < jlim; ++j) PRINTF("Variable %s == `%i`\n"_fmt, repeat[j].name, repeat[j].value); diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index 311ca76..bcfb1da 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -1918,8 +1918,8 @@ ATCE atcommand_mobinfo(Session *s, dumb_ptr sd, clif_displaymessage(s, STRPRINTF("Level: %i, HP: %i, SP: %i, Base EXP: %i, JEXP: %i"_fmt, get_mob_db(mob_id).lv, get_mob_db(mob_id).max_hp, get_mob_db(mob_id).max_sp, get_mob_db(mob_id).base_exp, get_mob_db(mob_id).job_exp)); clif_displaymessage(s, STRPRINTF("Range1: %i, ATK1: %i, ATK2: %i, DEF: %i, MDEF: %i, CRITICAL_DEF: %i"_fmt, get_mob_db(mob_id).range, get_mob_db(mob_id).atk1, get_mob_db(mob_id).atk2, get_mob_db(mob_id).def, get_mob_db(mob_id).mdef, get_mob_db(mob_id).critical_def)); clif_displaymessage(s, STRPRINTF("Stats: STR: %i, AGI: %i, VIT: %i, INT: %i, DEX: %i, LUK: %i"_fmt, get_mob_db(mob_id).attrs[ATTR::STR], get_mob_db(mob_id).attrs[ATTR::AGI], get_mob_db(mob_id).attrs[ATTR::VIT], get_mob_db(mob_id).attrs[ATTR::INT], get_mob_db(mob_id).attrs[ATTR::DEX], get_mob_db(mob_id).attrs[ATTR::LUK])); - clif_displaymessage(s, STRPRINTF("Range2: %i, Range3: %i, Scale: %i, Race: %i, Element: %i, Element Level: %i, Mode: %i"_fmt, get_mob_db(mob_id).range2, get_mob_db(mob_id).range3, get_mob_db(mob_id).size, get_mob_db(mob_id).race, get_mob_db(mob_id).element.element, get_mob_db(mob_id).element.level, get_mob_db(mob_id).mode)); - clif_displaymessage(s, STRPRINTF("Speed: %i, Adelay: %i, Amotion: %i, Dmotion: %i"_fmt, get_mob_db(mob_id).speed.count(), get_mob_db(mob_id).adelay.count(), get_mob_db(mob_id).amotion.count(), get_mob_db(mob_id).dmotion.count())); + clif_displaymessage(s, STRPRINTF("Range2: %i, Range3: %i, Scale: %i, Race: %i, Element: %i, Element Level: %i, Mode: %i"_fmt, get_mob_db(mob_id).range2, get_mob_db(mob_id).range3, get_mob_db(mob_id).size, static_cast(get_mob_db(mob_id).race), static_cast(get_mob_db(mob_id).element.element), get_mob_db(mob_id).element.level, static_cast(get_mob_db(mob_id).mode))); + clif_displaymessage(s, STRPRINTF("Speed: %li, Adelay: %li, Amotion: %li, Dmotion: %li"_fmt, get_mob_db(mob_id).speed.count(), get_mob_db(mob_id).adelay.count(), get_mob_db(mob_id).amotion.count(), get_mob_db(mob_id).dmotion.count())); if (get_mob_db(mob_id).mutations_nr) clif_displaymessage(s, STRPRINTF("May mutate %i attribute up to %i%%"_fmt, get_mob_db(mob_id).mutations_nr, get_mob_db(mob_id).mutation_power)); @@ -2616,7 +2616,7 @@ ATCE atcommand_character_stats_full(Session *s, dumb_ptr, pl_sd->def2, pl_sd->def2_rate); clif_displaymessage(s, output); - output = STRPRINTF("ADD_SPEED: %d | SPEED_RATE: %d | SPEED_ADDRATE: %d | ASPD: %d | ASPD_RATE: %d | ASPD_ADDRATE: %d"_fmt, + output = STRPRINTF("ADD_SPEED: %ld | SPEED_RATE: %d | SPEED_ADDRATE: %d | ASPD: %ld | ASPD_RATE: %d | ASPD_ADDRATE: %d"_fmt, pl_sd->speed.count(), pl_sd->speed_rate, pl_sd->speed_add_rate, @@ -2640,7 +2640,7 @@ ATCE atcommand_character_stats_full(Session *s, dumb_ptr, pl_sd->hit, pl_sd->hit_rate); clif_displaymessage(s, output); - output = STRPRINTF("DEADLY_STRIKE_RATE: %d | DEADLY_STRIKE_ADD_RATE: %d | BASE_WEAPON_DELAY_ADJUST: %d"_fmt, + output = STRPRINTF("DEADLY_STRIKE_RATE: %d | DEADLY_STRIKE_ADD_RATE: %d | BASE_WEAPON_DELAY_ADJUST: %ld"_fmt, pl_sd->deadly_strike, pl_sd->deadly_strike_add_rate, pl_sd->base_weapon_delay_adjust.count()); -- cgit v1.2.3-60-g2f50 From 254a60784bd69642afadc3188ae593b738a20a93 Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Fri, 12 Apr 2024 17:52:27 +0200 Subject: Fix Wformat-zero-length warnings Squash with: remove empty lines --- src/admin/ladmin.cpp | 1 - src/char/char.cpp | 2 -- src/login/login.cpp | 2 -- 3 files changed, 5 deletions(-) diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp index f538069..240582b 100644 --- a/src/admin/ladmin.cpp +++ b/src/admin/ladmin.cpp @@ -2789,7 +2789,6 @@ int do_init(Slice argv) admin::eathena_interactive_session = isatty(0); - LADMIN_LOG(""_fmt); LADMIN_LOG("Configuration file readed.\n"_fmt); Iprintf("EAthena login-server administration tool.\n"_fmt); diff --git a/src/char/char.cpp b/src/char/char.cpp index 70ad049..a7f1490 100644 --- a/src/char/char.cpp +++ b/src/char/char.cpp @@ -2900,8 +2900,6 @@ int do_init(Slice argv) if (!loaded_config_yet) runflag &= load_config_file("conf/tmwa-char.conf"_s, char_::char_confs); - // a newline in the log... - CHAR_LOG(""_fmt); CHAR_LOG("do_init: char-server starting...\n"_fmt); runflag &= lan_check(); diff --git a/src/login/login.cpp b/src/login/login.cpp index 8f3aa70..e4c1197 100644 --- a/src/login/login.cpp +++ b/src/login/login.cpp @@ -3104,8 +3104,6 @@ bool display_conf_warnings(void) static void save_config_in_log(void) { - // a newline in the log... - LOGIN_LOG(""_fmt); LOGIN_LOG("The login-server starting...\n"_fmt); // save configuration in log file -- cgit v1.2.3-60-g2f50 From c6f6862303b28323f039a0b3bb17a189ce136658 Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Fri, 12 Apr 2024 16:53:52 +0200 Subject: O(48logN) -> O(1logN) + reformat very long lines --- src/map/atcommand.cpp | 73 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index bcfb1da..346b0ac 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -1914,22 +1914,47 @@ ATCE atcommand_mobinfo(Session *s, dumb_ptr sd, if (mob_id == Species()) return ATCE::EXIST; - clif_displaymessage(s, STRPRINTF("Monster ID: %i, English Name: %s, Japanese Name: %s"_fmt, mob_id, get_mob_db(mob_id).name, get_mob_db(mob_id).jname)); - clif_displaymessage(s, STRPRINTF("Level: %i, HP: %i, SP: %i, Base EXP: %i, JEXP: %i"_fmt, get_mob_db(mob_id).lv, get_mob_db(mob_id).max_hp, get_mob_db(mob_id).max_sp, get_mob_db(mob_id).base_exp, get_mob_db(mob_id).job_exp)); - clif_displaymessage(s, STRPRINTF("Range1: %i, ATK1: %i, ATK2: %i, DEF: %i, MDEF: %i, CRITICAL_DEF: %i"_fmt, get_mob_db(mob_id).range, get_mob_db(mob_id).atk1, get_mob_db(mob_id).atk2, get_mob_db(mob_id).def, get_mob_db(mob_id).mdef, get_mob_db(mob_id).critical_def)); - clif_displaymessage(s, STRPRINTF("Stats: STR: %i, AGI: %i, VIT: %i, INT: %i, DEX: %i, LUK: %i"_fmt, get_mob_db(mob_id).attrs[ATTR::STR], get_mob_db(mob_id).attrs[ATTR::AGI], get_mob_db(mob_id).attrs[ATTR::VIT], get_mob_db(mob_id).attrs[ATTR::INT], get_mob_db(mob_id).attrs[ATTR::DEX], get_mob_db(mob_id).attrs[ATTR::LUK])); - clif_displaymessage(s, STRPRINTF("Range2: %i, Range3: %i, Scale: %i, Race: %i, Element: %i, Element Level: %i, Mode: %i"_fmt, get_mob_db(mob_id).range2, get_mob_db(mob_id).range3, get_mob_db(mob_id).size, static_cast(get_mob_db(mob_id).race), static_cast(get_mob_db(mob_id).element.element), get_mob_db(mob_id).element.level, static_cast(get_mob_db(mob_id).mode))); - clif_displaymessage(s, STRPRINTF("Speed: %li, Adelay: %li, Amotion: %li, Dmotion: %li"_fmt, get_mob_db(mob_id).speed.count(), get_mob_db(mob_id).adelay.count(), get_mob_db(mob_id).amotion.count(), get_mob_db(mob_id).dmotion.count())); - if (get_mob_db(mob_id).mutations_nr) - clif_displaymessage(s, STRPRINTF("May mutate %i attribute up to %i%%"_fmt, get_mob_db(mob_id).mutations_nr, get_mob_db(mob_id).mutation_power)); + const struct mob_db_& mob = get_mob_db(mob_id); + + clif_displaymessage(s, STRPRINTF( + "Monster ID: %i, English Name: %s, Japanese Name: %s"_fmt, + mob_id, mob.name, mob.jname)); + clif_displaymessage(s, STRPRINTF( + "Level: %i, HP: %i, SP: %i, Base EXP: %i, JEXP: %i"_fmt, + mob.lv, mob.max_hp, mob.max_sp, mob.base_exp, mob.job_exp)); + clif_displaymessage(s, STRPRINTF( + "Range1: %i, ATK1: %i, ATK2: %i, DEF: %i, MDEF: %i, CRITICAL_DEF: %i"_fmt, + mob.range, mob.atk1, mob.atk2, + mob.def, mob.mdef, mob.critical_def)); + clif_displaymessage(s, STRPRINTF( + "Stats: STR: %i, AGI: %i, VIT: %i, INT: %i, DEX: %i, LUK: %i"_fmt, + mob.attrs[ATTR::STR], mob.attrs[ATTR::AGI], + mob.attrs[ATTR::VIT], mob.attrs[ATTR::INT], + mob.attrs[ATTR::DEX], mob.attrs[ATTR::LUK])); + clif_displaymessage(s, STRPRINTF( + "Range2: %i, Range3: %i, Scale: %i, Race: %i, Element: %i, Element Level: %i, Mode: %i"_fmt, + mob.range2, mob.range3, mob.size, static_cast(mob.race), + static_cast(mob.element.element), mob.element.level, + static_cast(mob.mode))); + clif_displaymessage(s, STRPRINTF( + "Speed: %li, Adelay: %li, Amotion: %li, Dmotion: %li"_fmt, + mob.speed.count(), mob.adelay.count(), + mob.amotion.count(), mob.dmotion.count())); + + if (mob.mutations_nr) + { + clif_displaymessage(s, STRPRINTF( + "May mutate %i attribute up to %i%%"_fmt, + mob.mutations_nr, mob.mutation_power)); + } for (int i = 0; i < MaxDrops; ++i) - if (get_mob_db(mob_id).dropitem[i].nameid) + if (mob.dropitem[i].nameid) { - Option> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[i].nameid)); + Option> i_data = Some(itemdb_search(mob.dropitem[i].nameid)); RString item_name = i_data.pmd_pget(&item_data::name).copy_or(stringish(""_s)); - int drop_rate = get_mob_db(mob_id).dropitem[i].p.num; + int drop_rate = mob.dropitem[i].p.num; char str[6]; char strpos = 0; @@ -1986,33 +2011,35 @@ ATCE atcommand_mobinfo(Session *s, dumb_ptr sd, str[strpos] = '\0'; int drop_rate2 = 10000/drop_rate; - clif_displaymessage(s, STRPRINTF("Drop ID %i: %i, Item Name: %s, Drop Chance: %s%% (1:%i)"_fmt,i, get_mob_db(mob_id).dropitem[i].nameid, item_name, str, drop_rate2)); + clif_displaymessage(s, STRPRINTF( + "Drop ID %i: %i, Item Name: %s, Drop Chance: %s%% (1:%i)"_fmt, + i, mob.dropitem[i].nameid, item_name, str, drop_rate2)); } else break; clif_displaymessage(s, STRPRINTF("Mob Mode Info:"_fmt)); - if (!bool(get_mob_db(mob_id).mode & MobMode::ZERO)) + if (!bool(mob.mode & MobMode::ZERO)) { - if (bool(get_mob_db(mob_id).mode & MobMode::CAN_MOVE)) + if (bool(mob.mode & MobMode::CAN_MOVE)) clif_displaymessage(s, STRPRINTF("Mobile"_fmt)); - if (bool(get_mob_db(mob_id).mode & MobMode::LOOTER)) + if (bool(mob.mode & MobMode::LOOTER)) clif_displaymessage(s, STRPRINTF("Picks up loot"_fmt)); - if (bool(get_mob_db(mob_id).mode & MobMode::AGGRESSIVE)) + if (bool(mob.mode & MobMode::AGGRESSIVE)) clif_displaymessage(s, STRPRINTF("Aggro"_fmt)); - if (bool(get_mob_db(mob_id).mode & MobMode::ASSIST)) + if (bool(mob.mode & MobMode::ASSIST)) clif_displaymessage(s, STRPRINTF("Assists"_fmt)); - if (bool(get_mob_db(mob_id).mode & MobMode::CAST_SENSOR)) + if (bool(mob.mode & MobMode::CAST_SENSOR)) clif_displaymessage(s, STRPRINTF("Cast Sensor"_fmt)); - if (bool(get_mob_db(mob_id).mode & MobMode::BOSS)) + if (bool(mob.mode & MobMode::BOSS)) clif_displaymessage(s, STRPRINTF("Boss"_fmt)); - if (bool(get_mob_db(mob_id).mode & MobMode::PLANT)) + if (bool(mob.mode & MobMode::PLANT)) clif_displaymessage(s, STRPRINTF("Plant"_fmt)); - if (bool(get_mob_db(mob_id).mode & MobMode::CAN_ATTACK)) + if (bool(mob.mode & MobMode::CAN_ATTACK)) clif_displaymessage(s, STRPRINTF("Can attack"_fmt)); - if (bool(get_mob_db(mob_id).mode & MobMode::DETECTOR)) + if (bool(mob.mode & MobMode::DETECTOR)) clif_displaymessage(s, STRPRINTF("Detector"_fmt)); - if (bool(get_mob_db(mob_id).mode & MobMode::CHANGE_TARGET)) + if (bool(mob.mode & MobMode::CHANGE_TARGET)) clif_displaymessage(s, STRPRINTF("Change Target"_fmt)); /* Not needed here i guess -- cgit v1.2.3-60-g2f50 From 000cdca878911759643e18a9648c587e9730fa91 Mon Sep 17 00:00:00 2001 From: Fedja Beader Date: Fri, 12 Apr 2024 19:03:32 +0200 Subject: Fix warning: 'T& tmwa::Slice::operator[](size_t) is deprecated --- src/char/char.cpp | 14 +++++++++----- src/map/script-call.cpp | 8 ++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/char/char.cpp b/src/char/char.cpp index a7f1490..7ffdd0f 100644 --- a/src/char/char.cpp +++ b/src/char/char.cpp @@ -1040,11 +1040,15 @@ int set_account_reg2(AccountId acc, Slice reg) { if (cd.key.account_id == acc) { - for (int i = 0; i < num; ++i) - cd.data->account_reg2[i] = reg[i]; - cd.data->account_reg2_num = num; - for (int i = num; i < ACCOUNT_REG2_NUM; ++i) - cd.data->account_reg2[i] = GlobalReg{}; + int i = 0; + for (const GlobalReg& r : reg) + cd.data->account_reg2[i++] = r; + + cd.data->account_reg2_num = i; + + while (i < ACCOUNT_REG2_NUM) + cd.data->account_reg2[i++] = GlobalReg{}; + c++; } } diff --git a/src/map/script-call.cpp b/src/map/script-call.cpp index 76bae8d..f551ec4 100644 --- a/src/map/script-call.cpp +++ b/src/map/script-call.cpp @@ -1004,12 +1004,12 @@ int run_script_l(ScriptPointer sp, BlockId rid, BlockId oid, st.freeloop = 0; st.is_true = 0; - for (i = 0; i < args.size(); i++) + for (const argrec_t& arg : args) { - if (args[i].name.back() == '$') - pc_setregstr(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.s); + if (arg.name.back() == '$') + pc_setregstr(sd, SIR::from(variable_names.intern(arg.name)), arg.v.s); else - pc_setreg(sd, SIR::from(variable_names.intern(args[i].name)), args[i].v.i); + pc_setreg(sd, SIR::from(variable_names.intern(arg.name)), arg.v.i); } run_script_main(&st, rootscript); -- cgit v1.2.3-60-g2f50 From 96d1f29db8260ddd478391f87fb6299682f8ae55 Mon Sep 17 00:00:00 2001 From: Freeyorp Date: Mon, 15 Apr 2024 14:26:44 +0000 Subject: script_nullpo_end: Change error printf fmt -> arg PR !270 is an ongoing investigation into outstanding compiler warnings. One such warning is `warning: format '%s' expects a matching 'char*' argument` from `script_nullpo_end(nd, STRPRINTF("no such npc: '%s'"_fmt, name));` in `src/map/script-fun.cpp`. No such warning is emitted from a similar line in `src/map/atcommand.cpp`: `AString output = STRPRINTF("Jump to %s"_fmt, npc);`. `script_nullpo_end` is a macro, rather than a function. `error` is passed in verbatim by the preprocessor. The macro definition of `script_nullpo_end` includes lines such as the following: ```cpp PRINTF("script:%s: " #error " @ %s\n"_fmt, BUILTIN_NAME(), nullpo_nd->name); ``` `#` instructs the preprocessor to transform `` into a literal string. In this case, the string literal: ```cpp "STRPRINTF(\"no such npc: '%s'\"_fmt, name)" ``` So the entire line partly resolves to something like: ```cpp printf("script:%s: STRPRINTF(\"no such npc: '%s'\"_fmt, name) @ %s\n"_fmt, BUILTIN_NAME(), nullpo_nd->name); ``` Once the compiler sees this, it throws out the warning seen here, because printf is seeing three placeholders in the formatter, but is only given two value arguments. Checking with `gcc -E`, the full expansion is as follows: ```cpp if (nullpo_chk("script-fun.cpp", 4885, __PRETTY_FUNCTION__, nd)) { if (st->oid) { dumb_ptr nullpo_nd = map_id_is_npc(st->oid); if (nullpo_nd && nullpo_nd->name) { ({ struct format_impl { constexpr static FormatString print_format() __asm__("_print_format") { return "script:%s: " "STRPRINTF(\"no such npc: '%s'\"_fmt, npc)" " @ %s\n"_fmt; } }; cxxstdio::PrintFormatter::print(( # 4885 "script-fun.cpp" 3 4 stdout # 4885 "script-fun.cpp" ), (builtin_functions[st->stack->stack_datav[st->start].get_if()->numi].name), nullpo_nd->name); }); } else if (nullpo_nd) { ({ struct format_impl { constexpr static FormatString print_format() __asm__("_print_format") { return "script:%s: " "STRPRINTF(\"no such npc: '%s'\"_fmt, npc)" " (unnamed npc)\n"_fmt; } }; cxxstdio::PrintFormatter::print(( # 4885 "script-fun.cpp" 3 4 stdout # 4885 "script-fun.cpp" ), (builtin_functions[st->stack->stack_datav[st->start].get_if()->numi].name)); }); } else { ({ struct format_impl { constexpr static FormatString print_format() __asm__("_print_format") { return "script:%s: " "STRPRINTF(\"no such npc: '%s'\"_fmt, npc)" " (no npc)\n"_fmt; } }; cxxstdio::PrintFormatter::print(( # 4885 "script-fun.cpp" 3 4 stdout # 4885 "script-fun.cpp" ), (builtin_functions[st->stack->stack_datav[st->start].get_if()->numi].name)); }); } } else { ({ struct format_impl { constexpr static FormatString print_format() __asm__("_print_format") { return "script:%s: " "STRPRINTF(\"no such npc: '%s'\"_fmt, npc)" " (no npc)\n"_fmt; } }; cxxstdio::PrintFormatter::print(( # 4885 "script-fun.cpp" 3 4 stdout # 4885 "script-fun.cpp" ), (builtin_functions[st->stack->stack_datav[st->start].get_if()->numi].name)); }); } st->state = ScriptEndState::END; return; }; ``` Note the string literal: `"STRPRINTF(\"no such npc: '%s'\"_fmt, npc)"`. `script_nullpo_end` currently can't take as its argument for `error` any expression which, when stringified, contains something which `printf` will interpret as its formatter expecting another argument. This appears to have been broken since it was first introduced in https://git.themanaworld.org/legacy/tmwa/-/commit/594eafd4f231a897cbefd since the first revision implicitly had these requirements, and also introduced usage which didn't meet them. This rewrites each PRINTF line in `script_nullpo_end` from the template ```cpp PRINTF("script:%s: " #error " @ %s\n"_fmt, BUILTIN_NAME(), nullpo_nd->name); ``` to something like ```cpp PRINTF("script:%s: %s @ %s\n"_fmt, BUILTIN_NAME(), error, nullpo_nd->name); ``` This means that instead of an expression being stringified, error is interpreted as an expression that resolves (or decays) to a string. This seems consistent with all current usage of `script_nullpo_end`. --- src/map/script-fun.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp index 2be0333..deb781a 100644 --- a/src/map/script-fun.cpp +++ b/src/map/script-fun.cpp @@ -80,14 +80,14 @@ namespace map if (st->oid) { \ dumb_ptr nullpo_nd = map_id_is_npc(st->oid); \ if (nullpo_nd && nullpo_nd->name) { \ - PRINTF("script:%s: " #error " @ %s\n"_fmt, BUILTIN_NAME(), nullpo_nd->name); \ + PRINTF("script:%s: %s @ %s\n"_fmt, BUILTIN_NAME(), error, nullpo_nd->name); \ } else if (nullpo_nd) { \ - PRINTF("script:%s: " #error " (unnamed npc)\n"_fmt, BUILTIN_NAME()); \ + PRINTF("script:%s: %s (unnamed npc)\n"_fmt, BUILTIN_NAME(), error); \ } else { \ - PRINTF("script:%s: " #error " (no npc)\n"_fmt, BUILTIN_NAME()); \ + PRINTF("script:%s: %s (no npc)\n"_fmt, BUILTIN_NAME(), error); \ } \ } else { \ - PRINTF("script:%s: " #error " (no npc)\n"_fmt, BUILTIN_NAME()); \ + PRINTF("script:%s: %s (no npc)\n"_fmt, BUILTIN_NAME(), error); \ } \ st->state = ScriptEndState::END; \ return; \ -- cgit v1.2.3-60-g2f50 From 89a49910c30769ed85a715d003fdcbbced44feee Mon Sep 17 00:00:00 2001 From: Freeyorp Date: Wed, 17 Apr 2024 05:16:53 +0000 Subject: script-fun: Convert string literals to LString This is some rather impressive type safety, albeit safety that has evidently desensitized people to warnings for rather serious problems. --- src/map/script-fun.cpp | 166 ++++++++++++++++++++++++------------------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp index deb781a..0014805 100644 --- a/src/map/script-fun.cpp +++ b/src/map/script-fun.cpp @@ -111,7 +111,7 @@ static void builtin_mes(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); sd->state.npc_dialog_mes = 1; RString mes = HARG(0) ? conv_str(st, &AARG(0)) : ""_s; clif_scriptmes(sd, st->oid, mes); @@ -125,7 +125,7 @@ static void builtin_mesq(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); sd->state.npc_dialog_mes = 1; RString mes = HARG(0) ? conv_str(st, &AARG(0)) : ""_s; MString mesq; @@ -145,8 +145,8 @@ void builtin_mesn(ScriptState *st) dumb_ptr sd = script_rid2sd(st); dumb_ptr nd; nd = map_id_is_npc(st->oid); - script_nullpo_end(nd, "npc not found"); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(nd, "npc not found"_s); + script_nullpo_end(sd, "player not found"_s); sd->state.npc_dialog_mes = 1; RString mes = HARG(0) ? conv_str(st, &AARG(0)) : RString(nd->name.xislice_h(std::find(nd->name.begin(), nd->name.end(), '#'))); // strnpcinf MString mesq; @@ -164,7 +164,7 @@ static void builtin_clear(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); clif_npc_action(sd, st->oid, 9, 0, 0, 0); } @@ -389,7 +389,7 @@ static void builtin_next(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); st->state = ScriptEndState::STOP; clif_scriptnext(sd, st->oid); } @@ -411,7 +411,7 @@ void builtin_close(ScriptState *st) } st->state = ScriptEndState::END; dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (sd->state.npc_dialog_mes) clif_scriptclose(sd, st->oid); @@ -428,7 +428,7 @@ void builtin_close2(ScriptState *st) { st->state = ScriptEndState::STOP; dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (sd->state.npc_dialog_mes) clif_scriptclose(sd, st->oid); else @@ -443,7 +443,7 @@ static void builtin_menu(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (sd->state.menu_or_input == 0) { @@ -676,7 +676,7 @@ void builtin_isat(ScriptState *st) x = conv_num(st, &AARG(1)); y = conv_num(st, &AARG(2)); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int(st->stack, (x == sd->bl_x) && (y == sd->bl_y) @@ -695,7 +695,7 @@ void builtin_warp(ScriptState *st) MapName str = stringish(ZString(conv_str(st, &AARG(0)))); x = conv_num(st, &AARG(1)); y = conv_num(st, &AARG(2)); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); pc_setpos(sd, str, x, y, BeingRemoveWhy::GONE); } @@ -748,7 +748,7 @@ void builtin_heal(ScriptState *st) hp = conv_num(st, &AARG(0)); sp = conv_num(st, &AARG(1)); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if(sd != nullptr && (sd->status.hp < 1 && hp > 0)){ pc_setstand(sd); @@ -901,7 +901,7 @@ void builtin_input(ScriptState *st) char postfix = name.back(); sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (sd->state.menu_or_input) { // Second time (rerun) @@ -962,7 +962,7 @@ void builtin_requestitem(ScriptState *st) } sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (sd->state.menu_or_input) { // Second time (rerunline) @@ -1055,7 +1055,7 @@ void builtin_requestlang(ScriptState *st) ZString name = variable_names.outtern(reg.base()); char postfix = name.back(); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (postfix != '$') { @@ -1247,7 +1247,7 @@ void builtin_foreach(ScriptState *st) else if (st->rid) sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); map_foreachinarea(std::bind(builtin_foreach_sub, ph::_1, event, sd->bl_id), m, @@ -1279,7 +1279,7 @@ void builtin_destroy(ScriptState *st) /* Not safe to call destroy if others may also be paused on this NPC! */ if (st->rid) { sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); npc_event_dequeue(sd); } @@ -1453,7 +1453,7 @@ void builtin_set(ScriptState *st) else { bl = script_rid2sd(st); - script_nullpo_end(bl, "player not found"); + script_nullpo_end(bl, "player not found"_s); } int val = conv_num(st, &AARG(1)); @@ -1628,14 +1628,14 @@ void builtin_setarray(ScriptState *st) else bl = map_id_is_npc(wrap(tid)); } - script_nullpo_end(bl, "npc not found"); + script_nullpo_end(bl, "npc not found"_s); if (st->oid && bl->bl_id != st->oid) j = getarraysize2(reg, bl); } else if (prefix != '$' && !name.startswith(".@"_s)) { bl = map_id_is_player(st->rid); - script_nullpo_end(bl, "player not found"); + script_nullpo_end(bl, "player not found"_s); } for (; i < st->end - st->start - 2 && j < 256; i++, j++) @@ -1674,7 +1674,7 @@ void builtin_cleararray(ScriptState *st) else if (prefix != '$' && !name.startswith(".@"_s)) { bl = map_id_is_player(st->rid); - script_nullpo_end(bl, "player not found"); + script_nullpo_end(bl, "player not found"_s); } for (int i = 0; i < sz; i++) @@ -1847,7 +1847,7 @@ void builtin_gmlog(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); ZString message = ZString(conv_str(st, &AARG(0))); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); log_atcommand(sd, STRPRINTF("{SCRIPT} %s"_fmt, message)); } @@ -1861,7 +1861,7 @@ void builtin_setlook(ScriptState *st) dumb_ptr sd = script_rid2sd(st); LOOK type = LOOK(conv_num(st, &AARG(0))); int val = conv_num(st, &AARG(1)); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); pc_changelook(sd, type, val); @@ -1881,7 +1881,7 @@ void builtin_countitem(ScriptState *st) struct script_data *data; sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); data = &AARG(0); get_val(st, data); @@ -1929,7 +1929,7 @@ void builtin_checkweight(ScriptState *st) struct script_data *data; sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); data = &AARG(0); get_val(st, data); @@ -1978,7 +1978,7 @@ void builtin_getitem(ScriptState *st) struct script_data *data; sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); data = &AARG(0); get_val(st, data); @@ -2082,7 +2082,7 @@ void builtin_delitem(ScriptState *st) struct script_data *data; sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); data = &AARG(0); get_val(st, data); @@ -2133,7 +2133,7 @@ static void builtin_getversion(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int(st->stack, unwrap(sd->client_version)); } @@ -2220,7 +2220,7 @@ void builtin_strcharinfo(ScriptState *st) else sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); num = conv_num(st, &AARG(0)); if (num == 0) @@ -2277,7 +2277,7 @@ void builtin_getequipid(ScriptState *st) else sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); num = conv_num(st, &AARG(0)); IOff0 i = pc_checkequip(sd, equip[num - 1]); if (i.ok()) @@ -2336,7 +2336,7 @@ void builtin_bonus(ScriptState *st) type = SP(conv_num(st, &AARG(0))); int val = conv_num(st, &AARG(1)); dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); pc_bonus(sd, type, val); } @@ -2357,7 +2357,7 @@ void builtin_bonus2(ScriptState *st) int type2 = conv_num(st, &AARG(1)); int val = conv_num(st, &AARG(2)); dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); pc_bonus2(sd, type, type2, val); } @@ -2378,7 +2378,7 @@ void builtin_skill(ScriptState *st) if (HARG(2)) flag = conv_num(st, &AARG(2)); sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); pc_skill(sd, id, level, flag); clif_skillinfoblock(sd); @@ -2397,7 +2397,7 @@ void builtin_setskill(ScriptState *st) SkillID id = static_cast(conv_num(st, &AARG(0))); level = conv_num(st, &AARG(1)); sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); level = std::min(level, MAX_SKILL_LEVEL); level = std::max(level, 0); @@ -2415,7 +2415,7 @@ void builtin_getskilllv(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); SkillID id = SkillID(conv_num(st, &AARG(0))); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int(st->stack, pc_checkskill(sd, id)); } @@ -2427,7 +2427,7 @@ static void builtin_overrideattack(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (HARG(0)) { @@ -2464,7 +2464,7 @@ static void builtin_getgmlevel(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int(st->stack, pc_isGM(sd).get_all_bits()); } @@ -2497,7 +2497,7 @@ void builtin_getopt2(ScriptState *st) dumb_ptr sd; sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int(st->stack, static_cast(sd->opt2)); @@ -2514,7 +2514,7 @@ void builtin_setopt2(ScriptState *st) Opt2 new_opt2 = Opt2(conv_num(st, &AARG(0))); sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (new_opt2 == sd->opt2) return; @@ -2535,7 +2535,7 @@ void builtin_savepoint(ScriptState *st) dumb_ptr sd = script_rid2sd(st); int x, y; MapName str = stringish(ZString(conv_str(st, &AARG(0)))); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); x = conv_num(st, &AARG(1)); y = conv_num(st, &AARG(2)); @@ -2635,7 +2635,7 @@ void builtin_openstorage(ScriptState *st) // int sync = 0; // if (st->end >= 3) sync = conv_num(st,& (st->stack->stack_data[st->start+2])); dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); // if (sync) { st->state = ScriptEndState::STOP; @@ -2655,7 +2655,7 @@ void builtin_getexp(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); int base = 0, job = 0; - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); base = conv_num(st, &AARG(0)); job = conv_num(st, &AARG(1)); @@ -2968,7 +2968,7 @@ void builtin_mobinfo_droparrays(ScriptState *st) else if (prefix != '$' && !name.startswith(".@"_s)) { bl = map_id_is_player(st->rid); - script_nullpo_end(bl, "player not found"); + script_nullpo_end(bl, "player not found"_s); } switch (request) @@ -3321,7 +3321,7 @@ void builtin_addtimer(ScriptState *st) else if (st->rid) sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); pc_addeventtimer(sd, tick, event); } @@ -3354,7 +3354,7 @@ void builtin_initnpctimer(ScriptState *st) nd_ = npc_name2id(stringish(ZString(conv_str(st, &AARG(0))))); else nd_ = map_id_is_npc(st->oid); - script_nullpo_end(nd_, "no npc"); + script_nullpo_end(nd_, "no npc"_s); assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); dumb_ptr nd = nd_->is_script(); @@ -3375,7 +3375,7 @@ void builtin_startnpctimer(ScriptState *st) nd_ = npc_name2id(stringish(ZString(conv_str(st, &AARG(0))))); else nd_ = map_id_is_npc(st->oid); - script_nullpo_end(nd_, "no npc"); + script_nullpo_end(nd_, "no npc"_s); assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); dumb_ptr nd = nd_->is_script(); @@ -3395,7 +3395,7 @@ void builtin_stopnpctimer(ScriptState *st) nd_ = npc_name2id(stringish(ZString(conv_str(st, &AARG(0))))); else nd_ = map_id_is_npc(st->oid); - script_nullpo_end(nd_, "no npc"); + script_nullpo_end(nd_, "no npc"_s); assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); dumb_ptr nd = nd_->is_script(); @@ -3417,7 +3417,7 @@ void builtin_getnpctimer(ScriptState *st) nd_ = npc_name2id(stringish(ZString(conv_str(st, &AARG(1))))); else nd_ = map_id_is_npc(st->oid); - script_nullpo_end(nd_, "no npc"); + script_nullpo_end(nd_, "no npc"_s); assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); dumb_ptr nd = nd_->is_script(); @@ -3450,7 +3450,7 @@ void builtin_setnpctimer(ScriptState *st) nd_ = npc_name2id(stringish(ZString(conv_str(st, &AARG(1))))); else nd_ = map_id_is_npc(st->oid); - script_nullpo_end(nd_, "no npc"); + script_nullpo_end(nd_, "no npc"_s); assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT); dumb_ptr nd = nd_->is_script(); @@ -3469,7 +3469,7 @@ void builtin_npcaction(ScriptState *st) int id = 0; short x = HARG(2) ? conv_num(st, &AARG(2)) : 0; short y = HARG(3) ? conv_num(st, &AARG(3)) : 0; - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if(HARG(1)) { @@ -3494,7 +3494,7 @@ static void builtin_camera(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (HARG(0)) { @@ -3551,7 +3551,7 @@ void builtin_setnpcdirection(ScriptState *st) else nd_ = map_id_is_npc(st->oid); - script_nullpo_end(nd_, "no npc"); + script_nullpo_end(nd_, "no npc"_s); if (bool(conv_num(st, &AARG(1)))) action = DamageType::SIT; @@ -3567,7 +3567,7 @@ void builtin_setnpcdirection(ScriptState *st) if (st->rid) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); clif_sitnpc_towards(sd, nd_, action); clif_setnpcdirection_towards(sd, nd_, dir); } @@ -3597,7 +3597,7 @@ void builtin_announce(ScriptState *st) bl = map_id2bl(st->oid); else bl = script_rid2sd(st); - script_nullpo_end(bl, "player not found"); + script_nullpo_end(bl, "player not found"_s); clif_GMmessage(bl, str, flag); } else @@ -3979,7 +3979,7 @@ void builtin_resetstatus(ScriptState *st) { dumb_ptr sd; sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); pc_resetstate(sd); } @@ -4161,7 +4161,7 @@ void builtin_setpvpchannel(ScriptState *st) if (flag < 1) flag = 0; - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); sd->state.pvpchannel = flag; } @@ -4178,7 +4178,7 @@ void builtin_getpvpflag(ScriptState *st) else sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); int num = conv_num(st, &AARG(0)); int flag = 0; @@ -4360,7 +4360,7 @@ static void builtin_getpartnerid2(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int(st->stack, unwrap(sd->status.partner_id)); } @@ -4425,7 +4425,7 @@ void builtin_explode(ScriptState *st) else if (prefix != '$' && prefix != '.') { bl = map_id2bl(st->rid)->is_player(); - script_nullpo_end(bl, "target player not found"); + script_nullpo_end(bl, "target player not found"_s); } @@ -4476,7 +4476,7 @@ void builtin_getinventorylist(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); int j = 0; - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); for (IOff0 i : IOff0::iter()) { @@ -4507,7 +4507,7 @@ void builtin_getactivatedpoolskilllist(ScriptState *st) int skill_pool_size = skill_pool(sd, pool_skills); int i, count = 0; - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); for (i = 0; i < skill_pool_size; i++) { @@ -4540,7 +4540,7 @@ void builtin_getunactivatedpoolskilllist(ScriptState *st) dumb_ptr sd = script_rid2sd(st); int i, count = 0; - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); for (i = 0; i < skill_pool_skills.size(); i++) { @@ -4572,7 +4572,7 @@ void builtin_poolskill(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); SkillID skill_id = SkillID(conv_num(st, &AARG(0))); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); skill_pool_activate(sd, skill_id); clif_skillinfoblock(sd); @@ -4587,7 +4587,7 @@ void builtin_unpoolskill(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); SkillID skill_id = SkillID(conv_num(st, &AARG(0))); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); skill_pool_deactivate(sd, skill_id); clif_skillinfoblock(sd); @@ -4831,7 +4831,7 @@ void builtin_nude(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); for (EQUIP i : EQUIPs) { @@ -4851,7 +4851,7 @@ static void builtin_unequipbyid(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); EQUIP slot_id = EQUIP(conv_num(st, &AARG(0))); @@ -4992,7 +4992,7 @@ void builtin_title(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); ZString msg = ZString(conv_str(st, &AARG(0))); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); clif_npc_send_title(sd->sess, st->oid, msg); } @@ -5013,7 +5013,7 @@ void builtin_smsg(ScriptState *st) int type = HARG(1) ? conv_num(st, &AARG(0)) : 0; ZString msg = ZString(conv_str(st, (HARG(1) ? &AARG(1) : &AARG(0)))); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (type < 0 || type > 0xFF) type = 0; @@ -5028,7 +5028,7 @@ static void builtin_remotecmd(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (HARG(1)) { @@ -5055,7 +5055,7 @@ void builtin_sendcollision(ScriptState *st) short x1, y1, x2, y2; x1 = x2 = conv_num(st, &AARG(2)); y1 = y2 = conv_num(st, &AARG(3)); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (HARG(5)) { @@ -5088,7 +5088,7 @@ void builtin_music(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); ZString msg = ZString(conv_str(st, &AARG(0))); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); clif_change_music(sd, msg); } @@ -5113,7 +5113,7 @@ void builtin_mapmask(ScriptState *st) else if(HARG(1) && nd) nd->bl_m->mask = map_mask; - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); clif_send_mask(sd, map_mask); } @@ -5195,7 +5195,7 @@ void builtin_getlook(ScriptState *st) dumb_ptr sd = script_rid2sd(st); LOOK type = LOOK(conv_num(st, &AARG(0))); int val = -1; - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); switch (type) { @@ -5239,7 +5239,7 @@ void builtin_getsavepoint(ScriptState *st) { int x, y, type; dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); type = conv_num(st, &AARG(0)); @@ -5333,7 +5333,7 @@ void builtin_isin(ScriptState *st) x2 = conv_num(st, &AARG(3)); y2 = conv_num(st, &AARG(4)); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int(st->stack, (sd->bl_x >= x1 && sd->bl_x <= x2) @@ -5369,7 +5369,7 @@ void builtin_shop(ScriptState *st) dumb_ptr sd = script_rid2sd(st); dumb_ptr nd; - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); NpcName name = stringish(ZString(conv_str(st, &AARG(0)))); nd = npc_name2id(name); @@ -5387,7 +5387,7 @@ static void builtin_isdead(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int(st->stack, pc_isdead(sd)); } @@ -5421,7 +5421,7 @@ static void builtin_getx(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int(st->stack, sd->bl_x); } @@ -5433,7 +5433,7 @@ static void builtin_gety(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int(st->stack, sd->bl_y); } @@ -5445,7 +5445,7 @@ static void builtin_getdir(ScriptState *st) { dumb_ptr sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int(st->stack, static_cast(sd->dir)); } @@ -5524,7 +5524,7 @@ void builtin_strnpcinfo(ScriptState *st) nd = map_id_is_npc(st->oid); } - script_nullpo_end(nd, "npc not found"); + script_nullpo_end(nd, "npc not found"_s); switch(num) { @@ -5562,7 +5562,7 @@ void builtin_getnpcx(ScriptState *st) nd = map_id_is_npc(st->oid); } - script_nullpo_end(nd, "no npc"); + script_nullpo_end(nd, "no npc"_s); push_int(st->stack, nd->bl_x); } @@ -5584,7 +5584,7 @@ void builtin_getnpcy(ScriptState *st) nd = map_id_is_npc(st->oid); } - script_nullpo_end(nd, "no npc"); + script_nullpo_end(nd, "no npc"_s); push_int(st->stack, nd->bl_y); } -- cgit v1.2.3-60-g2f50 From bafec99026144d807bb140b6daf86853c4603e51 Mon Sep 17 00:00:00 2001 From: Freeyorp Date: Mon, 29 Apr 2024 08:36:26 +0000 Subject: mob: Avoid dangling else --- src/map/mob.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/map/mob.cpp b/src/map/mob.cpp index f2f6815..996e2bb 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -2556,10 +2556,12 @@ int mob_damage(dumb_ptr src, dumb_ptr md, int damage, // activity if (sd) + { if (sd->activity.attacks == 2147483647) sd->activity.attacks = 1; else sd->activity.attacks++; + } if (md->hp > 0) { -- cgit v1.2.3-60-g2f50 From 9d00c99cd0c636aa5a6c25677d334d455cd9195f Mon Sep 17 00:00:00 2001 From: Freeyorp Date: Mon, 29 Apr 2024 10:15:51 +0000 Subject: npc_destroy: Defer NPC destruction via timer 055-1 _nodes.txt will call `destroy;` from within OnInit, that is during an iteration of the global `ev_db`. Previously, concurrent modification invalidated this iteration, resulting in a crash. This still nullifes `oid`, dequeues all timers, and effectively calls `builtin_end`. `npc_data::deletion_pending` is extended to include a third state. In addition to no deletion happening, and indicating when `npc_free` is currently on the stack, it now also tracks whether the NPC is about to be deleted by a timer. This means that an NPC which is about to be deleted is still blocked from triggering new events, much like an NPC actively being deleted would. Starting or continuing an NPC dialog with an NPC that does not, or is about to no longer exist, is now also blocked. --- src/map/map.hpp | 6 +++++- src/map/npc-parse.cpp | 8 ++++---- src/map/npc.cpp | 19 ++++++++++++++----- src/map/script-fun.cpp | 15 +++++++++++++-- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/map/map.hpp b/src/map/map.hpp index 7cf43d5..56d07a5 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -360,7 +360,11 @@ struct npc_data : block_list Opt0 option; short flag; - bool deletion_pending; + enum { + NOT_DELETING = 0, + DELETION_QUEUED = 1, + DELETION_ACTIVE = 2 + } deletion_pending; Array eventtimer; diff --git a/src/map/npc-parse.cpp b/src/map/npc-parse.cpp index 47b851c..df1a09a 100644 --- a/src/map/npc-parse.cpp +++ b/src/map/npc-parse.cpp @@ -164,7 +164,7 @@ bool npc_load_warp(ast::npc::Warp& warp) nd->warp.xs = xs; nd->warp.ys = ys; - nd->deletion_pending = false; + nd->deletion_pending = npc_data::NOT_DELETING; npc_warp++; nd->bl_type = BL::NPC; @@ -228,7 +228,7 @@ bool npc_load_shop(ast::npc::Shop& shop) nd->opt2 = Opt2::ZERO; nd->opt3 = Opt3::ZERO; - nd->deletion_pending = false; + nd->deletion_pending = npc_data::NOT_DELETING; npc_shop++; nd->bl_type = BL::NPC; @@ -458,7 +458,7 @@ bool npc_load_script_none(ast::script::ScriptBody& body, ast::npc::ScriptNone& s nd->opt2 = Opt2::ZERO; nd->opt3 = Opt3::ZERO; - nd->deletion_pending = false; + nd->deletion_pending = npc_data::NOT_DELETING; npc_script++; nd->bl_type = BL::NPC; @@ -568,7 +568,7 @@ bool npc_load_script_map(ast::script::ScriptBody& body, ast::npc::ScriptMap& scr nd->opt2 = Opt2::ZERO; nd->opt3 = Opt3::ZERO; - nd->deletion_pending = false; + nd->deletion_pending = npc_data::NOT_DELETING; npc_script++; nd->bl_type = BL::NPC; diff --git a/src/map/npc.cpp b/src/map/npc.cpp index e8d6d4b..8a6bead 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -357,7 +357,7 @@ void npc_eventtimer(TimerData *, tick_t, BlockId, NpcEvent data) data); return; }); - if ((nd = ev->nd) == nullptr || nd->deletion_pending == true) + if ((nd = ev->nd) == nullptr || nd->deletion_pending != npc_data::NOT_DELETING) { if (battle_config.error_log) PRINTF("npc_event: event not found [%s]\n"_fmt, @@ -591,7 +591,7 @@ int npc_event(dumb_ptr sd, NpcEvent eventname, ev.pos = ev2->pos; } - if ((nd = ev.nd) == nullptr || nd->deletion_pending == true) + if ((nd = ev.nd) == nullptr || nd->deletion_pending != npc_data::NOT_DELETING) { if (!mob_kill && battle_config.error_log) PRINTF("npc_event: event not found [%s]\n"_fmt, @@ -774,6 +774,14 @@ int npc_click(dumb_ptr sd, BlockId id) nd = map_id_is_npc(id); + // If someone clicked on an NPC that is about to no longer exist, then + // release them + if (nd->deletion_pending != npc_data::NOT_DELETING) + { + clif_scriptclose(sd, id); + return 1; + } + if (nd->flag & 1) // 無効化されている return 1; @@ -818,7 +826,8 @@ int npc_scriptcont(dumb_ptr sd, BlockId id) nd = map_id_is_npc(id); - if (!nd /* NPC was disposed? */) + // If the NPC is about to be deleted, release the PC + if (nd->deletion_pending != npc_data::NOT_DELETING) { clif_scriptclose(sd, id); npc_event_dequeue(sd); @@ -1091,10 +1100,10 @@ void npc_propagate_update(dumb_ptr nd) void npc_free(dumb_ptr nd) { - if (nd == nullptr || nd->deletion_pending == true) + if (nd == nullptr || nd->deletion_pending == npc_data::DELETION_ACTIVE) return; - nd->deletion_pending = true; + nd->deletion_pending = npc_data::DELETION_ACTIVE; nd->flag |= 1; clif_clearchar(nd, BeingRemoveWhy::GONE); npc_propagate_update(nd); diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp index 0014805..13b4bc8 100644 --- a/src/map/script-fun.cpp +++ b/src/map/script-fun.cpp @@ -1283,8 +1283,19 @@ void builtin_destroy(ScriptState *st) npc_event_dequeue(sd); } + // Cancel all existing timers on the NPC. + // They "would" never fire, and we don't want race conditions here. + for (int i = 0; i < MAX_EVENTTIMER; i++) + { + nd->eventtimer[i].cancel(); + } + // Schedule the NPC to be freed on the next available tick. + // Scripts can be invoked under iteration of the ev_db global event + // database, and we don't want to invalidate active iterators. + nd->deletion_pending = npc_data::DELETION_QUEUED; + nd->eventtimer[0] = Timer(gettick(), std::bind(npc_free, nd)); + nd = nd->is_script(); - npc_free(nd); st->oid = BlockId(); if (!HARG(0)) @@ -1350,7 +1361,7 @@ void builtin_puppet(ScriptState *st) nd->npc_subtype = NpcSubtype::SCRIPT; npc_script++; - nd->deletion_pending = false; + nd->deletion_pending = npc_data::NOT_DELETING; nd->n = map_addnpc(nd->bl_m, nd); -- cgit v1.2.3-60-g2f50 From 04dfb9a9af92352af291ea8b1fa7150644614397 Mon Sep 17 00:00:00 2001 From: Freeyorp Date: Mon, 29 Apr 2024 12:21:55 +0000 Subject: npc_scripcont: consistent return value `npc_scriptcont` now consistently returns 1 on failure, though in practice, this return value was always ignored. --- src/map/npc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map/npc.cpp b/src/map/npc.cpp index 8a6bead..930b9de 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -831,14 +831,14 @@ int npc_scriptcont(dumb_ptr sd, BlockId id) { clif_scriptclose(sd, id); npc_event_dequeue(sd); - return 0; + return 1; } if (nd->is_script()->scr.parent && map_id2bl(nd->is_script()->scr.parent) == nullptr) { npc_free(nd); - return 0; + return 1; } sd->npc_pos = run_script(ScriptPointer(script_or_parent(nd->is_script()), sd->npc_pos), sd->bl_id, id); -- cgit v1.2.3-60-g2f50 From aaaf2321b8a0e47c46f8ba8a2090c423f5947cf9 Mon Sep 17 00:00:00 2001 From: Freeyorp Date: Mon, 29 Apr 2024 12:22:29 +0000 Subject: npc: formatting Nice consistency there, past!Freeyorp. --- src/map/npc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/map/npc.cpp b/src/map/npc.cpp index 930b9de..7d3e62b 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -767,7 +767,8 @@ int npc_click(dumb_ptr sd, BlockId id) } } - if (npc_checknear(sd, id)) { + if (npc_checknear(sd, id)) + { clif_scriptclose(sd, id); return 1; } -- cgit v1.2.3-60-g2f50