summaryrefslogblamecommitdiff
path: root/src/map/script-startup.cpp
blob: 7c77a27fa58d81e1f5fc193eef38f97bc9a8a6cf (plain) (tree)





























                                                                           
                            




                           


































































                                                                                                                         
                                         































































































































































                                                                            
#include "script-startup-internal.hpp"
//    script-startup.cpp - EAthena script frontend, engine, and library.
//
//    Copyright © ????-2004 Athena Dev Teams
//    Copyright © 2004-2011 The Mana World Development Team
//    Copyright © 2011 Chuck Miller
//    Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
//    Copyright © 2013 wushin
//
//    This file is part of The Mana World (Athena server)
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "../strings/zstring.hpp"

#include "../generic/db.hpp"
#include "../generic/intern-pool.hpp"

#include "../io/cxxstdio.hpp"
#include "../io/extract.hpp"
#include "../io/read.hpp"
#include "../io/lock.hpp"

#include "../net/timer.hpp"

#include "map.hpp"
#include "script-parse-internal.hpp"
#include "script-persist.hpp"

#include "../poison.hpp"


namespace tmwa
{
DMap<SIR, int> mapreg_db;
Map<SIR, RString> mapregstr_db;
int mapreg_dirty = -1;
AString mapreg_txt = "save/mapreg.txt"_s;
constexpr std::chrono::milliseconds MAPREG_AUTOSAVE_INTERVAL = 10_s;

bool read_constdb(ZString filename)
{
    io::ReadFile in(filename);
    if (!in.is_open())
    {
        PRINTF("can't read %s\n"_fmt, filename);
        return false;
    }

    bool rv = true;
    AString line_;
    while (in.getline(line_))
    {
        // is_comment only works for whole-line comments
        // that could change once the Z dependency is dropped ...
        LString comment = "//"_s;
        XString line = line_.xislice_h(std::search(line_.begin(), line_.end(), comment.begin(), comment.end())).rstrip();
        if (!line)
            continue;
        // "%m[A-Za-z0-9_] %i %i"

        // TODO promote either qsplit() or asplit()
        auto _it = std::find(line.begin(), line.end(), ' ');
        auto name = line.xislice_h(_it);
        auto _rest = line.xislice_t(_it);
        while (_rest.startswith(' '))
            _rest = _rest.xslice_t(1);
        auto _it2 = std::find(_rest.begin(), _rest.end(), ' ');
        auto val_ = _rest.xislice_h(_it2);
        auto type_ = _rest.xislice_t(_it2);
        while (type_.startswith(' '))
            type_ = type_.xslice_t(1);
        // yes, the above actually DTRT even for underlength input

        int val;
        int type = 0;
        // Note for future archeaologists: this code is indented correctly
        if (std::find_if_not(name.begin(), name.end(),
                    [](char c)
                    {
                        return ('0' <= c && c <= '9')
                            || ('A' <= c && c <= 'Z')
                            || ('a' <= c && c <= 'z')
                            || (c == '_');
                    }) != name.end()
                || !extract(val_, &val)
                || (!extract(type_, &type) && type_))
        {
            PRINTF("Bad const line: %s\n"_fmt, line_);
            rv = false;
            continue;
        }
        P<str_data_t> n = add_strp(name);
        n->type = type ? StringCode::PARAM : StringCode::INT;
        n->val = val;
    }
    return rv;
}

/*==========================================
 * マップ変数の変更
 *------------------------------------------
 */
void mapreg_setreg(SIR reg, int val)
{
    mapreg_db.put(reg, val);

    mapreg_dirty = 1;
}

/*==========================================
 * 文字列型マップ変数の変更
 *------------------------------------------
 */
void mapreg_setregstr(SIR reg, XString str)
{
    if (!str)
        mapregstr_db.erase(reg);
    else
        mapregstr_db.insert(reg, str);

    mapreg_dirty = 1;
}

/*==========================================
 * 永続的マップ変数の読み込み
 *------------------------------------------
 */
static
void script_load_mapreg(void)
{
    io::ReadFile in(mapreg_txt);

    if (!in.is_open())
        return;

    AString line;
    while (in.getline(line))
    {
        XString buf1, buf2;
        int index = 0;
        if (extract(line,
                    record<'\t'>(
                        record<','>(&buf1, &index),
                        &buf2))
            || extract(line,
                    record<'\t'>(
                        record<','>(&buf1),
                        &buf2)))
        {
            int s = variable_names.intern(buf1);
            SIR key = SIR::from(s, index);
            if (buf1.back() == '$')
            {
                mapregstr_db.insert(key, buf2);
            }
            else
            {
                int v;
                if (!extract(buf2, &v))
                    goto borken;
                mapreg_db.put(key, v);
            }
        }
        else
        {
        borken:
            PRINTF("%s: %s broken data !\n"_fmt, mapreg_txt, AString(buf1));
            continue;
        }
    }
    mapreg_dirty = 0;
}

/*==========================================
 * 永続的マップ変数の書き込み
 *------------------------------------------
 */
static
void script_save_mapreg_intsub(SIR key, int data, io::WriteFile& fp)
{
    int num = key.base(), i = key.index();
    ZString name = variable_names.outtern(num);
    if (name[1] != '@')
    {
        if (i == 0)
            FPRINTF(fp, "%s\t%d\n"_fmt, name, data);
        else
            FPRINTF(fp, "%s,%d\t%d\n"_fmt, name, i, data);
    }
}

static
void script_save_mapreg_strsub(SIR key, ZString data, io::WriteFile& fp)
{
    int num = key.base(), i = key.index();
    ZString name = variable_names.outtern(num);
    if (name[1] != '@')
    {
        if (i == 0)
            FPRINTF(fp, "%s\t%s\n"_fmt, name, data);
        else
            FPRINTF(fp, "%s,%d\t%s\n"_fmt, name, i, data);
    }
}

static
void script_save_mapreg(void)
{
    io::WriteLock fp(mapreg_txt);
    if (!fp.is_open())
        return;
    for (auto& pair : mapreg_db)
        script_save_mapreg_intsub(pair.first, pair.second, fp);
    for (auto& pair : mapregstr_db)
        script_save_mapreg_strsub(pair.first, pair.second, fp);
    mapreg_dirty = 0;
}

static
void script_autosave_mapreg(TimerData *, tick_t)
{
    if (mapreg_dirty)
        script_save_mapreg();
}

void do_final_script(void)
{
    if (mapreg_dirty >= 0)
        script_save_mapreg();

    mapreg_db.clear();
    mapregstr_db.clear();
    scriptlabel_db.clear();
    userfunc_db.clear();

    str_datam.clear();
}

/*==========================================
 * 初期化
 *------------------------------------------
 */
void do_init_script(void)
{
    script_load_mapreg();

    Timer(gettick() + MAPREG_AUTOSAVE_INTERVAL,
            script_autosave_mapreg,
            MAPREG_AUTOSAVE_INTERVAL
    ).detach();
}
} // namespace tmwa