diff options
-rw-r--r-- | .gitlab-ci.yml | 91 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile.in | 11 | ||||
-rw-r--r-- | README.md | 98 | ||||
-rwxr-xr-x | configure | 8 | ||||
m--------- | deps/googletest | 0 | ||||
-rw-r--r-- | src/admin/ladmin.cpp | 3 | ||||
-rw-r--r-- | src/ast/item_test.cpp | 18 | ||||
-rw-r--r-- | src/char/char.cpp | 16 | ||||
-rw-r--r-- | src/login/login.cpp | 2 | ||||
-rw-r--r-- | src/map/atcommand.cpp | 174 | ||||
-rw-r--r-- | src/map/map.hpp | 6 | ||||
-rw-r--r-- | src/map/mob.cpp | 5 | ||||
-rw-r--r-- | src/map/npc-parse.cpp | 8 | ||||
-rw-r--r-- | src/map/npc.cpp | 43 | ||||
-rw-r--r-- | src/map/pc.cpp | 6 | ||||
-rw-r--r-- | src/map/script-call.cpp | 8 | ||||
-rw-r--r-- | src/map/script-fun.cpp | 215 | ||||
-rwxr-xr-x | tools/config.py | 2 | ||||
-rw-r--r-- | tools/debug-debug.gdb | 15 | ||||
-rwxr-xr-x | tools/protocol.py | 67 |
21 files changed, 547 insertions, 252 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..f7c750a --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,91 @@ +# Copied in from Moubootaur Legends's Hercules .gitlab-ci.yml +stages: + - build + - test + +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: + - 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: + - 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)" + - ./configure --user + - make + - whoami + - make install + artifacts: # required for test stage + untracked: true + expire_in: 30 mins + + + + +# 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 + 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 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/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) @@ -2,22 +2,13 @@ ![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 - -<br><br> - -<details> -<summary>click _here_ for the old readme</summary> - -<br> - 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. @@ -26,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: @@ -154,22 +135,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"). @@ -221,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 @@ -242,37 +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). -</details> + +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! @@ -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 +Subproject 2fe3bd994b3189899d93f1d5a881e725e046fdc diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp index 6e8642b..240582b 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); @@ -2789,7 +2789,6 @@ int do_init(Slice<ZString> 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/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<Item>(); 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); } } diff --git a/src/char/char.cpp b/src/char/char.cpp index 70ad049..7ffdd0f 100644 --- a/src/char/char.cpp +++ b/src/char/char.cpp @@ -1040,11 +1040,15 @@ int set_account_reg2(AccountId acc, Slice<GlobalReg> 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++; } } @@ -2900,8 +2904,6 @@ int do_init(Slice<ZString> 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 diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index eaefd4f..346b0ac 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -1667,36 +1667,100 @@ ATCE atcommand_pvpoff(Session *s, dumb_ptr<map_session_data> 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<map_session_data>, ZString message) { - int rate; + int rate = extract_rate(s, message); + if (rate < 0) + return ATCE::USAGE; - if (!extract(message, &rate) || !rate) - { - clif_displaymessage(s, - "Please, enter a rate adjustement (usage: @exprate <percent>)."_s); + 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); + clif_displaymessage(s, output); + return ATCE::OKAY; +} + +static +ATCE atcommand_bexprate(Session *s, dumb_ptr<map_session_data>, + ZString message) +{ + 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); + return ATCE::OKAY; +} + +static +ATCE atcommand_jexprate(Session *s, dumb_ptr<map_session_data>, + ZString message) +{ + int rate = extract_rate(s, message); + if (rate < 0) + return ATCE::USAGE; + battle_config.job_exp_rate = rate; - AString output = STRPRINTF("All Xp at %d percent"_fmt, 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<map_session_data>, +ATCE atcommand_droprate(Session *s, dumb_ptr<map_session_data>, ZString message) { - AString output = STRPRINTF("Experience rates: Base %d%% / Job %d%%"_fmt, battle_config.base_exp_rate, battle_config.job_exp_rate); + 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); - output = STRPRINTF("Drop rate: 100%%"_fmt); + return ATCE::OKAY; +} + +static +ATCE atcommand_rates(Session *s, dumb_ptr<map_session_data>, + ZString message) +{ + AString output = STRPRINTF( + "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; } + static ATCE atcommand_pvpon(Session *s, dumb_ptr<map_session_data> sd, ZString) @@ -1850,22 +1914,47 @@ ATCE atcommand_mobinfo(Session *s, dumb_ptr<map_session_data> 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, 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())); - 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<int>(mob.race), + static_cast<int>(mob.element.element), mob.element.level, + static_cast<int>(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<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[i].nameid)); + Option<P<struct item_data>> i_data = Some(itemdb_search(mob.dropitem[i].nameid)); RString item_name = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_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; @@ -1922,33 +2011,35 @@ ATCE atcommand_mobinfo(Session *s, dumb_ptr<map_session_data> 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 @@ -2552,7 +2643,7 @@ ATCE atcommand_character_stats_full(Session *s, dumb_ptr<map_session_data>, 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, @@ -2576,7 +2667,7 @@ ATCE atcommand_character_stats_full(Session *s, dumb_ptr<map_session_data>, 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()); @@ -5599,10 +5690,19 @@ Map<XString, AtCommandInfo> atcommand_info = "Enable PvP on your map"_s}}, {"exprate"_s, {"<percent>"_s, 60, atcommand_exprate, - "Set base job/exp rate"_s}}, + "Set base and job exp rate"_s}}, + {"bexprate"_s, {"<percent>"_s, + 60, atcommand_bexprate, + "Set base exp rate"_s}}, + {"jexprate"_s, {"<percent>"_s, + 60, atcommand_jexprate, + "Set job exp rate"_s}}, + {"droprate"_s, {"<percent>"_s, + 60, atcommand_droprate, + "Set drop rate"_s}}, {"rates"_s, {""_s, 0, atcommand_rates, - "Show base job/exp rate"_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/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<Timer, MAX_EVENTTIMER> eventtimer; diff --git a/src/map/mob.cpp b/src/map/mob.cpp index 4fd9d6d..996e2bb 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -2556,10 +2556,12 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> md, int damage, // activity if (sd) + { if (sd->activity.attacks == 2147483647) sd->activity.attacks = 1; else sd->activity.attacks++; + } if (md->hp > 0) { @@ -2736,6 +2738,9 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> 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/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 ee2f30c..7d3e62b 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<map_session_data> 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, @@ -767,13 +767,22 @@ int npc_click(dumb_ptr<map_session_data> sd, BlockId id) } } - if (npc_checknear(sd, id)) { + if (npc_checknear(sd, id)) + { clif_scriptclose(sd, id); return 1; } 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,18 +827,19 @@ int npc_scriptcont(dumb_ptr<map_session_data> 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); - 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); @@ -1050,6 +1060,23 @@ void npc_free_internal(dumb_ptr<npc_data> 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()) + { + std::vector<NpcEvent> 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_(); } @@ -1074,10 +1101,10 @@ void npc_propagate_update(dumb_ptr<npc_data> nd) void npc_free(dumb_ptr<npc_data> 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/pc.cpp b/src/map/pc.cpp index c7c6acf..12af48f 100644 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -1135,7 +1135,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first) b_weight = sd->weight; b_max_weight = sd->max_weight; earray<int, ATTR, ATTR::COUNT> b_paramb = sd->paramb; - earray<int, ATTR, ATTR::COUNT> b_parame = sd->paramc; + earray<int, ATTR, ATTR::COUNT> b_parame = sd->parame; earray<SkillValue, SkillID, MAX_SKILL> b_skill = sd->status.skill; b_hit = sd->hit; b_flee = sd->flee; @@ -1566,9 +1566,9 @@ int pc_calcstatus(dumb_ptr<map_session_data> 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); 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); diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp index e51a692..2c09ba6 100644 --- a/src/map/script-fun.cpp +++ b/src/map/script-fun.cpp @@ -80,14 +80,14 @@ namespace map if (st->oid) { \ dumb_ptr<npc_data> 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; \ @@ -111,7 +111,7 @@ static void builtin_mes(ScriptState *st) { dumb_ptr<map_session_data> 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<map_session_data> 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<map_session_data> sd = script_rid2sd(st); dumb_ptr<npc_data> 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<map_session_data> 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<map_session_data> 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<map_session_data> 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<map_session_data> 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<map_session_data> 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<ScriptDataInt>(st->stack, (x == sd->bl_x) && (y == sd->bl_y) @@ -695,7 +695,7 @@ void builtin_warp(ScriptState *st) MapName str = stringish<MapName>(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,12 +1279,23 @@ 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); } + // 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); @@ -1453,7 +1464,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 +1639,14 @@ void builtin_setarray(ScriptState *st) else bl = map_id_is_npc(wrap<BlockId>(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 +1685,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 +1858,7 @@ void builtin_gmlog(ScriptState *st) { dumb_ptr<map_session_data> 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 +1872,7 @@ void builtin_setlook(ScriptState *st) dumb_ptr<map_session_data> 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 +1892,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 +1940,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 +1989,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 +2093,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 +2144,7 @@ static void builtin_getversion(ScriptState *st) { dumb_ptr<map_session_data> sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int<ScriptDataInt>(st->stack, unwrap<ClientVersion>(sd->client_version)); } @@ -2220,7 +2231,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 +2288,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 +2347,7 @@ void builtin_bonus(ScriptState *st) type = SP(conv_num(st, &AARG(0))); int val = conv_num(st, &AARG(1)); dumb_ptr<map_session_data> 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 +2368,7 @@ void builtin_bonus2(ScriptState *st) int type2 = conv_num(st, &AARG(1)); int val = conv_num(st, &AARG(2)); dumb_ptr<map_session_data> 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 +2389,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 +2408,7 @@ void builtin_setskill(ScriptState *st) SkillID id = static_cast<SkillID>(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 +2426,7 @@ void builtin_getskilllv(ScriptState *st) { dumb_ptr<map_session_data> 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<ScriptDataInt>(st->stack, pc_checkskill(sd, id)); } @@ -2427,7 +2438,7 @@ static void builtin_overrideattack(ScriptState *st) { dumb_ptr<map_session_data> sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); //PRINTF("inside override attack!!\n"_fmt); if (HARG(0)) @@ -2477,7 +2488,7 @@ static void builtin_getgmlevel(ScriptState *st) { dumb_ptr<map_session_data> sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int<ScriptDataInt>(st->stack, pc_isGM(sd).get_all_bits()); } @@ -2510,7 +2521,7 @@ void builtin_getopt2(ScriptState *st) dumb_ptr<map_session_data> sd; sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int<ScriptDataInt>(st->stack, static_cast<uint16_t>(sd->opt2)); @@ -2527,7 +2538,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; @@ -2548,7 +2559,7 @@ void builtin_savepoint(ScriptState *st) dumb_ptr<map_session_data> sd = script_rid2sd(st); int x, y; MapName str = stringish<MapName>(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)); @@ -2648,7 +2659,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<map_session_data> 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; @@ -2668,7 +2679,7 @@ void builtin_getexp(ScriptState *st) { dumb_ptr<map_session_data> 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)); @@ -2981,7 +2992,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) @@ -3334,7 +3345,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); } @@ -3367,7 +3378,7 @@ void builtin_initnpctimer(ScriptState *st) nd_ = npc_name2id(stringish<NpcName>(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<npc_data_script> nd = nd_->is_script(); @@ -3388,7 +3399,7 @@ void builtin_startnpctimer(ScriptState *st) nd_ = npc_name2id(stringish<NpcName>(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<npc_data_script> nd = nd_->is_script(); @@ -3408,7 +3419,7 @@ void builtin_stopnpctimer(ScriptState *st) nd_ = npc_name2id(stringish<NpcName>(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<npc_data_script> nd = nd_->is_script(); @@ -3430,7 +3441,7 @@ void builtin_getnpctimer(ScriptState *st) nd_ = npc_name2id(stringish<NpcName>(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<npc_data_script> nd = nd_->is_script(); @@ -3463,7 +3474,7 @@ void builtin_setnpctimer(ScriptState *st) nd_ = npc_name2id(stringish<NpcName>(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<npc_data_script> nd = nd_->is_script(); @@ -3482,7 +3493,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)) { @@ -3507,7 +3518,7 @@ static void builtin_camera(ScriptState *st) { dumb_ptr<map_session_data> sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (HARG(0)) { @@ -3564,7 +3575,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; @@ -3580,7 +3591,7 @@ void builtin_setnpcdirection(ScriptState *st) if (st->rid) { dumb_ptr<map_session_data> 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); } @@ -3610,7 +3621,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 @@ -3992,7 +4003,7 @@ void builtin_resetstatus(ScriptState *st) { dumb_ptr<map_session_data> sd; sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); pc_resetstate(sd); } @@ -4174,7 +4185,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; } @@ -4191,7 +4202,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; @@ -4373,7 +4384,7 @@ static void builtin_getpartnerid2(ScriptState *st) { dumb_ptr<map_session_data> sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status.partner_id)); } @@ -4438,7 +4449,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); } @@ -4489,7 +4500,7 @@ void builtin_getinventorylist(ScriptState *st) { dumb_ptr<map_session_data> 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()) { @@ -4520,7 +4531,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++) { @@ -4553,7 +4564,7 @@ void builtin_getunactivatedpoolskilllist(ScriptState *st) dumb_ptr<map_session_data> 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++) { @@ -4585,7 +4596,7 @@ void builtin_poolskill(ScriptState *st) { dumb_ptr<map_session_data> 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); @@ -4600,7 +4611,7 @@ void builtin_unpoolskill(ScriptState *st) { dumb_ptr<map_session_data> 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); @@ -4844,7 +4855,7 @@ void builtin_nude(ScriptState *st) { dumb_ptr<map_session_data> sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); for (EQUIP i : EQUIPs) { @@ -4864,7 +4875,7 @@ static void builtin_unequipbyid(ScriptState *st) { dumb_ptr<map_session_data> 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))); @@ -5005,7 +5016,7 @@ void builtin_title(ScriptState *st) { dumb_ptr<map_session_data> 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); } @@ -5026,7 +5037,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; @@ -5041,7 +5052,7 @@ static void builtin_remotecmd(ScriptState *st) { dumb_ptr<map_session_data> sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); if (HARG(1)) { @@ -5068,7 +5079,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)) { @@ -5101,7 +5112,7 @@ void builtin_music(ScriptState *st) { dumb_ptr<map_session_data> 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); } @@ -5126,7 +5137,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); } @@ -5208,7 +5219,7 @@ void builtin_getlook(ScriptState *st) dumb_ptr<map_session_data> 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) { @@ -5252,7 +5263,7 @@ void builtin_getsavepoint(ScriptState *st) { int x, y, type; dumb_ptr<map_session_data> 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)); @@ -5346,7 +5357,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<ScriptDataInt>(st->stack, (sd->bl_x >= x1 && sd->bl_x <= x2) @@ -5382,7 +5393,7 @@ void builtin_shop(ScriptState *st) dumb_ptr<map_session_data> sd = script_rid2sd(st); dumb_ptr<npc_data> nd; - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0)))); nd = npc_name2id(name); @@ -5400,7 +5411,7 @@ static void builtin_isdead(ScriptState *st) { dumb_ptr<map_session_data> sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int<ScriptDataInt>(st->stack, pc_isdead(sd)); } @@ -5434,7 +5445,7 @@ static void builtin_getx(ScriptState *st) { dumb_ptr<map_session_data> sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int<ScriptDataInt>(st->stack, sd->bl_x); } @@ -5446,7 +5457,7 @@ static void builtin_gety(ScriptState *st) { dumb_ptr<map_session_data> sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int<ScriptDataInt>(st->stack, sd->bl_y); } @@ -5458,7 +5469,7 @@ static void builtin_getdir(ScriptState *st) { dumb_ptr<map_session_data> sd = script_rid2sd(st); - script_nullpo_end(sd, "player not found"); + script_nullpo_end(sd, "player not found"_s); push_int<ScriptDataInt>(st->stack, static_cast<uint8_t>(sd->dir)); } @@ -5485,6 +5496,30 @@ void builtin_getmap(ScriptState *st) } /*========================================== + * Get the maximum x coordinate of a map + *------------------------------------------ + */ +static +void builtin_getmapmaxx(ScriptState *st) +{ + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + push_int<ScriptDataInt>(st->stack, m->xs-1); +} + +/*========================================== + * Get the maximum y coordinate of a map + *------------------------------------------ + */ +static +void builtin_getmapmaxy(ScriptState *st) +{ + MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0)))); + P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return); + push_int<ScriptDataInt>(st->stack, m->ys-1); +} + +/*========================================== * Get the NPC's info *------------------------------------------ */ @@ -5513,7 +5548,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) { @@ -5551,7 +5586,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<ScriptDataInt>(st->stack, nd->bl_x); } @@ -5573,7 +5608,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<ScriptDataInt>(st->stack, nd->bl_y); } @@ -5745,6 +5780,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'), diff --git a/tools/config.py b/tools/config.py index a187c06..2f0781d 100755 --- a/tools/config.py +++ b/tools/config.py @@ -601,6 +601,8 @@ 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('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') 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() 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' |