
Added support for signed 64 bit integer value computation into the script engine. This is required because newer official features require support for bigger numbers inside the scripts. This also cleans up a lot of messy code and fixes a few issues like script stack corruptions. Thanks to @aleos89 and everyone who supported me with this.
359 lines
9.2 KiB
C++
359 lines
9.2 KiB
C++
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
|
|
// For more information, see LICENCE in the main folder
|
|
|
|
#include "mapreg.hpp"
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "../common/cbasetypes.hpp"
|
|
#include "../common/db.hpp"
|
|
#include "../common/ers.hpp"
|
|
#include "../common/malloc.hpp"
|
|
#include "../common/showmsg.hpp"
|
|
#include "../common/sql.hpp"
|
|
#include "../common/strlib.hpp"
|
|
#include "../common/timer.hpp"
|
|
|
|
#include "map.hpp" // mmysql_handle
|
|
#include "script.hpp"
|
|
|
|
static struct eri *mapreg_ers;
|
|
|
|
bool skip_insert = false;
|
|
|
|
static char mapreg_table[32] = "mapreg";
|
|
static bool mapreg_dirty = false; // Whether there are modified regs to be saved
|
|
struct reg_db regs;
|
|
|
|
#define MAPREG_AUTOSAVE_INTERVAL (300*1000)
|
|
|
|
|
|
/**
|
|
* Looks up the value of an integer variable using its uid.
|
|
*
|
|
* @param uid: variable's unique identifier.
|
|
* @return: variable's integer value
|
|
*/
|
|
int64 mapreg_readreg(int64 uid)
|
|
{
|
|
struct mapreg_save *m = (struct mapreg_save *)i64db_get(regs.vars, uid);
|
|
return m ? m->u.i : 0;
|
|
}
|
|
|
|
/**
|
|
* Looks up the value of a string variable using its uid.
|
|
*
|
|
* @param uid: variable's unique identifier
|
|
* @return: variable's string value
|
|
*/
|
|
char* mapreg_readregstr(int64 uid)
|
|
{
|
|
struct mapreg_save *m = (struct mapreg_save *)i64db_get(regs.vars, uid);
|
|
return m ? m->u.str : NULL;
|
|
}
|
|
|
|
/**
|
|
* Modifies the value of an integer variable.
|
|
*
|
|
* @param uid: variable's unique identifier
|
|
* @param val new value
|
|
* @return: true value was successfully set
|
|
*/
|
|
bool mapreg_setreg(int64 uid, int64 val)
|
|
{
|
|
struct mapreg_save *m;
|
|
int num = script_getvarid(uid);
|
|
uint32 i = script_getvaridx(uid);
|
|
const char* name = get_str(num);
|
|
|
|
if (val != 0) {
|
|
if ((m = static_cast<mapreg_save *>(i64db_get(regs.vars, uid)))) {
|
|
m->u.i = val;
|
|
if (name[1] != '@') {
|
|
m->save = true;
|
|
mapreg_dirty = true;
|
|
}
|
|
} else {
|
|
if (i)
|
|
script_array_update(®s, uid, false);
|
|
|
|
m = ers_alloc(mapreg_ers, struct mapreg_save);
|
|
|
|
m->u.i = val;
|
|
m->uid = uid;
|
|
m->save = false;
|
|
m->is_string = false;
|
|
|
|
if (name[1] != '@' && !skip_insert) {// write new variable to database
|
|
char esc_name[32 * 2 + 1];
|
|
Sql_EscapeStringLen(mmysql_handle, esc_name, name, strnlen(name, 32));
|
|
if (SQL_ERROR == Sql_Query(mmysql_handle, "INSERT INTO `%s`(`varname`,`index`,`value`) VALUES ('%s','%" PRIu32 "','%" PRId64 "')", mapreg_table, esc_name, i, val))
|
|
Sql_ShowDebug(mmysql_handle);
|
|
}
|
|
i64db_put(regs.vars, uid, m);
|
|
}
|
|
} else { // val == 0
|
|
if (i)
|
|
script_array_update(®s, uid, true);
|
|
if ((m = static_cast<mapreg_save *>(i64db_get(regs.vars, uid)))) {
|
|
ers_free(mapreg_ers, m);
|
|
}
|
|
i64db_remove(regs.vars, uid);
|
|
|
|
if (name[1] != '@') {// Remove from database because it is unused.
|
|
char esc_name[32 * 2 + 1];
|
|
Sql_EscapeStringLen(mmysql_handle, esc_name, name, strnlen(name, 32));
|
|
if (SQL_ERROR == Sql_Query(mmysql_handle, "DELETE FROM `%s` WHERE `varname`='%s' AND `index`='%" PRIu32 "'", mapreg_table, esc_name, i))
|
|
Sql_ShowDebug(mmysql_handle);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Modifies the value of a string variable.
|
|
*
|
|
* @param uid: variable's unique identifier
|
|
* @param str: new value
|
|
* @return: true value was successfully set
|
|
*/
|
|
bool mapreg_setregstr(int64 uid, const char* str)
|
|
{
|
|
struct mapreg_save *m;
|
|
int num = script_getvarid(uid);
|
|
uint32 i = script_getvaridx(uid);
|
|
const char* name = get_str(num);
|
|
|
|
if (str == NULL || *str == 0) {
|
|
if (i)
|
|
script_array_update(®s, uid, true);
|
|
if (name[1] != '@') {
|
|
char esc_name[32 * 2 + 1];
|
|
Sql_EscapeStringLen(mmysql_handle, esc_name, name, strnlen(name, 32));
|
|
if (SQL_ERROR == Sql_Query(mmysql_handle, "DELETE FROM `%s` WHERE `varname`='%s' AND `index`='%" PRIu32 "'", mapreg_table, esc_name, i))
|
|
Sql_ShowDebug(mmysql_handle);
|
|
}
|
|
if ((m = static_cast<mapreg_save *>(i64db_get(regs.vars, uid)))) {
|
|
if (m->u.str != NULL)
|
|
aFree(m->u.str);
|
|
ers_free(mapreg_ers, m);
|
|
}
|
|
i64db_remove(regs.vars, uid);
|
|
} else {
|
|
if ((m = static_cast<mapreg_save *>(i64db_get(regs.vars, uid)))) {
|
|
if (m->u.str != NULL)
|
|
aFree(m->u.str);
|
|
m->u.str = aStrdup(str);
|
|
if (name[1] != '@') {
|
|
mapreg_dirty = true;
|
|
m->save = true;
|
|
}
|
|
} else {
|
|
if (i)
|
|
script_array_update(®s, uid, false);
|
|
|
|
m = ers_alloc(mapreg_ers, struct mapreg_save);
|
|
|
|
m->uid = uid;
|
|
m->u.str = aStrdup(str);
|
|
m->save = false;
|
|
m->is_string = true;
|
|
|
|
if (name[1] != '@' && !skip_insert) { //put returned null, so we must insert.
|
|
char esc_name[32 * 2 + 1];
|
|
char esc_str[255 * 2 + 1];
|
|
Sql_EscapeStringLen(mmysql_handle, esc_name, name, strnlen(name, 32));
|
|
Sql_EscapeStringLen(mmysql_handle, esc_str, str, strnlen(str, 255));
|
|
if (SQL_ERROR == Sql_Query(mmysql_handle, "INSERT INTO `%s`(`varname`,`index`,`value`) VALUES ('%s','%" PRIu32 "','%s')", mapreg_table, esc_name, i, esc_str))
|
|
Sql_ShowDebug(mmysql_handle);
|
|
}
|
|
i64db_put(regs.vars, uid, m);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Loads permanent variables from database.
|
|
*/
|
|
static void script_load_mapreg(void)
|
|
{
|
|
/*
|
|
0 1 2
|
|
+-------------------------+
|
|
| varname | index | value |
|
|
+-------------------------+
|
|
*/
|
|
SqlStmt* stmt = SqlStmt_Malloc(mmysql_handle);
|
|
char varname[32+1];
|
|
uint32 index;
|
|
char value[255+1];
|
|
uint32 length;
|
|
|
|
if ( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `varname`, `index`, `value` FROM `%s`", mapreg_table)
|
|
|| SQL_ERROR == SqlStmt_Execute(stmt)
|
|
) {
|
|
SqlStmt_ShowDebug(stmt);
|
|
SqlStmt_Free(stmt);
|
|
return;
|
|
}
|
|
|
|
skip_insert = true;
|
|
|
|
SqlStmt_BindColumn(stmt, 0, SQLDT_STRING, &varname[0], sizeof(varname), &length, NULL);
|
|
SqlStmt_BindColumn(stmt, 1, SQLDT_UINT32, &index, 0, NULL, NULL);
|
|
SqlStmt_BindColumn(stmt, 2, SQLDT_STRING, &value[0], sizeof(value), NULL, NULL);
|
|
|
|
while ( SQL_SUCCESS == SqlStmt_NextRow(stmt) ) {
|
|
int s = add_str(varname);
|
|
int64 uid = reference_uid(s, index);
|
|
|
|
if( i64db_exists(regs.vars, uid) ) {
|
|
ShowWarning("load_mapreg: duplicate! '%s' => '%s' skipping...\n",varname,value);
|
|
continue;
|
|
}
|
|
if( varname[length-1] == '$' ) {
|
|
mapreg_setregstr(uid, value);
|
|
} else {
|
|
mapreg_setreg(uid, strtoll(value,NULL,10));
|
|
}
|
|
}
|
|
|
|
SqlStmt_Free(stmt);
|
|
|
|
skip_insert = false;
|
|
mapreg_dirty = false;
|
|
}
|
|
|
|
/**
|
|
* Saves permanent variables to database.
|
|
*/
|
|
static void script_save_mapreg(void)
|
|
{
|
|
if (mapreg_dirty) {
|
|
DBIterator *iter = db_iterator(regs.vars);
|
|
struct mapreg_save *m;
|
|
for (m = static_cast<mapreg_save *>(dbi_first(iter)); dbi_exists(iter); m = static_cast<mapreg_save *>(dbi_next(iter))) {
|
|
if (m->save) {
|
|
int num = script_getvarid(m->uid);
|
|
uint32 i = script_getvaridx(m->uid);
|
|
const char* name = get_str(num);
|
|
if (!m->is_string) {
|
|
char esc_name[32 * 2 + 1];
|
|
Sql_EscapeStringLen(mmysql_handle, esc_name, name, strnlen(name, 32));
|
|
if (SQL_ERROR == Sql_Query(mmysql_handle, "UPDATE `%s` SET `value`='%" PRId64 "' WHERE `varname`='%s' AND `index`='%" PRIu32 "' LIMIT 1", mapreg_table, m->u.i, esc_name, i))
|
|
Sql_ShowDebug(mmysql_handle);
|
|
} else {
|
|
char esc_str[2 * 255 + 1];
|
|
char esc_name[32 * 2 + 1];
|
|
Sql_EscapeStringLen(mmysql_handle, esc_name, name, strnlen(name, 32));
|
|
Sql_EscapeStringLen(mmysql_handle, esc_str, m->u.str, safestrnlen(m->u.str, 255));
|
|
if (SQL_ERROR == Sql_Query(mmysql_handle, "UPDATE `%s` SET `value`='%s' WHERE `varname`='%s' AND `index`='%" PRIu32 "' LIMIT 1", mapreg_table, esc_str, esc_name, i))
|
|
Sql_ShowDebug(mmysql_handle);
|
|
}
|
|
m->save = false;
|
|
}
|
|
}
|
|
dbi_destroy(iter);
|
|
mapreg_dirty = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Timer event to auto-save permanent variables.
|
|
*/
|
|
static TIMER_FUNC(script_autosave_mapreg){
|
|
script_save_mapreg();
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Destroys a mapreg_save structure, freeing the contained string, if any.
|
|
*
|
|
* @see DBApply
|
|
*/
|
|
int mapreg_destroyreg(DBKey key, DBData *data, va_list ap)
|
|
{
|
|
struct mapreg_save *m = NULL;
|
|
|
|
if (data->type != DB_DATA_PTR) // Sanity check
|
|
return 0;
|
|
|
|
m = static_cast<mapreg_save *>(db_data2ptr(data));
|
|
|
|
if (m->is_string) {
|
|
if (m->u.str)
|
|
aFree(m->u.str);
|
|
}
|
|
ers_free(mapreg_ers, m);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Reloads mapregs, saving to database beforehand.
|
|
*
|
|
* This has the effect of clearing the temporary variables, and
|
|
* reloading the permanent ones.
|
|
*/
|
|
void mapreg_reload(void)
|
|
{
|
|
script_save_mapreg();
|
|
|
|
regs.vars->clear(regs.vars, mapreg_destroyreg);
|
|
|
|
if (regs.arrays) {
|
|
regs.arrays->destroy(regs.arrays, script_free_array_db);
|
|
regs.arrays = NULL;
|
|
}
|
|
|
|
script_load_mapreg();
|
|
}
|
|
|
|
/**
|
|
* Finalizer.
|
|
*/
|
|
void mapreg_final(void)
|
|
{
|
|
script_save_mapreg();
|
|
|
|
regs.vars->destroy(regs.vars, mapreg_destroyreg);
|
|
|
|
ers_destroy(mapreg_ers);
|
|
|
|
if (regs.arrays)
|
|
regs.arrays->destroy(regs.arrays, script_free_array_db);
|
|
}
|
|
|
|
/**
|
|
* Initializer.
|
|
*/
|
|
void mapreg_init(void)
|
|
{
|
|
regs.vars = i64db_alloc(DB_OPT_BASE);
|
|
mapreg_ers = ers_new(sizeof(struct mapreg_save), "mapreg.cpp:mapreg_ers", ERS_OPT_CLEAN);
|
|
|
|
skip_insert = false;
|
|
regs.arrays = NULL;
|
|
|
|
script_load_mapreg();
|
|
|
|
add_timer_func_list(script_autosave_mapreg, "script_autosave_mapreg");
|
|
add_timer_interval(gettick() + MAPREG_AUTOSAVE_INTERVAL, script_autosave_mapreg, 0, 0, MAPREG_AUTOSAVE_INTERVAL);
|
|
}
|
|
|
|
/**
|
|
* Loads the mapreg configuration file.
|
|
*/
|
|
bool mapreg_config_read(const char* w1, const char* w2)
|
|
{
|
|
if(!strcmpi(w1, "mapreg_table"))
|
|
safestrncpy(mapreg_table, w2, sizeof(mapreg_table));
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|