// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL // For more information, see LICENCE in the main folder #include "mapreg.hpp" #include #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 */ int 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, int val) { struct mapreg_save *m; int num = script_getvarid(uid); unsigned int i = script_getvaridx(uid); const char* name = get_str(num); if (val != 0) { if ((m = static_cast(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','%d','%d')", 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(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`='%d'", 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); unsigned int 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`='%d'", mapreg_table, esc_name, i)) Sql_ShowDebug(mmysql_handle); } if ((m = static_cast(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(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','%d','%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]; int 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_INT, &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); int i = index; if( i64db_exists(regs.vars, reference_uid(s, i)) ) { ShowWarning("load_mapreg: duplicate! '%s' => '%s' skipping...\n",varname,value); continue; } if( varname[length-1] == '$' ) { mapreg_setregstr(reference_uid(s, i), value); } else { mapreg_setreg(reference_uid(s, i), atoi(value)); } } 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(dbi_first(iter)); dbi_exists(iter); m = static_cast(dbi_next(iter))) { if (m->save) { int num = script_getvarid(m->uid); int 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`='%d' WHERE `varname`='%s' AND `index`='%d' 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`='%d' 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(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.c: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; }