From 288490094a7fe9167747dc78d416940759a31197 Mon Sep 17 00:00:00 2001 From: FlavioJS Date: Tue, 5 Dec 2006 13:23:07 +0000 Subject: - Massive EOL normalization & 'svn:eol-style native' flag setting for all txt/conf/h/c files. git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@9410 54d463be-8e91-2dee-dedb-b68131a5f0ec --- src/char/char.c | 8886 +++++++++++++++++------------------ src/char/char.h | 116 +- src/char/int_guild.c | 3130 ++++++------ src/char/int_guild.h | 44 +- src/char/int_homun.c | 742 +-- src/char/int_homun.h | 32 +- src/char/int_party.c | 1494 +++--- src/char/int_party.h | 40 +- src/char/int_pet.c | 844 ++-- src/char/int_pet.h | 36 +- src/char/int_status.c | 374 +- src/char/int_status.h | 48 +- src/char/int_storage.c | 956 ++-- src/char/int_storage.h | 44 +- src/char/inter.c | 1384 +++--- src/char/inter.h | 56 +- src/char_sql/char.c | 8870 +++++++++++++++++----------------- src/char_sql/char.h | 220 +- src/char_sql/int_guild.c | 4212 ++++++++--------- src/char_sql/int_guild.h | 70 +- src/char_sql/int_homun.c | 592 +-- src/char_sql/int_homun.h | 28 +- src/char_sql/int_party.c | 1874 ++++---- src/char_sql/int_party.h | 60 +- src/char_sql/int_pet.c | 744 +-- src/char_sql/int_pet.h | 36 +- src/char_sql/int_storage.c | 758 +-- src/char_sql/int_storage.h | 34 +- src/char_sql/inter.c | 1644 +++---- src/char_sql/inter.h | 112 +- src/char_sql/itemdb.c | 444 +- src/char_sql/itemdb.h | 86 +- src/common/core.c | 606 +-- src/common/core.h | 44 +- src/common/db.c | 4896 +++++++++---------- src/common/db.h | 1496 +++--- src/common/ers.h | 386 +- src/common/graph.c | 636 +-- src/common/graph.h | 54 +- src/common/grfio.c | 2062 ++++---- src/common/grfio.h | 44 +- src/common/lock.c | 142 +- src/common/lock.h | 22 +- src/common/malloc.c | 1466 +++--- src/common/malloc.h | 356 +- src/common/nullpo.c | 188 +- src/common/nullpo.h | 474 +- src/common/plugin.h | 80 +- src/common/plugins.c | 734 +-- src/common/plugins.h | 122 +- src/common/showmsg.c | 1652 +++---- src/common/showmsg.h | 192 +- src/common/strlib.h | 48 +- src/common/timer.c | 872 ++-- src/common/timer.h | 120 +- src/common/utils.c | 768 +-- src/common/utils.h | 104 +- src/common/version.h | 60 +- src/ladmin/ladmin.c | 8872 +++++++++++++++++----------------- src/ladmin/ladmin.h | 26 +- src/ladmin/md5calc.c | 478 +- src/ladmin/md5calc.h | 20 +- src/login/login.c | 8396 ++++++++++++++++----------------- src/login/md5calc.c | 472 +- src/login/md5calc.h | 14 +- src/login_sql/login.c | 4796 +++++++++---------- src/login_sql/login.h | 114 +- src/login_sql/md5calc.c | 478 +- src/login_sql/md5calc.h | 20 +- src/map/atcommand.h | 670 +-- src/map/battle.h | 910 ++-- src/map/charcommand.c | 3692 +++++++-------- src/map/charcommand.h | 148 +- src/map/charsave.c | 1046 ++--- src/map/charsave.h | 42 +- src/map/chat.c | 780 +-- src/map/chat.h | 44 +- src/map/chrif.c | 3272 ++++++------- src/map/chrif.h | 116 +- src/map/clif.h | 736 +-- src/map/date.c | 144 +- src/map/date.h | 34 +- src/map/guild.c | 4034 ++++++++-------- src/map/guild.h | 190 +- src/map/intif.h | 144 +- src/map/irc.c | 1086 ++--- src/map/irc.h | 110 +- src/map/itemdb.c | 2554 +++++----- src/map/itemdb.h | 296 +- src/map/log.c | 1064 ++--- src/map/log.h | 86 +- src/map/mail.c | 712 +-- src/map/mail.h | 24 +- src/map/map.h | 2982 ++++++------ src/map/mercenary.c | 1914 ++++---- src/map/mercenary.h | 158 +- src/map/mob.h | 414 +- src/map/npc.c | 6168 ++++++++++++------------ src/map/npc.h | 180 +- src/map/npc_chat.c | 1034 ++-- src/map/party.c | 1804 +++---- src/map/party.h | 98 +- src/map/pc.h | 636 +-- src/map/pcre.h | 516 +- src/map/pet.c | 2800 +++++------ src/map/pet.h | 136 +- src/map/script.h | 182 +- src/map/skill.h | 1916 ++++---- src/map/status.h | 1276 ++--- src/map/storage.c | 1522 +++--- src/map/storage.h | 90 +- src/map/trade.c | 1106 ++--- src/map/trade.h | 30 +- src/map/vending.c | 534 +-- src/map/vending.h | 28 +- src/plugins/gui.c | 202 +- src/plugins/gui.txt | 28 +- src/plugins/httpd.c | 1502 +++--- src/plugins/httpd.h | 214 +- src/plugins/httpd.txt | 38 +- src/plugins/pid.c | 108 +- src/plugins/sample.c | 154 +- src/plugins/sig.c | 422 +- src/plugins/upnp.txt | 60 +- src/tool/adduser.c | 200 +- src/tool/convert.c | 598 +-- src/txt-converter/char-converter.c | 562 +-- src/txt-converter/login-converter.c | 456 +- src/webserver/doc/API.txt | 100 +- src/webserver/generate.c | 76 +- src/webserver/htmlstyle.c | 102 +- src/webserver/logs.c | 16 +- src/webserver/main.c | 284 +- src/webserver/pages/about.c | 12 +- src/webserver/pages/notdone.c | 10 +- src/webserver/pages/sample.c | 48 +- src/webserver/parse.c | 270 +- src/zlib/crypt.h | 264 +- src/zlib/ioapi.c | 354 +- src/zlib/ioapi.h | 150 +- src/zlib/iowin32.c | 540 +-- src/zlib/iowin32.h | 42 +- src/zlib/unzip.c | 3204 ++++++------- src/zlib/unzip.h | 708 +-- src/zlib/zconf.h | 664 +-- src/zlib/zlib.h | 2714 +++++------ 146 files changed, 70440 insertions(+), 70440 deletions(-) (limited to 'src') diff --git a/src/char/char.c b/src/char/char.c index 6c234984a..1978b225b 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -1,4443 +1,4443 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../common/strlib.h" -#include "../common/core.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/mmo.h" -#include "../common/db.h" -#include "../common/version.h" -#include "../common/lock.h" -#include "../common/showmsg.h" -#include "../common/malloc.h" - -#include "char.h" -#include "inter.h" -#include "int_pet.h" -#include "int_homun.h" -#include "int_guild.h" -#include "int_party.h" -#include "int_storage.h" -#ifdef ENABLE_SC_SAVING -#include "int_status.h" -#endif - -#ifndef TXT_SQL_CONVERT -struct mmo_map_server{ - long ip; - short port; - int users; - unsigned short map[MAX_MAP_PER_SERVER]; -} server[MAX_MAP_SERVERS]; -int server_fd[MAX_MAP_SERVERS]; - -int login_fd, char_fd; -char userid[24]; -char passwd[24]; -char server_name[20]; -char wisp_server_name[NAME_LENGTH] = "Server"; -char login_ip_str[128]; -in_addr_t login_ip; -int login_port = 6900; -char char_ip_str[128]; -in_addr_t char_ip; -char bind_ip_str[128]; -in_addr_t bind_ip; -int char_port = 6121; -int char_maintenance; -int char_new; -int char_new_display; -int email_creation = 0; // disabled by default -#endif -char char_txt[1024]="save/athena.txt"; -char backup_txt[1024]="save/backup.txt"; //By zanetheinsane -char friends_txt[1024]="save/friends.txt"; // davidsiaw -#ifndef TXT_SQL_CONVERT -char backup_txt_flag = 0; // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. => option By [Yor] -char unknown_char_name[1024] = "Unknown"; -char char_log_filename[1024] = "log/char.log"; -char db_path[1024]="db"; - -// Advanced subnet check [LuzZza] -struct _subnet { - long subnet; - long mask; - long char_ip; - long map_ip; -} subnet[16]; - -int subnet_count = 0; - -int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor] -int char_name_option = 0; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor] -//The following are characters that are trimmed regardless because they cause confusion and problems on the servers. [Skotlex] -#define TRIM_CHARS "\032\t\x0A\x0D " -char char_name_letters[1024] = ""; // list of letters/symbols authorised (or not) in a character name. by [Yor] - -int log_char = 1; // loggin char or not [devil] -int log_inter = 1; // loggin inter or not [devil] - -struct char_session_data{ - int account_id, login_id1, login_id2, sex; - int found_char[9]; - char email[40]; // e-mail (default: a@a.com) by [Yor] - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) -}; - -#define AUTH_FIFO_SIZE 256 -struct { - int account_id, char_id, login_id1, login_id2, ip, char_pos, delflag, sex; - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) -} auth_fifo[AUTH_FIFO_SIZE]; -int auth_fifo_pos = 0; - -int check_ip_flag = 1; // It's to check IP of a player between char-server and other servers (part of anti-hacking system) -static int online_check = 1; //If one, it won't let players connect when their account is already registered online and will send the relevant map server a kick user request. [Skotlex] - -int char_id_count = START_CHAR_NUM; -struct character_data *char_dat; - -int char_num, char_max; -int max_connect_user = 0; -int gm_allow_level = 99; -int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; -int save_log = 1; -int start_zeny = 500; -int start_weapon = 1201; -int start_armor = 2301; -int guild_exp_rate = 100; - -//Custom limits for the fame lists. [Skotlex] -int fame_list_size_chemist = MAX_FAME_LIST; -int fame_list_size_smith = MAX_FAME_LIST; -int fame_list_size_taekwon = MAX_FAME_LIST; - -// Char-server-side stored fame lists [DracoRPG] -struct fame_list smith_fame_list[MAX_FAME_LIST]; -struct fame_list chemist_fame_list[MAX_FAME_LIST]; -struct fame_list taekwon_fame_list[MAX_FAME_LIST]; - -// Initial position (it's possible to set it in conf file) -struct point start_point = { 0, 53, 111}; - -struct gm_account *gm_account = NULL; -int GM_num = 0; - -// online players by [Yor] -char online_txt_filename[1024] = "online.txt"; -char online_html_filename[1024] = "online.html"; -int online_sorting_option = 0; // sorting option to display online players in online files -int online_display_option = 1; // display options: to know which columns must be displayed -int online_refresh_html = 20; // refresh time (in sec) of the html file in the explorer -int online_gm_display_min_level = 20; // minimum GM level to display 'GM' when we want to display it - -//These are used to aid the map server in identifying valid clients. [Skotlex] -static int max_account_id = DEFAULT_MAX_ACCOUNT_ID, max_char_id = DEFAULT_MAX_CHAR_ID; - -struct online_char_data { - int account_id; - int char_id; - short server; - unsigned waiting_disconnect :1; -}; - -struct dbt *online_char_db; //Holds all online characters. - -time_t update_online; // to update online files when we receiving information from a server (not less than 8 seconds) - -int console = 0; - -//------------------------------ -// Writing function of logs file -//------------------------------ -int char_log(char *fmt, ...) { - if(log_char) - { - FILE *logfp; - va_list ap; - time_t raw_time; - char tmpstr[2048]; - - va_start(ap, fmt); - - logfp = fopen(char_log_filename, "a"); - if (logfp) { - if (fmt[0] == '\0') // jump a line if no message - fprintf(logfp, RETCODE); - else { - time(&raw_time); - strftime(tmpstr, 24, "%d-%m-%Y %H:%M:%S", localtime(&raw_time)); - sprintf(tmpstr + 19, ": %s", fmt); - vfprintf(logfp, tmpstr, ap); - } - fclose(logfp); - } - va_end(ap); - } - return 0; -} - -//---------------------------------------------------------------------- -// Determine if an account (id) is a GM account -// and returns its level (or 0 if it isn't a GM account or if not found) -//---------------------------------------------------------------------- -int isGM(int account_id) { - int i; - - for(i = 0; i < GM_num; i++) - if (gm_account[i].account_id == account_id) - return gm_account[i].level; - return 0; -} - -//Search character data from the aid/cid givem -struct mmo_charstatus* search_character(int aid, int cid) { - int i; - for (i = 0; i < char_num; i++) { - if (char_dat[i].status.char_id == cid && char_dat[i].status.account_id == aid) - return &char_dat[i].status; - } - return NULL; -} - -struct mmo_charstatus* search_character_byname(char* character_name) -{ - int i = search_character_index(character_name); - if (i == -1) return NULL; - return &char_dat[i].status; -} - -//---------------------------------------------- -// Search an character id -// (return character index or -1 (if not found)) -// If exact character name is not found, -// the function checks without case sensitive -// and returns index if only 1 character is found -// and similar to the searched name. -//---------------------------------------------- -int search_character_index(char* character_name) { - int i, quantity, index; - - quantity = 0; - index = -1; - for(i = 0; i < char_num; i++) { - // Without case sensitive check (increase the number of similar character names found) - if (stricmp(char_dat[i].status.name, character_name) == 0) { - // Strict comparison (if found, we finish the function immediatly with correct value) - if (strcmp(char_dat[i].status.name, character_name) == 0) - return i; - quantity++; - index = i; - } - } - // Here, the exact character name is not found - // We return the found index of a similar account ONLY if there is 1 similar character - if (quantity == 1) - return index; - - // Exact character name is not found and 0 or more than 1 similar characters have been found ==> we say not found - return -1; -} - -//------------------------------------- -// Return character name with the index -//------------------------------------- -char * search_character_name(int index) { - - if (index >= 0 && index < char_num) - return char_dat[index].status.name; - - return unknown_char_name; -} - -// Searches if the given character is online, and returns the fd of the -// map-server it is connected to. -int search_character_online(int aid, int cid) -{ - //Look for online character. - struct online_char_data* character; - character = idb_get(online_char_db, aid); - if(character && - character->char_id == cid && - character->server > -1) - return server_fd[character->server]; - return -1; -} -static void * create_online_char_data(DBKey key, va_list args) { - struct online_char_data* character; - character = aCalloc(1, sizeof(struct online_char_data)); - character->account_id = key.i; - character->char_id = -1; - character->server = -1; - return character; -} - -static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data); - -//------------------------------------------------- -// Set Character online/offline [Wizputer] -//------------------------------------------------- - -void set_char_online(int map_id, int char_id, int account_id) { - struct online_char_data* character; - - if ( char_id != 99 && (max_account_id < account_id || max_char_id < char_id)) - { //Notify map-server of the new max IDs [Skotlex] - if (account_id > max_account_id) - max_account_id = account_id; - if (char_id > max_char_id) - max_char_id = char_id; - mapif_send_maxid(max_account_id, max_char_id); - } - character = idb_ensure(online_char_db, account_id, create_online_char_data); - if (online_check && character->char_id != -1 && character->server > -1 && character->server != map_id) - { - //char == 99 <- Character logging in, so someone has logged in while one - //char is still on map-server, so kick him out, but don't print "error" - //as this is normal behaviour. [Skotlex] - if (char_id != 99) - ShowNotice("set_char_online: Character %d:%d marked in map server %d, but map server %d claims to have (%d:%d) online!\n", - character->account_id, character->char_id, character->server, map_id, account_id, char_id); - mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); - } - character->waiting_disconnect = 0; - character->char_id = (char_id==99)?-1:char_id; - character->server = (char_id==99)?-1:map_id; - - if (login_fd <= 0 || session[login_fd]->eof) - return; - WFIFOHEAD(login_fd, 6); - WFIFOW(login_fd,0) = 0x272b; - WFIFOL(login_fd,2) = account_id; - WFIFOSET(login_fd,6); - - //printf ("set online\n"); -} -void set_char_offline(int char_id, int account_id) { - struct online_char_data* character; - - if ((character = idb_get(online_char_db, account_id)) != NULL) - { //We don't free yet to avoid aCalloc/aFree spamming during char change. [Skotlex] - character->char_id = -1; - character->server = -1; - character->waiting_disconnect = 0; - } - if (login_fd <= 0 || session[login_fd]->eof) - return; - WFIFOHEAD(login_fd, 6); - WFIFOW(login_fd,0) = 0x272c; - WFIFOL(login_fd,2) = account_id; - WFIFOSET(login_fd,6); - -} - -static int char_db_setoffline(DBKey key, void* data, va_list ap) { - struct online_char_data* character = (struct online_char_data*)data; - int server = va_arg(ap, int); - if (server == -1) { - character->char_id = -1; - character->server = -1; - character->waiting_disconnect = 0; - } else if (character->server == server) - character->server = -2; //In some map server that we aren't connected to. - return 0; -} - -static int char_db_kickoffline(DBKey key, void* data, va_list ap) { - struct online_char_data* character = (struct online_char_data*)data; - int server = va_arg(ap, int); - - if (server > -1 && character->server != server) - return 0; - - //Kick out any connected characters, and set them offline as appropiate. - if (character->server > -1) - mapif_disconnectplayer(server_fd[character->server], - character->account_id, character->char_id, 1); - else if (!character->waiting_disconnect) - set_char_offline(character->char_id, character->account_id); - else return 0; - return 1; -} - -void set_all_offline(int id) { - if (id < 0) - ShowNotice("Sending all users offline.\n"); - else - ShowNotice("Sending users of map-server %d offline.\n", id); - online_char_db->foreach(online_char_db,char_db_kickoffline,id); - - if (id >= 0 || login_fd <= 0 || session[login_fd]->eof) - return; - //Tell login-server to also mark all our characters as offline. - WFIFOHEAD(login_fd, 2); - WFIFOW(login_fd,0) = 0x2737; - WFIFOSET(login_fd,2); -} - -/*--------------------------------------------------- - Make a data line for friends list - --------------------------------------------------*/ - -int mmo_friends_list_data_str(char *str, struct mmo_charstatus *p) { - int i; - char *str_p = str; - str_p += sprintf(str_p, "%d", p->char_id); - - for (i=0;ifriends[i].account_id > 0 && p->friends[i].char_id > 0 && p->friends[i].name[0]) - str_p += sprintf(str_p, ",%d,%d,%s", p->friends[i].account_id, p->friends[i].char_id, p->friends[i].name); - } - - str_p += '\0'; - - return 0; -} - -//------------------------------------------------- -// Function to create the character line (for save) -//------------------------------------------------- -int mmo_char_tostr(char *str, struct mmo_charstatus *p, struct global_reg *reg, int reg_num) { - int i,j; - char *str_p = str; - - /* We shouldn't need this anymore... [Skotlex] - // on multi-map server, sometimes it's posssible that last_point become void. (reason???) We check that to not lost character at restart. - if (!p->last_point.map) { - p->last_point.map = mapindex_name2id(MAP_PRONTERA); - p->last_point.x = 273; - p->last_point.y = 354; - } - */ - str_p += sprintf(str_p, - "%d\t%d,%d\t%s\t%d,%d,%d\t%u,%u,%d" //Up to Zeny field - "\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" //Up to Skill Point - "\t%d,%d,%d\t%d,%d,%d,%d" //Up to hom id - "\t%d,%d,%d\t%d,%d,%d,%d,%d" //Up to head bottom - "\t%d,%d,%d\t%d,%d,%d" //last point + save point - ",%d,%d,%d,%d,%d\t", //Family info - p->char_id, p->account_id, p->char_num, p->name, // - p->class_, p->base_level, p->job_level, - p->base_exp, p->job_exp, p->zeny, - p->hp, p->max_hp, p->sp, p->max_sp, - p->str, p->agi, p->vit, p->int_, p->dex, p->luk, - p->status_point, p->skill_point, - p->option, p->karma, p->manner, // - p->party_id, p->guild_id, p->pet_id, p->hom_id, - p->hair, p->hair_color, p->clothes_color, - p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom, - p->last_point.map, p->last_point.x, p->last_point.y, // - p->save_point.map, p->save_point.x, p->save_point.y, - p->partner_id,p->father,p->mother,p->child,p->fame); - for(i = 0; i < MAX_MEMOPOINTS; i++) - if (p->memo_point[i].map) { - str_p += sprintf(str_p, "%d,%d,%d ", p->memo_point[i].map, p->memo_point[i].x, p->memo_point[i].y); - } - *(str_p++) = '\t'; - - for(i = 0; i < MAX_INVENTORY; i++) - if (p->inventory[i].nameid) { - str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d", - p->inventory[i].id,p->inventory[i].nameid,p->inventory[i].amount,p->inventory[i].equip, - p->inventory[i].identify,p->inventory[i].refine,p->inventory[i].attribute); - for(j=0; jinventory[i].card[j]); - str_p += sprintf(str_p," "); - } - *(str_p++) = '\t'; - - for(i = 0; i < MAX_CART; i++) - if (p->cart[i].nameid) { - str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d", - p->cart[i].id,p->cart[i].nameid,p->cart[i].amount,p->cart[i].equip, - p->cart[i].identify,p->cart[i].refine,p->cart[i].attribute); - for(j=0; jcart[i].card[j]); - str_p += sprintf(str_p," "); - } - *(str_p++) = '\t'; - - for(i = 0; i < MAX_SKILL; i++) - if (p->skill[i].id && p->skill[i].flag != 1) { - str_p += sprintf(str_p, "%d,%d ", p->skill[i].id, (p->skill[i].flag == 0) ? p->skill[i].lv : p->skill[i].flag-2); - } - *(str_p++) = '\t'; - - for(i = 0; i < reg_num; i++) - if (reg[i].str[0]) - str_p += sprintf(str_p, "%s,%s ", reg[i].str, reg[i].value); - *(str_p++) = '\t'; - - *str_p = '\0'; - return 0; -} -#endif //TXT_SQL_CONVERT -//------------------------------------------------------------------------- -// Function to set the character from the line (at read of characters file) -//------------------------------------------------------------------------- -int mmo_char_fromstr(char *str, struct mmo_charstatus *p, struct global_reg *reg, int *reg_num) { - char tmp_str[3][128]; //To avoid deleting chars with too long names. - int tmp_int[256]; - unsigned int tmp_uint[2]; //To read exp.... - int next, len, i, j; - - // initilialise character - memset(p, '\0', sizeof(struct mmo_charstatus)); - -// Char structure of version 1500 (homun + mapindex maps) - if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" - "\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" - "\t%d,%d,%d\t%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], - &tmp_int[3], &tmp_int[4], &tmp_int[5], - &tmp_uint[0], &tmp_uint[1], &tmp_int[8], - &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], - &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], - &tmp_int[19], &tmp_int[20], - &tmp_int[21], &tmp_int[22], &tmp_int[23], // - &tmp_int[24], &tmp_int[25], &tmp_int[26], &tmp_int[44], - &tmp_int[27], &tmp_int[28], &tmp_int[29], - &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], - &tmp_int[45], &tmp_int[35], &tmp_int[36], - &tmp_int[46], &tmp_int[37], &tmp_int[38], &tmp_int[39], - &tmp_int[40], &tmp_int[41], &tmp_int[42], &tmp_int[43], &next) != 48) - { - tmp_int[44] = 0; //Hom ID. -// Char structure of version 1488 (fame field addition) - if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" - "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" - "\t%127[^,],%d,%d\t%127[^,],%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], - &tmp_int[3], &tmp_int[4], &tmp_int[5], - &tmp_uint[0], &tmp_uint[1], &tmp_int[8], - &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], - &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], - &tmp_int[19], &tmp_int[20], - &tmp_int[21], &tmp_int[22], &tmp_int[23], // - &tmp_int[24], &tmp_int[25], &tmp_int[26], - &tmp_int[27], &tmp_int[28], &tmp_int[29], - &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], - tmp_str[1], &tmp_int[35], &tmp_int[36], - tmp_str[2], &tmp_int[37], &tmp_int[38], &tmp_int[39], - &tmp_int[40], &tmp_int[41], &tmp_int[42], &tmp_int[43], &next) != 47) - { - tmp_int[43] = 0; //Fame -// Char structure of version 1363 (family data addition) - if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" - "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" - "\t%127[^,],%d,%d\t%127[^,],%d,%d,%d,%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], // - &tmp_int[3], &tmp_int[4], &tmp_int[5], - &tmp_uint[0], &tmp_uint[1], &tmp_int[8], - &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], - &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], - &tmp_int[19], &tmp_int[20], - &tmp_int[21], &tmp_int[22], &tmp_int[23], // - &tmp_int[24], &tmp_int[25], &tmp_int[26], - &tmp_int[27], &tmp_int[28], &tmp_int[29], - &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], - tmp_str[1], &tmp_int[35], &tmp_int[36], // - tmp_str[2], &tmp_int[37], &tmp_int[38], &tmp_int[39], - &tmp_int[40], &tmp_int[41], &tmp_int[42], &next) != 46) - { - tmp_int[40] = 0; // father - tmp_int[41] = 0; // mother - tmp_int[42] = 0; // child -// Char structure version 1008 (marriage partner addition) - if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" - "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" - "\t%127[^,],%d,%d\t%127[^,],%d,%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], // - &tmp_int[3], &tmp_int[4], &tmp_int[5], - &tmp_uint[0], &tmp_uint[1], &tmp_int[8], - &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], - &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], - &tmp_int[19], &tmp_int[20], - &tmp_int[21], &tmp_int[22], &tmp_int[23], // - &tmp_int[24], &tmp_int[25], &tmp_int[26], - &tmp_int[27], &tmp_int[28], &tmp_int[29], - &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], - tmp_str[1], &tmp_int[35], &tmp_int[36], // - tmp_str[2], &tmp_int[37], &tmp_int[38], &tmp_int[39], &next) != 43) - { - tmp_int[39] = 0; // partner id -// Char structure version 384 (pet addition) - if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" - "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" - "\t%127[^,],%d,%d\t%127[^,],%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], // - &tmp_int[3], &tmp_int[4], &tmp_int[5], - &tmp_uint[0], &tmp_uint[1], &tmp_int[8], - &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], - &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], - &tmp_int[19], &tmp_int[20], - &tmp_int[21], &tmp_int[22], &tmp_int[23], // - &tmp_int[24], &tmp_int[25], &tmp_int[26], - &tmp_int[27], &tmp_int[28], &tmp_int[29], - &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], - tmp_str[1], &tmp_int[35], &tmp_int[36], // - tmp_str[2], &tmp_int[37], &tmp_int[38], &next) != 42) - { - tmp_int[26] = 0; // pet id -// Char structure of a version 1 (original data structure) - if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" - "\t%d,%d,%d\t%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" - "\t%127[^,],%d,%d\t%127[^,],%d,%d%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], // - &tmp_int[3], &tmp_int[4], &tmp_int[5], - &tmp_uint[0], &tmp_uint[1], &tmp_int[8], - &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], - &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], - &tmp_int[19], &tmp_int[20], - &tmp_int[21], &tmp_int[22], &tmp_int[23], // - &tmp_int[24], &tmp_int[25], // - &tmp_int[27], &tmp_int[28], &tmp_int[29], - &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], - tmp_str[1], &tmp_int[35], &tmp_int[36], // - tmp_str[2], &tmp_int[37], &tmp_int[38], &next) != 41) - { - ShowError("Char-loading: Unrecognized character data version, info lost!\n"); - ShowDebug("Character info: %s\n", str); - return 0; - } - } // Char structure version 384 (pet addition) - } // Char structure version 1008 (marriage partner addition) - } // Char structure of version 1363 (family data addition) - } // Char structure of version 1488 (fame field addition) - //Convert save data from string to integer for older formats - tmp_int[45] = mapindex_name2id(tmp_str[1]); - tmp_int[46] = mapindex_name2id(tmp_str[2]); - } // Char structure of version 1500 (homun + mapindex maps) - - memcpy(p->name, tmp_str[0], NAME_LENGTH-1); //Overflow protection [Skotlex] - p->char_id = tmp_int[0]; - p->account_id = tmp_int[1]; - p->char_num = tmp_int[2]; - p->class_ = tmp_int[3]; -/* Unneeded unless you are running a real old character database now. - //Temporal fix until all chars are reverted from peco-flying-class to - //normal classes. [Skotlex] - switch (p->class_) { - case JOB_KNIGHT2: //Job_Knight2 - p->class_ = JOB_KNIGHT; - break; - case JOB_CRUSADER2: //Job_Crusader2 - p->class_ = JOB_CRUSADER; - break; - case JOB_LORD_KNIGHT2: //Job_Lord_Knight2 - p->class_ = JOB_LORD_KNIGHT; - break; - case JOB_PALADIN2: //Job_Paladin2 - p->class_ = JOB_PALADIN; - break; - case JOB_BABY_KNIGHT2: //Job_Baby_Knight2 - p->class_ = JOB_BABY_KNIGHT; - break; - case JOB_BABY_CRUSADER2: //Job_Baby_Crusader2 - p->class_ = JOB_BABY_CRUSADER; - break; - case JOB_STAR_GLADIATOR2: //Job_Star_Gladiator2 - p->class_ = JOB_STAR_GLADIATOR; - break; - } -*/ - p->base_level = tmp_int[4]; - p->job_level = tmp_int[5]; - p->base_exp = tmp_uint[0]; - p->job_exp = tmp_uint[1]; - p->zeny = tmp_int[8]; - p->hp = tmp_int[9]; - p->max_hp = tmp_int[10]; - p->sp = tmp_int[11]; - p->max_sp = tmp_int[12]; - p->str = tmp_int[13]; - p->agi = tmp_int[14]; - p->vit = tmp_int[15]; - p->int_ = tmp_int[16]; - p->dex = tmp_int[17]; - p->luk = tmp_int[18]; - p->status_point = tmp_int[19] > USHRT_MAX ? USHRT_MAX : tmp_int[19]; - p->skill_point = tmp_int[20] > USHRT_MAX ? USHRT_MAX : tmp_int[20]; - p->option = tmp_int[21]; - p->karma = tmp_int[22]; - p->manner = tmp_int[23]; - p->party_id = tmp_int[24]; - p->guild_id = tmp_int[25]; - p->pet_id = tmp_int[26]; - p->hair = tmp_int[27]; - p->hair_color = tmp_int[28]; - p->clothes_color = tmp_int[29]; - p->weapon = tmp_int[30]; - p->shield = tmp_int[31]; - p->head_top = tmp_int[32]; - p->head_mid = tmp_int[33]; - p->head_bottom = tmp_int[34]; - p->last_point.x = tmp_int[35]; - p->last_point.y = tmp_int[36]; - p->save_point.x = tmp_int[37]; - p->save_point.y = tmp_int[38]; - p->partner_id = tmp_int[39]; - p->father = tmp_int[40]; - p->mother = tmp_int[41]; - p->child = tmp_int[42]; - p->fame = tmp_int[43]; - p->hom_id = tmp_int[44]; - p->last_point.map = tmp_int[45]; - p->save_point.map = tmp_int[46]; - -#ifndef TXT_SQL_CONVERT - // Some checks - for(i = 0; i < char_num; i++) { - if (char_dat[i].status.char_id == p->char_id) { - ShowError(CL_RED"mmmo_auth_init: a character has an identical id to another.\n"); - ShowError(" character id #%d -> new character not readed.\n", p->char_id); - ShowError(" Character saved in log file."CL_RESET"\n"); - return -1; - } else if (strcmp(char_dat[i].status.name, p->name) == 0) { - ShowError(CL_RED"mmmo_auth_init: a character name already exists.\n"); - ShowError(" character name '%s' -> new character not read.\n", p->name); - ShowError(" Character saved in log file."CL_RESET"\n"); - return -2; - } - } - - if (strcmpi(wisp_server_name, p->name) == 0) { - ShowWarning("mmo_auth_init: ******WARNING: character name has wisp server name.\n"); - ShowWarning(" Character name '%s' = wisp server name '%s'.\n", p->name, wisp_server_name); - ShowWarning(" Character readed. Suggestion: change the wisp server name.\n"); - char_log("mmo_auth_init: ******WARNING: character name has wisp server name: Character name '%s' = wisp server name '%s'." RETCODE, - p->name, wisp_server_name); - } -#endif //TXT_SQL_CONVERT - if (str[next] == '\n' || str[next] == '\r') - return 1; // 新規データ - - next++; - - for(i = 0; str[next] && str[next] != '\t'; i++) { - //mapindex memo format - if (sscanf(str+next, "%d,%d,%d%n", &tmp_int[2], &tmp_int[0], &tmp_int[1], &len) != 3) - { //Old string-based memo format. - if (sscanf(str+next, "%[^,],%d,%d%n", tmp_str[0], &tmp_int[0], &tmp_int[1], &len) != 3) - return -3; - tmp_int[2] = mapindex_name2id(tmp_str[0]); - } - if (i < MAX_MEMOPOINTS) - { //Avoid overflowing (but we must also read through all saved memos) - p->memo_point[i].x = tmp_int[0]; - p->memo_point[i].y = tmp_int[1]; - p->memo_point[i].map = tmp_int[2]; - } - next += len; - if (str[next] == ' ') - next++; - } - - next++; - - for(i = 0; str[next] && str[next] != '\t'; i++) { - if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d%[0-9,-]%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str[0], &len) == 8) - { - p->inventory[i].id = tmp_int[0]; - p->inventory[i].nameid = tmp_int[1]; - p->inventory[i].amount = tmp_int[2]; - p->inventory[i].equip = tmp_int[3]; - p->inventory[i].identify = tmp_int[4]; - p->inventory[i].refine = tmp_int[5]; - p->inventory[i].attribute = tmp_int[6]; - - for(j = 0; j < MAX_SLOTS && tmp_str[0] && sscanf(tmp_str[0], ",%d%[0-9,-]",&tmp_int[0], tmp_str[0]) > 0; j++) - p->inventory[i].card[j] = tmp_int[0]; - - next += len; - if (str[next] == ' ') - next++; - } else // invalid structure - return -4; - } - next++; - - for(i = 0; str[next] && str[next] != '\t'; i++) { - if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d%[0-9,-]%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str[0], &len) == 8) - { - p->cart[i].id = tmp_int[0]; - p->cart[i].nameid = tmp_int[1]; - p->cart[i].amount = tmp_int[2]; - p->cart[i].equip = tmp_int[3]; - p->cart[i].identify = tmp_int[4]; - p->cart[i].refine = tmp_int[5]; - p->cart[i].attribute = tmp_int[6]; - - for(j = 0; j < MAX_SLOTS && tmp_str && sscanf(tmp_str[0], ",%d%[0-9,-]",&tmp_int[0], tmp_str[0]) > 0; j++) - p->cart[i].card[j] = tmp_int[0]; - - next += len; - if (str[next] == ' ') - next++; - } else // invalid structure - return -5; - } - - next++; - - for(i = 0; str[next] && str[next] != '\t'; i++) { - if (sscanf(str + next, "%d,%d%n", &tmp_int[0], &tmp_int[1], &len) != 2) - return -6; - p->skill[tmp_int[0]].id = tmp_int[0]; - p->skill[tmp_int[0]].lv = tmp_int[1]; - next += len; - if (str[next] == ' ') - next++; - } - - next++; - - for(i = 0; str[next] && str[next] != '\t' && str[next] != '\n' && str[next] != '\r'; i++) { // global_reg実装以前のathena.txt互換のため一応'\n'チェック - if (sscanf(str + next, "%[^,],%[^ ] %n", reg[i].str, reg[i].value, &len) != 2) { - // because some scripts are not correct, the str can be "". So, we must check that. - // If it's, we must not refuse the character, but just this REG value. - // Character line will have something like: nov_2nd_cos,9 ,9 nov_1_2_cos_c,1 (here, ,9 is not good) - if (str[next] == ',' && sscanf(str + next, ",%[^ ] %n", reg[i].value, &len) == 1) - i--; - else - return -7; - } - next += len; - if (str[next] == ' ') - next++; - } - *reg_num = i; - - return 1; -} -//--------------------------------- -// Function to read friend list -//--------------------------------- - -int parse_friend_txt(struct mmo_charstatus *p) -{ - char line[1024], temp[1024]; - int pos = 0, count = 0, next; - int i,len; - FILE *fp; - - // Open the file and look for the ID - fp = fopen(friends_txt, "r"); - - if(fp == NULL) - return -1; - - while(fgets(line, sizeof(line)-1, fp)) { - - if(line[0] == '/' && line[1] == '/') - continue; - if (sscanf(line, "%d%n",&i, &pos) < 1 || i != p->char_id) - continue; //Not this line... - //Read friends - len = strlen(line); - next = pos; - for (count = 0; next < len && count < MAX_FRIENDS; count++) - { //Read friends. - if (sscanf(line+next, ",%d,%d,%23[^,^\n]%n",&p->friends[count].account_id,&p->friends[count].char_id, p->friends[count].name, &pos) < 3) - { //Invalid friend? - memset(&p->friends[count], 0, sizeof(p->friends[count])); - break; - } - next+=pos; - //What IF the name contains a comma? while the next field is not a - //number, we assume it belongs to the current name. [Skotlex] - //NOTE: Of course, this will fail if someone sets their name to something like - //Bob,2005 but... meh, it's the problem of parsing a text file (encasing it in " - //won't do as quotes are also valid name chars!) - while(next < len && sscanf(line+next, ",%23[^,^\n]%n", temp, &pos) > 0) - { - if (atoi(temp)) //We read the next friend, just continue. - break; - //Append the name. - next+=pos; - i = strlen(p->friends[count].name); - if (i + strlen(temp) +1 < NAME_LENGTH) - { - p->friends[count].name[i] = ','; - strcpy(p->friends[count].name+i+1, temp); - } - } //End Guess Block - } //Friend's for. - break; //Found friends. - } - fclose(fp); - return count; -} -#ifndef TXT_SQL_CONVERT -//--------------------------------- -// Function to read characters file -//--------------------------------- -int mmo_char_init(void) { - char line[65536]; - int ret, line_count; - FILE *fp; - - char_max = 256; - char_dat = (struct character_data*)aCalloc(sizeof(struct character_data) * 256, 1); - if (!char_dat) { - ShowFatalError("out of memory: mmo_char_init (calloc of char_dat).\n"); - exit(1); - } - char_num = 0; - - fp = fopen(char_txt, "r"); - - if (fp == NULL) { - ShowError("Characters file not found: %s.\n", char_txt); - char_log("Characters file not found: %s." RETCODE, char_txt); - char_log("Id for the next created character: %d." RETCODE, char_id_count); - return 0; - } - - line_count = 0; - while(fgets(line, sizeof(line)-1, fp)) { - int i, j; - line_count++; - - if (line[0] == '/' && line[1] == '/') - continue; - line[sizeof(line)-1] = '\0'; - - j = 0; - if (sscanf(line, "%d\t%%newid%%%n", &i, &j) == 1 && j > 0) { - if (char_id_count < i) - char_id_count = i; - continue; - } - - if (char_num >= char_max) { - char_max += 256; - char_dat = (struct character_data*)aRealloc(char_dat, sizeof(struct character_data) * char_max); - if (!char_dat) { - ShowFatalError("Out of memory: mmo_char_init (realloc of char_dat).\n"); - char_log("Out of memory: mmo_char_init (realloc of char_dat)." RETCODE); - exit(1); - } - } - - ret = mmo_char_fromstr(line, &char_dat[char_num].status, char_dat[char_num].global, &char_dat[char_num].global_num); - - // Initialize friends list - parse_friend_txt(&char_dat[char_num].status); // Grab friends for the character - - if (ret > 0) { // negative value or zero for errors - if (char_dat[char_num].status.char_id >= char_id_count) - char_id_count = char_dat[char_num].status.char_id + 1; - char_num++; - } else { - ShowError("mmo_char_init: in characters file, unable to read the line #%d.\n", line_count); - ShowError(" -> Character saved in log file.\n"); - switch (ret) { - case -1: - char_log("Duplicate character id in the next character line (character not readed):" RETCODE); - break; - case -2: - char_log("Duplicate character name in the next character line (character not readed):" RETCODE); - break; - case -3: - char_log("Invalid memo point structure in the next character line (character not readed):" RETCODE); - break; - case -4: - char_log("Invalid inventory item structure in the next character line (character not readed):" RETCODE); - break; - case -5: - char_log("Invalid cart item structure in the next character line (character not readed):" RETCODE); - break; - case -6: - char_log("Invalid skill structure in the next character line (character not readed):" RETCODE); - break; - case -7: - char_log("Invalid register structure in the next character line (character not readed):" RETCODE); - break; - default: // 0 - char_log("Unabled to get a character in the next line - Basic structure of line (before inventory) is incorrect (character not readed):" RETCODE); - break; - } - char_log("%s", line); - } - } - fclose(fp); - - if (char_num == 0) { - ShowNotice("mmo_char_init: No character found in %s.\n", char_txt); - char_log("mmo_char_init: No character found in %s." RETCODE, char_txt); - } else if (char_num == 1) { - ShowStatus("mmo_char_init: 1 character read in %s.\n", char_txt); - char_log("mmo_char_init: 1 character read in %s." RETCODE, char_txt); - } else { - ShowStatus("mmo_char_init: %d characters read in %s.\n", char_num, char_txt); - char_log("mmo_char_init: %d characters read in %s." RETCODE, char_num, char_txt); - } - - char_log("Id for the next created character: %d." RETCODE, char_id_count); - - return 0; -} - -//--------------------------------------------------------- -// Function to save characters in files (speed up by [Yor]) -//--------------------------------------------------------- -void mmo_char_sync(void) { - char line[65536],f_line[1024]; - int i, j, k; - int lock; - FILE *fp,*f_fp; - //int *id = (int *) aMalloc(sizeof(int) * char_num); - CREATE_BUFFER(id, int, char_num); - - // Sorting before save (by [Yor]) - for(i = 0; i < char_num; i++) { - id[i] = i; - for(j = 0; j < i; j++) { - if ((char_dat[i].status.account_id < char_dat[id[j]].status.account_id) || - // if same account id, we sort by slot. - (char_dat[i].status.account_id == char_dat[id[j]].status.account_id && - char_dat[i].status.char_num < char_dat[id[j]].status.char_num)) { - for(k = i; k > j; k--) - id[k] = id[k-1]; - id[j] = i; // id[i] - break; - } - } - } - - // Data save - fp = lock_fopen(char_txt, &lock); - if (fp == NULL) { - ShowWarning("Server can't not save characters.\n"); - char_log("WARNING: Server can't not save characters." RETCODE); - } else { - for(i = 0; i < char_num; i++) { - // create only once the line, and save it in the 2 files (it's speeder than repeat twice the loop and create twice the line) - mmo_char_tostr(line, &char_dat[id[i]].status, char_dat[id[i]].global, char_dat[id[i]].global_num); // use of sorted index - fprintf(fp, "%s" RETCODE, line); - } - fprintf(fp, "%d\t%%newid%%" RETCODE, char_id_count); - lock_fclose(fp, char_txt, &lock); - } - - // Data save (backup) - if (backup_txt_flag) { // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. => option By [Yor] - fp = lock_fopen(backup_txt, &lock); - if (fp == NULL) { - ShowWarning("Server can't not create backup of characters file.\n"); - char_log("WARNING: Server can't not create backup of characters file." RETCODE); - //aFree(id); // free up the memory before leaving -.- [Ajarn] - DELETE_BUFFER(id); - return; - } - for(i = 0; i < char_num; i++) { - // create only once the line, and save it in the 2 files (it's speeder than repeat twice the loop and create twice the line) - mmo_char_tostr(line, &char_dat[id[i]].status,char_dat[id[i]].global, char_dat[id[i]].global_num); // use of sorted index - fprintf(fp, "%s" RETCODE, line); - } - fprintf(fp, "%d\t%%newid%%" RETCODE, char_id_count); - lock_fclose(fp, backup_txt, &lock); - } - - // Friends List data save (davidsiaw) - f_fp = lock_fopen(friends_txt, &lock); - for(i = 0; i < char_num; i++) { - mmo_friends_list_data_str(f_line, &char_dat[id[i]].status); - fprintf(f_fp, "%s" RETCODE, f_line); - } - - lock_fclose(f_fp, friends_txt, &lock); - - //aFree(id); - DELETE_BUFFER(id); - - return; -} - -//---------------------------------------------------- -// Function to save (in a periodic way) datas in files -//---------------------------------------------------- -int mmo_char_sync_timer(int tid, unsigned int tick, int id, int data) { - if (save_log) - ShowInfo("Saving all files...\n"); - mmo_char_sync(); - inter_save(); - return 0; -} - -//----------------------------------- -// Function to create a new character -//----------------------------------- -int make_new_char(int fd, unsigned char *dat) { - int i; - struct char_session_data *sd; - char name[NAME_LENGTH]; - - sd = (struct char_session_data*)session[fd]->session_data; - - // remove control characters from the name - strncpy(name, dat, NAME_LENGTH); - name[NAME_LENGTH-1] = '\0'; //Trunc name to max possible value (23) - - trim(name,TRIM_CHARS); //Trim character name. [Skotlex] - - //check name != main chat nick [LuzZza] - if(strcmpi(name, main_chat_nick) == 0) { - char_log("Create char failed (%d): this nick (%s) reserved for mainchat messages." RETCODE, - sd->account_id, name); - return -1; - } - - if (remove_control_chars((unsigned char *)name)) { - char_log("Make new char error (control char received in the name): (connection #%d, account: %d)." RETCODE, - fd, sd->account_id); - return -1; - } - - // check lenght of character name - if (strlen(name) < 4) { - char_log("Make new char error (character name too small): (connection #%d, account: %d, name: '%s')." RETCODE, - fd, sd->account_id, dat); - return -1; - } - - // Check Authorised letters/symbols in the name of the character - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) { - char_log("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c." RETCODE, - fd, sd->account_id, name, name[i]); - return -1; - } - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) { - char_log("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c." RETCODE, - fd, sd->account_id, dat, dat[i]); - return -1; - } - } // else, all letters/symbols are authorised (except control char removed before) - - if (dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29] != 5*6 || // stats - dat[30] >= 9 || // slots (dat[30] can not be negativ) - dat[33] <= 0 || dat[33] >= 24 || // hair style - dat[31] >= 9) { // hair color (dat[31] can not be negativ) - char_log("Make new char error (invalid values): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d" RETCODE, - fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); - return -1; - } - - // check individual stat value - for(i = 24; i <= 29; i++) { - if (dat[i] < 1 || dat[i] > 9) { - char_log("Make new char error (invalid stat value: not between 1 to 9): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d" RETCODE, - fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); - return -1; - } - } // now we know that every stat has proper value but we have to check if str/int agi/luk vit/dex pairs are correct - - if( ((dat[24]+dat[27]) > 10) || ((dat[25]+dat[29]) > 10) || ((dat[26]+dat[28]) > 10) ) { - if (log_char) { - char_log("Make new char error (invalid stat value): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d" RETCODE, - fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); - return -1; - } - } // now when we have passed all stat checks - - for(i = 0; i < char_num; i++) { - if ((name_ignoring_case != 0 && strncmp(char_dat[i].status.name, name, NAME_LENGTH) == 0) || - (name_ignoring_case == 0 && strncmpi(char_dat[i].status.name, name, NAME_LENGTH) == 0)) { - char_log("Make new char error (name already exists): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d." RETCODE, - fd, sd->account_id, dat[30], dat, char_dat[i].status.name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); - return -1; - } - if (char_dat[i].status.account_id == sd->account_id && char_dat[i].status.char_num == dat[30]) { - char_log("Make new char error (slot already used): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d." RETCODE, - fd, sd->account_id, dat[30], dat, char_dat[i].status.name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); - return -1; - } - } - - if (strcmp(wisp_server_name, name) == 0) { - char_log("Make new char error (name used is wisp name for server): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d." RETCODE, - fd, sd->account_id, dat[30], name, char_dat[i].status.name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); - return -1; - } - - if (char_num >= char_max) { - char_max += 256; - char_dat = (struct character_data*)aRealloc(char_dat, sizeof(struct character_data) * char_max); - if (!char_dat) { - ShowFatalError("Out of memory: make_new_char (realloc of char_dat).\n"); - char_log("Out of memory: make_new_char (realloc of char_dat)." RETCODE); - exit(1); - } - } - - char_log("Creation of New Character: (connection #%d, account: %d) slot %d, character Name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d." RETCODE, - fd, sd->account_id, dat[30], name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); - - memset(&char_dat[i], 0, sizeof(struct character_data)); - - char_dat[i].status.char_id = char_id_count++; - char_dat[i].status.account_id = sd->account_id; - char_dat[i].status.char_num = dat[30]; - strcpy(char_dat[i].status.name,name); - char_dat[i].status.class_ = 0; - char_dat[i].status.base_level = 1; - char_dat[i].status.job_level = 1; - char_dat[i].status.base_exp = 0; - char_dat[i].status.job_exp = 0; - char_dat[i].status.zeny = start_zeny; - char_dat[i].status.str = dat[24]; - char_dat[i].status.agi = dat[25]; - char_dat[i].status.vit = dat[26]; - char_dat[i].status.int_ = dat[27]; - char_dat[i].status.dex = dat[28]; - char_dat[i].status.luk = dat[29]; - char_dat[i].status.max_hp = 40 * (100 + char_dat[i].status.vit) / 100; - char_dat[i].status.max_sp = 11 * (100 + char_dat[i].status.int_) / 100; - char_dat[i].status.hp = char_dat[i].status.max_hp; - char_dat[i].status.sp = char_dat[i].status.max_sp; - char_dat[i].status.status_point = 0; - char_dat[i].status.skill_point = 0; - char_dat[i].status.option = 0; - char_dat[i].status.karma = 0; - char_dat[i].status.manner = 0; - char_dat[i].status.party_id = 0; - char_dat[i].status.guild_id = 0; - char_dat[i].status.hair = dat[33]; - char_dat[i].status.hair_color = dat[31]; - char_dat[i].status.clothes_color = 0; - char_dat[i].status.inventory[0].nameid = start_weapon; // Knife - char_dat[i].status.inventory[0].amount = 1; - char_dat[i].status.inventory[0].equip = 0x02; - char_dat[i].status.inventory[0].identify = 1; - char_dat[i].status.inventory[1].nameid = start_armor; // Cotton Shirt - char_dat[i].status.inventory[1].amount = 1; - char_dat[i].status.inventory[1].equip = 0x10; - char_dat[i].status.inventory[1].identify = 1; - char_dat[i].status.weapon = 1; - char_dat[i].status.shield = 0; - char_dat[i].status.head_top = 0; - char_dat[i].status.head_mid = 0; - char_dat[i].status.head_bottom = 0; - memcpy(&char_dat[i].status.last_point, &start_point, sizeof(start_point)); - memcpy(&char_dat[i].status.save_point, &start_point, sizeof(start_point)); - char_num++; - - mmo_char_sync(); - return i; -} - -//---------------------------------------------------- -// This function return the name of the job (by [Yor]) -//---------------------------------------------------- -char * job_name(int class_) { - switch (class_) { - case JOB_NOVICE: return "Novice"; - case JOB_SWORDMAN: return "Swordsman"; - case JOB_MAGE: return "Mage"; - case JOB_ARCHER: return "Archer"; - case JOB_ACOLYTE: return "Acolyte"; - case JOB_MERCHANT: return "Merchant"; - case JOB_THIEF: return "Thief"; - case JOB_KNIGHT: return "Knight"; - case JOB_PRIEST: return "Priest"; - case JOB_WIZARD: return "Wizard"; - case JOB_BLACKSMITH: return "Blacksmith"; - case JOB_HUNTER: return "Hunter"; - case JOB_ASSASSIN: return "Assassin"; - case JOB_KNIGHT2: return "Peco-Knight"; - case JOB_CRUSADER: return "Crusader"; - case JOB_MONK: return "Monk"; - case JOB_SAGE: return "Sage"; - case JOB_ROGUE: return "Rogue"; - case JOB_ALCHEMIST: return "Alchemist"; - case JOB_BARD: return "Bard"; - case JOB_DANCER: return "Dancer"; - case JOB_CRUSADER2: return "Peco-Crusader"; - case JOB_WEDDING: return "Wedding"; - case JOB_SUPER_NOVICE: return "Super Novice"; - case JOB_GUNSLINGER: return "Gunslinger"; - case JOB_NINJA: return "Ninja"; - case JOB_XMAS: return "Christmas"; - case JOB_NOVICE_HIGH: return "Novice High"; - case JOB_SWORDMAN_HIGH: return "Swordsman High"; - case JOB_MAGE_HIGH: return "Mage High"; - case JOB_ARCHER_HIGH: return "Archer High"; - case JOB_ACOLYTE_HIGH: return "Acolyte High"; - case JOB_MERCHANT_HIGH: return "Merchant High"; - case JOB_THIEF_HIGH: return "Thief High"; - case JOB_LORD_KNIGHT: return "Lord Knight"; - case JOB_HIGH_PRIEST: return "High Priest"; - case JOB_HIGH_WIZARD: return "High Wizard"; - case JOB_WHITESMITH: return "Whitesmith"; - case JOB_SNIPER: return "Sniper"; - case JOB_ASSASSIN_CROSS: return "Assassin Cross"; - case JOB_LORD_KNIGHT2: return "Peko Knight"; - case JOB_PALADIN: return "Paladin"; - case JOB_CHAMPION: return "Champion"; - case JOB_PROFESSOR: return "Professor"; - case JOB_STALKER: return "Stalker"; - case JOB_CREATOR: return "Creator"; - case JOB_CLOWN: return "Clown"; - case JOB_GYPSY: return "Gypsy"; - case JOB_PALADIN2: return "Peko Paladin"; - case JOB_BABY: return "Baby Novice"; - case JOB_BABY_SWORDMAN: return "Baby Swordsman"; - case JOB_BABY_MAGE: return "Baby Mage"; - case JOB_BABY_ARCHER: return "Baby Archer"; - case JOB_BABY_ACOLYTE: return "Baby Acolyte"; - case JOB_BABY_MERCHANT: return "Baby Merchant"; - case JOB_BABY_THIEF: return "Baby Thief"; - case JOB_BABY_KNIGHT: return "Baby Knight"; - case JOB_BABY_PRIEST: return "Baby Priest"; - case JOB_BABY_WIZARD: return "Baby Wizard"; - case JOB_BABY_BLACKSMITH: return "Baby Blacksmith"; - case JOB_BABY_HUNTER: return "Baby Hunter"; - case JOB_BABY_ASSASSIN: return "Baby Assassin"; - case JOB_BABY_KNIGHT2: return "Baby Peco Knight"; - case JOB_BABY_CRUSADER: return "Baby Crusader"; - case JOB_BABY_MONK: return "Baby Monk"; - case JOB_BABY_SAGE: return "Baby Sage"; - case JOB_BABY_ROGUE: return "Baby Rogue"; - case JOB_BABY_ALCHEMIST: return "Baby Alchemist"; - case JOB_BABY_BARD: return "Baby Bard"; - case JOB_BABY_DANCER: return "Baby Dancer"; - case JOB_BABY_CRUSADER2: return "Baby Peco Crusader"; - case JOB_SUPER_BABY: return "Super Baby"; - case JOB_TAEKWON: return "Taekwon"; - case JOB_STAR_GLADIATOR: return "Star Gladiator"; - case JOB_STAR_GLADIATOR2: return "Flying Star Gladiator"; - case JOB_SOUL_LINKER: return "Soul Linker"; - } - return "Unknown Job"; -} - -static int create_online_files_sub(DBKey key, void* data, va_list va) -{ - struct online_char_data *character; - int* players; - int *id; - int j,k,l; - character = (struct online_char_data*) data; - players = va_arg(va, int*); - id = va_arg(va, int*); - - // check if map-server is online - if (character->server == -1 || character->char_id == -1) { //Character not currently online. - return -1; - } - - j = character->server; - if (server_fd[j] < 0) { - server[j].users = 0; - return -1; - } - // search position of character in char_dat and sort online characters. - for(j = 0; j < char_num; j++) { - if (char_dat[j].status.char_id != character->char_id) - continue; - id[*players] = j; - // use sorting option - switch (online_sorting_option) { - case 1: // by name (without case sensitive) - for(k = 0; k < *players; k++) - if (stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0 || - // if same name, we sort with case sensitive. - (stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) == 0 && - strcmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0)) { - for(l = *players; l > k; l--) - id[l] = id[l-1]; - id[k] = j; // id[*players] - break; - } - break; - case 2: // by zeny - for(k = 0; k < *players; k++) - if (char_dat[j].status.zeny < char_dat[id[k]].status.zeny || - // if same number of zenys, we sort by name. - (char_dat[j].status.zeny == char_dat[id[k]].status.zeny && - stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0)) { - for(l = *players; l > k; l--) - id[l] = id[l-1]; - id[k] = j; // id[*players] - break; - } - break; - case 3: // by base level - for(k = 0; k < *players; k++) - if (char_dat[j].status.base_level < char_dat[id[k]].status.base_level || - // if same base level, we sort by base exp. - (char_dat[j].status.base_level == char_dat[id[k]].status.base_level && - char_dat[j].status.base_exp < char_dat[id[k]].status.base_exp)) { - for(l = *players; l > k; l--) - id[l] = id[l-1]; - id[k] = j; // id[*players] - break; - } - break; - case 4: // by job (and job level) - for(k = 0; k < *players; k++) - if (char_dat[j].status.class_ < char_dat[id[k]].status.class_ || - // if same job, we sort by job level. - (char_dat[j].status.class_ == char_dat[id[k]].status.class_ && - char_dat[j].status.job_level < char_dat[id[k]].status.job_level) || - // if same job and job level, we sort by job exp. - (char_dat[j].status.class_ == char_dat[id[k]].status.class_ && - char_dat[j].status.job_level == char_dat[id[k]].status.job_level && - char_dat[j].status.job_exp < char_dat[id[k]].status.job_exp)) { - for(l = *players; l > k; l--) - id[l] = id[l-1]; - id[k] = j; // id[*players] - break; - } - break; - case 5: // by location map name - { - const char *map1, *map2; - map1 = mapindex_id2name(char_dat[j].status.last_point.map); - - for(k = 0; k < *players; k++) { - map2 = mapindex_id2name(char_dat[id[k]].status.last_point.map); - if (!map1 || !map2 || //Avoid sorting if either one failed to resolve. - stricmp(map1, map2) < 0 || - // if same map name, we sort by name. - (stricmp(map1, map2) == 0 && - stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0)) { - for(l = *players; l > k; l--) - id[l] = id[l-1]; - id[k] = j; // id[*players] - break; - } - } - } - break; - default: // 0 or invalid value: no sorting - break; - } - (*players)++; - break; - } - return 0; -} -//------------------------------------------------------------- -// Function to create the online files (txt and html). by [Yor] -//------------------------------------------------------------- -void create_online_files(void) { - unsigned int k, j; // for loop with strlen comparing - int i, l; // for loops - int players; // count the number of players - FILE *fp; // for the txt file - FILE *fp2; // for the html file - char temp[256]; // to prepare what we must display - time_t time_server; // for number of seconds - struct tm *datetime; // variable for time in structure ->tm_mday, ->tm_sec, ... - int id[4096]; - - if (online_display_option == 0) // we display nothing, so return - return; - - // Get number of online players, id of each online players, and verify if a server is offline - players = 0; - online_char_db->foreach(online_char_db, create_online_files_sub, &players, &id); - - // write files - fp = fopen(online_txt_filename, "w"); - if (fp != NULL) { - fp2 = fopen(online_html_filename, "w"); - if (fp2 != NULL) { - // get time - time(&time_server); // get time in seconds since 1/1/1970 - datetime = localtime(&time_server); // convert seconds in structure - strftime(temp, sizeof(temp), "%d %b %Y %X", datetime); // like sprintf, but only for date/time (05 dec 2003 15:12:52) - // write heading - fprintf(fp2, "\n"); - fprintf(fp2, " \n", online_refresh_html); // update on client explorer every x seconds - fprintf(fp2, " \n"); - fprintf(fp2, " Online Players on %s\n", server_name); - fprintf(fp2, " \n"); - fprintf(fp2, " \n"); - fprintf(fp2, "

Online Players on %s (%s):

\n", server_name, temp); - fprintf(fp, "Online Players on %s (%s):\n", server_name, temp); - fprintf(fp, "\n"); - - for (i = 0; i < players; i++) { - // if it's the first player - if (i == 0) { - j = 0; // count the number of characters for the txt version and to set the separate line - fprintf(fp2, " \n"); - fprintf(fp2, " \n"); - if ((online_display_option & 1) || (online_display_option & 64)) { - fprintf(fp2, " \n"); - if (online_display_option & 64) { - fprintf(fp, "Name "); // 30 - j += 30; - } else { - fprintf(fp, "Name "); // 25 - j += 25; - } - } - if ((online_display_option & 6) == 6) { - fprintf(fp2, " \n"); - fprintf(fp, "Job Levels "); // 27 - j += 27; - } else if (online_display_option & 2) { - fprintf(fp2, " \n"); - fprintf(fp, "Job "); // 19 - j += 19; - } else if (online_display_option & 4) { - fprintf(fp2, " \n"); - fprintf(fp, " Levels "); // 8 - j += 8; - } - if (online_display_option & 24) { // 8 or 16 - fprintf(fp2, " \n"); - if (online_display_option & 16) { - fprintf(fp, "Location ( x , y ) "); // 23 - j += 23; - } else { - fprintf(fp, "Location "); // 13 - j += 13; - } - } - if (online_display_option & 32) { - fprintf(fp2, " \n"); - fprintf(fp, " Zenys "); // 16 - j += 16; - } - fprintf(fp2, " \n"); - fprintf(fp, "\n"); - for (k = 0; k < j; k++) - fprintf(fp, "-"); - fprintf(fp, "\n"); - } - fprintf(fp2, " \n"); - // get id of the character (more speed) - j = id[i]; - // displaying the character name - if ((online_display_option & 1) || (online_display_option & 64)) { // without/with 'GM' display - strcpy(temp, char_dat[j].status.name); - l = isGM(char_dat[j].status.account_id); - if (online_display_option & 64) { - if (l >= online_gm_display_min_level) - fprintf(fp, "%-24s (GM) ", temp); - else - fprintf(fp, "%-24s ", temp); - } else - fprintf(fp, "%-24s ", temp); - // name of the character in the html (no < >, because that create problem in html code) - fprintf(fp2, " \n"); - } - // displaying of the job - if (online_display_option & 6) { - char * jobname = job_name(char_dat[j].status.class_); - if ((online_display_option & 6) == 6) { - fprintf(fp2, " \n", jobname, char_dat[j].status.base_level, char_dat[j].status.job_level); - fprintf(fp, "%-18s %3d/%3d ", jobname, char_dat[j].status.base_level, char_dat[j].status.job_level); - } else if (online_display_option & 2) { - fprintf(fp2, " \n", jobname); - fprintf(fp, "%-18s ", jobname); - } else if (online_display_option & 4) { - fprintf(fp2, " \n", char_dat[j].status.base_level, char_dat[j].status.job_level); - fprintf(fp, "%3d/%3d ", char_dat[j].status.base_level, char_dat[j].status.job_level); - } - } - // displaying of the map - if (online_display_option & 24) { // 8 or 16 - // prepare map name - memcpy(temp, mapindex_id2name(char_dat[j].status.last_point.map), MAP_NAME_LENGTH); - temp[MAP_NAME_LENGTH] = '\0'; - if (strstr(temp, ".gat") != NULL) { - temp[strstr(temp, ".gat") - temp] = 0; // suppress the '.gat' - } - // write map name - if (online_display_option & 16) { // map-name AND coordinates - fprintf(fp2, " \n", temp, char_dat[j].status.last_point.x, char_dat[j].status.last_point.y); - fprintf(fp, "%-12s (%3d,%3d) ", temp, char_dat[j].status.last_point.x, char_dat[j].status.last_point.y); - } else { - fprintf(fp2, " \n", temp); - fprintf(fp, "%-12s ", temp); - } - } - // displaying nimber of zenys - if (online_display_option & 32) { - // write number of zenys - if (char_dat[j].status.zeny == 0) { // if no zeny - fprintf(fp2, " \n"); - fprintf(fp, " no zeny "); - } else { - fprintf(fp2, " \n", char_dat[j].status.zeny); - fprintf(fp, "%13d z ", char_dat[j].status.zeny); - } - } - fprintf(fp, "\n"); - fprintf(fp2, " \n"); - } - // If we display at least 1 player - if (players > 0) { - fprintf(fp2, "
NameJob (levels)JobLevelsLocationzenys
"); - if ((online_display_option & 64) && l >= online_gm_display_min_level) - fprintf(fp2, ""); - for (k = 0; k < strlen(temp); k++) { - switch(temp[k]) { - case '<': // < - fprintf(fp2, "<"); - break; - case '>': // > - fprintf(fp2, ">"); - break; - default: - fprintf(fp2, "%c", temp[k]); - break; - }; - } - if ((online_display_option & 64) && l >= online_gm_display_min_level) - fprintf(fp2, " (GM)"); - fprintf(fp2, "%s %d/%d%s%d/%d%s (%d, %d)%sno zeny%d z
\n"); - fprintf(fp, "\n"); - } - - // Displaying number of online players - if (players == 0) { - fprintf(fp2, "

No user is online.

\n"); - fprintf(fp, "No user is online.\n"); - } else if (players == 1) { - fprintf(fp2, "

%d user is online.

\n", players); - fprintf(fp, "%d user is online.\n", players); - } else { - fprintf(fp2, "

%d users are online.

\n", players); - fprintf(fp, "%d users are online.\n", players); - } - fprintf(fp2, " \n"); - fprintf(fp2, "\n"); - fclose(fp2); - } - fclose(fp); - } - - return; -} - -//--------------------------------------------------------------------- -// This function return the number of online players in all map-servers -//--------------------------------------------------------------------- -int count_users(void) { - int i, users; - - users = 0; - for(i = 0; i < MAX_MAP_SERVERS; i++) - if (server_fd[i] >= 0) - users += server[i].users; - - return users; -} - -//---------------------------------------- -// Function to send characters to a player -//---------------------------------------- -int mmo_char_send006b(int fd, struct char_session_data *sd) { - int i, j, found_num; - struct mmo_charstatus *p; -//#ifdef NEW_006b - const int offset = 24; -//#else -// const int offset = 4; -//#endif - - set_char_online(-1, 99,sd->account_id); - - found_num = 0; - for(i = 0; i < char_num; i++) { - if (char_dat[i].status.account_id == sd->account_id) { - sd->found_char[found_num] = i; - found_num++; - if (found_num == 9) - break; - } - } - for(i = found_num; i < 9; i++) - sd->found_char[i] = -1; - - WFIFOHEAD(fd, offset + found_num * 106); - memset(WFIFOP(fd,0), 0, offset + found_num * 106); - WFIFOW(fd,0) = 0x6b; - WFIFOW(fd,2) = offset + found_num * 106; - - for(i = 0; i < found_num; i++) { - p = &char_dat[sd->found_char[i]].status; - j = offset + (i * 106); // increase speed of code - - WFIFOL(fd,j) = p->char_id; - WFIFOL(fd,j+4) = p->base_exp>LONG_MAX?LONG_MAX:p->base_exp; - WFIFOL(fd,j+8) = p->zeny; - WFIFOL(fd,j+12) = p->job_exp>LONG_MAX?LONG_MAX:p->job_exp; - WFIFOL(fd,j+16) = p->job_level; - - WFIFOL(fd,j+20) = 0; - WFIFOL(fd,j+24) = 0; - WFIFOL(fd,j+28) = p->option; - - WFIFOL(fd,j+32) = p->karma; - WFIFOL(fd,j+36) = p->manner; - - WFIFOW(fd,j+40) = (p->status_point>SHRT_MAX) ? SHRT_MAX : p->status_point; - WFIFOW(fd,j+42) = (p->hp > SHRT_MAX) ? SHRT_MAX : p->hp; - WFIFOW(fd,j+44) = (p->max_hp > SHRT_MAX) ? SHRT_MAX : p->max_hp; - WFIFOW(fd,j+46) = (p->sp > SHRT_MAX) ? SHRT_MAX : p->sp; - WFIFOW(fd,j+48) = (p->max_sp > SHRT_MAX) ? SHRT_MAX : p->max_sp; - WFIFOW(fd,j+50) = DEFAULT_WALK_SPEED; // p->speed; - WFIFOW(fd,j+52) = p->class_; - WFIFOW(fd,j+54) = p->hair; - WFIFOW(fd,j+56) = p->option&0x20?0:p->weapon; //When the weapon is sent and your option is riding, the client crashes on login!? - WFIFOW(fd,j+58) = p->base_level; - WFIFOW(fd,j+60) = (p->skill_point>SHRT_MAX)? SHRT_MAX : p->skill_point; - WFIFOW(fd,j+62) = p->head_bottom; - WFIFOW(fd,j+64) = p->shield; - WFIFOW(fd,j+66) = p->head_top; - WFIFOW(fd,j+68) = p->head_mid; - WFIFOW(fd,j+70) = p->hair_color; - WFIFOW(fd,j+72) = p->clothes_color; - - memcpy(WFIFOP(fd,j+74), p->name, NAME_LENGTH); - - WFIFOB(fd,j+98) = (p->str > 255) ? 255 : p->str; - WFIFOB(fd,j+99) = (p->agi > 255) ? 255 : p->agi; - WFIFOB(fd,j+100) = (p->vit > 255) ? 255 : p->vit; - WFIFOB(fd,j+101) = (p->int_ > 255) ? 255 : p->int_; - WFIFOB(fd,j+102) = (p->dex > 255) ? 255 : p->dex; - WFIFOB(fd,j+103) = (p->luk > 255) ? 255 : p->luk; - WFIFOB(fd,j+104) = p->char_num; - } - - WFIFOSET(fd,WFIFOW(fd,2)); - - return 0; -} - -// 離婚(char削除時に使用) -int char_divorce(struct mmo_charstatus *cs) { - if (cs == NULL) - return 0; - - if (cs->partner_id > 0){ - int i, j; - for(i = 0; i < char_num; i++) { - if (char_dat[i].status.char_id == cs->partner_id && char_dat[i].status.partner_id == cs->char_id) { - cs->partner_id = 0; - char_dat[i].status.partner_id = 0; - for(j = 0; j < MAX_INVENTORY; j++) - if (char_dat[i].status.inventory[j].nameid == WEDDING_RING_M || char_dat[i].status.inventory[j].nameid == WEDDING_RING_F) - memset(&char_dat[i].status.inventory[j], 0, sizeof(char_dat[i].status.inventory[0])); - if (cs->inventory[j].nameid == WEDDING_RING_M || cs->inventory[j].nameid == WEDDING_RING_F) - memset(&cs->inventory[j], 0, sizeof(cs->inventory[0])); - return 0; - } - } - } - return 0; -} - -int char_married(int pl1,int pl2) { - return (char_dat[pl1].status.char_id == char_dat[pl2].status.partner_id && char_dat[pl2].status.char_id == char_dat[pl1].status.partner_id); -} - -int char_child(int parent_id, int child_id) { - return (char_dat[parent_id].status.child == char_dat[child_id].status.char_id && - ((char_dat[parent_id].status.char_id == char_dat[child_id].status.father) || - (char_dat[parent_id].status.char_id == char_dat[child_id].status.mother))); -} - -int char_family(int cid1, int cid2, int cid3) { - int i, idx1 = -1, idx2 =-1;//, idx3 =-1; - for(i = 0; i < char_num && (idx1 == -1 || idx2 == -1/* || idx3 == 1*/); i++) - { - if (char_dat[i].status.char_id == cid1) - idx1 = i; - if (char_dat[i].status.char_id == cid2) - idx2 = i; -// if (char_dat[i].status.char_id == cid2) -// idx3 = i; - } - if (idx1 == -1 || idx2 == -1/* || idx3 == -1*/) - return 0; //Some character not found?? - - //Unless the dbs are corrupted, these 3 checks should suffice, even though - //we could do a lot more checks and force cross-reference integrity. - if(char_dat[idx1].status.partner_id == cid2 && - char_dat[idx1].status.child == cid3) - return cid3; //cid1/cid2 parents. cid3 child. - - if(char_dat[idx1].status.partner_id == cid3 && - char_dat[idx1].status.child == cid2) - return cid2; //cid1/cid3 parents. cid2 child. - - if(char_dat[idx2].status.partner_id == cid3 && - char_dat[idx2].status.child == cid1) - return cid1; //cid2/cid3 parents. cid1 child. - return 0; -} - -//Clears the given party id from all characters. -//Since sometimes the party format changes and parties must be wiped, this -//method is required to prevent stress during the "party not found!" stages. -void char_clearparty(int party_id) -{ - int i; - for(i = 0; i < char_num; i++) - { - if (char_dat[i].status.party_id == party_id) - char_dat[i].status.party_id = 0; - } -} - -//------------------------------------------------------------ -// E-mail check: return 0 (not correct) or 1 (valid). by [Yor] -//------------------------------------------------------------ -int e_mail_check(char *email) { - char ch; - char* last_arobas; - - // athena limits - if (strlen(email) < 3 || strlen(email) > 39) - return 0; - - // part of RFC limits (official reference of e-mail description) - if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') - return 0; - - if (email[strlen(email)-1] == '.') - return 0; - - last_arobas = strrchr(email, '@'); - - if (strstr(last_arobas, "@.") != NULL || - strstr(last_arobas, "..") != NULL) - return 0; - - for(ch = 1; ch < 32; ch++) { - if (strchr(last_arobas, ch) != NULL) { - return 0; - break; - } - } - - if (strchr(last_arobas, ' ') != NULL || - strchr(last_arobas, ';') != NULL) - return 0; - - // all correct - return 1; -} - -//---------------------------------------------------------------------- -// Force disconnection of an online player (with account value) by [Yor] -//---------------------------------------------------------------------- -int disconnect_player(int account_id) { - int i; - struct char_session_data *sd; - - // disconnect player if online on char-server - for(i = 0; i < fd_max; i++) { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { - if (sd->account_id == account_id) { - session[i]->eof = 1; - return 1; - } - } - } - - return 0; -} - -// キャラ削除に伴うデータ削除 -static int char_delete(struct mmo_charstatus *cs) { - int j; - - // ペット削除 - if (cs->pet_id) - inter_pet_delete(cs->pet_id); - if (cs->hom_id) - inter_homun_delete(cs->hom_id); - for (j = 0; j < MAX_INVENTORY; j++) - if (cs->inventory[j].card[0] == (short)0xff00) - inter_pet_delete(MakeDWord(cs->inventory[j].card[1],cs->inventory[j].card[2])); - for (j = 0; j < MAX_CART; j++) - if (cs->cart[j].card[0] == (short)0xff00) - inter_pet_delete( MakeDWord(cs->cart[j].card[1],cs->cart[j].card[2]) ); - // ギルド脱退 - if (cs->guild_id) - inter_guild_leave(cs->guild_id, cs->account_id, cs->char_id); - // パーティー脱退 - if (cs->party_id) - inter_party_leave(cs->party_id, cs->account_id, cs->char_id); - // 離婚 - if (cs->partner_id){ - // 離婚情報をmapに通知 - unsigned char buf[10]; - WBUFW(buf,0) = 0x2b12; - WBUFL(buf,2) = cs->char_id; - WBUFL(buf,6) = cs->partner_id; - mapif_sendall(buf,10); - // 離婚 - char_divorce(cs); - } -#ifdef ENABLE_SC_SAVING - status_delete_scdata(cs->account_id, cs->char_id); -#endif - return 0; -} - -int send_accounts_tologin(int tid, unsigned int tick, int id, int data); - -int parse_tologin(int fd) { - int i; - struct char_session_data *sd; - RFIFOHEAD(fd); - - // only login-server can have an access to here. - // so, if it isn't the login-server, we disconnect the session (fd != login_fd). - if (fd != login_fd) - session[fd]->eof = 1; - if(session[fd]->eof) { - if (fd == login_fd) { - ShowWarning("Connection to login-server lost (connection #%d).\n", fd); - login_fd = -1; - } - do_close(fd); - return 0; - } - - sd = (struct char_session_data*)session[fd]->session_data; - - while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { -// printf("parse_tologin: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); - - switch(RFIFOW(fd,0)) { - case 0x2711: - if (RFIFOREST(fd) < 3) - return 0; - if (RFIFOB(fd,2)) { -// printf("connect login server error : %d\n", RFIFOB(fd,2)); - ShowError("Can not connect to the login-server.\n"); - ShowError("The server communication passwords (default s1/p1) are probably invalid.\n"); - ShowInfo("Also, please make sure your accounts file (default: accounts.txt) has those values present.\n"); - ShowInfo("The communication passwords can be changed in map_athena.conf and char_athena.conf\n"); - exit(1); - } else { - ShowStatus("Connected to login-server (connection #%d).\n", fd); - - //Send to login accounts currently connected. - send_accounts_tologin(-1, gettick(), 0, 0); - - // if no map-server already connected, display a message... - for(i = 0; i < MAX_MAP_SERVERS; i++) - if (server_fd[i] >= 0 && server[i].map[0]) // if map-server online and at least 1 map - break; - if (i == MAX_MAP_SERVERS) - ShowStatus("Awaiting maps from map-server.\n"); - } - RFIFOSKIP(fd,3); - break; - - case 0x2713: - if (RFIFOREST(fd) < 51) - return 0; -// printf("parse_tologin 2713 : %d\n", RFIFOB(fd,6)); - for(i = 0; i < fd_max; i++) { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2)) { - if (RFIFOB(fd,6) != 0) { - WFIFOHEAD(i, 3); - WFIFOW(i,0) = 0x6c; - WFIFOB(i,2) = 0x42; - WFIFOSET(i,3); - } else if (max_connect_user == 0 || count_users() < max_connect_user) { -// if (max_connect_user == 0) -// printf("max_connect_user (unlimited) -> accepted.\n"); -// else -// printf("count_users(): %d < max_connect_user (%d) -> accepted.\n", count_users(), max_connect_user); - memcpy(sd->email, RFIFOP(fd, 7), 40); - if (e_mail_check(sd->email) == 0) - strncpy(sd->email, "a@a.com", 40); // default e-mail - sd->connect_until_time = (time_t)RFIFOL(fd,47); - // send characters to player - mmo_char_send006b(i, sd); - } else if(isGM(sd->account_id) >= gm_allow_level) { - sd->connect_until_time = (time_t)RFIFOL(fd,47); - // send characters to player - mmo_char_send006b(i, sd); - } else { - // refuse connection: too much online players -// printf("count_users(): %d < max_connect_use (%d) -> fail...\n", count_users(), max_connect_user); - WFIFOHEAD(i, 3); - WFIFOW(i,0) = 0x6c; - WFIFOW(i,2) = 0; - WFIFOSET(i,3); - } - break; - } - } - RFIFOSKIP(fd,51); - break; - - // Receiving of an e-mail/time limit from the login-server (answer of a request because a player comes back from map-server to char-server) by [Yor] - case 0x2717: - if (RFIFOREST(fd) < 50) - return 0; - for(i = 0; i < fd_max; i++) { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { - if (sd->account_id == RFIFOL(fd,2)) { - memcpy(sd->email, RFIFOP(fd,6), 40); - if (e_mail_check(sd->email) == 0) - strncpy(sd->email, "a@a.com", 40); // default e-mail - sd->connect_until_time = (time_t)RFIFOL(fd,46); - break; - } - } - } - RFIFOSKIP(fd,50); - break; - - // login-server alive packet - case 0x2718: - if (RFIFOREST(fd) < 2) - return 0; - RFIFOSKIP(fd,2); - break; - - // Receiving authentification from Freya-type login server (to avoid char->login->char) - case 0x2719: - if (RFIFOREST(fd) < 18) - return 0; - // to conserv a maximum of authentification, search if account is already authentified and replace it - // that will reduce multiple connection too - for(i = 0; i < AUTH_FIFO_SIZE; i++) - if (auth_fifo[i].account_id == RFIFOL(fd,2)) - break; - // if not found, use next value - if (i == AUTH_FIFO_SIZE) { - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - i = auth_fifo_pos; - auth_fifo_pos++; - } - auth_fifo[i].account_id = RFIFOL(fd,2); - auth_fifo[i].char_id = 0; - auth_fifo[i].login_id1 = RFIFOL(fd,6); - auth_fifo[i].login_id2 = RFIFOL(fd,10); - auth_fifo[i].delflag = 2; // 0: auth_fifo canceled/void, 2: auth_fifo received from login/map server in memory, 1: connection authentified - auth_fifo[i].char_pos = 0; - auth_fifo[i].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) - auth_fifo[i].ip = RFIFOL(fd,14); - RFIFOSKIP(fd,18); - break; - - case 0x2721: // gm reply - if (RFIFOREST(fd) < 10) - return 0; - { - unsigned char buf[10]; - WBUFW(buf,0) = 0x2b0b; - WBUFL(buf,2) = RFIFOL(fd,2); // account - WBUFL(buf,6) = RFIFOL(fd,6); // GM level - mapif_sendall(buf,10); -// printf("parse_tologin: To become GM answer: char -> map.\n"); - } - RFIFOSKIP(fd,10); - break; - - case 0x2723: // changesex reply (modified by [Yor]) - if (RFIFOREST(fd) < 7) - return 0; - { - int acc, sex, i, j; - unsigned char buf[7]; - acc = RFIFOL(fd,2); - sex = RFIFOB(fd,6); - RFIFOSKIP(fd, 7); - if (acc > 0) { - for(i = 0; i < AUTH_FIFO_SIZE; i++) { - if (auth_fifo[i].account_id == acc) - auth_fifo[i].sex = sex; - } - for (i = 0; i < char_num; i++) { - if (char_dat[i].status.account_id == acc) { - int jobclass = char_dat[i].status.class_; - char_dat[i].status.sex = sex; - if (jobclass == JOB_BARD || jobclass == JOB_DANCER || - jobclass == JOB_CLOWN || jobclass == JOB_GYPSY || - jobclass == JOB_BABY_BARD || jobclass == JOB_BABY_DANCER) { - // job modification - if (jobclass == JOB_BARD || jobclass == JOB_DANCER) { - char_dat[i].status.class_ = (sex) ? JOB_BARD : JOB_DANCER; - } else if (jobclass == JOB_CLOWN || jobclass == JOB_GYPSY) { - char_dat[i].status.class_ = (sex) ? JOB_CLOWN : JOB_GYPSY; - } else if (jobclass == JOB_BABY_BARD || jobclass == JOB_BABY_DANCER) { - char_dat[i].status.class_ = (sex) ? JOB_BABY_BARD : JOB_BABY_DANCER; - } - // remove specifical skills of classes 19, 4020 and 4042 - for(j = 315; j <= 322; j++) { - if (char_dat[i].status.skill[j].id > 0 && !char_dat[i].status.skill[j].flag) { - if (char_dat[i].status.skill_point > USHRT_MAX - char_dat[i].status.skill[j].lv) - char_dat[i].status.skill_point = USHRT_MAX; - else - char_dat[i].status.skill_point += char_dat[i].status.skill[j].lv; - char_dat[i].status.skill[j].id = 0; - char_dat[i].status.skill[j].lv = 0; - } - } - // remove specifical skills of classes 20, 4021 and 4043 - for(j = 323; j <= 330; j++) { - if (char_dat[i].status.skill[j].id > 0 && !char_dat[i].status.skill[j].flag) { - if (char_dat[i].status.skill_point > USHRT_MAX - char_dat[i].status.skill[j].lv) - char_dat[i].status.skill_point = USHRT_MAX; - else - char_dat[i].status.skill_point += char_dat[i].status.skill[j].lv; - - char_dat[i].status.skill[j].id = 0; - char_dat[i].status.skill[j].lv = 0; - } - } - } - // to avoid any problem with equipment and invalid sex, equipment is unequiped. - for (j = 0; j < MAX_INVENTORY; j++) { - if (char_dat[i].status.inventory[j].nameid && char_dat[i].status.inventory[j].equip) - char_dat[i].status.inventory[j].equip = 0; - } - char_dat[i].status.weapon = 0; - char_dat[i].status.shield = 0; - char_dat[i].status.head_top = 0; - char_dat[i].status.head_mid = 0; - char_dat[i].status.head_bottom = 0; - - if (char_dat[i].status.guild_id) //If there is a guild, update the guild_member data [Skotlex] - inter_guild_sex_changed(char_dat[i].status.guild_id, acc, char_dat[i].status.char_id, sex); - } - } - // disconnect player if online on char-server - disconnect_player(acc); - } - WBUFW(buf,0) = 0x2b0d; - WBUFL(buf,2) = acc; - WBUFB(buf,6) = sex; - mapif_sendall(buf, 7); - } - break; - - case 0x2726: // Request to send a broadcast message (no answer) - if (RFIFOREST(fd) < 8 || RFIFOREST(fd) < (8 + RFIFOL(fd,4))) - return 0; - if (RFIFOL(fd,4) < 1) - char_log("Receiving a message for broadcast, but message is void." RETCODE); - else { - // at least 1 map-server - for(i = 0; i < MAX_MAP_SERVERS; i++) - if (server_fd[i] >= 0) - break; - if (i == MAX_MAP_SERVERS) - char_log("'ladmin': Receiving a message for broadcast, but no map-server is online." RETCODE); - else { - unsigned char buf[128]; - char message[4096]; // +1 to add a null terminated if not exist in the packet - int lp; - char *p; - memset(message, '\0', sizeof(message)); - memcpy(message, RFIFOP(fd,8), RFIFOL(fd,4)); - message[sizeof(message)-1] = '\0'; - remove_control_chars((unsigned char *)message); - // remove all first spaces - p = message; - while(p[0] == ' ') - p++; - // if message is only composed of spaces - if (p[0] == '\0') - char_log("Receiving a message for broadcast, but message is only a lot of spaces." RETCODE); - // else send message to all map-servers - else { - if (RFIFOW(fd,2) == 0) { - char_log("'ladmin': Receiving a message for broadcast (message (in yellow): %s)" RETCODE, - message); - lp = 4; - } else { - char_log("'ladmin': Receiving a message for broadcast (message (in blue): %s)" RETCODE, - message); - lp = 8; - } - // split message to max 80 char - while(p[0] != '\0') { // if not finish - if (p[0] == ' ') // jump if first char is a space - p++; - else { - char split[80]; - char* last_space; - sscanf(p, "%79[^\t]", split); // max 79 char, any char (\t is control char and control char was removed before) - split[sizeof(split)-1] = '\0'; // last char always \0 - if ((last_space = strrchr(split, ' ')) != NULL) { // searching space from end of the string - last_space[0] = '\0'; // replace it by NULL to have correct length of split - p++; // to jump the new NULL - } - p += strlen(split); - // send broadcast to all map-servers - WBUFW(buf,0) = 0x3800; - WBUFW(buf,2) = lp + strlen(split) + 1; - WBUFL(buf,4) = 0x65756c62; // only write if in blue (lp = 8) - memcpy(WBUFP(buf,lp), split, strlen(split) + 1); - mapif_sendall(buf, WBUFW(buf,2)); - } - } - } - } - } - RFIFOSKIP(fd,8 + RFIFOL(fd,4)); - break; - - // account_reg2変更通知 - case 0x2729: - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { //Receive account_reg2 registry, forward to map servers. - unsigned char buf[ACCOUNT_REG2_NUM*(256+32+2)+16]; - memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2)); -// WBUFW(buf,0) = 0x2b11; - WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex] - mapif_sendall(buf, WBUFW(buf,2)); - RFIFOSKIP(fd, RFIFOW(fd,2)); - } - break; - - // Account deletion notification (from login-server) - case 0x2730: - if (RFIFOREST(fd) < 6) - return 0; - // Deletion of all characters of the account - for(i = 0; i < char_num; i++) { - if (char_dat[i].status.account_id == RFIFOL(fd,2)) { - char_delete(&char_dat[i].status); - if (i < char_num - 1) { - memcpy(&char_dat[i], &char_dat[char_num-1], sizeof(struct character_data)); - // if moved character owns to deleted account, check again it's character - if (char_dat[i].status.account_id == RFIFOL(fd,2)) { - i--; - // Correct moved character reference in the character's owner by [Yor] - } else { - int j, k; - struct char_session_data *sd2; - for (j = 0; j < fd_max; j++) { - if (session[j] && (sd2 = (struct char_session_data*)session[j]->session_data) && - sd2->account_id == char_dat[char_num-1].status.account_id) { - for (k = 0; k < 9; k++) { - if (sd2->found_char[k] == char_num-1) { - sd2->found_char[k] = i; - break; - } - } - break; - } - } - } - } - char_num--; - } - } - // Deletion of the storage - inter_storage_delete(RFIFOL(fd,2)); - // send to all map-servers to disconnect the player - { - unsigned char buf[6]; - WBUFW(buf,0) = 0x2b13; - WBUFL(buf,2) = RFIFOL(fd,2); - mapif_sendall(buf, 6); - } - // disconnect player if online on char-server - disconnect_player(RFIFOL(fd,2)); - RFIFOSKIP(fd,6); - break; - - // State change of account/ban notification (from login-server) by [Yor] - case 0x2731: - if (RFIFOREST(fd) < 11) - return 0; - // send to all map-servers to disconnect the player - { - unsigned char buf[11]; - WBUFW(buf,0) = 0x2b14; - WBUFL(buf,2) = RFIFOL(fd,2); - WBUFB(buf,6) = RFIFOB(fd,6); // 0: change of statut, 1: ban - WBUFL(buf,7) = RFIFOL(fd,7); // status or final date of a banishment - mapif_sendall(buf, 11); - } - // disconnect player if online on char-server - disconnect_player(RFIFOL(fd,2)); - RFIFOSKIP(fd,11); - break; - - // Receiving GM acounts info from login-server (by [Yor]) - case 0x2732: - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { - unsigned char buf[32000]; - if (gm_account != NULL) - aFree(gm_account); - gm_account = (struct gm_account*)aCalloc(sizeof(struct gm_account) * ((RFIFOW(fd,2) - 4) / 5), 1); - GM_num = 0; - for (i = 4; i < RFIFOW(fd,2); i = i + 5) { - gm_account[GM_num].account_id = RFIFOL(fd,i); - gm_account[GM_num].level = (int)RFIFOB(fd,i+4); - //printf("GM account: %d -> level %d\n", gm_account[GM_num].account_id, gm_account[GM_num].level); - GM_num++; - } - ShowStatus("From login-server: receiving information of %d GM accounts.\n", GM_num); - char_log("From login-server: receiving information of %d GM accounts." RETCODE, GM_num); - create_online_files(); // update online players files (perhaps some online players change of GM level) - // send new gm acccounts level to map-servers - memcpy(buf, RFIFOP(fd,0), RFIFOW(fd,2)); - WBUFW(buf,0) = 0x2b15; - mapif_sendall(buf, RFIFOW(fd,2)); - } - RFIFOSKIP(fd,RFIFOW(fd,2)); - break; - - // Receive GM accounts [Freya login server packet by Yor] - case 0x2733: - // add test here to remember that the login-server is Freya-type - // sprintf (login_server_type, "Freya"); - if (RFIFOREST(fd) < 7) - return 0; - { - unsigned char buf[32000]; - int new_level = 0; - for(i = 0; i < GM_num; i++) - if (gm_account[i].account_id == RFIFOL(fd,2)) { - if (gm_account[i].level != (int)RFIFOB(fd,6)) { - gm_account[i].level = (int)RFIFOB(fd,6); - new_level = 1; - } - break; - } - // if not found, add it - if (i == GM_num) { - // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???) - // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) - if (((int)RFIFOB(fd,6)) > 0 && GM_num < 4000) { - if (GM_num == 0) { - gm_account = (struct gm_account*)aMalloc(sizeof(struct gm_account)); - } else { - gm_account = (struct gm_account*)aRealloc(gm_account, sizeof(struct gm_account) * (GM_num + 1)); - } - gm_account[GM_num].account_id = RFIFOL(fd,2); - gm_account[GM_num].level = (int)RFIFOB(fd,6); - new_level = 1; - GM_num++; - if (GM_num >= 4000) { - ShowWarning("4000 GM accounts found. Next GM accounts are not readed.\n"); - char_log("***WARNING: 4000 GM accounts found. Next GM accounts are not readed." RETCODE); - } - } - } - if (new_level == 1) { - int len; - ShowStatus("From login-server: receiving GM account information (%d: level %d).\n", RFIFOL(fd,2), (int)RFIFOB(fd,6)); - char_log("From login-server: receiving a GM account information (%d: level %d)." RETCODE, RFIFOL(fd,2), (int)RFIFOB(fd,6)); - //create_online_files(); // not change online file for only 1 player (in next timer, that will be done - // send gm acccounts level to map-servers - len = 4; - WBUFW(buf,0) = 0x2b15; - - for(i = 0; i < GM_num; i++) { - WBUFL(buf, len) = gm_account[i].account_id; - WBUFB(buf, len+4) = (unsigned char)gm_account[i].level; - len += 5; - } - WBUFW(buf, 2) = len; - mapif_sendall(buf, len); - } - } - RFIFOSKIP(fd,7); - break; - - //Login server request to kick a character out. [Skotlex] - case 0x2734: - if (RFIFOREST(fd) < 6) - return 0; - { - struct online_char_data* character; - int aid = RFIFOL(fd,2); - if ((character = idb_get(online_char_db, aid)) != NULL) - { //Kick out this player. - if (character->server > -1) - { //Kick it from the map server it is on. - mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); - if (!character->waiting_disconnect) - add_timer(gettick()+15000, chardb_waiting_disconnect, character->account_id, 0); - character->waiting_disconnect = 1; - } else { //Manual kick from char server. - struct char_session_data *tsd; - int i; - for(i = 0; i < fd_max; i++) { - if (session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid) - { - WFIFOHEAD(i, 3); - WFIFOW(i,0) = 0x81; - WFIFOB(i,2) = 2; - WFIFOSET(i,3); - break; - } - } - if (i == fd_max) //Shouldn't happen, but just in case. - set_char_offline(99, aid); - } - } - RFIFOSKIP(fd,6); - } - break; - case 0x2735: - { - unsigned char buf[2]; - in_addr_t new_ip = 0; - RFIFOSKIP(fd,2); - - WBUFW(buf,0) = 0x2b1e; - mapif_sendall(buf, 2); - - new_ip = resolve_hostbyname(login_ip_str, NULL, NULL); - if (new_ip && new_ip != login_ip) - login_ip = new_ip; //Update login up. - - new_ip = resolve_hostbyname(char_ip_str, NULL, NULL); - if (new_ip && new_ip != char_ip) - { //Update ip. - WFIFOHEAD(fd,6); - char_ip = new_ip; - ShowInfo("Updating IP for [%s].\n",char_ip_str); - WFIFOW(fd,0) = 0x2736; - WFIFOL(fd,2) = char_ip; - WFIFOSET(fd,6); - } - break; - } - default: - ShowWarning("Unknown packet 0x%04x received from login-server, disconnecting.\n", RFIFOW(fd,0)); - session[fd]->eof = 1; - return 0; - } - } - RFIFOFLUSH(fd); - - return 0; -} - -int request_accreg2(int account_id, int char_id) { - if (login_fd > 0) { - WFIFOHEAD(login_fd, 10); - WFIFOW(login_fd, 0) = 0x272e; - WFIFOL(login_fd, 2) = account_id; - WFIFOL(login_fd, 6) = char_id; - WFIFOSET(login_fd, 10); - return 1; - } - return 0; -} - -//Send packet forward to login-server for account saving -int save_accreg2(unsigned char* buf, int len) { - if (login_fd > 0) { - WFIFOHEAD(login_fd, len+4); - memcpy(WFIFOP(login_fd,4), buf, len); - WFIFOW(login_fd,0) = 0x2728; - WFIFOW(login_fd,2) = len+4; - WFIFOSET(login_fd,len+4); - return 1; - } - return 0; -} - -//Receive Registry information for a character. -int char_parse_Registry(int account_id, int char_id, unsigned char *buf, int buf_len) { - int i,j,p,len; - for (i = 0; i < char_num; i++) { - if (char_dat[i].status.account_id == account_id && char_dat[i].status.char_id == char_id) - break; - } - if(i >= char_num) //Character not found? - return 1; - for(j=0,p=0;j= char_num){ //Character not found? Sent empty packet. - WFIFOW(fd,2)=13; - }else{ - for (p=13,j = 0; j < char_dat[i].global_num; j++) { - if (char_dat[i].global[j].str[0]) { - p+= sprintf(WFIFOP(fd,p), "%s", char_dat[i].global[j].str)+1; //We add 1 to consider the '\0' in place. - p+= sprintf(WFIFOP(fd,p), "%s", char_dat[i].global[j].value)+1; - } - } - WFIFOW(fd,2)=p; - } - WFIFOSET(fd,WFIFOW(fd,2)); - return 0; -} - -void char_read_fame_list(void) -{ - int i, j, k; - struct fame_list fame_item; - CREATE_BUFFER(id, int, char_num); - - for(i = 0; i < char_num; i++) { - id[i] = i; - for(j = 0; j < i; j++) { - if (char_dat[i].status.fame > char_dat[id[j]].status.fame) { - for(k = i; k > j; k--) - id[k] = id[k-1]; - id[j] = i; // id[i] - break; - } - } - } - - // Empty ranking lists - memset(smith_fame_list, 0, sizeof(smith_fame_list)); - memset(chemist_fame_list, 0, sizeof(chemist_fame_list)); - memset(taekwon_fame_list, 0, sizeof(taekwon_fame_list)); - // Build Blacksmith ranking list - for (i = 0, j = 0; i < char_num && j < fame_list_size_smith; i++) { - if (char_dat[id[i]].status.fame && ( - char_dat[id[i]].status.class_ == JOB_BLACKSMITH || - char_dat[id[i]].status.class_ == JOB_WHITESMITH || - char_dat[id[i]].status.class_ == JOB_BABY_BLACKSMITH)) - { - fame_item.id = char_dat[id[i]].status.char_id; - fame_item.fame = char_dat[id[i]].status.fame; - strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); - - memcpy(&smith_fame_list[j],&fame_item,sizeof(struct fame_list)); - j++; - } - } - // Build Alchemist ranking list - for (i = 0, j = 0; i < char_num && j < fame_list_size_chemist; i++) { - if (char_dat[id[i]].status.fame && ( - char_dat[id[i]].status.class_ == JOB_ALCHEMIST || - char_dat[id[i]].status.class_ == JOB_CREATOR || - char_dat[id[i]].status.class_ == JOB_BABY_ALCHEMIST)) - { - fame_item.id = char_dat[id[i]].status.char_id; - fame_item.fame = char_dat[id[i]].status.fame; - strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); - - memcpy(&chemist_fame_list[j],&fame_item,sizeof(struct fame_list)); - - j++; - } - } - // Build Taekwon ranking list - for (i = 0, j = 0; i < char_num && j < fame_list_size_taekwon; i++) { - if (char_dat[id[i]].status.fame && - char_dat[id[i]].status.class_ == JOB_TAEKWON) - { - fame_item.id = char_dat[id[i]].status.char_id; - fame_item.fame = char_dat[id[i]].status.fame; - strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); - - memcpy(&taekwon_fame_list[j],&fame_item,sizeof(struct fame_list)); - - j++; - } - } - DELETE_BUFFER(id); -} -// Send map-servers the fame ranking lists -int char_send_fame_list(int fd) { - int i, len = 8; - unsigned char buf[32000]; - - WBUFW(buf,0) = 0x2b1b; - - for(i = 0; i < fame_list_size_smith && smith_fame_list[i].id; i++) { - memcpy(WBUFP(buf, len), &smith_fame_list[i], sizeof(struct fame_list)); - len += sizeof(struct fame_list); - } - // add blacksmith's block length - WBUFW(buf, 6) = len; - - for(i = 0; i < fame_list_size_chemist && chemist_fame_list[i].id; i++) { - memcpy(WBUFP(buf, len), &chemist_fame_list[i], sizeof(struct fame_list)); - len += sizeof(struct fame_list); - } - // add alchemist's block length - WBUFW(buf, 4) = len; - - for(i = 0; i < fame_list_size_taekwon && taekwon_fame_list[i].id; i++) { - memcpy(WBUFP(buf, len), &taekwon_fame_list[i], sizeof(struct fame_list)); - len += sizeof(struct fame_list); - } - // add total packet length - WBUFW(buf, 2) = len; - - if(fd!=-1) - mapif_send(fd, buf, len); - else - mapif_sendall(buf, len); - - return 0; -} - -int search_mapserver(unsigned short map, long ip, short port); - -int parse_frommap(int fd) { - int i, j; - int id; - RFIFOHEAD(fd); - - for(id = 0; id < MAX_MAP_SERVERS; id++) - if (server_fd[id] == fd) - break; - if(id==MAX_MAP_SERVERS) - session[fd]->eof=1; - if(session[fd]->eof){ - if (id < MAX_MAP_SERVERS) { - unsigned char buf[16384]; - ShowStatus("Map-server %d has disconnected.\n", id); - //Notify other map servers that this one is gone. [Skotlex] - WBUFW(buf,0) = 0x2b20; - WBUFL(buf,4) = server[id].ip; - WBUFW(buf,8) = server[id].port; - j = 0; - for(i = 0; i < MAX_MAP_PER_SERVER; i++) - if (server[id].map[i]) - WBUFW(buf,10+(j++)*4) = server[id].map[i]; - if (j > 0) { - WBUFW(buf,2) = j * 4 + 10; - mapif_sendallwos(fd, buf, WBUFW(buf,2)); - } - server_fd[id] = -1; - online_char_db->foreach(online_char_db,char_db_setoffline,i); //Tag relevant chars as 'in disconnected' server. - } - do_close(fd); - create_online_files(); - return 0; - } - - while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { - //ShowDebug("Received packet 0x%4x (%d bytes) from map-server (connection %d)\n", RFIFOW(fd, 0), RFIFOREST(fd), fd); - - switch(RFIFOW(fd,0)) { - - // map-server alive packet - case 0x2718: - if (RFIFOREST(fd) < 2) - return 0; - RFIFOSKIP(fd,2); - break; - - // request from map-server to reload GM accounts. Transmission to login-server (by Yor) - case 0x2af7: - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 2); - WFIFOW(login_fd,0) = 0x2709; - WFIFOSET(login_fd, 2); - } - RFIFOSKIP(fd,2); - break; - - // Receiving map names list from the map-server - case 0x2afa: - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - memset(server[id].map, 0, sizeof(server[id].map)); - j = 0; - for(i = 4; i < RFIFOW(fd,2); i += 4) { - server[id].map[j] = RFIFOW(fd,i); - j++; - } - { - unsigned char *p = (unsigned char *)&server[id].ip; - ShowStatus("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n", - id, j, p[0], p[1], p[2], p[3], server[id].port); - ShowStatus("Map-server %d loading complete.\n", id); - char_log("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d. Map-server %d loading complete." RETCODE, - id, j, p[0], p[1], p[2], p[3], server[id].port, id); - - if (max_account_id != DEFAULT_MAX_ACCOUNT_ID || max_char_id != DEFAULT_MAX_CHAR_ID) - mapif_send_maxid(max_account_id, max_char_id); //Send the current max ids to the server to keep in sync [Skotlex] - } - WFIFOHEAD(fd, 3 + NAME_LENGTH); - WFIFOW(fd,0) = 0x2afb; - WFIFOB(fd,2) = 0; - memcpy(WFIFOP(fd,3), wisp_server_name, NAME_LENGTH); // name for wisp to player - WFIFOSET(fd,3+NAME_LENGTH); - //WFIFOSET(fd,27); - char_send_fame_list(fd); //Send fame list. - { - unsigned char buf[16384]; - int x; - if (j == 0) { - ShowWarning("Map-Server %d have NO map.\n", id); - char_log("WARNING: Map-Server %d have NO map." RETCODE, id); - // Transmitting maps information to the other map-servers - } else { - WBUFW(buf,0) = 0x2b04; - WBUFW(buf,2) = j * 4 + 10; - WBUFL(buf,4) = server[id].ip; - WBUFW(buf,8) = server[id].port; - memcpy(WBUFP(buf,10), RFIFOP(fd,4), j * 4); - mapif_sendallwos(fd, buf, WBUFW(buf,2)); - } - // Transmitting the maps of the other map-servers to the new map-server - for(x = 0; x < MAX_MAP_SERVERS; x++) { - if (server_fd[x] >= 0 && x != id) { - WFIFOW(fd,0) = 0x2b04; - WFIFOL(fd,4) = server[x].ip; - WFIFOW(fd,8) = server[x].port; - j = 0; - for(i = 0; i < MAX_MAP_PER_SERVER; i++) - if (server[x].map[i]) - WFIFOW(fd,10+(j++)*4) = server[x].map[i]; - if (j > 0) { - WFIFOW(fd,2) = j * 4 + 10; - WFIFOSET(fd,WFIFOW(fd,2)); - } - } - } - } - RFIFOSKIP(fd,RFIFOW(fd,2)); - break; - - //Packet command is now used for sc_data request. [Skotlex] - case 0x2afc: - if (RFIFOREST(fd) < 10) - return 0; - { -#ifdef ENABLE_SC_SAVING - int aid, cid; - struct scdata *data; - aid = RFIFOL(fd,2); - cid = RFIFOL(fd,6); -#endif - RFIFOSKIP(fd, 10); -#ifdef ENABLE_SC_SAVING - data = status_search_scdata(aid, cid); - if (data->count > 0) - { //Deliver status change data. - WFIFOW(fd,0) = 0x2b1d; - WFIFOW(fd,2) = 14 + data->count*sizeof(struct status_change_data); - WFIFOL(fd,4) = aid; - WFIFOL(fd,8) = cid; - WFIFOW(fd,12) = data->count; - for (i = 0; i < data->count; i++) - memcpy(WFIFOP(fd,14+i*sizeof(struct status_change_data)), &data->data[i], sizeof(struct status_change_data)); - WFIFOSET(fd, WFIFOW(fd,2)); - status_delete_scdata(aid, cid); //Data sent, so it needs be discarded now. - } -#endif - break; - } - - //set MAP user count - case 0x2afe: - if (RFIFOREST(fd) < 4) - return 0; - if (RFIFOW(fd,2) != server[id].users) { - server[id].users = RFIFOW(fd,2); - ShowInfo("User Count: %d (Server: %d)\n", server[id].users, id); - } - RFIFOSKIP(fd, 4); - break; - //set MAP users - case 0x2aff: - if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - //TODO: When data mismatches memory, update guild/party online/offline states. - server[id].users = RFIFOW(fd,4); - // add online players in the list by [Yor], adapted to use dbs by [Skotlex] - j = 0; - online_char_db->foreach(online_char_db,char_db_setoffline,id); //Set all chars from this server as 'unknown' - for(i = 0; i < server[id].users; i++) { - int aid, cid; - struct online_char_data* character; - aid = RFIFOL(fd,6+i*8); - cid = RFIFOL(fd,6+i*8+4); - character = idb_ensure(online_char_db, aid, create_online_char_data); - if (online_check && character->server > -1 && character->server != id) - { - ShowNotice("Set map user: Character (%d:%d) marked on map server %d, but map server %d claims to have (%d:%d) online!\n", - character->account_id, character->char_id, character->server, id, aid, cid); - mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); - } - character->char_id = cid; - character->server = id; - } - if (update_online < time(NULL)) { // Time is done - update_online = time(NULL) + 8; - create_online_files(); // only every 8 sec. (normally, 1 server send users every 5 sec.) Don't update every time, because that takes time, but only every 2 connection. - // it set to 8 sec because is more than 5 (sec) and if we have more than 1 map-server, informations can be received in shifted. - } - //If any chars remain in -2, they will be cleaned in the cleanup timer. - RFIFOSKIP(fd,6+i*8); - break; - - // キャラデータ保存 - // Recieve character data from map-server - case 0x2b01: - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - for(i = 0; i < char_num; i++) { - if (char_dat[i].status.account_id == RFIFOL(fd,4) && - char_dat[i].status.char_id == RFIFOL(fd,8)) - break; - } - if (i != char_num) - memcpy(&char_dat[i].status, RFIFOP(fd,13), sizeof(struct mmo_charstatus)); - if (RFIFOB(fd,12)) - { //Flag, set character offline. [Skotlex] - set_char_offline(RFIFOL(fd,8),RFIFOL(fd,4)); - WFIFOW(fd, 0) = 0x2b21; //Save ack only needed on final save. - WFIFOL(fd, 2) = RFIFOL(fd,4); - WFIFOL(fd, 6) = RFIFOL(fd,8); - WFIFOSET(fd, 10); - } - RFIFOSKIP(fd,RFIFOW(fd,2)); - break; - - // キャラセレ要求 - case 0x2b02: - if (RFIFOREST(fd) < 18) - return 0; - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - auth_fifo[auth_fifo_pos].account_id = RFIFOL(fd,2); - auth_fifo[auth_fifo_pos].char_id = 0; - auth_fifo[auth_fifo_pos].login_id1 = RFIFOL(fd,6); - auth_fifo[auth_fifo_pos].login_id2 = RFIFOL(fd,10); - auth_fifo[auth_fifo_pos].delflag = 2; - auth_fifo[auth_fifo_pos].char_pos = 0; - auth_fifo[auth_fifo_pos].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) - auth_fifo[auth_fifo_pos].ip = RFIFOL(fd,14); - auth_fifo_pos++; - WFIFOW(fd,0) = 0x2b03; - WFIFOL(fd,2) = RFIFOL(fd,2); - WFIFOB(fd,6) = 0; - WFIFOSET(fd,7); - RFIFOSKIP(fd,18); - break; - - // request "change map server" - case 0x2b05: - if (RFIFOREST(fd) < 35) - return 0; - { - unsigned short name; - int map_id, map_fd = -1; - struct online_char_data* data; - struct mmo_charstatus* char_data; - - name = RFIFOW(fd,18); - map_id = search_mapserver(name, RFIFOL(fd,24), RFIFOW(fd,28)); //Locate mapserver by ip and port. - if (map_id >= 0) - map_fd = server_fd[map_id]; - for(i = 0; i < char_num; i++) { - if (char_dat[i].status.account_id == RFIFOL(fd,2) && - char_dat[i].status.char_id == RFIFOL(fd,14)) - break; - } - char_data = i< char_num? &char_dat[i].status:NULL; - //Tell the new map server about this player using Kevin's new auth packet. [Skotlex] - if (map_fd>=0 && session[map_fd] && char_data) - { //Send the map server the auth of this player. - //Update the "last map" as this is where the player must be spawned on the new map server. - WFIFOHEAD(map_fd, 20 + sizeof(struct mmo_charstatus)); - char_data->last_point.map = RFIFOW(fd,18); - char_data->last_point.x = RFIFOW(fd,20); - char_data->last_point.y = RFIFOW(fd,22); - char_data->sex = RFIFOB(fd,30); // Buuyo^ - - WFIFOW(map_fd,0) = 0x2afd; - WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus); - WFIFOL(map_fd,4) = RFIFOL(fd, 2); //Account ID - WFIFOL(map_fd,8) = RFIFOL(fd, 6); //Login1 - WFIFOL(map_fd,16) = RFIFOL(fd,10); //Login2 - WFIFOL(map_fd,12) = (unsigned long)0; //TODO: connect_until_time, how do I figure it out right now? - memcpy(WFIFOP(map_fd,20), char_data, sizeof(struct mmo_charstatus)); - WFIFOSET(map_fd, WFIFOW(map_fd,2)); - data = idb_ensure(online_char_db, RFIFOL(fd, 2), create_online_char_data); - data->char_id = char_data->char_id; - data->server = map_id; //Update server where char is. - - //Reply with an ack. - WFIFOW(fd, 0) = 0x2b06; - memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28); - WFIFOSET(fd, 30); - } else { //Reply with nak - WFIFOW(fd, 0) = 0x2b06; - memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28); - WFIFOL(fd, 6) = 0; //Set login1 to 0. - WFIFOSET(fd, 30); - } - RFIFOSKIP(fd, 35); - } - break; - - // キャラ名検索 - case 0x2b08: - if (RFIFOREST(fd) < 6) - return 0; - for(i = 0; i < char_num; i++) { - if (char_dat[i].status.char_id == RFIFOL(fd,2)) - break; - } - WFIFOW(fd,0) = 0x2b09; - WFIFOL(fd,2) = RFIFOL(fd,2); - if (i != char_num) - memcpy(WFIFOP(fd,6), char_dat[i].status.name, NAME_LENGTH); - else - memcpy(WFIFOP(fd,6), unknown_char_name, NAME_LENGTH); - WFIFOSET(fd,6+NAME_LENGTH); - RFIFOSKIP(fd,6); - break; - - // it is a request to become GM - case 0x2b0a: - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; -// printf("parse_frommap: change gm -> login, account: %d, pass: '%s'.\n", RFIFOL(fd,4), RFIFOP(fd,8)); - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, RFIFOW(fd,2)); - WFIFOW(login_fd,0) = 0x2720; - memcpy(WFIFOP(login_fd,2), RFIFOP(fd,2), RFIFOW(fd,2)-2); - WFIFOSET(login_fd, RFIFOW(fd,2)); - } else { - WFIFOHEAD(fd, 10); - WFIFOW(fd,0) = 0x2b0b; - WFIFOL(fd,2) = RFIFOL(fd,4); - WFIFOL(fd,6) = 0; - WFIFOSET(fd, 10); - } - RFIFOSKIP(fd, RFIFOW(fd,2)); - break; - - // Map server send information to change an email of an account -> login-server - case 0x2b0c: - if (RFIFOREST(fd) < 86) - return 0; - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 86); - memcpy(WFIFOP(login_fd,0), RFIFOP(fd,0), 86); // 0x2722 .L .40B .40B - WFIFOW(login_fd,0) = 0x2722; - WFIFOSET(login_fd, 86); - } - RFIFOSKIP(fd, 86); - break; - - // Map server ask char-server about a character name to do some operations (all operations are transmitted to login-server) - case 0x2b0e: - if (RFIFOREST(fd) < 44) - return 0; - { - char character_name[NAME_LENGTH]; - int acc = RFIFOL(fd,2); // account_id of who ask (-1 if nobody) - memcpy(character_name, RFIFOP(fd,6), NAME_LENGTH-1); - character_name[NAME_LENGTH -1] = '\0'; - // prepare answer - WFIFOW(fd,0) = 0x2b0f; // answer - WFIFOL(fd,2) = acc; // who want do operation - WFIFOW(fd,30) = RFIFOW(fd, 30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5-changesex - // search character - i = search_character_index(character_name); - if (i >= 0) { - memcpy(WFIFOP(fd,6), search_character_name(i), NAME_LENGTH); // put correct name if found - WFIFOW(fd,6+NAME_LENGTH) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - //WFIFOW(fd,32) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - switch(RFIFOW(fd, 30)) { - case 1: // block - if (acc == -1 || isGM(acc) >= isGM(char_dat[i].status.account_id)) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 10); - WFIFOW(login_fd,0) = 0x2724; - WFIFOL(login_fd,2) = char_dat[i].status.account_id; // account value - WFIFOL(login_fd,6) = 5; // status of the account - WFIFOSET(login_fd, 10); -// printf("char : status -> login: account %d, status: %d \n", char_dat[i].account_id, 5); - } else - WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } else - WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 2: // ban - if (acc == -1 || isGM(acc) >= isGM(char_dat[i].status.account_id)) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd,18); - WFIFOW(login_fd, 0) = 0x2725; - WFIFOL(login_fd, 2) = char_dat[i].status.account_id; // account value - WFIFOW(login_fd, 6) = RFIFOW(fd,32); // year - WFIFOW(login_fd, 8) = RFIFOW(fd,34); // month - WFIFOW(login_fd,10) = RFIFOW(fd,36); // day - WFIFOW(login_fd,12) = RFIFOW(fd,38); // hour - WFIFOW(login_fd,14) = RFIFOW(fd,40); // minute - WFIFOW(login_fd,16) = RFIFOW(fd,42); // second - WFIFOSET(login_fd,18); -// printf("char : status -> login: account %d, ban: %dy %dm %dd %dh %dmn %ds\n", -// char_dat[i].account_id, (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), (short)RFIFOW(fd,38), (short)RFIFOW(fd,40), (short)RFIFOW(fd,42)); - } else - WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } else - WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 3: // unblock - if (acc == -1 || isGM(acc) >= isGM(char_dat[i].status.account_id)) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 10); - WFIFOW(login_fd,0) = 0x2724; - WFIFOL(login_fd,2) = char_dat[i].status.account_id; // account value - WFIFOL(login_fd,6) = 0; // status of the account - WFIFOSET(login_fd, 10); -// printf("char : status -> login: account %d, status: %d \n", char_dat[i].account_id, 0); - } else - WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } else - WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 4: // unban - if (acc == -1 || isGM(acc) >= isGM(char_dat[i].status.account_id)) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 6); - WFIFOW(login_fd, 0) = 0x272a; - WFIFOL(login_fd, 2) = char_dat[i].status.account_id; // account value - WFIFOSET(login_fd, 6); -// printf("char : status -> login: account %d, unban request\n", char_dat[i].account_id); - } else - WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } else - WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 5: // changesex - if (acc == -1 || isGM(acc) >= isGM(char_dat[i].status.account_id)) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 6); - WFIFOW(login_fd, 0) = 0x2727; - WFIFOL(login_fd, 2) = char_dat[i].status.account_id; // account value - WFIFOSET(login_fd, 6); -// printf("char : status -> login: account %d, change sex request\n", char_dat[i].account_id); - } else - WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } else - WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - } - } else { - // character name not found - memcpy(WFIFOP(fd,6), character_name, NAME_LENGTH); - WFIFOW(fd,8+NAME_LENGTH) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - //WFIFOW(fd,32) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } - // send answer if a player ask, not if the server ask - if (acc != -1) { - //WFIFOSET(fd, 34); - WFIFOSET(fd, 10+NAME_LENGTH); - } - RFIFOSKIP(fd, 44); - break; - } - -// case 0x2b0f: Not used anymore, available for future use - - // Update and send fame ranking list [DracoRPG] - case 0x2b10: - if (RFIFOREST(fd) < 12) - return 0; - { - int cid = RFIFOL(fd, 2); - int fame = RFIFOL(fd, 6); - char type = RFIFOB(fd, 10); - char pos = RFIFOB(fd, 11); - int size; - struct fame_list *list = NULL; - RFIFOSKIP(fd,12); - - switch(type) { - case 1: - size = fame_list_size_smith; - list = smith_fame_list; - break; - case 2: - size = fame_list_size_chemist; - list = chemist_fame_list; - break; - case 3: - size = fame_list_size_taekwon; - list = taekwon_fame_list; - break; - default: - size = 0; - break; - } - if(!size) - break; - if(pos) - { - pos--; //Convert from pos to index. - if( - (pos == 0 || fame < list[pos-1].fame) && - (pos == size-1 || fame > list[pos+1].fame) - ) { //No change in order. - list[(int)pos].fame = fame; - char_send_fame_list(fd); - break; - } - // If the player's already in the list, remove the entry and shift the following ones 1 step up - memmove(list+pos, list+pos+1, (size-pos-1) * sizeof(struct fame_list)); - //Clear out last entry. - list[size-1].id = 0; - list[size-1].fame = 0; - } - // Find the position where the player has to be inserted - for(i = 0; i < size && fame < list[i].fame; i++); - // When found someone with less or as much fame, insert just above - if(i >= size) break;//Out of ranking. - memmove(list+i+1, list+i, (size-i-1) * sizeof(struct fame_list)); - list[i].id = cid; - list[i].fame = fame; - // Look for the player's name - for(j = 0; j < char_num && char_dat[j].status.char_id != id; j++); - if(j < char_num) - strncpy(list[i].name, char_dat[j].status.name, NAME_LENGTH); - else //Not found?? - strncpy(list[i].name, "Unknown", NAME_LENGTH); - char_send_fame_list(-1); - } - break; - - // Recieve rates [Wizputer] - case 0x2b16: - if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,8)) - return 0; - // Txt doesn't need this packet, so just skip it - RFIFOSKIP(fd,RFIFOW(fd,8)); - break; - - // Character disconnected set online 0 [Wizputer] - case 0x2b17: - if (RFIFOREST(fd) < 6) - return 0; - set_char_offline(RFIFOL(fd,2),RFIFOL(fd,6)); - RFIFOSKIP(fd,10); - break; - - // Reset all chars to offline [Wizputer] - case 0x2b18: - set_all_offline(id); - RFIFOSKIP(fd,2); - break; - - // Character set online [Wizputer] - case 0x2b19: - if (RFIFOREST(fd) < 6) - return 0; - set_char_online(id, RFIFOL(fd,2),RFIFOL(fd,6)); - RFIFOSKIP(fd,10); - break; - - // Build and send fame ranking lists [DracoRPG] - case 0x2b1a: - if (RFIFOREST(fd) < 2) - return 0; - char_read_fame_list(); - char_send_fame_list(-1); - RFIFOSKIP(fd,2); - break; - - //Request to save status change data. [Skotlex] - case 0x2b1c: - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { -#ifdef ENABLE_SC_SAVING - int count, aid, cid; - struct scdata *data; - aid = RFIFOL(fd, 4); - cid = RFIFOL(fd, 8); - count = RFIFOW(fd, 12); - data = status_search_scdata(aid, cid); - if (data->count != count) - { - data->count = count; - data->data = aRealloc(data->data, count*sizeof(struct status_change_data)); - } - for (i = 0; i < count; i++) - memcpy (&data->data[i], RFIFOP(fd, 14+i*sizeof(struct status_change_data)), sizeof(struct status_change_data)); -#endif - RFIFOSKIP(fd, RFIFOW(fd, 2)); - break; - } - case 0x2736: - if (RFIFOREST(fd) < 6) return 0; - ShowInfo("Updated IP address of Server #%d to %d.%d.%d.%d.\n",id, - (int)RFIFOB(fd,2),(int)RFIFOB(fd,3), - (int)RFIFOB(fd,4),(int)RFIFOB(fd,5)); - server[id].ip = RFIFOL(fd, 2); - RFIFOSKIP(fd,6); - break; - default: - // inter server処理に渡す - { - int r = inter_parse_frommap(fd); - if (r == 1) // 処理できた - break; - if (r == 2) // パケット長が足りない - return 0; - } - // inter server処理でもない場合は切断 - ShowError("Unknown packet 0x%04x from map server, disconnecting.\n", RFIFOW(fd,0)); - session[fd]->eof = 1; - return 0; - } - } - return 0; -} - -int search_mapserver(unsigned short map, long ip, short port) { - int i, j; - - for(i = 0; i < MAX_MAP_SERVERS; i++) - if (server_fd[i] >= 0) - for (j = 0; server[i].map[j]; j++) - if (server[i].map[j] == map) { - if (ip > 0 && server[i].ip != ip) - continue; - if (port > 0 && server[i].port != port) - continue; - return i; - } - - return -1; -} - -// char_mapifの初期化処理(現在はinter_mapif初期化のみ) -static int char_mapif_init(int fd) { - return inter_mapif_init(fd); -} - -//-------------------------------------------- -// Test to know if an IP come from LAN or WAN. -// Rewrote: Adnvanced subnet check [LuzZza] -//-------------------------------------------- -int lan_subnetcheck(long *p) { - - int i; - unsigned char *sbn, *msk, *src = (unsigned char *)p; - - for(i=0; iclient_addr.sin_addr; - long subnet_map_ip; - - RFIFOHEAD(fd); - - sd = (struct char_session_data*)session[fd]->session_data; - - if (login_fd < 0) - session[fd]->eof = 1; - if(session[fd]->eof) { // disconnect any player (already connected to char-server or coming back from map-server) if login-server is diconnected. - if (fd == login_fd) - login_fd = -1; - if (sd != NULL) - { - struct online_char_data* data = idb_get(online_char_db, sd->account_id); - if (!data || data->server== -1) //If it is not in any server, send it offline. [Skotlex] - set_char_offline(99,sd->account_id); - } - do_close(fd); - return 0; - } - - while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { - cmd = RFIFOW(fd,0); - // crc32のスキップ用 - if( sd==NULL && // 未ログインor管理パケット - RFIFOREST(fd)>=4 && // 最低バイト数制限 & 0x7530,0x7532管理パケ除去 - RFIFOREST(fd)<=21 && // 最大バイト数制限 & サーバーログイン除去 - cmd!=0x20b && // md5通知パケット除去 - (RFIFOREST(fd)<6 || RFIFOW(fd,4)==0x65) ){ // 次に何かパケットが来てるなら、接続でないとだめ - RFIFOSKIP(fd,4); - cmd = RFIFOW(fd,0); - ShowDebug("parse_char : %d crc32 skipped\n",fd); - if(RFIFOREST(fd)==0) - return 0; - } - -//For use in packets that depend on an sd being present [Skotlex] -#define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL) { RFIFOSKIP(fd,rest); return 0; } } - - switch(cmd){ - case 0x20b: //20040622暗号化ragexe対応 - if (RFIFOREST(fd) < 19) - return 0; - RFIFOSKIP(fd,19); - break; - - case 0x65: // 接続要求 - if (RFIFOREST(fd) < 17) - return 0; - { - int GM_value; - WFIFOHEAD(fd, 4); - if (sd) { - //Received again auth packet for already authentified account?? Discard it. - //TODO: Perhaps log this as a hack attempt? - RFIFOSKIP(fd,17); - break; - } - if ((GM_value = isGM(RFIFOL(fd,2)))) - ShowInfo("Account Logged On; Account ID: %d (GM level %d).\n", RFIFOL(fd,2), GM_value); - else - ShowInfo("Account Logged On; Account ID: %d.\n", RFIFOL(fd,2)); - sd = (struct char_session_data*)aCalloc(sizeof(struct char_session_data), 1); - session[fd]->session_data = sd; - strncpy(sd->email, "no mail", 40); // put here a mail without '@' to refuse deletion if we don't receive the e-mail - sd->connect_until_time = 0; // unknow or illimited (not displaying on map-server) - sd->account_id = RFIFOL(fd,2); - sd->login_id1 = RFIFOL(fd,6); - sd->login_id2 = RFIFOL(fd,10); - sd->sex = RFIFOB(fd,16); - // send back account_id - WFIFOL(fd,0) = RFIFOL(fd,2); - WFIFOSET(fd,4); - // search authentification - for(i = 0; i < AUTH_FIFO_SIZE; i++) { - if (auth_fifo[i].account_id == sd->account_id && - auth_fifo[i].login_id1 == sd->login_id1 && -#if CMP_AUTHFIFO_LOGIN2 != 0 - auth_fifo[i].login_id2 == sd->login_id2 && // relate to the versions higher than 18 -#endif - (!check_ip_flag || auth_fifo[i].ip == session[fd]->client_addr.sin_addr.s_addr) && - auth_fifo[i].delflag == 2) { - auth_fifo[i].delflag = 1; - - if (online_check) - { // check if character is not online already. [Skotlex] - struct online_char_data* character; - character = idb_get(online_char_db, sd->account_id); - - if (character) - { - if(character->server > -1) - { //Kick it from the map server it is on. - mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); - if (!character->waiting_disconnect) - add_timer(gettick()+20000, chardb_waiting_disconnect, character->account_id, 0); - character->waiting_disconnect = 1; - /* Not a good idea because this would trigger when you do a char-change from the map server! [Skotlex] - } else { //Manual kick from char server. - struct char_session_data *tsd; - int i; - for(i = 0; i < fd_max; i++) { - if (session[i] && i!=fd && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == sd->account_id) - { - WFIFOW(i,0) = 0x81; - WFIFOB(i,2) = 2; - WFIFOSET(i,3); - break; - } - } - if (i == fd_max) //Shouldn't happen, but just in case. - set_char_offline(99, sd->account_id); - */ - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 8; - WFIFOSET(fd,3); - break; - } - } - } - - if (max_connect_user == 0 || count_users() < max_connect_user) { - if (login_fd > 0) { // don't send request if no login-server - // request to login-server to obtain e-mail/time limit - WFIFOHEAD(login_fd, 6); - WFIFOW(login_fd,0) = 0x2716; - WFIFOL(login_fd,2) = sd->account_id; - WFIFOSET(login_fd,6); - } - // send characters to player - mmo_char_send006b(fd, sd); - } else { - // refuse connection (over populated) - WFIFOW(fd,0) = 0x6c; - WFIFOW(fd,2) = 0; - WFIFOSET(fd,3); - } - break; - } - } - // authentification not found - if (i == AUTH_FIFO_SIZE) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd,19); - WFIFOW(login_fd,0) = 0x2712; // ask login-server to authentify an account - WFIFOL(login_fd,2) = sd->account_id; - WFIFOL(login_fd,6) = sd->login_id1; - WFIFOL(login_fd,10) = sd->login_id2; // relate to the versions higher than 18 - WFIFOB(login_fd,14) = sd->sex; - WFIFOL(login_fd,15) = session[fd]->client_addr.sin_addr.s_addr; - WFIFOSET(login_fd,19); - } else { // if no login-server, we must refuse connection - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x6c; - WFIFOW(fd,2) = 0; - WFIFOSET(fd,3); - } - } - } - RFIFOSKIP(fd,17); - break; - - case 0x66: // キャラ選択 - FIFOSD_CHECK(3); - { - int char_num = RFIFOB(fd,2); - struct mmo_charstatus *cd; - RFIFOSKIP(fd,3); - - // if we activated email creation and email is default email - if (email_creation != 0 && strcmp(sd->email, "a@a.com") == 0 && login_fd > 0) { // to modify an e-mail, login-server must be online - WFIFOHEAD(fd, 3); - WFIFOW(fd, 0) = 0x70; - WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address - WFIFOSET(fd, 3); - break; - } - // otherwise, load the character - for (ch = 0; ch < 9; ch++) - if (sd->found_char[ch] >= 0 && char_dat[sd->found_char[ch]].status.char_num == char_num) - break; - if (ch == 9) - { //Not found?? May be forged packet. - break; - } - cd = &char_dat[sd->found_char[ch]].status; - char_log("Character Selected, Account ID: %d, Character Slot: %d, Character Name: %s." RETCODE, - sd->account_id, char_num, cd->name); - - cd->sex = sd->sex; - - // searching map server - i = search_mapserver(cd->last_point.map,-1,-1); - // if map is not found, we check major cities - if (i < 0) { - unsigned short j; - //First check that there's actually a map server online. - for(j = 0; j < MAX_MAP_SERVERS; j++) - if (server_fd[j] >= 0 && server[j].map[0]) - break; - if (j == MAX_MAP_SERVERS) { - ShowInfo("Connection Closed. No map servers available.\n"); - WFIFOHEAD(fd, 3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - break; - } - if ((i = search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) { - cd->last_point.x = 273; - cd->last_point.y = 354; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) { - cd->last_point.x = 120; - cd->last_point.y = 100; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) { - cd->last_point.x = 160; - cd->last_point.y = 94; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) { - cd->last_point.x = 116; - cd->last_point.y = 57; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) { - cd->last_point.x = 87; - cd->last_point.y = 117; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) { - cd->last_point.x = 94; - cd->last_point.y = 103; - } else { - ShowInfo("Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", mapindex_id2name(cd->last_point.map)); - WFIFOHEAD(fd, 3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - break; - } - ShowWarning("Unable to find map-server for '%s', sending to major city '%s'.\n", mapindex_id2name(cd->last_point.map), mapindex_id2name(j)); - cd->last_point.map = j; - } - { //Send player to map - WFIFOHEAD(fd, 28); - WFIFOW(fd,0) = 0x71; - WFIFOL(fd,2) = cd->char_id; - memcpy(WFIFOP(fd,6), mapindex_id2name(cd->last_point.map), MAP_NAME_LENGTH); - - // Advanced subnet check [LuzZza] - if((subnet_map_ip = lan_subnetcheck((long *)p))) - WFIFOL(fd,22) = subnet_map_ip; - else - WFIFOL(fd,22) = server[i].ip; - - WFIFOW(fd,26) = server[i].port; - WFIFOSET(fd,28); - - ShowInfo("Character selection '%s' (account: %d, slot: %d).\n", - cd->name, sd->account_id, ch); - } - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - auth_fifo[auth_fifo_pos].account_id = sd->account_id; - auth_fifo[auth_fifo_pos].char_id = cd->char_id; - auth_fifo[auth_fifo_pos].login_id1 = sd->login_id1; - auth_fifo[auth_fifo_pos].login_id2 = sd->login_id2; - auth_fifo[auth_fifo_pos].delflag = 0; - auth_fifo[auth_fifo_pos].char_pos = sd->found_char[ch]; - auth_fifo[auth_fifo_pos].sex = sd->sex; - auth_fifo[auth_fifo_pos].connect_until_time = sd->connect_until_time; - auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr.sin_addr.s_addr; - - //Send NEW auth packet [Kevin] - if ((map_fd = server_fd[i]) < 1 || session[map_fd] == NULL) - { //0 Should not be a valid server_fd [Skotlex] - WFIFOHEAD(fd, 3); - ShowError("parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i); - server_fd[i] = -1; - memset(&server[i], 0, sizeof(struct mmo_map_server)); - //Send server closed. - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - break; - } - { //Send auth to server. - WFIFOHEAD(map_fd, 20 + sizeof(struct mmo_charstatus)); - WFIFOW(map_fd,0) = 0x2afd; - WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus); - WFIFOL(map_fd,4) = auth_fifo[auth_fifo_pos].account_id; - WFIFOL(map_fd,8) = auth_fifo[auth_fifo_pos].login_id1; - WFIFOL(map_fd,16) = auth_fifo[auth_fifo_pos].login_id2; - WFIFOL(map_fd,12) = (unsigned long)auth_fifo[auth_fifo_pos].connect_until_time; - memcpy(WFIFOP(map_fd,20), cd, sizeof(struct mmo_charstatus)); - WFIFOSET(map_fd, WFIFOW(map_fd,2)); - } - set_char_online(i, cd->char_id, cd->account_id); - //Sets char online in the party and breaks even share if needed. - inter_party_logged(cd->party_id, cd->account_id, cd->char_id); - - auth_fifo_pos++; - } - break; - - case 0x67: // 作成 - FIFOSD_CHECK(37); - - if(char_new == 0) //turn character creation on/off [Kevin] - i = -2; - else - i = make_new_char(fd, RFIFOP(fd,2)); - //added some better fail reporting to client on the txt version [Kevin] - if (i < 0) - { - WFIFOHEAD(fd, 3); - WFIFOW(fd, 0) = 0x6e; - switch (i) { - case -1: WFIFOB(fd, 2) = 0x00; break; - case -2: WFIFOB(fd, 2) = 0x02; break; - case -3: WFIFOB(fd, 2) = 0x01; break; - } - WFIFOSET(fd, 3); - RFIFOSKIP(fd, 37); - break; - } - { //Send to player. - WFIFOHEAD(fd, 108); - WFIFOW(fd,0) = 0x6d; - memset(WFIFOP(fd,2), 0, 106); - - WFIFOL(fd,2) = char_dat[i].status.char_id; - WFIFOL(fd,2+4) = char_dat[i].status.base_exp>LONG_MAX?LONG_MAX:char_dat[i].status.base_exp; - WFIFOL(fd,2+8) = char_dat[i].status.zeny; - WFIFOL(fd,2+12) = char_dat[i].status.job_exp>LONG_MAX?LONG_MAX:char_dat[i].status.job_exp; - WFIFOL(fd,2+16) = char_dat[i].status.job_level; - - WFIFOL(fd,2+28) = char_dat[i].status.karma; - WFIFOL(fd,2+32) = char_dat[i].status.manner; - - WFIFOW(fd,2+40) = 0x30; - WFIFOW(fd,2+42) = (char_dat[i].status.hp > SHRT_MAX) ? SHRT_MAX : char_dat[i].status.hp; - WFIFOW(fd,2+44) = (char_dat[i].status.max_hp > SHRT_MAX) ? SHRT_MAX : char_dat[i].status.max_hp; - WFIFOW(fd,2+46) = (char_dat[i].status.sp > SHRT_MAX) ? SHRT_MAX : char_dat[i].status.sp; - WFIFOW(fd,2+48) = (char_dat[i].status.max_sp > SHRT_MAX) ? SHRT_MAX : char_dat[i].status.max_sp; - WFIFOW(fd,2+50) = DEFAULT_WALK_SPEED; // char_dat[i].status.speed; - WFIFOW(fd,2+52) = char_dat[i].status.class_; - WFIFOW(fd,2+54) = char_dat[i].status.hair; - - WFIFOW(fd,2+58) = char_dat[i].status.base_level; - WFIFOW(fd,2+60) = (char_dat[i].status.skill_point > SHRT_MAX) ? SHRT_MAX : char_dat[i].status.skill_point; - - WFIFOW(fd,2+64) = char_dat[i].status.shield; - WFIFOW(fd,2+66) = char_dat[i].status.head_top; - WFIFOW(fd,2+68) = char_dat[i].status.head_mid; - WFIFOW(fd,2+70) = char_dat[i].status.hair_color; - - memcpy(WFIFOP(fd,2+74), char_dat[i].status.name, NAME_LENGTH); - - WFIFOB(fd,2+98) = (char_dat[i].status.str > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.str; - WFIFOB(fd,2+99) = (char_dat[i].status.agi > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.agi; - WFIFOB(fd,2+100) = (char_dat[i].status.vit > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.vit; - WFIFOB(fd,2+101) = (char_dat[i].status.int_ > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.int_; - WFIFOB(fd,2+102) = (char_dat[i].status.dex > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.dex; - WFIFOB(fd,2+103) = (char_dat[i].status.luk > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.luk; - WFIFOB(fd,2+104) = char_dat[i].status.char_num; - - WFIFOSET(fd,108); - RFIFOSKIP(fd,37); - } - for(ch = 0; ch < 9; ch++) { - if (sd->found_char[ch] == -1) { - sd->found_char[ch] = i; - break; - } - } - - case 0x68: // delete char //Yor's Fix - FIFOSD_CHECK(46); - { - WFIFOHEAD(fd, 46); - WFIFOHEAD(login_fd,46); - memcpy(email, RFIFOP(fd,6), 40); - if (e_mail_check(email) == 0) - strncpy(email, "a@a.com", 40); // default e-mail - - // if we activated email creation and email is default email - if (email_creation != 0 && strcmp(sd->email, "a@a.com") == 0 && login_fd > 0) { // to modify an e-mail, login-server must be online - // if sended email is incorrect e-mail - if (strcmp(email, "a@a.com") == 0) { - WFIFOW(fd, 0) = 0x70; - WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address - WFIFOSET(fd, 3); - RFIFOSKIP(fd,46); - // we act like we have selected a character - } else { - // we change the packet to set it like selection. - for (i = 0; i < 9; i++) - if (char_dat[sd->found_char[i]].status.char_id == RFIFOL(fd,2)) { - // we save new e-mail - memcpy(sd->email, email, 40); - // we send new e-mail to login-server ('online' login-server is checked before) - WFIFOW(login_fd,0) = 0x2715; - WFIFOL(login_fd,2) = sd->account_id; - memcpy(WFIFOP(login_fd, 6), email, 40); - WFIFOSET(login_fd,46); - // skip part of the packet! (46, but leave the size of select packet: 3) - RFIFOSKIP(fd,43); - // change value to put new packet (char selection) - RFIFOW(fd, 0) = 0x66; - RFIFOB(fd, 2) = char_dat[sd->found_char[i]].status.char_num; - // not send packet, it's modify of actual packet - break; - } - if (i == 9) { - WFIFOW(fd, 0) = 0x70; - WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address - WFIFOSET(fd, 3); - RFIFOSKIP(fd,46); - } - } - - // otherwise, we delete the character - } else { - if (strcmpi(email, sd->email) != 0) { // if it's an invalid email - WFIFOW(fd, 0) = 0x70; - WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address - WFIFOSET(fd, 3); - // if mail is correct - } else { - for (i = 0; i < 9; i++) { - struct mmo_charstatus *cs = NULL; - if ((cs = &char_dat[sd->found_char[i]].status)->char_id == RFIFOL(fd,2)) { - char_delete(cs); // deletion process - - if (sd->found_char[i] != char_num - 1) { - memcpy(&char_dat[sd->found_char[i]], &char_dat[char_num-1], sizeof(struct mmo_charstatus)); - // Correct moved character reference in the character's owner - { - int j, k; - struct char_session_data *sd2; - for (j = 0; j < fd_max; j++) { - if (session[j] && (sd2 = (struct char_session_data*)session[j]->session_data) && - sd2->account_id == char_dat[char_num-1].status.account_id) { - for (k = 0; k < 9; k++) { - if (sd2->found_char[k] == char_num-1) { - sd2->found_char[k] = sd->found_char[i]; - break; - } - } - break; - } - } - } - } - - char_num--; - for(ch = i; ch < 9-1; ch++) - sd->found_char[ch] = sd->found_char[ch+1]; - sd->found_char[8] = -1; - WFIFOW(fd,0) = 0x6f; - WFIFOSET(fd,2); - break; - } - } - - if (i == 9) { - WFIFOW(fd,0) = 0x70; - WFIFOB(fd,2) = 0; - WFIFOSET(fd,3); - } - } - RFIFOSKIP(fd,46); - } - } - break; - - case 0x2af8: // マップサーバーログイン - if (RFIFOREST(fd) < 60) - return 0; - { - char *l_user = RFIFOP(fd, 2); - char *l_pass = RFIFOP(fd, 26); - WFIFOHEAD(fd, 4+5*GM_num); - l_user[23] = '\0'; - l_pass[23] = '\0'; - WFIFOW(fd,0) = 0x2af9; - for(i = 0; i < MAX_MAP_SERVERS; i++) { - if (server_fd[i] < 0) - break; - } - if (i == MAX_MAP_SERVERS || - strcmp(l_user, userid) || - strcmp(l_pass, passwd)){ - WFIFOB(fd,2) = 3; - WFIFOSET(fd,3); - RFIFOSKIP(fd,60); - } else { - int len; - WFIFOB(fd,2) = 0; - session[fd]->func_parse = parse_frommap; - server_fd[i] = fd; - server[i].ip = RFIFOL(fd,54); - server[i].port = RFIFOW(fd,58); - server[i].users = 0; - memset(server[i].map, 0, sizeof(server[i].map)); - WFIFOSET(fd,3); - RFIFOSKIP(fd,60); - realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); - char_mapif_init(fd); - // send gm acccounts level to map-servers - len = 4; - WFIFOW(fd,0) = 0x2b15; - for(i = 0; i < GM_num; i++) { - WFIFOL(fd,len) = gm_account[i].account_id; - WFIFOB(fd,len+4) = (unsigned char)gm_account[i].level; - len += 5; - } - WFIFOW(fd,2) = len; - WFIFOSET(fd,len); - return 0; - } - } - break; - - case 0x187: // Alive信号? - if (RFIFOREST(fd) < 6) - return 0; - RFIFOSKIP(fd, 6); - break; - - case 0x7530: // Athena情報所得 - { - WFIFOHEAD(fd, 10); - WFIFOW(fd,0) = 0x7531; - WFIFOB(fd,2) = ATHENA_MAJOR_VERSION; - WFIFOB(fd,3) = ATHENA_MINOR_VERSION; - WFIFOB(fd,4) = ATHENA_REVISION; - WFIFOB(fd,5) = ATHENA_RELEASE_FLAG; - WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG; - WFIFOB(fd,7) = ATHENA_SERVER_INTER | ATHENA_SERVER_CHAR; - WFIFOW(fd,8) = ATHENA_MOD_VERSION; - WFIFOSET(fd,10); - RFIFOSKIP(fd,2); - return 0; - } - case 0x7532: // 接続の切断(defaultと処理は一緒だが明示的にするため) - default: - session[fd]->eof = 1; - return 0; - } - } - RFIFOFLUSH(fd); - return 0; -} - -// Console Command Parser [Wizputer] -int parse_console(char *buf) { - char *type,*command; - - type = (char *)aCalloc(64,1); - command = (char *)aCalloc(64,1); - -// memset(type,0,64); -// memset(command,0,64); - - ShowStatus("Console: %s\n",buf); - - if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 ) - sscanf(buf,"%[^\n]",type); - - ShowDebug("Type of command: %s || Command: %s \n",type,command); - - if(buf) aFree(buf); - if(type) aFree(type); - if(command) aFree(command); - - return 0; -} - -// 全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) -int mapif_sendall(unsigned char *buf, unsigned int len) { - int i, c; - - c = 0; - for(i = 0; i < MAX_MAP_SERVERS; i++) { - int fd; - if ((fd = server_fd[i]) >= 0) { -#if 0 //This seems to have been fixed long long ago. - if (session[fd] == NULL) - { //Could this be the crash's source? [Skotlex] - ShowError("mapif_sendall: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", fd, i); - server_fd[i] = -1; - memset(&server[i], 0, sizeof(struct mmo_map_server)); - continue; - } -#endif - WFIFOHEAD(fd, len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - c++; - } - } - return c; -} - -// 自分以外の全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) -int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { - int i, c; - - c = 0; - for(i = 0; i < MAX_MAP_SERVERS; i++) { - int fd; - if ((fd = server_fd[i]) >= 0 && fd != sfd) { - WFIFOHEAD(fd, len); - if (WFIFOSPACE(fd) < len) //Increase buffer size. - realloc_writefifo(fd, len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd, len); - c++; - } - } - return c; -} -// MAPサーバーにデータ送信(map鯖生存確認有り) -int mapif_send(int fd, unsigned char *buf, unsigned int len) { - int i; - - if (fd >= 0) { - for(i = 0; i < MAX_MAP_SERVERS; i++) { - if (fd == server_fd[i]) { - WFIFOHEAD(fd, len); - if (WFIFOSPACE(fd) < len) //Increase buffer size. - realloc_writefifo(fd, len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - return 1; - } - } - } - return 0; -} - -int send_users_tologin(int tid, unsigned int tick, int id, int data) { - int users = count_users(); - unsigned char buf[16]; - - if (login_fd > 0 && session[login_fd]) { - // send number of user to login server - WFIFOHEAD(login_fd, 6); - WFIFOW(login_fd,0) = 0x2714; - WFIFOL(login_fd,2) = users; - WFIFOSET(login_fd,6); - } - // send number of players to all map-servers - WBUFW(buf,0) = 0x2b00; - WBUFL(buf,2) = users; - mapif_sendall(buf, 6); - - return 0; -} - -static int send_accounts_tologin_sub(DBKey key, void* data, va_list ap) { - struct online_char_data* character = (struct online_char_data*)data; - int *i = va_arg(ap, int*); - int count = va_arg(ap, int); - if ((*i) >= count) - return 0; //This is an error that shouldn't happen.... - if(character->server > -1) { - WFIFOHEAD(login_fd, 8+count*4); - WFIFOL(login_fd, 8+(*i)*4) =character->account_id; - (*i)++; - return 1; - } - return 0; -} - -int send_accounts_tologin(int tid, unsigned int tick, int id, int data) { - int users = count_users(), i=0; - - if (login_fd > 0 && session[login_fd]) { - // send account list to login server - WFIFOHEAD(login_fd, 8+users*4); - WFIFOW(login_fd,0) = 0x272d; - WFIFOL(login_fd,4) = users; - online_char_db->foreach(online_char_db, send_accounts_tologin_sub, &i); - WFIFOW(login_fd,2) = 8+ i*4; - WFIFOSET(login_fd,WFIFOW(login_fd,2)); - } - return 0; -} - -int check_connect_login_server(int tid, unsigned int tick, int id, int data) { - if (login_fd > 0 && session[login_fd] != NULL) - return 0; - - ShowInfo("Attempt to connect to login-server...\n"); - login_fd = make_connection(login_ip, login_port); - if (login_fd == -1) - { //Try again later... [Skotlex] - login_fd = 0; - return 0; - } - session[login_fd]->func_parse = parse_tologin; - realloc_fifo(login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); - WFIFOHEAD(login_fd, 86); - WFIFOW(login_fd,0) = 0x2710; - memcpy(WFIFOP(login_fd,2), userid, 24); - memcpy(WFIFOP(login_fd,26), passwd, 24); - WFIFOL(login_fd,50) = 0; - WFIFOL(login_fd,54) = char_ip; - WFIFOL(login_fd,58) = char_port; - memcpy(WFIFOP(login_fd,60), server_name, 20); - WFIFOW(login_fd,80) = 0; - WFIFOW(login_fd,82) = char_maintenance; - - WFIFOW(login_fd,84) = char_new_display; //only display (New) if they want to [Kevin] - - WFIFOSET(login_fd,86); - return 1; -} - -//------------------------------------------------ -//Invoked 15 seconds after mapif_disconnectplayer in case the map server doesn't -//replies/disconnect the player we tried to kick. [Skotlex] -//------------------------------------------------ -static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data) -{ - struct online_char_data* character; - if ((character = idb_get(online_char_db, id)) != NULL && character->waiting_disconnect) - { //Mark it offline due to timeout. - set_char_offline(character->char_id, character->account_id); - } - return 0; -} - -//---------------------------------------------------------- -// Return numerical value of a switch configuration by [Yor] -// on/off, english, fran軋is, deutsch, espaol -//---------------------------------------------------------- -int config_switch(const char *str) { - if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) - return 1; - if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) - return 0; - - return atoi(str); -} - -//---------------------------------- -// Reading Lan Support configuration -// Rewrote: Anvanced subnet check [LuzZza] -//---------------------------------- -int char_lan_config_read(const char *lancfgName) { - - FILE *fp; - int line_num = 0; - char line[1024], w1[64], w2[64], w3[64], w4[64]; - - if((fp = fopen(lancfgName, "r")) == NULL) { - ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName); - return 1; - } - - ShowInfo("Reading the configuration file %s...\n", lancfgName); - - while(fgets(line, sizeof(line)-1, fp)) { - - line_num++; - if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n') - continue; - - line[sizeof(line)-1] = '\0'; - if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4) { - - ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num); - continue; - } - - remove_control_chars((unsigned char *)w1); - remove_control_chars((unsigned char *)w2); - remove_control_chars((unsigned char *)w3); - remove_control_chars((unsigned char *)w4); - - if(strcmpi(w1, "subnet") == 0) { - - subnet[subnet_count].mask = inet_addr(w2); - subnet[subnet_count].char_ip = inet_addr(w3); - subnet[subnet_count].map_ip = inet_addr(w4); - subnet[subnet_count].subnet = subnet[subnet_count].char_ip&subnet[subnet_count].mask; - if (subnet[subnet_count].subnet != (subnet[subnet_count].map_ip&subnet[subnet_count].mask)) { - ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4); - continue; - } - - subnet_count++; - } - - ShowStatus("Read information about %d subnetworks.\n", subnet_count); - } - - fclose(fp); - return 0; -} -#endif //TXT_SQL_CONVERT - -int char_config_read(const char *cfgName) { - char line[1024], w1[1024], w2[1024]; - FILE *fp = fopen(cfgName, "r"); - - if (fp == NULL) { - ShowFatalError("Configuration file not found: %s.\n", cfgName); - exit(1); - } - - ShowInfo("Reading configuration file %s...\n", cfgName); - while(fgets(line, sizeof(line)-1, fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - - line[sizeof(line)-1] = '\0'; - if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) - continue; - - remove_control_chars((unsigned char *)w1); - remove_control_chars((unsigned char *)w2); - if(strcmpi(w1,"timestamp_format") == 0) { - strncpy(timestamp_format, w2, 20); - } else if(strcmpi(w1,"console_silent")==0){ - msg_silent = 0; //To always allow the next line to show up. - ShowInfo("Console Silent Setting: %d\n", atoi(w2)); - msg_silent = atoi(w2); -#ifndef TXT_SQL_CONVERT - } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ - stdout_with_ansisequence = config_switch(w2); - } else if (strcmpi(w1, "userid") == 0) { - strncpy(userid, w2, 24); - } else if (strcmpi(w1, "passwd") == 0) { - strncpy(passwd, w2, 24); - } else if (strcmpi(w1, "server_name") == 0) { - strncpy(server_name, w2, 20); - server_name[sizeof(server_name) - 1] = '\0'; - ShowStatus("%s server has been initialized\n", w2); - } else if (strcmpi(w1, "wisp_server_name") == 0) { - if (strlen(w2) >= 4) { - memcpy(wisp_server_name, w2, sizeof(wisp_server_name)); - wisp_server_name[sizeof(wisp_server_name) - 1] = '\0'; - } - } else if (strcmpi(w1, "login_ip") == 0) { - char ip_str[16]; - login_ip = resolve_hostbyname(w2, NULL, ip_str); - if (login_ip) { - strncpy(login_ip_str, w2, sizeof(login_ip_str)); - ShowStatus("Login server IP address : %s -> %s\n", w2, ip_str); - } - } else if (strcmpi(w1, "login_port") == 0) { - login_port = atoi(w2); - } else if (strcmpi(w1, "char_ip") == 0) { - char ip_str[16]; - char_ip = resolve_hostbyname(w2, NULL, ip_str); - if (char_ip){ - strncpy(char_ip_str, w2, sizeof(char_ip_str)); - ShowStatus("Character server IP address : %s -> %s\n", w2, ip_str); - } - } else if (strcmpi(w1, "bind_ip") == 0) { - char ip_str[16]; - bind_ip = resolve_hostbyname(w2, NULL, ip_str); - if (bind_ip) { - strncpy(bind_ip_str, w2, sizeof(bind_ip_str)); - ShowStatus("Character server binding IP address : %s -> %s\n", w2, ip_str); - } - } else if (strcmpi(w1, "char_port") == 0) { - char_port = atoi(w2); - } else if (strcmpi(w1, "char_maintenance") == 0) { - char_maintenance = atoi(w2); - } else if (strcmpi(w1, "char_new") == 0) { - char_new = atoi(w2); - } else if (strcmpi(w1, "char_new_display") == 0) { - char_new_display = atoi(w2); - } else if (strcmpi(w1, "email_creation") == 0) { - email_creation = config_switch(w2); - } else if (strcmpi(w1, "scdata_txt") == 0) { //By Skotlex - strcpy(scdata_txt, w2); -#endif - } else if (strcmpi(w1, "char_txt") == 0) { - strcpy(char_txt, w2); - } else if (strcmpi(w1, "backup_txt") == 0) { //By zanetheinsane - strcpy(backup_txt, w2); - } else if (strcmpi(w1, "friends_txt") == 0) { //By davidsiaw - strcpy(friends_txt, w2); -#ifndef TXT_SQL_CONVERT - } else if (strcmpi(w1, "backup_txt_flag") == 0) { // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. By [Yor] - backup_txt_flag = config_switch(w2); - } else if (strcmpi(w1, "max_connect_user") == 0) { - max_connect_user = atoi(w2); - if (max_connect_user < 0) - max_connect_user = 0; // unlimited online players - } else if(strcmpi(w1, "gm_allow_level") == 0) { - gm_allow_level = atoi(w2); - if(gm_allow_level < 0) - gm_allow_level = 99; - } else if (strcmpi(w1, "check_ip_flag") == 0) { - check_ip_flag = config_switch(w2); - } else if (strcmpi(w1, "online_check") == 0) { - online_check = config_switch(w2); - } else if (strcmpi(w1, "autosave_time") == 0) { - autosave_interval = atoi(w2)*1000; - if (autosave_interval <= 0) - autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; - } else if (strcmpi(w1, "save_log") == 0) { - save_log = config_switch(w2); - } else if (strcmpi(w1, "start_point") == 0) { - char map[32]; - int x, y; - if (sscanf(w2, "%[^,],%d,%d", map, &x, &y) < 3) - continue; - if (strstr(map, ".gat") != NULL) { // Verify at least if '.gat' is in the map name - start_point.map = mapindex_name2id(map); - if (!start_point.map) { - ShowError("Specified start_point %s not found in map-index cache.\n", map); - start_point.map = 0; - } - start_point.x = x; - start_point.y = y; - } - } else if(strcmpi(w1,"log_char")==0) { //log char or not [devil] - log_char = atoi(w2); - } else if (strcmpi(w1, "start_zeny") == 0) { - start_zeny = atoi(w2); - if (start_zeny < 0) - start_zeny = 0; - } else if (strcmpi(w1, "start_weapon") == 0) { - start_weapon = atoi(w2); - if (start_weapon < 0) - start_weapon = 0; - } else if (strcmpi(w1, "start_armor") == 0) { - start_armor = atoi(w2); - if (start_armor < 0) - start_armor = 0; - } else if (strcmpi(w1, "unknown_char_name") == 0) { - strcpy(unknown_char_name, w2); - unknown_char_name[NAME_LENGTH-1] = '\0'; - } else if (strcmpi(w1, "char_log_filename") == 0) { - strcpy(char_log_filename, w2); - } else if (strcmpi(w1, "name_ignoring_case") == 0) { - name_ignoring_case = config_switch(w2); - } else if (strcmpi(w1, "char_name_option") == 0) { - char_name_option = atoi(w2); - } else if (strcmpi(w1, "char_name_letters") == 0) { - strcpy(char_name_letters, w2); -// online files options - } else if (strcmpi(w1, "online_txt_filename") == 0) { - strcpy(online_txt_filename, w2); - } else if (strcmpi(w1, "online_html_filename") == 0) { - strcpy(online_html_filename, w2); - } else if (strcmpi(w1, "online_sorting_option") == 0) { - online_sorting_option = atoi(w2); - } else if (strcmpi(w1, "online_display_option") == 0) { - online_display_option = atoi(w2); - } else if (strcmpi(w1, "online_gm_display_min_level") == 0) { // minimum GM level to display 'GM' when we want to display it - online_gm_display_min_level = atoi(w2); - if (online_gm_display_min_level < 5) // send online file every 5 seconds to player is enough - online_gm_display_min_level = 5; - } else if (strcmpi(w1, "online_refresh_html") == 0) { - online_refresh_html = atoi(w2); - if (online_refresh_html < 1) - online_refresh_html = 1; - } else if(strcmpi(w1,"db_path")==0) { - strcpy(db_path,w2); - } else if (strcmpi(w1, "console") == 0) { - if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) - console = 1; - } else if (strcmpi(w1, "fame_list_alchemist") == 0) { - fame_list_size_chemist = atoi(w2); - if (fame_list_size_chemist > MAX_FAME_LIST) { - ShowWarning("Max fame list size is %d (fame_list_alchemist)\n", MAX_FAME_LIST); - fame_list_size_chemist = MAX_FAME_LIST; - } - } else if (strcmpi(w1, "fame_list_blacksmith") == 0) { - fame_list_size_smith = atoi(w2); - if (fame_list_size_smith > MAX_FAME_LIST) { - ShowWarning("Max fame list size is %d (fame_list_blacksmith)\n", MAX_FAME_LIST); - fame_list_size_smith = MAX_FAME_LIST; - } - } else if (strcmpi(w1, "fame_list_taekwon") == 0) { - fame_list_size_taekwon = atoi(w2); - if (fame_list_size_taekwon > MAX_FAME_LIST) { - ShowWarning("Max fame list size is %d (fame_list_taekwon)\n", MAX_FAME_LIST); - fame_list_size_taekwon = MAX_FAME_LIST; - } - } else if (strcmpi(w1, "guild_exp_rate") == 0) { - guild_exp_rate = atoi(w2); -#endif //TXT_SQL_CONVERT - } else if (strcmpi(w1, "import") == 0) { - char_config_read(w2); - } - } - fclose(fp); - - ShowInfo("done reading %s.\n", cfgName); - return 0; -} - -#ifndef TXT_SQL_CONVERT -int chardb_final(int key, void* data, va_list va) -{ - aFree(data); - return 0; -} -void do_final(void) { - ShowStatus("Terminating server.\n"); - // write online players files with no player - online_char_db->clear(online_char_db, NULL); //clean the db... - create_online_files(); - online_char_db->destroy(online_char_db, NULL); //dispose the db... - - mmo_char_sync(); - inter_save(); - set_all_offline(-1); - flush_fifos(); - - if(gm_account) aFree(gm_account); - if(char_dat) aFree(char_dat); - - delete_session(login_fd); - delete_session(char_fd); - -#ifdef ENABLE_SC_SAVING - status_final(); -#endif - inter_final(); - mapindex_final(); - - char_log("----End of char-server (normal end with closing of all files)." RETCODE); -} - -void set_server_type(void) -{ - SERVER_TYPE = ATHENA_SERVER_CHAR; -} - -static int online_data_cleanup_sub(DBKey key, void *data, va_list ap) -{ - struct online_char_data *character= (struct online_char_data*)data; - if (character->server == -2) //Unknown server.. set them offline - set_char_offline(character->char_id, character->account_id); - if (character->server < 0) - //Free data from players that have not been online for a while. - db_remove(online_char_db, key); - return 0; -} - -static int online_data_cleanup(int tid, unsigned int tick, int id, int data) -{ - online_char_db->foreach(online_char_db, online_data_cleanup_sub); - return 0; -} - -int do_init(int argc, char **argv) { - int i; - - mapindex_init(); //Needed here for the start-point reading. - start_point.map = mapindex_name2id("new_zone01.gat"); - char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]); - char_lan_config_read((argc > 3) ? argv[3] : LOGIN_LAN_CONF_NAME); - - if (strcmp(userid, "s1")==0 && strcmp(passwd, "p1")==0) { - ShowError("Using the default user/password s1/p1 is NOT RECOMMENDED.\n"); - ShowNotice("Please edit your save/account.txt file to create a proper inter-server user/password (gender 'S')\n"); - ShowNotice("And then change the user/password to use in conf/char_athena.conf (or conf/import/char_conf.txt)\n"); - } - - // a newline in the log... - char_log(""); - // moved behind char_config_read in case we changed the filename [celest] - char_log("The char-server starting..." RETCODE); - - if ((naddr_ != 0) && (!login_ip || !char_ip)) { - // The char server should know what IP address it is running on - // - MouseJstr - int localaddr = ntohl(addr_[0]); - unsigned char *ptr = (unsigned char *) &localaddr; - char buf[16]; - sprintf(buf, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);; - if (naddr_ != 1) - ShowStatus("Multiple interfaces detected.. using %s as our IP address\n", buf); - else - ShowStatus("Defaulting to %s as our IP address\n", buf); - if (!login_ip) { - strcpy(login_ip_str, buf); - login_ip = inet_addr(login_ip_str); - } - if (!char_ip) { - strcpy(char_ip_str, buf); - char_ip = inet_addr(char_ip_str); - } - - if (ptr[0] == 192 && ptr[1] == 168) - ShowWarning("Firewall detected.. edit subnet_athena.conf and char_athena.conf\n"); - } - - for(i = 0; i < MAX_MAP_SERVERS; i++) { - memset(&server[i], 0, sizeof(struct mmo_map_server)); - server_fd[i] = -1; - } - - online_char_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - mmo_char_init(); -#ifdef ENABLE_SC_SAVING - status_init(); -#endif - update_online = time(NULL); - create_online_files(); // update online players files at start of the server - - inter_init_txt((argc > 2) ? argv[2] : inter_cfgName); // inter server 初期化 - - set_defaultparse(parse_char); - - char_fd = make_listen_bind(bind_ip?bind_ip:INADDR_ANY,char_port); - - add_timer_func_list(check_connect_login_server, "check_connect_login_server"); - add_timer_func_list(send_users_tologin, "send_users_tologin"); - add_timer_func_list(send_accounts_tologin, "send_accounts_tologin"); - add_timer_func_list(mmo_char_sync_timer, "mmo_char_sync_timer"); - add_timer_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect"); - add_timer_func_list(online_data_cleanup, "online_data_cleanup"); - - add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600 * 1000); - add_timer_interval(gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000); - add_timer_interval(gettick() + 1000, send_users_tologin, 0, 0, 5 * 1000); - add_timer_interval(gettick() + 3600*1000, send_accounts_tologin, 0, 0, 3600*1000); //Sync online accounts every hour - add_timer_interval(gettick() + autosave_interval, mmo_char_sync_timer, 0, 0, autosave_interval); - - char_read_fame_list(); //Read fame lists. - - if(console) { - set_defaultconsoleparse(parse_console); - start_console(); - } - - char_log("The char-server is ready (Server is listening on the port %d)." RETCODE, char_port); - - ShowStatus("The char-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", char_port); - - return 0; -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common/strlib.h" +#include "../common/core.h" +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/mmo.h" +#include "../common/db.h" +#include "../common/version.h" +#include "../common/lock.h" +#include "../common/showmsg.h" +#include "../common/malloc.h" + +#include "char.h" +#include "inter.h" +#include "int_pet.h" +#include "int_homun.h" +#include "int_guild.h" +#include "int_party.h" +#include "int_storage.h" +#ifdef ENABLE_SC_SAVING +#include "int_status.h" +#endif + +#ifndef TXT_SQL_CONVERT +struct mmo_map_server{ + long ip; + short port; + int users; + unsigned short map[MAX_MAP_PER_SERVER]; +} server[MAX_MAP_SERVERS]; +int server_fd[MAX_MAP_SERVERS]; + +int login_fd, char_fd; +char userid[24]; +char passwd[24]; +char server_name[20]; +char wisp_server_name[NAME_LENGTH] = "Server"; +char login_ip_str[128]; +in_addr_t login_ip; +int login_port = 6900; +char char_ip_str[128]; +in_addr_t char_ip; +char bind_ip_str[128]; +in_addr_t bind_ip; +int char_port = 6121; +int char_maintenance; +int char_new; +int char_new_display; +int email_creation = 0; // disabled by default +#endif +char char_txt[1024]="save/athena.txt"; +char backup_txt[1024]="save/backup.txt"; //By zanetheinsane +char friends_txt[1024]="save/friends.txt"; // davidsiaw +#ifndef TXT_SQL_CONVERT +char backup_txt_flag = 0; // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. => option By [Yor] +char unknown_char_name[1024] = "Unknown"; +char char_log_filename[1024] = "log/char.log"; +char db_path[1024]="db"; + +// Advanced subnet check [LuzZza] +struct _subnet { + long subnet; + long mask; + long char_ip; + long map_ip; +} subnet[16]; + +int subnet_count = 0; + +int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor] +int char_name_option = 0; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor] +//The following are characters that are trimmed regardless because they cause confusion and problems on the servers. [Skotlex] +#define TRIM_CHARS "\032\t\x0A\x0D " +char char_name_letters[1024] = ""; // list of letters/symbols authorised (or not) in a character name. by [Yor] + +int log_char = 1; // loggin char or not [devil] +int log_inter = 1; // loggin inter or not [devil] + +struct char_session_data{ + int account_id, login_id1, login_id2, sex; + int found_char[9]; + char email[40]; // e-mail (default: a@a.com) by [Yor] + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) +}; + +#define AUTH_FIFO_SIZE 256 +struct { + int account_id, char_id, login_id1, login_id2, ip, char_pos, delflag, sex; + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) +} auth_fifo[AUTH_FIFO_SIZE]; +int auth_fifo_pos = 0; + +int check_ip_flag = 1; // It's to check IP of a player between char-server and other servers (part of anti-hacking system) +static int online_check = 1; //If one, it won't let players connect when their account is already registered online and will send the relevant map server a kick user request. [Skotlex] + +int char_id_count = START_CHAR_NUM; +struct character_data *char_dat; + +int char_num, char_max; +int max_connect_user = 0; +int gm_allow_level = 99; +int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; +int save_log = 1; +int start_zeny = 500; +int start_weapon = 1201; +int start_armor = 2301; +int guild_exp_rate = 100; + +//Custom limits for the fame lists. [Skotlex] +int fame_list_size_chemist = MAX_FAME_LIST; +int fame_list_size_smith = MAX_FAME_LIST; +int fame_list_size_taekwon = MAX_FAME_LIST; + +// Char-server-side stored fame lists [DracoRPG] +struct fame_list smith_fame_list[MAX_FAME_LIST]; +struct fame_list chemist_fame_list[MAX_FAME_LIST]; +struct fame_list taekwon_fame_list[MAX_FAME_LIST]; + +// Initial position (it's possible to set it in conf file) +struct point start_point = { 0, 53, 111}; + +struct gm_account *gm_account = NULL; +int GM_num = 0; + +// online players by [Yor] +char online_txt_filename[1024] = "online.txt"; +char online_html_filename[1024] = "online.html"; +int online_sorting_option = 0; // sorting option to display online players in online files +int online_display_option = 1; // display options: to know which columns must be displayed +int online_refresh_html = 20; // refresh time (in sec) of the html file in the explorer +int online_gm_display_min_level = 20; // minimum GM level to display 'GM' when we want to display it + +//These are used to aid the map server in identifying valid clients. [Skotlex] +static int max_account_id = DEFAULT_MAX_ACCOUNT_ID, max_char_id = DEFAULT_MAX_CHAR_ID; + +struct online_char_data { + int account_id; + int char_id; + short server; + unsigned waiting_disconnect :1; +}; + +struct dbt *online_char_db; //Holds all online characters. + +time_t update_online; // to update online files when we receiving information from a server (not less than 8 seconds) + +int console = 0; + +//------------------------------ +// Writing function of logs file +//------------------------------ +int char_log(char *fmt, ...) { + if(log_char) + { + FILE *logfp; + va_list ap; + time_t raw_time; + char tmpstr[2048]; + + va_start(ap, fmt); + + logfp = fopen(char_log_filename, "a"); + if (logfp) { + if (fmt[0] == '\0') // jump a line if no message + fprintf(logfp, RETCODE); + else { + time(&raw_time); + strftime(tmpstr, 24, "%d-%m-%Y %H:%M:%S", localtime(&raw_time)); + sprintf(tmpstr + 19, ": %s", fmt); + vfprintf(logfp, tmpstr, ap); + } + fclose(logfp); + } + va_end(ap); + } + return 0; +} + +//---------------------------------------------------------------------- +// Determine if an account (id) is a GM account +// and returns its level (or 0 if it isn't a GM account or if not found) +//---------------------------------------------------------------------- +int isGM(int account_id) { + int i; + + for(i = 0; i < GM_num; i++) + if (gm_account[i].account_id == account_id) + return gm_account[i].level; + return 0; +} + +//Search character data from the aid/cid givem +struct mmo_charstatus* search_character(int aid, int cid) { + int i; + for (i = 0; i < char_num; i++) { + if (char_dat[i].status.char_id == cid && char_dat[i].status.account_id == aid) + return &char_dat[i].status; + } + return NULL; +} + +struct mmo_charstatus* search_character_byname(char* character_name) +{ + int i = search_character_index(character_name); + if (i == -1) return NULL; + return &char_dat[i].status; +} + +//---------------------------------------------- +// Search an character id +// (return character index or -1 (if not found)) +// If exact character name is not found, +// the function checks without case sensitive +// and returns index if only 1 character is found +// and similar to the searched name. +//---------------------------------------------- +int search_character_index(char* character_name) { + int i, quantity, index; + + quantity = 0; + index = -1; + for(i = 0; i < char_num; i++) { + // Without case sensitive check (increase the number of similar character names found) + if (stricmp(char_dat[i].status.name, character_name) == 0) { + // Strict comparison (if found, we finish the function immediatly with correct value) + if (strcmp(char_dat[i].status.name, character_name) == 0) + return i; + quantity++; + index = i; + } + } + // Here, the exact character name is not found + // We return the found index of a similar account ONLY if there is 1 similar character + if (quantity == 1) + return index; + + // Exact character name is not found and 0 or more than 1 similar characters have been found ==> we say not found + return -1; +} + +//------------------------------------- +// Return character name with the index +//------------------------------------- +char * search_character_name(int index) { + + if (index >= 0 && index < char_num) + return char_dat[index].status.name; + + return unknown_char_name; +} + +// Searches if the given character is online, and returns the fd of the +// map-server it is connected to. +int search_character_online(int aid, int cid) +{ + //Look for online character. + struct online_char_data* character; + character = idb_get(online_char_db, aid); + if(character && + character->char_id == cid && + character->server > -1) + return server_fd[character->server]; + return -1; +} +static void * create_online_char_data(DBKey key, va_list args) { + struct online_char_data* character; + character = aCalloc(1, sizeof(struct online_char_data)); + character->account_id = key.i; + character->char_id = -1; + character->server = -1; + return character; +} + +static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data); + +//------------------------------------------------- +// Set Character online/offline [Wizputer] +//------------------------------------------------- + +void set_char_online(int map_id, int char_id, int account_id) { + struct online_char_data* character; + + if ( char_id != 99 && (max_account_id < account_id || max_char_id < char_id)) + { //Notify map-server of the new max IDs [Skotlex] + if (account_id > max_account_id) + max_account_id = account_id; + if (char_id > max_char_id) + max_char_id = char_id; + mapif_send_maxid(max_account_id, max_char_id); + } + character = idb_ensure(online_char_db, account_id, create_online_char_data); + if (online_check && character->char_id != -1 && character->server > -1 && character->server != map_id) + { + //char == 99 <- Character logging in, so someone has logged in while one + //char is still on map-server, so kick him out, but don't print "error" + //as this is normal behaviour. [Skotlex] + if (char_id != 99) + ShowNotice("set_char_online: Character %d:%d marked in map server %d, but map server %d claims to have (%d:%d) online!\n", + character->account_id, character->char_id, character->server, map_id, account_id, char_id); + mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); + } + character->waiting_disconnect = 0; + character->char_id = (char_id==99)?-1:char_id; + character->server = (char_id==99)?-1:map_id; + + if (login_fd <= 0 || session[login_fd]->eof) + return; + WFIFOHEAD(login_fd, 6); + WFIFOW(login_fd,0) = 0x272b; + WFIFOL(login_fd,2) = account_id; + WFIFOSET(login_fd,6); + + //printf ("set online\n"); +} +void set_char_offline(int char_id, int account_id) { + struct online_char_data* character; + + if ((character = idb_get(online_char_db, account_id)) != NULL) + { //We don't free yet to avoid aCalloc/aFree spamming during char change. [Skotlex] + character->char_id = -1; + character->server = -1; + character->waiting_disconnect = 0; + } + if (login_fd <= 0 || session[login_fd]->eof) + return; + WFIFOHEAD(login_fd, 6); + WFIFOW(login_fd,0) = 0x272c; + WFIFOL(login_fd,2) = account_id; + WFIFOSET(login_fd,6); + +} + +static int char_db_setoffline(DBKey key, void* data, va_list ap) { + struct online_char_data* character = (struct online_char_data*)data; + int server = va_arg(ap, int); + if (server == -1) { + character->char_id = -1; + character->server = -1; + character->waiting_disconnect = 0; + } else if (character->server == server) + character->server = -2; //In some map server that we aren't connected to. + return 0; +} + +static int char_db_kickoffline(DBKey key, void* data, va_list ap) { + struct online_char_data* character = (struct online_char_data*)data; + int server = va_arg(ap, int); + + if (server > -1 && character->server != server) + return 0; + + //Kick out any connected characters, and set them offline as appropiate. + if (character->server > -1) + mapif_disconnectplayer(server_fd[character->server], + character->account_id, character->char_id, 1); + else if (!character->waiting_disconnect) + set_char_offline(character->char_id, character->account_id); + else return 0; + return 1; +} + +void set_all_offline(int id) { + if (id < 0) + ShowNotice("Sending all users offline.\n"); + else + ShowNotice("Sending users of map-server %d offline.\n", id); + online_char_db->foreach(online_char_db,char_db_kickoffline,id); + + if (id >= 0 || login_fd <= 0 || session[login_fd]->eof) + return; + //Tell login-server to also mark all our characters as offline. + WFIFOHEAD(login_fd, 2); + WFIFOW(login_fd,0) = 0x2737; + WFIFOSET(login_fd,2); +} + +/*--------------------------------------------------- + Make a data line for friends list + --------------------------------------------------*/ + +int mmo_friends_list_data_str(char *str, struct mmo_charstatus *p) { + int i; + char *str_p = str; + str_p += sprintf(str_p, "%d", p->char_id); + + for (i=0;ifriends[i].account_id > 0 && p->friends[i].char_id > 0 && p->friends[i].name[0]) + str_p += sprintf(str_p, ",%d,%d,%s", p->friends[i].account_id, p->friends[i].char_id, p->friends[i].name); + } + + str_p += '\0'; + + return 0; +} + +//------------------------------------------------- +// Function to create the character line (for save) +//------------------------------------------------- +int mmo_char_tostr(char *str, struct mmo_charstatus *p, struct global_reg *reg, int reg_num) { + int i,j; + char *str_p = str; + + /* We shouldn't need this anymore... [Skotlex] + // on multi-map server, sometimes it's posssible that last_point become void. (reason???) We check that to not lost character at restart. + if (!p->last_point.map) { + p->last_point.map = mapindex_name2id(MAP_PRONTERA); + p->last_point.x = 273; + p->last_point.y = 354; + } + */ + str_p += sprintf(str_p, + "%d\t%d,%d\t%s\t%d,%d,%d\t%u,%u,%d" //Up to Zeny field + "\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" //Up to Skill Point + "\t%d,%d,%d\t%d,%d,%d,%d" //Up to hom id + "\t%d,%d,%d\t%d,%d,%d,%d,%d" //Up to head bottom + "\t%d,%d,%d\t%d,%d,%d" //last point + save point + ",%d,%d,%d,%d,%d\t", //Family info + p->char_id, p->account_id, p->char_num, p->name, // + p->class_, p->base_level, p->job_level, + p->base_exp, p->job_exp, p->zeny, + p->hp, p->max_hp, p->sp, p->max_sp, + p->str, p->agi, p->vit, p->int_, p->dex, p->luk, + p->status_point, p->skill_point, + p->option, p->karma, p->manner, // + p->party_id, p->guild_id, p->pet_id, p->hom_id, + p->hair, p->hair_color, p->clothes_color, + p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom, + p->last_point.map, p->last_point.x, p->last_point.y, // + p->save_point.map, p->save_point.x, p->save_point.y, + p->partner_id,p->father,p->mother,p->child,p->fame); + for(i = 0; i < MAX_MEMOPOINTS; i++) + if (p->memo_point[i].map) { + str_p += sprintf(str_p, "%d,%d,%d ", p->memo_point[i].map, p->memo_point[i].x, p->memo_point[i].y); + } + *(str_p++) = '\t'; + + for(i = 0; i < MAX_INVENTORY; i++) + if (p->inventory[i].nameid) { + str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d", + p->inventory[i].id,p->inventory[i].nameid,p->inventory[i].amount,p->inventory[i].equip, + p->inventory[i].identify,p->inventory[i].refine,p->inventory[i].attribute); + for(j=0; jinventory[i].card[j]); + str_p += sprintf(str_p," "); + } + *(str_p++) = '\t'; + + for(i = 0; i < MAX_CART; i++) + if (p->cart[i].nameid) { + str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d", + p->cart[i].id,p->cart[i].nameid,p->cart[i].amount,p->cart[i].equip, + p->cart[i].identify,p->cart[i].refine,p->cart[i].attribute); + for(j=0; jcart[i].card[j]); + str_p += sprintf(str_p," "); + } + *(str_p++) = '\t'; + + for(i = 0; i < MAX_SKILL; i++) + if (p->skill[i].id && p->skill[i].flag != 1) { + str_p += sprintf(str_p, "%d,%d ", p->skill[i].id, (p->skill[i].flag == 0) ? p->skill[i].lv : p->skill[i].flag-2); + } + *(str_p++) = '\t'; + + for(i = 0; i < reg_num; i++) + if (reg[i].str[0]) + str_p += sprintf(str_p, "%s,%s ", reg[i].str, reg[i].value); + *(str_p++) = '\t'; + + *str_p = '\0'; + return 0; +} +#endif //TXT_SQL_CONVERT +//------------------------------------------------------------------------- +// Function to set the character from the line (at read of characters file) +//------------------------------------------------------------------------- +int mmo_char_fromstr(char *str, struct mmo_charstatus *p, struct global_reg *reg, int *reg_num) { + char tmp_str[3][128]; //To avoid deleting chars with too long names. + int tmp_int[256]; + unsigned int tmp_uint[2]; //To read exp.... + int next, len, i, j; + + // initilialise character + memset(p, '\0', sizeof(struct mmo_charstatus)); + +// Char structure of version 1500 (homun + mapindex maps) + if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%d,%d,%d\t%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_uint[0], &tmp_uint[1], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], &tmp_int[44], + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + &tmp_int[45], &tmp_int[35], &tmp_int[36], + &tmp_int[46], &tmp_int[37], &tmp_int[38], &tmp_int[39], + &tmp_int[40], &tmp_int[41], &tmp_int[42], &tmp_int[43], &next) != 48) + { + tmp_int[44] = 0; //Hom ID. +// Char structure of version 1488 (fame field addition) + if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%127[^,],%d,%d\t%127[^,],%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_uint[0], &tmp_uint[1], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + tmp_str[1], &tmp_int[35], &tmp_int[36], + tmp_str[2], &tmp_int[37], &tmp_int[38], &tmp_int[39], + &tmp_int[40], &tmp_int[41], &tmp_int[42], &tmp_int[43], &next) != 47) + { + tmp_int[43] = 0; //Fame +// Char structure of version 1363 (family data addition) + if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%127[^,],%d,%d\t%127[^,],%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], // + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_uint[0], &tmp_uint[1], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + tmp_str[1], &tmp_int[35], &tmp_int[36], // + tmp_str[2], &tmp_int[37], &tmp_int[38], &tmp_int[39], + &tmp_int[40], &tmp_int[41], &tmp_int[42], &next) != 46) + { + tmp_int[40] = 0; // father + tmp_int[41] = 0; // mother + tmp_int[42] = 0; // child +// Char structure version 1008 (marriage partner addition) + if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%127[^,],%d,%d\t%127[^,],%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], // + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_uint[0], &tmp_uint[1], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + tmp_str[1], &tmp_int[35], &tmp_int[36], // + tmp_str[2], &tmp_int[37], &tmp_int[38], &tmp_int[39], &next) != 43) + { + tmp_int[39] = 0; // partner id +// Char structure version 384 (pet addition) + if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%127[^,],%d,%d\t%127[^,],%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], // + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_uint[0], &tmp_uint[1], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + tmp_str[1], &tmp_int[35], &tmp_int[36], // + tmp_str[2], &tmp_int[37], &tmp_int[38], &next) != 42) + { + tmp_int[26] = 0; // pet id +// Char structure of a version 1 (original data structure) + if (sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%127[^,],%d,%d\t%127[^,],%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], tmp_str[0], // + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_uint[0], &tmp_uint[1], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], // + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + tmp_str[1], &tmp_int[35], &tmp_int[36], // + tmp_str[2], &tmp_int[37], &tmp_int[38], &next) != 41) + { + ShowError("Char-loading: Unrecognized character data version, info lost!\n"); + ShowDebug("Character info: %s\n", str); + return 0; + } + } // Char structure version 384 (pet addition) + } // Char structure version 1008 (marriage partner addition) + } // Char structure of version 1363 (family data addition) + } // Char structure of version 1488 (fame field addition) + //Convert save data from string to integer for older formats + tmp_int[45] = mapindex_name2id(tmp_str[1]); + tmp_int[46] = mapindex_name2id(tmp_str[2]); + } // Char structure of version 1500 (homun + mapindex maps) + + memcpy(p->name, tmp_str[0], NAME_LENGTH-1); //Overflow protection [Skotlex] + p->char_id = tmp_int[0]; + p->account_id = tmp_int[1]; + p->char_num = tmp_int[2]; + p->class_ = tmp_int[3]; +/* Unneeded unless you are running a real old character database now. + //Temporal fix until all chars are reverted from peco-flying-class to + //normal classes. [Skotlex] + switch (p->class_) { + case JOB_KNIGHT2: //Job_Knight2 + p->class_ = JOB_KNIGHT; + break; + case JOB_CRUSADER2: //Job_Crusader2 + p->class_ = JOB_CRUSADER; + break; + case JOB_LORD_KNIGHT2: //Job_Lord_Knight2 + p->class_ = JOB_LORD_KNIGHT; + break; + case JOB_PALADIN2: //Job_Paladin2 + p->class_ = JOB_PALADIN; + break; + case JOB_BABY_KNIGHT2: //Job_Baby_Knight2 + p->class_ = JOB_BABY_KNIGHT; + break; + case JOB_BABY_CRUSADER2: //Job_Baby_Crusader2 + p->class_ = JOB_BABY_CRUSADER; + break; + case JOB_STAR_GLADIATOR2: //Job_Star_Gladiator2 + p->class_ = JOB_STAR_GLADIATOR; + break; + } +*/ + p->base_level = tmp_int[4]; + p->job_level = tmp_int[5]; + p->base_exp = tmp_uint[0]; + p->job_exp = tmp_uint[1]; + p->zeny = tmp_int[8]; + p->hp = tmp_int[9]; + p->max_hp = tmp_int[10]; + p->sp = tmp_int[11]; + p->max_sp = tmp_int[12]; + p->str = tmp_int[13]; + p->agi = tmp_int[14]; + p->vit = tmp_int[15]; + p->int_ = tmp_int[16]; + p->dex = tmp_int[17]; + p->luk = tmp_int[18]; + p->status_point = tmp_int[19] > USHRT_MAX ? USHRT_MAX : tmp_int[19]; + p->skill_point = tmp_int[20] > USHRT_MAX ? USHRT_MAX : tmp_int[20]; + p->option = tmp_int[21]; + p->karma = tmp_int[22]; + p->manner = tmp_int[23]; + p->party_id = tmp_int[24]; + p->guild_id = tmp_int[25]; + p->pet_id = tmp_int[26]; + p->hair = tmp_int[27]; + p->hair_color = tmp_int[28]; + p->clothes_color = tmp_int[29]; + p->weapon = tmp_int[30]; + p->shield = tmp_int[31]; + p->head_top = tmp_int[32]; + p->head_mid = tmp_int[33]; + p->head_bottom = tmp_int[34]; + p->last_point.x = tmp_int[35]; + p->last_point.y = tmp_int[36]; + p->save_point.x = tmp_int[37]; + p->save_point.y = tmp_int[38]; + p->partner_id = tmp_int[39]; + p->father = tmp_int[40]; + p->mother = tmp_int[41]; + p->child = tmp_int[42]; + p->fame = tmp_int[43]; + p->hom_id = tmp_int[44]; + p->last_point.map = tmp_int[45]; + p->save_point.map = tmp_int[46]; + +#ifndef TXT_SQL_CONVERT + // Some checks + for(i = 0; i < char_num; i++) { + if (char_dat[i].status.char_id == p->char_id) { + ShowError(CL_RED"mmmo_auth_init: a character has an identical id to another.\n"); + ShowError(" character id #%d -> new character not readed.\n", p->char_id); + ShowError(" Character saved in log file."CL_RESET"\n"); + return -1; + } else if (strcmp(char_dat[i].status.name, p->name) == 0) { + ShowError(CL_RED"mmmo_auth_init: a character name already exists.\n"); + ShowError(" character name '%s' -> new character not read.\n", p->name); + ShowError(" Character saved in log file."CL_RESET"\n"); + return -2; + } + } + + if (strcmpi(wisp_server_name, p->name) == 0) { + ShowWarning("mmo_auth_init: ******WARNING: character name has wisp server name.\n"); + ShowWarning(" Character name '%s' = wisp server name '%s'.\n", p->name, wisp_server_name); + ShowWarning(" Character readed. Suggestion: change the wisp server name.\n"); + char_log("mmo_auth_init: ******WARNING: character name has wisp server name: Character name '%s' = wisp server name '%s'." RETCODE, + p->name, wisp_server_name); + } +#endif //TXT_SQL_CONVERT + if (str[next] == '\n' || str[next] == '\r') + return 1; // 新規データ + + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + //mapindex memo format + if (sscanf(str+next, "%d,%d,%d%n", &tmp_int[2], &tmp_int[0], &tmp_int[1], &len) != 3) + { //Old string-based memo format. + if (sscanf(str+next, "%[^,],%d,%d%n", tmp_str[0], &tmp_int[0], &tmp_int[1], &len) != 3) + return -3; + tmp_int[2] = mapindex_name2id(tmp_str[0]); + } + if (i < MAX_MEMOPOINTS) + { //Avoid overflowing (but we must also read through all saved memos) + p->memo_point[i].x = tmp_int[0]; + p->memo_point[i].y = tmp_int[1]; + p->memo_point[i].map = tmp_int[2]; + } + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d%[0-9,-]%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str[0], &len) == 8) + { + p->inventory[i].id = tmp_int[0]; + p->inventory[i].nameid = tmp_int[1]; + p->inventory[i].amount = tmp_int[2]; + p->inventory[i].equip = tmp_int[3]; + p->inventory[i].identify = tmp_int[4]; + p->inventory[i].refine = tmp_int[5]; + p->inventory[i].attribute = tmp_int[6]; + + for(j = 0; j < MAX_SLOTS && tmp_str[0] && sscanf(tmp_str[0], ",%d%[0-9,-]",&tmp_int[0], tmp_str[0]) > 0; j++) + p->inventory[i].card[j] = tmp_int[0]; + + next += len; + if (str[next] == ' ') + next++; + } else // invalid structure + return -4; + } + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d%[0-9,-]%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str[0], &len) == 8) + { + p->cart[i].id = tmp_int[0]; + p->cart[i].nameid = tmp_int[1]; + p->cart[i].amount = tmp_int[2]; + p->cart[i].equip = tmp_int[3]; + p->cart[i].identify = tmp_int[4]; + p->cart[i].refine = tmp_int[5]; + p->cart[i].attribute = tmp_int[6]; + + for(j = 0; j < MAX_SLOTS && tmp_str && sscanf(tmp_str[0], ",%d%[0-9,-]",&tmp_int[0], tmp_str[0]) > 0; j++) + p->cart[i].card[j] = tmp_int[0]; + + next += len; + if (str[next] == ' ') + next++; + } else // invalid structure + return -5; + } + + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + if (sscanf(str + next, "%d,%d%n", &tmp_int[0], &tmp_int[1], &len) != 2) + return -6; + p->skill[tmp_int[0]].id = tmp_int[0]; + p->skill[tmp_int[0]].lv = tmp_int[1]; + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for(i = 0; str[next] && str[next] != '\t' && str[next] != '\n' && str[next] != '\r'; i++) { // global_reg実装以前のathena.txt互換のため一応'\n'チェック + if (sscanf(str + next, "%[^,],%[^ ] %n", reg[i].str, reg[i].value, &len) != 2) { + // because some scripts are not correct, the str can be "". So, we must check that. + // If it's, we must not refuse the character, but just this REG value. + // Character line will have something like: nov_2nd_cos,9 ,9 nov_1_2_cos_c,1 (here, ,9 is not good) + if (str[next] == ',' && sscanf(str + next, ",%[^ ] %n", reg[i].value, &len) == 1) + i--; + else + return -7; + } + next += len; + if (str[next] == ' ') + next++; + } + *reg_num = i; + + return 1; +} +//--------------------------------- +// Function to read friend list +//--------------------------------- + +int parse_friend_txt(struct mmo_charstatus *p) +{ + char line[1024], temp[1024]; + int pos = 0, count = 0, next; + int i,len; + FILE *fp; + + // Open the file and look for the ID + fp = fopen(friends_txt, "r"); + + if(fp == NULL) + return -1; + + while(fgets(line, sizeof(line)-1, fp)) { + + if(line[0] == '/' && line[1] == '/') + continue; + if (sscanf(line, "%d%n",&i, &pos) < 1 || i != p->char_id) + continue; //Not this line... + //Read friends + len = strlen(line); + next = pos; + for (count = 0; next < len && count < MAX_FRIENDS; count++) + { //Read friends. + if (sscanf(line+next, ",%d,%d,%23[^,^\n]%n",&p->friends[count].account_id,&p->friends[count].char_id, p->friends[count].name, &pos) < 3) + { //Invalid friend? + memset(&p->friends[count], 0, sizeof(p->friends[count])); + break; + } + next+=pos; + //What IF the name contains a comma? while the next field is not a + //number, we assume it belongs to the current name. [Skotlex] + //NOTE: Of course, this will fail if someone sets their name to something like + //Bob,2005 but... meh, it's the problem of parsing a text file (encasing it in " + //won't do as quotes are also valid name chars!) + while(next < len && sscanf(line+next, ",%23[^,^\n]%n", temp, &pos) > 0) + { + if (atoi(temp)) //We read the next friend, just continue. + break; + //Append the name. + next+=pos; + i = strlen(p->friends[count].name); + if (i + strlen(temp) +1 < NAME_LENGTH) + { + p->friends[count].name[i] = ','; + strcpy(p->friends[count].name+i+1, temp); + } + } //End Guess Block + } //Friend's for. + break; //Found friends. + } + fclose(fp); + return count; +} +#ifndef TXT_SQL_CONVERT +//--------------------------------- +// Function to read characters file +//--------------------------------- +int mmo_char_init(void) { + char line[65536]; + int ret, line_count; + FILE *fp; + + char_max = 256; + char_dat = (struct character_data*)aCalloc(sizeof(struct character_data) * 256, 1); + if (!char_dat) { + ShowFatalError("out of memory: mmo_char_init (calloc of char_dat).\n"); + exit(1); + } + char_num = 0; + + fp = fopen(char_txt, "r"); + + if (fp == NULL) { + ShowError("Characters file not found: %s.\n", char_txt); + char_log("Characters file not found: %s." RETCODE, char_txt); + char_log("Id for the next created character: %d." RETCODE, char_id_count); + return 0; + } + + line_count = 0; + while(fgets(line, sizeof(line)-1, fp)) { + int i, j; + line_count++; + + if (line[0] == '/' && line[1] == '/') + continue; + line[sizeof(line)-1] = '\0'; + + j = 0; + if (sscanf(line, "%d\t%%newid%%%n", &i, &j) == 1 && j > 0) { + if (char_id_count < i) + char_id_count = i; + continue; + } + + if (char_num >= char_max) { + char_max += 256; + char_dat = (struct character_data*)aRealloc(char_dat, sizeof(struct character_data) * char_max); + if (!char_dat) { + ShowFatalError("Out of memory: mmo_char_init (realloc of char_dat).\n"); + char_log("Out of memory: mmo_char_init (realloc of char_dat)." RETCODE); + exit(1); + } + } + + ret = mmo_char_fromstr(line, &char_dat[char_num].status, char_dat[char_num].global, &char_dat[char_num].global_num); + + // Initialize friends list + parse_friend_txt(&char_dat[char_num].status); // Grab friends for the character + + if (ret > 0) { // negative value or zero for errors + if (char_dat[char_num].status.char_id >= char_id_count) + char_id_count = char_dat[char_num].status.char_id + 1; + char_num++; + } else { + ShowError("mmo_char_init: in characters file, unable to read the line #%d.\n", line_count); + ShowError(" -> Character saved in log file.\n"); + switch (ret) { + case -1: + char_log("Duplicate character id in the next character line (character not readed):" RETCODE); + break; + case -2: + char_log("Duplicate character name in the next character line (character not readed):" RETCODE); + break; + case -3: + char_log("Invalid memo point structure in the next character line (character not readed):" RETCODE); + break; + case -4: + char_log("Invalid inventory item structure in the next character line (character not readed):" RETCODE); + break; + case -5: + char_log("Invalid cart item structure in the next character line (character not readed):" RETCODE); + break; + case -6: + char_log("Invalid skill structure in the next character line (character not readed):" RETCODE); + break; + case -7: + char_log("Invalid register structure in the next character line (character not readed):" RETCODE); + break; + default: // 0 + char_log("Unabled to get a character in the next line - Basic structure of line (before inventory) is incorrect (character not readed):" RETCODE); + break; + } + char_log("%s", line); + } + } + fclose(fp); + + if (char_num == 0) { + ShowNotice("mmo_char_init: No character found in %s.\n", char_txt); + char_log("mmo_char_init: No character found in %s." RETCODE, char_txt); + } else if (char_num == 1) { + ShowStatus("mmo_char_init: 1 character read in %s.\n", char_txt); + char_log("mmo_char_init: 1 character read in %s." RETCODE, char_txt); + } else { + ShowStatus("mmo_char_init: %d characters read in %s.\n", char_num, char_txt); + char_log("mmo_char_init: %d characters read in %s." RETCODE, char_num, char_txt); + } + + char_log("Id for the next created character: %d." RETCODE, char_id_count); + + return 0; +} + +//--------------------------------------------------------- +// Function to save characters in files (speed up by [Yor]) +//--------------------------------------------------------- +void mmo_char_sync(void) { + char line[65536],f_line[1024]; + int i, j, k; + int lock; + FILE *fp,*f_fp; + //int *id = (int *) aMalloc(sizeof(int) * char_num); + CREATE_BUFFER(id, int, char_num); + + // Sorting before save (by [Yor]) + for(i = 0; i < char_num; i++) { + id[i] = i; + for(j = 0; j < i; j++) { + if ((char_dat[i].status.account_id < char_dat[id[j]].status.account_id) || + // if same account id, we sort by slot. + (char_dat[i].status.account_id == char_dat[id[j]].status.account_id && + char_dat[i].status.char_num < char_dat[id[j]].status.char_num)) { + for(k = i; k > j; k--) + id[k] = id[k-1]; + id[j] = i; // id[i] + break; + } + } + } + + // Data save + fp = lock_fopen(char_txt, &lock); + if (fp == NULL) { + ShowWarning("Server can't not save characters.\n"); + char_log("WARNING: Server can't not save characters." RETCODE); + } else { + for(i = 0; i < char_num; i++) { + // create only once the line, and save it in the 2 files (it's speeder than repeat twice the loop and create twice the line) + mmo_char_tostr(line, &char_dat[id[i]].status, char_dat[id[i]].global, char_dat[id[i]].global_num); // use of sorted index + fprintf(fp, "%s" RETCODE, line); + } + fprintf(fp, "%d\t%%newid%%" RETCODE, char_id_count); + lock_fclose(fp, char_txt, &lock); + } + + // Data save (backup) + if (backup_txt_flag) { // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. => option By [Yor] + fp = lock_fopen(backup_txt, &lock); + if (fp == NULL) { + ShowWarning("Server can't not create backup of characters file.\n"); + char_log("WARNING: Server can't not create backup of characters file." RETCODE); + //aFree(id); // free up the memory before leaving -.- [Ajarn] + DELETE_BUFFER(id); + return; + } + for(i = 0; i < char_num; i++) { + // create only once the line, and save it in the 2 files (it's speeder than repeat twice the loop and create twice the line) + mmo_char_tostr(line, &char_dat[id[i]].status,char_dat[id[i]].global, char_dat[id[i]].global_num); // use of sorted index + fprintf(fp, "%s" RETCODE, line); + } + fprintf(fp, "%d\t%%newid%%" RETCODE, char_id_count); + lock_fclose(fp, backup_txt, &lock); + } + + // Friends List data save (davidsiaw) + f_fp = lock_fopen(friends_txt, &lock); + for(i = 0; i < char_num; i++) { + mmo_friends_list_data_str(f_line, &char_dat[id[i]].status); + fprintf(f_fp, "%s" RETCODE, f_line); + } + + lock_fclose(f_fp, friends_txt, &lock); + + //aFree(id); + DELETE_BUFFER(id); + + return; +} + +//---------------------------------------------------- +// Function to save (in a periodic way) datas in files +//---------------------------------------------------- +int mmo_char_sync_timer(int tid, unsigned int tick, int id, int data) { + if (save_log) + ShowInfo("Saving all files...\n"); + mmo_char_sync(); + inter_save(); + return 0; +} + +//----------------------------------- +// Function to create a new character +//----------------------------------- +int make_new_char(int fd, unsigned char *dat) { + int i; + struct char_session_data *sd; + char name[NAME_LENGTH]; + + sd = (struct char_session_data*)session[fd]->session_data; + + // remove control characters from the name + strncpy(name, dat, NAME_LENGTH); + name[NAME_LENGTH-1] = '\0'; //Trunc name to max possible value (23) + + trim(name,TRIM_CHARS); //Trim character name. [Skotlex] + + //check name != main chat nick [LuzZza] + if(strcmpi(name, main_chat_nick) == 0) { + char_log("Create char failed (%d): this nick (%s) reserved for mainchat messages." RETCODE, + sd->account_id, name); + return -1; + } + + if (remove_control_chars((unsigned char *)name)) { + char_log("Make new char error (control char received in the name): (connection #%d, account: %d)." RETCODE, + fd, sd->account_id); + return -1; + } + + // check lenght of character name + if (strlen(name) < 4) { + char_log("Make new char error (character name too small): (connection #%d, account: %d, name: '%s')." RETCODE, + fd, sd->account_id, dat); + return -1; + } + + // Check Authorised letters/symbols in the name of the character + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) == NULL) { + char_log("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c." RETCODE, + fd, sd->account_id, name, name[i]); + return -1; + } + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) != NULL) { + char_log("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c." RETCODE, + fd, sd->account_id, dat, dat[i]); + return -1; + } + } // else, all letters/symbols are authorised (except control char removed before) + + if (dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29] != 5*6 || // stats + dat[30] >= 9 || // slots (dat[30] can not be negativ) + dat[33] <= 0 || dat[33] >= 24 || // hair style + dat[31] >= 9) { // hair color (dat[31] can not be negativ) + char_log("Make new char error (invalid values): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d" RETCODE, + fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + return -1; + } + + // check individual stat value + for(i = 24; i <= 29; i++) { + if (dat[i] < 1 || dat[i] > 9) { + char_log("Make new char error (invalid stat value: not between 1 to 9): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d" RETCODE, + fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + return -1; + } + } // now we know that every stat has proper value but we have to check if str/int agi/luk vit/dex pairs are correct + + if( ((dat[24]+dat[27]) > 10) || ((dat[25]+dat[29]) > 10) || ((dat[26]+dat[28]) > 10) ) { + if (log_char) { + char_log("Make new char error (invalid stat value): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d" RETCODE, + fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + return -1; + } + } // now when we have passed all stat checks + + for(i = 0; i < char_num; i++) { + if ((name_ignoring_case != 0 && strncmp(char_dat[i].status.name, name, NAME_LENGTH) == 0) || + (name_ignoring_case == 0 && strncmpi(char_dat[i].status.name, name, NAME_LENGTH) == 0)) { + char_log("Make new char error (name already exists): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d." RETCODE, + fd, sd->account_id, dat[30], dat, char_dat[i].status.name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + return -1; + } + if (char_dat[i].status.account_id == sd->account_id && char_dat[i].status.char_num == dat[30]) { + char_log("Make new char error (slot already used): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d." RETCODE, + fd, sd->account_id, dat[30], dat, char_dat[i].status.name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + return -1; + } + } + + if (strcmp(wisp_server_name, name) == 0) { + char_log("Make new char error (name used is wisp name for server): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d." RETCODE, + fd, sd->account_id, dat[30], name, char_dat[i].status.name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + return -1; + } + + if (char_num >= char_max) { + char_max += 256; + char_dat = (struct character_data*)aRealloc(char_dat, sizeof(struct character_data) * char_max); + if (!char_dat) { + ShowFatalError("Out of memory: make_new_char (realloc of char_dat).\n"); + char_log("Out of memory: make_new_char (realloc of char_dat)." RETCODE); + exit(1); + } + } + + char_log("Creation of New Character: (connection #%d, account: %d) slot %d, character Name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d." RETCODE, + fd, sd->account_id, dat[30], name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + + memset(&char_dat[i], 0, sizeof(struct character_data)); + + char_dat[i].status.char_id = char_id_count++; + char_dat[i].status.account_id = sd->account_id; + char_dat[i].status.char_num = dat[30]; + strcpy(char_dat[i].status.name,name); + char_dat[i].status.class_ = 0; + char_dat[i].status.base_level = 1; + char_dat[i].status.job_level = 1; + char_dat[i].status.base_exp = 0; + char_dat[i].status.job_exp = 0; + char_dat[i].status.zeny = start_zeny; + char_dat[i].status.str = dat[24]; + char_dat[i].status.agi = dat[25]; + char_dat[i].status.vit = dat[26]; + char_dat[i].status.int_ = dat[27]; + char_dat[i].status.dex = dat[28]; + char_dat[i].status.luk = dat[29]; + char_dat[i].status.max_hp = 40 * (100 + char_dat[i].status.vit) / 100; + char_dat[i].status.max_sp = 11 * (100 + char_dat[i].status.int_) / 100; + char_dat[i].status.hp = char_dat[i].status.max_hp; + char_dat[i].status.sp = char_dat[i].status.max_sp; + char_dat[i].status.status_point = 0; + char_dat[i].status.skill_point = 0; + char_dat[i].status.option = 0; + char_dat[i].status.karma = 0; + char_dat[i].status.manner = 0; + char_dat[i].status.party_id = 0; + char_dat[i].status.guild_id = 0; + char_dat[i].status.hair = dat[33]; + char_dat[i].status.hair_color = dat[31]; + char_dat[i].status.clothes_color = 0; + char_dat[i].status.inventory[0].nameid = start_weapon; // Knife + char_dat[i].status.inventory[0].amount = 1; + char_dat[i].status.inventory[0].equip = 0x02; + char_dat[i].status.inventory[0].identify = 1; + char_dat[i].status.inventory[1].nameid = start_armor; // Cotton Shirt + char_dat[i].status.inventory[1].amount = 1; + char_dat[i].status.inventory[1].equip = 0x10; + char_dat[i].status.inventory[1].identify = 1; + char_dat[i].status.weapon = 1; + char_dat[i].status.shield = 0; + char_dat[i].status.head_top = 0; + char_dat[i].status.head_mid = 0; + char_dat[i].status.head_bottom = 0; + memcpy(&char_dat[i].status.last_point, &start_point, sizeof(start_point)); + memcpy(&char_dat[i].status.save_point, &start_point, sizeof(start_point)); + char_num++; + + mmo_char_sync(); + return i; +} + +//---------------------------------------------------- +// This function return the name of the job (by [Yor]) +//---------------------------------------------------- +char * job_name(int class_) { + switch (class_) { + case JOB_NOVICE: return "Novice"; + case JOB_SWORDMAN: return "Swordsman"; + case JOB_MAGE: return "Mage"; + case JOB_ARCHER: return "Archer"; + case JOB_ACOLYTE: return "Acolyte"; + case JOB_MERCHANT: return "Merchant"; + case JOB_THIEF: return "Thief"; + case JOB_KNIGHT: return "Knight"; + case JOB_PRIEST: return "Priest"; + case JOB_WIZARD: return "Wizard"; + case JOB_BLACKSMITH: return "Blacksmith"; + case JOB_HUNTER: return "Hunter"; + case JOB_ASSASSIN: return "Assassin"; + case JOB_KNIGHT2: return "Peco-Knight"; + case JOB_CRUSADER: return "Crusader"; + case JOB_MONK: return "Monk"; + case JOB_SAGE: return "Sage"; + case JOB_ROGUE: return "Rogue"; + case JOB_ALCHEMIST: return "Alchemist"; + case JOB_BARD: return "Bard"; + case JOB_DANCER: return "Dancer"; + case JOB_CRUSADER2: return "Peco-Crusader"; + case JOB_WEDDING: return "Wedding"; + case JOB_SUPER_NOVICE: return "Super Novice"; + case JOB_GUNSLINGER: return "Gunslinger"; + case JOB_NINJA: return "Ninja"; + case JOB_XMAS: return "Christmas"; + case JOB_NOVICE_HIGH: return "Novice High"; + case JOB_SWORDMAN_HIGH: return "Swordsman High"; + case JOB_MAGE_HIGH: return "Mage High"; + case JOB_ARCHER_HIGH: return "Archer High"; + case JOB_ACOLYTE_HIGH: return "Acolyte High"; + case JOB_MERCHANT_HIGH: return "Merchant High"; + case JOB_THIEF_HIGH: return "Thief High"; + case JOB_LORD_KNIGHT: return "Lord Knight"; + case JOB_HIGH_PRIEST: return "High Priest"; + case JOB_HIGH_WIZARD: return "High Wizard"; + case JOB_WHITESMITH: return "Whitesmith"; + case JOB_SNIPER: return "Sniper"; + case JOB_ASSASSIN_CROSS: return "Assassin Cross"; + case JOB_LORD_KNIGHT2: return "Peko Knight"; + case JOB_PALADIN: return "Paladin"; + case JOB_CHAMPION: return "Champion"; + case JOB_PROFESSOR: return "Professor"; + case JOB_STALKER: return "Stalker"; + case JOB_CREATOR: return "Creator"; + case JOB_CLOWN: return "Clown"; + case JOB_GYPSY: return "Gypsy"; + case JOB_PALADIN2: return "Peko Paladin"; + case JOB_BABY: return "Baby Novice"; + case JOB_BABY_SWORDMAN: return "Baby Swordsman"; + case JOB_BABY_MAGE: return "Baby Mage"; + case JOB_BABY_ARCHER: return "Baby Archer"; + case JOB_BABY_ACOLYTE: return "Baby Acolyte"; + case JOB_BABY_MERCHANT: return "Baby Merchant"; + case JOB_BABY_THIEF: return "Baby Thief"; + case JOB_BABY_KNIGHT: return "Baby Knight"; + case JOB_BABY_PRIEST: return "Baby Priest"; + case JOB_BABY_WIZARD: return "Baby Wizard"; + case JOB_BABY_BLACKSMITH: return "Baby Blacksmith"; + case JOB_BABY_HUNTER: return "Baby Hunter"; + case JOB_BABY_ASSASSIN: return "Baby Assassin"; + case JOB_BABY_KNIGHT2: return "Baby Peco Knight"; + case JOB_BABY_CRUSADER: return "Baby Crusader"; + case JOB_BABY_MONK: return "Baby Monk"; + case JOB_BABY_SAGE: return "Baby Sage"; + case JOB_BABY_ROGUE: return "Baby Rogue"; + case JOB_BABY_ALCHEMIST: return "Baby Alchemist"; + case JOB_BABY_BARD: return "Baby Bard"; + case JOB_BABY_DANCER: return "Baby Dancer"; + case JOB_BABY_CRUSADER2: return "Baby Peco Crusader"; + case JOB_SUPER_BABY: return "Super Baby"; + case JOB_TAEKWON: return "Taekwon"; + case JOB_STAR_GLADIATOR: return "Star Gladiator"; + case JOB_STAR_GLADIATOR2: return "Flying Star Gladiator"; + case JOB_SOUL_LINKER: return "Soul Linker"; + } + return "Unknown Job"; +} + +static int create_online_files_sub(DBKey key, void* data, va_list va) +{ + struct online_char_data *character; + int* players; + int *id; + int j,k,l; + character = (struct online_char_data*) data; + players = va_arg(va, int*); + id = va_arg(va, int*); + + // check if map-server is online + if (character->server == -1 || character->char_id == -1) { //Character not currently online. + return -1; + } + + j = character->server; + if (server_fd[j] < 0) { + server[j].users = 0; + return -1; + } + // search position of character in char_dat and sort online characters. + for(j = 0; j < char_num; j++) { + if (char_dat[j].status.char_id != character->char_id) + continue; + id[*players] = j; + // use sorting option + switch (online_sorting_option) { + case 1: // by name (without case sensitive) + for(k = 0; k < *players; k++) + if (stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0 || + // if same name, we sort with case sensitive. + (stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) == 0 && + strcmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0)) { + for(l = *players; l > k; l--) + id[l] = id[l-1]; + id[k] = j; // id[*players] + break; + } + break; + case 2: // by zeny + for(k = 0; k < *players; k++) + if (char_dat[j].status.zeny < char_dat[id[k]].status.zeny || + // if same number of zenys, we sort by name. + (char_dat[j].status.zeny == char_dat[id[k]].status.zeny && + stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0)) { + for(l = *players; l > k; l--) + id[l] = id[l-1]; + id[k] = j; // id[*players] + break; + } + break; + case 3: // by base level + for(k = 0; k < *players; k++) + if (char_dat[j].status.base_level < char_dat[id[k]].status.base_level || + // if same base level, we sort by base exp. + (char_dat[j].status.base_level == char_dat[id[k]].status.base_level && + char_dat[j].status.base_exp < char_dat[id[k]].status.base_exp)) { + for(l = *players; l > k; l--) + id[l] = id[l-1]; + id[k] = j; // id[*players] + break; + } + break; + case 4: // by job (and job level) + for(k = 0; k < *players; k++) + if (char_dat[j].status.class_ < char_dat[id[k]].status.class_ || + // if same job, we sort by job level. + (char_dat[j].status.class_ == char_dat[id[k]].status.class_ && + char_dat[j].status.job_level < char_dat[id[k]].status.job_level) || + // if same job and job level, we sort by job exp. + (char_dat[j].status.class_ == char_dat[id[k]].status.class_ && + char_dat[j].status.job_level == char_dat[id[k]].status.job_level && + char_dat[j].status.job_exp < char_dat[id[k]].status.job_exp)) { + for(l = *players; l > k; l--) + id[l] = id[l-1]; + id[k] = j; // id[*players] + break; + } + break; + case 5: // by location map name + { + const char *map1, *map2; + map1 = mapindex_id2name(char_dat[j].status.last_point.map); + + for(k = 0; k < *players; k++) { + map2 = mapindex_id2name(char_dat[id[k]].status.last_point.map); + if (!map1 || !map2 || //Avoid sorting if either one failed to resolve. + stricmp(map1, map2) < 0 || + // if same map name, we sort by name. + (stricmp(map1, map2) == 0 && + stricmp(char_dat[j].status.name, char_dat[id[k]].status.name) < 0)) { + for(l = *players; l > k; l--) + id[l] = id[l-1]; + id[k] = j; // id[*players] + break; + } + } + } + break; + default: // 0 or invalid value: no sorting + break; + } + (*players)++; + break; + } + return 0; +} +//------------------------------------------------------------- +// Function to create the online files (txt and html). by [Yor] +//------------------------------------------------------------- +void create_online_files(void) { + unsigned int k, j; // for loop with strlen comparing + int i, l; // for loops + int players; // count the number of players + FILE *fp; // for the txt file + FILE *fp2; // for the html file + char temp[256]; // to prepare what we must display + time_t time_server; // for number of seconds + struct tm *datetime; // variable for time in structure ->tm_mday, ->tm_sec, ... + int id[4096]; + + if (online_display_option == 0) // we display nothing, so return + return; + + // Get number of online players, id of each online players, and verify if a server is offline + players = 0; + online_char_db->foreach(online_char_db, create_online_files_sub, &players, &id); + + // write files + fp = fopen(online_txt_filename, "w"); + if (fp != NULL) { + fp2 = fopen(online_html_filename, "w"); + if (fp2 != NULL) { + // get time + time(&time_server); // get time in seconds since 1/1/1970 + datetime = localtime(&time_server); // convert seconds in structure + strftime(temp, sizeof(temp), "%d %b %Y %X", datetime); // like sprintf, but only for date/time (05 dec 2003 15:12:52) + // write heading + fprintf(fp2, "\n"); + fprintf(fp2, " \n", online_refresh_html); // update on client explorer every x seconds + fprintf(fp2, " \n"); + fprintf(fp2, " Online Players on %s\n", server_name); + fprintf(fp2, " \n"); + fprintf(fp2, " \n"); + fprintf(fp2, "

Online Players on %s (%s):

\n", server_name, temp); + fprintf(fp, "Online Players on %s (%s):\n", server_name, temp); + fprintf(fp, "\n"); + + for (i = 0; i < players; i++) { + // if it's the first player + if (i == 0) { + j = 0; // count the number of characters for the txt version and to set the separate line + fprintf(fp2, " \n"); + fprintf(fp2, " \n"); + if ((online_display_option & 1) || (online_display_option & 64)) { + fprintf(fp2, " \n"); + if (online_display_option & 64) { + fprintf(fp, "Name "); // 30 + j += 30; + } else { + fprintf(fp, "Name "); // 25 + j += 25; + } + } + if ((online_display_option & 6) == 6) { + fprintf(fp2, " \n"); + fprintf(fp, "Job Levels "); // 27 + j += 27; + } else if (online_display_option & 2) { + fprintf(fp2, " \n"); + fprintf(fp, "Job "); // 19 + j += 19; + } else if (online_display_option & 4) { + fprintf(fp2, " \n"); + fprintf(fp, " Levels "); // 8 + j += 8; + } + if (online_display_option & 24) { // 8 or 16 + fprintf(fp2, " \n"); + if (online_display_option & 16) { + fprintf(fp, "Location ( x , y ) "); // 23 + j += 23; + } else { + fprintf(fp, "Location "); // 13 + j += 13; + } + } + if (online_display_option & 32) { + fprintf(fp2, " \n"); + fprintf(fp, " Zenys "); // 16 + j += 16; + } + fprintf(fp2, " \n"); + fprintf(fp, "\n"); + for (k = 0; k < j; k++) + fprintf(fp, "-"); + fprintf(fp, "\n"); + } + fprintf(fp2, " \n"); + // get id of the character (more speed) + j = id[i]; + // displaying the character name + if ((online_display_option & 1) || (online_display_option & 64)) { // without/with 'GM' display + strcpy(temp, char_dat[j].status.name); + l = isGM(char_dat[j].status.account_id); + if (online_display_option & 64) { + if (l >= online_gm_display_min_level) + fprintf(fp, "%-24s (GM) ", temp); + else + fprintf(fp, "%-24s ", temp); + } else + fprintf(fp, "%-24s ", temp); + // name of the character in the html (no < >, because that create problem in html code) + fprintf(fp2, " \n"); + } + // displaying of the job + if (online_display_option & 6) { + char * jobname = job_name(char_dat[j].status.class_); + if ((online_display_option & 6) == 6) { + fprintf(fp2, " \n", jobname, char_dat[j].status.base_level, char_dat[j].status.job_level); + fprintf(fp, "%-18s %3d/%3d ", jobname, char_dat[j].status.base_level, char_dat[j].status.job_level); + } else if (online_display_option & 2) { + fprintf(fp2, " \n", jobname); + fprintf(fp, "%-18s ", jobname); + } else if (online_display_option & 4) { + fprintf(fp2, " \n", char_dat[j].status.base_level, char_dat[j].status.job_level); + fprintf(fp, "%3d/%3d ", char_dat[j].status.base_level, char_dat[j].status.job_level); + } + } + // displaying of the map + if (online_display_option & 24) { // 8 or 16 + // prepare map name + memcpy(temp, mapindex_id2name(char_dat[j].status.last_point.map), MAP_NAME_LENGTH); + temp[MAP_NAME_LENGTH] = '\0'; + if (strstr(temp, ".gat") != NULL) { + temp[strstr(temp, ".gat") - temp] = 0; // suppress the '.gat' + } + // write map name + if (online_display_option & 16) { // map-name AND coordinates + fprintf(fp2, " \n", temp, char_dat[j].status.last_point.x, char_dat[j].status.last_point.y); + fprintf(fp, "%-12s (%3d,%3d) ", temp, char_dat[j].status.last_point.x, char_dat[j].status.last_point.y); + } else { + fprintf(fp2, " \n", temp); + fprintf(fp, "%-12s ", temp); + } + } + // displaying nimber of zenys + if (online_display_option & 32) { + // write number of zenys + if (char_dat[j].status.zeny == 0) { // if no zeny + fprintf(fp2, " \n"); + fprintf(fp, " no zeny "); + } else { + fprintf(fp2, " \n", char_dat[j].status.zeny); + fprintf(fp, "%13d z ", char_dat[j].status.zeny); + } + } + fprintf(fp, "\n"); + fprintf(fp2, " \n"); + } + // If we display at least 1 player + if (players > 0) { + fprintf(fp2, "
NameJob (levels)JobLevelsLocationzenys
"); + if ((online_display_option & 64) && l >= online_gm_display_min_level) + fprintf(fp2, ""); + for (k = 0; k < strlen(temp); k++) { + switch(temp[k]) { + case '<': // < + fprintf(fp2, "<"); + break; + case '>': // > + fprintf(fp2, ">"); + break; + default: + fprintf(fp2, "%c", temp[k]); + break; + }; + } + if ((online_display_option & 64) && l >= online_gm_display_min_level) + fprintf(fp2, " (GM)"); + fprintf(fp2, "%s %d/%d%s%d/%d%s (%d, %d)%sno zeny%d z
\n"); + fprintf(fp, "\n"); + } + + // Displaying number of online players + if (players == 0) { + fprintf(fp2, "

No user is online.

\n"); + fprintf(fp, "No user is online.\n"); + } else if (players == 1) { + fprintf(fp2, "

%d user is online.

\n", players); + fprintf(fp, "%d user is online.\n", players); + } else { + fprintf(fp2, "

%d users are online.

\n", players); + fprintf(fp, "%d users are online.\n", players); + } + fprintf(fp2, " \n"); + fprintf(fp2, "\n"); + fclose(fp2); + } + fclose(fp); + } + + return; +} + +//--------------------------------------------------------------------- +// This function return the number of online players in all map-servers +//--------------------------------------------------------------------- +int count_users(void) { + int i, users; + + users = 0; + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0) + users += server[i].users; + + return users; +} + +//---------------------------------------- +// Function to send characters to a player +//---------------------------------------- +int mmo_char_send006b(int fd, struct char_session_data *sd) { + int i, j, found_num; + struct mmo_charstatus *p; +//#ifdef NEW_006b + const int offset = 24; +//#else +// const int offset = 4; +//#endif + + set_char_online(-1, 99,sd->account_id); + + found_num = 0; + for(i = 0; i < char_num; i++) { + if (char_dat[i].status.account_id == sd->account_id) { + sd->found_char[found_num] = i; + found_num++; + if (found_num == 9) + break; + } + } + for(i = found_num; i < 9; i++) + sd->found_char[i] = -1; + + WFIFOHEAD(fd, offset + found_num * 106); + memset(WFIFOP(fd,0), 0, offset + found_num * 106); + WFIFOW(fd,0) = 0x6b; + WFIFOW(fd,2) = offset + found_num * 106; + + for(i = 0; i < found_num; i++) { + p = &char_dat[sd->found_char[i]].status; + j = offset + (i * 106); // increase speed of code + + WFIFOL(fd,j) = p->char_id; + WFIFOL(fd,j+4) = p->base_exp>LONG_MAX?LONG_MAX:p->base_exp; + WFIFOL(fd,j+8) = p->zeny; + WFIFOL(fd,j+12) = p->job_exp>LONG_MAX?LONG_MAX:p->job_exp; + WFIFOL(fd,j+16) = p->job_level; + + WFIFOL(fd,j+20) = 0; + WFIFOL(fd,j+24) = 0; + WFIFOL(fd,j+28) = p->option; + + WFIFOL(fd,j+32) = p->karma; + WFIFOL(fd,j+36) = p->manner; + + WFIFOW(fd,j+40) = (p->status_point>SHRT_MAX) ? SHRT_MAX : p->status_point; + WFIFOW(fd,j+42) = (p->hp > SHRT_MAX) ? SHRT_MAX : p->hp; + WFIFOW(fd,j+44) = (p->max_hp > SHRT_MAX) ? SHRT_MAX : p->max_hp; + WFIFOW(fd,j+46) = (p->sp > SHRT_MAX) ? SHRT_MAX : p->sp; + WFIFOW(fd,j+48) = (p->max_sp > SHRT_MAX) ? SHRT_MAX : p->max_sp; + WFIFOW(fd,j+50) = DEFAULT_WALK_SPEED; // p->speed; + WFIFOW(fd,j+52) = p->class_; + WFIFOW(fd,j+54) = p->hair; + WFIFOW(fd,j+56) = p->option&0x20?0:p->weapon; //When the weapon is sent and your option is riding, the client crashes on login!? + WFIFOW(fd,j+58) = p->base_level; + WFIFOW(fd,j+60) = (p->skill_point>SHRT_MAX)? SHRT_MAX : p->skill_point; + WFIFOW(fd,j+62) = p->head_bottom; + WFIFOW(fd,j+64) = p->shield; + WFIFOW(fd,j+66) = p->head_top; + WFIFOW(fd,j+68) = p->head_mid; + WFIFOW(fd,j+70) = p->hair_color; + WFIFOW(fd,j+72) = p->clothes_color; + + memcpy(WFIFOP(fd,j+74), p->name, NAME_LENGTH); + + WFIFOB(fd,j+98) = (p->str > 255) ? 255 : p->str; + WFIFOB(fd,j+99) = (p->agi > 255) ? 255 : p->agi; + WFIFOB(fd,j+100) = (p->vit > 255) ? 255 : p->vit; + WFIFOB(fd,j+101) = (p->int_ > 255) ? 255 : p->int_; + WFIFOB(fd,j+102) = (p->dex > 255) ? 255 : p->dex; + WFIFOB(fd,j+103) = (p->luk > 255) ? 255 : p->luk; + WFIFOB(fd,j+104) = p->char_num; + } + + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +// 離婚(char削除時に使用) +int char_divorce(struct mmo_charstatus *cs) { + if (cs == NULL) + return 0; + + if (cs->partner_id > 0){ + int i, j; + for(i = 0; i < char_num; i++) { + if (char_dat[i].status.char_id == cs->partner_id && char_dat[i].status.partner_id == cs->char_id) { + cs->partner_id = 0; + char_dat[i].status.partner_id = 0; + for(j = 0; j < MAX_INVENTORY; j++) + if (char_dat[i].status.inventory[j].nameid == WEDDING_RING_M || char_dat[i].status.inventory[j].nameid == WEDDING_RING_F) + memset(&char_dat[i].status.inventory[j], 0, sizeof(char_dat[i].status.inventory[0])); + if (cs->inventory[j].nameid == WEDDING_RING_M || cs->inventory[j].nameid == WEDDING_RING_F) + memset(&cs->inventory[j], 0, sizeof(cs->inventory[0])); + return 0; + } + } + } + return 0; +} + +int char_married(int pl1,int pl2) { + return (char_dat[pl1].status.char_id == char_dat[pl2].status.partner_id && char_dat[pl2].status.char_id == char_dat[pl1].status.partner_id); +} + +int char_child(int parent_id, int child_id) { + return (char_dat[parent_id].status.child == char_dat[child_id].status.char_id && + ((char_dat[parent_id].status.char_id == char_dat[child_id].status.father) || + (char_dat[parent_id].status.char_id == char_dat[child_id].status.mother))); +} + +int char_family(int cid1, int cid2, int cid3) { + int i, idx1 = -1, idx2 =-1;//, idx3 =-1; + for(i = 0; i < char_num && (idx1 == -1 || idx2 == -1/* || idx3 == 1*/); i++) + { + if (char_dat[i].status.char_id == cid1) + idx1 = i; + if (char_dat[i].status.char_id == cid2) + idx2 = i; +// if (char_dat[i].status.char_id == cid2) +// idx3 = i; + } + if (idx1 == -1 || idx2 == -1/* || idx3 == -1*/) + return 0; //Some character not found?? + + //Unless the dbs are corrupted, these 3 checks should suffice, even though + //we could do a lot more checks and force cross-reference integrity. + if(char_dat[idx1].status.partner_id == cid2 && + char_dat[idx1].status.child == cid3) + return cid3; //cid1/cid2 parents. cid3 child. + + if(char_dat[idx1].status.partner_id == cid3 && + char_dat[idx1].status.child == cid2) + return cid2; //cid1/cid3 parents. cid2 child. + + if(char_dat[idx2].status.partner_id == cid3 && + char_dat[idx2].status.child == cid1) + return cid1; //cid2/cid3 parents. cid1 child. + return 0; +} + +//Clears the given party id from all characters. +//Since sometimes the party format changes and parties must be wiped, this +//method is required to prevent stress during the "party not found!" stages. +void char_clearparty(int party_id) +{ + int i; + for(i = 0; i < char_num; i++) + { + if (char_dat[i].status.party_id == party_id) + char_dat[i].status.party_id = 0; + } +} + +//------------------------------------------------------------ +// E-mail check: return 0 (not correct) or 1 (valid). by [Yor] +//------------------------------------------------------------ +int e_mail_check(char *email) { + char ch; + char* last_arobas; + + // athena limits + if (strlen(email) < 3 || strlen(email) > 39) + return 0; + + // part of RFC limits (official reference of e-mail description) + if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') + return 0; + + if (email[strlen(email)-1] == '.') + return 0; + + last_arobas = strrchr(email, '@'); + + if (strstr(last_arobas, "@.") != NULL || + strstr(last_arobas, "..") != NULL) + return 0; + + for(ch = 1; ch < 32; ch++) { + if (strchr(last_arobas, ch) != NULL) { + return 0; + break; + } + } + + if (strchr(last_arobas, ' ') != NULL || + strchr(last_arobas, ';') != NULL) + return 0; + + // all correct + return 1; +} + +//---------------------------------------------------------------------- +// Force disconnection of an online player (with account value) by [Yor] +//---------------------------------------------------------------------- +int disconnect_player(int account_id) { + int i; + struct char_session_data *sd; + + // disconnect player if online on char-server + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { + if (sd->account_id == account_id) { + session[i]->eof = 1; + return 1; + } + } + } + + return 0; +} + +// キャラ削除に伴うデータ削除 +static int char_delete(struct mmo_charstatus *cs) { + int j; + + // ペット削除 + if (cs->pet_id) + inter_pet_delete(cs->pet_id); + if (cs->hom_id) + inter_homun_delete(cs->hom_id); + for (j = 0; j < MAX_INVENTORY; j++) + if (cs->inventory[j].card[0] == (short)0xff00) + inter_pet_delete(MakeDWord(cs->inventory[j].card[1],cs->inventory[j].card[2])); + for (j = 0; j < MAX_CART; j++) + if (cs->cart[j].card[0] == (short)0xff00) + inter_pet_delete( MakeDWord(cs->cart[j].card[1],cs->cart[j].card[2]) ); + // ギルド脱退 + if (cs->guild_id) + inter_guild_leave(cs->guild_id, cs->account_id, cs->char_id); + // パーティー脱退 + if (cs->party_id) + inter_party_leave(cs->party_id, cs->account_id, cs->char_id); + // 離婚 + if (cs->partner_id){ + // 離婚情報をmapに通知 + unsigned char buf[10]; + WBUFW(buf,0) = 0x2b12; + WBUFL(buf,2) = cs->char_id; + WBUFL(buf,6) = cs->partner_id; + mapif_sendall(buf,10); + // 離婚 + char_divorce(cs); + } +#ifdef ENABLE_SC_SAVING + status_delete_scdata(cs->account_id, cs->char_id); +#endif + return 0; +} + +int send_accounts_tologin(int tid, unsigned int tick, int id, int data); + +int parse_tologin(int fd) { + int i; + struct char_session_data *sd; + RFIFOHEAD(fd); + + // only login-server can have an access to here. + // so, if it isn't the login-server, we disconnect the session (fd != login_fd). + if (fd != login_fd) + session[fd]->eof = 1; + if(session[fd]->eof) { + if (fd == login_fd) { + ShowWarning("Connection to login-server lost (connection #%d).\n", fd); + login_fd = -1; + } + do_close(fd); + return 0; + } + + sd = (struct char_session_data*)session[fd]->session_data; + + while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { +// printf("parse_tologin: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + + switch(RFIFOW(fd,0)) { + case 0x2711: + if (RFIFOREST(fd) < 3) + return 0; + if (RFIFOB(fd,2)) { +// printf("connect login server error : %d\n", RFIFOB(fd,2)); + ShowError("Can not connect to the login-server.\n"); + ShowError("The server communication passwords (default s1/p1) are probably invalid.\n"); + ShowInfo("Also, please make sure your accounts file (default: accounts.txt) has those values present.\n"); + ShowInfo("The communication passwords can be changed in map_athena.conf and char_athena.conf\n"); + exit(1); + } else { + ShowStatus("Connected to login-server (connection #%d).\n", fd); + + //Send to login accounts currently connected. + send_accounts_tologin(-1, gettick(), 0, 0); + + // if no map-server already connected, display a message... + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0 && server[i].map[0]) // if map-server online and at least 1 map + break; + if (i == MAX_MAP_SERVERS) + ShowStatus("Awaiting maps from map-server.\n"); + } + RFIFOSKIP(fd,3); + break; + + case 0x2713: + if (RFIFOREST(fd) < 51) + return 0; +// printf("parse_tologin 2713 : %d\n", RFIFOB(fd,6)); + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2)) { + if (RFIFOB(fd,6) != 0) { + WFIFOHEAD(i, 3); + WFIFOW(i,0) = 0x6c; + WFIFOB(i,2) = 0x42; + WFIFOSET(i,3); + } else if (max_connect_user == 0 || count_users() < max_connect_user) { +// if (max_connect_user == 0) +// printf("max_connect_user (unlimited) -> accepted.\n"); +// else +// printf("count_users(): %d < max_connect_user (%d) -> accepted.\n", count_users(), max_connect_user); + memcpy(sd->email, RFIFOP(fd, 7), 40); + if (e_mail_check(sd->email) == 0) + strncpy(sd->email, "a@a.com", 40); // default e-mail + sd->connect_until_time = (time_t)RFIFOL(fd,47); + // send characters to player + mmo_char_send006b(i, sd); + } else if(isGM(sd->account_id) >= gm_allow_level) { + sd->connect_until_time = (time_t)RFIFOL(fd,47); + // send characters to player + mmo_char_send006b(i, sd); + } else { + // refuse connection: too much online players +// printf("count_users(): %d < max_connect_use (%d) -> fail...\n", count_users(), max_connect_user); + WFIFOHEAD(i, 3); + WFIFOW(i,0) = 0x6c; + WFIFOW(i,2) = 0; + WFIFOSET(i,3); + } + break; + } + } + RFIFOSKIP(fd,51); + break; + + // Receiving of an e-mail/time limit from the login-server (answer of a request because a player comes back from map-server to char-server) by [Yor] + case 0x2717: + if (RFIFOREST(fd) < 50) + return 0; + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { + if (sd->account_id == RFIFOL(fd,2)) { + memcpy(sd->email, RFIFOP(fd,6), 40); + if (e_mail_check(sd->email) == 0) + strncpy(sd->email, "a@a.com", 40); // default e-mail + sd->connect_until_time = (time_t)RFIFOL(fd,46); + break; + } + } + } + RFIFOSKIP(fd,50); + break; + + // login-server alive packet + case 0x2718: + if (RFIFOREST(fd) < 2) + return 0; + RFIFOSKIP(fd,2); + break; + + // Receiving authentification from Freya-type login server (to avoid char->login->char) + case 0x2719: + if (RFIFOREST(fd) < 18) + return 0; + // to conserv a maximum of authentification, search if account is already authentified and replace it + // that will reduce multiple connection too + for(i = 0; i < AUTH_FIFO_SIZE; i++) + if (auth_fifo[i].account_id == RFIFOL(fd,2)) + break; + // if not found, use next value + if (i == AUTH_FIFO_SIZE) { + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + i = auth_fifo_pos; + auth_fifo_pos++; + } + auth_fifo[i].account_id = RFIFOL(fd,2); + auth_fifo[i].char_id = 0; + auth_fifo[i].login_id1 = RFIFOL(fd,6); + auth_fifo[i].login_id2 = RFIFOL(fd,10); + auth_fifo[i].delflag = 2; // 0: auth_fifo canceled/void, 2: auth_fifo received from login/map server in memory, 1: connection authentified + auth_fifo[i].char_pos = 0; + auth_fifo[i].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) + auth_fifo[i].ip = RFIFOL(fd,14); + RFIFOSKIP(fd,18); + break; + + case 0x2721: // gm reply + if (RFIFOREST(fd) < 10) + return 0; + { + unsigned char buf[10]; + WBUFW(buf,0) = 0x2b0b; + WBUFL(buf,2) = RFIFOL(fd,2); // account + WBUFL(buf,6) = RFIFOL(fd,6); // GM level + mapif_sendall(buf,10); +// printf("parse_tologin: To become GM answer: char -> map.\n"); + } + RFIFOSKIP(fd,10); + break; + + case 0x2723: // changesex reply (modified by [Yor]) + if (RFIFOREST(fd) < 7) + return 0; + { + int acc, sex, i, j; + unsigned char buf[7]; + acc = RFIFOL(fd,2); + sex = RFIFOB(fd,6); + RFIFOSKIP(fd, 7); + if (acc > 0) { + for(i = 0; i < AUTH_FIFO_SIZE; i++) { + if (auth_fifo[i].account_id == acc) + auth_fifo[i].sex = sex; + } + for (i = 0; i < char_num; i++) { + if (char_dat[i].status.account_id == acc) { + int jobclass = char_dat[i].status.class_; + char_dat[i].status.sex = sex; + if (jobclass == JOB_BARD || jobclass == JOB_DANCER || + jobclass == JOB_CLOWN || jobclass == JOB_GYPSY || + jobclass == JOB_BABY_BARD || jobclass == JOB_BABY_DANCER) { + // job modification + if (jobclass == JOB_BARD || jobclass == JOB_DANCER) { + char_dat[i].status.class_ = (sex) ? JOB_BARD : JOB_DANCER; + } else if (jobclass == JOB_CLOWN || jobclass == JOB_GYPSY) { + char_dat[i].status.class_ = (sex) ? JOB_CLOWN : JOB_GYPSY; + } else if (jobclass == JOB_BABY_BARD || jobclass == JOB_BABY_DANCER) { + char_dat[i].status.class_ = (sex) ? JOB_BABY_BARD : JOB_BABY_DANCER; + } + // remove specifical skills of classes 19, 4020 and 4042 + for(j = 315; j <= 322; j++) { + if (char_dat[i].status.skill[j].id > 0 && !char_dat[i].status.skill[j].flag) { + if (char_dat[i].status.skill_point > USHRT_MAX - char_dat[i].status.skill[j].lv) + char_dat[i].status.skill_point = USHRT_MAX; + else + char_dat[i].status.skill_point += char_dat[i].status.skill[j].lv; + char_dat[i].status.skill[j].id = 0; + char_dat[i].status.skill[j].lv = 0; + } + } + // remove specifical skills of classes 20, 4021 and 4043 + for(j = 323; j <= 330; j++) { + if (char_dat[i].status.skill[j].id > 0 && !char_dat[i].status.skill[j].flag) { + if (char_dat[i].status.skill_point > USHRT_MAX - char_dat[i].status.skill[j].lv) + char_dat[i].status.skill_point = USHRT_MAX; + else + char_dat[i].status.skill_point += char_dat[i].status.skill[j].lv; + + char_dat[i].status.skill[j].id = 0; + char_dat[i].status.skill[j].lv = 0; + } + } + } + // to avoid any problem with equipment and invalid sex, equipment is unequiped. + for (j = 0; j < MAX_INVENTORY; j++) { + if (char_dat[i].status.inventory[j].nameid && char_dat[i].status.inventory[j].equip) + char_dat[i].status.inventory[j].equip = 0; + } + char_dat[i].status.weapon = 0; + char_dat[i].status.shield = 0; + char_dat[i].status.head_top = 0; + char_dat[i].status.head_mid = 0; + char_dat[i].status.head_bottom = 0; + + if (char_dat[i].status.guild_id) //If there is a guild, update the guild_member data [Skotlex] + inter_guild_sex_changed(char_dat[i].status.guild_id, acc, char_dat[i].status.char_id, sex); + } + } + // disconnect player if online on char-server + disconnect_player(acc); + } + WBUFW(buf,0) = 0x2b0d; + WBUFL(buf,2) = acc; + WBUFB(buf,6) = sex; + mapif_sendall(buf, 7); + } + break; + + case 0x2726: // Request to send a broadcast message (no answer) + if (RFIFOREST(fd) < 8 || RFIFOREST(fd) < (8 + RFIFOL(fd,4))) + return 0; + if (RFIFOL(fd,4) < 1) + char_log("Receiving a message for broadcast, but message is void." RETCODE); + else { + // at least 1 map-server + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0) + break; + if (i == MAX_MAP_SERVERS) + char_log("'ladmin': Receiving a message for broadcast, but no map-server is online." RETCODE); + else { + unsigned char buf[128]; + char message[4096]; // +1 to add a null terminated if not exist in the packet + int lp; + char *p; + memset(message, '\0', sizeof(message)); + memcpy(message, RFIFOP(fd,8), RFIFOL(fd,4)); + message[sizeof(message)-1] = '\0'; + remove_control_chars((unsigned char *)message); + // remove all first spaces + p = message; + while(p[0] == ' ') + p++; + // if message is only composed of spaces + if (p[0] == '\0') + char_log("Receiving a message for broadcast, but message is only a lot of spaces." RETCODE); + // else send message to all map-servers + else { + if (RFIFOW(fd,2) == 0) { + char_log("'ladmin': Receiving a message for broadcast (message (in yellow): %s)" RETCODE, + message); + lp = 4; + } else { + char_log("'ladmin': Receiving a message for broadcast (message (in blue): %s)" RETCODE, + message); + lp = 8; + } + // split message to max 80 char + while(p[0] != '\0') { // if not finish + if (p[0] == ' ') // jump if first char is a space + p++; + else { + char split[80]; + char* last_space; + sscanf(p, "%79[^\t]", split); // max 79 char, any char (\t is control char and control char was removed before) + split[sizeof(split)-1] = '\0'; // last char always \0 + if ((last_space = strrchr(split, ' ')) != NULL) { // searching space from end of the string + last_space[0] = '\0'; // replace it by NULL to have correct length of split + p++; // to jump the new NULL + } + p += strlen(split); + // send broadcast to all map-servers + WBUFW(buf,0) = 0x3800; + WBUFW(buf,2) = lp + strlen(split) + 1; + WBUFL(buf,4) = 0x65756c62; // only write if in blue (lp = 8) + memcpy(WBUFP(buf,lp), split, strlen(split) + 1); + mapif_sendall(buf, WBUFW(buf,2)); + } + } + } + } + } + RFIFOSKIP(fd,8 + RFIFOL(fd,4)); + break; + + // account_reg2変更通知 + case 0x2729: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { //Receive account_reg2 registry, forward to map servers. + unsigned char buf[ACCOUNT_REG2_NUM*(256+32+2)+16]; + memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2)); +// WBUFW(buf,0) = 0x2b11; + WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex] + mapif_sendall(buf, WBUFW(buf,2)); + RFIFOSKIP(fd, RFIFOW(fd,2)); + } + break; + + // Account deletion notification (from login-server) + case 0x2730: + if (RFIFOREST(fd) < 6) + return 0; + // Deletion of all characters of the account + for(i = 0; i < char_num; i++) { + if (char_dat[i].status.account_id == RFIFOL(fd,2)) { + char_delete(&char_dat[i].status); + if (i < char_num - 1) { + memcpy(&char_dat[i], &char_dat[char_num-1], sizeof(struct character_data)); + // if moved character owns to deleted account, check again it's character + if (char_dat[i].status.account_id == RFIFOL(fd,2)) { + i--; + // Correct moved character reference in the character's owner by [Yor] + } else { + int j, k; + struct char_session_data *sd2; + for (j = 0; j < fd_max; j++) { + if (session[j] && (sd2 = (struct char_session_data*)session[j]->session_data) && + sd2->account_id == char_dat[char_num-1].status.account_id) { + for (k = 0; k < 9; k++) { + if (sd2->found_char[k] == char_num-1) { + sd2->found_char[k] = i; + break; + } + } + break; + } + } + } + } + char_num--; + } + } + // Deletion of the storage + inter_storage_delete(RFIFOL(fd,2)); + // send to all map-servers to disconnect the player + { + unsigned char buf[6]; + WBUFW(buf,0) = 0x2b13; + WBUFL(buf,2) = RFIFOL(fd,2); + mapif_sendall(buf, 6); + } + // disconnect player if online on char-server + disconnect_player(RFIFOL(fd,2)); + RFIFOSKIP(fd,6); + break; + + // State change of account/ban notification (from login-server) by [Yor] + case 0x2731: + if (RFIFOREST(fd) < 11) + return 0; + // send to all map-servers to disconnect the player + { + unsigned char buf[11]; + WBUFW(buf,0) = 0x2b14; + WBUFL(buf,2) = RFIFOL(fd,2); + WBUFB(buf,6) = RFIFOB(fd,6); // 0: change of statut, 1: ban + WBUFL(buf,7) = RFIFOL(fd,7); // status or final date of a banishment + mapif_sendall(buf, 11); + } + // disconnect player if online on char-server + disconnect_player(RFIFOL(fd,2)); + RFIFOSKIP(fd,11); + break; + + // Receiving GM acounts info from login-server (by [Yor]) + case 0x2732: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + unsigned char buf[32000]; + if (gm_account != NULL) + aFree(gm_account); + gm_account = (struct gm_account*)aCalloc(sizeof(struct gm_account) * ((RFIFOW(fd,2) - 4) / 5), 1); + GM_num = 0; + for (i = 4; i < RFIFOW(fd,2); i = i + 5) { + gm_account[GM_num].account_id = RFIFOL(fd,i); + gm_account[GM_num].level = (int)RFIFOB(fd,i+4); + //printf("GM account: %d -> level %d\n", gm_account[GM_num].account_id, gm_account[GM_num].level); + GM_num++; + } + ShowStatus("From login-server: receiving information of %d GM accounts.\n", GM_num); + char_log("From login-server: receiving information of %d GM accounts." RETCODE, GM_num); + create_online_files(); // update online players files (perhaps some online players change of GM level) + // send new gm acccounts level to map-servers + memcpy(buf, RFIFOP(fd,0), RFIFOW(fd,2)); + WBUFW(buf,0) = 0x2b15; + mapif_sendall(buf, RFIFOW(fd,2)); + } + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + + // Receive GM accounts [Freya login server packet by Yor] + case 0x2733: + // add test here to remember that the login-server is Freya-type + // sprintf (login_server_type, "Freya"); + if (RFIFOREST(fd) < 7) + return 0; + { + unsigned char buf[32000]; + int new_level = 0; + for(i = 0; i < GM_num; i++) + if (gm_account[i].account_id == RFIFOL(fd,2)) { + if (gm_account[i].level != (int)RFIFOB(fd,6)) { + gm_account[i].level = (int)RFIFOB(fd,6); + new_level = 1; + } + break; + } + // if not found, add it + if (i == GM_num) { + // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???) + // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) + if (((int)RFIFOB(fd,6)) > 0 && GM_num < 4000) { + if (GM_num == 0) { + gm_account = (struct gm_account*)aMalloc(sizeof(struct gm_account)); + } else { + gm_account = (struct gm_account*)aRealloc(gm_account, sizeof(struct gm_account) * (GM_num + 1)); + } + gm_account[GM_num].account_id = RFIFOL(fd,2); + gm_account[GM_num].level = (int)RFIFOB(fd,6); + new_level = 1; + GM_num++; + if (GM_num >= 4000) { + ShowWarning("4000 GM accounts found. Next GM accounts are not readed.\n"); + char_log("***WARNING: 4000 GM accounts found. Next GM accounts are not readed." RETCODE); + } + } + } + if (new_level == 1) { + int len; + ShowStatus("From login-server: receiving GM account information (%d: level %d).\n", RFIFOL(fd,2), (int)RFIFOB(fd,6)); + char_log("From login-server: receiving a GM account information (%d: level %d)." RETCODE, RFIFOL(fd,2), (int)RFIFOB(fd,6)); + //create_online_files(); // not change online file for only 1 player (in next timer, that will be done + // send gm acccounts level to map-servers + len = 4; + WBUFW(buf,0) = 0x2b15; + + for(i = 0; i < GM_num; i++) { + WBUFL(buf, len) = gm_account[i].account_id; + WBUFB(buf, len+4) = (unsigned char)gm_account[i].level; + len += 5; + } + WBUFW(buf, 2) = len; + mapif_sendall(buf, len); + } + } + RFIFOSKIP(fd,7); + break; + + //Login server request to kick a character out. [Skotlex] + case 0x2734: + if (RFIFOREST(fd) < 6) + return 0; + { + struct online_char_data* character; + int aid = RFIFOL(fd,2); + if ((character = idb_get(online_char_db, aid)) != NULL) + { //Kick out this player. + if (character->server > -1) + { //Kick it from the map server it is on. + mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); + if (!character->waiting_disconnect) + add_timer(gettick()+15000, chardb_waiting_disconnect, character->account_id, 0); + character->waiting_disconnect = 1; + } else { //Manual kick from char server. + struct char_session_data *tsd; + int i; + for(i = 0; i < fd_max; i++) { + if (session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid) + { + WFIFOHEAD(i, 3); + WFIFOW(i,0) = 0x81; + WFIFOB(i,2) = 2; + WFIFOSET(i,3); + break; + } + } + if (i == fd_max) //Shouldn't happen, but just in case. + set_char_offline(99, aid); + } + } + RFIFOSKIP(fd,6); + } + break; + case 0x2735: + { + unsigned char buf[2]; + in_addr_t new_ip = 0; + RFIFOSKIP(fd,2); + + WBUFW(buf,0) = 0x2b1e; + mapif_sendall(buf, 2); + + new_ip = resolve_hostbyname(login_ip_str, NULL, NULL); + if (new_ip && new_ip != login_ip) + login_ip = new_ip; //Update login up. + + new_ip = resolve_hostbyname(char_ip_str, NULL, NULL); + if (new_ip && new_ip != char_ip) + { //Update ip. + WFIFOHEAD(fd,6); + char_ip = new_ip; + ShowInfo("Updating IP for [%s].\n",char_ip_str); + WFIFOW(fd,0) = 0x2736; + WFIFOL(fd,2) = char_ip; + WFIFOSET(fd,6); + } + break; + } + default: + ShowWarning("Unknown packet 0x%04x received from login-server, disconnecting.\n", RFIFOW(fd,0)); + session[fd]->eof = 1; + return 0; + } + } + RFIFOFLUSH(fd); + + return 0; +} + +int request_accreg2(int account_id, int char_id) { + if (login_fd > 0) { + WFIFOHEAD(login_fd, 10); + WFIFOW(login_fd, 0) = 0x272e; + WFIFOL(login_fd, 2) = account_id; + WFIFOL(login_fd, 6) = char_id; + WFIFOSET(login_fd, 10); + return 1; + } + return 0; +} + +//Send packet forward to login-server for account saving +int save_accreg2(unsigned char* buf, int len) { + if (login_fd > 0) { + WFIFOHEAD(login_fd, len+4); + memcpy(WFIFOP(login_fd,4), buf, len); + WFIFOW(login_fd,0) = 0x2728; + WFIFOW(login_fd,2) = len+4; + WFIFOSET(login_fd,len+4); + return 1; + } + return 0; +} + +//Receive Registry information for a character. +int char_parse_Registry(int account_id, int char_id, unsigned char *buf, int buf_len) { + int i,j,p,len; + for (i = 0; i < char_num; i++) { + if (char_dat[i].status.account_id == account_id && char_dat[i].status.char_id == char_id) + break; + } + if(i >= char_num) //Character not found? + return 1; + for(j=0,p=0;j= char_num){ //Character not found? Sent empty packet. + WFIFOW(fd,2)=13; + }else{ + for (p=13,j = 0; j < char_dat[i].global_num; j++) { + if (char_dat[i].global[j].str[0]) { + p+= sprintf(WFIFOP(fd,p), "%s", char_dat[i].global[j].str)+1; //We add 1 to consider the '\0' in place. + p+= sprintf(WFIFOP(fd,p), "%s", char_dat[i].global[j].value)+1; + } + } + WFIFOW(fd,2)=p; + } + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} + +void char_read_fame_list(void) +{ + int i, j, k; + struct fame_list fame_item; + CREATE_BUFFER(id, int, char_num); + + for(i = 0; i < char_num; i++) { + id[i] = i; + for(j = 0; j < i; j++) { + if (char_dat[i].status.fame > char_dat[id[j]].status.fame) { + for(k = i; k > j; k--) + id[k] = id[k-1]; + id[j] = i; // id[i] + break; + } + } + } + + // Empty ranking lists + memset(smith_fame_list, 0, sizeof(smith_fame_list)); + memset(chemist_fame_list, 0, sizeof(chemist_fame_list)); + memset(taekwon_fame_list, 0, sizeof(taekwon_fame_list)); + // Build Blacksmith ranking list + for (i = 0, j = 0; i < char_num && j < fame_list_size_smith; i++) { + if (char_dat[id[i]].status.fame && ( + char_dat[id[i]].status.class_ == JOB_BLACKSMITH || + char_dat[id[i]].status.class_ == JOB_WHITESMITH || + char_dat[id[i]].status.class_ == JOB_BABY_BLACKSMITH)) + { + fame_item.id = char_dat[id[i]].status.char_id; + fame_item.fame = char_dat[id[i]].status.fame; + strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); + + memcpy(&smith_fame_list[j],&fame_item,sizeof(struct fame_list)); + j++; + } + } + // Build Alchemist ranking list + for (i = 0, j = 0; i < char_num && j < fame_list_size_chemist; i++) { + if (char_dat[id[i]].status.fame && ( + char_dat[id[i]].status.class_ == JOB_ALCHEMIST || + char_dat[id[i]].status.class_ == JOB_CREATOR || + char_dat[id[i]].status.class_ == JOB_BABY_ALCHEMIST)) + { + fame_item.id = char_dat[id[i]].status.char_id; + fame_item.fame = char_dat[id[i]].status.fame; + strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); + + memcpy(&chemist_fame_list[j],&fame_item,sizeof(struct fame_list)); + + j++; + } + } + // Build Taekwon ranking list + for (i = 0, j = 0; i < char_num && j < fame_list_size_taekwon; i++) { + if (char_dat[id[i]].status.fame && + char_dat[id[i]].status.class_ == JOB_TAEKWON) + { + fame_item.id = char_dat[id[i]].status.char_id; + fame_item.fame = char_dat[id[i]].status.fame; + strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); + + memcpy(&taekwon_fame_list[j],&fame_item,sizeof(struct fame_list)); + + j++; + } + } + DELETE_BUFFER(id); +} +// Send map-servers the fame ranking lists +int char_send_fame_list(int fd) { + int i, len = 8; + unsigned char buf[32000]; + + WBUFW(buf,0) = 0x2b1b; + + for(i = 0; i < fame_list_size_smith && smith_fame_list[i].id; i++) { + memcpy(WBUFP(buf, len), &smith_fame_list[i], sizeof(struct fame_list)); + len += sizeof(struct fame_list); + } + // add blacksmith's block length + WBUFW(buf, 6) = len; + + for(i = 0; i < fame_list_size_chemist && chemist_fame_list[i].id; i++) { + memcpy(WBUFP(buf, len), &chemist_fame_list[i], sizeof(struct fame_list)); + len += sizeof(struct fame_list); + } + // add alchemist's block length + WBUFW(buf, 4) = len; + + for(i = 0; i < fame_list_size_taekwon && taekwon_fame_list[i].id; i++) { + memcpy(WBUFP(buf, len), &taekwon_fame_list[i], sizeof(struct fame_list)); + len += sizeof(struct fame_list); + } + // add total packet length + WBUFW(buf, 2) = len; + + if(fd!=-1) + mapif_send(fd, buf, len); + else + mapif_sendall(buf, len); + + return 0; +} + +int search_mapserver(unsigned short map, long ip, short port); + +int parse_frommap(int fd) { + int i, j; + int id; + RFIFOHEAD(fd); + + for(id = 0; id < MAX_MAP_SERVERS; id++) + if (server_fd[id] == fd) + break; + if(id==MAX_MAP_SERVERS) + session[fd]->eof=1; + if(session[fd]->eof){ + if (id < MAX_MAP_SERVERS) { + unsigned char buf[16384]; + ShowStatus("Map-server %d has disconnected.\n", id); + //Notify other map servers that this one is gone. [Skotlex] + WBUFW(buf,0) = 0x2b20; + WBUFL(buf,4) = server[id].ip; + WBUFW(buf,8) = server[id].port; + j = 0; + for(i = 0; i < MAX_MAP_PER_SERVER; i++) + if (server[id].map[i]) + WBUFW(buf,10+(j++)*4) = server[id].map[i]; + if (j > 0) { + WBUFW(buf,2) = j * 4 + 10; + mapif_sendallwos(fd, buf, WBUFW(buf,2)); + } + server_fd[id] = -1; + online_char_db->foreach(online_char_db,char_db_setoffline,i); //Tag relevant chars as 'in disconnected' server. + } + do_close(fd); + create_online_files(); + return 0; + } + + while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { + //ShowDebug("Received packet 0x%4x (%d bytes) from map-server (connection %d)\n", RFIFOW(fd, 0), RFIFOREST(fd), fd); + + switch(RFIFOW(fd,0)) { + + // map-server alive packet + case 0x2718: + if (RFIFOREST(fd) < 2) + return 0; + RFIFOSKIP(fd,2); + break; + + // request from map-server to reload GM accounts. Transmission to login-server (by Yor) + case 0x2af7: + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 2); + WFIFOW(login_fd,0) = 0x2709; + WFIFOSET(login_fd, 2); + } + RFIFOSKIP(fd,2); + break; + + // Receiving map names list from the map-server + case 0x2afa: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + memset(server[id].map, 0, sizeof(server[id].map)); + j = 0; + for(i = 4; i < RFIFOW(fd,2); i += 4) { + server[id].map[j] = RFIFOW(fd,i); + j++; + } + { + unsigned char *p = (unsigned char *)&server[id].ip; + ShowStatus("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n", + id, j, p[0], p[1], p[2], p[3], server[id].port); + ShowStatus("Map-server %d loading complete.\n", id); + char_log("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d. Map-server %d loading complete." RETCODE, + id, j, p[0], p[1], p[2], p[3], server[id].port, id); + + if (max_account_id != DEFAULT_MAX_ACCOUNT_ID || max_char_id != DEFAULT_MAX_CHAR_ID) + mapif_send_maxid(max_account_id, max_char_id); //Send the current max ids to the server to keep in sync [Skotlex] + } + WFIFOHEAD(fd, 3 + NAME_LENGTH); + WFIFOW(fd,0) = 0x2afb; + WFIFOB(fd,2) = 0; + memcpy(WFIFOP(fd,3), wisp_server_name, NAME_LENGTH); // name for wisp to player + WFIFOSET(fd,3+NAME_LENGTH); + //WFIFOSET(fd,27); + char_send_fame_list(fd); //Send fame list. + { + unsigned char buf[16384]; + int x; + if (j == 0) { + ShowWarning("Map-Server %d have NO map.\n", id); + char_log("WARNING: Map-Server %d have NO map." RETCODE, id); + // Transmitting maps information to the other map-servers + } else { + WBUFW(buf,0) = 0x2b04; + WBUFW(buf,2) = j * 4 + 10; + WBUFL(buf,4) = server[id].ip; + WBUFW(buf,8) = server[id].port; + memcpy(WBUFP(buf,10), RFIFOP(fd,4), j * 4); + mapif_sendallwos(fd, buf, WBUFW(buf,2)); + } + // Transmitting the maps of the other map-servers to the new map-server + for(x = 0; x < MAX_MAP_SERVERS; x++) { + if (server_fd[x] >= 0 && x != id) { + WFIFOW(fd,0) = 0x2b04; + WFIFOL(fd,4) = server[x].ip; + WFIFOW(fd,8) = server[x].port; + j = 0; + for(i = 0; i < MAX_MAP_PER_SERVER; i++) + if (server[x].map[i]) + WFIFOW(fd,10+(j++)*4) = server[x].map[i]; + if (j > 0) { + WFIFOW(fd,2) = j * 4 + 10; + WFIFOSET(fd,WFIFOW(fd,2)); + } + } + } + } + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + + //Packet command is now used for sc_data request. [Skotlex] + case 0x2afc: + if (RFIFOREST(fd) < 10) + return 0; + { +#ifdef ENABLE_SC_SAVING + int aid, cid; + struct scdata *data; + aid = RFIFOL(fd,2); + cid = RFIFOL(fd,6); +#endif + RFIFOSKIP(fd, 10); +#ifdef ENABLE_SC_SAVING + data = status_search_scdata(aid, cid); + if (data->count > 0) + { //Deliver status change data. + WFIFOW(fd,0) = 0x2b1d; + WFIFOW(fd,2) = 14 + data->count*sizeof(struct status_change_data); + WFIFOL(fd,4) = aid; + WFIFOL(fd,8) = cid; + WFIFOW(fd,12) = data->count; + for (i = 0; i < data->count; i++) + memcpy(WFIFOP(fd,14+i*sizeof(struct status_change_data)), &data->data[i], sizeof(struct status_change_data)); + WFIFOSET(fd, WFIFOW(fd,2)); + status_delete_scdata(aid, cid); //Data sent, so it needs be discarded now. + } +#endif + break; + } + + //set MAP user count + case 0x2afe: + if (RFIFOREST(fd) < 4) + return 0; + if (RFIFOW(fd,2) != server[id].users) { + server[id].users = RFIFOW(fd,2); + ShowInfo("User Count: %d (Server: %d)\n", server[id].users, id); + } + RFIFOSKIP(fd, 4); + break; + //set MAP users + case 0x2aff: + if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + //TODO: When data mismatches memory, update guild/party online/offline states. + server[id].users = RFIFOW(fd,4); + // add online players in the list by [Yor], adapted to use dbs by [Skotlex] + j = 0; + online_char_db->foreach(online_char_db,char_db_setoffline,id); //Set all chars from this server as 'unknown' + for(i = 0; i < server[id].users; i++) { + int aid, cid; + struct online_char_data* character; + aid = RFIFOL(fd,6+i*8); + cid = RFIFOL(fd,6+i*8+4); + character = idb_ensure(online_char_db, aid, create_online_char_data); + if (online_check && character->server > -1 && character->server != id) + { + ShowNotice("Set map user: Character (%d:%d) marked on map server %d, but map server %d claims to have (%d:%d) online!\n", + character->account_id, character->char_id, character->server, id, aid, cid); + mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); + } + character->char_id = cid; + character->server = id; + } + if (update_online < time(NULL)) { // Time is done + update_online = time(NULL) + 8; + create_online_files(); // only every 8 sec. (normally, 1 server send users every 5 sec.) Don't update every time, because that takes time, but only every 2 connection. + // it set to 8 sec because is more than 5 (sec) and if we have more than 1 map-server, informations can be received in shifted. + } + //If any chars remain in -2, they will be cleaned in the cleanup timer. + RFIFOSKIP(fd,6+i*8); + break; + + // キャラデータ保存 + // Recieve character data from map-server + case 0x2b01: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + for(i = 0; i < char_num; i++) { + if (char_dat[i].status.account_id == RFIFOL(fd,4) && + char_dat[i].status.char_id == RFIFOL(fd,8)) + break; + } + if (i != char_num) + memcpy(&char_dat[i].status, RFIFOP(fd,13), sizeof(struct mmo_charstatus)); + if (RFIFOB(fd,12)) + { //Flag, set character offline. [Skotlex] + set_char_offline(RFIFOL(fd,8),RFIFOL(fd,4)); + WFIFOW(fd, 0) = 0x2b21; //Save ack only needed on final save. + WFIFOL(fd, 2) = RFIFOL(fd,4); + WFIFOL(fd, 6) = RFIFOL(fd,8); + WFIFOSET(fd, 10); + } + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + + // キャラセレ要求 + case 0x2b02: + if (RFIFOREST(fd) < 18) + return 0; + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + auth_fifo[auth_fifo_pos].account_id = RFIFOL(fd,2); + auth_fifo[auth_fifo_pos].char_id = 0; + auth_fifo[auth_fifo_pos].login_id1 = RFIFOL(fd,6); + auth_fifo[auth_fifo_pos].login_id2 = RFIFOL(fd,10); + auth_fifo[auth_fifo_pos].delflag = 2; + auth_fifo[auth_fifo_pos].char_pos = 0; + auth_fifo[auth_fifo_pos].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) + auth_fifo[auth_fifo_pos].ip = RFIFOL(fd,14); + auth_fifo_pos++; + WFIFOW(fd,0) = 0x2b03; + WFIFOL(fd,2) = RFIFOL(fd,2); + WFIFOB(fd,6) = 0; + WFIFOSET(fd,7); + RFIFOSKIP(fd,18); + break; + + // request "change map server" + case 0x2b05: + if (RFIFOREST(fd) < 35) + return 0; + { + unsigned short name; + int map_id, map_fd = -1; + struct online_char_data* data; + struct mmo_charstatus* char_data; + + name = RFIFOW(fd,18); + map_id = search_mapserver(name, RFIFOL(fd,24), RFIFOW(fd,28)); //Locate mapserver by ip and port. + if (map_id >= 0) + map_fd = server_fd[map_id]; + for(i = 0; i < char_num; i++) { + if (char_dat[i].status.account_id == RFIFOL(fd,2) && + char_dat[i].status.char_id == RFIFOL(fd,14)) + break; + } + char_data = i< char_num? &char_dat[i].status:NULL; + //Tell the new map server about this player using Kevin's new auth packet. [Skotlex] + if (map_fd>=0 && session[map_fd] && char_data) + { //Send the map server the auth of this player. + //Update the "last map" as this is where the player must be spawned on the new map server. + WFIFOHEAD(map_fd, 20 + sizeof(struct mmo_charstatus)); + char_data->last_point.map = RFIFOW(fd,18); + char_data->last_point.x = RFIFOW(fd,20); + char_data->last_point.y = RFIFOW(fd,22); + char_data->sex = RFIFOB(fd,30); // Buuyo^ + + WFIFOW(map_fd,0) = 0x2afd; + WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus); + WFIFOL(map_fd,4) = RFIFOL(fd, 2); //Account ID + WFIFOL(map_fd,8) = RFIFOL(fd, 6); //Login1 + WFIFOL(map_fd,16) = RFIFOL(fd,10); //Login2 + WFIFOL(map_fd,12) = (unsigned long)0; //TODO: connect_until_time, how do I figure it out right now? + memcpy(WFIFOP(map_fd,20), char_data, sizeof(struct mmo_charstatus)); + WFIFOSET(map_fd, WFIFOW(map_fd,2)); + data = idb_ensure(online_char_db, RFIFOL(fd, 2), create_online_char_data); + data->char_id = char_data->char_id; + data->server = map_id; //Update server where char is. + + //Reply with an ack. + WFIFOW(fd, 0) = 0x2b06; + memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28); + WFIFOSET(fd, 30); + } else { //Reply with nak + WFIFOW(fd, 0) = 0x2b06; + memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28); + WFIFOL(fd, 6) = 0; //Set login1 to 0. + WFIFOSET(fd, 30); + } + RFIFOSKIP(fd, 35); + } + break; + + // キャラ名検索 + case 0x2b08: + if (RFIFOREST(fd) < 6) + return 0; + for(i = 0; i < char_num; i++) { + if (char_dat[i].status.char_id == RFIFOL(fd,2)) + break; + } + WFIFOW(fd,0) = 0x2b09; + WFIFOL(fd,2) = RFIFOL(fd,2); + if (i != char_num) + memcpy(WFIFOP(fd,6), char_dat[i].status.name, NAME_LENGTH); + else + memcpy(WFIFOP(fd,6), unknown_char_name, NAME_LENGTH); + WFIFOSET(fd,6+NAME_LENGTH); + RFIFOSKIP(fd,6); + break; + + // it is a request to become GM + case 0x2b0a: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; +// printf("parse_frommap: change gm -> login, account: %d, pass: '%s'.\n", RFIFOL(fd,4), RFIFOP(fd,8)); + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, RFIFOW(fd,2)); + WFIFOW(login_fd,0) = 0x2720; + memcpy(WFIFOP(login_fd,2), RFIFOP(fd,2), RFIFOW(fd,2)-2); + WFIFOSET(login_fd, RFIFOW(fd,2)); + } else { + WFIFOHEAD(fd, 10); + WFIFOW(fd,0) = 0x2b0b; + WFIFOL(fd,2) = RFIFOL(fd,4); + WFIFOL(fd,6) = 0; + WFIFOSET(fd, 10); + } + RFIFOSKIP(fd, RFIFOW(fd,2)); + break; + + // Map server send information to change an email of an account -> login-server + case 0x2b0c: + if (RFIFOREST(fd) < 86) + return 0; + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 86); + memcpy(WFIFOP(login_fd,0), RFIFOP(fd,0), 86); // 0x2722 .L .40B .40B + WFIFOW(login_fd,0) = 0x2722; + WFIFOSET(login_fd, 86); + } + RFIFOSKIP(fd, 86); + break; + + // Map server ask char-server about a character name to do some operations (all operations are transmitted to login-server) + case 0x2b0e: + if (RFIFOREST(fd) < 44) + return 0; + { + char character_name[NAME_LENGTH]; + int acc = RFIFOL(fd,2); // account_id of who ask (-1 if nobody) + memcpy(character_name, RFIFOP(fd,6), NAME_LENGTH-1); + character_name[NAME_LENGTH -1] = '\0'; + // prepare answer + WFIFOW(fd,0) = 0x2b0f; // answer + WFIFOL(fd,2) = acc; // who want do operation + WFIFOW(fd,30) = RFIFOW(fd, 30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5-changesex + // search character + i = search_character_index(character_name); + if (i >= 0) { + memcpy(WFIFOP(fd,6), search_character_name(i), NAME_LENGTH); // put correct name if found + WFIFOW(fd,6+NAME_LENGTH) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + //WFIFOW(fd,32) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + switch(RFIFOW(fd, 30)) { + case 1: // block + if (acc == -1 || isGM(acc) >= isGM(char_dat[i].status.account_id)) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 10); + WFIFOW(login_fd,0) = 0x2724; + WFIFOL(login_fd,2) = char_dat[i].status.account_id; // account value + WFIFOL(login_fd,6) = 5; // status of the account + WFIFOSET(login_fd, 10); +// printf("char : status -> login: account %d, status: %d \n", char_dat[i].account_id, 5); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 2: // ban + if (acc == -1 || isGM(acc) >= isGM(char_dat[i].status.account_id)) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd,18); + WFIFOW(login_fd, 0) = 0x2725; + WFIFOL(login_fd, 2) = char_dat[i].status.account_id; // account value + WFIFOW(login_fd, 6) = RFIFOW(fd,32); // year + WFIFOW(login_fd, 8) = RFIFOW(fd,34); // month + WFIFOW(login_fd,10) = RFIFOW(fd,36); // day + WFIFOW(login_fd,12) = RFIFOW(fd,38); // hour + WFIFOW(login_fd,14) = RFIFOW(fd,40); // minute + WFIFOW(login_fd,16) = RFIFOW(fd,42); // second + WFIFOSET(login_fd,18); +// printf("char : status -> login: account %d, ban: %dy %dm %dd %dh %dmn %ds\n", +// char_dat[i].account_id, (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), (short)RFIFOW(fd,38), (short)RFIFOW(fd,40), (short)RFIFOW(fd,42)); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 3: // unblock + if (acc == -1 || isGM(acc) >= isGM(char_dat[i].status.account_id)) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 10); + WFIFOW(login_fd,0) = 0x2724; + WFIFOL(login_fd,2) = char_dat[i].status.account_id; // account value + WFIFOL(login_fd,6) = 0; // status of the account + WFIFOSET(login_fd, 10); +// printf("char : status -> login: account %d, status: %d \n", char_dat[i].account_id, 0); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 4: // unban + if (acc == -1 || isGM(acc) >= isGM(char_dat[i].status.account_id)) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 6); + WFIFOW(login_fd, 0) = 0x272a; + WFIFOL(login_fd, 2) = char_dat[i].status.account_id; // account value + WFIFOSET(login_fd, 6); +// printf("char : status -> login: account %d, unban request\n", char_dat[i].account_id); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 5: // changesex + if (acc == -1 || isGM(acc) >= isGM(char_dat[i].status.account_id)) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 6); + WFIFOW(login_fd, 0) = 0x2727; + WFIFOL(login_fd, 2) = char_dat[i].status.account_id; // account value + WFIFOSET(login_fd, 6); +// printf("char : status -> login: account %d, change sex request\n", char_dat[i].account_id); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + } + } else { + // character name not found + memcpy(WFIFOP(fd,6), character_name, NAME_LENGTH); + WFIFOW(fd,8+NAME_LENGTH) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + //WFIFOW(fd,32) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } + // send answer if a player ask, not if the server ask + if (acc != -1) { + //WFIFOSET(fd, 34); + WFIFOSET(fd, 10+NAME_LENGTH); + } + RFIFOSKIP(fd, 44); + break; + } + +// case 0x2b0f: Not used anymore, available for future use + + // Update and send fame ranking list [DracoRPG] + case 0x2b10: + if (RFIFOREST(fd) < 12) + return 0; + { + int cid = RFIFOL(fd, 2); + int fame = RFIFOL(fd, 6); + char type = RFIFOB(fd, 10); + char pos = RFIFOB(fd, 11); + int size; + struct fame_list *list = NULL; + RFIFOSKIP(fd,12); + + switch(type) { + case 1: + size = fame_list_size_smith; + list = smith_fame_list; + break; + case 2: + size = fame_list_size_chemist; + list = chemist_fame_list; + break; + case 3: + size = fame_list_size_taekwon; + list = taekwon_fame_list; + break; + default: + size = 0; + break; + } + if(!size) + break; + if(pos) + { + pos--; //Convert from pos to index. + if( + (pos == 0 || fame < list[pos-1].fame) && + (pos == size-1 || fame > list[pos+1].fame) + ) { //No change in order. + list[(int)pos].fame = fame; + char_send_fame_list(fd); + break; + } + // If the player's already in the list, remove the entry and shift the following ones 1 step up + memmove(list+pos, list+pos+1, (size-pos-1) * sizeof(struct fame_list)); + //Clear out last entry. + list[size-1].id = 0; + list[size-1].fame = 0; + } + // Find the position where the player has to be inserted + for(i = 0; i < size && fame < list[i].fame; i++); + // When found someone with less or as much fame, insert just above + if(i >= size) break;//Out of ranking. + memmove(list+i+1, list+i, (size-i-1) * sizeof(struct fame_list)); + list[i].id = cid; + list[i].fame = fame; + // Look for the player's name + for(j = 0; j < char_num && char_dat[j].status.char_id != id; j++); + if(j < char_num) + strncpy(list[i].name, char_dat[j].status.name, NAME_LENGTH); + else //Not found?? + strncpy(list[i].name, "Unknown", NAME_LENGTH); + char_send_fame_list(-1); + } + break; + + // Recieve rates [Wizputer] + case 0x2b16: + if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,8)) + return 0; + // Txt doesn't need this packet, so just skip it + RFIFOSKIP(fd,RFIFOW(fd,8)); + break; + + // Character disconnected set online 0 [Wizputer] + case 0x2b17: + if (RFIFOREST(fd) < 6) + return 0; + set_char_offline(RFIFOL(fd,2),RFIFOL(fd,6)); + RFIFOSKIP(fd,10); + break; + + // Reset all chars to offline [Wizputer] + case 0x2b18: + set_all_offline(id); + RFIFOSKIP(fd,2); + break; + + // Character set online [Wizputer] + case 0x2b19: + if (RFIFOREST(fd) < 6) + return 0; + set_char_online(id, RFIFOL(fd,2),RFIFOL(fd,6)); + RFIFOSKIP(fd,10); + break; + + // Build and send fame ranking lists [DracoRPG] + case 0x2b1a: + if (RFIFOREST(fd) < 2) + return 0; + char_read_fame_list(); + char_send_fame_list(-1); + RFIFOSKIP(fd,2); + break; + + //Request to save status change data. [Skotlex] + case 0x2b1c: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { +#ifdef ENABLE_SC_SAVING + int count, aid, cid; + struct scdata *data; + aid = RFIFOL(fd, 4); + cid = RFIFOL(fd, 8); + count = RFIFOW(fd, 12); + data = status_search_scdata(aid, cid); + if (data->count != count) + { + data->count = count; + data->data = aRealloc(data->data, count*sizeof(struct status_change_data)); + } + for (i = 0; i < count; i++) + memcpy (&data->data[i], RFIFOP(fd, 14+i*sizeof(struct status_change_data)), sizeof(struct status_change_data)); +#endif + RFIFOSKIP(fd, RFIFOW(fd, 2)); + break; + } + case 0x2736: + if (RFIFOREST(fd) < 6) return 0; + ShowInfo("Updated IP address of Server #%d to %d.%d.%d.%d.\n",id, + (int)RFIFOB(fd,2),(int)RFIFOB(fd,3), + (int)RFIFOB(fd,4),(int)RFIFOB(fd,5)); + server[id].ip = RFIFOL(fd, 2); + RFIFOSKIP(fd,6); + break; + default: + // inter server処理に渡す + { + int r = inter_parse_frommap(fd); + if (r == 1) // 処理できた + break; + if (r == 2) // パケット長が足りない + return 0; + } + // inter server処理でもない場合は切断 + ShowError("Unknown packet 0x%04x from map server, disconnecting.\n", RFIFOW(fd,0)); + session[fd]->eof = 1; + return 0; + } + } + return 0; +} + +int search_mapserver(unsigned short map, long ip, short port) { + int i, j; + + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0) + for (j = 0; server[i].map[j]; j++) + if (server[i].map[j] == map) { + if (ip > 0 && server[i].ip != ip) + continue; + if (port > 0 && server[i].port != port) + continue; + return i; + } + + return -1; +} + +// char_mapifの初期化処理(現在はinter_mapif初期化のみ) +static int char_mapif_init(int fd) { + return inter_mapif_init(fd); +} + +//-------------------------------------------- +// Test to know if an IP come from LAN or WAN. +// Rewrote: Adnvanced subnet check [LuzZza] +//-------------------------------------------- +int lan_subnetcheck(long *p) { + + int i; + unsigned char *sbn, *msk, *src = (unsigned char *)p; + + for(i=0; iclient_addr.sin_addr; + long subnet_map_ip; + + RFIFOHEAD(fd); + + sd = (struct char_session_data*)session[fd]->session_data; + + if (login_fd < 0) + session[fd]->eof = 1; + if(session[fd]->eof) { // disconnect any player (already connected to char-server or coming back from map-server) if login-server is diconnected. + if (fd == login_fd) + login_fd = -1; + if (sd != NULL) + { + struct online_char_data* data = idb_get(online_char_db, sd->account_id); + if (!data || data->server== -1) //If it is not in any server, send it offline. [Skotlex] + set_char_offline(99,sd->account_id); + } + do_close(fd); + return 0; + } + + while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { + cmd = RFIFOW(fd,0); + // crc32のスキップ用 + if( sd==NULL && // 未ログインor管理パケット + RFIFOREST(fd)>=4 && // 最低バイト数制限 & 0x7530,0x7532管理パケ除去 + RFIFOREST(fd)<=21 && // 最大バイト数制限 & サーバーログイン除去 + cmd!=0x20b && // md5通知パケット除去 + (RFIFOREST(fd)<6 || RFIFOW(fd,4)==0x65) ){ // 次に何かパケットが来てるなら、接続でないとだめ + RFIFOSKIP(fd,4); + cmd = RFIFOW(fd,0); + ShowDebug("parse_char : %d crc32 skipped\n",fd); + if(RFIFOREST(fd)==0) + return 0; + } + +//For use in packets that depend on an sd being present [Skotlex] +#define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL) { RFIFOSKIP(fd,rest); return 0; } } + + switch(cmd){ + case 0x20b: //20040622暗号化ragexe対応 + if (RFIFOREST(fd) < 19) + return 0; + RFIFOSKIP(fd,19); + break; + + case 0x65: // 接続要求 + if (RFIFOREST(fd) < 17) + return 0; + { + int GM_value; + WFIFOHEAD(fd, 4); + if (sd) { + //Received again auth packet for already authentified account?? Discard it. + //TODO: Perhaps log this as a hack attempt? + RFIFOSKIP(fd,17); + break; + } + if ((GM_value = isGM(RFIFOL(fd,2)))) + ShowInfo("Account Logged On; Account ID: %d (GM level %d).\n", RFIFOL(fd,2), GM_value); + else + ShowInfo("Account Logged On; Account ID: %d.\n", RFIFOL(fd,2)); + sd = (struct char_session_data*)aCalloc(sizeof(struct char_session_data), 1); + session[fd]->session_data = sd; + strncpy(sd->email, "no mail", 40); // put here a mail without '@' to refuse deletion if we don't receive the e-mail + sd->connect_until_time = 0; // unknow or illimited (not displaying on map-server) + sd->account_id = RFIFOL(fd,2); + sd->login_id1 = RFIFOL(fd,6); + sd->login_id2 = RFIFOL(fd,10); + sd->sex = RFIFOB(fd,16); + // send back account_id + WFIFOL(fd,0) = RFIFOL(fd,2); + WFIFOSET(fd,4); + // search authentification + for(i = 0; i < AUTH_FIFO_SIZE; i++) { + if (auth_fifo[i].account_id == sd->account_id && + auth_fifo[i].login_id1 == sd->login_id1 && +#if CMP_AUTHFIFO_LOGIN2 != 0 + auth_fifo[i].login_id2 == sd->login_id2 && // relate to the versions higher than 18 +#endif + (!check_ip_flag || auth_fifo[i].ip == session[fd]->client_addr.sin_addr.s_addr) && + auth_fifo[i].delflag == 2) { + auth_fifo[i].delflag = 1; + + if (online_check) + { // check if character is not online already. [Skotlex] + struct online_char_data* character; + character = idb_get(online_char_db, sd->account_id); + + if (character) + { + if(character->server > -1) + { //Kick it from the map server it is on. + mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); + if (!character->waiting_disconnect) + add_timer(gettick()+20000, chardb_waiting_disconnect, character->account_id, 0); + character->waiting_disconnect = 1; + /* Not a good idea because this would trigger when you do a char-change from the map server! [Skotlex] + } else { //Manual kick from char server. + struct char_session_data *tsd; + int i; + for(i = 0; i < fd_max; i++) { + if (session[i] && i!=fd && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == sd->account_id) + { + WFIFOW(i,0) = 0x81; + WFIFOB(i,2) = 2; + WFIFOSET(i,3); + break; + } + } + if (i == fd_max) //Shouldn't happen, but just in case. + set_char_offline(99, sd->account_id); + */ + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 8; + WFIFOSET(fd,3); + break; + } + } + } + + if (max_connect_user == 0 || count_users() < max_connect_user) { + if (login_fd > 0) { // don't send request if no login-server + // request to login-server to obtain e-mail/time limit + WFIFOHEAD(login_fd, 6); + WFIFOW(login_fd,0) = 0x2716; + WFIFOL(login_fd,2) = sd->account_id; + WFIFOSET(login_fd,6); + } + // send characters to player + mmo_char_send006b(fd, sd); + } else { + // refuse connection (over populated) + WFIFOW(fd,0) = 0x6c; + WFIFOW(fd,2) = 0; + WFIFOSET(fd,3); + } + break; + } + } + // authentification not found + if (i == AUTH_FIFO_SIZE) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd,19); + WFIFOW(login_fd,0) = 0x2712; // ask login-server to authentify an account + WFIFOL(login_fd,2) = sd->account_id; + WFIFOL(login_fd,6) = sd->login_id1; + WFIFOL(login_fd,10) = sd->login_id2; // relate to the versions higher than 18 + WFIFOB(login_fd,14) = sd->sex; + WFIFOL(login_fd,15) = session[fd]->client_addr.sin_addr.s_addr; + WFIFOSET(login_fd,19); + } else { // if no login-server, we must refuse connection + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6c; + WFIFOW(fd,2) = 0; + WFIFOSET(fd,3); + } + } + } + RFIFOSKIP(fd,17); + break; + + case 0x66: // キャラ選択 + FIFOSD_CHECK(3); + { + int char_num = RFIFOB(fd,2); + struct mmo_charstatus *cd; + RFIFOSKIP(fd,3); + + // if we activated email creation and email is default email + if (email_creation != 0 && strcmp(sd->email, "a@a.com") == 0 && login_fd > 0) { // to modify an e-mail, login-server must be online + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address + WFIFOSET(fd, 3); + break; + } + // otherwise, load the character + for (ch = 0; ch < 9; ch++) + if (sd->found_char[ch] >= 0 && char_dat[sd->found_char[ch]].status.char_num == char_num) + break; + if (ch == 9) + { //Not found?? May be forged packet. + break; + } + cd = &char_dat[sd->found_char[ch]].status; + char_log("Character Selected, Account ID: %d, Character Slot: %d, Character Name: %s." RETCODE, + sd->account_id, char_num, cd->name); + + cd->sex = sd->sex; + + // searching map server + i = search_mapserver(cd->last_point.map,-1,-1); + // if map is not found, we check major cities + if (i < 0) { + unsigned short j; + //First check that there's actually a map server online. + for(j = 0; j < MAX_MAP_SERVERS; j++) + if (server_fd[j] >= 0 && server[j].map[0]) + break; + if (j == MAX_MAP_SERVERS) { + ShowInfo("Connection Closed. No map servers available.\n"); + WFIFOHEAD(fd, 3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + break; + } + if ((i = search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) { + cd->last_point.x = 273; + cd->last_point.y = 354; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) { + cd->last_point.x = 120; + cd->last_point.y = 100; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) { + cd->last_point.x = 160; + cd->last_point.y = 94; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) { + cd->last_point.x = 116; + cd->last_point.y = 57; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) { + cd->last_point.x = 87; + cd->last_point.y = 117; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) { + cd->last_point.x = 94; + cd->last_point.y = 103; + } else { + ShowInfo("Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", mapindex_id2name(cd->last_point.map)); + WFIFOHEAD(fd, 3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + break; + } + ShowWarning("Unable to find map-server for '%s', sending to major city '%s'.\n", mapindex_id2name(cd->last_point.map), mapindex_id2name(j)); + cd->last_point.map = j; + } + { //Send player to map + WFIFOHEAD(fd, 28); + WFIFOW(fd,0) = 0x71; + WFIFOL(fd,2) = cd->char_id; + memcpy(WFIFOP(fd,6), mapindex_id2name(cd->last_point.map), MAP_NAME_LENGTH); + + // Advanced subnet check [LuzZza] + if((subnet_map_ip = lan_subnetcheck((long *)p))) + WFIFOL(fd,22) = subnet_map_ip; + else + WFIFOL(fd,22) = server[i].ip; + + WFIFOW(fd,26) = server[i].port; + WFIFOSET(fd,28); + + ShowInfo("Character selection '%s' (account: %d, slot: %d).\n", + cd->name, sd->account_id, ch); + } + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + auth_fifo[auth_fifo_pos].account_id = sd->account_id; + auth_fifo[auth_fifo_pos].char_id = cd->char_id; + auth_fifo[auth_fifo_pos].login_id1 = sd->login_id1; + auth_fifo[auth_fifo_pos].login_id2 = sd->login_id2; + auth_fifo[auth_fifo_pos].delflag = 0; + auth_fifo[auth_fifo_pos].char_pos = sd->found_char[ch]; + auth_fifo[auth_fifo_pos].sex = sd->sex; + auth_fifo[auth_fifo_pos].connect_until_time = sd->connect_until_time; + auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr.sin_addr.s_addr; + + //Send NEW auth packet [Kevin] + if ((map_fd = server_fd[i]) < 1 || session[map_fd] == NULL) + { //0 Should not be a valid server_fd [Skotlex] + WFIFOHEAD(fd, 3); + ShowError("parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i); + server_fd[i] = -1; + memset(&server[i], 0, sizeof(struct mmo_map_server)); + //Send server closed. + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + break; + } + { //Send auth to server. + WFIFOHEAD(map_fd, 20 + sizeof(struct mmo_charstatus)); + WFIFOW(map_fd,0) = 0x2afd; + WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus); + WFIFOL(map_fd,4) = auth_fifo[auth_fifo_pos].account_id; + WFIFOL(map_fd,8) = auth_fifo[auth_fifo_pos].login_id1; + WFIFOL(map_fd,16) = auth_fifo[auth_fifo_pos].login_id2; + WFIFOL(map_fd,12) = (unsigned long)auth_fifo[auth_fifo_pos].connect_until_time; + memcpy(WFIFOP(map_fd,20), cd, sizeof(struct mmo_charstatus)); + WFIFOSET(map_fd, WFIFOW(map_fd,2)); + } + set_char_online(i, cd->char_id, cd->account_id); + //Sets char online in the party and breaks even share if needed. + inter_party_logged(cd->party_id, cd->account_id, cd->char_id); + + auth_fifo_pos++; + } + break; + + case 0x67: // 作成 + FIFOSD_CHECK(37); + + if(char_new == 0) //turn character creation on/off [Kevin] + i = -2; + else + i = make_new_char(fd, RFIFOP(fd,2)); + //added some better fail reporting to client on the txt version [Kevin] + if (i < 0) + { + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x6e; + switch (i) { + case -1: WFIFOB(fd, 2) = 0x00; break; + case -2: WFIFOB(fd, 2) = 0x02; break; + case -3: WFIFOB(fd, 2) = 0x01; break; + } + WFIFOSET(fd, 3); + RFIFOSKIP(fd, 37); + break; + } + { //Send to player. + WFIFOHEAD(fd, 108); + WFIFOW(fd,0) = 0x6d; + memset(WFIFOP(fd,2), 0, 106); + + WFIFOL(fd,2) = char_dat[i].status.char_id; + WFIFOL(fd,2+4) = char_dat[i].status.base_exp>LONG_MAX?LONG_MAX:char_dat[i].status.base_exp; + WFIFOL(fd,2+8) = char_dat[i].status.zeny; + WFIFOL(fd,2+12) = char_dat[i].status.job_exp>LONG_MAX?LONG_MAX:char_dat[i].status.job_exp; + WFIFOL(fd,2+16) = char_dat[i].status.job_level; + + WFIFOL(fd,2+28) = char_dat[i].status.karma; + WFIFOL(fd,2+32) = char_dat[i].status.manner; + + WFIFOW(fd,2+40) = 0x30; + WFIFOW(fd,2+42) = (char_dat[i].status.hp > SHRT_MAX) ? SHRT_MAX : char_dat[i].status.hp; + WFIFOW(fd,2+44) = (char_dat[i].status.max_hp > SHRT_MAX) ? SHRT_MAX : char_dat[i].status.max_hp; + WFIFOW(fd,2+46) = (char_dat[i].status.sp > SHRT_MAX) ? SHRT_MAX : char_dat[i].status.sp; + WFIFOW(fd,2+48) = (char_dat[i].status.max_sp > SHRT_MAX) ? SHRT_MAX : char_dat[i].status.max_sp; + WFIFOW(fd,2+50) = DEFAULT_WALK_SPEED; // char_dat[i].status.speed; + WFIFOW(fd,2+52) = char_dat[i].status.class_; + WFIFOW(fd,2+54) = char_dat[i].status.hair; + + WFIFOW(fd,2+58) = char_dat[i].status.base_level; + WFIFOW(fd,2+60) = (char_dat[i].status.skill_point > SHRT_MAX) ? SHRT_MAX : char_dat[i].status.skill_point; + + WFIFOW(fd,2+64) = char_dat[i].status.shield; + WFIFOW(fd,2+66) = char_dat[i].status.head_top; + WFIFOW(fd,2+68) = char_dat[i].status.head_mid; + WFIFOW(fd,2+70) = char_dat[i].status.hair_color; + + memcpy(WFIFOP(fd,2+74), char_dat[i].status.name, NAME_LENGTH); + + WFIFOB(fd,2+98) = (char_dat[i].status.str > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.str; + WFIFOB(fd,2+99) = (char_dat[i].status.agi > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.agi; + WFIFOB(fd,2+100) = (char_dat[i].status.vit > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.vit; + WFIFOB(fd,2+101) = (char_dat[i].status.int_ > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.int_; + WFIFOB(fd,2+102) = (char_dat[i].status.dex > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.dex; + WFIFOB(fd,2+103) = (char_dat[i].status.luk > UCHAR_MAX) ? UCHAR_MAX : char_dat[i].status.luk; + WFIFOB(fd,2+104) = char_dat[i].status.char_num; + + WFIFOSET(fd,108); + RFIFOSKIP(fd,37); + } + for(ch = 0; ch < 9; ch++) { + if (sd->found_char[ch] == -1) { + sd->found_char[ch] = i; + break; + } + } + + case 0x68: // delete char //Yor's Fix + FIFOSD_CHECK(46); + { + WFIFOHEAD(fd, 46); + WFIFOHEAD(login_fd,46); + memcpy(email, RFIFOP(fd,6), 40); + if (e_mail_check(email) == 0) + strncpy(email, "a@a.com", 40); // default e-mail + + // if we activated email creation and email is default email + if (email_creation != 0 && strcmp(sd->email, "a@a.com") == 0 && login_fd > 0) { // to modify an e-mail, login-server must be online + // if sended email is incorrect e-mail + if (strcmp(email, "a@a.com") == 0) { + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address + WFIFOSET(fd, 3); + RFIFOSKIP(fd,46); + // we act like we have selected a character + } else { + // we change the packet to set it like selection. + for (i = 0; i < 9; i++) + if (char_dat[sd->found_char[i]].status.char_id == RFIFOL(fd,2)) { + // we save new e-mail + memcpy(sd->email, email, 40); + // we send new e-mail to login-server ('online' login-server is checked before) + WFIFOW(login_fd,0) = 0x2715; + WFIFOL(login_fd,2) = sd->account_id; + memcpy(WFIFOP(login_fd, 6), email, 40); + WFIFOSET(login_fd,46); + // skip part of the packet! (46, but leave the size of select packet: 3) + RFIFOSKIP(fd,43); + // change value to put new packet (char selection) + RFIFOW(fd, 0) = 0x66; + RFIFOB(fd, 2) = char_dat[sd->found_char[i]].status.char_num; + // not send packet, it's modify of actual packet + break; + } + if (i == 9) { + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address + WFIFOSET(fd, 3); + RFIFOSKIP(fd,46); + } + } + + // otherwise, we delete the character + } else { + if (strcmpi(email, sd->email) != 0) { // if it's an invalid email + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address + WFIFOSET(fd, 3); + // if mail is correct + } else { + for (i = 0; i < 9; i++) { + struct mmo_charstatus *cs = NULL; + if ((cs = &char_dat[sd->found_char[i]].status)->char_id == RFIFOL(fd,2)) { + char_delete(cs); // deletion process + + if (sd->found_char[i] != char_num - 1) { + memcpy(&char_dat[sd->found_char[i]], &char_dat[char_num-1], sizeof(struct mmo_charstatus)); + // Correct moved character reference in the character's owner + { + int j, k; + struct char_session_data *sd2; + for (j = 0; j < fd_max; j++) { + if (session[j] && (sd2 = (struct char_session_data*)session[j]->session_data) && + sd2->account_id == char_dat[char_num-1].status.account_id) { + for (k = 0; k < 9; k++) { + if (sd2->found_char[k] == char_num-1) { + sd2->found_char[k] = sd->found_char[i]; + break; + } + } + break; + } + } + } + } + + char_num--; + for(ch = i; ch < 9-1; ch++) + sd->found_char[ch] = sd->found_char[ch+1]; + sd->found_char[8] = -1; + WFIFOW(fd,0) = 0x6f; + WFIFOSET(fd,2); + break; + } + } + + if (i == 9) { + WFIFOW(fd,0) = 0x70; + WFIFOB(fd,2) = 0; + WFIFOSET(fd,3); + } + } + RFIFOSKIP(fd,46); + } + } + break; + + case 0x2af8: // マップサーバーログイン + if (RFIFOREST(fd) < 60) + return 0; + { + char *l_user = RFIFOP(fd, 2); + char *l_pass = RFIFOP(fd, 26); + WFIFOHEAD(fd, 4+5*GM_num); + l_user[23] = '\0'; + l_pass[23] = '\0'; + WFIFOW(fd,0) = 0x2af9; + for(i = 0; i < MAX_MAP_SERVERS; i++) { + if (server_fd[i] < 0) + break; + } + if (i == MAX_MAP_SERVERS || + strcmp(l_user, userid) || + strcmp(l_pass, passwd)){ + WFIFOB(fd,2) = 3; + WFIFOSET(fd,3); + RFIFOSKIP(fd,60); + } else { + int len; + WFIFOB(fd,2) = 0; + session[fd]->func_parse = parse_frommap; + server_fd[i] = fd; + server[i].ip = RFIFOL(fd,54); + server[i].port = RFIFOW(fd,58); + server[i].users = 0; + memset(server[i].map, 0, sizeof(server[i].map)); + WFIFOSET(fd,3); + RFIFOSKIP(fd,60); + realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + char_mapif_init(fd); + // send gm acccounts level to map-servers + len = 4; + WFIFOW(fd,0) = 0x2b15; + for(i = 0; i < GM_num; i++) { + WFIFOL(fd,len) = gm_account[i].account_id; + WFIFOB(fd,len+4) = (unsigned char)gm_account[i].level; + len += 5; + } + WFIFOW(fd,2) = len; + WFIFOSET(fd,len); + return 0; + } + } + break; + + case 0x187: // Alive信号? + if (RFIFOREST(fd) < 6) + return 0; + RFIFOSKIP(fd, 6); + break; + + case 0x7530: // Athena情報所得 + { + WFIFOHEAD(fd, 10); + WFIFOW(fd,0) = 0x7531; + WFIFOB(fd,2) = ATHENA_MAJOR_VERSION; + WFIFOB(fd,3) = ATHENA_MINOR_VERSION; + WFIFOB(fd,4) = ATHENA_REVISION; + WFIFOB(fd,5) = ATHENA_RELEASE_FLAG; + WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG; + WFIFOB(fd,7) = ATHENA_SERVER_INTER | ATHENA_SERVER_CHAR; + WFIFOW(fd,8) = ATHENA_MOD_VERSION; + WFIFOSET(fd,10); + RFIFOSKIP(fd,2); + return 0; + } + case 0x7532: // 接続の切断(defaultと処理は一緒だが明示的にするため) + default: + session[fd]->eof = 1; + return 0; + } + } + RFIFOFLUSH(fd); + return 0; +} + +// Console Command Parser [Wizputer] +int parse_console(char *buf) { + char *type,*command; + + type = (char *)aCalloc(64,1); + command = (char *)aCalloc(64,1); + +// memset(type,0,64); +// memset(command,0,64); + + ShowStatus("Console: %s\n",buf); + + if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 ) + sscanf(buf,"%[^\n]",type); + + ShowDebug("Type of command: %s || Command: %s \n",type,command); + + if(buf) aFree(buf); + if(type) aFree(type); + if(command) aFree(command); + + return 0; +} + +// 全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) +int mapif_sendall(unsigned char *buf, unsigned int len) { + int i, c; + + c = 0; + for(i = 0; i < MAX_MAP_SERVERS; i++) { + int fd; + if ((fd = server_fd[i]) >= 0) { +#if 0 //This seems to have been fixed long long ago. + if (session[fd] == NULL) + { //Could this be the crash's source? [Skotlex] + ShowError("mapif_sendall: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", fd, i); + server_fd[i] = -1; + memset(&server[i], 0, sizeof(struct mmo_map_server)); + continue; + } +#endif + WFIFOHEAD(fd, len); + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd,len); + c++; + } + } + return c; +} + +// 自分以外の全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) +int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { + int i, c; + + c = 0; + for(i = 0; i < MAX_MAP_SERVERS; i++) { + int fd; + if ((fd = server_fd[i]) >= 0 && fd != sfd) { + WFIFOHEAD(fd, len); + if (WFIFOSPACE(fd) < len) //Increase buffer size. + realloc_writefifo(fd, len); + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd, len); + c++; + } + } + return c; +} +// MAPサーバーにデータ送信(map鯖生存確認有り) +int mapif_send(int fd, unsigned char *buf, unsigned int len) { + int i; + + if (fd >= 0) { + for(i = 0; i < MAX_MAP_SERVERS; i++) { + if (fd == server_fd[i]) { + WFIFOHEAD(fd, len); + if (WFIFOSPACE(fd) < len) //Increase buffer size. + realloc_writefifo(fd, len); + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd,len); + return 1; + } + } + } + return 0; +} + +int send_users_tologin(int tid, unsigned int tick, int id, int data) { + int users = count_users(); + unsigned char buf[16]; + + if (login_fd > 0 && session[login_fd]) { + // send number of user to login server + WFIFOHEAD(login_fd, 6); + WFIFOW(login_fd,0) = 0x2714; + WFIFOL(login_fd,2) = users; + WFIFOSET(login_fd,6); + } + // send number of players to all map-servers + WBUFW(buf,0) = 0x2b00; + WBUFL(buf,2) = users; + mapif_sendall(buf, 6); + + return 0; +} + +static int send_accounts_tologin_sub(DBKey key, void* data, va_list ap) { + struct online_char_data* character = (struct online_char_data*)data; + int *i = va_arg(ap, int*); + int count = va_arg(ap, int); + if ((*i) >= count) + return 0; //This is an error that shouldn't happen.... + if(character->server > -1) { + WFIFOHEAD(login_fd, 8+count*4); + WFIFOL(login_fd, 8+(*i)*4) =character->account_id; + (*i)++; + return 1; + } + return 0; +} + +int send_accounts_tologin(int tid, unsigned int tick, int id, int data) { + int users = count_users(), i=0; + + if (login_fd > 0 && session[login_fd]) { + // send account list to login server + WFIFOHEAD(login_fd, 8+users*4); + WFIFOW(login_fd,0) = 0x272d; + WFIFOL(login_fd,4) = users; + online_char_db->foreach(online_char_db, send_accounts_tologin_sub, &i); + WFIFOW(login_fd,2) = 8+ i*4; + WFIFOSET(login_fd,WFIFOW(login_fd,2)); + } + return 0; +} + +int check_connect_login_server(int tid, unsigned int tick, int id, int data) { + if (login_fd > 0 && session[login_fd] != NULL) + return 0; + + ShowInfo("Attempt to connect to login-server...\n"); + login_fd = make_connection(login_ip, login_port); + if (login_fd == -1) + { //Try again later... [Skotlex] + login_fd = 0; + return 0; + } + session[login_fd]->func_parse = parse_tologin; + realloc_fifo(login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + WFIFOHEAD(login_fd, 86); + WFIFOW(login_fd,0) = 0x2710; + memcpy(WFIFOP(login_fd,2), userid, 24); + memcpy(WFIFOP(login_fd,26), passwd, 24); + WFIFOL(login_fd,50) = 0; + WFIFOL(login_fd,54) = char_ip; + WFIFOL(login_fd,58) = char_port; + memcpy(WFIFOP(login_fd,60), server_name, 20); + WFIFOW(login_fd,80) = 0; + WFIFOW(login_fd,82) = char_maintenance; + + WFIFOW(login_fd,84) = char_new_display; //only display (New) if they want to [Kevin] + + WFIFOSET(login_fd,86); + return 1; +} + +//------------------------------------------------ +//Invoked 15 seconds after mapif_disconnectplayer in case the map server doesn't +//replies/disconnect the player we tried to kick. [Skotlex] +//------------------------------------------------ +static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data) +{ + struct online_char_data* character; + if ((character = idb_get(online_char_db, id)) != NULL && character->waiting_disconnect) + { //Mark it offline due to timeout. + set_char_offline(character->char_id, character->account_id); + } + return 0; +} + +//---------------------------------------------------------- +// Return numerical value of a switch configuration by [Yor] +// on/off, english, fran軋is, deutsch, espaol +//---------------------------------------------------------- +int config_switch(const char *str) { + if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) + return 1; + if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) + return 0; + + return atoi(str); +} + +//---------------------------------- +// Reading Lan Support configuration +// Rewrote: Anvanced subnet check [LuzZza] +//---------------------------------- +int char_lan_config_read(const char *lancfgName) { + + FILE *fp; + int line_num = 0; + char line[1024], w1[64], w2[64], w3[64], w4[64]; + + if((fp = fopen(lancfgName, "r")) == NULL) { + ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName); + return 1; + } + + ShowInfo("Reading the configuration file %s...\n", lancfgName); + + while(fgets(line, sizeof(line)-1, fp)) { + + line_num++; + if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n') + continue; + + line[sizeof(line)-1] = '\0'; + if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4) { + + ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num); + continue; + } + + remove_control_chars((unsigned char *)w1); + remove_control_chars((unsigned char *)w2); + remove_control_chars((unsigned char *)w3); + remove_control_chars((unsigned char *)w4); + + if(strcmpi(w1, "subnet") == 0) { + + subnet[subnet_count].mask = inet_addr(w2); + subnet[subnet_count].char_ip = inet_addr(w3); + subnet[subnet_count].map_ip = inet_addr(w4); + subnet[subnet_count].subnet = subnet[subnet_count].char_ip&subnet[subnet_count].mask; + if (subnet[subnet_count].subnet != (subnet[subnet_count].map_ip&subnet[subnet_count].mask)) { + ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4); + continue; + } + + subnet_count++; + } + + ShowStatus("Read information about %d subnetworks.\n", subnet_count); + } + + fclose(fp); + return 0; +} +#endif //TXT_SQL_CONVERT + +int char_config_read(const char *cfgName) { + char line[1024], w1[1024], w2[1024]; + FILE *fp = fopen(cfgName, "r"); + + if (fp == NULL) { + ShowFatalError("Configuration file not found: %s.\n", cfgName); + exit(1); + } + + ShowInfo("Reading configuration file %s...\n", cfgName); + while(fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof(line)-1] = '\0'; + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + remove_control_chars((unsigned char *)w1); + remove_control_chars((unsigned char *)w2); + if(strcmpi(w1,"timestamp_format") == 0) { + strncpy(timestamp_format, w2, 20); + } else if(strcmpi(w1,"console_silent")==0){ + msg_silent = 0; //To always allow the next line to show up. + ShowInfo("Console Silent Setting: %d\n", atoi(w2)); + msg_silent = atoi(w2); +#ifndef TXT_SQL_CONVERT + } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ + stdout_with_ansisequence = config_switch(w2); + } else if (strcmpi(w1, "userid") == 0) { + strncpy(userid, w2, 24); + } else if (strcmpi(w1, "passwd") == 0) { + strncpy(passwd, w2, 24); + } else if (strcmpi(w1, "server_name") == 0) { + strncpy(server_name, w2, 20); + server_name[sizeof(server_name) - 1] = '\0'; + ShowStatus("%s server has been initialized\n", w2); + } else if (strcmpi(w1, "wisp_server_name") == 0) { + if (strlen(w2) >= 4) { + memcpy(wisp_server_name, w2, sizeof(wisp_server_name)); + wisp_server_name[sizeof(wisp_server_name) - 1] = '\0'; + } + } else if (strcmpi(w1, "login_ip") == 0) { + char ip_str[16]; + login_ip = resolve_hostbyname(w2, NULL, ip_str); + if (login_ip) { + strncpy(login_ip_str, w2, sizeof(login_ip_str)); + ShowStatus("Login server IP address : %s -> %s\n", w2, ip_str); + } + } else if (strcmpi(w1, "login_port") == 0) { + login_port = atoi(w2); + } else if (strcmpi(w1, "char_ip") == 0) { + char ip_str[16]; + char_ip = resolve_hostbyname(w2, NULL, ip_str); + if (char_ip){ + strncpy(char_ip_str, w2, sizeof(char_ip_str)); + ShowStatus("Character server IP address : %s -> %s\n", w2, ip_str); + } + } else if (strcmpi(w1, "bind_ip") == 0) { + char ip_str[16]; + bind_ip = resolve_hostbyname(w2, NULL, ip_str); + if (bind_ip) { + strncpy(bind_ip_str, w2, sizeof(bind_ip_str)); + ShowStatus("Character server binding IP address : %s -> %s\n", w2, ip_str); + } + } else if (strcmpi(w1, "char_port") == 0) { + char_port = atoi(w2); + } else if (strcmpi(w1, "char_maintenance") == 0) { + char_maintenance = atoi(w2); + } else if (strcmpi(w1, "char_new") == 0) { + char_new = atoi(w2); + } else if (strcmpi(w1, "char_new_display") == 0) { + char_new_display = atoi(w2); + } else if (strcmpi(w1, "email_creation") == 0) { + email_creation = config_switch(w2); + } else if (strcmpi(w1, "scdata_txt") == 0) { //By Skotlex + strcpy(scdata_txt, w2); +#endif + } else if (strcmpi(w1, "char_txt") == 0) { + strcpy(char_txt, w2); + } else if (strcmpi(w1, "backup_txt") == 0) { //By zanetheinsane + strcpy(backup_txt, w2); + } else if (strcmpi(w1, "friends_txt") == 0) { //By davidsiaw + strcpy(friends_txt, w2); +#ifndef TXT_SQL_CONVERT + } else if (strcmpi(w1, "backup_txt_flag") == 0) { // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. By [Yor] + backup_txt_flag = config_switch(w2); + } else if (strcmpi(w1, "max_connect_user") == 0) { + max_connect_user = atoi(w2); + if (max_connect_user < 0) + max_connect_user = 0; // unlimited online players + } else if(strcmpi(w1, "gm_allow_level") == 0) { + gm_allow_level = atoi(w2); + if(gm_allow_level < 0) + gm_allow_level = 99; + } else if (strcmpi(w1, "check_ip_flag") == 0) { + check_ip_flag = config_switch(w2); + } else if (strcmpi(w1, "online_check") == 0) { + online_check = config_switch(w2); + } else if (strcmpi(w1, "autosave_time") == 0) { + autosave_interval = atoi(w2)*1000; + if (autosave_interval <= 0) + autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; + } else if (strcmpi(w1, "save_log") == 0) { + save_log = config_switch(w2); + } else if (strcmpi(w1, "start_point") == 0) { + char map[32]; + int x, y; + if (sscanf(w2, "%[^,],%d,%d", map, &x, &y) < 3) + continue; + if (strstr(map, ".gat") != NULL) { // Verify at least if '.gat' is in the map name + start_point.map = mapindex_name2id(map); + if (!start_point.map) { + ShowError("Specified start_point %s not found in map-index cache.\n", map); + start_point.map = 0; + } + start_point.x = x; + start_point.y = y; + } + } else if(strcmpi(w1,"log_char")==0) { //log char or not [devil] + log_char = atoi(w2); + } else if (strcmpi(w1, "start_zeny") == 0) { + start_zeny = atoi(w2); + if (start_zeny < 0) + start_zeny = 0; + } else if (strcmpi(w1, "start_weapon") == 0) { + start_weapon = atoi(w2); + if (start_weapon < 0) + start_weapon = 0; + } else if (strcmpi(w1, "start_armor") == 0) { + start_armor = atoi(w2); + if (start_armor < 0) + start_armor = 0; + } else if (strcmpi(w1, "unknown_char_name") == 0) { + strcpy(unknown_char_name, w2); + unknown_char_name[NAME_LENGTH-1] = '\0'; + } else if (strcmpi(w1, "char_log_filename") == 0) { + strcpy(char_log_filename, w2); + } else if (strcmpi(w1, "name_ignoring_case") == 0) { + name_ignoring_case = config_switch(w2); + } else if (strcmpi(w1, "char_name_option") == 0) { + char_name_option = atoi(w2); + } else if (strcmpi(w1, "char_name_letters") == 0) { + strcpy(char_name_letters, w2); +// online files options + } else if (strcmpi(w1, "online_txt_filename") == 0) { + strcpy(online_txt_filename, w2); + } else if (strcmpi(w1, "online_html_filename") == 0) { + strcpy(online_html_filename, w2); + } else if (strcmpi(w1, "online_sorting_option") == 0) { + online_sorting_option = atoi(w2); + } else if (strcmpi(w1, "online_display_option") == 0) { + online_display_option = atoi(w2); + } else if (strcmpi(w1, "online_gm_display_min_level") == 0) { // minimum GM level to display 'GM' when we want to display it + online_gm_display_min_level = atoi(w2); + if (online_gm_display_min_level < 5) // send online file every 5 seconds to player is enough + online_gm_display_min_level = 5; + } else if (strcmpi(w1, "online_refresh_html") == 0) { + online_refresh_html = atoi(w2); + if (online_refresh_html < 1) + online_refresh_html = 1; + } else if(strcmpi(w1,"db_path")==0) { + strcpy(db_path,w2); + } else if (strcmpi(w1, "console") == 0) { + if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) + console = 1; + } else if (strcmpi(w1, "fame_list_alchemist") == 0) { + fame_list_size_chemist = atoi(w2); + if (fame_list_size_chemist > MAX_FAME_LIST) { + ShowWarning("Max fame list size is %d (fame_list_alchemist)\n", MAX_FAME_LIST); + fame_list_size_chemist = MAX_FAME_LIST; + } + } else if (strcmpi(w1, "fame_list_blacksmith") == 0) { + fame_list_size_smith = atoi(w2); + if (fame_list_size_smith > MAX_FAME_LIST) { + ShowWarning("Max fame list size is %d (fame_list_blacksmith)\n", MAX_FAME_LIST); + fame_list_size_smith = MAX_FAME_LIST; + } + } else if (strcmpi(w1, "fame_list_taekwon") == 0) { + fame_list_size_taekwon = atoi(w2); + if (fame_list_size_taekwon > MAX_FAME_LIST) { + ShowWarning("Max fame list size is %d (fame_list_taekwon)\n", MAX_FAME_LIST); + fame_list_size_taekwon = MAX_FAME_LIST; + } + } else if (strcmpi(w1, "guild_exp_rate") == 0) { + guild_exp_rate = atoi(w2); +#endif //TXT_SQL_CONVERT + } else if (strcmpi(w1, "import") == 0) { + char_config_read(w2); + } + } + fclose(fp); + + ShowInfo("done reading %s.\n", cfgName); + return 0; +} + +#ifndef TXT_SQL_CONVERT +int chardb_final(int key, void* data, va_list va) +{ + aFree(data); + return 0; +} +void do_final(void) { + ShowStatus("Terminating server.\n"); + // write online players files with no player + online_char_db->clear(online_char_db, NULL); //clean the db... + create_online_files(); + online_char_db->destroy(online_char_db, NULL); //dispose the db... + + mmo_char_sync(); + inter_save(); + set_all_offline(-1); + flush_fifos(); + + if(gm_account) aFree(gm_account); + if(char_dat) aFree(char_dat); + + delete_session(login_fd); + delete_session(char_fd); + +#ifdef ENABLE_SC_SAVING + status_final(); +#endif + inter_final(); + mapindex_final(); + + char_log("----End of char-server (normal end with closing of all files)." RETCODE); +} + +void set_server_type(void) +{ + SERVER_TYPE = ATHENA_SERVER_CHAR; +} + +static int online_data_cleanup_sub(DBKey key, void *data, va_list ap) +{ + struct online_char_data *character= (struct online_char_data*)data; + if (character->server == -2) //Unknown server.. set them offline + set_char_offline(character->char_id, character->account_id); + if (character->server < 0) + //Free data from players that have not been online for a while. + db_remove(online_char_db, key); + return 0; +} + +static int online_data_cleanup(int tid, unsigned int tick, int id, int data) +{ + online_char_db->foreach(online_char_db, online_data_cleanup_sub); + return 0; +} + +int do_init(int argc, char **argv) { + int i; + + mapindex_init(); //Needed here for the start-point reading. + start_point.map = mapindex_name2id("new_zone01.gat"); + char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]); + char_lan_config_read((argc > 3) ? argv[3] : LOGIN_LAN_CONF_NAME); + + if (strcmp(userid, "s1")==0 && strcmp(passwd, "p1")==0) { + ShowError("Using the default user/password s1/p1 is NOT RECOMMENDED.\n"); + ShowNotice("Please edit your save/account.txt file to create a proper inter-server user/password (gender 'S')\n"); + ShowNotice("And then change the user/password to use in conf/char_athena.conf (or conf/import/char_conf.txt)\n"); + } + + // a newline in the log... + char_log(""); + // moved behind char_config_read in case we changed the filename [celest] + char_log("The char-server starting..." RETCODE); + + if ((naddr_ != 0) && (!login_ip || !char_ip)) { + // The char server should know what IP address it is running on + // - MouseJstr + int localaddr = ntohl(addr_[0]); + unsigned char *ptr = (unsigned char *) &localaddr; + char buf[16]; + sprintf(buf, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);; + if (naddr_ != 1) + ShowStatus("Multiple interfaces detected.. using %s as our IP address\n", buf); + else + ShowStatus("Defaulting to %s as our IP address\n", buf); + if (!login_ip) { + strcpy(login_ip_str, buf); + login_ip = inet_addr(login_ip_str); + } + if (!char_ip) { + strcpy(char_ip_str, buf); + char_ip = inet_addr(char_ip_str); + } + + if (ptr[0] == 192 && ptr[1] == 168) + ShowWarning("Firewall detected.. edit subnet_athena.conf and char_athena.conf\n"); + } + + for(i = 0; i < MAX_MAP_SERVERS; i++) { + memset(&server[i], 0, sizeof(struct mmo_map_server)); + server_fd[i] = -1; + } + + online_char_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + mmo_char_init(); +#ifdef ENABLE_SC_SAVING + status_init(); +#endif + update_online = time(NULL); + create_online_files(); // update online players files at start of the server + + inter_init_txt((argc > 2) ? argv[2] : inter_cfgName); // inter server 初期化 + + set_defaultparse(parse_char); + + char_fd = make_listen_bind(bind_ip?bind_ip:INADDR_ANY,char_port); + + add_timer_func_list(check_connect_login_server, "check_connect_login_server"); + add_timer_func_list(send_users_tologin, "send_users_tologin"); + add_timer_func_list(send_accounts_tologin, "send_accounts_tologin"); + add_timer_func_list(mmo_char_sync_timer, "mmo_char_sync_timer"); + add_timer_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect"); + add_timer_func_list(online_data_cleanup, "online_data_cleanup"); + + add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600 * 1000); + add_timer_interval(gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000); + add_timer_interval(gettick() + 1000, send_users_tologin, 0, 0, 5 * 1000); + add_timer_interval(gettick() + 3600*1000, send_accounts_tologin, 0, 0, 3600*1000); //Sync online accounts every hour + add_timer_interval(gettick() + autosave_interval, mmo_char_sync_timer, 0, 0, autosave_interval); + + char_read_fame_list(); //Read fame lists. + + if(console) { + set_defaultconsoleparse(parse_console); + start_console(); + } + + char_log("The char-server is ready (Server is listening on the port %d)." RETCODE, char_port); + + ShowStatus("The char-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", char_port); + + return 0; +} +#endif //TXT_SQL_CONVERT diff --git a/src/char/char.h b/src/char/char.h index d6ec8e5cb..e70fe7e4f 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -1,58 +1,58 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _CHAR_H_ -#define _CHAR_H_ - -#include "../common/mmo.h" -#include "../common/mapindex.h" - -#define START_CHAR_NUM 150000 -#define MAX_MAP_SERVERS 30 - -#define CHAR_CONF_NAME "conf/char_athena.conf" - -#define LOGIN_LAN_CONF_NAME "conf/subnet_athena.conf" - -#define DEFAULT_AUTOSAVE_INTERVAL 300*1000 - -struct character_data { - struct mmo_charstatus status; - int global_num; - struct global_reg global[GLOBAL_REG_NUM]; -}; - -struct mmo_charstatus* search_character(int aid, int cid); -struct mmo_charstatus* search_character_byname(char* character_name); -int search_character_index(char* character_name); -char * search_character_name(int index); -int search_character_online(int aid, int cid); - -int mapif_sendall(unsigned char *buf, unsigned int len); -int mapif_sendallwos(int fd,unsigned char *buf, unsigned int len); -int mapif_send(int fd,unsigned char *buf, unsigned int len); - -int char_married(int pl1,int pl2); -int char_child(int parent_id, int child_id); -int char_family(int cid1, int cid2, int cid3); -void char_clearparty(int party_id); - -int char_log(char *fmt, ...); - -int request_accreg2(int account_id, int char_id); -int char_parse_Registry(int account_id, int char_id, unsigned char *buf, int len); -int save_accreg2(unsigned char *buf, int len); -int char_account_reg_reply(int fd,int account_id,int char_id); - -extern int char_name_option; -extern char char_name_letters[]; -extern int autosave_interval; -extern char db_path[]; -extern int guild_exp_rate; -extern int log_inter; -//Exported for use in the TXT-SQL converter. -extern char char_txt[]; -int char_config_read(const char *cfgName); -int mmo_char_fromstr(char *str, struct mmo_charstatus *p, struct global_reg *reg, int *reg_num); -int parse_friend_txt(struct mmo_charstatus *p); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _CHAR_H_ +#define _CHAR_H_ + +#include "../common/mmo.h" +#include "../common/mapindex.h" + +#define START_CHAR_NUM 150000 +#define MAX_MAP_SERVERS 30 + +#define CHAR_CONF_NAME "conf/char_athena.conf" + +#define LOGIN_LAN_CONF_NAME "conf/subnet_athena.conf" + +#define DEFAULT_AUTOSAVE_INTERVAL 300*1000 + +struct character_data { + struct mmo_charstatus status; + int global_num; + struct global_reg global[GLOBAL_REG_NUM]; +}; + +struct mmo_charstatus* search_character(int aid, int cid); +struct mmo_charstatus* search_character_byname(char* character_name); +int search_character_index(char* character_name); +char * search_character_name(int index); +int search_character_online(int aid, int cid); + +int mapif_sendall(unsigned char *buf, unsigned int len); +int mapif_sendallwos(int fd,unsigned char *buf, unsigned int len); +int mapif_send(int fd,unsigned char *buf, unsigned int len); + +int char_married(int pl1,int pl2); +int char_child(int parent_id, int child_id); +int char_family(int cid1, int cid2, int cid3); +void char_clearparty(int party_id); + +int char_log(char *fmt, ...); + +int request_accreg2(int account_id, int char_id); +int char_parse_Registry(int account_id, int char_id, unsigned char *buf, int len); +int save_accreg2(unsigned char *buf, int len); +int char_account_reg_reply(int fd,int account_id,int char_id); + +extern int char_name_option; +extern char char_name_letters[]; +extern int autosave_interval; +extern char db_path[]; +extern int guild_exp_rate; +extern int log_inter; +//Exported for use in the TXT-SQL converter. +extern char char_txt[]; +int char_config_read(const char *cfgName); +int mmo_char_fromstr(char *str, struct mmo_charstatus *p, struct global_reg *reg, int *reg_num); +int parse_friend_txt(struct mmo_charstatus *p); +#endif diff --git a/src/char/int_guild.c b/src/char/int_guild.c index 7a34b081f..e6c4105c8 100644 --- a/src/char/int_guild.c +++ b/src/char/int_guild.c @@ -1,1565 +1,1565 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include - -#include "../common/mmo.h" -#include "../common/socket.h" -#include "../common/db.h" -#include "../common/lock.h" -#include "../common/showmsg.h" -#include "char.h" -#include "inter.h" -#include "int_storage.h" -#include "int_guild.h" - -char guild_txt[1024] = "save/guild.txt"; -char castle_txt[1024] = "save/castle.txt"; - -#ifndef TXT_SQL_CONVERT -static struct dbt *guild_db; -static struct dbt *castle_db; - -static int guild_newid = 10000; - -static unsigned int guild_exp[100]; - -int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char_id, int flag, const char *mes); -int mapif_guild_broken(int guild_id, int flag); -int guild_check_empty(struct guild *g); -int guild_calcinfo(struct guild *g); -int mapif_guild_basicinfochanged(int guild_id, int type, const void *data, int len); -int mapif_guild_info(int fd, struct guild *g); -int guild_break_sub(DBKey key, void *data, va_list ap); - -// ギルドデータの文字列への変換 -int inter_guild_tostr(char *str, struct guild *g) { - int i, c, len; - - // 基本データ - len = sprintf(str, "%d\t%s\t%s\t%d,%d,%u,%d,%d\t%s#\t%s#\t", - g->guild_id, g->name, g->master, - g->guild_lv, g->max_member, g->exp, g->skill_point, g->castle_id, - g->mes1, g->mes2); - // メンバー - for(i = 0; i < g->max_member; i++) { - struct guild_member *m = &g->member[i]; - len += sprintf(str + len, "%d,%d,%d,%d,%d,%d,%d,%u,%d,%d\t%s\t", - m->account_id, m->char_id, - m->hair, m->hair_color, m->gender, - m->class_, m->lv, m->exp, m->exp_payper, m->position, - ((m->account_id > 0) ? m->name : "-")); - } - // 役職 - for(i = 0; i < MAX_GUILDPOSITION; i++) { - struct guild_position *p = &g->position[i]; - len += sprintf(str + len, "%d,%d\t%s#\t", p->mode, p->exp_mode, p->name); - } - // エンブレム - len += sprintf(str + len, "%d,%d,", g->emblem_len, g->emblem_id); - for(i = 0; i < g->emblem_len; i++) { - len += sprintf(str + len, "%02x", (unsigned char)(g->emblem_data[i])); - } - len += sprintf(str + len, "$\t"); - // 同盟リスト - c = 0; - for(i = 0; i < MAX_GUILDALLIANCE; i++) - if (g->alliance[i].guild_id > 0) - c++; - len += sprintf(str + len, "%d\t", c); - for(i = 0; i < MAX_GUILDALLIANCE; i++) { - struct guild_alliance *a = &g->alliance[i]; - if (a->guild_id > 0) - len += sprintf(str + len, "%d,%d\t%s\t", a->guild_id, a->opposition, a->name); - } - // 追放リスト - c = 0; - for(i = 0; i < MAX_GUILDEXPULSION; i++) - if (g->expulsion[i].account_id > 0) - c++; - len += sprintf(str + len, "%d\t", c); - for(i = 0; i < MAX_GUILDEXPULSION; i++) { - struct guild_expulsion *e = &g->expulsion[i]; - if (e->account_id > 0) - len += sprintf(str + len, "%d,%d,%d,%d\t%s\t%s\t%s#\t", - e->account_id, e->rsv1, e->rsv2, e->rsv3, - e->name, e->acc, e->mes ); - } - // ギルドスキル - for(i = 0; i < MAX_GUILDSKILL; i++) { - len += sprintf(str + len, "%d,%d ", g->skill[i].id, g->skill[i].lv); - } - len += sprintf(str + len, "\t"); - - return 0; -} -#endif //TXT_SQL_CONVERT -// ギルドデータの文字列からの変換 -int inter_guild_fromstr(char *str, struct guild *g) { - int i, j, c; - unsigned int exp; - int tmp_int[16]; - char tmp_str[4][256]; - char tmp_str2[4096]; - char *pstr; - - // 基本データ - memset(g, 0, sizeof(struct guild)); - if (sscanf(str, "%d\t%[^\t]\t%[^\t]\t%d,%d,%u,%d,%d\t%[^\t]\t%[^\t]\t", &tmp_int[0], - tmp_str[0], tmp_str[1], - &tmp_int[1], &tmp_int[2], &exp, &tmp_int[4], &tmp_int[5], - tmp_str[2], tmp_str[3]) < 8) - return 1; - - g->guild_id = tmp_int[0]; - g->guild_lv = tmp_int[1]; - g->max_member = tmp_int[2]; - g->exp = exp; - g->skill_point = tmp_int[4]; -#ifndef TXT_SQL_CONVERT - g->castle_id = tmp_int[5]; -#endif - memcpy(g->name, tmp_str[0], NAME_LENGTH-1); - memcpy(g->master, tmp_str[1], NAME_LENGTH-1); - memcpy(g->mes1, tmp_str[2], 60); - memcpy(g->mes2, tmp_str[3], 120); - g->mes1[strlen(g->mes1)-1] = 0; - g->mes2[strlen(g->mes2)-1] = 0; - - for(j = 0; j < 6 && str != NULL; j++) // 位置スキップ - str = strchr(str + 1, '\t'); -// printf("GuildBaseInfo OK\n"); - - // メンバー - for(i = 0; i < g->max_member; i++) { - struct guild_member *m = &g->member[i]; - if (sscanf(str + 1, "%d,%d,%d,%d,%d,%d,%d,%u,%d,%d\t%[^\t]\t", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], - &tmp_int[5], &tmp_int[6], &exp, &tmp_int[8], &tmp_int[9], - tmp_str[0]) < 11) - return 1; - m->account_id = tmp_int[0]; - m->char_id = tmp_int[1]; - m->hair = tmp_int[2]; - m->hair_color = tmp_int[3]; - m->gender = tmp_int[4]; - m->class_ = tmp_int[5]; - m->lv = tmp_int[6]; - m->exp = exp; - m->exp_payper = tmp_int[8]; - m->position = tmp_int[9]; - memcpy(m->name, tmp_str[0], NAME_LENGTH-1); - - for(j = 0; j < 2 && str != NULL; j++) // 位置スキップ - str = strchr(str + 1, '\t'); - } -// printf("GuildMemberInfo OK\n"); - // 役職 - i = 0; - while (sscanf(str+1, "%d,%d%n", &tmp_int[0], &tmp_int[1], &j) == 2 && str[1+j] == '\t') { - struct guild_position *p = &g->position[i]; - if (sscanf(str+1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str[0]) < 3) - return 1; - p->mode = tmp_int[0]; - p->exp_mode = tmp_int[1]; - tmp_str[0][strlen(tmp_str[0])-1] = 0; - memcpy(p->name, tmp_str[0], NAME_LENGTH-1); - - for(j = 0; j < 2 && str != NULL; j++) // 位置スキップ - str = strchr(str+1, '\t'); - i++; - } -// printf("GuildPositionInfo OK\n"); - // エンブレム - tmp_int[1] = 0; - if (sscanf(str + 1, "%d,%d,%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str2)< 3 && - sscanf(str + 1, "%d,%[^\t]\t", &tmp_int[0], tmp_str2) < 2) - return 1; - g->emblem_len = tmp_int[0]; - g->emblem_id = tmp_int[1]; - for(i = 0, pstr = tmp_str2; i < g->emblem_len; i++, pstr += 2) { - int c1 = pstr[0], c2 = pstr[1], x1 = 0, x2 = 0; - if (c1 >= '0' && c1 <= '9') x1 = c1 - '0'; - if (c1 >= 'a' && c1 <= 'f') x1 = c1 - 'a' + 10; - if (c1 >= 'A' && c1 <= 'F') x1 = c1 - 'A' + 10; - if (c2 >= '0' && c2 <= '9') x2 = c2 - '0'; - if (c2 >= 'a' && c2 <= 'f') x2 = c2 - 'a' + 10; - if (c2 >= 'A' && c2 <= 'F') x2 = c2 - 'A' + 10; - g->emblem_data[i] = (x1<<4) | x2; - } -// printf("GuildEmblemInfo OK\n"); - str=strchr(str + 1, '\t'); // 位置スキップ - - // 同盟リスト - if (sscanf(str + 1, "%d\t", &c) < 1) - return 1; - str = strchr(str + 1, '\t'); // 位置スキップ - for(i = 0; i < c; i++) { - struct guild_alliance *a = &g->alliance[i]; - if (sscanf(str + 1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str[0]) < 3) - return 1; - a->guild_id = tmp_int[0]; - a->opposition = tmp_int[1]; - memcpy(a->name, tmp_str[0], NAME_LENGTH-1); - - for(j = 0; j < 2 && str != NULL; j++) // 位置スキップ - str = strchr(str + 1, '\t'); - } -// printf("GuildAllianceInfo OK\n"); - // 追放リスト - if (sscanf(str+1, "%d\t", &c) < 1) - return 1; - str = strchr(str + 1, '\t'); // 位置スキップ - for(i = 0; i < c; i++) { - struct guild_expulsion *e = &g->expulsion[i]; - if (sscanf(str + 1, "%d,%d,%d,%d\t%[^\t]\t%[^\t]\t%[^\t]\t", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - tmp_str[0], tmp_str[1], tmp_str[2]) < 6) - return 1; - e->account_id = tmp_int[0]; - e->rsv1 = tmp_int[1]; - e->rsv2 = tmp_int[2]; - e->rsv3 = tmp_int[3]; - memcpy(e->name, tmp_str[0], NAME_LENGTH-1); - memcpy(e->acc, tmp_str[1], 24); - tmp_str[2][strlen(tmp_str[2])-1] = 0; - memcpy(e->mes, tmp_str[2], 40); - - for(j = 0; j < 4 && str != NULL; j++) // 位置スキップ - str = strchr(str + 1, '\t'); - } -// printf("GuildExplusionInfo OK\n"); - // ギルドスキル - for(i = 0; i < MAX_GUILDSKILL; i++) { - if (sscanf(str+1,"%d,%d ", &tmp_int[0], &tmp_int[1]) < 2) - break; - g->skill[i].id = tmp_int[0]; - g->skill[i].lv = tmp_int[1]; - str = strchr(str + 1, ' '); - } - str = strchr(str + 1, '\t'); -// printf("GuildSkillInfo OK\n"); - - return 0; -} -#ifndef TXT_SQL_CONVERT -// ギルド城データの文字列への変換 -int inter_guildcastle_tostr(char *str, struct guild_castle *gc) { - int len; - - len = sprintf(str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", // added Guardian HP [Valaris] - gc->castle_id, gc->guild_id, gc->economy, gc->defense, gc->triggerE, - gc->triggerD, gc->nextTime, gc->payTime, gc->createTime, gc->visibleC, - gc->guardian[0].visible, gc->guardian[1].visible, gc->guardian[2].visible, gc->guardian[3].visible, - gc->guardian[4].visible, gc->guardian[5].visible, gc->guardian[6].visible, gc->guardian[7].visible, - gc->guardian[0].hp, gc->guardian[1].hp, gc->guardian[2].hp, gc->guardian[3].hp, - gc->guardian[4].hp, gc->guardian[5].hp, gc->guardian[6].hp, gc->guardian[7].hp); - - return 0; -} -#endif ///TXT_SQL_CONVERT -// ギルド城データの文字列からの変換 -int inter_guildcastle_fromstr(char *str, struct guild_castle *gc) { - int tmp_int[26]; - - memset(gc, 0, sizeof(struct guild_castle)); - // new structure of guild castle - if (sscanf(str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], - &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], &tmp_int[20], - &tmp_int[21], &tmp_int[22], &tmp_int[23], &tmp_int[24], &tmp_int[25]) == 26) { - gc->castle_id = tmp_int[0]; - gc->guild_id = tmp_int[1]; - gc->economy = tmp_int[2]; - gc->defense = tmp_int[3]; - gc->triggerE = tmp_int[4]; - gc->triggerD = tmp_int[5]; - gc->nextTime = tmp_int[6]; - gc->payTime = tmp_int[7]; - gc->createTime = tmp_int[8]; - gc->visibleC = tmp_int[9]; - gc->guardian[0].visible = tmp_int[10]; - gc->guardian[1].visible = tmp_int[11]; - gc->guardian[2].visible = tmp_int[12]; - gc->guardian[3].visible = tmp_int[13]; - gc->guardian[4].visible = tmp_int[14]; - gc->guardian[5].visible = tmp_int[15]; - gc->guardian[6].visible = tmp_int[16]; - gc->guardian[7].visible = tmp_int[17]; - gc->guardian[0].hp = tmp_int[18]; - gc->guardian[1].hp = tmp_int[19]; - gc->guardian[2].hp = tmp_int[20]; - gc->guardian[3].hp = tmp_int[21]; - gc->guardian[4].hp = tmp_int[22]; - gc->guardian[5].hp = tmp_int[23]; - gc->guardian[6].hp = tmp_int[24]; - gc->guardian[7].hp = tmp_int[25]; // end additions [Valaris] - // old structure of guild castle - } else if (sscanf(str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], - &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], - &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17]) == 18) { - int i; - - gc->castle_id = tmp_int[0]; - gc->guild_id = tmp_int[1]; - gc->economy = tmp_int[2]; - gc->defense = tmp_int[3]; - gc->triggerE = tmp_int[4]; - gc->triggerD = tmp_int[5]; - gc->nextTime = tmp_int[6]; - gc->payTime = tmp_int[7]; - gc->createTime = tmp_int[8]; - gc->visibleC = tmp_int[9]; - gc->guardian[0].visible = tmp_int[10]; - gc->guardian[1].visible = tmp_int[11]; - gc->guardian[2].visible = tmp_int[12]; - gc->guardian[3].visible = tmp_int[13]; - gc->guardian[4].visible = tmp_int[14]; - gc->guardian[5].visible = tmp_int[15]; - gc->guardian[6].visible = tmp_int[16]; - gc->guardian[7].visible = tmp_int[17]; - - for (i = 0; i < MAX_GUARDIANS; i++) - { - if (gc->guardian[i].visible) - gc->guardian[i].hp = 15000 + 2000 * gc->defense; - else - gc->guardian[i].hp = 0; - } - } else { - return 1; - } - - return 0; -} -#ifndef TXT_SQL_CONVERT -// ギルド関連データベース読み込み -int inter_guild_readdb(void) { - int i; - FILE *fp; - char line[1024]; - char path[1024]; - - sprintf(path, "%s%s", db_path, "/exp_guild.txt"); - fp = fopen(path, "r"); - if (fp == NULL) { - ShowError("can't read db/exp_guild.txt\n"); - return 1; - } - i = 0; - while(fgets(line, sizeof(line)-1, fp) && i < 100){ - if (line[0] == '/' && line[1] == '/') - continue; - guild_exp[i] = (unsigned int)atof(line); - i++; - } - fclose(fp); - - return 0; -} - -// ギルドデータの読み込み -int inter_guild_init() { - char line[16384]; - struct guild *g; - struct guild_castle *gc; - FILE *fp; - int i, j, c = 0; - - inter_guild_readdb(); - - guild_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - castle_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - if ((fp = fopen(guild_txt,"r")) == NULL) - return 1; - while(fgets(line, sizeof(line)-1, fp)) { - j = 0; - if (sscanf(line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 && guild_newid <= i) { - guild_newid = i; - continue; - } - - g = (struct guild *) aCalloc(sizeof(struct guild), 1); - if(g == NULL){ - ShowFatalError("int_guild: out of memory!\n"); - exit(0); - } -// memset(g, 0, sizeof(struct guild)); not needed... - if (inter_guild_fromstr(line, g) == 0 && g->guild_id > 0) { - if (g->guild_id >= guild_newid) - guild_newid = g->guild_id + 1; - idb_put(guild_db, g->guild_id, g); - guild_check_empty(g); - guild_calcinfo(g); - } else { - ShowError("int_guild: broken data [%s] line %d\n", guild_txt, c); - aFree(g); - } - c++; - } - fclose(fp); -// printf("int_guild: %s read done (%d guilds)\n", guild_txt, c); - - c = 0;//カウンタ初期化 - - if ((fp = fopen(castle_txt, "r")) == NULL) { - return 1; - } - - while(fgets(line, sizeof(line)-1, fp)) { - gc = (struct guild_castle *) aCalloc(sizeof(struct guild_castle), 1); - if(gc == NULL){ - ShowFatalError("int_guild: out of memory!\n"); - exit(0); - } -// memset(gc, 0, sizeof(struct guild_castle)); No need... - if (inter_guildcastle_fromstr(line, gc) == 0) { - idb_put(castle_db, gc->castle_id, gc); - } else { - ShowError("int_guild: broken data [%s] line %d\n", castle_txt, c); - aFree(gc); - } - c++; - } - - if (!c) { - ShowStatus(" %s - making Default Data...\n", castle_txt); - //デフォルトデータを作成 - for(i = 0; i < MAX_GUILDCASTLE; i++) { - gc = (struct guild_castle *) aCalloc(sizeof(struct guild_castle), 1); - if (gc == NULL) { - ShowFatalError("int_guild: out of memory!\n"); - exit(0); - } - gc->castle_id = i; - idb_put(castle_db, gc->castle_id, gc); - } - ShowStatus(" %s - making done\n",castle_txt); - return 0; - } - - fclose(fp); - - return 0; -} - -void inter_guild_final() { - castle_db->destroy(castle_db, NULL); - guild_db->destroy(guild_db, NULL); - return; -} - -struct guild *inter_guild_search(int guild_id) { - return idb_get(guild_db, guild_id); -} - -// ギルドデータのセーブ用 -int inter_guild_save_sub(DBKey key,void *data,va_list ap) { - char line[16384]; - FILE *fp; - - inter_guild_tostr(line,(struct guild *)data); - fp=va_arg(ap,FILE *); - fprintf(fp,"%s" RETCODE,line); - - return 0; -} - -// ギルド城データのセーブ用 -int inter_castle_save_sub(DBKey key, void *data, va_list ap) { - char line[16384]; - FILE *fp; - - inter_guildcastle_tostr(line, (struct guild_castle *)data); - fp = va_arg(ap, FILE *); - fprintf(fp, "%s" RETCODE, line); - - return 0; -} - -// ギルドデータのセーブ -int inter_guild_save() { - FILE *fp; - int lock; - - if ((fp = lock_fopen(guild_txt, &lock)) == NULL) { - ShowError("int_guild: cant write [%s] !!! data is lost !!!\n", guild_txt); - return 1; - } - guild_db->foreach(guild_db, inter_guild_save_sub, fp); -// fprintf(fp, "%d\t%%newid%%\n", guild_newid); - lock_fclose(fp, guild_txt, &lock); -// printf("int_guild: %s saved.\n", guild_txt); - - if ((fp = lock_fopen(castle_txt,&lock)) == NULL) { - ShowError("int_guild: cant write [%s] !!! data is lost !!!\n", castle_txt); - return 1; - } - castle_db->foreach(castle_db, inter_castle_save_sub, fp); - lock_fclose(fp, castle_txt, &lock); - - return 0; -} - -// ギルド名検索用 -int search_guildname_sub(DBKey key, void *data, va_list ap) { - struct guild *g = (struct guild *)data, **dst; - char *str; - - str = va_arg(ap, char *); - dst = va_arg(ap, struct guild **); - if (strcmpi(g->name, str) == 0) - *dst = g; - return 0; -} - -// ギルド名検索 -struct guild* search_guildname(char *str) { - struct guild *g = NULL; - guild_db->foreach(guild_db, search_guildname_sub, str, &g); - return g; -} - -// ギルドが空かどうかチェック -int guild_check_empty(struct guild *g) { - int i; - - for(i = 0; i < g->max_member; i++) { - if (g->member[i].account_id > 0) { - return 0; - } - } - // 誰もいないので解散 - guild_db->foreach(guild_db, guild_break_sub, g->guild_id); - inter_guild_storage_delete(g->guild_id); - mapif_guild_broken(g->guild_id, 0); - idb_remove(guild_db, g->guild_id); - return 1; -} - -// キャラの競合がないかチェック用 -int guild_check_conflict_sub(DBKey key, void *data, va_list ap) { - struct guild *g = (struct guild *)data; - int guild_id, account_id, char_id, i; - - guild_id = va_arg(ap, int); - account_id = va_arg(ap, int); - char_id = va_arg(ap, int); - - if (g->guild_id == guild_id) // 本来の所属なので問題なし - return 0; - - for(i = 0; i < MAX_GUILD; i++) { - if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) { - // 別のギルドに偽の所属データがあるので脱退 - ShowWarning("int_guild: guild conflict! %d,%d %d!=%d\n", account_id, char_id, guild_id, g->guild_id); - mapif_parse_GuildLeave(-1, g->guild_id, account_id, char_id, 0, "**データ競合**"); - } - } - - return 0; -} -// キャラの競合がないかチェック -int guild_check_conflict(int guild_id, int account_id, int char_id) { - guild_db->foreach(guild_db, guild_check_conflict_sub, guild_id, account_id, char_id); - - return 0; -} - -unsigned int guild_nextexp (int level) -{ - if (level == 0) - return 1; - if (level > 0 && level < 100) - return guild_exp[level-1]; - - return 0; -} - -// ギルドスキルがあるか確認 -int guild_checkskill(struct guild *g, int id) { - int idx = id - GD_SKILLBASE; - - - if(idx < 0 || idx >= MAX_GUILDSKILL) - - return 0; - - return g->skill[idx].lv; -} - -// ギルドの情報の再計算 -int guild_calcinfo(struct guild *g) { - int i, c; - unsigned int nextexp; - struct guild before = *g; - - // スキルIDの設定 - for(i = 0; i < MAX_GUILDSKILL; i++) - g->skill[i].id=i+GD_SKILLBASE; - - // ギルドレベル - if (g->guild_lv <= 0) - g->guild_lv = 1; - nextexp = guild_nextexp(g->guild_lv); - if (nextexp > 0) { - while(g->exp >= nextexp && nextexp > 0) { //fixed guild exp overflow [Kevin] - g->exp -= nextexp; - g->guild_lv++; - g->skill_point++; - nextexp = guild_nextexp(g->guild_lv); - } - } - - // ギルドの次の経験値 - g->next_exp = guild_nextexp(g->guild_lv); - - // メンバ上限(ギルド拡張適用) - g->max_member = 16 + guild_checkskill(g, GD_EXTENSION) * 6; //Guild Extention skill - currently adds 6 to max per skill lv. - if(g->max_member > MAX_GUILD) - { - ShowError("Guild %d:%s has capacity for too many guild members (%d), max supported is %d\n", g->guild_id, g->name, g->max_member, MAX_GUILD); - g->max_member = MAX_GUILD; - } - - // 平均レベルとオンライン人数 - g->average_lv = 0; - g->connect_member = 0; - c = 0; - for(i = 0; i < g->max_member; i++) { - if (g->member[i].account_id > 0) { - g->average_lv += g->member[i].lv; - c++; - if (g->member[i].online > 0) - g->connect_member++; - } - } - if(c) g->average_lv /= c; - - // 全データを送る必要がありそう - if (g->max_member != before.max_member || - g->guild_lv != before.guild_lv || - g->skill_point != before.skill_point) { - mapif_guild_info(-1, g); - return 1; - } - - return 0; -} - -//------------------------------------------------------------------- -// map serverへの通信 - -// ギルド作成可否 -int mapif_guild_created(int fd, int account_id, struct guild *g) { - WFIFOHEAD(fd, 10); - WFIFOW(fd,0) = 0x3830; - WFIFOL(fd,2) = account_id; - if (g != NULL) { - WFIFOL(fd,6) = g->guild_id; - ShowInfo("Created Guild (%d %s)\n", g->guild_id, g->name); - }else{ - WFIFOL(fd,6) = 0; - } - WFIFOSET(fd,10); - return 0; -} - -// ギルド情報見つからず -int mapif_guild_noinfo(int fd, int guild_id) { - WFIFOHEAD(fd, 8); - WFIFOW(fd,0) = 0x3831; - WFIFOW(fd,2) = 8; - WFIFOL(fd,4) = guild_id; - WFIFOSET(fd,8); - ShowNotice("int_guild: info not found %d\n", guild_id); - - return 0; -} - -// ギルド情報まとめ送り -int mapif_guild_info(int fd, struct guild *g) { - unsigned char buf[8+sizeof(struct guild)]; - - WBUFW(buf,0) = 0x3831; - memcpy(buf + 4, g, sizeof(struct guild)); - WBUFW(buf,2) = 4 + sizeof(struct guild); -// printf("int_guild: sizeof(guild)=%d\n", sizeof(struct guild)); - if (fd < 0) - mapif_sendall(buf, WBUFW(buf,2)); - else - mapif_send(fd, buf, WBUFW(buf,2)); -// printf("int_guild: info %d %s\n", p->guild_id, p->name); - - return 0; -} - -// メンバ追加可否 -int mapif_guild_memberadded(int fd, int guild_id, int account_id, int char_id, int flag) { - WFIFOHEAD(fd, 15); - WFIFOW(fd,0) = 0x3832; - WFIFOL(fd,2) = guild_id; - WFIFOL(fd,6) = account_id; - WFIFOL(fd,10) = char_id; - WFIFOB(fd,14) = flag; - WFIFOSET(fd, 15); - - return 0; -} - -// 脱退/追放通知 -int mapif_guild_leaved(int guild_id, int account_id, int char_id, int flag, const char *name, const char *mes) { - unsigned char buf[79]; - - WBUFW(buf, 0) = 0x3834; - WBUFL(buf, 2) = guild_id; - WBUFL(buf, 6) = account_id; - WBUFL(buf,10) = char_id; - WBUFB(buf,14) = flag; - memcpy(WBUFP(buf,15), mes, 40); - memcpy(WBUFP(buf,55), name, NAME_LENGTH); - mapif_sendall(buf, 55+NAME_LENGTH); -// mapif_sendall(buf, 79); - ShowInfo("Character left guild (Guild %d, %d - %s: %s)\n", guild_id, account_id, name, mes); - - return 0; -} - -// オンライン状態とLv更新通知 -int mapif_guild_memberinfoshort(struct guild *g, int idx) { - unsigned char buf[19]; - - WBUFW(buf, 0) = 0x3835; - WBUFL(buf, 2) = g->guild_id; - WBUFL(buf, 6) = g->member[idx].account_id; - WBUFL(buf,10) = g->member[idx].char_id; - WBUFB(buf,14) = (unsigned char)g->member[idx].online; - WBUFW(buf,15) = g->member[idx].lv; - WBUFW(buf,17) = g->member[idx].class_; - mapif_sendall(buf, 19); - return 0; -} - -// 解散通知 -int mapif_guild_broken(int guild_id, int flag) { - unsigned char buf[7]; - - WBUFW(buf,0) = 0x3836; - WBUFL(buf,2) = guild_id; - WBUFB(buf,6) = flag; - mapif_sendall(buf, 7); - ShowInfo("Guild Break (%d)\n", guild_id); - - return 0; -} - -// ギルド内発言 -int mapif_guild_message(int guild_id, int account_id, char *mes, int len, int sfd) { - unsigned char buf[2048]; - - WBUFW(buf,0) = 0x3837; - WBUFW(buf,2) = len + 12; - WBUFL(buf,4) = guild_id; - WBUFL(buf,8) = account_id; - memcpy(WBUFP(buf,12), mes, len); - mapif_sendallwos(sfd, buf, len + 12); - - return 0; -} - -// ギルド基本情報変更通知 -int mapif_guild_basicinfochanged(int guild_id, int type, const void *data, int len) { - unsigned char buf[2048]; - - WBUFW(buf,0) = 0x3839; - WBUFW(buf,2) = len+10; - WBUFL(buf,4) = guild_id; - WBUFW(buf,8) = type; - memcpy(WBUFP(buf,10),data,len); - mapif_sendall(buf,len+10); - return 0; -} - -// ギルドメンバ情報変更通知 -int mapif_guild_memberinfochanged(int guild_id, int account_id, int char_id, int type, const void *data, int len) { - unsigned char buf[4096]; - - WBUFW(buf, 0) = 0x383a; - WBUFW(buf, 2) = len + 18; - WBUFL(buf, 4) = guild_id; - WBUFL(buf, 8) = account_id; - WBUFL(buf,12) = char_id; - WBUFW(buf,16) = type; - memcpy(WBUFP(buf,18), data, len); - mapif_sendall(buf,len+18); - - return 0; -} - -// ギルドスキルアップ通知 -int mapif_guild_skillupack(int guild_id, int skill_num, int account_id) { - unsigned char buf[14]; - - WBUFW(buf, 0) = 0x383c; - WBUFL(buf, 2) = guild_id; - WBUFL(buf, 6) = skill_num; - WBUFL(buf,10) = account_id; - mapif_sendall(buf, 14); - - return 0; -} - -// ギルド同盟/敵対通知 -int mapif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag, const char *name1, const char *name2) { - unsigned char buf[67]; - - WBUFW(buf, 0) = 0x383d; - WBUFL(buf, 2) = guild_id1; - WBUFL(buf, 6) = guild_id2; - WBUFL(buf,10) = account_id1; - WBUFL(buf,14) = account_id2; - WBUFB(buf,18) = flag; - memcpy(WBUFP(buf,19), name1, NAME_LENGTH); - memcpy(WBUFP(buf,19+NAME_LENGTH), name2, NAME_LENGTH); - mapif_sendall(buf,19+2*NAME_LENGTH); -/* - memcpy(WBUFP(buf,43), name2, NAME_LENGTH); - mapif_sendall(buf, 67); -*/ - return 0; -} - -// ギルド役職変更通知 -int mapif_guild_position(struct guild *g, int idx) { - unsigned char buf[2048]; - - WBUFW(buf,0) = 0x383b; - WBUFW(buf,2) = sizeof(struct guild_position) + 12; - WBUFL(buf,4) = g->guild_id; - WBUFL(buf,8) = idx; - memcpy(WBUFP(buf,12), &g->position[idx], sizeof(struct guild_position)); - mapif_sendall(buf, WBUFW(buf,2)); - - return 0; -} - -// ギルド告知変更通知 -int mapif_guild_notice(struct guild *g) { - unsigned char buf[186]; - - WBUFW(buf,0) = 0x383e; - WBUFL(buf,2) = g->guild_id; - memcpy(WBUFP(buf,6), g->mes1, 60); - memcpy(WBUFP(buf,66), g->mes2, 120); - mapif_sendall(buf, 186); - - return 0; -} - -// ギルドエンブレム変更通知 -int mapif_guild_emblem(struct guild *g) { - unsigned char buf[2048]; - - WBUFW(buf,0) = 0x383f; - WBUFW(buf,2) = g->emblem_len + 12; - WBUFL(buf,4) = g->guild_id; - WBUFL(buf,8) = g->emblem_id; - memcpy(WBUFP(buf,12), g->emblem_data, g->emblem_len); - mapif_sendall(buf, WBUFW(buf,2)); - - return 0; -} - -int mapif_guild_master_changed(struct guild *g, int position) -{ - unsigned char buf[12]; - WBUFW(buf,0)=0x3843; - WBUFL(buf,2)=g->guild_id; - WBUFL(buf,6)=position; - mapif_sendall(buf,10); - return 0; -} - -int mapif_guild_castle_dataload(int castle_id, int index, int value) { - unsigned char buf[9]; - - WBUFW(buf,0) = 0x3840; - WBUFW(buf,2) = castle_id; - WBUFB(buf,4) = index; - WBUFL(buf,5) = value; - mapif_sendall(buf,9); - - return 0; -} - -int mapif_guild_castle_datasave(int castle_id, int index, int value) { - unsigned char buf[9]; - - WBUFW(buf,0) = 0x3841; - WBUFW(buf,2) = castle_id; - WBUFB(buf,4) = index; - WBUFL(buf,5) = value; - mapif_sendall(buf,9); - - return 0; -} - -int mapif_guild_castle_alldataload_sub(DBKey key, void *data, va_list ap) { - int fd = va_arg(ap, int); - int *p = va_arg(ap, int*); - - WFIFOHEAD(fd, sizeof(struct guild_castle)); - memcpy(WFIFOP(fd,*p), (struct guild_castle*)data, sizeof(struct guild_castle)); - (*p) += sizeof(struct guild_castle); - - return 0; -} - -int mapif_guild_castle_alldataload(int fd) { - int len = 4; - - WFIFOHEAD(fd, 0); - WFIFOW(fd,0) = 0x3842; - castle_db->foreach(castle_db, mapif_guild_castle_alldataload_sub, fd, &len); - WFIFOW(fd,2) = len; - WFIFOSET(fd, len); - - return 0; -} - -//------------------------------------------------------------------- -// map serverからの通信 - -// ギルド作成要求 -int mapif_parse_CreateGuild(int fd, int account_id, char *name, struct guild_member *master) { - struct guild *g; - int i; - - for(i = 0; i < NAME_LENGTH && name[i]; i++) { - if (!(name[i] & 0xe0) || name[i] == 0x7f) { - ShowInfo("Create Guild: illegal guild name [%s]\n", name); - mapif_guild_created(fd, account_id, NULL); - return 0; - } - } - - if ((g = search_guildname(name)) != NULL) { - ShowInfo("Create Guild: same name guild exists [%s]\n", name); - mapif_guild_created(fd, account_id, NULL); - return 0; - } - - // Check Authorised letters/symbols in the name of the character - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) { - mapif_guild_created(fd,account_id,NULL); - return 0; - } - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) { - mapif_guild_created(fd,account_id,NULL); - return 0; - } - } - - g = (struct guild *) aCalloc(sizeof(struct guild), 1); - if (g == NULL) { - ShowFatalError("int_guild: CreateGuild: out of memory !\n"); - mapif_guild_created(fd, account_id, NULL); - exit(0); - } -// memset(g, 0, sizeof(struct guild)); Meh... - g->guild_id = guild_newid++; - memcpy(g->name, name, NAME_LENGTH-1); - memcpy(g->master, master->name, NAME_LENGTH-1); - memcpy(&g->member[0], master, sizeof(struct guild_member)); - - g->position[0].mode = 0x11; - strcpy(g->position[ 0].name, "GuildMaster"); - strcpy(g->position[MAX_GUILDPOSITION-1].name, "Newbie"); - for(i = 1; i < MAX_GUILDPOSITION-1; i++) - sprintf(g->position[i].name, "Position %d", i + 1); - - // ここでギルド情報計算が必要と思われる - g->max_member = 16; - g->average_lv = master->lv; - for(i = 0; i < MAX_GUILDSKILL; i++) - g->skill[i].id=i + GD_SKILLBASE; - - idb_put(guild_db, g->guild_id, g); - - mapif_guild_created(fd, account_id, g); - mapif_guild_info(fd, g); - - if(log_inter) - inter_log("guild %s (id=%d) created by master %s (id=%d)" RETCODE, - name, g->guild_id, master->name, master->account_id); - - return 0; -} - -// ギルド情報要求 -int mapif_parse_GuildInfo(int fd, int guild_id) { - struct guild *g; - - g = idb_get(guild_db, guild_id); - if (g != NULL){ - guild_calcinfo(g); - mapif_guild_info(fd, g); - } else - mapif_guild_noinfo(fd, guild_id); - - return 0; -} - -// ギルドメンバ追加要求 -int mapif_parse_GuildAddMember(int fd, int guild_id, struct guild_member *m) { - struct guild *g; - int i; - - g = idb_get(guild_db, guild_id); - if (g == NULL) { - mapif_guild_memberadded(fd, guild_id, m->account_id, m->char_id, 1); - return 0; - } - - for(i = 0; i < g->max_member; i++) { - if (g->member[i].account_id == 0) { - memcpy(&g->member[i], m, sizeof(struct guild_member)); - mapif_guild_memberadded(fd, guild_id, m->account_id, m->char_id, 0); - guild_calcinfo(g); - mapif_guild_info(-1, g); - - return 0; - } - } - mapif_guild_memberadded(fd, guild_id, m->account_id, m->char_id, 1); - - return 0; -} - -// ギルド脱退/追放要求 -int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char_id, int flag, const char *mes) { - struct guild *g = NULL; - int i, j; - - g = idb_get(guild_db, guild_id); - if (g != NULL) { - for(i = 0; i < MAX_GUILD; i++) { - if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) { -// printf("%d %d\n", i, (int)(&g->member[i])); -// printf("%d %s\n", i, g->member[i].name); - - if (flag) { // 追放の場合追放リストに入れる - for(j = 0; j < MAX_GUILDEXPULSION; j++) { - if (g->expulsion[j].account_id == 0) - break; - } - if (j == MAX_GUILDEXPULSION) { // 一杯なので古いのを消す - for(j = 0; j < MAX_GUILDEXPULSION - 1; j++) - g->expulsion[j] = g->expulsion[j+1]; - j = MAX_GUILDEXPULSION - 1; - } - g->expulsion[j].account_id = account_id; - memcpy(g->expulsion[j].acc, "dummy", NAME_LENGTH-1); - memcpy(g->expulsion[j].name, g->member[i].name, NAME_LENGTH-1); - memcpy(g->expulsion[j].mes, mes, 40); - } - - mapif_guild_leaved(guild_id, account_id, char_id, flag, g->member[i].name, mes); -// printf("%d %d\n", i, (int)(&g->member[i])); -// printf("%d %s\n", i, (&g->member[i])->name); - memset(&g->member[i], 0, sizeof(struct guild_member)); - - if (guild_check_empty(g) == 0) - mapif_guild_info(-1,g);// まだ人がいるのでデータ送信 - - return 0; - } - } - } - return 0; -} - -// オンライン/Lv更新 -int mapif_parse_GuildChangeMemberInfoShort(int fd, int guild_id, int account_id, int char_id, int online, int lv, int class_) { - struct guild *g; - int i, alv, c; - - g = idb_get(guild_db, guild_id); - if (g == NULL) - return 0; - - g->connect_member = 0; - - alv = 0; - c = 0; - for(i = 0; i < MAX_GUILD; i++) { - if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) { - g->member[i].online = online; - g->member[i].lv = lv; - g->member[i].class_ = class_; - mapif_guild_memberinfoshort(g, i); - } - if (g->member[i].account_id > 0) { - alv += g->member[i].lv; - c++; - } - if (g->member[i].online) - g->connect_member++; - } - - if (c) - // 平均レベル - g->average_lv = alv / c; - - return 0; -} - -// ギルド解散処理用(同盟/敵対を解除) -int guild_break_sub(DBKey key, void *data, va_list ap) { - struct guild *g = (struct guild *)data; - int guild_id = va_arg(ap, int); - int i; - - for(i = 0; i < MAX_GUILDALLIANCE; i++) { - if (g->alliance[i].guild_id == guild_id) - g->alliance[i].guild_id = 0; - } - return 0; -} - -// ギルド解散要求 -int mapif_parse_BreakGuild(int fd, int guild_id) { - struct guild *g; - - g = idb_get(guild_db, guild_id); - if(g == NULL) - return 0; - - guild_db->foreach(guild_db, guild_break_sub, guild_id); - inter_guild_storage_delete(guild_id); - mapif_guild_broken(guild_id, 0); - - if(log_inter) - inter_log("guild %s (id=%d) broken" RETCODE, g->name, guild_id); - - idb_remove(guild_db, guild_id); - return 0; -} - -// ギルドメッセージ送信 -int mapif_parse_GuildMessage(int fd, int guild_id, int account_id, char *mes, int len) { - return mapif_guild_message(guild_id, account_id, mes, len, fd); -} - -// ギルド基本データ変更要求 -int mapif_parse_GuildBasicInfoChange(int fd, int guild_id, int type, const char *data, int len) { - struct guild *g; - short dw = *((short *)data); - - g = idb_get(guild_db, guild_id); - if (g == NULL) - return 0; - - switch(type) { - case GBI_GUILDLV: - if (dw > 0 && g->guild_lv + dw <= 50) { - g->guild_lv+=dw; - g->skill_point+=dw; - } else if (dw < 0 && g->guild_lv + dw >= 1) - g->guild_lv += dw; - mapif_guild_info(-1, g); - return 0; - default: - ShowError("int_guild: GuildBasicInfoChange: Unknown type %d\n", type); - break; - } - mapif_guild_basicinfochanged(guild_id, type, data, len); - - return 0; -} - -// ギルドメンバデータ変更要求 -int mapif_parse_GuildMemberInfoChange(int fd, int guild_id, int account_id, int char_id, int type, const char *data, int len) { - int i; - struct guild *g; - - g = idb_get(guild_db, guild_id); - if(g == NULL) - return 0; - - for(i = 0; i < g->max_member; i++) - if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) - break; - if (i == g->max_member) { - ShowWarning("int_guild: GuildMemberChange: Not found %d,%d in %d[%s]\n", account_id, char_id, guild_id, g->name); - return 0; - } - switch(type) { - case GMI_POSITION: // 役職 - g->member[i].position = *((int *)data); - break; - case GMI_EXP: // EXP - { - unsigned int exp, old_exp=g->member[i].exp; - g->member[i].exp=*((unsigned int *)data); - if (g->member[i].exp > old_exp) - { - exp = g->member[i].exp - old_exp; - if (guild_exp_rate != 100) - exp = exp*guild_exp_rate/100; - if (exp > UINT_MAX - g->exp) - g->exp = UINT_MAX; - else - g->exp+=exp; - guild_calcinfo(g); - mapif_guild_basicinfochanged(guild_id,GBI_EXP,&g->exp,4); - } - break; - } - case GMI_HAIR: - { - g->member[i].hair=*((int *)data); - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - break; - } - case GMI_HAIR_COLOR: - { - g->member[i].hair_color=*((int *)data); - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - break; - } - case GMI_GENDER: - { - g->member[i].gender=*((int *)data); - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - break; - } - case GMI_CLASS: - { - g->member[i].class_=*((int *)data); - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - break; - } - case GMI_LEVEL: - { - g->member[i].lv=*((int *)data); - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - break; - } - - default: - ShowError("int_guild: GuildMemberInfoChange: Unknown type %d\n", type); - break; - } - mapif_guild_memberinfochanged(guild_id, account_id, char_id, type, data, len); - - return 0; -} - -int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender) -{ - return mapif_parse_GuildMemberInfoChange(0, guild_id, account_id, char_id, GMI_GENDER, (const char*)&gender, sizeof(gender)); -} - -// ギルド役職名変更要求 -int mapif_parse_GuildPosition(int fd, int guild_id, int idx, struct guild_position *p) { - struct guild *g = idb_get(guild_db, guild_id); - - if (g == NULL || idx < 0 || idx >= MAX_GUILDPOSITION) { - return 0; - } - memcpy(&g->position[idx], p, sizeof(struct guild_position)); - mapif_guild_position(g, idx); - ShowInfo("int_guild: position [%d] changed\n", idx); - - return 0; -} - -// ギルドスキルアップ要求 -int mapif_parse_GuildSkillUp(int fd, int guild_id, int skill_num, int account_id) { - struct guild *g = idb_get(guild_db, guild_id); - int idx = skill_num - GD_SKILLBASE; - - if (g == NULL || idx < 0 || idx >= MAX_GUILDSKILL) - return 0; - - if (g->skill_point > 0 && g->skill[idx].id > 0 && g->skill[idx].lv < 10) { - g->skill[idx].lv++; - g->skill_point--; - if (guild_calcinfo(g) == 0) - mapif_guild_info(-1, g); - mapif_guild_skillupack(guild_id, skill_num, account_id); - } - - return 0; -} - -//Manual deletion of an alliance when partnering guild does not exists. [Skotlex] -static int mapif_parse_GuildDeleteAlliance(struct guild *g, int guild_id, int account_id1, int account_id2, int flag) -{ - int i; - char name[NAME_LENGTH]; - for(i=0;ialliance[i].guild_id == guild_id) - { - strcpy(name, g->alliance[i].name); - g->alliance[i].guild_id=0; - break; - } - if (i == MAX_GUILDALLIANCE) - return -1; - - mapif_guild_alliance(g->guild_id,guild_id,account_id1,account_id2,flag,g->name,name); - return 0; -} -// ギルド同盟要求 -int mapif_parse_GuildAlliance(int fd, int guild_id1, int guild_id2, int account_id1, int account_id2, int flag) { - struct guild *g[2]; - int j, i; - - g[0] = idb_get(guild_db, guild_id1); - g[1] = idb_get(guild_db, guild_id2); - - if(g[0] && g[1]==NULL && (flag&0x8)) //Requested to remove an alliance with a not found guild. - return mapif_parse_GuildDeleteAlliance(g[0], guild_id2, - account_id1, account_id2, flag); //Try to do a manual removal of said guild. - - if (g[0] == NULL || g[1] == NULL) - return 0; - - if (!(flag & 0x8)) { - for(i = 0; i < 2 - (flag & 1); i++) { - for(j = 0; j < MAX_GUILDALLIANCE; j++) - if (g[i]->alliance[j].guild_id == 0) { - g[i]->alliance[j].guild_id = g[1-i]->guild_id; - memcpy(g[i]->alliance[j].name, g[1-i]->name, NAME_LENGTH-1); - g[i]->alliance[j].opposition = flag & 1; - break; - } - } - } else { // 関係解消 - for(i = 0; i < 2 - (flag & 1); i++) { - for(j = 0; j < MAX_GUILDALLIANCE; j++) - if (g[i]->alliance[j].guild_id == g[1-i]->guild_id && g[i]->alliance[j].opposition == (flag & 1)) { - g[i]->alliance[j].guild_id = 0; - break; - } - } - } - mapif_guild_alliance(guild_id1, guild_id2, account_id1, account_id2, flag, g[0]->name, g[1]->name); - - return 0; -} - -// ギルド告知変更要求 -int mapif_parse_GuildNotice(int fd, int guild_id, const char *mes1, const char *mes2) { - struct guild *g; - - g = idb_get(guild_db, guild_id); - if (g == NULL) - return 0; - memcpy(g->mes1, mes1, 60); - memcpy(g->mes2, mes2, 120); - - return mapif_guild_notice(g); -} - -// ギルドエンブレム変更要求 -int mapif_parse_GuildEmblem(int fd, int len, int guild_id, int dummy, const char *data) { - struct guild *g; - - g = idb_get(guild_db, guild_id); - if (g == NULL) - return 0; - memcpy(g->emblem_data, data, len); - g->emblem_len = len; - g->emblem_id++; - - return mapif_guild_emblem(g); -} - -int mapif_parse_GuildCastleDataLoad(int fd, int castle_id, int index) { - struct guild_castle *gc = idb_get(castle_db, castle_id); - - if (gc == NULL) { - return mapif_guild_castle_dataload(castle_id, 0, 0); - } - switch(index) { - case 1: return mapif_guild_castle_dataload(gc->castle_id, index, gc->guild_id); - case 2: return mapif_guild_castle_dataload(gc->castle_id, index, gc->economy); - case 3: return mapif_guild_castle_dataload(gc->castle_id, index, gc->defense); - case 4: return mapif_guild_castle_dataload(gc->castle_id, index, gc->triggerE); - case 5: return mapif_guild_castle_dataload(gc->castle_id, index, gc->triggerD); - case 6: return mapif_guild_castle_dataload(gc->castle_id, index, gc->nextTime); - case 7: return mapif_guild_castle_dataload(gc->castle_id, index, gc->payTime); - case 8: return mapif_guild_castle_dataload(gc->castle_id, index, gc->createTime); - case 9: return mapif_guild_castle_dataload(gc->castle_id, index, gc->visibleC); - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - return mapif_guild_castle_dataload(gc->castle_id, index, gc->guardian[index-10].visible); - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - case 24: - case 25: - return mapif_guild_castle_dataload(gc->castle_id, index, gc->guardian[index-18].hp); // end additions [Valaris] - - default: - ShowError("mapif_parse_GuildCastleDataLoad ERROR!! (Not found index=%d)\n", index); - return 0; - } - - return 0; -} - -int mapif_parse_GuildCastleDataSave(int fd, int castle_id, int index, int value) { - struct guild_castle *gc= idb_get(castle_db, castle_id); - - if (gc == NULL) { - return mapif_guild_castle_datasave(castle_id, index, value); - } - switch(index) { - case 1: - if (gc->guild_id != value) { - int gid = (value) ? value : gc->guild_id; - struct guild *g = idb_get(guild_db, gid); - if(log_inter) - inter_log("guild %s (id=%d) %s castle id=%d" RETCODE, - (g) ? g->name : "??", gid, (value) ? "occupy" : "abandon", castle_id); - } - gc->guild_id = value; - if(gc->guild_id == 0) { - //Delete guardians. - memset(&gc->guardian, 0, sizeof(gc->guardian)); - } - break; - case 2: gc->economy = value; break; - case 3: gc->defense = value; break; - case 4: gc->triggerE = value; break; - case 5: gc->triggerD = value; break; - case 6: gc->nextTime = value; break; - case 7: gc->payTime = value; break; - case 8: gc->createTime = value; break; - case 9: gc->visibleC = value; break; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - gc->guardian[index-10].visible = value; break; - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - case 24: - case 25: - gc->guardian[index-18].hp = value; break; // end additions [Valaris] - default: - ShowError("mapif_parse_GuildCastleDataSave ERROR!! (Not found index=%d)\n", index); - return 0; - } - - return mapif_guild_castle_datasave(gc->castle_id, index, value); -} - -// ギルドチェック要求 -int mapif_parse_GuildCheck(int fd, int guild_id, int account_id, int char_id) { - return guild_check_conflict(guild_id, account_id, char_id); -} - -int mapif_parse_GuildMasterChange(int fd, int guild_id, const char* name, int len) -{ - struct guild *g = idb_get(guild_db, guild_id); - struct guild_member gm; - int pos; - - if(g==NULL || g->guild_id<=0 || len > NAME_LENGTH) - return 0; - - for (pos = 0; pos < g->max_member && strncmp(g->member[pos].name, name, len); pos++); - - if (pos == g->max_member) - return 0; //Character not found?? - - memcpy(&gm, &g->member[pos], sizeof (struct guild_member)); - memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member)); - memcpy(&g->member[0], &gm, sizeof(struct guild_member)); - - g->member[pos].position = g->member[0].position; - g->member[0].position = 0; //Position 0: guild Master. - strncpy(g->master, name, len); - if (len < NAME_LENGTH) - g->master[len] = '\0'; - - ShowInfo("int_guild: Guildmaster Changed to %s (Guild %d - %s)\n",name, guild_id, g->name); - return mapif_guild_master_changed(g, pos); -} - -// map server からの通信 -// ・1パケットのみ解析すること -// ・パケット長データはinter.cにセットしておくこと -// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない -// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない -int inter_guild_parse_frommap(int fd) { - RFIFOHEAD(fd); - switch(RFIFOW(fd,0)) { - case 0x3030: mapif_parse_CreateGuild(fd, RFIFOL(fd,4), (char*)RFIFOP(fd,8), (struct guild_member *)RFIFOP(fd,32)); break; - case 0x3031: mapif_parse_GuildInfo(fd, RFIFOL(fd,2)); break; - case 0x3032: mapif_parse_GuildAddMember(fd, RFIFOL(fd,4), (struct guild_member *)RFIFOP(fd,8)); break; - case 0x3033: mapif_parse_GuildMasterChange(fd,RFIFOL(fd,4),(const char*)RFIFOP(fd,8),RFIFOW(fd,2)-8); break; - case 0x3034: mapif_parse_GuildLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOB(fd,14), (const char*)RFIFOP(fd,15)); break; - case 0x3035: mapif_parse_GuildChangeMemberInfoShort(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOB(fd,14), RFIFOW(fd,15), RFIFOW(fd,17)); break; - case 0x3036: mapif_parse_BreakGuild(fd, RFIFOL(fd,2)); break; - case 0x3037: mapif_parse_GuildMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), (char*)RFIFOP(fd,12), RFIFOW(fd,2)-12); break; - case 0x3038: mapif_parse_GuildMasterChange(fd,RFIFOL(fd,4),(const char*)RFIFOP(fd,8),RFIFOW(fd,2)-8); break; - case 0x3039: mapif_parse_GuildBasicInfoChange(fd, RFIFOL(fd,4), RFIFOW(fd,8), (const char*)RFIFOP(fd,10), RFIFOW(fd,2)-10); break; - case 0x303A: mapif_parse_GuildMemberInfoChange(fd, RFIFOL(fd,4), RFIFOL(fd,8), RFIFOL(fd,12), RFIFOW(fd,16), (const char*)RFIFOP(fd,18), RFIFOW(fd,2)-18); break; - case 0x303B: mapif_parse_GuildPosition(fd, RFIFOL(fd,4), RFIFOL(fd,8), (struct guild_position *)RFIFOP(fd,12)); break; - case 0x303C: mapif_parse_GuildSkillUp(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; - case 0x303D: mapif_parse_GuildAlliance(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), RFIFOB(fd,18)); break; - case 0x303E: mapif_parse_GuildNotice(fd, RFIFOL(fd,2), (const char*)RFIFOP(fd,6), (const char*)RFIFOP(fd,66)); break; - case 0x303F: mapif_parse_GuildEmblem(fd, RFIFOW(fd,2)-12, RFIFOL(fd,4), RFIFOL(fd,8), (const char*)RFIFOP(fd,12)); break; - case 0x3040: mapif_parse_GuildCastleDataLoad(fd, RFIFOW(fd,2), RFIFOB(fd,4)); break; - case 0x3041: mapif_parse_GuildCastleDataSave(fd, RFIFOW(fd,2), RFIFOB(fd,4), RFIFOL(fd,5)); break; - - default: - return 0; - } - - return 1; -} - -// マップサーバーの接続時処理 -int inter_guild_mapif_init(int fd) { - return mapif_guild_castle_alldataload(fd); -} - -// サーバーから脱退要求(キャラ削除用) -int inter_guild_leave(int guild_id, int account_id, int char_id) { - return mapif_parse_GuildLeave(-1, guild_id, account_id, char_id, 0, "** Character Deleted **"); -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include + +#include "../common/mmo.h" +#include "../common/socket.h" +#include "../common/db.h" +#include "../common/lock.h" +#include "../common/showmsg.h" +#include "char.h" +#include "inter.h" +#include "int_storage.h" +#include "int_guild.h" + +char guild_txt[1024] = "save/guild.txt"; +char castle_txt[1024] = "save/castle.txt"; + +#ifndef TXT_SQL_CONVERT +static struct dbt *guild_db; +static struct dbt *castle_db; + +static int guild_newid = 10000; + +static unsigned int guild_exp[100]; + +int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char_id, int flag, const char *mes); +int mapif_guild_broken(int guild_id, int flag); +int guild_check_empty(struct guild *g); +int guild_calcinfo(struct guild *g); +int mapif_guild_basicinfochanged(int guild_id, int type, const void *data, int len); +int mapif_guild_info(int fd, struct guild *g); +int guild_break_sub(DBKey key, void *data, va_list ap); + +// ギルドデータの文字列への変換 +int inter_guild_tostr(char *str, struct guild *g) { + int i, c, len; + + // 基本データ + len = sprintf(str, "%d\t%s\t%s\t%d,%d,%u,%d,%d\t%s#\t%s#\t", + g->guild_id, g->name, g->master, + g->guild_lv, g->max_member, g->exp, g->skill_point, g->castle_id, + g->mes1, g->mes2); + // メンバー + for(i = 0; i < g->max_member; i++) { + struct guild_member *m = &g->member[i]; + len += sprintf(str + len, "%d,%d,%d,%d,%d,%d,%d,%u,%d,%d\t%s\t", + m->account_id, m->char_id, + m->hair, m->hair_color, m->gender, + m->class_, m->lv, m->exp, m->exp_payper, m->position, + ((m->account_id > 0) ? m->name : "-")); + } + // 役職 + for(i = 0; i < MAX_GUILDPOSITION; i++) { + struct guild_position *p = &g->position[i]; + len += sprintf(str + len, "%d,%d\t%s#\t", p->mode, p->exp_mode, p->name); + } + // エンブレム + len += sprintf(str + len, "%d,%d,", g->emblem_len, g->emblem_id); + for(i = 0; i < g->emblem_len; i++) { + len += sprintf(str + len, "%02x", (unsigned char)(g->emblem_data[i])); + } + len += sprintf(str + len, "$\t"); + // 同盟リスト + c = 0; + for(i = 0; i < MAX_GUILDALLIANCE; i++) + if (g->alliance[i].guild_id > 0) + c++; + len += sprintf(str + len, "%d\t", c); + for(i = 0; i < MAX_GUILDALLIANCE; i++) { + struct guild_alliance *a = &g->alliance[i]; + if (a->guild_id > 0) + len += sprintf(str + len, "%d,%d\t%s\t", a->guild_id, a->opposition, a->name); + } + // 追放リスト + c = 0; + for(i = 0; i < MAX_GUILDEXPULSION; i++) + if (g->expulsion[i].account_id > 0) + c++; + len += sprintf(str + len, "%d\t", c); + for(i = 0; i < MAX_GUILDEXPULSION; i++) { + struct guild_expulsion *e = &g->expulsion[i]; + if (e->account_id > 0) + len += sprintf(str + len, "%d,%d,%d,%d\t%s\t%s\t%s#\t", + e->account_id, e->rsv1, e->rsv2, e->rsv3, + e->name, e->acc, e->mes ); + } + // ギルドスキル + for(i = 0; i < MAX_GUILDSKILL; i++) { + len += sprintf(str + len, "%d,%d ", g->skill[i].id, g->skill[i].lv); + } + len += sprintf(str + len, "\t"); + + return 0; +} +#endif //TXT_SQL_CONVERT +// ギルドデータの文字列からの変換 +int inter_guild_fromstr(char *str, struct guild *g) { + int i, j, c; + unsigned int exp; + int tmp_int[16]; + char tmp_str[4][256]; + char tmp_str2[4096]; + char *pstr; + + // 基本データ + memset(g, 0, sizeof(struct guild)); + if (sscanf(str, "%d\t%[^\t]\t%[^\t]\t%d,%d,%u,%d,%d\t%[^\t]\t%[^\t]\t", &tmp_int[0], + tmp_str[0], tmp_str[1], + &tmp_int[1], &tmp_int[2], &exp, &tmp_int[4], &tmp_int[5], + tmp_str[2], tmp_str[3]) < 8) + return 1; + + g->guild_id = tmp_int[0]; + g->guild_lv = tmp_int[1]; + g->max_member = tmp_int[2]; + g->exp = exp; + g->skill_point = tmp_int[4]; +#ifndef TXT_SQL_CONVERT + g->castle_id = tmp_int[5]; +#endif + memcpy(g->name, tmp_str[0], NAME_LENGTH-1); + memcpy(g->master, tmp_str[1], NAME_LENGTH-1); + memcpy(g->mes1, tmp_str[2], 60); + memcpy(g->mes2, tmp_str[3], 120); + g->mes1[strlen(g->mes1)-1] = 0; + g->mes2[strlen(g->mes2)-1] = 0; + + for(j = 0; j < 6 && str != NULL; j++) // 位置スキップ + str = strchr(str + 1, '\t'); +// printf("GuildBaseInfo OK\n"); + + // メンバー + for(i = 0; i < g->max_member; i++) { + struct guild_member *m = &g->member[i]; + if (sscanf(str + 1, "%d,%d,%d,%d,%d,%d,%d,%u,%d,%d\t%[^\t]\t", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], + &tmp_int[5], &tmp_int[6], &exp, &tmp_int[8], &tmp_int[9], + tmp_str[0]) < 11) + return 1; + m->account_id = tmp_int[0]; + m->char_id = tmp_int[1]; + m->hair = tmp_int[2]; + m->hair_color = tmp_int[3]; + m->gender = tmp_int[4]; + m->class_ = tmp_int[5]; + m->lv = tmp_int[6]; + m->exp = exp; + m->exp_payper = tmp_int[8]; + m->position = tmp_int[9]; + memcpy(m->name, tmp_str[0], NAME_LENGTH-1); + + for(j = 0; j < 2 && str != NULL; j++) // 位置スキップ + str = strchr(str + 1, '\t'); + } +// printf("GuildMemberInfo OK\n"); + // 役職 + i = 0; + while (sscanf(str+1, "%d,%d%n", &tmp_int[0], &tmp_int[1], &j) == 2 && str[1+j] == '\t') { + struct guild_position *p = &g->position[i]; + if (sscanf(str+1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str[0]) < 3) + return 1; + p->mode = tmp_int[0]; + p->exp_mode = tmp_int[1]; + tmp_str[0][strlen(tmp_str[0])-1] = 0; + memcpy(p->name, tmp_str[0], NAME_LENGTH-1); + + for(j = 0; j < 2 && str != NULL; j++) // 位置スキップ + str = strchr(str+1, '\t'); + i++; + } +// printf("GuildPositionInfo OK\n"); + // エンブレム + tmp_int[1] = 0; + if (sscanf(str + 1, "%d,%d,%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str2)< 3 && + sscanf(str + 1, "%d,%[^\t]\t", &tmp_int[0], tmp_str2) < 2) + return 1; + g->emblem_len = tmp_int[0]; + g->emblem_id = tmp_int[1]; + for(i = 0, pstr = tmp_str2; i < g->emblem_len; i++, pstr += 2) { + int c1 = pstr[0], c2 = pstr[1], x1 = 0, x2 = 0; + if (c1 >= '0' && c1 <= '9') x1 = c1 - '0'; + if (c1 >= 'a' && c1 <= 'f') x1 = c1 - 'a' + 10; + if (c1 >= 'A' && c1 <= 'F') x1 = c1 - 'A' + 10; + if (c2 >= '0' && c2 <= '9') x2 = c2 - '0'; + if (c2 >= 'a' && c2 <= 'f') x2 = c2 - 'a' + 10; + if (c2 >= 'A' && c2 <= 'F') x2 = c2 - 'A' + 10; + g->emblem_data[i] = (x1<<4) | x2; + } +// printf("GuildEmblemInfo OK\n"); + str=strchr(str + 1, '\t'); // 位置スキップ + + // 同盟リスト + if (sscanf(str + 1, "%d\t", &c) < 1) + return 1; + str = strchr(str + 1, '\t'); // 位置スキップ + for(i = 0; i < c; i++) { + struct guild_alliance *a = &g->alliance[i]; + if (sscanf(str + 1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str[0]) < 3) + return 1; + a->guild_id = tmp_int[0]; + a->opposition = tmp_int[1]; + memcpy(a->name, tmp_str[0], NAME_LENGTH-1); + + for(j = 0; j < 2 && str != NULL; j++) // 位置スキップ + str = strchr(str + 1, '\t'); + } +// printf("GuildAllianceInfo OK\n"); + // 追放リスト + if (sscanf(str+1, "%d\t", &c) < 1) + return 1; + str = strchr(str + 1, '\t'); // 位置スキップ + for(i = 0; i < c; i++) { + struct guild_expulsion *e = &g->expulsion[i]; + if (sscanf(str + 1, "%d,%d,%d,%d\t%[^\t]\t%[^\t]\t%[^\t]\t", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + tmp_str[0], tmp_str[1], tmp_str[2]) < 6) + return 1; + e->account_id = tmp_int[0]; + e->rsv1 = tmp_int[1]; + e->rsv2 = tmp_int[2]; + e->rsv3 = tmp_int[3]; + memcpy(e->name, tmp_str[0], NAME_LENGTH-1); + memcpy(e->acc, tmp_str[1], 24); + tmp_str[2][strlen(tmp_str[2])-1] = 0; + memcpy(e->mes, tmp_str[2], 40); + + for(j = 0; j < 4 && str != NULL; j++) // 位置スキップ + str = strchr(str + 1, '\t'); + } +// printf("GuildExplusionInfo OK\n"); + // ギルドスキル + for(i = 0; i < MAX_GUILDSKILL; i++) { + if (sscanf(str+1,"%d,%d ", &tmp_int[0], &tmp_int[1]) < 2) + break; + g->skill[i].id = tmp_int[0]; + g->skill[i].lv = tmp_int[1]; + str = strchr(str + 1, ' '); + } + str = strchr(str + 1, '\t'); +// printf("GuildSkillInfo OK\n"); + + return 0; +} +#ifndef TXT_SQL_CONVERT +// ギルド城データの文字列への変換 +int inter_guildcastle_tostr(char *str, struct guild_castle *gc) { + int len; + + len = sprintf(str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", // added Guardian HP [Valaris] + gc->castle_id, gc->guild_id, gc->economy, gc->defense, gc->triggerE, + gc->triggerD, gc->nextTime, gc->payTime, gc->createTime, gc->visibleC, + gc->guardian[0].visible, gc->guardian[1].visible, gc->guardian[2].visible, gc->guardian[3].visible, + gc->guardian[4].visible, gc->guardian[5].visible, gc->guardian[6].visible, gc->guardian[7].visible, + gc->guardian[0].hp, gc->guardian[1].hp, gc->guardian[2].hp, gc->guardian[3].hp, + gc->guardian[4].hp, gc->guardian[5].hp, gc->guardian[6].hp, gc->guardian[7].hp); + + return 0; +} +#endif ///TXT_SQL_CONVERT +// ギルド城データの文字列からの変換 +int inter_guildcastle_fromstr(char *str, struct guild_castle *gc) { + int tmp_int[26]; + + memset(gc, 0, sizeof(struct guild_castle)); + // new structure of guild castle + if (sscanf(str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], + &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], &tmp_int[24], &tmp_int[25]) == 26) { + gc->castle_id = tmp_int[0]; + gc->guild_id = tmp_int[1]; + gc->economy = tmp_int[2]; + gc->defense = tmp_int[3]; + gc->triggerE = tmp_int[4]; + gc->triggerD = tmp_int[5]; + gc->nextTime = tmp_int[6]; + gc->payTime = tmp_int[7]; + gc->createTime = tmp_int[8]; + gc->visibleC = tmp_int[9]; + gc->guardian[0].visible = tmp_int[10]; + gc->guardian[1].visible = tmp_int[11]; + gc->guardian[2].visible = tmp_int[12]; + gc->guardian[3].visible = tmp_int[13]; + gc->guardian[4].visible = tmp_int[14]; + gc->guardian[5].visible = tmp_int[15]; + gc->guardian[6].visible = tmp_int[16]; + gc->guardian[7].visible = tmp_int[17]; + gc->guardian[0].hp = tmp_int[18]; + gc->guardian[1].hp = tmp_int[19]; + gc->guardian[2].hp = tmp_int[20]; + gc->guardian[3].hp = tmp_int[21]; + gc->guardian[4].hp = tmp_int[22]; + gc->guardian[5].hp = tmp_int[23]; + gc->guardian[6].hp = tmp_int[24]; + gc->guardian[7].hp = tmp_int[25]; // end additions [Valaris] + // old structure of guild castle + } else if (sscanf(str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], + &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17]) == 18) { + int i; + + gc->castle_id = tmp_int[0]; + gc->guild_id = tmp_int[1]; + gc->economy = tmp_int[2]; + gc->defense = tmp_int[3]; + gc->triggerE = tmp_int[4]; + gc->triggerD = tmp_int[5]; + gc->nextTime = tmp_int[6]; + gc->payTime = tmp_int[7]; + gc->createTime = tmp_int[8]; + gc->visibleC = tmp_int[9]; + gc->guardian[0].visible = tmp_int[10]; + gc->guardian[1].visible = tmp_int[11]; + gc->guardian[2].visible = tmp_int[12]; + gc->guardian[3].visible = tmp_int[13]; + gc->guardian[4].visible = tmp_int[14]; + gc->guardian[5].visible = tmp_int[15]; + gc->guardian[6].visible = tmp_int[16]; + gc->guardian[7].visible = tmp_int[17]; + + for (i = 0; i < MAX_GUARDIANS; i++) + { + if (gc->guardian[i].visible) + gc->guardian[i].hp = 15000 + 2000 * gc->defense; + else + gc->guardian[i].hp = 0; + } + } else { + return 1; + } + + return 0; +} +#ifndef TXT_SQL_CONVERT +// ギルド関連データベース読み込み +int inter_guild_readdb(void) { + int i; + FILE *fp; + char line[1024]; + char path[1024]; + + sprintf(path, "%s%s", db_path, "/exp_guild.txt"); + fp = fopen(path, "r"); + if (fp == NULL) { + ShowError("can't read db/exp_guild.txt\n"); + return 1; + } + i = 0; + while(fgets(line, sizeof(line)-1, fp) && i < 100){ + if (line[0] == '/' && line[1] == '/') + continue; + guild_exp[i] = (unsigned int)atof(line); + i++; + } + fclose(fp); + + return 0; +} + +// ギルドデータの読み込み +int inter_guild_init() { + char line[16384]; + struct guild *g; + struct guild_castle *gc; + FILE *fp; + int i, j, c = 0; + + inter_guild_readdb(); + + guild_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + castle_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + if ((fp = fopen(guild_txt,"r")) == NULL) + return 1; + while(fgets(line, sizeof(line)-1, fp)) { + j = 0; + if (sscanf(line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 && guild_newid <= i) { + guild_newid = i; + continue; + } + + g = (struct guild *) aCalloc(sizeof(struct guild), 1); + if(g == NULL){ + ShowFatalError("int_guild: out of memory!\n"); + exit(0); + } +// memset(g, 0, sizeof(struct guild)); not needed... + if (inter_guild_fromstr(line, g) == 0 && g->guild_id > 0) { + if (g->guild_id >= guild_newid) + guild_newid = g->guild_id + 1; + idb_put(guild_db, g->guild_id, g); + guild_check_empty(g); + guild_calcinfo(g); + } else { + ShowError("int_guild: broken data [%s] line %d\n", guild_txt, c); + aFree(g); + } + c++; + } + fclose(fp); +// printf("int_guild: %s read done (%d guilds)\n", guild_txt, c); + + c = 0;//カウンタ初期化 + + if ((fp = fopen(castle_txt, "r")) == NULL) { + return 1; + } + + while(fgets(line, sizeof(line)-1, fp)) { + gc = (struct guild_castle *) aCalloc(sizeof(struct guild_castle), 1); + if(gc == NULL){ + ShowFatalError("int_guild: out of memory!\n"); + exit(0); + } +// memset(gc, 0, sizeof(struct guild_castle)); No need... + if (inter_guildcastle_fromstr(line, gc) == 0) { + idb_put(castle_db, gc->castle_id, gc); + } else { + ShowError("int_guild: broken data [%s] line %d\n", castle_txt, c); + aFree(gc); + } + c++; + } + + if (!c) { + ShowStatus(" %s - making Default Data...\n", castle_txt); + //デフォルトデータを作成 + for(i = 0; i < MAX_GUILDCASTLE; i++) { + gc = (struct guild_castle *) aCalloc(sizeof(struct guild_castle), 1); + if (gc == NULL) { + ShowFatalError("int_guild: out of memory!\n"); + exit(0); + } + gc->castle_id = i; + idb_put(castle_db, gc->castle_id, gc); + } + ShowStatus(" %s - making done\n",castle_txt); + return 0; + } + + fclose(fp); + + return 0; +} + +void inter_guild_final() { + castle_db->destroy(castle_db, NULL); + guild_db->destroy(guild_db, NULL); + return; +} + +struct guild *inter_guild_search(int guild_id) { + return idb_get(guild_db, guild_id); +} + +// ギルドデータのセーブ用 +int inter_guild_save_sub(DBKey key,void *data,va_list ap) { + char line[16384]; + FILE *fp; + + inter_guild_tostr(line,(struct guild *)data); + fp=va_arg(ap,FILE *); + fprintf(fp,"%s" RETCODE,line); + + return 0; +} + +// ギルド城データのセーブ用 +int inter_castle_save_sub(DBKey key, void *data, va_list ap) { + char line[16384]; + FILE *fp; + + inter_guildcastle_tostr(line, (struct guild_castle *)data); + fp = va_arg(ap, FILE *); + fprintf(fp, "%s" RETCODE, line); + + return 0; +} + +// ギルドデータのセーブ +int inter_guild_save() { + FILE *fp; + int lock; + + if ((fp = lock_fopen(guild_txt, &lock)) == NULL) { + ShowError("int_guild: cant write [%s] !!! data is lost !!!\n", guild_txt); + return 1; + } + guild_db->foreach(guild_db, inter_guild_save_sub, fp); +// fprintf(fp, "%d\t%%newid%%\n", guild_newid); + lock_fclose(fp, guild_txt, &lock); +// printf("int_guild: %s saved.\n", guild_txt); + + if ((fp = lock_fopen(castle_txt,&lock)) == NULL) { + ShowError("int_guild: cant write [%s] !!! data is lost !!!\n", castle_txt); + return 1; + } + castle_db->foreach(castle_db, inter_castle_save_sub, fp); + lock_fclose(fp, castle_txt, &lock); + + return 0; +} + +// ギルド名検索用 +int search_guildname_sub(DBKey key, void *data, va_list ap) { + struct guild *g = (struct guild *)data, **dst; + char *str; + + str = va_arg(ap, char *); + dst = va_arg(ap, struct guild **); + if (strcmpi(g->name, str) == 0) + *dst = g; + return 0; +} + +// ギルド名検索 +struct guild* search_guildname(char *str) { + struct guild *g = NULL; + guild_db->foreach(guild_db, search_guildname_sub, str, &g); + return g; +} + +// ギルドが空かどうかチェック +int guild_check_empty(struct guild *g) { + int i; + + for(i = 0; i < g->max_member; i++) { + if (g->member[i].account_id > 0) { + return 0; + } + } + // 誰もいないので解散 + guild_db->foreach(guild_db, guild_break_sub, g->guild_id); + inter_guild_storage_delete(g->guild_id); + mapif_guild_broken(g->guild_id, 0); + idb_remove(guild_db, g->guild_id); + return 1; +} + +// キャラの競合がないかチェック用 +int guild_check_conflict_sub(DBKey key, void *data, va_list ap) { + struct guild *g = (struct guild *)data; + int guild_id, account_id, char_id, i; + + guild_id = va_arg(ap, int); + account_id = va_arg(ap, int); + char_id = va_arg(ap, int); + + if (g->guild_id == guild_id) // 本来の所属なので問題なし + return 0; + + for(i = 0; i < MAX_GUILD; i++) { + if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) { + // 別のギルドに偽の所属データがあるので脱退 + ShowWarning("int_guild: guild conflict! %d,%d %d!=%d\n", account_id, char_id, guild_id, g->guild_id); + mapif_parse_GuildLeave(-1, g->guild_id, account_id, char_id, 0, "**データ競合**"); + } + } + + return 0; +} +// キャラの競合がないかチェック +int guild_check_conflict(int guild_id, int account_id, int char_id) { + guild_db->foreach(guild_db, guild_check_conflict_sub, guild_id, account_id, char_id); + + return 0; +} + +unsigned int guild_nextexp (int level) +{ + if (level == 0) + return 1; + if (level > 0 && level < 100) + return guild_exp[level-1]; + + return 0; +} + +// ギルドスキルがあるか確認 +int guild_checkskill(struct guild *g, int id) { + int idx = id - GD_SKILLBASE; + + + if(idx < 0 || idx >= MAX_GUILDSKILL) + + return 0; + + return g->skill[idx].lv; +} + +// ギルドの情報の再計算 +int guild_calcinfo(struct guild *g) { + int i, c; + unsigned int nextexp; + struct guild before = *g; + + // スキルIDの設定 + for(i = 0; i < MAX_GUILDSKILL; i++) + g->skill[i].id=i+GD_SKILLBASE; + + // ギルドレベル + if (g->guild_lv <= 0) + g->guild_lv = 1; + nextexp = guild_nextexp(g->guild_lv); + if (nextexp > 0) { + while(g->exp >= nextexp && nextexp > 0) { //fixed guild exp overflow [Kevin] + g->exp -= nextexp; + g->guild_lv++; + g->skill_point++; + nextexp = guild_nextexp(g->guild_lv); + } + } + + // ギルドの次の経験値 + g->next_exp = guild_nextexp(g->guild_lv); + + // メンバ上限(ギルド拡張適用) + g->max_member = 16 + guild_checkskill(g, GD_EXTENSION) * 6; //Guild Extention skill - currently adds 6 to max per skill lv. + if(g->max_member > MAX_GUILD) + { + ShowError("Guild %d:%s has capacity for too many guild members (%d), max supported is %d\n", g->guild_id, g->name, g->max_member, MAX_GUILD); + g->max_member = MAX_GUILD; + } + + // 平均レベルとオンライン人数 + g->average_lv = 0; + g->connect_member = 0; + c = 0; + for(i = 0; i < g->max_member; i++) { + if (g->member[i].account_id > 0) { + g->average_lv += g->member[i].lv; + c++; + if (g->member[i].online > 0) + g->connect_member++; + } + } + if(c) g->average_lv /= c; + + // 全データを送る必要がありそう + if (g->max_member != before.max_member || + g->guild_lv != before.guild_lv || + g->skill_point != before.skill_point) { + mapif_guild_info(-1, g); + return 1; + } + + return 0; +} + +//------------------------------------------------------------------- +// map serverへの通信 + +// ギルド作成可否 +int mapif_guild_created(int fd, int account_id, struct guild *g) { + WFIFOHEAD(fd, 10); + WFIFOW(fd,0) = 0x3830; + WFIFOL(fd,2) = account_id; + if (g != NULL) { + WFIFOL(fd,6) = g->guild_id; + ShowInfo("Created Guild (%d %s)\n", g->guild_id, g->name); + }else{ + WFIFOL(fd,6) = 0; + } + WFIFOSET(fd,10); + return 0; +} + +// ギルド情報見つからず +int mapif_guild_noinfo(int fd, int guild_id) { + WFIFOHEAD(fd, 8); + WFIFOW(fd,0) = 0x3831; + WFIFOW(fd,2) = 8; + WFIFOL(fd,4) = guild_id; + WFIFOSET(fd,8); + ShowNotice("int_guild: info not found %d\n", guild_id); + + return 0; +} + +// ギルド情報まとめ送り +int mapif_guild_info(int fd, struct guild *g) { + unsigned char buf[8+sizeof(struct guild)]; + + WBUFW(buf,0) = 0x3831; + memcpy(buf + 4, g, sizeof(struct guild)); + WBUFW(buf,2) = 4 + sizeof(struct guild); +// printf("int_guild: sizeof(guild)=%d\n", sizeof(struct guild)); + if (fd < 0) + mapif_sendall(buf, WBUFW(buf,2)); + else + mapif_send(fd, buf, WBUFW(buf,2)); +// printf("int_guild: info %d %s\n", p->guild_id, p->name); + + return 0; +} + +// メンバ追加可否 +int mapif_guild_memberadded(int fd, int guild_id, int account_id, int char_id, int flag) { + WFIFOHEAD(fd, 15); + WFIFOW(fd,0) = 0x3832; + WFIFOL(fd,2) = guild_id; + WFIFOL(fd,6) = account_id; + WFIFOL(fd,10) = char_id; + WFIFOB(fd,14) = flag; + WFIFOSET(fd, 15); + + return 0; +} + +// 脱退/追放通知 +int mapif_guild_leaved(int guild_id, int account_id, int char_id, int flag, const char *name, const char *mes) { + unsigned char buf[79]; + + WBUFW(buf, 0) = 0x3834; + WBUFL(buf, 2) = guild_id; + WBUFL(buf, 6) = account_id; + WBUFL(buf,10) = char_id; + WBUFB(buf,14) = flag; + memcpy(WBUFP(buf,15), mes, 40); + memcpy(WBUFP(buf,55), name, NAME_LENGTH); + mapif_sendall(buf, 55+NAME_LENGTH); +// mapif_sendall(buf, 79); + ShowInfo("Character left guild (Guild %d, %d - %s: %s)\n", guild_id, account_id, name, mes); + + return 0; +} + +// オンライン状態とLv更新通知 +int mapif_guild_memberinfoshort(struct guild *g, int idx) { + unsigned char buf[19]; + + WBUFW(buf, 0) = 0x3835; + WBUFL(buf, 2) = g->guild_id; + WBUFL(buf, 6) = g->member[idx].account_id; + WBUFL(buf,10) = g->member[idx].char_id; + WBUFB(buf,14) = (unsigned char)g->member[idx].online; + WBUFW(buf,15) = g->member[idx].lv; + WBUFW(buf,17) = g->member[idx].class_; + mapif_sendall(buf, 19); + return 0; +} + +// 解散通知 +int mapif_guild_broken(int guild_id, int flag) { + unsigned char buf[7]; + + WBUFW(buf,0) = 0x3836; + WBUFL(buf,2) = guild_id; + WBUFB(buf,6) = flag; + mapif_sendall(buf, 7); + ShowInfo("Guild Break (%d)\n", guild_id); + + return 0; +} + +// ギルド内発言 +int mapif_guild_message(int guild_id, int account_id, char *mes, int len, int sfd) { + unsigned char buf[2048]; + + WBUFW(buf,0) = 0x3837; + WBUFW(buf,2) = len + 12; + WBUFL(buf,4) = guild_id; + WBUFL(buf,8) = account_id; + memcpy(WBUFP(buf,12), mes, len); + mapif_sendallwos(sfd, buf, len + 12); + + return 0; +} + +// ギルド基本情報変更通知 +int mapif_guild_basicinfochanged(int guild_id, int type, const void *data, int len) { + unsigned char buf[2048]; + + WBUFW(buf,0) = 0x3839; + WBUFW(buf,2) = len+10; + WBUFL(buf,4) = guild_id; + WBUFW(buf,8) = type; + memcpy(WBUFP(buf,10),data,len); + mapif_sendall(buf,len+10); + return 0; +} + +// ギルドメンバ情報変更通知 +int mapif_guild_memberinfochanged(int guild_id, int account_id, int char_id, int type, const void *data, int len) { + unsigned char buf[4096]; + + WBUFW(buf, 0) = 0x383a; + WBUFW(buf, 2) = len + 18; + WBUFL(buf, 4) = guild_id; + WBUFL(buf, 8) = account_id; + WBUFL(buf,12) = char_id; + WBUFW(buf,16) = type; + memcpy(WBUFP(buf,18), data, len); + mapif_sendall(buf,len+18); + + return 0; +} + +// ギルドスキルアップ通知 +int mapif_guild_skillupack(int guild_id, int skill_num, int account_id) { + unsigned char buf[14]; + + WBUFW(buf, 0) = 0x383c; + WBUFL(buf, 2) = guild_id; + WBUFL(buf, 6) = skill_num; + WBUFL(buf,10) = account_id; + mapif_sendall(buf, 14); + + return 0; +} + +// ギルド同盟/敵対通知 +int mapif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag, const char *name1, const char *name2) { + unsigned char buf[67]; + + WBUFW(buf, 0) = 0x383d; + WBUFL(buf, 2) = guild_id1; + WBUFL(buf, 6) = guild_id2; + WBUFL(buf,10) = account_id1; + WBUFL(buf,14) = account_id2; + WBUFB(buf,18) = flag; + memcpy(WBUFP(buf,19), name1, NAME_LENGTH); + memcpy(WBUFP(buf,19+NAME_LENGTH), name2, NAME_LENGTH); + mapif_sendall(buf,19+2*NAME_LENGTH); +/* + memcpy(WBUFP(buf,43), name2, NAME_LENGTH); + mapif_sendall(buf, 67); +*/ + return 0; +} + +// ギルド役職変更通知 +int mapif_guild_position(struct guild *g, int idx) { + unsigned char buf[2048]; + + WBUFW(buf,0) = 0x383b; + WBUFW(buf,2) = sizeof(struct guild_position) + 12; + WBUFL(buf,4) = g->guild_id; + WBUFL(buf,8) = idx; + memcpy(WBUFP(buf,12), &g->position[idx], sizeof(struct guild_position)); + mapif_sendall(buf, WBUFW(buf,2)); + + return 0; +} + +// ギルド告知変更通知 +int mapif_guild_notice(struct guild *g) { + unsigned char buf[186]; + + WBUFW(buf,0) = 0x383e; + WBUFL(buf,2) = g->guild_id; + memcpy(WBUFP(buf,6), g->mes1, 60); + memcpy(WBUFP(buf,66), g->mes2, 120); + mapif_sendall(buf, 186); + + return 0; +} + +// ギルドエンブレム変更通知 +int mapif_guild_emblem(struct guild *g) { + unsigned char buf[2048]; + + WBUFW(buf,0) = 0x383f; + WBUFW(buf,2) = g->emblem_len + 12; + WBUFL(buf,4) = g->guild_id; + WBUFL(buf,8) = g->emblem_id; + memcpy(WBUFP(buf,12), g->emblem_data, g->emblem_len); + mapif_sendall(buf, WBUFW(buf,2)); + + return 0; +} + +int mapif_guild_master_changed(struct guild *g, int position) +{ + unsigned char buf[12]; + WBUFW(buf,0)=0x3843; + WBUFL(buf,2)=g->guild_id; + WBUFL(buf,6)=position; + mapif_sendall(buf,10); + return 0; +} + +int mapif_guild_castle_dataload(int castle_id, int index, int value) { + unsigned char buf[9]; + + WBUFW(buf,0) = 0x3840; + WBUFW(buf,2) = castle_id; + WBUFB(buf,4) = index; + WBUFL(buf,5) = value; + mapif_sendall(buf,9); + + return 0; +} + +int mapif_guild_castle_datasave(int castle_id, int index, int value) { + unsigned char buf[9]; + + WBUFW(buf,0) = 0x3841; + WBUFW(buf,2) = castle_id; + WBUFB(buf,4) = index; + WBUFL(buf,5) = value; + mapif_sendall(buf,9); + + return 0; +} + +int mapif_guild_castle_alldataload_sub(DBKey key, void *data, va_list ap) { + int fd = va_arg(ap, int); + int *p = va_arg(ap, int*); + + WFIFOHEAD(fd, sizeof(struct guild_castle)); + memcpy(WFIFOP(fd,*p), (struct guild_castle*)data, sizeof(struct guild_castle)); + (*p) += sizeof(struct guild_castle); + + return 0; +} + +int mapif_guild_castle_alldataload(int fd) { + int len = 4; + + WFIFOHEAD(fd, 0); + WFIFOW(fd,0) = 0x3842; + castle_db->foreach(castle_db, mapif_guild_castle_alldataload_sub, fd, &len); + WFIFOW(fd,2) = len; + WFIFOSET(fd, len); + + return 0; +} + +//------------------------------------------------------------------- +// map serverからの通信 + +// ギルド作成要求 +int mapif_parse_CreateGuild(int fd, int account_id, char *name, struct guild_member *master) { + struct guild *g; + int i; + + for(i = 0; i < NAME_LENGTH && name[i]; i++) { + if (!(name[i] & 0xe0) || name[i] == 0x7f) { + ShowInfo("Create Guild: illegal guild name [%s]\n", name); + mapif_guild_created(fd, account_id, NULL); + return 0; + } + } + + if ((g = search_guildname(name)) != NULL) { + ShowInfo("Create Guild: same name guild exists [%s]\n", name); + mapif_guild_created(fd, account_id, NULL); + return 0; + } + + // Check Authorised letters/symbols in the name of the character + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) == NULL) { + mapif_guild_created(fd,account_id,NULL); + return 0; + } + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) != NULL) { + mapif_guild_created(fd,account_id,NULL); + return 0; + } + } + + g = (struct guild *) aCalloc(sizeof(struct guild), 1); + if (g == NULL) { + ShowFatalError("int_guild: CreateGuild: out of memory !\n"); + mapif_guild_created(fd, account_id, NULL); + exit(0); + } +// memset(g, 0, sizeof(struct guild)); Meh... + g->guild_id = guild_newid++; + memcpy(g->name, name, NAME_LENGTH-1); + memcpy(g->master, master->name, NAME_LENGTH-1); + memcpy(&g->member[0], master, sizeof(struct guild_member)); + + g->position[0].mode = 0x11; + strcpy(g->position[ 0].name, "GuildMaster"); + strcpy(g->position[MAX_GUILDPOSITION-1].name, "Newbie"); + for(i = 1; i < MAX_GUILDPOSITION-1; i++) + sprintf(g->position[i].name, "Position %d", i + 1); + + // ここでギルド情報計算が必要と思われる + g->max_member = 16; + g->average_lv = master->lv; + for(i = 0; i < MAX_GUILDSKILL; i++) + g->skill[i].id=i + GD_SKILLBASE; + + idb_put(guild_db, g->guild_id, g); + + mapif_guild_created(fd, account_id, g); + mapif_guild_info(fd, g); + + if(log_inter) + inter_log("guild %s (id=%d) created by master %s (id=%d)" RETCODE, + name, g->guild_id, master->name, master->account_id); + + return 0; +} + +// ギルド情報要求 +int mapif_parse_GuildInfo(int fd, int guild_id) { + struct guild *g; + + g = idb_get(guild_db, guild_id); + if (g != NULL){ + guild_calcinfo(g); + mapif_guild_info(fd, g); + } else + mapif_guild_noinfo(fd, guild_id); + + return 0; +} + +// ギルドメンバ追加要求 +int mapif_parse_GuildAddMember(int fd, int guild_id, struct guild_member *m) { + struct guild *g; + int i; + + g = idb_get(guild_db, guild_id); + if (g == NULL) { + mapif_guild_memberadded(fd, guild_id, m->account_id, m->char_id, 1); + return 0; + } + + for(i = 0; i < g->max_member; i++) { + if (g->member[i].account_id == 0) { + memcpy(&g->member[i], m, sizeof(struct guild_member)); + mapif_guild_memberadded(fd, guild_id, m->account_id, m->char_id, 0); + guild_calcinfo(g); + mapif_guild_info(-1, g); + + return 0; + } + } + mapif_guild_memberadded(fd, guild_id, m->account_id, m->char_id, 1); + + return 0; +} + +// ギルド脱退/追放要求 +int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char_id, int flag, const char *mes) { + struct guild *g = NULL; + int i, j; + + g = idb_get(guild_db, guild_id); + if (g != NULL) { + for(i = 0; i < MAX_GUILD; i++) { + if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) { +// printf("%d %d\n", i, (int)(&g->member[i])); +// printf("%d %s\n", i, g->member[i].name); + + if (flag) { // 追放の場合追放リストに入れる + for(j = 0; j < MAX_GUILDEXPULSION; j++) { + if (g->expulsion[j].account_id == 0) + break; + } + if (j == MAX_GUILDEXPULSION) { // 一杯なので古いのを消す + for(j = 0; j < MAX_GUILDEXPULSION - 1; j++) + g->expulsion[j] = g->expulsion[j+1]; + j = MAX_GUILDEXPULSION - 1; + } + g->expulsion[j].account_id = account_id; + memcpy(g->expulsion[j].acc, "dummy", NAME_LENGTH-1); + memcpy(g->expulsion[j].name, g->member[i].name, NAME_LENGTH-1); + memcpy(g->expulsion[j].mes, mes, 40); + } + + mapif_guild_leaved(guild_id, account_id, char_id, flag, g->member[i].name, mes); +// printf("%d %d\n", i, (int)(&g->member[i])); +// printf("%d %s\n", i, (&g->member[i])->name); + memset(&g->member[i], 0, sizeof(struct guild_member)); + + if (guild_check_empty(g) == 0) + mapif_guild_info(-1,g);// まだ人がいるのでデータ送信 + + return 0; + } + } + } + return 0; +} + +// オンライン/Lv更新 +int mapif_parse_GuildChangeMemberInfoShort(int fd, int guild_id, int account_id, int char_id, int online, int lv, int class_) { + struct guild *g; + int i, alv, c; + + g = idb_get(guild_db, guild_id); + if (g == NULL) + return 0; + + g->connect_member = 0; + + alv = 0; + c = 0; + for(i = 0; i < MAX_GUILD; i++) { + if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) { + g->member[i].online = online; + g->member[i].lv = lv; + g->member[i].class_ = class_; + mapif_guild_memberinfoshort(g, i); + } + if (g->member[i].account_id > 0) { + alv += g->member[i].lv; + c++; + } + if (g->member[i].online) + g->connect_member++; + } + + if (c) + // 平均レベル + g->average_lv = alv / c; + + return 0; +} + +// ギルド解散処理用(同盟/敵対を解除) +int guild_break_sub(DBKey key, void *data, va_list ap) { + struct guild *g = (struct guild *)data; + int guild_id = va_arg(ap, int); + int i; + + for(i = 0; i < MAX_GUILDALLIANCE; i++) { + if (g->alliance[i].guild_id == guild_id) + g->alliance[i].guild_id = 0; + } + return 0; +} + +// ギルド解散要求 +int mapif_parse_BreakGuild(int fd, int guild_id) { + struct guild *g; + + g = idb_get(guild_db, guild_id); + if(g == NULL) + return 0; + + guild_db->foreach(guild_db, guild_break_sub, guild_id); + inter_guild_storage_delete(guild_id); + mapif_guild_broken(guild_id, 0); + + if(log_inter) + inter_log("guild %s (id=%d) broken" RETCODE, g->name, guild_id); + + idb_remove(guild_db, guild_id); + return 0; +} + +// ギルドメッセージ送信 +int mapif_parse_GuildMessage(int fd, int guild_id, int account_id, char *mes, int len) { + return mapif_guild_message(guild_id, account_id, mes, len, fd); +} + +// ギルド基本データ変更要求 +int mapif_parse_GuildBasicInfoChange(int fd, int guild_id, int type, const char *data, int len) { + struct guild *g; + short dw = *((short *)data); + + g = idb_get(guild_db, guild_id); + if (g == NULL) + return 0; + + switch(type) { + case GBI_GUILDLV: + if (dw > 0 && g->guild_lv + dw <= 50) { + g->guild_lv+=dw; + g->skill_point+=dw; + } else if (dw < 0 && g->guild_lv + dw >= 1) + g->guild_lv += dw; + mapif_guild_info(-1, g); + return 0; + default: + ShowError("int_guild: GuildBasicInfoChange: Unknown type %d\n", type); + break; + } + mapif_guild_basicinfochanged(guild_id, type, data, len); + + return 0; +} + +// ギルドメンバデータ変更要求 +int mapif_parse_GuildMemberInfoChange(int fd, int guild_id, int account_id, int char_id, int type, const char *data, int len) { + int i; + struct guild *g; + + g = idb_get(guild_db, guild_id); + if(g == NULL) + return 0; + + for(i = 0; i < g->max_member; i++) + if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) + break; + if (i == g->max_member) { + ShowWarning("int_guild: GuildMemberChange: Not found %d,%d in %d[%s]\n", account_id, char_id, guild_id, g->name); + return 0; + } + switch(type) { + case GMI_POSITION: // 役職 + g->member[i].position = *((int *)data); + break; + case GMI_EXP: // EXP + { + unsigned int exp, old_exp=g->member[i].exp; + g->member[i].exp=*((unsigned int *)data); + if (g->member[i].exp > old_exp) + { + exp = g->member[i].exp - old_exp; + if (guild_exp_rate != 100) + exp = exp*guild_exp_rate/100; + if (exp > UINT_MAX - g->exp) + g->exp = UINT_MAX; + else + g->exp+=exp; + guild_calcinfo(g); + mapif_guild_basicinfochanged(guild_id,GBI_EXP,&g->exp,4); + } + break; + } + case GMI_HAIR: + { + g->member[i].hair=*((int *)data); + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + break; + } + case GMI_HAIR_COLOR: + { + g->member[i].hair_color=*((int *)data); + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + break; + } + case GMI_GENDER: + { + g->member[i].gender=*((int *)data); + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + break; + } + case GMI_CLASS: + { + g->member[i].class_=*((int *)data); + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + break; + } + case GMI_LEVEL: + { + g->member[i].lv=*((int *)data); + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + break; + } + + default: + ShowError("int_guild: GuildMemberInfoChange: Unknown type %d\n", type); + break; + } + mapif_guild_memberinfochanged(guild_id, account_id, char_id, type, data, len); + + return 0; +} + +int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender) +{ + return mapif_parse_GuildMemberInfoChange(0, guild_id, account_id, char_id, GMI_GENDER, (const char*)&gender, sizeof(gender)); +} + +// ギルド役職名変更要求 +int mapif_parse_GuildPosition(int fd, int guild_id, int idx, struct guild_position *p) { + struct guild *g = idb_get(guild_db, guild_id); + + if (g == NULL || idx < 0 || idx >= MAX_GUILDPOSITION) { + return 0; + } + memcpy(&g->position[idx], p, sizeof(struct guild_position)); + mapif_guild_position(g, idx); + ShowInfo("int_guild: position [%d] changed\n", idx); + + return 0; +} + +// ギルドスキルアップ要求 +int mapif_parse_GuildSkillUp(int fd, int guild_id, int skill_num, int account_id) { + struct guild *g = idb_get(guild_db, guild_id); + int idx = skill_num - GD_SKILLBASE; + + if (g == NULL || idx < 0 || idx >= MAX_GUILDSKILL) + return 0; + + if (g->skill_point > 0 && g->skill[idx].id > 0 && g->skill[idx].lv < 10) { + g->skill[idx].lv++; + g->skill_point--; + if (guild_calcinfo(g) == 0) + mapif_guild_info(-1, g); + mapif_guild_skillupack(guild_id, skill_num, account_id); + } + + return 0; +} + +//Manual deletion of an alliance when partnering guild does not exists. [Skotlex] +static int mapif_parse_GuildDeleteAlliance(struct guild *g, int guild_id, int account_id1, int account_id2, int flag) +{ + int i; + char name[NAME_LENGTH]; + for(i=0;ialliance[i].guild_id == guild_id) + { + strcpy(name, g->alliance[i].name); + g->alliance[i].guild_id=0; + break; + } + if (i == MAX_GUILDALLIANCE) + return -1; + + mapif_guild_alliance(g->guild_id,guild_id,account_id1,account_id2,flag,g->name,name); + return 0; +} +// ギルド同盟要求 +int mapif_parse_GuildAlliance(int fd, int guild_id1, int guild_id2, int account_id1, int account_id2, int flag) { + struct guild *g[2]; + int j, i; + + g[0] = idb_get(guild_db, guild_id1); + g[1] = idb_get(guild_db, guild_id2); + + if(g[0] && g[1]==NULL && (flag&0x8)) //Requested to remove an alliance with a not found guild. + return mapif_parse_GuildDeleteAlliance(g[0], guild_id2, + account_id1, account_id2, flag); //Try to do a manual removal of said guild. + + if (g[0] == NULL || g[1] == NULL) + return 0; + + if (!(flag & 0x8)) { + for(i = 0; i < 2 - (flag & 1); i++) { + for(j = 0; j < MAX_GUILDALLIANCE; j++) + if (g[i]->alliance[j].guild_id == 0) { + g[i]->alliance[j].guild_id = g[1-i]->guild_id; + memcpy(g[i]->alliance[j].name, g[1-i]->name, NAME_LENGTH-1); + g[i]->alliance[j].opposition = flag & 1; + break; + } + } + } else { // 関係解消 + for(i = 0; i < 2 - (flag & 1); i++) { + for(j = 0; j < MAX_GUILDALLIANCE; j++) + if (g[i]->alliance[j].guild_id == g[1-i]->guild_id && g[i]->alliance[j].opposition == (flag & 1)) { + g[i]->alliance[j].guild_id = 0; + break; + } + } + } + mapif_guild_alliance(guild_id1, guild_id2, account_id1, account_id2, flag, g[0]->name, g[1]->name); + + return 0; +} + +// ギルド告知変更要求 +int mapif_parse_GuildNotice(int fd, int guild_id, const char *mes1, const char *mes2) { + struct guild *g; + + g = idb_get(guild_db, guild_id); + if (g == NULL) + return 0; + memcpy(g->mes1, mes1, 60); + memcpy(g->mes2, mes2, 120); + + return mapif_guild_notice(g); +} + +// ギルドエンブレム変更要求 +int mapif_parse_GuildEmblem(int fd, int len, int guild_id, int dummy, const char *data) { + struct guild *g; + + g = idb_get(guild_db, guild_id); + if (g == NULL) + return 0; + memcpy(g->emblem_data, data, len); + g->emblem_len = len; + g->emblem_id++; + + return mapif_guild_emblem(g); +} + +int mapif_parse_GuildCastleDataLoad(int fd, int castle_id, int index) { + struct guild_castle *gc = idb_get(castle_db, castle_id); + + if (gc == NULL) { + return mapif_guild_castle_dataload(castle_id, 0, 0); + } + switch(index) { + case 1: return mapif_guild_castle_dataload(gc->castle_id, index, gc->guild_id); + case 2: return mapif_guild_castle_dataload(gc->castle_id, index, gc->economy); + case 3: return mapif_guild_castle_dataload(gc->castle_id, index, gc->defense); + case 4: return mapif_guild_castle_dataload(gc->castle_id, index, gc->triggerE); + case 5: return mapif_guild_castle_dataload(gc->castle_id, index, gc->triggerD); + case 6: return mapif_guild_castle_dataload(gc->castle_id, index, gc->nextTime); + case 7: return mapif_guild_castle_dataload(gc->castle_id, index, gc->payTime); + case 8: return mapif_guild_castle_dataload(gc->castle_id, index, gc->createTime); + case 9: return mapif_guild_castle_dataload(gc->castle_id, index, gc->visibleC); + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + return mapif_guild_castle_dataload(gc->castle_id, index, gc->guardian[index-10].visible); + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + return mapif_guild_castle_dataload(gc->castle_id, index, gc->guardian[index-18].hp); // end additions [Valaris] + + default: + ShowError("mapif_parse_GuildCastleDataLoad ERROR!! (Not found index=%d)\n", index); + return 0; + } + + return 0; +} + +int mapif_parse_GuildCastleDataSave(int fd, int castle_id, int index, int value) { + struct guild_castle *gc= idb_get(castle_db, castle_id); + + if (gc == NULL) { + return mapif_guild_castle_datasave(castle_id, index, value); + } + switch(index) { + case 1: + if (gc->guild_id != value) { + int gid = (value) ? value : gc->guild_id; + struct guild *g = idb_get(guild_db, gid); + if(log_inter) + inter_log("guild %s (id=%d) %s castle id=%d" RETCODE, + (g) ? g->name : "??", gid, (value) ? "occupy" : "abandon", castle_id); + } + gc->guild_id = value; + if(gc->guild_id == 0) { + //Delete guardians. + memset(&gc->guardian, 0, sizeof(gc->guardian)); + } + break; + case 2: gc->economy = value; break; + case 3: gc->defense = value; break; + case 4: gc->triggerE = value; break; + case 5: gc->triggerD = value; break; + case 6: gc->nextTime = value; break; + case 7: gc->payTime = value; break; + case 8: gc->createTime = value; break; + case 9: gc->visibleC = value; break; + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + gc->guardian[index-10].visible = value; break; + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + gc->guardian[index-18].hp = value; break; // end additions [Valaris] + default: + ShowError("mapif_parse_GuildCastleDataSave ERROR!! (Not found index=%d)\n", index); + return 0; + } + + return mapif_guild_castle_datasave(gc->castle_id, index, value); +} + +// ギルドチェック要求 +int mapif_parse_GuildCheck(int fd, int guild_id, int account_id, int char_id) { + return guild_check_conflict(guild_id, account_id, char_id); +} + +int mapif_parse_GuildMasterChange(int fd, int guild_id, const char* name, int len) +{ + struct guild *g = idb_get(guild_db, guild_id); + struct guild_member gm; + int pos; + + if(g==NULL || g->guild_id<=0 || len > NAME_LENGTH) + return 0; + + for (pos = 0; pos < g->max_member && strncmp(g->member[pos].name, name, len); pos++); + + if (pos == g->max_member) + return 0; //Character not found?? + + memcpy(&gm, &g->member[pos], sizeof (struct guild_member)); + memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member)); + memcpy(&g->member[0], &gm, sizeof(struct guild_member)); + + g->member[pos].position = g->member[0].position; + g->member[0].position = 0; //Position 0: guild Master. + strncpy(g->master, name, len); + if (len < NAME_LENGTH) + g->master[len] = '\0'; + + ShowInfo("int_guild: Guildmaster Changed to %s (Guild %d - %s)\n",name, guild_id, g->name); + return mapif_guild_master_changed(g, pos); +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_guild_parse_frommap(int fd) { + RFIFOHEAD(fd); + switch(RFIFOW(fd,0)) { + case 0x3030: mapif_parse_CreateGuild(fd, RFIFOL(fd,4), (char*)RFIFOP(fd,8), (struct guild_member *)RFIFOP(fd,32)); break; + case 0x3031: mapif_parse_GuildInfo(fd, RFIFOL(fd,2)); break; + case 0x3032: mapif_parse_GuildAddMember(fd, RFIFOL(fd,4), (struct guild_member *)RFIFOP(fd,8)); break; + case 0x3033: mapif_parse_GuildMasterChange(fd,RFIFOL(fd,4),(const char*)RFIFOP(fd,8),RFIFOW(fd,2)-8); break; + case 0x3034: mapif_parse_GuildLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOB(fd,14), (const char*)RFIFOP(fd,15)); break; + case 0x3035: mapif_parse_GuildChangeMemberInfoShort(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOB(fd,14), RFIFOW(fd,15), RFIFOW(fd,17)); break; + case 0x3036: mapif_parse_BreakGuild(fd, RFIFOL(fd,2)); break; + case 0x3037: mapif_parse_GuildMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), (char*)RFIFOP(fd,12), RFIFOW(fd,2)-12); break; + case 0x3038: mapif_parse_GuildMasterChange(fd,RFIFOL(fd,4),(const char*)RFIFOP(fd,8),RFIFOW(fd,2)-8); break; + case 0x3039: mapif_parse_GuildBasicInfoChange(fd, RFIFOL(fd,4), RFIFOW(fd,8), (const char*)RFIFOP(fd,10), RFIFOW(fd,2)-10); break; + case 0x303A: mapif_parse_GuildMemberInfoChange(fd, RFIFOL(fd,4), RFIFOL(fd,8), RFIFOL(fd,12), RFIFOW(fd,16), (const char*)RFIFOP(fd,18), RFIFOW(fd,2)-18); break; + case 0x303B: mapif_parse_GuildPosition(fd, RFIFOL(fd,4), RFIFOL(fd,8), (struct guild_position *)RFIFOP(fd,12)); break; + case 0x303C: mapif_parse_GuildSkillUp(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; + case 0x303D: mapif_parse_GuildAlliance(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), RFIFOB(fd,18)); break; + case 0x303E: mapif_parse_GuildNotice(fd, RFIFOL(fd,2), (const char*)RFIFOP(fd,6), (const char*)RFIFOP(fd,66)); break; + case 0x303F: mapif_parse_GuildEmblem(fd, RFIFOW(fd,2)-12, RFIFOL(fd,4), RFIFOL(fd,8), (const char*)RFIFOP(fd,12)); break; + case 0x3040: mapif_parse_GuildCastleDataLoad(fd, RFIFOW(fd,2), RFIFOB(fd,4)); break; + case 0x3041: mapif_parse_GuildCastleDataSave(fd, RFIFOW(fd,2), RFIFOB(fd,4), RFIFOL(fd,5)); break; + + default: + return 0; + } + + return 1; +} + +// マップサーバーの接続時処理 +int inter_guild_mapif_init(int fd) { + return mapif_guild_castle_alldataload(fd); +} + +// サーバーから脱退要求(キャラ削除用) +int inter_guild_leave(int guild_id, int account_id, int char_id) { + return mapif_parse_GuildLeave(-1, guild_id, account_id, char_id, 0, "** Character Deleted **"); +} +#endif //TXT_SQL_CONVERT diff --git a/src/char/int_guild.h b/src/char/int_guild.h index 2cd26a516..616384b94 100644 --- a/src/char/int_guild.h +++ b/src/char/int_guild.h @@ -1,22 +1,22 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INT_GUILD_H_ -#define _INT_GUILD_H_ - -int inter_guild_init(void); -void inter_guild_final(void); -int inter_guild_save(void); -int inter_guild_parse_frommap(int fd); -struct guild *inter_guild_search(int guild_id); -int inter_guild_mapif_init(int fd); -int inter_guild_leave(int guild_id,int account_id,int char_id); -int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender); - -extern char guild_txt[1024]; -extern char castle_txt[1024]; - -//For the TXT->SQL converter -int inter_guild_fromstr(char *str, struct guild *g); -int inter_guildcastle_fromstr(char *str, struct guild_castle *gc); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INT_GUILD_H_ +#define _INT_GUILD_H_ + +int inter_guild_init(void); +void inter_guild_final(void); +int inter_guild_save(void); +int inter_guild_parse_frommap(int fd); +struct guild *inter_guild_search(int guild_id); +int inter_guild_mapif_init(int fd); +int inter_guild_leave(int guild_id,int account_id,int char_id); +int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender); + +extern char guild_txt[1024]; +extern char castle_txt[1024]; + +//For the TXT->SQL converter +int inter_guild_fromstr(char *str, struct guild *g); +int inter_guildcastle_fromstr(char *str, struct guild_castle *gc); +#endif diff --git a/src/char/int_homun.c b/src/char/int_homun.c index 3de472d74..a07378c2a 100644 --- a/src/char/int_homun.c +++ b/src/char/int_homun.c @@ -1,371 +1,371 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include - -#include "../common/mmo.h" -#include "../common/socket.h" -#include "../common/db.h" -#include "../common/lock.h" -#include "../common/showmsg.h" -#include "char.h" -#include "inter.h" -#include "int_homun.h" - -char homun_txt[1024]="save/homun.txt"; - -static struct dbt *homun_db; -static int homun_newid = 100; - -int inter_homun_tostr(char *str,struct s_homunculus *p) -{ - int i; - - str+=sprintf(str,"%d,%d\t%s\t%d,%d,%d,%d,%d," - "%u,%d,%d,%d," - "%u,%d,%d," - "%d,%d,%d,%d,%d,%d\t", - p->hom_id, p->class_, p->name, - p->char_id, p->hp, p->max_hp, p->sp, p->max_sp, - p->intimacy, p->hunger, p->skillpts, p->level, - p->exp, p->rename_flag, p->vaporize, - p->str, p->agi, p->vit, p->int_, p->dex, p->luk); - - for (i = 0; i < MAX_HOMUNSKILL; i++) - { - if (p->hskill[i].id && !p->hskill[i].flag) - str+=sprintf(str,"%d,%d,", p->hskill[i].id, p->hskill[i].lv); - } - - return 0; -} - -int inter_homun_fromstr(char *str,struct s_homunculus *p) -{ - int i, next, len; - int tmp_int[25]; - unsigned int tmp_uint[5]; - char tmp_str[256]; - - memset(p,0,sizeof(struct s_homunculus)); - - i=sscanf(str,"%d,%d\t%127[^\t]\t%d,%d,%d,%d,%d," - "%u,%d,%d,%d," - "%u,%d,%d," - "%d,%d,%d,%d,%d,%d\t%n", - &tmp_int[0],&tmp_int[1],tmp_str, - &tmp_int[2],&tmp_int[3],&tmp_int[4],&tmp_int[5],&tmp_int[6], - &tmp_uint[0],&tmp_int[7],&tmp_int[8],&tmp_int[9], - &tmp_uint[1],&tmp_int[10],&tmp_int[11], - &tmp_int[12],&tmp_int[13],&tmp_int[14],&tmp_int[15],&tmp_int[16],&tmp_int[17], - &next); - - if(i!=21) - return 1; - - p->hom_id = tmp_int[0]; - p->class_ = tmp_int[1]; - memcpy(p->name, tmp_str, NAME_LENGTH-1); - - p->char_id = tmp_int[2]; - p->hp = tmp_int[3]; - p->max_hp = tmp_int[4]; - p->sp = tmp_int[5]; - p->max_sp = tmp_int[6]; - - p->intimacy = tmp_uint[0]; - p->hunger = tmp_int[7]; - p->skillpts = tmp_int[8]; - p->level = tmp_int[9]; - - p->exp = tmp_uint[1]; - p->rename_flag = tmp_int[10]; - p->vaporize = tmp_int[11]; - - p->str = tmp_int[12]; - p->agi = tmp_int[13]; - p->vit = tmp_int[14]; - p->int_= tmp_int[15]; - p->dex = tmp_int[16]; - p->luk = tmp_int[17]; - - //Read skills. - while(str[next] && str[next] != '\n' && str[next] != '\r') { - if (sscanf(str+next, "%d,%d,%n", &tmp_int[0], &tmp_int[1], &len) != 2) - return 2; - - if (tmp_int[0] > HM_SKILLBASE && tmp_int[0] <= HM_SKILLBASE+MAX_HOMUNSKILL) - { - i = tmp_int[0] - HM_SKILLBASE -1; - p->hskill[i].id = tmp_int[0]; - p->hskill[i].lv = tmp_int[1]; - } else - ShowError("Read Homun: Unsupported Skill ID %d for homunculus (Homun ID=%d\n", tmp_int[0], p->hom_id); - next += len; - if (str[next] == ' ') - next++; - } - return 0; -} - -int inter_homun_init() -{ - char line[8192]; - struct s_homunculus *p; - FILE *fp; - int c=0; - - homun_db= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - if( (fp=fopen(homun_txt,"r"))==NULL ) - return 1; - while(fgets(line,sizeof(line),fp)){ - p = (struct s_homunculus*)aCalloc(sizeof(struct s_homunculus), 1); - if(p==NULL){ - ShowFatalError("int_homun: out of memory!\n"); - exit(0); - } - if(inter_homun_fromstr(line,p)==0 && p->hom_id>0){ - if( p->hom_id >= homun_newid) - homun_newid=p->hom_id+1; - idb_put(homun_db,p->hom_id,p); - }else{ - ShowError("int_homun: broken data [%s] line %d\n",homun_txt,c); - aFree(p); - } - c++; - } - fclose(fp); -// printf("int_homun: %s read done (%d homuns)\n",homun_txt,c); - return 0; -} - -void inter_homun_final() -{ - homun_db->destroy(homun_db, NULL); - return; -} - -int inter_homun_save_sub(DBKey key,void *data,va_list ap) -{ - char line[8192]; - FILE *fp; - inter_homun_tostr(line,(struct s_homunculus *)data); - fp=va_arg(ap,FILE *); - fprintf(fp,"%s" RETCODE,line); - return 0; -} - -int inter_homun_save() -{ - FILE *fp; - int lock; - if( (fp=lock_fopen(homun_txt,&lock))==NULL ){ - ShowError("int_homun: cant write [%s] !!! data is lost !!!\n",homun_txt); - return 1; - } - homun_db->foreach(homun_db,inter_homun_save_sub,fp); - lock_fclose(fp,homun_txt,&lock); -// printf("int_homun: %s saved.\n",homun_txt); - return 0; -} - -int inter_homun_delete(int hom_id) -{ - struct s_homunculus *p; - p = idb_get(homun_db,hom_id); - if( p == NULL) - return 0; - idb_remove(homun_db,hom_id); - ShowInfo("Deleted homun (hom_id: %d)\n",hom_id); - return 1; -} - -int mapif_homun_created(int fd,int account_id, struct s_homunculus *p) -{ - WFIFOHEAD(fd, sizeof(struct s_homunculus)+9); - WFIFOW(fd, 0) =0x3890; - WFIFOW(fd,2) = sizeof(struct s_homunculus)+9; - WFIFOL(fd,4) = account_id; - WFIFOB(fd,8)= p->hom_id?1:0; - memcpy(WFIFOP(fd,9), p, sizeof(struct s_homunculus)); - WFIFOSET(fd, WFIFOW(fd,2)); - return 0; -} - -int mapif_homun_info(int fd,int account_id,struct s_homunculus *p) -{ - WFIFOHEAD(fd, sizeof(struct s_homunculus)+9); - WFIFOW(fd,0) = 0x3891; - WFIFOW(fd,2) = sizeof(struct s_homunculus)+9; - WFIFOL(fd,4) = account_id; - WFIFOB(fd,8) = 1; // account loaded with success - - memcpy(WFIFOP(fd,9), p, sizeof(struct s_homunculus)); - WFIFOSET(fd,WFIFOW(fd,2)); - return 0; -} - -int mapif_homun_noinfo(int fd,int account_id) -{ - WFIFOHEAD(fd,sizeof(struct s_homunculus) + 9); - WFIFOW(fd,0)=0x3891; - WFIFOW(fd,2)=sizeof(struct s_homunculus) + 9; - WFIFOL(fd,4)=account_id; - WFIFOB(fd,8)=0; - memset(WFIFOP(fd,9),0,sizeof(struct s_homunculus)); - WFIFOSET(fd,WFIFOW(fd,2)); - - return 0; -} - -int mapif_save_homun_ack(int fd,int account_id,int flag) -{ - WFIFOHEAD(fd, 7); - WFIFOW(fd,0)=0x3892; - WFIFOL(fd,2)=account_id; - WFIFOB(fd,6)=flag; - WFIFOSET(fd,7); - return 0; -} - -int mapif_delete_homun_ack(int fd,int flag) -{ - WFIFOHEAD(fd, 3); - WFIFOW(fd,0)=0x3893; - WFIFOB(fd,2)=flag; - WFIFOSET(fd,3); - return 0; -} - -int mapif_rename_homun_ack(int fd, int account_id, int char_id, int flag, char *name){ - WFIFOHEAD(fd, NAME_LENGTH+12); - WFIFOW(fd, 0) =0x3894; - WFIFOL(fd, 2) =account_id; - WFIFOL(fd, 6) =char_id; - WFIFOB(fd, 10) =flag; - memcpy(WFIFOP(fd, 11), name, NAME_LENGTH); - WFIFOSET(fd, NAME_LENGTH+12); - - return 0; -} - -int mapif_create_homun(int fd) -{ - struct s_homunculus *p; - RFIFOHEAD(fd); - p= (struct s_homunculus *) aCalloc(sizeof(struct s_homunculus), 1); - if(p==NULL){ - ShowFatalError("int_homun: out of memory !\n"); - //Sending the received data will pass hom_id == 0 <- fail. - mapif_homun_created(fd,RFIFOL(fd,4),(struct s_homunculus*)RFIFOP(fd,8)); - return 0; - } - memcpy(p, RFIFOP(fd,8), sizeof(struct s_homunculus)); - p->hom_id = homun_newid++; //New ID - idb_put(homun_db,p->hom_id,p); - mapif_homun_created(fd,RFIFOL(fd,4),p); - return 0; -} - -int mapif_load_homun(int fd) -{ - struct s_homunculus *p; - int account_id; - RFIFOHEAD(fd); - account_id = RFIFOL(fd,2); - - p= idb_get(homun_db,RFIFOL(fd,6)); - if(p==NULL) { - mapif_homun_noinfo(fd,account_id); - return 0; - } - mapif_homun_info(fd,account_id,p); - return 0; -} - -static void* create_homun(DBKey key, va_list args) { - struct s_homunculus *p; - p=(struct s_homunculus *)aCalloc(sizeof(struct s_homunculus),1); - p->hom_id = key.i; - return p; -} -int mapif_save_homun(int fd,int account_id,struct s_homunculus *data) -{ - struct s_homunculus *p; - int hom_id; - - if (data->hom_id == 0) - data->hom_id = homun_newid++; - hom_id = data->hom_id; - p= idb_ensure(homun_db,hom_id,create_homun); - memcpy(p,data,sizeof(struct s_homunculus)); - mapif_save_homun_ack(fd,account_id,1); - return 0; -} - -int mapif_delete_homun(int fd,int hom_id) -{ - mapif_delete_homun_ack(fd,inter_homun_delete(hom_id)); - return 0; -} - -int mapif_rename_homun(int fd, int account_id, int char_id, char *name){ - int i; - - // Check Authorised letters/symbols in the name of the homun - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) { - mapif_rename_homun_ack(fd, account_id, char_id, 0, name); - return 0; - } - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) { - mapif_rename_homun_ack(fd, account_id, char_id, 0, name); - return 0; - } - } - - mapif_rename_homun_ack(fd, account_id, char_id, 1, name); - return 0; -} - -int mapif_parse_SaveHomun(int fd) -{ - RFIFOHEAD(fd); - mapif_save_homun(fd,RFIFOL(fd,4),(struct s_homunculus *)RFIFOP(fd,8)); - return 0; -} - -int mapif_parse_DeleteHomun(int fd) -{ - RFIFOHEAD(fd); - mapif_delete_homun(fd,RFIFOL(fd,2)); - return 0; -} - -int mapif_parse_RenameHomun(int fd){ - RFIFOHEAD(fd); - mapif_rename_homun(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOP(fd, 10)); - return 0; -} - -int inter_homun_parse_frommap(int fd) -{ - RFIFOHEAD(fd); - switch(RFIFOW(fd,0)){ - case 0x3090: mapif_create_homun(fd); break; - case 0x3091: mapif_load_homun(fd); break; - case 0x3092: mapif_parse_SaveHomun(fd); break; - case 0x3093: mapif_parse_DeleteHomun(fd); break; - case 0x3094: mapif_parse_RenameHomun(fd); break; - default: - return 0; - } - return 1; -} - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include + +#include "../common/mmo.h" +#include "../common/socket.h" +#include "../common/db.h" +#include "../common/lock.h" +#include "../common/showmsg.h" +#include "char.h" +#include "inter.h" +#include "int_homun.h" + +char homun_txt[1024]="save/homun.txt"; + +static struct dbt *homun_db; +static int homun_newid = 100; + +int inter_homun_tostr(char *str,struct s_homunculus *p) +{ + int i; + + str+=sprintf(str,"%d,%d\t%s\t%d,%d,%d,%d,%d," + "%u,%d,%d,%d," + "%u,%d,%d," + "%d,%d,%d,%d,%d,%d\t", + p->hom_id, p->class_, p->name, + p->char_id, p->hp, p->max_hp, p->sp, p->max_sp, + p->intimacy, p->hunger, p->skillpts, p->level, + p->exp, p->rename_flag, p->vaporize, + p->str, p->agi, p->vit, p->int_, p->dex, p->luk); + + for (i = 0; i < MAX_HOMUNSKILL; i++) + { + if (p->hskill[i].id && !p->hskill[i].flag) + str+=sprintf(str,"%d,%d,", p->hskill[i].id, p->hskill[i].lv); + } + + return 0; +} + +int inter_homun_fromstr(char *str,struct s_homunculus *p) +{ + int i, next, len; + int tmp_int[25]; + unsigned int tmp_uint[5]; + char tmp_str[256]; + + memset(p,0,sizeof(struct s_homunculus)); + + i=sscanf(str,"%d,%d\t%127[^\t]\t%d,%d,%d,%d,%d," + "%u,%d,%d,%d," + "%u,%d,%d," + "%d,%d,%d,%d,%d,%d\t%n", + &tmp_int[0],&tmp_int[1],tmp_str, + &tmp_int[2],&tmp_int[3],&tmp_int[4],&tmp_int[5],&tmp_int[6], + &tmp_uint[0],&tmp_int[7],&tmp_int[8],&tmp_int[9], + &tmp_uint[1],&tmp_int[10],&tmp_int[11], + &tmp_int[12],&tmp_int[13],&tmp_int[14],&tmp_int[15],&tmp_int[16],&tmp_int[17], + &next); + + if(i!=21) + return 1; + + p->hom_id = tmp_int[0]; + p->class_ = tmp_int[1]; + memcpy(p->name, tmp_str, NAME_LENGTH-1); + + p->char_id = tmp_int[2]; + p->hp = tmp_int[3]; + p->max_hp = tmp_int[4]; + p->sp = tmp_int[5]; + p->max_sp = tmp_int[6]; + + p->intimacy = tmp_uint[0]; + p->hunger = tmp_int[7]; + p->skillpts = tmp_int[8]; + p->level = tmp_int[9]; + + p->exp = tmp_uint[1]; + p->rename_flag = tmp_int[10]; + p->vaporize = tmp_int[11]; + + p->str = tmp_int[12]; + p->agi = tmp_int[13]; + p->vit = tmp_int[14]; + p->int_= tmp_int[15]; + p->dex = tmp_int[16]; + p->luk = tmp_int[17]; + + //Read skills. + while(str[next] && str[next] != '\n' && str[next] != '\r') { + if (sscanf(str+next, "%d,%d,%n", &tmp_int[0], &tmp_int[1], &len) != 2) + return 2; + + if (tmp_int[0] > HM_SKILLBASE && tmp_int[0] <= HM_SKILLBASE+MAX_HOMUNSKILL) + { + i = tmp_int[0] - HM_SKILLBASE -1; + p->hskill[i].id = tmp_int[0]; + p->hskill[i].lv = tmp_int[1]; + } else + ShowError("Read Homun: Unsupported Skill ID %d for homunculus (Homun ID=%d\n", tmp_int[0], p->hom_id); + next += len; + if (str[next] == ' ') + next++; + } + return 0; +} + +int inter_homun_init() +{ + char line[8192]; + struct s_homunculus *p; + FILE *fp; + int c=0; + + homun_db= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + if( (fp=fopen(homun_txt,"r"))==NULL ) + return 1; + while(fgets(line,sizeof(line),fp)){ + p = (struct s_homunculus*)aCalloc(sizeof(struct s_homunculus), 1); + if(p==NULL){ + ShowFatalError("int_homun: out of memory!\n"); + exit(0); + } + if(inter_homun_fromstr(line,p)==0 && p->hom_id>0){ + if( p->hom_id >= homun_newid) + homun_newid=p->hom_id+1; + idb_put(homun_db,p->hom_id,p); + }else{ + ShowError("int_homun: broken data [%s] line %d\n",homun_txt,c); + aFree(p); + } + c++; + } + fclose(fp); +// printf("int_homun: %s read done (%d homuns)\n",homun_txt,c); + return 0; +} + +void inter_homun_final() +{ + homun_db->destroy(homun_db, NULL); + return; +} + +int inter_homun_save_sub(DBKey key,void *data,va_list ap) +{ + char line[8192]; + FILE *fp; + inter_homun_tostr(line,(struct s_homunculus *)data); + fp=va_arg(ap,FILE *); + fprintf(fp,"%s" RETCODE,line); + return 0; +} + +int inter_homun_save() +{ + FILE *fp; + int lock; + if( (fp=lock_fopen(homun_txt,&lock))==NULL ){ + ShowError("int_homun: cant write [%s] !!! data is lost !!!\n",homun_txt); + return 1; + } + homun_db->foreach(homun_db,inter_homun_save_sub,fp); + lock_fclose(fp,homun_txt,&lock); +// printf("int_homun: %s saved.\n",homun_txt); + return 0; +} + +int inter_homun_delete(int hom_id) +{ + struct s_homunculus *p; + p = idb_get(homun_db,hom_id); + if( p == NULL) + return 0; + idb_remove(homun_db,hom_id); + ShowInfo("Deleted homun (hom_id: %d)\n",hom_id); + return 1; +} + +int mapif_homun_created(int fd,int account_id, struct s_homunculus *p) +{ + WFIFOHEAD(fd, sizeof(struct s_homunculus)+9); + WFIFOW(fd, 0) =0x3890; + WFIFOW(fd,2) = sizeof(struct s_homunculus)+9; + WFIFOL(fd,4) = account_id; + WFIFOB(fd,8)= p->hom_id?1:0; + memcpy(WFIFOP(fd,9), p, sizeof(struct s_homunculus)); + WFIFOSET(fd, WFIFOW(fd,2)); + return 0; +} + +int mapif_homun_info(int fd,int account_id,struct s_homunculus *p) +{ + WFIFOHEAD(fd, sizeof(struct s_homunculus)+9); + WFIFOW(fd,0) = 0x3891; + WFIFOW(fd,2) = sizeof(struct s_homunculus)+9; + WFIFOL(fd,4) = account_id; + WFIFOB(fd,8) = 1; // account loaded with success + + memcpy(WFIFOP(fd,9), p, sizeof(struct s_homunculus)); + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} + +int mapif_homun_noinfo(int fd,int account_id) +{ + WFIFOHEAD(fd,sizeof(struct s_homunculus) + 9); + WFIFOW(fd,0)=0x3891; + WFIFOW(fd,2)=sizeof(struct s_homunculus) + 9; + WFIFOL(fd,4)=account_id; + WFIFOB(fd,8)=0; + memset(WFIFOP(fd,9),0,sizeof(struct s_homunculus)); + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +int mapif_save_homun_ack(int fd,int account_id,int flag) +{ + WFIFOHEAD(fd, 7); + WFIFOW(fd,0)=0x3892; + WFIFOL(fd,2)=account_id; + WFIFOB(fd,6)=flag; + WFIFOSET(fd,7); + return 0; +} + +int mapif_delete_homun_ack(int fd,int flag) +{ + WFIFOHEAD(fd, 3); + WFIFOW(fd,0)=0x3893; + WFIFOB(fd,2)=flag; + WFIFOSET(fd,3); + return 0; +} + +int mapif_rename_homun_ack(int fd, int account_id, int char_id, int flag, char *name){ + WFIFOHEAD(fd, NAME_LENGTH+12); + WFIFOW(fd, 0) =0x3894; + WFIFOL(fd, 2) =account_id; + WFIFOL(fd, 6) =char_id; + WFIFOB(fd, 10) =flag; + memcpy(WFIFOP(fd, 11), name, NAME_LENGTH); + WFIFOSET(fd, NAME_LENGTH+12); + + return 0; +} + +int mapif_create_homun(int fd) +{ + struct s_homunculus *p; + RFIFOHEAD(fd); + p= (struct s_homunculus *) aCalloc(sizeof(struct s_homunculus), 1); + if(p==NULL){ + ShowFatalError("int_homun: out of memory !\n"); + //Sending the received data will pass hom_id == 0 <- fail. + mapif_homun_created(fd,RFIFOL(fd,4),(struct s_homunculus*)RFIFOP(fd,8)); + return 0; + } + memcpy(p, RFIFOP(fd,8), sizeof(struct s_homunculus)); + p->hom_id = homun_newid++; //New ID + idb_put(homun_db,p->hom_id,p); + mapif_homun_created(fd,RFIFOL(fd,4),p); + return 0; +} + +int mapif_load_homun(int fd) +{ + struct s_homunculus *p; + int account_id; + RFIFOHEAD(fd); + account_id = RFIFOL(fd,2); + + p= idb_get(homun_db,RFIFOL(fd,6)); + if(p==NULL) { + mapif_homun_noinfo(fd,account_id); + return 0; + } + mapif_homun_info(fd,account_id,p); + return 0; +} + +static void* create_homun(DBKey key, va_list args) { + struct s_homunculus *p; + p=(struct s_homunculus *)aCalloc(sizeof(struct s_homunculus),1); + p->hom_id = key.i; + return p; +} +int mapif_save_homun(int fd,int account_id,struct s_homunculus *data) +{ + struct s_homunculus *p; + int hom_id; + + if (data->hom_id == 0) + data->hom_id = homun_newid++; + hom_id = data->hom_id; + p= idb_ensure(homun_db,hom_id,create_homun); + memcpy(p,data,sizeof(struct s_homunculus)); + mapif_save_homun_ack(fd,account_id,1); + return 0; +} + +int mapif_delete_homun(int fd,int hom_id) +{ + mapif_delete_homun_ack(fd,inter_homun_delete(hom_id)); + return 0; +} + +int mapif_rename_homun(int fd, int account_id, int char_id, char *name){ + int i; + + // Check Authorised letters/symbols in the name of the homun + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) == NULL) { + mapif_rename_homun_ack(fd, account_id, char_id, 0, name); + return 0; + } + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) != NULL) { + mapif_rename_homun_ack(fd, account_id, char_id, 0, name); + return 0; + } + } + + mapif_rename_homun_ack(fd, account_id, char_id, 1, name); + return 0; +} + +int mapif_parse_SaveHomun(int fd) +{ + RFIFOHEAD(fd); + mapif_save_homun(fd,RFIFOL(fd,4),(struct s_homunculus *)RFIFOP(fd,8)); + return 0; +} + +int mapif_parse_DeleteHomun(int fd) +{ + RFIFOHEAD(fd); + mapif_delete_homun(fd,RFIFOL(fd,2)); + return 0; +} + +int mapif_parse_RenameHomun(int fd){ + RFIFOHEAD(fd); + mapif_rename_homun(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOP(fd, 10)); + return 0; +} + +int inter_homun_parse_frommap(int fd) +{ + RFIFOHEAD(fd); + switch(RFIFOW(fd,0)){ + case 0x3090: mapif_create_homun(fd); break; + case 0x3091: mapif_load_homun(fd); break; + case 0x3092: mapif_parse_SaveHomun(fd); break; + case 0x3093: mapif_parse_DeleteHomun(fd); break; + case 0x3094: mapif_parse_RenameHomun(fd); break; + default: + return 0; + } + return 1; +} + diff --git a/src/char/int_homun.h b/src/char/int_homun.h index d9c0e4689..5987d23f8 100644 --- a/src/char/int_homun.h +++ b/src/char/int_homun.h @@ -1,16 +1,16 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INT_HOMUN_H_ -#define _INT_HOMUN_H_ - -int inter_homun_init(void); -void inter_homun_final(void); -int inter_homun_save(void); -int inter_homun_delete(int homun_id); - -int inter_homun_parse_frommap(int fd); - -extern char homun_txt[1024]; - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INT_HOMUN_H_ +#define _INT_HOMUN_H_ + +int inter_homun_init(void); +void inter_homun_final(void); +int inter_homun_save(void); +int inter_homun_delete(int homun_id); + +int inter_homun_parse_frommap(int fd); + +extern char homun_txt[1024]; + +#endif diff --git a/src/char/int_party.c b/src/char/int_party.c index 13fbaf489..be8c6a04c 100644 --- a/src/char/int_party.c +++ b/src/char/int_party.c @@ -1,747 +1,747 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include - -#include "../common/mmo.h" -#include "../common/socket.h" -#include "../common/db.h" -#include "../common/lock.h" -#include "../common/showmsg.h" -#include "char.h" -#include "inter.h" -#include "int_party.h" - -char party_txt[1024] = "save/party.txt"; -#ifndef TXT_SQL_CONVERT -struct party_data { - struct party party; - unsigned int min_lv, max_lv; - int family; //Is this party a family? if so, this holds the child id. - unsigned char size; //Total size of party. -}; - -static struct dbt *party_db; -static int party_newid = 100; - -int mapif_party_broken(int party_id, int flag); -int party_check_empty(struct party *p); -int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id); -int party_check_exp_share(struct party_data *p); -int mapif_party_optionchanged(int fd,struct party *p, int account_id, int flag); - -//Updates party's level range and unsets even share if broken. -static int int_party_check_lv(struct party_data *p) { - int i; - unsigned int lv; - p->min_lv = UINT_MAX; - p->max_lv = 0; - for(i=0;iparty.member[i].online) - continue; - - lv=p->party.member[i].lv; - if (lv < p->min_lv) p->min_lv = lv; - if (lv > p->max_lv) p->max_lv = lv; - } - - if (p->party.exp && !party_check_exp_share(p)) { - p->party.exp = 0; - mapif_party_optionchanged(0, &p->party, 0, 0); - return 0; - } - return 1; -} - -//Calculates the state of a party. -static void int_party_calc_state(struct party_data *p) -{ - int i; - unsigned int lv; - p->min_lv = UINT_MAX; - p->max_lv = 0; - p->party.count = - p->size = - p->family = 0; - - //Check party size. - for(i=0;iparty.member[i].lv) continue; - p->size++; - if(p->party.member[i].online) - p->party.count++; - } - if(p->size == 3) { - //Check Family State. - p->family = char_family( - p->party.member[0].char_id, - p->party.member[1].char_id, - p->party.member[2].char_id - ); - } - //max/min levels. - for(i=0;iparty.member[i].lv; - if (!lv) continue; - if(p->party.member[i].online && - //On families, the kid is not counted towards exp share rules. - p->party.member[i].char_id != p->family) - { - if( lv < p->min_lv ) p->min_lv=lv; - if( p->max_lv < lv ) p->max_lv=lv; - } - } - - if (p->party.exp && !party_check_exp_share(p)) { - p->party.exp = 0; //Set off even share. - mapif_party_optionchanged(0, &p->party, 0, 0); - } - return; -} - -// パ?ティデ?タの文字列への?換 -int inter_party_tostr(char *str, struct party *p) { - int i, len; - - len = sprintf(str, "%d\t%s\t%d,%d\t", p->party_id, p->name, p->exp, p->item); - for(i = 0; i < MAX_PARTY; i++) { - struct party_member *m = &p->member[i]; - len += sprintf(str + len, "%d,%d,%d\t", m->account_id, m->char_id, m->leader); - } - - return 0; -} -#endif //TXT_SQL_CONVERT -// パ?ティデ?タの文字列からの?換 -int inter_party_fromstr(char *str, struct party *p) { - int i, j; - int tmp_int[16]; - char tmp_str[256]; -#ifndef TXT_SQL_CONVERT - struct mmo_charstatus* status; -#endif - - memset(p, 0, sizeof(struct party)); - -// printf("sscanf party main info\n"); - if (sscanf(str, "%d\t%255[^\t]\t%d,%d\t", &tmp_int[0], tmp_str, &tmp_int[1], &tmp_int[2]) != 4) - return 1; - - p->party_id = tmp_int[0]; - memcpy(p->name, tmp_str, NAME_LENGTH-1); - p->exp = tmp_int[1]?1:0; - p->item = tmp_int[2]; -// printf("%d [%s] %d %d\n", tmp_int[0], tmp_str[0], tmp_int[1], tmp_int[2]); - - for(j = 0; j < 3 && str != NULL; j++) - str = strchr(str + 1, '\t'); - - for(i = 0; i < MAX_PARTY; i++) { - struct party_member *m = &p->member[i]; - if (str == NULL) - return 1; -// printf("sscanf party member info %d\n", i); - - if (sscanf(str + 1, "%d,%d,%d\t", &tmp_int[0], &tmp_int[1], &tmp_int[2]) != 3) - return 1; - - m->account_id = tmp_int[0]; - m->char_id = tmp_int[1]; - m->leader = tmp_int[2]?1:0; - - str = strchr(str + 1, '\t'); -#ifndef TXT_SQL_CONVERT - if (!m->account_id) continue; - //Lookup player for rest of data. - status = search_character(m->account_id, m->char_id); - if (!status) continue; - - memcpy(m->name, status->name, NAME_LENGTH); - m->class_ = status->class_; - m->map = status->last_point.map; - m->lv = status->base_level; -#endif //TXT_SQL_CONVERT - } - - return 0; -} -#ifndef TXT_SQL_CONVERT -// パ?ティデ?タのロ?ド -int inter_party_init() { - char line[8192]; - struct party_data *p; - FILE *fp; - int c = 0; - int i, j; - - party_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - if ((fp = fopen(party_txt, "r")) == NULL) - return 1; - - while(fgets(line, sizeof(line) - 1, fp)) { - j = 0; - if (sscanf(line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 && party_newid <= i) { - party_newid = i; - continue; - } - - p = (struct party_data*)aCalloc(sizeof(struct party_data), 1); - if (p == NULL){ - ShowFatalError("int_party: out of memory!\n"); - exit(0); - } - memset(p, 0, sizeof(struct party_data)); - if (inter_party_fromstr(line, &p->party) == 0 && p->party.party_id > 0) { - int_party_calc_state(p); - if (p->party.party_id >= party_newid) - party_newid = p->party.party_id + 1; - idb_put(party_db, p->party.party_id, p); - party_check_empty(&p->party); - } else { - ShowError("int_party: broken data [%s] line %d\n", party_txt, c + 1); - aFree(p); - } - c++; - } - fclose(fp); - - return 0; -} - -void inter_party_final() -{ - party_db->destroy(party_db, NULL); - return; -} - -// パ?ティ?デ?タのセ?ブ用 -int inter_party_save_sub(DBKey key, void *data, va_list ap) { - char line[8192]; - FILE *fp; - - inter_party_tostr(line, &((struct party_data*)data)->party); - fp = va_arg(ap, FILE *); - fprintf(fp, "%s" RETCODE, line); - - return 0; -} - -// パ?ティ?デ?タのセ?ブ -int inter_party_save() { - FILE *fp; - int lock; - - if ((fp = lock_fopen(party_txt, &lock)) == NULL) { - ShowError("int_party: cant write [%s] !!! data is lost !!!\n", party_txt); - return 1; - } - party_db->foreach(party_db, inter_party_save_sub, fp); - lock_fclose(fp,party_txt, &lock); - return 0; -} - -// パ?ティ名?索用 -int search_partyname_sub(DBKey key,void *data,va_list ap) { - struct party_data *p = (struct party_data *)data,**dst; - char *str; - - str = va_arg(ap, char *); - dst = va_arg(ap, struct party_data **); - if (strncmpi(p->party.name, str, NAME_LENGTH) == 0) - *dst = p; - - return 0; -} - -// パ?ティ名?索 -struct party_data* search_partyname(char *str) { - struct party_data *p = NULL; - party_db->foreach(party_db, search_partyname_sub, str, &p); - return p; -} - -// Returns whether this party can keep having exp share or not. -int party_check_exp_share(struct party_data *p) { - return (p->party.count < 2|| p->max_lv - p->min_lv <= party_share_level); -} - -// パ?ティが空かどうかチェック -int party_check_empty(struct party *p) { - int i; - - for(i = 0; i < MAX_PARTY; i++) { - if (p->member[i].account_id > 0) { - return 0; - } - } - mapif_party_broken(p->party_id, 0); - idb_remove(party_db, p->party_id); - - return 1; -} - -// キャラの競合がないかチェック用 -int party_check_conflict_sub(DBKey key, void *data, va_list ap) { - struct party_data *p = (struct party_data *)data; - int party_id, account_id, char_id, i; - - party_id=va_arg(ap, int); - account_id=va_arg(ap, int); - char_id=va_arg(ap, int); - - if (p->party.party_id == party_id) //No conflict to check - return 0; - - for(i = 0; i < MAX_PARTY; i++) { - if(p->party.member[i].account_id == account_id && - p->party.member[i].char_id == char_id) - { - ShowWarning("int_party: party conflict! %d %d %d\n", account_id, party_id, p->party.party_id); - mapif_parse_PartyLeave(-1, p->party.party_id, account_id, char_id); - } - } - - return 0; -} - -// キャラの競合がないかチェック -int party_check_conflict(int party_id, int account_id, int char_id) { - party_db->foreach(party_db, party_check_conflict_sub, party_id, account_id, char_id); - return 0; -} - -//------------------------------------------------------------------- -// map serverへの通信 - -// パ?ティ作成可否 -int mapif_party_created(int fd,int account_id, int char_id, struct party *p) { - WFIFOHEAD(fd, 39); - WFIFOW(fd,0) = 0x3820; - WFIFOL(fd,2) = account_id; - WFIFOL(fd,6) = char_id; - if (p != NULL) { - WFIFOB(fd,10) = 0; - WFIFOL(fd,11) = p->party_id; - memcpy(WFIFOP(fd,15), p->name, NAME_LENGTH); - ShowInfo("Created party (%d - %s)\n", p->party_id, p->name); - } else { - WFIFOB(fd,10) = 1; - WFIFOL(fd,11) = 0; - memset(WFIFOP(fd,15), 0, NAME_LENGTH); - } - WFIFOSET(fd,39); - return 0; -} - -// パ?ティ情報見つからず -int mapif_party_noinfo(int fd, int party_id) { - WFIFOHEAD(fd, 8); - WFIFOW(fd,0) = 0x3821; - WFIFOW(fd,2) = 8; - WFIFOL(fd,4) = party_id; - WFIFOSET(fd,8); - ShowWarning("int_party: info not found %d\n", party_id); - - return 0; -} - -// パ?ティ情報まとめ送り -int mapif_party_info(int fd, struct party *p) { - unsigned char buf[2048]; - - WBUFW(buf,0) = 0x3821; - memcpy(buf + 4, p, sizeof(struct party)); - WBUFW(buf,2) = 4 + sizeof(struct party); - if (fd < 0) - mapif_sendall(buf, WBUFW(buf,2)); - else - mapif_send(fd, buf, WBUFW(buf,2)); - return 0; -} - -// パ?ティメンバ追加可否 -int mapif_party_memberadded(int fd, int party_id, int account_id, int char_id, int flag) { - WFIFOHEAD(fd, 15); - WFIFOW(fd,0) = 0x3822; - WFIFOL(fd,2) = party_id; - WFIFOL(fd,6) = account_id; - WFIFOL(fd,10) = char_id; - WFIFOB(fd,14) = flag; - WFIFOSET(fd,15); - - return 0; -} - -// パ?ティ設定?更通知 -int mapif_party_optionchanged(int fd,struct party *p, int account_id, int flag) { - unsigned char buf[15]; - - WBUFW(buf,0) = 0x3823; - WBUFL(buf,2) = p->party_id; - WBUFL(buf,6) = account_id; - WBUFW(buf,10) = p->exp; - WBUFW(buf,12) = p->item; - WBUFB(buf,14) = flag; - if (flag == 0) - mapif_sendall(buf, 15); - else - mapif_send(fd, buf, 15); - return 0; -} - -//Checks whether the even-share setting of a party is broken when a character logs in. [Skotlex] -int inter_party_logged(int party_id, int account_id, int char_id) -{ - struct party_data *p; - int i; - if (!party_id) - return 0; - - p = idb_get(party_db, party_id); - if(p==NULL) - return 0; - for (i = 0; i < MAX_PARTY; i++) - if(p->party.member[i].account_id == account_id && - p->party.member[i].char_id == char_id) - { - p->party.member[i].online = 1; - p->party.count++; - if(p->party.member[i].lv < p->min_lv || - p->party.member[i].lv > p->max_lv) - int_party_check_lv(p); - break; - } - return 0; -} - -// パ?ティ?退通知 -int mapif_party_leaved(int party_id,int account_id, int char_id) { - unsigned char buf[16]; - - WBUFW(buf,0) = 0x3824; - WBUFL(buf,2) = party_id; - WBUFL(buf,6) = account_id; - WBUFL(buf,10) = char_id; - mapif_sendall(buf, 14); - return 0; -} - -// パ?ティマップ更新通知 -int mapif_party_membermoved(struct party *p, int idx) { - unsigned char buf[20]; - - WBUFW(buf,0) = 0x3825; - WBUFL(buf,2) = p->party_id; - WBUFL(buf,6) = p->member[idx].account_id; - WBUFL(buf,10) = p->member[idx].char_id; - WBUFW(buf,14) = p->member[idx].map; - WBUFB(buf,16) = p->member[idx].online; - WBUFW(buf,17) = p->member[idx].lv; - mapif_sendall(buf, 19); - return 0; -} - -// パ?ティ解散通知 -int mapif_party_broken(int party_id, int flag) { - unsigned char buf[7]; - WBUFW(buf,0) = 0x3826; - WBUFL(buf,2) = party_id; - WBUFB(buf,6) = flag; - mapif_sendall(buf, 7); - ShowInfo("Party broken (%d)\n", party_id); - - return 0; -} - -// パ?ティ??言 -int mapif_party_message(int party_id, int account_id, char *mes, int len, int sfd) { - unsigned char buf[2048]; - - WBUFW(buf,0) = 0x3827; - WBUFW(buf,2) = len + 12; - WBUFL(buf,4) = party_id; - WBUFL(buf,8) = account_id; - memcpy(WBUFP(buf,12), mes, len); - mapif_sendallwos(sfd, buf,len + 12); - - return 0; -} - -//------------------------------------------------------------------- -// map serverからの通信 - - -// パ?ティ -int mapif_parse_CreateParty(int fd, char *name, int item, int item2, struct party_member *leader) { - struct party_data *p; - int i; - - for(i = 0; i < NAME_LENGTH && name[i]; i++) { - if (!(name[i] & 0xe0) || name[i] == 0x7f) { - ShowInfo("int_party: illegal party name [%s]\n", name); - mapif_party_created(fd, leader->account_id, leader->char_id, NULL); - return 0; - } - } - - if ((p = search_partyname(name)) != NULL) { - ShowInfo("int_party: same name party exists [%s]\n", name); - mapif_party_created(fd, leader->account_id, leader->char_id, NULL); - return 0; - } - - // Check Authorised letters/symbols in the name of the character - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) { - mapif_party_created(fd, leader->account_id, leader->char_id, NULL); - return 0; - } - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) { - mapif_party_created(fd, leader->account_id, leader->char_id, NULL); - return 0; - } - } - - p = (struct party_data *) aCalloc(sizeof(struct party_data), 1); - if (p == NULL) { - ShowFatalError("int_party: out of memory !\n"); - mapif_party_created(fd,leader->account_id,leader->char_id,NULL); - return 0; - } - p->party.party_id = party_newid++; - memcpy(p->party.name, name, NAME_LENGTH); - p->party.exp = 0; - p->party.item=(item?1:0)|(item2?2:0); - memcpy(&p->party.member[0], leader, sizeof(struct party_member)); - p->party.member[0].leader = 1; - int_party_calc_state(p); - idb_put(party_db, p->party.party_id, p); - - mapif_party_created(fd, leader->account_id, leader->char_id, &p->party); - mapif_party_info(fd, &p->party); - - return 0; -} - -// パ?ティ情報要求 -int mapif_parse_PartyInfo(int fd, int party_id) { - struct party_data *p; - - p = idb_get(party_db, party_id); - if (p != NULL) - mapif_party_info(fd, &p->party); - else { - mapif_party_noinfo(fd, party_id); - char_clearparty(party_id); - } - - return 0; -} - -// パ?ティ追加要求 -int mapif_parse_PartyAddMember(int fd, int party_id, struct party_member *member) { - struct party_data *p; - int i; - - p = idb_get(party_db, party_id); - if (p == NULL || p->size == MAX_PARTY) { - mapif_party_memberadded(fd, party_id, member->account_id, member->char_id, 1); - return 0; - } - - for(i = 0; i < MAX_PARTY; i++) { - if (p->party.member[i].account_id == 0) { - memcpy(&p->party.member[i], member, sizeof(struct party_member)); - p->party.member[i].leader = 0; - if (p->party.member[i].online) p->party.count++; - p->size++; - if (p->size == 3) //Check family state. - int_party_calc_state(p); - else //Check even share range. - if (member->lv < p->min_lv || member->lv > p->max_lv || p->family) { - if (p->family) p->family = 0; //Family state broken. - int_party_check_lv(p); - } - mapif_party_memberadded(fd, party_id, member->account_id, member->char_id, 0); - mapif_party_info(-1, &p->party); - return 0; - } - } - mapif_party_memberadded(fd, party_id, member->account_id, member->char_id, 1); - - return 0; -} - -// パ?ティ?設定?更要求 -int mapif_parse_PartyChangeOption(int fd, int party_id, int account_id, int exp, int item) { - struct party_data *p; - int flag = 0; - - p = idb_get(party_db, party_id); - if (p == NULL) - return 0; - - p->party.exp = exp; - if (exp>0 && !party_check_exp_share(p)) { - flag |= 0x01; - p->party.exp = 0; - } - p->party.item = item&0x3; - mapif_party_optionchanged(fd, &p->party, account_id, flag); - return 0; -} - -// パ?ティ?退要求 -int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) { - struct party_data *p; - int i,lv; - - p = idb_get(party_db, party_id); - if (!p) return 0; - - for(i = 0; i < MAX_PARTY; i++) { - if(p->party.member[i].account_id == account_id && - p->party.member[i].char_id == char_id) - { - mapif_party_leaved(party_id, account_id, char_id); - lv = p->party.member[i].lv; - if(p->party.member[i].online) p->party.count--; - memset(&p->party.member[i], 0, sizeof(struct party_member)); - p->size--; - if (lv == p->min_lv || lv == p->max_lv || p->family) - { - if(p->family) p->family = 0; //Family state broken. - int_party_check_lv(p); - } - if (party_check_empty(&p->party) == 0) - mapif_party_info(-1, &p->party); - return 0; - } - } - return 0; -} - -int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id, unsigned short map, int online, unsigned int lv) -{ - struct party_data *p; - int i; - - p = idb_get(party_db, party_id); - if (p == NULL) - return 0; - - for(i = 0; i < MAX_PARTY; i++) { - if(p->party.member[i].account_id == account_id && - p->party.member[i].char_id == char_id) - { - p->party.member[i].map = map; - if (p->party.member[i].online != online) - { - p->party.member[i].online = online; - if (online) - p->party.count++; - else - p->party.count--; - // Even share check situations: Family state (always breaks) - // character logging on/off is max/min level (update level range) - // or character logging on/off has a different level (update level range using new level) - if (p->family || - (p->party.member[i].lv <= p->min_lv || p->party.member[i].lv >= p->max_lv) || - (p->party.member[i].lv != lv && (lv <= p->min_lv || lv >= p->max_lv)) - ) - { - p->party.member[i].lv = lv; - int_party_check_lv(p); - } - } - if (p->party.member[i].lv != lv) { - if(p->party.member[i].lv == p->min_lv || - p->party.member[i].lv == p->max_lv) - { - p->party.member[i].lv = lv; - int_party_check_lv(p); - } else - p->party.member[i].lv = lv; - } - mapif_party_membermoved(&p->party, i); - break; - } - } - return 0; -} - -// パ?ティ解散要求 -int mapif_parse_BreakParty(int fd, int party_id) { - - idb_remove(party_db, party_id); - mapif_party_broken(fd, party_id); - - return 0; -} - -// パ?ティメッセ?ジ送信 -int mapif_parse_PartyMessage(int fd, int party_id, int account_id, char *mes, int len) { - return mapif_party_message(party_id, account_id, mes, len, fd); -} -// パ?ティチェック要求 -int mapif_parse_PartyCheck(int fd, int party_id, int account_id, int char_id) { - return party_check_conflict(party_id, account_id, char_id); -} - -int mapif_parse_PartyLeaderChange(int fd,int party_id,int account_id,int char_id) -{ - struct party_data *p; - int i; - - p = idb_get(party_db, party_id); - if (p == NULL) - return 0; - - for (i = 0; i < MAX_PARTY; i++) - { - if(p->party.member[i].leader) - p->party.member[i].leader = 0; - if(p->party.member[i].account_id == account_id && - p->party.member[i].char_id == char_id) - p->party.member[i].leader = 1; - } - return 1; -} - -// map server からの通信 -// ?1パケットのみ解析すること -// ?パケット長デ?タはinter.cにセットしておくこと -// ?パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない -// ?エラ?なら0(false)、そうでないなら1(true)をかえさなければならない -int inter_party_parse_frommap(int fd) { - RFIFOHEAD(fd); - switch(RFIFOW(fd,0)) { - case 0x3020: mapif_parse_CreateParty(fd, (char*)RFIFOP(fd,4), RFIFOB(fd,28), RFIFOB(fd,29), (struct party_member*)RFIFOP(fd,30)); break; - case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2)); break; - case 0x3022: mapif_parse_PartyAddMember(fd, RFIFOL(fd,4), (struct party_member*)RFIFOP(fd,8)); break; - case 0x3023: mapif_parse_PartyChangeOption(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOW(fd,10), RFIFOW(fd,12)); break; - case 0x3024: mapif_parse_PartyLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; - case 0x3025: mapif_parse_PartyChangeMap(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOW(fd,14), RFIFOB(fd,16), RFIFOW(fd,17)); break; - case 0x3026: mapif_parse_BreakParty(fd, RFIFOL(fd,2)); break; - case 0x3027: mapif_parse_PartyMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), (char*)RFIFOP(fd,12), RFIFOW(fd,2)-12); break; - case 0x3028: mapif_parse_PartyCheck(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; - case 0x3029: mapif_parse_PartyLeaderChange(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; - default: - return 0; - } - - return 1; -} - -// サ?バ?から?退要求(キャラ削除用) -int inter_party_leave(int party_id, int account_id, int char_id) { - return mapif_parse_PartyLeave(-1, party_id, account_id, char_id); -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include + +#include "../common/mmo.h" +#include "../common/socket.h" +#include "../common/db.h" +#include "../common/lock.h" +#include "../common/showmsg.h" +#include "char.h" +#include "inter.h" +#include "int_party.h" + +char party_txt[1024] = "save/party.txt"; +#ifndef TXT_SQL_CONVERT +struct party_data { + struct party party; + unsigned int min_lv, max_lv; + int family; //Is this party a family? if so, this holds the child id. + unsigned char size; //Total size of party. +}; + +static struct dbt *party_db; +static int party_newid = 100; + +int mapif_party_broken(int party_id, int flag); +int party_check_empty(struct party *p); +int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id); +int party_check_exp_share(struct party_data *p); +int mapif_party_optionchanged(int fd,struct party *p, int account_id, int flag); + +//Updates party's level range and unsets even share if broken. +static int int_party_check_lv(struct party_data *p) { + int i; + unsigned int lv; + p->min_lv = UINT_MAX; + p->max_lv = 0; + for(i=0;iparty.member[i].online) + continue; + + lv=p->party.member[i].lv; + if (lv < p->min_lv) p->min_lv = lv; + if (lv > p->max_lv) p->max_lv = lv; + } + + if (p->party.exp && !party_check_exp_share(p)) { + p->party.exp = 0; + mapif_party_optionchanged(0, &p->party, 0, 0); + return 0; + } + return 1; +} + +//Calculates the state of a party. +static void int_party_calc_state(struct party_data *p) +{ + int i; + unsigned int lv; + p->min_lv = UINT_MAX; + p->max_lv = 0; + p->party.count = + p->size = + p->family = 0; + + //Check party size. + for(i=0;iparty.member[i].lv) continue; + p->size++; + if(p->party.member[i].online) + p->party.count++; + } + if(p->size == 3) { + //Check Family State. + p->family = char_family( + p->party.member[0].char_id, + p->party.member[1].char_id, + p->party.member[2].char_id + ); + } + //max/min levels. + for(i=0;iparty.member[i].lv; + if (!lv) continue; + if(p->party.member[i].online && + //On families, the kid is not counted towards exp share rules. + p->party.member[i].char_id != p->family) + { + if( lv < p->min_lv ) p->min_lv=lv; + if( p->max_lv < lv ) p->max_lv=lv; + } + } + + if (p->party.exp && !party_check_exp_share(p)) { + p->party.exp = 0; //Set off even share. + mapif_party_optionchanged(0, &p->party, 0, 0); + } + return; +} + +// パ?ティデ?タの文字列への?換 +int inter_party_tostr(char *str, struct party *p) { + int i, len; + + len = sprintf(str, "%d\t%s\t%d,%d\t", p->party_id, p->name, p->exp, p->item); + for(i = 0; i < MAX_PARTY; i++) { + struct party_member *m = &p->member[i]; + len += sprintf(str + len, "%d,%d,%d\t", m->account_id, m->char_id, m->leader); + } + + return 0; +} +#endif //TXT_SQL_CONVERT +// パ?ティデ?タの文字列からの?換 +int inter_party_fromstr(char *str, struct party *p) { + int i, j; + int tmp_int[16]; + char tmp_str[256]; +#ifndef TXT_SQL_CONVERT + struct mmo_charstatus* status; +#endif + + memset(p, 0, sizeof(struct party)); + +// printf("sscanf party main info\n"); + if (sscanf(str, "%d\t%255[^\t]\t%d,%d\t", &tmp_int[0], tmp_str, &tmp_int[1], &tmp_int[2]) != 4) + return 1; + + p->party_id = tmp_int[0]; + memcpy(p->name, tmp_str, NAME_LENGTH-1); + p->exp = tmp_int[1]?1:0; + p->item = tmp_int[2]; +// printf("%d [%s] %d %d\n", tmp_int[0], tmp_str[0], tmp_int[1], tmp_int[2]); + + for(j = 0; j < 3 && str != NULL; j++) + str = strchr(str + 1, '\t'); + + for(i = 0; i < MAX_PARTY; i++) { + struct party_member *m = &p->member[i]; + if (str == NULL) + return 1; +// printf("sscanf party member info %d\n", i); + + if (sscanf(str + 1, "%d,%d,%d\t", &tmp_int[0], &tmp_int[1], &tmp_int[2]) != 3) + return 1; + + m->account_id = tmp_int[0]; + m->char_id = tmp_int[1]; + m->leader = tmp_int[2]?1:0; + + str = strchr(str + 1, '\t'); +#ifndef TXT_SQL_CONVERT + if (!m->account_id) continue; + //Lookup player for rest of data. + status = search_character(m->account_id, m->char_id); + if (!status) continue; + + memcpy(m->name, status->name, NAME_LENGTH); + m->class_ = status->class_; + m->map = status->last_point.map; + m->lv = status->base_level; +#endif //TXT_SQL_CONVERT + } + + return 0; +} +#ifndef TXT_SQL_CONVERT +// パ?ティデ?タのロ?ド +int inter_party_init() { + char line[8192]; + struct party_data *p; + FILE *fp; + int c = 0; + int i, j; + + party_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + if ((fp = fopen(party_txt, "r")) == NULL) + return 1; + + while(fgets(line, sizeof(line) - 1, fp)) { + j = 0; + if (sscanf(line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 && party_newid <= i) { + party_newid = i; + continue; + } + + p = (struct party_data*)aCalloc(sizeof(struct party_data), 1); + if (p == NULL){ + ShowFatalError("int_party: out of memory!\n"); + exit(0); + } + memset(p, 0, sizeof(struct party_data)); + if (inter_party_fromstr(line, &p->party) == 0 && p->party.party_id > 0) { + int_party_calc_state(p); + if (p->party.party_id >= party_newid) + party_newid = p->party.party_id + 1; + idb_put(party_db, p->party.party_id, p); + party_check_empty(&p->party); + } else { + ShowError("int_party: broken data [%s] line %d\n", party_txt, c + 1); + aFree(p); + } + c++; + } + fclose(fp); + + return 0; +} + +void inter_party_final() +{ + party_db->destroy(party_db, NULL); + return; +} + +// パ?ティ?デ?タのセ?ブ用 +int inter_party_save_sub(DBKey key, void *data, va_list ap) { + char line[8192]; + FILE *fp; + + inter_party_tostr(line, &((struct party_data*)data)->party); + fp = va_arg(ap, FILE *); + fprintf(fp, "%s" RETCODE, line); + + return 0; +} + +// パ?ティ?デ?タのセ?ブ +int inter_party_save() { + FILE *fp; + int lock; + + if ((fp = lock_fopen(party_txt, &lock)) == NULL) { + ShowError("int_party: cant write [%s] !!! data is lost !!!\n", party_txt); + return 1; + } + party_db->foreach(party_db, inter_party_save_sub, fp); + lock_fclose(fp,party_txt, &lock); + return 0; +} + +// パ?ティ名?索用 +int search_partyname_sub(DBKey key,void *data,va_list ap) { + struct party_data *p = (struct party_data *)data,**dst; + char *str; + + str = va_arg(ap, char *); + dst = va_arg(ap, struct party_data **); + if (strncmpi(p->party.name, str, NAME_LENGTH) == 0) + *dst = p; + + return 0; +} + +// パ?ティ名?索 +struct party_data* search_partyname(char *str) { + struct party_data *p = NULL; + party_db->foreach(party_db, search_partyname_sub, str, &p); + return p; +} + +// Returns whether this party can keep having exp share or not. +int party_check_exp_share(struct party_data *p) { + return (p->party.count < 2|| p->max_lv - p->min_lv <= party_share_level); +} + +// パ?ティが空かどうかチェック +int party_check_empty(struct party *p) { + int i; + + for(i = 0; i < MAX_PARTY; i++) { + if (p->member[i].account_id > 0) { + return 0; + } + } + mapif_party_broken(p->party_id, 0); + idb_remove(party_db, p->party_id); + + return 1; +} + +// キャラの競合がないかチェック用 +int party_check_conflict_sub(DBKey key, void *data, va_list ap) { + struct party_data *p = (struct party_data *)data; + int party_id, account_id, char_id, i; + + party_id=va_arg(ap, int); + account_id=va_arg(ap, int); + char_id=va_arg(ap, int); + + if (p->party.party_id == party_id) //No conflict to check + return 0; + + for(i = 0; i < MAX_PARTY; i++) { + if(p->party.member[i].account_id == account_id && + p->party.member[i].char_id == char_id) + { + ShowWarning("int_party: party conflict! %d %d %d\n", account_id, party_id, p->party.party_id); + mapif_parse_PartyLeave(-1, p->party.party_id, account_id, char_id); + } + } + + return 0; +} + +// キャラの競合がないかチェック +int party_check_conflict(int party_id, int account_id, int char_id) { + party_db->foreach(party_db, party_check_conflict_sub, party_id, account_id, char_id); + return 0; +} + +//------------------------------------------------------------------- +// map serverへの通信 + +// パ?ティ作成可否 +int mapif_party_created(int fd,int account_id, int char_id, struct party *p) { + WFIFOHEAD(fd, 39); + WFIFOW(fd,0) = 0x3820; + WFIFOL(fd,2) = account_id; + WFIFOL(fd,6) = char_id; + if (p != NULL) { + WFIFOB(fd,10) = 0; + WFIFOL(fd,11) = p->party_id; + memcpy(WFIFOP(fd,15), p->name, NAME_LENGTH); + ShowInfo("Created party (%d - %s)\n", p->party_id, p->name); + } else { + WFIFOB(fd,10) = 1; + WFIFOL(fd,11) = 0; + memset(WFIFOP(fd,15), 0, NAME_LENGTH); + } + WFIFOSET(fd,39); + return 0; +} + +// パ?ティ情報見つからず +int mapif_party_noinfo(int fd, int party_id) { + WFIFOHEAD(fd, 8); + WFIFOW(fd,0) = 0x3821; + WFIFOW(fd,2) = 8; + WFIFOL(fd,4) = party_id; + WFIFOSET(fd,8); + ShowWarning("int_party: info not found %d\n", party_id); + + return 0; +} + +// パ?ティ情報まとめ送り +int mapif_party_info(int fd, struct party *p) { + unsigned char buf[2048]; + + WBUFW(buf,0) = 0x3821; + memcpy(buf + 4, p, sizeof(struct party)); + WBUFW(buf,2) = 4 + sizeof(struct party); + if (fd < 0) + mapif_sendall(buf, WBUFW(buf,2)); + else + mapif_send(fd, buf, WBUFW(buf,2)); + return 0; +} + +// パ?ティメンバ追加可否 +int mapif_party_memberadded(int fd, int party_id, int account_id, int char_id, int flag) { + WFIFOHEAD(fd, 15); + WFIFOW(fd,0) = 0x3822; + WFIFOL(fd,2) = party_id; + WFIFOL(fd,6) = account_id; + WFIFOL(fd,10) = char_id; + WFIFOB(fd,14) = flag; + WFIFOSET(fd,15); + + return 0; +} + +// パ?ティ設定?更通知 +int mapif_party_optionchanged(int fd,struct party *p, int account_id, int flag) { + unsigned char buf[15]; + + WBUFW(buf,0) = 0x3823; + WBUFL(buf,2) = p->party_id; + WBUFL(buf,6) = account_id; + WBUFW(buf,10) = p->exp; + WBUFW(buf,12) = p->item; + WBUFB(buf,14) = flag; + if (flag == 0) + mapif_sendall(buf, 15); + else + mapif_send(fd, buf, 15); + return 0; +} + +//Checks whether the even-share setting of a party is broken when a character logs in. [Skotlex] +int inter_party_logged(int party_id, int account_id, int char_id) +{ + struct party_data *p; + int i; + if (!party_id) + return 0; + + p = idb_get(party_db, party_id); + if(p==NULL) + return 0; + for (i = 0; i < MAX_PARTY; i++) + if(p->party.member[i].account_id == account_id && + p->party.member[i].char_id == char_id) + { + p->party.member[i].online = 1; + p->party.count++; + if(p->party.member[i].lv < p->min_lv || + p->party.member[i].lv > p->max_lv) + int_party_check_lv(p); + break; + } + return 0; +} + +// パ?ティ?退通知 +int mapif_party_leaved(int party_id,int account_id, int char_id) { + unsigned char buf[16]; + + WBUFW(buf,0) = 0x3824; + WBUFL(buf,2) = party_id; + WBUFL(buf,6) = account_id; + WBUFL(buf,10) = char_id; + mapif_sendall(buf, 14); + return 0; +} + +// パ?ティマップ更新通知 +int mapif_party_membermoved(struct party *p, int idx) { + unsigned char buf[20]; + + WBUFW(buf,0) = 0x3825; + WBUFL(buf,2) = p->party_id; + WBUFL(buf,6) = p->member[idx].account_id; + WBUFL(buf,10) = p->member[idx].char_id; + WBUFW(buf,14) = p->member[idx].map; + WBUFB(buf,16) = p->member[idx].online; + WBUFW(buf,17) = p->member[idx].lv; + mapif_sendall(buf, 19); + return 0; +} + +// パ?ティ解散通知 +int mapif_party_broken(int party_id, int flag) { + unsigned char buf[7]; + WBUFW(buf,0) = 0x3826; + WBUFL(buf,2) = party_id; + WBUFB(buf,6) = flag; + mapif_sendall(buf, 7); + ShowInfo("Party broken (%d)\n", party_id); + + return 0; +} + +// パ?ティ??言 +int mapif_party_message(int party_id, int account_id, char *mes, int len, int sfd) { + unsigned char buf[2048]; + + WBUFW(buf,0) = 0x3827; + WBUFW(buf,2) = len + 12; + WBUFL(buf,4) = party_id; + WBUFL(buf,8) = account_id; + memcpy(WBUFP(buf,12), mes, len); + mapif_sendallwos(sfd, buf,len + 12); + + return 0; +} + +//------------------------------------------------------------------- +// map serverからの通信 + + +// パ?ティ +int mapif_parse_CreateParty(int fd, char *name, int item, int item2, struct party_member *leader) { + struct party_data *p; + int i; + + for(i = 0; i < NAME_LENGTH && name[i]; i++) { + if (!(name[i] & 0xe0) || name[i] == 0x7f) { + ShowInfo("int_party: illegal party name [%s]\n", name); + mapif_party_created(fd, leader->account_id, leader->char_id, NULL); + return 0; + } + } + + if ((p = search_partyname(name)) != NULL) { + ShowInfo("int_party: same name party exists [%s]\n", name); + mapif_party_created(fd, leader->account_id, leader->char_id, NULL); + return 0; + } + + // Check Authorised letters/symbols in the name of the character + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) == NULL) { + mapif_party_created(fd, leader->account_id, leader->char_id, NULL); + return 0; + } + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) != NULL) { + mapif_party_created(fd, leader->account_id, leader->char_id, NULL); + return 0; + } + } + + p = (struct party_data *) aCalloc(sizeof(struct party_data), 1); + if (p == NULL) { + ShowFatalError("int_party: out of memory !\n"); + mapif_party_created(fd,leader->account_id,leader->char_id,NULL); + return 0; + } + p->party.party_id = party_newid++; + memcpy(p->party.name, name, NAME_LENGTH); + p->party.exp = 0; + p->party.item=(item?1:0)|(item2?2:0); + memcpy(&p->party.member[0], leader, sizeof(struct party_member)); + p->party.member[0].leader = 1; + int_party_calc_state(p); + idb_put(party_db, p->party.party_id, p); + + mapif_party_created(fd, leader->account_id, leader->char_id, &p->party); + mapif_party_info(fd, &p->party); + + return 0; +} + +// パ?ティ情報要求 +int mapif_parse_PartyInfo(int fd, int party_id) { + struct party_data *p; + + p = idb_get(party_db, party_id); + if (p != NULL) + mapif_party_info(fd, &p->party); + else { + mapif_party_noinfo(fd, party_id); + char_clearparty(party_id); + } + + return 0; +} + +// パ?ティ追加要求 +int mapif_parse_PartyAddMember(int fd, int party_id, struct party_member *member) { + struct party_data *p; + int i; + + p = idb_get(party_db, party_id); + if (p == NULL || p->size == MAX_PARTY) { + mapif_party_memberadded(fd, party_id, member->account_id, member->char_id, 1); + return 0; + } + + for(i = 0; i < MAX_PARTY; i++) { + if (p->party.member[i].account_id == 0) { + memcpy(&p->party.member[i], member, sizeof(struct party_member)); + p->party.member[i].leader = 0; + if (p->party.member[i].online) p->party.count++; + p->size++; + if (p->size == 3) //Check family state. + int_party_calc_state(p); + else //Check even share range. + if (member->lv < p->min_lv || member->lv > p->max_lv || p->family) { + if (p->family) p->family = 0; //Family state broken. + int_party_check_lv(p); + } + mapif_party_memberadded(fd, party_id, member->account_id, member->char_id, 0); + mapif_party_info(-1, &p->party); + return 0; + } + } + mapif_party_memberadded(fd, party_id, member->account_id, member->char_id, 1); + + return 0; +} + +// パ?ティ?設定?更要求 +int mapif_parse_PartyChangeOption(int fd, int party_id, int account_id, int exp, int item) { + struct party_data *p; + int flag = 0; + + p = idb_get(party_db, party_id); + if (p == NULL) + return 0; + + p->party.exp = exp; + if (exp>0 && !party_check_exp_share(p)) { + flag |= 0x01; + p->party.exp = 0; + } + p->party.item = item&0x3; + mapif_party_optionchanged(fd, &p->party, account_id, flag); + return 0; +} + +// パ?ティ?退要求 +int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) { + struct party_data *p; + int i,lv; + + p = idb_get(party_db, party_id); + if (!p) return 0; + + for(i = 0; i < MAX_PARTY; i++) { + if(p->party.member[i].account_id == account_id && + p->party.member[i].char_id == char_id) + { + mapif_party_leaved(party_id, account_id, char_id); + lv = p->party.member[i].lv; + if(p->party.member[i].online) p->party.count--; + memset(&p->party.member[i], 0, sizeof(struct party_member)); + p->size--; + if (lv == p->min_lv || lv == p->max_lv || p->family) + { + if(p->family) p->family = 0; //Family state broken. + int_party_check_lv(p); + } + if (party_check_empty(&p->party) == 0) + mapif_party_info(-1, &p->party); + return 0; + } + } + return 0; +} + +int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id, unsigned short map, int online, unsigned int lv) +{ + struct party_data *p; + int i; + + p = idb_get(party_db, party_id); + if (p == NULL) + return 0; + + for(i = 0; i < MAX_PARTY; i++) { + if(p->party.member[i].account_id == account_id && + p->party.member[i].char_id == char_id) + { + p->party.member[i].map = map; + if (p->party.member[i].online != online) + { + p->party.member[i].online = online; + if (online) + p->party.count++; + else + p->party.count--; + // Even share check situations: Family state (always breaks) + // character logging on/off is max/min level (update level range) + // or character logging on/off has a different level (update level range using new level) + if (p->family || + (p->party.member[i].lv <= p->min_lv || p->party.member[i].lv >= p->max_lv) || + (p->party.member[i].lv != lv && (lv <= p->min_lv || lv >= p->max_lv)) + ) + { + p->party.member[i].lv = lv; + int_party_check_lv(p); + } + } + if (p->party.member[i].lv != lv) { + if(p->party.member[i].lv == p->min_lv || + p->party.member[i].lv == p->max_lv) + { + p->party.member[i].lv = lv; + int_party_check_lv(p); + } else + p->party.member[i].lv = lv; + } + mapif_party_membermoved(&p->party, i); + break; + } + } + return 0; +} + +// パ?ティ解散要求 +int mapif_parse_BreakParty(int fd, int party_id) { + + idb_remove(party_db, party_id); + mapif_party_broken(fd, party_id); + + return 0; +} + +// パ?ティメッセ?ジ送信 +int mapif_parse_PartyMessage(int fd, int party_id, int account_id, char *mes, int len) { + return mapif_party_message(party_id, account_id, mes, len, fd); +} +// パ?ティチェック要求 +int mapif_parse_PartyCheck(int fd, int party_id, int account_id, int char_id) { + return party_check_conflict(party_id, account_id, char_id); +} + +int mapif_parse_PartyLeaderChange(int fd,int party_id,int account_id,int char_id) +{ + struct party_data *p; + int i; + + p = idb_get(party_db, party_id); + if (p == NULL) + return 0; + + for (i = 0; i < MAX_PARTY; i++) + { + if(p->party.member[i].leader) + p->party.member[i].leader = 0; + if(p->party.member[i].account_id == account_id && + p->party.member[i].char_id == char_id) + p->party.member[i].leader = 1; + } + return 1; +} + +// map server からの通信 +// ?1パケットのみ解析すること +// ?パケット長デ?タはinter.cにセットしておくこと +// ?パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ?エラ?なら0(false)、そうでないなら1(true)をかえさなければならない +int inter_party_parse_frommap(int fd) { + RFIFOHEAD(fd); + switch(RFIFOW(fd,0)) { + case 0x3020: mapif_parse_CreateParty(fd, (char*)RFIFOP(fd,4), RFIFOB(fd,28), RFIFOB(fd,29), (struct party_member*)RFIFOP(fd,30)); break; + case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2)); break; + case 0x3022: mapif_parse_PartyAddMember(fd, RFIFOL(fd,4), (struct party_member*)RFIFOP(fd,8)); break; + case 0x3023: mapif_parse_PartyChangeOption(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOW(fd,10), RFIFOW(fd,12)); break; + case 0x3024: mapif_parse_PartyLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; + case 0x3025: mapif_parse_PartyChangeMap(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOW(fd,14), RFIFOB(fd,16), RFIFOW(fd,17)); break; + case 0x3026: mapif_parse_BreakParty(fd, RFIFOL(fd,2)); break; + case 0x3027: mapif_parse_PartyMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), (char*)RFIFOP(fd,12), RFIFOW(fd,2)-12); break; + case 0x3028: mapif_parse_PartyCheck(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; + case 0x3029: mapif_parse_PartyLeaderChange(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; + default: + return 0; + } + + return 1; +} + +// サ?バ?から?退要求(キャラ削除用) +int inter_party_leave(int party_id, int account_id, int char_id) { + return mapif_parse_PartyLeave(-1, party_id, account_id, char_id); +} +#endif //TXT_SQL_CONVERT diff --git a/src/char/int_party.h b/src/char/int_party.h index 5c2fbefb1..be3ae65f3 100644 --- a/src/char/int_party.h +++ b/src/char/int_party.h @@ -1,20 +1,20 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INT_PARTY_H_ -#define _INT_PARTY_H_ - -int inter_party_init(void); -void inter_party_final(void); -int inter_party_save(void); - -int inter_party_parse_frommap(int fd); - -int inter_party_leave(int party_id,int account_id, int char_id); -int inter_party_logged(int party_id, int account_id, int char_id); - -extern char party_txt[1024]; - -//For the TXT->SQL converter -int inter_party_fromstr(char *str, struct party *p); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INT_PARTY_H_ +#define _INT_PARTY_H_ + +int inter_party_init(void); +void inter_party_final(void); +int inter_party_save(void); + +int inter_party_parse_frommap(int fd); + +int inter_party_leave(int party_id,int account_id, int char_id); +int inter_party_logged(int party_id, int account_id, int char_id); + +extern char party_txt[1024]; + +//For the TXT->SQL converter +int inter_party_fromstr(char *str, struct party *p); +#endif diff --git a/src/char/int_pet.c b/src/char/int_pet.c index 3ab48c8ae..97fef3799 100644 --- a/src/char/int_pet.c +++ b/src/char/int_pet.c @@ -1,422 +1,422 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include - -#include "../common/mmo.h" -#include "../common/socket.h" -#include "../common/db.h" -#include "../common/lock.h" -#include "../common/showmsg.h" -#include "char.h" -#include "inter.h" -#include "int_pet.h" - -char pet_txt[1024]="save/pet.txt"; - -#ifndef TXT_SQL_CONVERT -static struct dbt *pet_db; -static int pet_newid = 100; - -int inter_pet_tostr(char *str,struct s_pet *p) -{ - int len; - - if(p->hungry < 0) - p->hungry = 0; - else if(p->hungry > 100) - p->hungry = 100; - if(p->intimate < 0) - p->intimate = 0; - else if(p->intimate > 1000) - p->intimate = 1000; - - len=sprintf(str,"%d,%d,%s\t%d,%d,%d,%d,%d,%d,%d,%d,%d", - p->pet_id,p->class_,p->name,p->account_id,p->char_id,p->level,p->egg_id, - p->equip,p->intimate,p->hungry,p->rename_flag,p->incuvate); - - return 0; -} -#endif //TXT_SQL_CONVERT -int inter_pet_fromstr(char *str,struct s_pet *p) -{ - int s; - int tmp_int[16]; - char tmp_str[256]; - - memset(p,0,sizeof(struct s_pet)); - -// printf("sscanf pet main info\n"); - s=sscanf(str,"%d,%d,%[^\t]\t%d,%d,%d,%d,%d,%d,%d,%d,%d",&tmp_int[0],&tmp_int[1],tmp_str,&tmp_int[2], - &tmp_int[3],&tmp_int[4],&tmp_int[5],&tmp_int[6],&tmp_int[7],&tmp_int[8],&tmp_int[9],&tmp_int[10]); - - if(s!=12) - return 1; - - p->pet_id = tmp_int[0]; - p->class_ = tmp_int[1]; - memcpy(p->name,tmp_str,NAME_LENGTH-1); - p->account_id = tmp_int[2]; - p->char_id = tmp_int[3]; - p->level = tmp_int[4]; - p->egg_id = tmp_int[5]; - p->equip = tmp_int[6]; - p->intimate = tmp_int[7]; - p->hungry = tmp_int[8]; - p->rename_flag = tmp_int[9]; - p->incuvate = tmp_int[10]; - - if(p->hungry < 0) - p->hungry = 0; - else if(p->hungry > 100) - p->hungry = 100; - if(p->intimate < 0) - p->intimate = 0; - else if(p->intimate > 1000) - p->intimate = 1000; - - return 0; -} -#ifndef TXT_SQL_CONVERT -int inter_pet_init() -{ - char line[8192]; - struct s_pet *p; - FILE *fp; - int c=0; - - pet_db= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - if( (fp=fopen(pet_txt,"r"))==NULL ) - return 1; - while(fgets(line,sizeof(line),fp)){ - p = (struct s_pet*)aCalloc(sizeof(struct s_pet), 1); - if(p==NULL){ - ShowFatalError("int_pet: out of memory!\n"); - exit(0); - } - memset(p,0,sizeof(struct s_pet)); - if(inter_pet_fromstr(line,p)==0 && p->pet_id>0){ - if( p->pet_id >= pet_newid) - pet_newid=p->pet_id+1; - idb_put(pet_db,p->pet_id,p); - }else{ - ShowError("int_pet: broken data [%s] line %d\n",pet_txt,c); - aFree(p); - } - c++; - } - fclose(fp); -// printf("int_pet: %s read done (%d pets)\n",pet_txt,c); - return 0; -} - -void inter_pet_final() -{ - pet_db->destroy(pet_db, NULL); - return; -} - -int inter_pet_save_sub(DBKey key,void *data,va_list ap) -{ - char line[8192]; - FILE *fp; - inter_pet_tostr(line,(struct s_pet *)data); - fp=va_arg(ap,FILE *); - fprintf(fp,"%s" RETCODE,line); - return 0; -} - -int inter_pet_save() -{ - FILE *fp; - int lock; - if( (fp=lock_fopen(pet_txt,&lock))==NULL ){ - ShowError("int_pet: cant write [%s] !!! data is lost !!!\n",pet_txt); - return 1; - } - pet_db->foreach(pet_db,inter_pet_save_sub,fp); - lock_fclose(fp,pet_txt,&lock); -// printf("int_pet: %s saved.\n",pet_txt); - return 0; -} - -int inter_pet_delete(int pet_id) -{ - struct s_pet *p; - p = idb_get(pet_db,pet_id); - if( p == NULL) - return 1; - else { - idb_remove(pet_db,pet_id); - ShowInfo("Deleted pet (pet_id: %d)\n",pet_id); - } - return 0; -} - -int mapif_pet_created(int fd,int account_id,struct s_pet *p) -{ - WFIFOHEAD(fd, 11); - WFIFOW(fd,0)=0x3880; - WFIFOL(fd,2)=account_id; - if(p!=NULL){ - WFIFOB(fd,6)=0; - WFIFOL(fd,7)=p->pet_id; - ShowInfo("Created pet (%d - %s)\n",p->pet_id,p->name); - }else{ - WFIFOB(fd,6)=1; - WFIFOL(fd,7)=0; - } - WFIFOSET(fd,11); - - return 0; -} - -int mapif_pet_info(int fd,int account_id,struct s_pet *p) -{ - WFIFOHEAD(fd, sizeof(struct s_pet) + 9); - WFIFOW(fd,0)=0x3881; - WFIFOW(fd,2)=sizeof(struct s_pet) + 9; - WFIFOL(fd,4)=account_id; - WFIFOB(fd,8)=0; - memcpy(WFIFOP(fd,9),p,sizeof(struct s_pet)); - WFIFOSET(fd,WFIFOW(fd,2)); - - return 0; -} - -int mapif_pet_noinfo(int fd,int account_id) -{ - WFIFOHEAD(fd, sizeof(struct s_pet) + 9); - WFIFOW(fd,0)=0x3881; - WFIFOW(fd,2)=sizeof(struct s_pet) + 9; - WFIFOL(fd,4)=account_id; - WFIFOB(fd,8)=1; - memset(WFIFOP(fd,9),0,sizeof(struct s_pet)); - WFIFOSET(fd,WFIFOW(fd,2)); - - return 0; -} - -int mapif_save_pet_ack(int fd,int account_id,int flag) -{ - WFIFOHEAD(fd, 7); - WFIFOW(fd,0)=0x3882; - WFIFOL(fd,2)=account_id; - WFIFOB(fd,6)=flag; - WFIFOSET(fd,7); - - return 0; -} - -int mapif_delete_pet_ack(int fd,int flag) -{ - WFIFOHEAD(fd, 3); - WFIFOW(fd,0)=0x3883; - WFIFOB(fd,2)=flag; - WFIFOSET(fd,3); - - return 0; -} - -int mapif_rename_pet_ack(int fd, int account_id, int char_id, int flag, char *name){ - WFIFOHEAD(fd, NAME_LENGTH+12); - WFIFOW(fd, 0) =0x3884; - WFIFOL(fd, 2) =account_id; - WFIFOL(fd, 6) =char_id; - WFIFOB(fd, 10) =flag; - memcpy(WFIFOP(fd, 11), name, NAME_LENGTH); - WFIFOSET(fd, NAME_LENGTH+12); - - return 0; -} - -int mapif_create_pet(int fd,int account_id,int char_id,short pet_class,short pet_lv,short pet_egg_id, - short pet_equip,short intimate,short hungry,char rename_flag,char incuvate,char *pet_name) -{ - struct s_pet *p; - p= (struct s_pet *) aCalloc(sizeof(struct s_pet), 1); - if(p==NULL){ - ShowFatalError("int_pet: out of memory !\n"); - mapif_pet_created(fd,account_id,NULL); - return 0; - } -// memset(p,0,sizeof(struct s_pet)); unnecessary after aCalloc [Skotlex] - p->pet_id = pet_newid++; - memcpy(p->name,pet_name,NAME_LENGTH-1); - if(incuvate == 1) - p->account_id = p->char_id = 0; - else { - p->account_id = account_id; - p->char_id = char_id; - } - p->class_ = pet_class; - p->level = pet_lv; - p->egg_id = pet_egg_id; - p->equip = pet_equip; - p->intimate = intimate; - p->hungry = hungry; - p->rename_flag = rename_flag; - p->incuvate = incuvate; - - if(p->hungry < 0) - p->hungry = 0; - else if(p->hungry > 100) - p->hungry = 100; - if(p->intimate < 0) - p->intimate = 0; - else if(p->intimate > 1000) - p->intimate = 1000; - - idb_put(pet_db,p->pet_id,p); - - mapif_pet_created(fd,account_id,p); - - return 0; -} - -int mapif_load_pet(int fd,int account_id,int char_id,int pet_id) -{ - struct s_pet *p; - p= idb_get(pet_db,pet_id); - if(p!=NULL) { - if(p->incuvate == 1) { - p->account_id = p->char_id = 0; - mapif_pet_info(fd,account_id,p); - } - else if(account_id == p->account_id && char_id == p->char_id) - mapif_pet_info(fd,account_id,p); - else - mapif_pet_noinfo(fd,account_id); - } - else - mapif_pet_noinfo(fd,account_id); - - return 0; -} - -static void* create_pet(DBKey key, va_list args) { - struct s_pet *p; - p=(struct s_pet *)aCalloc(sizeof(struct s_pet),1); - p->pet_id = key.i; - return p; -} -int mapif_save_pet(int fd,int account_id,struct s_pet *data) -{ - struct s_pet *p; - int pet_id, len; - RFIFOHEAD(fd); - len=RFIFOW(fd,2); - - if(sizeof(struct s_pet)!=len-8) { - ShowError("inter pet: data size error %d %d\n",sizeof(struct s_pet),len-8); - } - else{ - pet_id = data->pet_id; - if (pet_id == 0) - pet_id = data->pet_id = pet_newid++; - p= idb_ensure(pet_db,pet_id,create_pet); - if(data->hungry < 0) - data->hungry = 0; - else if(data->hungry > 100) - data->hungry = 100; - if(data->intimate < 0) - data->intimate = 0; - else if(data->intimate > 1000) - data->intimate = 1000; - memcpy(p,data,sizeof(struct s_pet)); - if(p->incuvate == 1) - p->account_id = p->char_id = 0; - - mapif_save_pet_ack(fd,account_id,0); - } - - return 0; -} - -int mapif_delete_pet(int fd,int pet_id) -{ - mapif_delete_pet_ack(fd,inter_pet_delete(pet_id)); - - return 0; -} - -int mapif_rename_pet(int fd, int account_id, int char_id, char *name){ - int i; - - // Check Authorised letters/symbols in the name of the pet - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) { - mapif_rename_pet_ack(fd, account_id, char_id, 0, name); - return 0; - } - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) { - mapif_rename_pet_ack(fd, account_id, char_id, 0, name); - return 0; - } - } - - mapif_rename_pet_ack(fd, account_id, char_id, 1, name); - return 0; -} - -int mapif_parse_CreatePet(int fd) -{ - RFIFOHEAD(fd); - mapif_create_pet(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOW(fd,10),RFIFOW(fd,12),RFIFOW(fd,14),RFIFOW(fd,16),RFIFOW(fd,18), - RFIFOW(fd,20),RFIFOB(fd,22),RFIFOB(fd,23),(char*)RFIFOP(fd,24)); - return 0; -} - -int mapif_parse_LoadPet(int fd) -{ - RFIFOHEAD(fd); - mapif_load_pet(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10)); - return 0; -} - -int mapif_parse_SavePet(int fd) -{ - RFIFOHEAD(fd); - mapif_save_pet(fd,RFIFOL(fd,4),(struct s_pet *)RFIFOP(fd,8)); - return 0; -} - -int mapif_parse_DeletePet(int fd) -{ - RFIFOHEAD(fd); - mapif_delete_pet(fd,RFIFOL(fd,2)); - return 0; -} - -int mapif_parse_RenamePet(int fd){ - RFIFOHEAD(fd); - mapif_rename_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOP(fd, 10)); - return 0; -} - -// map server からの通信 -// ・1パケットのみ解析すること -// ・パケット長データはinter.cにセットしておくこと -// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない -// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない -int inter_pet_parse_frommap(int fd) -{ - RFIFOHEAD(fd); - switch(RFIFOW(fd,0)){ - case 0x3080: mapif_parse_CreatePet(fd); break; - case 0x3081: mapif_parse_LoadPet(fd); break; - case 0x3082: mapif_parse_SavePet(fd); break; - case 0x3083: mapif_parse_DeletePet(fd); break; - case 0x3084: mapif_parse_RenamePet(fd); break; - default: - return 0; - } - return 1; -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include + +#include "../common/mmo.h" +#include "../common/socket.h" +#include "../common/db.h" +#include "../common/lock.h" +#include "../common/showmsg.h" +#include "char.h" +#include "inter.h" +#include "int_pet.h" + +char pet_txt[1024]="save/pet.txt"; + +#ifndef TXT_SQL_CONVERT +static struct dbt *pet_db; +static int pet_newid = 100; + +int inter_pet_tostr(char *str,struct s_pet *p) +{ + int len; + + if(p->hungry < 0) + p->hungry = 0; + else if(p->hungry > 100) + p->hungry = 100; + if(p->intimate < 0) + p->intimate = 0; + else if(p->intimate > 1000) + p->intimate = 1000; + + len=sprintf(str,"%d,%d,%s\t%d,%d,%d,%d,%d,%d,%d,%d,%d", + p->pet_id,p->class_,p->name,p->account_id,p->char_id,p->level,p->egg_id, + p->equip,p->intimate,p->hungry,p->rename_flag,p->incuvate); + + return 0; +} +#endif //TXT_SQL_CONVERT +int inter_pet_fromstr(char *str,struct s_pet *p) +{ + int s; + int tmp_int[16]; + char tmp_str[256]; + + memset(p,0,sizeof(struct s_pet)); + +// printf("sscanf pet main info\n"); + s=sscanf(str,"%d,%d,%[^\t]\t%d,%d,%d,%d,%d,%d,%d,%d,%d",&tmp_int[0],&tmp_int[1],tmp_str,&tmp_int[2], + &tmp_int[3],&tmp_int[4],&tmp_int[5],&tmp_int[6],&tmp_int[7],&tmp_int[8],&tmp_int[9],&tmp_int[10]); + + if(s!=12) + return 1; + + p->pet_id = tmp_int[0]; + p->class_ = tmp_int[1]; + memcpy(p->name,tmp_str,NAME_LENGTH-1); + p->account_id = tmp_int[2]; + p->char_id = tmp_int[3]; + p->level = tmp_int[4]; + p->egg_id = tmp_int[5]; + p->equip = tmp_int[6]; + p->intimate = tmp_int[7]; + p->hungry = tmp_int[8]; + p->rename_flag = tmp_int[9]; + p->incuvate = tmp_int[10]; + + if(p->hungry < 0) + p->hungry = 0; + else if(p->hungry > 100) + p->hungry = 100; + if(p->intimate < 0) + p->intimate = 0; + else if(p->intimate > 1000) + p->intimate = 1000; + + return 0; +} +#ifndef TXT_SQL_CONVERT +int inter_pet_init() +{ + char line[8192]; + struct s_pet *p; + FILE *fp; + int c=0; + + pet_db= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + if( (fp=fopen(pet_txt,"r"))==NULL ) + return 1; + while(fgets(line,sizeof(line),fp)){ + p = (struct s_pet*)aCalloc(sizeof(struct s_pet), 1); + if(p==NULL){ + ShowFatalError("int_pet: out of memory!\n"); + exit(0); + } + memset(p,0,sizeof(struct s_pet)); + if(inter_pet_fromstr(line,p)==0 && p->pet_id>0){ + if( p->pet_id >= pet_newid) + pet_newid=p->pet_id+1; + idb_put(pet_db,p->pet_id,p); + }else{ + ShowError("int_pet: broken data [%s] line %d\n",pet_txt,c); + aFree(p); + } + c++; + } + fclose(fp); +// printf("int_pet: %s read done (%d pets)\n",pet_txt,c); + return 0; +} + +void inter_pet_final() +{ + pet_db->destroy(pet_db, NULL); + return; +} + +int inter_pet_save_sub(DBKey key,void *data,va_list ap) +{ + char line[8192]; + FILE *fp; + inter_pet_tostr(line,(struct s_pet *)data); + fp=va_arg(ap,FILE *); + fprintf(fp,"%s" RETCODE,line); + return 0; +} + +int inter_pet_save() +{ + FILE *fp; + int lock; + if( (fp=lock_fopen(pet_txt,&lock))==NULL ){ + ShowError("int_pet: cant write [%s] !!! data is lost !!!\n",pet_txt); + return 1; + } + pet_db->foreach(pet_db,inter_pet_save_sub,fp); + lock_fclose(fp,pet_txt,&lock); +// printf("int_pet: %s saved.\n",pet_txt); + return 0; +} + +int inter_pet_delete(int pet_id) +{ + struct s_pet *p; + p = idb_get(pet_db,pet_id); + if( p == NULL) + return 1; + else { + idb_remove(pet_db,pet_id); + ShowInfo("Deleted pet (pet_id: %d)\n",pet_id); + } + return 0; +} + +int mapif_pet_created(int fd,int account_id,struct s_pet *p) +{ + WFIFOHEAD(fd, 11); + WFIFOW(fd,0)=0x3880; + WFIFOL(fd,2)=account_id; + if(p!=NULL){ + WFIFOB(fd,6)=0; + WFIFOL(fd,7)=p->pet_id; + ShowInfo("Created pet (%d - %s)\n",p->pet_id,p->name); + }else{ + WFIFOB(fd,6)=1; + WFIFOL(fd,7)=0; + } + WFIFOSET(fd,11); + + return 0; +} + +int mapif_pet_info(int fd,int account_id,struct s_pet *p) +{ + WFIFOHEAD(fd, sizeof(struct s_pet) + 9); + WFIFOW(fd,0)=0x3881; + WFIFOW(fd,2)=sizeof(struct s_pet) + 9; + WFIFOL(fd,4)=account_id; + WFIFOB(fd,8)=0; + memcpy(WFIFOP(fd,9),p,sizeof(struct s_pet)); + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +int mapif_pet_noinfo(int fd,int account_id) +{ + WFIFOHEAD(fd, sizeof(struct s_pet) + 9); + WFIFOW(fd,0)=0x3881; + WFIFOW(fd,2)=sizeof(struct s_pet) + 9; + WFIFOL(fd,4)=account_id; + WFIFOB(fd,8)=1; + memset(WFIFOP(fd,9),0,sizeof(struct s_pet)); + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +int mapif_save_pet_ack(int fd,int account_id,int flag) +{ + WFIFOHEAD(fd, 7); + WFIFOW(fd,0)=0x3882; + WFIFOL(fd,2)=account_id; + WFIFOB(fd,6)=flag; + WFIFOSET(fd,7); + + return 0; +} + +int mapif_delete_pet_ack(int fd,int flag) +{ + WFIFOHEAD(fd, 3); + WFIFOW(fd,0)=0x3883; + WFIFOB(fd,2)=flag; + WFIFOSET(fd,3); + + return 0; +} + +int mapif_rename_pet_ack(int fd, int account_id, int char_id, int flag, char *name){ + WFIFOHEAD(fd, NAME_LENGTH+12); + WFIFOW(fd, 0) =0x3884; + WFIFOL(fd, 2) =account_id; + WFIFOL(fd, 6) =char_id; + WFIFOB(fd, 10) =flag; + memcpy(WFIFOP(fd, 11), name, NAME_LENGTH); + WFIFOSET(fd, NAME_LENGTH+12); + + return 0; +} + +int mapif_create_pet(int fd,int account_id,int char_id,short pet_class,short pet_lv,short pet_egg_id, + short pet_equip,short intimate,short hungry,char rename_flag,char incuvate,char *pet_name) +{ + struct s_pet *p; + p= (struct s_pet *) aCalloc(sizeof(struct s_pet), 1); + if(p==NULL){ + ShowFatalError("int_pet: out of memory !\n"); + mapif_pet_created(fd,account_id,NULL); + return 0; + } +// memset(p,0,sizeof(struct s_pet)); unnecessary after aCalloc [Skotlex] + p->pet_id = pet_newid++; + memcpy(p->name,pet_name,NAME_LENGTH-1); + if(incuvate == 1) + p->account_id = p->char_id = 0; + else { + p->account_id = account_id; + p->char_id = char_id; + } + p->class_ = pet_class; + p->level = pet_lv; + p->egg_id = pet_egg_id; + p->equip = pet_equip; + p->intimate = intimate; + p->hungry = hungry; + p->rename_flag = rename_flag; + p->incuvate = incuvate; + + if(p->hungry < 0) + p->hungry = 0; + else if(p->hungry > 100) + p->hungry = 100; + if(p->intimate < 0) + p->intimate = 0; + else if(p->intimate > 1000) + p->intimate = 1000; + + idb_put(pet_db,p->pet_id,p); + + mapif_pet_created(fd,account_id,p); + + return 0; +} + +int mapif_load_pet(int fd,int account_id,int char_id,int pet_id) +{ + struct s_pet *p; + p= idb_get(pet_db,pet_id); + if(p!=NULL) { + if(p->incuvate == 1) { + p->account_id = p->char_id = 0; + mapif_pet_info(fd,account_id,p); + } + else if(account_id == p->account_id && char_id == p->char_id) + mapif_pet_info(fd,account_id,p); + else + mapif_pet_noinfo(fd,account_id); + } + else + mapif_pet_noinfo(fd,account_id); + + return 0; +} + +static void* create_pet(DBKey key, va_list args) { + struct s_pet *p; + p=(struct s_pet *)aCalloc(sizeof(struct s_pet),1); + p->pet_id = key.i; + return p; +} +int mapif_save_pet(int fd,int account_id,struct s_pet *data) +{ + struct s_pet *p; + int pet_id, len; + RFIFOHEAD(fd); + len=RFIFOW(fd,2); + + if(sizeof(struct s_pet)!=len-8) { + ShowError("inter pet: data size error %d %d\n",sizeof(struct s_pet),len-8); + } + else{ + pet_id = data->pet_id; + if (pet_id == 0) + pet_id = data->pet_id = pet_newid++; + p= idb_ensure(pet_db,pet_id,create_pet); + if(data->hungry < 0) + data->hungry = 0; + else if(data->hungry > 100) + data->hungry = 100; + if(data->intimate < 0) + data->intimate = 0; + else if(data->intimate > 1000) + data->intimate = 1000; + memcpy(p,data,sizeof(struct s_pet)); + if(p->incuvate == 1) + p->account_id = p->char_id = 0; + + mapif_save_pet_ack(fd,account_id,0); + } + + return 0; +} + +int mapif_delete_pet(int fd,int pet_id) +{ + mapif_delete_pet_ack(fd,inter_pet_delete(pet_id)); + + return 0; +} + +int mapif_rename_pet(int fd, int account_id, int char_id, char *name){ + int i; + + // Check Authorised letters/symbols in the name of the pet + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) == NULL) { + mapif_rename_pet_ack(fd, account_id, char_id, 0, name); + return 0; + } + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) != NULL) { + mapif_rename_pet_ack(fd, account_id, char_id, 0, name); + return 0; + } + } + + mapif_rename_pet_ack(fd, account_id, char_id, 1, name); + return 0; +} + +int mapif_parse_CreatePet(int fd) +{ + RFIFOHEAD(fd); + mapif_create_pet(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOW(fd,10),RFIFOW(fd,12),RFIFOW(fd,14),RFIFOW(fd,16),RFIFOW(fd,18), + RFIFOW(fd,20),RFIFOB(fd,22),RFIFOB(fd,23),(char*)RFIFOP(fd,24)); + return 0; +} + +int mapif_parse_LoadPet(int fd) +{ + RFIFOHEAD(fd); + mapif_load_pet(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10)); + return 0; +} + +int mapif_parse_SavePet(int fd) +{ + RFIFOHEAD(fd); + mapif_save_pet(fd,RFIFOL(fd,4),(struct s_pet *)RFIFOP(fd,8)); + return 0; +} + +int mapif_parse_DeletePet(int fd) +{ + RFIFOHEAD(fd); + mapif_delete_pet(fd,RFIFOL(fd,2)); + return 0; +} + +int mapif_parse_RenamePet(int fd){ + RFIFOHEAD(fd); + mapif_rename_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOP(fd, 10)); + return 0; +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_pet_parse_frommap(int fd) +{ + RFIFOHEAD(fd); + switch(RFIFOW(fd,0)){ + case 0x3080: mapif_parse_CreatePet(fd); break; + case 0x3081: mapif_parse_LoadPet(fd); break; + case 0x3082: mapif_parse_SavePet(fd); break; + case 0x3083: mapif_parse_DeletePet(fd); break; + case 0x3084: mapif_parse_RenamePet(fd); break; + default: + return 0; + } + return 1; +} +#endif //TXT_SQL_CONVERT diff --git a/src/char/int_pet.h b/src/char/int_pet.h index 31489579f..2c72628bb 100644 --- a/src/char/int_pet.h +++ b/src/char/int_pet.h @@ -1,18 +1,18 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INT_PET_H_ -#define _INT_PET_H_ - -int inter_pet_init(void); -void inter_pet_final(void); -int inter_pet_save(void); -int inter_pet_delete(int pet_id); - -int inter_pet_parse_frommap(int fd); - -extern char pet_txt[1024]; - -//Exported for use in the TXT-SQL converter. -int inter_pet_fromstr(char *str,struct s_pet *p); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INT_PET_H_ +#define _INT_PET_H_ + +int inter_pet_init(void); +void inter_pet_final(void); +int inter_pet_save(void); +int inter_pet_delete(int pet_id); + +int inter_pet_parse_frommap(int fd); + +extern char pet_txt[1024]; + +//Exported for use in the TXT-SQL converter. +int inter_pet_fromstr(char *str,struct s_pet *p); +#endif diff --git a/src/char/int_status.c b/src/char/int_status.c index 3c3b65dc4..7ece61ccd 100644 --- a/src/char/int_status.c +++ b/src/char/int_status.c @@ -1,187 +1,187 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include - -#include "int_status.h" -#include "../common/db.h" -#include "../common/lock.h" -#include "../common/malloc.h" -#include "../common/showmsg.h" - -static struct dbt * scdata_db = NULL; //Contains all the status change data in-memory. [Skotlex] -char scdata_txt[1024]="save/scdata.txt"; //By [Skotlex] - -#ifdef ENABLE_SC_SAVING -static void* create_scdata(DBKey key, va_list args) { - struct scdata *data; - data = aCalloc(1, sizeof(struct scdata)); - data->account_id = va_arg(args, int); - data->char_id = key.i; - return data; -} - -/*========================================== - * Loads status change data of the player given. [Skotlex] - *------------------------------------------ - */ -struct scdata* status_search_scdata(int aid, int cid) -{ - struct scdata *data; - data = scdata_db->ensure(scdata_db, i2key(cid), create_scdata, aid); - return data; -} - -/*========================================== - * Deletes status change data of the player given. [Skotlex] - * Should be invoked after the data of said player was successfully loaded. - *------------------------------------------ - */ -void status_delete_scdata(int aid, int cid) -{ - struct scdata *scdata = idb_remove(scdata_db, cid); - if (scdata) - { - if (scdata->data) - aFree(scdata->data); - aFree(scdata); - } -} - - -static void inter_status_tostr(char* line, struct scdata *sc_data) -{ - int i, len; - - len = sprintf(line, "%d,%d,%d\t", sc_data->account_id, sc_data->char_id, sc_data->count); - for(i = 0; i < sc_data->count; i++) { - len += sprintf(line + len, "%d,%d,%d,%d,%d,%d\t", sc_data->data[i].type, sc_data->data[i].tick, - sc_data->data[i].val1, sc_data->data[i].val2, sc_data->data[i].val3, sc_data->data[i].val4); - } - return; -} - -static int inter_scdata_fromstr(char *line, struct scdata *sc_data) -{ - int i, len, next; - - if (sscanf(line,"%d,%d,%d\t%n",&sc_data->account_id, &sc_data->char_id, &sc_data->count, &next) < 3) - return 0; - - if (sc_data->count < 1) - return 0; - - sc_data->data = aCalloc(sc_data->count, sizeof (struct status_change_data)); - - for (i = 0; i < sc_data->count; i++) - { - if (sscanf(line + next, "%hu,%d,%d,%d,%d,%d\t%n", &sc_data->data[i].type, &sc_data->data[i].tick, - &sc_data->data[i].val1, &sc_data->data[i].val2, &sc_data->data[i].val3, &sc_data->data[i].val4, &len) < 6) - { - aFree(sc_data->data); - return 0; - } - next+=len; - } - return 1; -} -/*========================================== - * Loads all scdata from the given filename. - *------------------------------------------ - */ -void status_load_scdata(const char* filename) -{ - FILE *fp; - int sd_count=0, sc_count=0; - char line[8192]; - struct scdata *sc; - - if ((fp = fopen(filename, "r")) == NULL) { - ShowError("status_load_scdata: Cannot open file %s!\n", filename); - return; - } - - while(fgets(line, sizeof(line) - 1, fp)) { - sc = (struct scdata*)aCalloc(1, sizeof(struct scdata)); - if (inter_scdata_fromstr(line, sc)) { - sd_count++; - sc_count+= sc->count; - sc = idb_put(scdata_db, sc->char_id, sc); - if (sc) { - ShowError("Duplicate entry in %s for character %d\n", filename, sc->char_id); - if (sc->data) aFree(sc->data); - aFree(sc); - } - } else { - ShowError("status_load_scdata: Broken line data: %s\n", line); - aFree(sc); - } - } - fclose(fp); - ShowStatus("Loaded %d saved status changes for %d characters.\n", sc_count, sd_count); -} - -static int inter_status_save_sub(DBKey key, void *data, va_list ap) { - char line[8192]; - struct scdata * sc_data; - FILE *fp; - - sc_data = (struct scdata *)data; - if (sc_data->count < 1) - return 0; - - fp = va_arg(ap, FILE *); - inter_status_tostr(line, sc_data); - fprintf(fp, "%s" RETCODE, line); - return 0; -} - -/*========================================== - * Saves all scdata to the given filename. - *------------------------------------------ - */ -void inter_status_save() -{ - FILE *fp; - int lock; - - if ((fp = lock_fopen(scdata_txt, &lock)) == NULL) { - ShowError("int_status: cant write [%s] !!! data is lost !!!\n", scdata_txt); - return; - } - scdata_db->foreach(scdata_db, inter_status_save_sub, fp); - lock_fclose(fp,scdata_txt, &lock); -} - -/*========================================== - * Initializes db. - *------------------------------------------ - */ -void status_init() -{ - scdata_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); - status_load_scdata(scdata_txt); -} - -/*========================================== - * Frees up memory. - *------------------------------------------ - */ -static int scdata_db_final(DBKey k,void *d,va_list ap) -{ - struct scdata *data = (struct scdata*)d; - if (data->data) - aFree(data->data); - aFree(data); - return 0; -} - -/*========================================== - * Final cleanup. - *------------------------------------------ - */ -void status_final(void) -{ - scdata_db->destroy(scdata_db, scdata_db_final); -} -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include + +#include "int_status.h" +#include "../common/db.h" +#include "../common/lock.h" +#include "../common/malloc.h" +#include "../common/showmsg.h" + +static struct dbt * scdata_db = NULL; //Contains all the status change data in-memory. [Skotlex] +char scdata_txt[1024]="save/scdata.txt"; //By [Skotlex] + +#ifdef ENABLE_SC_SAVING +static void* create_scdata(DBKey key, va_list args) { + struct scdata *data; + data = aCalloc(1, sizeof(struct scdata)); + data->account_id = va_arg(args, int); + data->char_id = key.i; + return data; +} + +/*========================================== + * Loads status change data of the player given. [Skotlex] + *------------------------------------------ + */ +struct scdata* status_search_scdata(int aid, int cid) +{ + struct scdata *data; + data = scdata_db->ensure(scdata_db, i2key(cid), create_scdata, aid); + return data; +} + +/*========================================== + * Deletes status change data of the player given. [Skotlex] + * Should be invoked after the data of said player was successfully loaded. + *------------------------------------------ + */ +void status_delete_scdata(int aid, int cid) +{ + struct scdata *scdata = idb_remove(scdata_db, cid); + if (scdata) + { + if (scdata->data) + aFree(scdata->data); + aFree(scdata); + } +} + + +static void inter_status_tostr(char* line, struct scdata *sc_data) +{ + int i, len; + + len = sprintf(line, "%d,%d,%d\t", sc_data->account_id, sc_data->char_id, sc_data->count); + for(i = 0; i < sc_data->count; i++) { + len += sprintf(line + len, "%d,%d,%d,%d,%d,%d\t", sc_data->data[i].type, sc_data->data[i].tick, + sc_data->data[i].val1, sc_data->data[i].val2, sc_data->data[i].val3, sc_data->data[i].val4); + } + return; +} + +static int inter_scdata_fromstr(char *line, struct scdata *sc_data) +{ + int i, len, next; + + if (sscanf(line,"%d,%d,%d\t%n",&sc_data->account_id, &sc_data->char_id, &sc_data->count, &next) < 3) + return 0; + + if (sc_data->count < 1) + return 0; + + sc_data->data = aCalloc(sc_data->count, sizeof (struct status_change_data)); + + for (i = 0; i < sc_data->count; i++) + { + if (sscanf(line + next, "%hu,%d,%d,%d,%d,%d\t%n", &sc_data->data[i].type, &sc_data->data[i].tick, + &sc_data->data[i].val1, &sc_data->data[i].val2, &sc_data->data[i].val3, &sc_data->data[i].val4, &len) < 6) + { + aFree(sc_data->data); + return 0; + } + next+=len; + } + return 1; +} +/*========================================== + * Loads all scdata from the given filename. + *------------------------------------------ + */ +void status_load_scdata(const char* filename) +{ + FILE *fp; + int sd_count=0, sc_count=0; + char line[8192]; + struct scdata *sc; + + if ((fp = fopen(filename, "r")) == NULL) { + ShowError("status_load_scdata: Cannot open file %s!\n", filename); + return; + } + + while(fgets(line, sizeof(line) - 1, fp)) { + sc = (struct scdata*)aCalloc(1, sizeof(struct scdata)); + if (inter_scdata_fromstr(line, sc)) { + sd_count++; + sc_count+= sc->count; + sc = idb_put(scdata_db, sc->char_id, sc); + if (sc) { + ShowError("Duplicate entry in %s for character %d\n", filename, sc->char_id); + if (sc->data) aFree(sc->data); + aFree(sc); + } + } else { + ShowError("status_load_scdata: Broken line data: %s\n", line); + aFree(sc); + } + } + fclose(fp); + ShowStatus("Loaded %d saved status changes for %d characters.\n", sc_count, sd_count); +} + +static int inter_status_save_sub(DBKey key, void *data, va_list ap) { + char line[8192]; + struct scdata * sc_data; + FILE *fp; + + sc_data = (struct scdata *)data; + if (sc_data->count < 1) + return 0; + + fp = va_arg(ap, FILE *); + inter_status_tostr(line, sc_data); + fprintf(fp, "%s" RETCODE, line); + return 0; +} + +/*========================================== + * Saves all scdata to the given filename. + *------------------------------------------ + */ +void inter_status_save() +{ + FILE *fp; + int lock; + + if ((fp = lock_fopen(scdata_txt, &lock)) == NULL) { + ShowError("int_status: cant write [%s] !!! data is lost !!!\n", scdata_txt); + return; + } + scdata_db->foreach(scdata_db, inter_status_save_sub, fp); + lock_fclose(fp,scdata_txt, &lock); +} + +/*========================================== + * Initializes db. + *------------------------------------------ + */ +void status_init() +{ + scdata_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); + status_load_scdata(scdata_txt); +} + +/*========================================== + * Frees up memory. + *------------------------------------------ + */ +static int scdata_db_final(DBKey k,void *d,va_list ap) +{ + struct scdata *data = (struct scdata*)d; + if (data->data) + aFree(data->data); + aFree(data); + return 0; +} + +/*========================================== + * Final cleanup. + *------------------------------------------ + */ +void status_final(void) +{ + scdata_db->destroy(scdata_db, scdata_db_final); +} +#endif diff --git a/src/char/int_status.h b/src/char/int_status.h index 0e22fb201..bc93b1024 100644 --- a/src/char/int_status.h +++ b/src/char/int_status.h @@ -1,24 +1,24 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef __INT_STATUS__ -#define __INT_STATUS__ - -#include "char.h" - -struct scdata { - int account_id, char_id; - int count; - struct status_change_data* data; -}; - -extern char scdata_txt[1024]; - -#ifdef ENABLE_SC_SAVING -struct scdata *status_search_scdata(int aid, int cid); -void status_delete_scdata(int aid, int cid); -void inter_status_save(void); -void status_init(void); -void status_final(void); -#endif -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef __INT_STATUS__ +#define __INT_STATUS__ + +#include "char.h" + +struct scdata { + int account_id, char_id; + int count; + struct status_change_data* data; +}; + +extern char scdata_txt[1024]; + +#ifdef ENABLE_SC_SAVING +struct scdata *status_search_scdata(int aid, int cid); +void status_delete_scdata(int aid, int cid); +void inter_status_save(void); +void status_init(void); +void status_final(void); +#endif +#endif diff --git a/src/char/int_storage.c b/src/char/int_storage.c index 89e9a540a..06b05ba25 100644 --- a/src/char/int_storage.c +++ b/src/char/int_storage.c @@ -1,478 +1,478 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include - -#include "../common/mmo.h" -#include "../common/socket.h" -#include "../common/db.h" -#include "../common/lock.h" -#include "../common/showmsg.h" -#include "char.h" -#include "inter.h" -#include "int_storage.h" -#include "int_pet.h" -#include "int_guild.h" - -// ファイル名のデフォルト -// inter_config_read()で再設定される -char storage_txt[1024]="save/storage.txt"; -char guild_storage_txt[1024]="save/g_storage.txt"; - -#ifndef TXT_SQL_CONVERT -static struct dbt *storage_db; -static struct dbt *guild_storage_db; - -// 倉庫データを文字列に変換 -int storage_tostr(char *str,struct storage *p) -{ - int i,j,f=0; - char *str_p = str; - str_p += sprintf(str_p,"%d,%d\t",p->account_id,p->storage_amount); - - for(i=0;istorage_[i].nameid) && (p->storage_[i].amount) ){ - str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d", - p->storage_[i].id,p->storage_[i].nameid,p->storage_[i].amount,p->storage_[i].equip, - p->storage_[i].identify,p->storage_[i].refine,p->storage_[i].attribute); - for(j=0; jstorage_[i].card[j]); - str_p += sprintf(str_p," "); - f++; - } - - *(str_p++)='\t'; - - *str_p='\0'; - if(!f) - str[0]=0; - return 0; -} -#endif //TXT_SQL_CONVERT -// 文字列を倉庫データに変換 -int storage_fromstr(char *str,struct storage *p) -{ - int tmp_int[256]; - char tmp_str[256]; - int set,next,len,i,j; - - set=sscanf(str,"%d,%d%n",&tmp_int[0],&tmp_int[1],&next); - p->storage_amount=tmp_int[1]; - - if(set!=2) - return 1; - if(str[next]=='\n' || str[next]=='\r') - return 0; - next++; - for(i=0;str[next] && str[next]!='\t' && i < MAX_STORAGE;i++){ - if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d%[0-9,-]%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str, &len) == 8) { - p->storage_[i].id = tmp_int[0]; - p->storage_[i].nameid = tmp_int[1]; - p->storage_[i].amount = tmp_int[2]; - p->storage_[i].equip = tmp_int[3]; - p->storage_[i].identify = tmp_int[4]; - p->storage_[i].refine = tmp_int[5]; - p->storage_[i].attribute = tmp_int[6]; - - for(j = 0; j < MAX_SLOTS && tmp_str && sscanf(tmp_str, ",%d%[0-9,-]",&tmp_int[0], tmp_str) > 0; j++) - p->storage_[i].card[j] = tmp_int[0]; - - next += len; - if (str[next] == ' ') - next++; - } - else return 1; - } - if (i >= MAX_STORAGE && str[next] && str[next]!='\t') - ShowWarning("storage_fromstr: Found a storage line with more items than MAX_STORAGE (%d), remaining items have been discarded!\n", MAX_STORAGE); - return 0; -} -#ifndef TXT_SQL_CONVERT -int guild_storage_tostr(char *str,struct guild_storage *p) -{ - int i,j,f=0; - char *str_p = str; - str_p+=sprintf(str,"%d,%d\t",p->guild_id,p->storage_amount); - - for(i=0;istorage_[i].nameid) && (p->storage_[i].amount) ){ - str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d", - p->storage_[i].id,p->storage_[i].nameid,p->storage_[i].amount,p->storage_[i].equip, - p->storage_[i].identify,p->storage_[i].refine,p->storage_[i].attribute); - for(j=0; jstorage_[i].card[j]); - str_p += sprintf(str_p," "); - f++; - } - - *(str_p++)='\t'; - - *str_p='\0'; - if(!f) - str[0]=0; - return 0; -} -#endif //TXT_SQL_CONVERT -int guild_storage_fromstr(char *str,struct guild_storage *p) -{ - int tmp_int[256]; - char tmp_str[256]; - int set,next,len,i,j; - - set=sscanf(str,"%d,%d%n",&tmp_int[0],&tmp_int[1],&next); - p->storage_amount=tmp_int[1]; - - if(set!=2) - return 1; - if(str[next]=='\n' || str[next]=='\r') - return 0; - next++; - for(i=0;str[next] && str[next]!='\t' && i < MAX_GUILD_STORAGE;i++){ - if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d%[0-9,-]%n", - &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], - &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str, &len) == 8) - { - p->storage_[i].id = tmp_int[0]; - p->storage_[i].nameid = tmp_int[1]; - p->storage_[i].amount = tmp_int[2]; - p->storage_[i].equip = tmp_int[3]; - p->storage_[i].identify = tmp_int[4]; - p->storage_[i].refine = tmp_int[5]; - p->storage_[i].attribute = tmp_int[6]; - for(j = 0; j < MAX_SLOTS && tmp_str && sscanf(tmp_str, ",%d%[0-9,-]",&tmp_int[0], tmp_str) > 0; j++) - p->storage_[i].card[j] = tmp_int[0]; - next += len; - if (str[next] == ' ') - next++; - } - else return 1; - } - if (i >= MAX_GUILD_STORAGE && str[next] && str[next]!='\t') - ShowWarning("guild_storage_fromstr: Found a storage line with more items than MAX_GUILD_STORAGE (%d), remaining items have been discarded!\n", MAX_GUILD_STORAGE); - return 0; -} -#ifndef TXT_SQL_CONVERT -static void* create_storage(DBKey key, va_list args) { - struct storage *s; - s = (struct storage *) aCalloc(sizeof(struct storage), 1); - s->account_id=key.i; - return s; -} - -// アカウントから倉庫データインデックスを得る(新規倉庫追加可能) -struct storage *account2storage(int account_id) -{ - struct storage *s; - s= idb_ensure(storage_db, account_id, create_storage); - return s; -} - -static void* create_guildstorage(DBKey key, va_list args) { - struct guild_storage *gs = NULL; - gs = (struct guild_storage *) aCalloc(sizeof(struct guild_storage), 1); - gs->guild_id=key.i; - return gs; -} - -struct guild_storage *guild2storage(int guild_id) -{ - struct guild_storage *gs = NULL; - if(inter_guild_search(guild_id) != NULL) - gs= idb_ensure(guild_storage_db, guild_id, create_guildstorage); - return gs; -} - -//--------------------------------------------------------- -// 倉庫データを読み込む -int inter_storage_init() -{ - char line[65536]; - int c=0,tmp_int; - struct storage *s; - struct guild_storage *gs; - FILE *fp; - - storage_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - fp=fopen(storage_txt,"r"); - if(fp==NULL){ - ShowError("cant't read : %s\n",storage_txt); - return 1; - } - while(fgets(line,65535,fp)){ - sscanf(line,"%d",&tmp_int); - s = (struct storage*)aCalloc(sizeof(struct storage), 1); - if(s==NULL){ - ShowFatalError("int_storage: out of memory!\n"); - exit(0); - } -// memset(s,0,sizeof(struct storage)); aCalloc does this... - s->account_id=tmp_int; - if(s->account_id > 0 && storage_fromstr(line,s) == 0) { - idb_put(storage_db,s->account_id,s); - } - else{ - ShowError("int_storage: broken data [%s] line %d\n",storage_txt,c); - aFree(s); - } - c++; - } - fclose(fp); - - c = 0; - guild_storage_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - fp=fopen(guild_storage_txt,"r"); - if(fp==NULL){ - ShowError("cant't read : %s\n",guild_storage_txt); - return 1; - } - while(fgets(line,65535,fp)){ - sscanf(line,"%d",&tmp_int); - gs = (struct guild_storage*)aCalloc(sizeof(struct guild_storage), 1); - if(gs==NULL){ - ShowFatalError("int_storage: out of memory!\n"); - exit(0); - } -// memset(gs,0,sizeof(struct guild_storage)); aCalloc... - gs->guild_id=tmp_int; - if(gs->guild_id > 0 && guild_storage_fromstr(line,gs) == 0) { - idb_put(guild_storage_db,gs->guild_id,gs); - } - else{ - ShowError("int_storage: broken data [%s] line %d\n",guild_storage_txt,c); - aFree(gs); - } - c++; - } - fclose(fp); - - return 0; -} - -void inter_storage_final() { - storage_db->destroy(storage_db, NULL); - guild_storage_db->destroy(guild_storage_db, NULL); - return; -} - -int inter_storage_save_sub(DBKey key,void *data,va_list ap) -{ - char line[65536]; - FILE *fp; - storage_tostr(line,(struct storage *)data); - fp=va_arg(ap,FILE *); - if(*line) - fprintf(fp,"%s" RETCODE,line); - return 0; -} -//--------------------------------------------------------- -// 倉庫データを書き込む -int inter_storage_save() -{ - FILE *fp; - int lock; - if( (fp=lock_fopen(storage_txt,&lock))==NULL ){ - ShowError("int_storage: cant write [%s] !!! data is lost !!!\n",storage_txt); - return 1; - } - storage_db->foreach(storage_db,inter_storage_save_sub,fp); - lock_fclose(fp,storage_txt,&lock); -// printf("int_storage: %s saved.\n",storage_txt); - return 0; -} - -int inter_guild_storage_save_sub(DBKey key,void *data,va_list ap) -{ - char line[65536]; - FILE *fp; - if(inter_guild_search(((struct guild_storage *)data)->guild_id) != NULL) { - guild_storage_tostr(line,(struct guild_storage *)data); - fp=va_arg(ap,FILE *); - if(*line) - fprintf(fp,"%s" RETCODE,line); - } - return 0; -} -//--------------------------------------------------------- -// 倉庫データを書き込む -int inter_guild_storage_save() -{ - FILE *fp; - int lock; - if( (fp=lock_fopen(guild_storage_txt,&lock))==NULL ){ - ShowError("int_storage: cant write [%s] !!! data is lost !!!\n",guild_storage_txt); - return 1; - } - guild_storage_db->foreach(guild_storage_db,inter_guild_storage_save_sub,fp); - lock_fclose(fp,guild_storage_txt,&lock); -// printf("int_storage: %s saved.\n",guild_storage_txt); - return 0; -} - -// 倉庫データ削除 -int inter_storage_delete(int account_id) -{ - struct storage *s = idb_get(storage_db,account_id); - if(s) { - int i; - for(i=0;istorage_amount;i++){ - if(s->storage_[i].card[0] == (short)0xff00) - inter_pet_delete( MakeDWord(s->storage_[i].card[1],s->storage_[i].card[2]) ); - } - idb_remove(storage_db,account_id); - } - return 0; -} - -// ギルド倉庫データ削除 -int inter_guild_storage_delete(int guild_id) -{ - struct guild_storage *gs = idb_get(guild_storage_db,guild_id); - if(gs) { - int i; - for(i=0;istorage_amount;i++){ - if(gs->storage_[i].card[0] == (short)0xff00) - inter_pet_delete( MakeDWord(gs->storage_[i].card[1],gs->storage_[i].card[2]) ); - } - idb_remove(guild_storage_db,guild_id); - } - return 0; -} - -//--------------------------------------------------------- -// map serverへの通信 - -// 倉庫データの送信 -int mapif_load_storage(int fd,int account_id) -{ - struct storage *s=account2storage(account_id); - WFIFOHEAD(fd, sizeof(struct storage)+8); - WFIFOW(fd,0)=0x3810; - WFIFOW(fd,2)=sizeof(struct storage)+8; - WFIFOL(fd,4)=account_id; - memcpy(WFIFOP(fd,8),s,sizeof(struct storage)); - WFIFOSET(fd,WFIFOW(fd,2)); - return 0; -} -// 倉庫データ保存完了送信 -int mapif_save_storage_ack(int fd,int account_id) -{ - WFIFOHEAD(fd, 7); - WFIFOW(fd,0)=0x3811; - WFIFOL(fd,2)=account_id; - WFIFOB(fd,6)=0; - WFIFOSET(fd,7); - return 0; -} - -int mapif_load_guild_storage(int fd,int account_id,int guild_id) -{ - struct guild_storage *gs=guild2storage(guild_id); - WFIFOHEAD(fd, sizeof(struct guild_storage)+12); - WFIFOW(fd,0)=0x3818; - if(gs) { - WFIFOW(fd,2)=sizeof(struct guild_storage)+12; - WFIFOL(fd,4)=account_id; - WFIFOL(fd,8)=guild_id; - memcpy(WFIFOP(fd,12),gs,sizeof(struct guild_storage)); - } - else { - WFIFOW(fd,2)=12; - WFIFOL(fd,4)=account_id; - WFIFOL(fd,8)=0; - } - WFIFOSET(fd,WFIFOW(fd,2)); - - return 0; -} -int mapif_save_guild_storage_ack(int fd,int account_id,int guild_id,int fail) -{ - WFIFOHEAD(fd, 11); - WFIFOW(fd,0)=0x3819; - WFIFOL(fd,2)=account_id; - WFIFOL(fd,6)=guild_id; - WFIFOB(fd,10)=fail; - WFIFOSET(fd,11); - return 0; -} - -//--------------------------------------------------------- -// map serverからの通信 - -// 倉庫データ要求受信 -int mapif_parse_LoadStorage(int fd) -{ - RFIFOHEAD(fd); - mapif_load_storage(fd,RFIFOL(fd,2)); - return 0; -} -// 倉庫データ受信&保存 -int mapif_parse_SaveStorage(int fd) -{ - struct storage *s; - int account_id, len; - RFIFOHEAD(fd); - account_id=RFIFOL(fd,4); - len=RFIFOW(fd,2); - if(sizeof(struct storage)!=len-8){ - ShowError("inter storage: data size error %d %d\n",sizeof(struct storage),len-8); - } - else { - s=account2storage(account_id); - memcpy(s,RFIFOP(fd,8),sizeof(struct storage)); - mapif_save_storage_ack(fd,account_id); - } - return 0; -} - -int mapif_parse_LoadGuildStorage(int fd) -{ - RFIFOHEAD(fd); - mapif_load_guild_storage(fd,RFIFOL(fd,2),RFIFOL(fd,6)); - return 0; -} -int mapif_parse_SaveGuildStorage(int fd) -{ - struct guild_storage *gs; - int guild_id, len; - RFIFOHEAD(fd); - guild_id=RFIFOL(fd,8); - len=RFIFOW(fd,2); - if(sizeof(struct guild_storage)!=len-12){ - ShowError("inter storage: data size error %d %d\n",sizeof(struct guild_storage),len-12); - } - else { - gs=guild2storage(guild_id); - if(gs) { - memcpy(gs,RFIFOP(fd,12),sizeof(struct guild_storage)); - mapif_save_guild_storage_ack(fd,RFIFOL(fd,4),guild_id,0); - } - else - mapif_save_guild_storage_ack(fd,RFIFOL(fd,4),guild_id,1); - } - return 0; -} - -// map server からの通信 -// ・1パケットのみ解析すること -// ・パケット長データはinter.cにセットしておくこと -// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない -// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない -int inter_storage_parse_frommap(int fd) -{ - RFIFOHEAD(fd); - switch(RFIFOW(fd,0)){ - case 0x3010: mapif_parse_LoadStorage(fd); break; - case 0x3011: mapif_parse_SaveStorage(fd); break; - case 0x3018: mapif_parse_LoadGuildStorage(fd); break; - case 0x3019: mapif_parse_SaveGuildStorage(fd); break; - default: - return 0; - } - return 1; -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include + +#include "../common/mmo.h" +#include "../common/socket.h" +#include "../common/db.h" +#include "../common/lock.h" +#include "../common/showmsg.h" +#include "char.h" +#include "inter.h" +#include "int_storage.h" +#include "int_pet.h" +#include "int_guild.h" + +// ファイル名のデフォルト +// inter_config_read()で再設定される +char storage_txt[1024]="save/storage.txt"; +char guild_storage_txt[1024]="save/g_storage.txt"; + +#ifndef TXT_SQL_CONVERT +static struct dbt *storage_db; +static struct dbt *guild_storage_db; + +// 倉庫データを文字列に変換 +int storage_tostr(char *str,struct storage *p) +{ + int i,j,f=0; + char *str_p = str; + str_p += sprintf(str_p,"%d,%d\t",p->account_id,p->storage_amount); + + for(i=0;istorage_[i].nameid) && (p->storage_[i].amount) ){ + str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d", + p->storage_[i].id,p->storage_[i].nameid,p->storage_[i].amount,p->storage_[i].equip, + p->storage_[i].identify,p->storage_[i].refine,p->storage_[i].attribute); + for(j=0; jstorage_[i].card[j]); + str_p += sprintf(str_p," "); + f++; + } + + *(str_p++)='\t'; + + *str_p='\0'; + if(!f) + str[0]=0; + return 0; +} +#endif //TXT_SQL_CONVERT +// 文字列を倉庫データに変換 +int storage_fromstr(char *str,struct storage *p) +{ + int tmp_int[256]; + char tmp_str[256]; + int set,next,len,i,j; + + set=sscanf(str,"%d,%d%n",&tmp_int[0],&tmp_int[1],&next); + p->storage_amount=tmp_int[1]; + + if(set!=2) + return 1; + if(str[next]=='\n' || str[next]=='\r') + return 0; + next++; + for(i=0;str[next] && str[next]!='\t' && i < MAX_STORAGE;i++){ + if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d%[0-9,-]%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str, &len) == 8) { + p->storage_[i].id = tmp_int[0]; + p->storage_[i].nameid = tmp_int[1]; + p->storage_[i].amount = tmp_int[2]; + p->storage_[i].equip = tmp_int[3]; + p->storage_[i].identify = tmp_int[4]; + p->storage_[i].refine = tmp_int[5]; + p->storage_[i].attribute = tmp_int[6]; + + for(j = 0; j < MAX_SLOTS && tmp_str && sscanf(tmp_str, ",%d%[0-9,-]",&tmp_int[0], tmp_str) > 0; j++) + p->storage_[i].card[j] = tmp_int[0]; + + next += len; + if (str[next] == ' ') + next++; + } + else return 1; + } + if (i >= MAX_STORAGE && str[next] && str[next]!='\t') + ShowWarning("storage_fromstr: Found a storage line with more items than MAX_STORAGE (%d), remaining items have been discarded!\n", MAX_STORAGE); + return 0; +} +#ifndef TXT_SQL_CONVERT +int guild_storage_tostr(char *str,struct guild_storage *p) +{ + int i,j,f=0; + char *str_p = str; + str_p+=sprintf(str,"%d,%d\t",p->guild_id,p->storage_amount); + + for(i=0;istorage_[i].nameid) && (p->storage_[i].amount) ){ + str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d", + p->storage_[i].id,p->storage_[i].nameid,p->storage_[i].amount,p->storage_[i].equip, + p->storage_[i].identify,p->storage_[i].refine,p->storage_[i].attribute); + for(j=0; jstorage_[i].card[j]); + str_p += sprintf(str_p," "); + f++; + } + + *(str_p++)='\t'; + + *str_p='\0'; + if(!f) + str[0]=0; + return 0; +} +#endif //TXT_SQL_CONVERT +int guild_storage_fromstr(char *str,struct guild_storage *p) +{ + int tmp_int[256]; + char tmp_str[256]; + int set,next,len,i,j; + + set=sscanf(str,"%d,%d%n",&tmp_int[0],&tmp_int[1],&next); + p->storage_amount=tmp_int[1]; + + if(set!=2) + return 1; + if(str[next]=='\n' || str[next]=='\r') + return 0; + next++; + for(i=0;str[next] && str[next]!='\t' && i < MAX_GUILD_STORAGE;i++){ + if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d%[0-9,-]%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str, &len) == 8) + { + p->storage_[i].id = tmp_int[0]; + p->storage_[i].nameid = tmp_int[1]; + p->storage_[i].amount = tmp_int[2]; + p->storage_[i].equip = tmp_int[3]; + p->storage_[i].identify = tmp_int[4]; + p->storage_[i].refine = tmp_int[5]; + p->storage_[i].attribute = tmp_int[6]; + for(j = 0; j < MAX_SLOTS && tmp_str && sscanf(tmp_str, ",%d%[0-9,-]",&tmp_int[0], tmp_str) > 0; j++) + p->storage_[i].card[j] = tmp_int[0]; + next += len; + if (str[next] == ' ') + next++; + } + else return 1; + } + if (i >= MAX_GUILD_STORAGE && str[next] && str[next]!='\t') + ShowWarning("guild_storage_fromstr: Found a storage line with more items than MAX_GUILD_STORAGE (%d), remaining items have been discarded!\n", MAX_GUILD_STORAGE); + return 0; +} +#ifndef TXT_SQL_CONVERT +static void* create_storage(DBKey key, va_list args) { + struct storage *s; + s = (struct storage *) aCalloc(sizeof(struct storage), 1); + s->account_id=key.i; + return s; +} + +// アカウントから倉庫データインデックスを得る(新規倉庫追加可能) +struct storage *account2storage(int account_id) +{ + struct storage *s; + s= idb_ensure(storage_db, account_id, create_storage); + return s; +} + +static void* create_guildstorage(DBKey key, va_list args) { + struct guild_storage *gs = NULL; + gs = (struct guild_storage *) aCalloc(sizeof(struct guild_storage), 1); + gs->guild_id=key.i; + return gs; +} + +struct guild_storage *guild2storage(int guild_id) +{ + struct guild_storage *gs = NULL; + if(inter_guild_search(guild_id) != NULL) + gs= idb_ensure(guild_storage_db, guild_id, create_guildstorage); + return gs; +} + +//--------------------------------------------------------- +// 倉庫データを読み込む +int inter_storage_init() +{ + char line[65536]; + int c=0,tmp_int; + struct storage *s; + struct guild_storage *gs; + FILE *fp; + + storage_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + fp=fopen(storage_txt,"r"); + if(fp==NULL){ + ShowError("cant't read : %s\n",storage_txt); + return 1; + } + while(fgets(line,65535,fp)){ + sscanf(line,"%d",&tmp_int); + s = (struct storage*)aCalloc(sizeof(struct storage), 1); + if(s==NULL){ + ShowFatalError("int_storage: out of memory!\n"); + exit(0); + } +// memset(s,0,sizeof(struct storage)); aCalloc does this... + s->account_id=tmp_int; + if(s->account_id > 0 && storage_fromstr(line,s) == 0) { + idb_put(storage_db,s->account_id,s); + } + else{ + ShowError("int_storage: broken data [%s] line %d\n",storage_txt,c); + aFree(s); + } + c++; + } + fclose(fp); + + c = 0; + guild_storage_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + fp=fopen(guild_storage_txt,"r"); + if(fp==NULL){ + ShowError("cant't read : %s\n",guild_storage_txt); + return 1; + } + while(fgets(line,65535,fp)){ + sscanf(line,"%d",&tmp_int); + gs = (struct guild_storage*)aCalloc(sizeof(struct guild_storage), 1); + if(gs==NULL){ + ShowFatalError("int_storage: out of memory!\n"); + exit(0); + } +// memset(gs,0,sizeof(struct guild_storage)); aCalloc... + gs->guild_id=tmp_int; + if(gs->guild_id > 0 && guild_storage_fromstr(line,gs) == 0) { + idb_put(guild_storage_db,gs->guild_id,gs); + } + else{ + ShowError("int_storage: broken data [%s] line %d\n",guild_storage_txt,c); + aFree(gs); + } + c++; + } + fclose(fp); + + return 0; +} + +void inter_storage_final() { + storage_db->destroy(storage_db, NULL); + guild_storage_db->destroy(guild_storage_db, NULL); + return; +} + +int inter_storage_save_sub(DBKey key,void *data,va_list ap) +{ + char line[65536]; + FILE *fp; + storage_tostr(line,(struct storage *)data); + fp=va_arg(ap,FILE *); + if(*line) + fprintf(fp,"%s" RETCODE,line); + return 0; +} +//--------------------------------------------------------- +// 倉庫データを書き込む +int inter_storage_save() +{ + FILE *fp; + int lock; + if( (fp=lock_fopen(storage_txt,&lock))==NULL ){ + ShowError("int_storage: cant write [%s] !!! data is lost !!!\n",storage_txt); + return 1; + } + storage_db->foreach(storage_db,inter_storage_save_sub,fp); + lock_fclose(fp,storage_txt,&lock); +// printf("int_storage: %s saved.\n",storage_txt); + return 0; +} + +int inter_guild_storage_save_sub(DBKey key,void *data,va_list ap) +{ + char line[65536]; + FILE *fp; + if(inter_guild_search(((struct guild_storage *)data)->guild_id) != NULL) { + guild_storage_tostr(line,(struct guild_storage *)data); + fp=va_arg(ap,FILE *); + if(*line) + fprintf(fp,"%s" RETCODE,line); + } + return 0; +} +//--------------------------------------------------------- +// 倉庫データを書き込む +int inter_guild_storage_save() +{ + FILE *fp; + int lock; + if( (fp=lock_fopen(guild_storage_txt,&lock))==NULL ){ + ShowError("int_storage: cant write [%s] !!! data is lost !!!\n",guild_storage_txt); + return 1; + } + guild_storage_db->foreach(guild_storage_db,inter_guild_storage_save_sub,fp); + lock_fclose(fp,guild_storage_txt,&lock); +// printf("int_storage: %s saved.\n",guild_storage_txt); + return 0; +} + +// 倉庫データ削除 +int inter_storage_delete(int account_id) +{ + struct storage *s = idb_get(storage_db,account_id); + if(s) { + int i; + for(i=0;istorage_amount;i++){ + if(s->storage_[i].card[0] == (short)0xff00) + inter_pet_delete( MakeDWord(s->storage_[i].card[1],s->storage_[i].card[2]) ); + } + idb_remove(storage_db,account_id); + } + return 0; +} + +// ギルド倉庫データ削除 +int inter_guild_storage_delete(int guild_id) +{ + struct guild_storage *gs = idb_get(guild_storage_db,guild_id); + if(gs) { + int i; + for(i=0;istorage_amount;i++){ + if(gs->storage_[i].card[0] == (short)0xff00) + inter_pet_delete( MakeDWord(gs->storage_[i].card[1],gs->storage_[i].card[2]) ); + } + idb_remove(guild_storage_db,guild_id); + } + return 0; +} + +//--------------------------------------------------------- +// map serverへの通信 + +// 倉庫データの送信 +int mapif_load_storage(int fd,int account_id) +{ + struct storage *s=account2storage(account_id); + WFIFOHEAD(fd, sizeof(struct storage)+8); + WFIFOW(fd,0)=0x3810; + WFIFOW(fd,2)=sizeof(struct storage)+8; + WFIFOL(fd,4)=account_id; + memcpy(WFIFOP(fd,8),s,sizeof(struct storage)); + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} +// 倉庫データ保存完了送信 +int mapif_save_storage_ack(int fd,int account_id) +{ + WFIFOHEAD(fd, 7); + WFIFOW(fd,0)=0x3811; + WFIFOL(fd,2)=account_id; + WFIFOB(fd,6)=0; + WFIFOSET(fd,7); + return 0; +} + +int mapif_load_guild_storage(int fd,int account_id,int guild_id) +{ + struct guild_storage *gs=guild2storage(guild_id); + WFIFOHEAD(fd, sizeof(struct guild_storage)+12); + WFIFOW(fd,0)=0x3818; + if(gs) { + WFIFOW(fd,2)=sizeof(struct guild_storage)+12; + WFIFOL(fd,4)=account_id; + WFIFOL(fd,8)=guild_id; + memcpy(WFIFOP(fd,12),gs,sizeof(struct guild_storage)); + } + else { + WFIFOW(fd,2)=12; + WFIFOL(fd,4)=account_id; + WFIFOL(fd,8)=0; + } + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} +int mapif_save_guild_storage_ack(int fd,int account_id,int guild_id,int fail) +{ + WFIFOHEAD(fd, 11); + WFIFOW(fd,0)=0x3819; + WFIFOL(fd,2)=account_id; + WFIFOL(fd,6)=guild_id; + WFIFOB(fd,10)=fail; + WFIFOSET(fd,11); + return 0; +} + +//--------------------------------------------------------- +// map serverからの通信 + +// 倉庫データ要求受信 +int mapif_parse_LoadStorage(int fd) +{ + RFIFOHEAD(fd); + mapif_load_storage(fd,RFIFOL(fd,2)); + return 0; +} +// 倉庫データ受信&保存 +int mapif_parse_SaveStorage(int fd) +{ + struct storage *s; + int account_id, len; + RFIFOHEAD(fd); + account_id=RFIFOL(fd,4); + len=RFIFOW(fd,2); + if(sizeof(struct storage)!=len-8){ + ShowError("inter storage: data size error %d %d\n",sizeof(struct storage),len-8); + } + else { + s=account2storage(account_id); + memcpy(s,RFIFOP(fd,8),sizeof(struct storage)); + mapif_save_storage_ack(fd,account_id); + } + return 0; +} + +int mapif_parse_LoadGuildStorage(int fd) +{ + RFIFOHEAD(fd); + mapif_load_guild_storage(fd,RFIFOL(fd,2),RFIFOL(fd,6)); + return 0; +} +int mapif_parse_SaveGuildStorage(int fd) +{ + struct guild_storage *gs; + int guild_id, len; + RFIFOHEAD(fd); + guild_id=RFIFOL(fd,8); + len=RFIFOW(fd,2); + if(sizeof(struct guild_storage)!=len-12){ + ShowError("inter storage: data size error %d %d\n",sizeof(struct guild_storage),len-12); + } + else { + gs=guild2storage(guild_id); + if(gs) { + memcpy(gs,RFIFOP(fd,12),sizeof(struct guild_storage)); + mapif_save_guild_storage_ack(fd,RFIFOL(fd,4),guild_id,0); + } + else + mapif_save_guild_storage_ack(fd,RFIFOL(fd,4),guild_id,1); + } + return 0; +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_storage_parse_frommap(int fd) +{ + RFIFOHEAD(fd); + switch(RFIFOW(fd,0)){ + case 0x3010: mapif_parse_LoadStorage(fd); break; + case 0x3011: mapif_parse_SaveStorage(fd); break; + case 0x3018: mapif_parse_LoadGuildStorage(fd); break; + case 0x3019: mapif_parse_SaveGuildStorage(fd); break; + default: + return 0; + } + return 1; +} +#endif //TXT_SQL_CONVERT diff --git a/src/char/int_storage.h b/src/char/int_storage.h index c15af003e..7007646f9 100644 --- a/src/char/int_storage.h +++ b/src/char/int_storage.h @@ -1,22 +1,22 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INT_STORAGE_H_ -#define _INT_STORAGE_H_ - -int inter_storage_init(void); -void inter_storage_final(void); -int inter_storage_save(void); -int inter_guild_storage_save(void); -int inter_storage_delete(int account_id); -int inter_guild_storage_delete(int guild_id); - -int inter_storage_parse_frommap(int fd); - -extern char storage_txt[1024]; -extern char guild_storage_txt[1024]; - -//Exported for use in the TXT-SQL converter. -int storage_fromstr(char *str,struct storage *p); -int guild_storage_fromstr(char *str,struct guild_storage *p); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INT_STORAGE_H_ +#define _INT_STORAGE_H_ + +int inter_storage_init(void); +void inter_storage_final(void); +int inter_storage_save(void); +int inter_guild_storage_save(void); +int inter_storage_delete(int account_id); +int inter_guild_storage_delete(int guild_id); + +int inter_storage_parse_frommap(int fd); + +extern char storage_txt[1024]; +extern char guild_storage_txt[1024]; + +//Exported for use in the TXT-SQL converter. +int storage_fromstr(char *str,struct storage *p); +int guild_storage_fromstr(char *str,struct guild_storage *p); +#endif diff --git a/src/char/inter.c b/src/char/inter.c index ab97c6512..fd46e0b33 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -1,692 +1,692 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include - -#include "../common/db.h" -#include "../common/mmo.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/malloc.h" -#include "../common/lock.h" -#include "../common/showmsg.h" - -#include "char.h" -#include "inter.h" -#include "int_party.h" -#include "int_guild.h" -#include "int_status.h" -#include "int_storage.h" -#include "int_pet.h" -#include "int_homun.h" - -#define WISDATA_TTL (60*1000) // Existence time of Wisp/page data (60 seconds) - // that is the waiting time of answers of all map-servers -#define WISDELLIST_MAX 256 // Number of elements of Wisp/page data deletion list - -char accreg_txt[1024] = "save/accreg.txt"; -#ifndef TXT_SQL_CONVERT -char inter_log_filename[1024] = "log/inter.log"; -char main_chat_nick[16] = "Main"; - -static struct dbt *accreg_db = NULL; - -unsigned int party_share_level = 10; - -// sending packet list -// NOTE: This variable ain't used at all! And it's confusing.. where do I add that the length of packet 0x2b07 is 10? x.x [Skotlex] -int inter_send_packet_length[]={ - -1,-1,27,-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3000-0x300f - -1, 7, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, - 35,-1,11,15, 34,29, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, - 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, - 9, 9,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 11,-1, 7, 3, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; -// recv. packet list -int inter_recv_packet_length[]={ - -1,-1, 7,-1, -1,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3000-0x300f - 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, //0x3010-0x301f - -1, 6,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, //0x3020-0x302f - -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 14,19,186,-1, //0x3030-0x303f - 5, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 48,14,-1, 6, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3080-0x308f - -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3090 - 0x309f Homunculus packets [albator] -}; - -struct WisData { - int id, fd, count, len; - unsigned long tick; - unsigned char src[24], dst[24], msg[1024]; -}; -static struct dbt * wis_db = NULL; -static int wis_dellist[WISDELLIST_MAX], wis_delnum; - - -//-------------------------------------------------------- - -// アカウント変数を文字列へ変換 -int inter_accreg_tostr(char *str, struct accreg *reg) { - int j; - char *p = str; - - p += sprintf(p, "%d\t", reg->account_id); - for(j = 0; j < reg->reg_num; j++) { - p += sprintf(p,"%s,%s ", reg->reg[j].str, reg->reg[j].value); - } - - return 0; -} -#endif //TXT_SQL_CONVERT -// アカウント変数を文字列から変換 -int inter_accreg_fromstr(const char *str, struct accreg *reg) { - int j, n; - const char *p = str; - - if (sscanf(p, "%d\t%n", ®->account_id, &n ) != 1 || reg->account_id <= 0) - return 1; - - for(j = 0, p += n; j < ACCOUNT_REG_NUM; j++, p += n) { - if (sscanf(p, "%[^,],%[^ ] %n", reg->reg[j].str, reg->reg[j].value, &n) != 2) - break; - } - reg->reg_num = j; - - return 0; -} -#ifndef TXT_SQL_CONVERT -// アカウント変数の読み込み -int inter_accreg_init(void) { - char line[8192]; - FILE *fp; - int c = 0; - struct accreg *reg; - - accreg_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - if( (fp = fopen(accreg_txt, "r")) == NULL) - return 1; - while(fgets(line, sizeof(line)-1, fp)){ - line[sizeof(line)-1] = '\0'; - - reg = (struct accreg*)aCalloc(sizeof(struct accreg), 1); - if (reg == NULL) { - ShowFatalError("inter: accreg: out of memory!\n"); - exit(0); - } - if (inter_accreg_fromstr(line, reg) == 0 && reg->account_id > 0) { - idb_put(accreg_db, reg->account_id, reg); - } else { - ShowError("inter: accreg: broken data [%s] line %d\n", accreg_txt, c); - aFree(reg); - } - c++; - } - fclose(fp); -// printf("inter: %s read done (%d)\n", accreg_txt, c); - - return 0; -} - -// アカウント変数のセーブ用 -int inter_accreg_save_sub(DBKey key, void *data, va_list ap) { - char line[8192]; - FILE *fp; - struct accreg *reg = (struct accreg *)data; - - if (reg->reg_num > 0) { - inter_accreg_tostr(line,reg); - fp = va_arg(ap, FILE *); - fprintf(fp, "%s" RETCODE, line); - } - - return 0; -} - -// アカウント変数のセーブ -int inter_accreg_save(void) { - FILE *fp; - int lock; - - if ((fp = lock_fopen(accreg_txt,&lock)) == NULL) { - ShowError("int_accreg: cant write [%s] !!! data is lost !!!\n", accreg_txt); - return 1; - } - accreg_db->foreach(accreg_db, inter_accreg_save_sub,fp); - lock_fclose(fp, accreg_txt, &lock); -// printf("inter: %s saved.\n", accreg_txt); - - return 0; -} - -//-------------------------------------------------------- -#endif //TXT_SQL_CONVERT -/*========================================== - * 設定ファイルを読み込む - *------------------------------------------ - */ -static int inter_config_read(const char *cfgName) { - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - fp = fopen(cfgName, "r"); - if (fp == NULL) { - ShowError("file not found: %s\n", cfgName); - return 1; - } - while(fgets(line, sizeof(line) - 1, fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - line[sizeof(line)-1] = '\0'; - - if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2) - continue; - - if (strcmpi(w1, "storage_txt") == 0) { - strncpy(storage_txt, w2, sizeof(storage_txt)); - } else if (strcmpi(w1, "party_txt") == 0) { - strncpy(party_txt, w2, sizeof(party_txt)); - } else if (strcmpi(w1, "pet_txt") == 0) { - strncpy(pet_txt, w2, sizeof(pet_txt)); - } else if (strcmpi(w1, "accreg_txt") == 0) { - strncpy(accreg_txt, w2, sizeof(accreg_txt)); - } else if (strcmpi(w1, "guild_txt") == 0) { - strncpy(guild_txt, w2, sizeof(guild_txt)); - } else if (strcmpi(w1, "castle_txt") == 0) { - strncpy(castle_txt, w2, sizeof(castle_txt)); - } else if (strcmpi(w1, "guild_storage_txt") == 0) { - strncpy(guild_storage_txt, w2, sizeof(guild_storage_txt)); -#ifndef TXT_SQL_CONVERT - } else if (strcmpi(w1, "homun_txt") == 0) { - strncpy(homun_txt, w2, sizeof(homun_txt)); - } else if (strcmpi(w1, "party_share_level") == 0) { - party_share_level = (unsigned int)atof(w2); - } else if (strcmpi(w1, "inter_log_filename") == 0) { - strncpy(inter_log_filename, w2, sizeof(inter_log_filename)); - } else if(strcmpi(w1,"log_inter")==0) { - log_inter = atoi(w2); - } else if(strcmpi(w1, "main_chat_nick")==0){ // Main chat nick [LuzZza] - strcpy(main_chat_nick, w2); -#endif //TXT_SQL_CONVERT - } else if (strcmpi(w1, "import") == 0) { - inter_config_read(w2); - } - } - fclose(fp); - - return 0; -} -#ifndef TXT_SQL_CONVERT -// ログ書き出し -int inter_log(char *fmt,...) { - FILE *logfp; - va_list ap; - - va_start(ap,fmt); - logfp = fopen(inter_log_filename, "a"); - if (logfp) { - vfprintf(logfp, fmt, ap); - fclose(logfp); - } - va_end(ap); - - return 0; -} - -// セーブ -int inter_save(void) { -#ifdef ENABLE_SC_SAVING - inter_status_save(); -#endif - inter_party_save(); - inter_guild_save(); - inter_storage_save(); - inter_guild_storage_save(); - inter_pet_save(); - inter_homun_save(); - inter_accreg_save(); - - return 0; -} -#endif //TXT_SQL_CONVERT -// 初期化 -int inter_init_txt(const char *file) { - inter_config_read(file); - -#ifndef TXT_SQL_CONVERT - wis_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - inter_party_init(); - inter_guild_init(); - inter_storage_init(); - inter_pet_init(); - inter_homun_init(); - inter_accreg_init(); -#endif //TXT_SQL_CONVERT - return 0; -} -#ifndef TXT_SQL_CONVERT -// finalize -void inter_final(void) { - accreg_db->destroy(accreg_db, NULL); - wis_db->destroy(wis_db, NULL); - - inter_party_final(); - inter_guild_final(); - inter_storage_final(); - inter_pet_final(); - inter_homun_final(); - - return; -} - -// マップサーバー接続 -int inter_mapif_init(int fd) { - inter_guild_mapif_init(fd); - - return 0; -} - -//-------------------------------------------------------- -// sended packets to map-server - -//Sends to map server the current max Account/Char id [Skotlex] -void mapif_send_maxid(int account_id, int char_id) -{ - unsigned char buf[12]; - - WBUFW(buf,0) = 0x2b07; - WBUFL(buf,2) = account_id; - WBUFL(buf,6) = char_id; - mapif_sendall(buf, 10); -} - -// GMメッセージ送信 -int mapif_GMmessage(unsigned char *mes, int len, unsigned long color, int sfd) { - unsigned char buf[2048]; - - if (len > 2048) len = 2047; //Make it fit to avoid crashes. [Skotlex] - WBUFW(buf,0) = 0x3800; - WBUFW(buf,2) = len; - WBUFL(buf,4) = color; - memcpy(WBUFP(buf,8), mes, len - 8); - mapif_sendallwos(sfd, buf, len); -// printf("inter server: GM:%d %s\n", len, mes); - - return 0; -} - -// Wisp/page transmission to one map-server -int mapif_wis_message2(struct WisData *wd, int fd) { - WFIFOHEAD(fd, 56+wd->len); - WFIFOW(fd, 0) = 0x3801; - WFIFOW(fd, 2) = 56 + wd->len; - WFIFOL(fd, 4) = wd->id; - memcpy(WFIFOP(fd, 8), wd->src, NAME_LENGTH); - memcpy(WFIFOP(fd,32), wd->dst, NAME_LENGTH); - memcpy(WFIFOP(fd,56), wd->msg, wd->len); - wd->count = 1; - WFIFOSET(fd,WFIFOW(fd,2)); - return 1; -} - -// Wisp/page transmission to all map-server -int mapif_wis_message(struct WisData *wd) { - unsigned char buf[2048]; - if (wd->len > 2047-56) wd->len = 2047-56; //Force it to fit to avoid crashes. [Skotlex] - - WBUFW(buf, 0) = 0x3801; - WBUFW(buf, 2) = 56 + wd->len; - WBUFL(buf, 4) = wd->id; - memcpy(WBUFP(buf, 8), wd->src, NAME_LENGTH); - memcpy(WBUFP(buf,32), wd->dst, NAME_LENGTH); - memcpy(WBUFP(buf,56), wd->msg, wd->len); - wd->count = mapif_sendall(buf, WBUFW(buf,2)); - - return 0; -} - -int mapif_wis_fail(int fd, char *src) { - unsigned char buf[27]; - WBUFW(buf, 0) = 0x3802; - memcpy(WBUFP(buf, 2), src, NAME_LENGTH); - WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - mapif_send(fd, buf, 27); - return 0; - -} - -// Wisp/page transmission result to map-server -int mapif_wis_end(struct WisData *wd, int flag) { - unsigned char buf[27]; - - WBUFW(buf, 0) = 0x3802; - memcpy(WBUFP(buf, 2), wd->src, 24); - WBUFB(buf,26) = flag; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - mapif_send(wd->fd, buf, 27); -// printf("inter server wis_end: flag: %d\n", flag); - - return 0; -} - -// アカウント変数送信 -int mapif_account_reg(int fd, unsigned char *src) { - unsigned char *buf = aCalloc(1,WBUFW(src,2)); - - memcpy(WBUFP(buf,0),src,WBUFW(src,2)); - WBUFW(buf, 0) = 0x3804; - mapif_sendallwos(fd, buf, WBUFW(buf,2)); - - aFree(buf); - - return 0; -} - -// アカウント変数要求返信 -int mapif_account_reg_reply(int fd,int account_id, int char_id) { - struct accreg *reg = idb_get(accreg_db,account_id); - - WFIFOHEAD(fd, ACCOUNT_REG_NUM * 288+ 13); - WFIFOW(fd,0) = 0x3804; - WFIFOL(fd,4) = account_id; - WFIFOL(fd,8) = char_id; - WFIFOB(fd,12) = 2; //Acc Reg - if (reg == NULL) { - WFIFOW(fd,2) = 13; - } else { - int i, p; - for (p=13,i = 0; i < reg->reg_num; i++) { - p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].str)+1; //We add 1 to consider the '\0' in place. - p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].value)+1; - } - WFIFOW(fd,2)=p; - } - WFIFOSET(fd,WFIFOW(fd,2)); - return 0; -} - -//Request to kick char from a certain map server. [Skotlex] -int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason) -{ - if (fd < 0) - return -1; - - WFIFOHEAD(fd, 7); - WFIFOW(fd,0) = 0x2b1f; - WFIFOL(fd,2) = account_id; - WFIFOB(fd,6) = reason; - WFIFOSET(fd,7); - - return 0; -} - -//-------------------------------------------------------- - -// Existence check of WISP data -int check_ttl_wisdata_sub(DBKey key, void *data, va_list ap) { - unsigned long tick; - struct WisData *wd = (struct WisData *)data; - tick = va_arg(ap, unsigned long); - - if (DIFF_TICK(tick, wd->tick) > WISDATA_TTL && wis_delnum < WISDELLIST_MAX) - wis_dellist[wis_delnum++] = wd->id; - - return 0; -} - -int check_ttl_wisdata(void) { - unsigned long tick = gettick(); - int i; - - do { - wis_delnum = 0; - wis_db->foreach(wis_db, check_ttl_wisdata_sub, tick); - for(i = 0; i < wis_delnum; i++) { - struct WisData *wd = idb_get(wis_db, wis_dellist[i]); - ShowWarning("inter: wis data id=%d time out : from %s to %s\n", wd->id, wd->src, wd->dst); - // removed. not send information after a timeout. Just no answer for the player - //mapif_wis_end(wd, 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - idb_remove(wis_db, wd->id); - } - } while(wis_delnum >= WISDELLIST_MAX); - - return 0; -} - -//-------------------------------------------------------- -// received packets from map-server - -// GMメッセージ送信 -int mapif_parse_GMmessage(int fd) { - RFIFOHEAD(fd); - mapif_GMmessage(RFIFOP(fd,8), RFIFOW(fd,2), RFIFOL(fd,4), fd); - - return 0; -} - -static struct WisData* mapif_create_whisper(int fd, char* src, char* dst, char* mes, int meslen) -{ - static int wisid = 0; - struct WisData* wd = (struct WisData *)aCalloc(sizeof(struct WisData), 1); - if (wd == NULL){ - ShowFatalError("inter: WisRequest: out of memory !\n"); - return NULL; - } - wd->id = ++wisid; - wd->fd = fd; - wd->len= meslen; - memcpy(wd->src, src, NAME_LENGTH); - memcpy(wd->dst, dst, NAME_LENGTH); - memcpy(wd->msg, mes, meslen); - wd->tick = gettick(); - return wd; -} - -// Wisp/page request to send -int mapif_parse_WisRequest(int fd) { - struct mmo_charstatus* char_status; - struct WisData* wd; - char name[NAME_LENGTH]; - int fd2; - - RFIFOHEAD(fd); - - if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) { - ShowWarning("inter: Wis message size too long.\n"); - return 0; - } else if (RFIFOW(fd,2)-52 <= 0) { // normaly, impossible, but who knows... - ShowError("inter: Wis message doesn't exist.\n"); - return 0; - } - - memcpy(name, RFIFOP(fd,28), NAME_LENGTH); //Received name may be too large and not contain \0! [Skotlex] - name[NAME_LENGTH-1]= '\0'; - // search if character exists before to ask all map-servers - char_status = search_character_byname(name); - if (char_status == NULL) - return mapif_wis_fail(fd, RFIFOP(fd, 4)); - - // Character exists. - // to be sure of the correct name, rewrite it - memset(name, 0, NAME_LENGTH); - strncpy(name, char_status->name, NAME_LENGTH); - // if source is destination, don't ask other servers. - if (strcmp((char*)RFIFOP(fd,4),name) == 0) - return mapif_wis_fail(fd, RFIFOP(fd, 4)); - - //Look for online character. - fd2 = search_character_online(char_status->account_id, char_status->char_id); - if (fd2 >= 0) { //Character online, send whisper. - wd = mapif_create_whisper(fd, RFIFOP(fd, 4), RFIFOP(fd,28), RFIFOP(fd,52), RFIFOW(fd,2)-52); - if (!wd) return 1; - idb_put(wis_db, wd->id, wd); - mapif_wis_message2(wd, fd2); - return 0; - } - //Not found. - return mapif_wis_fail(fd, RFIFOP(fd, 4)); - -/* Scrapped since now we know where characters are online in. [Skotlex] - wd = mapif_create_whisper(fd, RFIFOP(fd, 4), RFIFOP(fd,28), RFIFOP(fd,52), RFIFOW(fd,2)-52); - if (!wd) return 0; - idb_put(wis_db, wd->id, wd); - mapif_wis_message(wd); - return 0; -*/ -} - -// Wisp/page transmission result -int mapif_parse_WisReply(int fd) { - int id, flag; - struct WisData *wd; - RFIFOHEAD(fd); - id = RFIFOL(fd,2); - flag = RFIFOB(fd,6); - wd = idb_get(wis_db, id); - - if (wd == NULL) - return 0; // This wisp was probably suppress before, because it was timeout or because of target was found on another map-server - - if ((--wd->count) <= 0 || flag != 1) { - mapif_wis_end(wd, flag); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - idb_remove(wis_db, id); - } - - return 0; -} - -// Received wisp message from map-server for ALL gm (just copy the message and resends it to ALL map-servers) -int mapif_parse_WisToGM(int fd) { - unsigned char buf[2048]; // 0x3003/0x3803 .w .24B .w .?B - RFIFOHEAD(fd); - memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2)); - WBUFW(buf, 0) = 0x3803; - mapif_sendall(buf, RFIFOW(fd,2)); - - return 0; -} - -static void* create_accreg(DBKey key, va_list args) { - struct accreg *reg; - reg = (struct accreg*)aCalloc(sizeof(struct accreg), 1); - reg->account_id = key.i; - return reg; -} - -// アカウント変数保存要求 -int mapif_parse_Registry(int fd) { - int j, p, len; - struct accreg *reg; - RFIFOHEAD(fd); - - switch (RFIFOB(fd,12)) { - case 3: //Character registry - return char_parse_Registry(RFIFOL(fd,4), RFIFOL(fd,8), RFIFOP(fd,13), RFIFOW(fd,2)-13); - case 2: //Acc Reg - break; - case 1: //Acc Reg2, forward to login - return save_accreg2(RFIFOP(fd,4), RFIFOW(fd,2)-4); - default: //Error? - return 1; - } - reg = idb_ensure(accreg_db, RFIFOL(fd,4), create_accreg); - - for(j=0,p=13;jreg[j].str,&len); - reg->reg[j].str[len]='\0'; - p +=len+1; //+1 to skip the '\0' between strings. - sscanf(RFIFOP(fd,p), "%255c%n",reg->reg[j].value,&len); - reg->reg[j].value[len]='\0'; - p +=len+1; - } - reg->reg_num=j; - mapif_account_reg(fd, RFIFOP(fd,0)); // 他のMAPサーバーに送信 - - return 0; -} - -// Request the value of all registries. -int mapif_parse_RegistryRequest(int fd) -{ - RFIFOHEAD(fd); - //Load Char Registry - if (RFIFOB(fd,12)) - char_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6)); - //Load Account Registry - if (RFIFOB(fd,11)) - mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6)); - //Ask Login Server for Account2 values. - if (RFIFOB(fd,10)) - request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6)-2); - return 1; -} - -//-------------------------------------------------------- - -// map server からの通信(1パケットのみ解析すること) -// エラーなら0(false)、処理できたなら1、 -// パケット長が足りなければ2をかえさなければならない -int inter_parse_frommap(int fd) { - int cmd, len; - RFIFOHEAD(fd); - cmd = RFIFOW(fd,0); - len = 0; - - // inter鯖管轄かを調べる - if (cmd < 0x3000 || cmd >= 0x3000 + (sizeof(inter_recv_packet_length) / sizeof(inter_recv_packet_length[0]))) - return 0; - - if (inter_recv_packet_length[cmd-0x3000] == 0) //This is necessary, because otherwise we return 2 and the char server will just hang waiting for packets! [Skotlex] - return 0; - - // パケット長を調べる - if ((len = inter_check_length(fd, inter_recv_packet_length[cmd - 0x3000])) == 0) - return 2; - - switch(cmd) { - case 0x3000: mapif_parse_GMmessage(fd); break; - case 0x3001: mapif_parse_WisRequest(fd); break; - case 0x3002: mapif_parse_WisReply(fd); break; - case 0x3003: mapif_parse_WisToGM(fd); break; - case 0x3004: mapif_parse_Registry(fd); break; - case 0x3005: mapif_parse_RegistryRequest(fd); break; - default: - if (inter_party_parse_frommap(fd)) - break; - if (inter_guild_parse_frommap(fd)) - break; - if (inter_storage_parse_frommap(fd)) - break; - if (inter_pet_parse_frommap(fd)) - break; - if (inter_homun_parse_frommap(fd)) - break; - return 0; - } - RFIFOSKIP(fd, len); - return 1; -} - -// RFIFOのパケット長確認 -// 必要パケット長があればパケット長、まだ足りなければ0 -int inter_check_length(int fd, int length) { - if (length == -1) { // 可変パケット長 - RFIFOHEAD(fd); - if (RFIFOREST(fd) < 4) // パケット長が未着 - return 0; - length = RFIFOW(fd,2); - } - - if ((int)RFIFOREST(fd) < length) // パケットが未着 - return 0; - - return length; -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include + +#include "../common/db.h" +#include "../common/mmo.h" +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/malloc.h" +#include "../common/lock.h" +#include "../common/showmsg.h" + +#include "char.h" +#include "inter.h" +#include "int_party.h" +#include "int_guild.h" +#include "int_status.h" +#include "int_storage.h" +#include "int_pet.h" +#include "int_homun.h" + +#define WISDATA_TTL (60*1000) // Existence time of Wisp/page data (60 seconds) + // that is the waiting time of answers of all map-servers +#define WISDELLIST_MAX 256 // Number of elements of Wisp/page data deletion list + +char accreg_txt[1024] = "save/accreg.txt"; +#ifndef TXT_SQL_CONVERT +char inter_log_filename[1024] = "log/inter.log"; +char main_chat_nick[16] = "Main"; + +static struct dbt *accreg_db = NULL; + +unsigned int party_share_level = 10; + +// sending packet list +// NOTE: This variable ain't used at all! And it's confusing.. where do I add that the length of packet 0x2b07 is 10? x.x [Skotlex] +int inter_send_packet_length[]={ + -1,-1,27,-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3000-0x300f + -1, 7, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, + 35,-1,11,15, 34,29, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, + 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, + 9, 9,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 11,-1, 7, 3, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +// recv. packet list +int inter_recv_packet_length[]={ + -1,-1, 7,-1, -1,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3000-0x300f + 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, //0x3010-0x301f + -1, 6,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, //0x3020-0x302f + -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 14,19,186,-1, //0x3030-0x303f + 5, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 48,14,-1, 6, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3080-0x308f + -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3090 - 0x309f Homunculus packets [albator] +}; + +struct WisData { + int id, fd, count, len; + unsigned long tick; + unsigned char src[24], dst[24], msg[1024]; +}; +static struct dbt * wis_db = NULL; +static int wis_dellist[WISDELLIST_MAX], wis_delnum; + + +//-------------------------------------------------------- + +// アカウント変数を文字列へ変換 +int inter_accreg_tostr(char *str, struct accreg *reg) { + int j; + char *p = str; + + p += sprintf(p, "%d\t", reg->account_id); + for(j = 0; j < reg->reg_num; j++) { + p += sprintf(p,"%s,%s ", reg->reg[j].str, reg->reg[j].value); + } + + return 0; +} +#endif //TXT_SQL_CONVERT +// アカウント変数を文字列から変換 +int inter_accreg_fromstr(const char *str, struct accreg *reg) { + int j, n; + const char *p = str; + + if (sscanf(p, "%d\t%n", ®->account_id, &n ) != 1 || reg->account_id <= 0) + return 1; + + for(j = 0, p += n; j < ACCOUNT_REG_NUM; j++, p += n) { + if (sscanf(p, "%[^,],%[^ ] %n", reg->reg[j].str, reg->reg[j].value, &n) != 2) + break; + } + reg->reg_num = j; + + return 0; +} +#ifndef TXT_SQL_CONVERT +// アカウント変数の読み込み +int inter_accreg_init(void) { + char line[8192]; + FILE *fp; + int c = 0; + struct accreg *reg; + + accreg_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + if( (fp = fopen(accreg_txt, "r")) == NULL) + return 1; + while(fgets(line, sizeof(line)-1, fp)){ + line[sizeof(line)-1] = '\0'; + + reg = (struct accreg*)aCalloc(sizeof(struct accreg), 1); + if (reg == NULL) { + ShowFatalError("inter: accreg: out of memory!\n"); + exit(0); + } + if (inter_accreg_fromstr(line, reg) == 0 && reg->account_id > 0) { + idb_put(accreg_db, reg->account_id, reg); + } else { + ShowError("inter: accreg: broken data [%s] line %d\n", accreg_txt, c); + aFree(reg); + } + c++; + } + fclose(fp); +// printf("inter: %s read done (%d)\n", accreg_txt, c); + + return 0; +} + +// アカウント変数のセーブ用 +int inter_accreg_save_sub(DBKey key, void *data, va_list ap) { + char line[8192]; + FILE *fp; + struct accreg *reg = (struct accreg *)data; + + if (reg->reg_num > 0) { + inter_accreg_tostr(line,reg); + fp = va_arg(ap, FILE *); + fprintf(fp, "%s" RETCODE, line); + } + + return 0; +} + +// アカウント変数のセーブ +int inter_accreg_save(void) { + FILE *fp; + int lock; + + if ((fp = lock_fopen(accreg_txt,&lock)) == NULL) { + ShowError("int_accreg: cant write [%s] !!! data is lost !!!\n", accreg_txt); + return 1; + } + accreg_db->foreach(accreg_db, inter_accreg_save_sub,fp); + lock_fclose(fp, accreg_txt, &lock); +// printf("inter: %s saved.\n", accreg_txt); + + return 0; +} + +//-------------------------------------------------------- +#endif //TXT_SQL_CONVERT +/*========================================== + * 設定ファイルを読み込む + *------------------------------------------ + */ +static int inter_config_read(const char *cfgName) { + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp = fopen(cfgName, "r"); + if (fp == NULL) { + ShowError("file not found: %s\n", cfgName); + return 1; + } + while(fgets(line, sizeof(line) - 1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + line[sizeof(line)-1] = '\0'; + + if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + if (strcmpi(w1, "storage_txt") == 0) { + strncpy(storage_txt, w2, sizeof(storage_txt)); + } else if (strcmpi(w1, "party_txt") == 0) { + strncpy(party_txt, w2, sizeof(party_txt)); + } else if (strcmpi(w1, "pet_txt") == 0) { + strncpy(pet_txt, w2, sizeof(pet_txt)); + } else if (strcmpi(w1, "accreg_txt") == 0) { + strncpy(accreg_txt, w2, sizeof(accreg_txt)); + } else if (strcmpi(w1, "guild_txt") == 0) { + strncpy(guild_txt, w2, sizeof(guild_txt)); + } else if (strcmpi(w1, "castle_txt") == 0) { + strncpy(castle_txt, w2, sizeof(castle_txt)); + } else if (strcmpi(w1, "guild_storage_txt") == 0) { + strncpy(guild_storage_txt, w2, sizeof(guild_storage_txt)); +#ifndef TXT_SQL_CONVERT + } else if (strcmpi(w1, "homun_txt") == 0) { + strncpy(homun_txt, w2, sizeof(homun_txt)); + } else if (strcmpi(w1, "party_share_level") == 0) { + party_share_level = (unsigned int)atof(w2); + } else if (strcmpi(w1, "inter_log_filename") == 0) { + strncpy(inter_log_filename, w2, sizeof(inter_log_filename)); + } else if(strcmpi(w1,"log_inter")==0) { + log_inter = atoi(w2); + } else if(strcmpi(w1, "main_chat_nick")==0){ // Main chat nick [LuzZza] + strcpy(main_chat_nick, w2); +#endif //TXT_SQL_CONVERT + } else if (strcmpi(w1, "import") == 0) { + inter_config_read(w2); + } + } + fclose(fp); + + return 0; +} +#ifndef TXT_SQL_CONVERT +// ログ書き出し +int inter_log(char *fmt,...) { + FILE *logfp; + va_list ap; + + va_start(ap,fmt); + logfp = fopen(inter_log_filename, "a"); + if (logfp) { + vfprintf(logfp, fmt, ap); + fclose(logfp); + } + va_end(ap); + + return 0; +} + +// セーブ +int inter_save(void) { +#ifdef ENABLE_SC_SAVING + inter_status_save(); +#endif + inter_party_save(); + inter_guild_save(); + inter_storage_save(); + inter_guild_storage_save(); + inter_pet_save(); + inter_homun_save(); + inter_accreg_save(); + + return 0; +} +#endif //TXT_SQL_CONVERT +// 初期化 +int inter_init_txt(const char *file) { + inter_config_read(file); + +#ifndef TXT_SQL_CONVERT + wis_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + inter_party_init(); + inter_guild_init(); + inter_storage_init(); + inter_pet_init(); + inter_homun_init(); + inter_accreg_init(); +#endif //TXT_SQL_CONVERT + return 0; +} +#ifndef TXT_SQL_CONVERT +// finalize +void inter_final(void) { + accreg_db->destroy(accreg_db, NULL); + wis_db->destroy(wis_db, NULL); + + inter_party_final(); + inter_guild_final(); + inter_storage_final(); + inter_pet_final(); + inter_homun_final(); + + return; +} + +// マップサーバー接続 +int inter_mapif_init(int fd) { + inter_guild_mapif_init(fd); + + return 0; +} + +//-------------------------------------------------------- +// sended packets to map-server + +//Sends to map server the current max Account/Char id [Skotlex] +void mapif_send_maxid(int account_id, int char_id) +{ + unsigned char buf[12]; + + WBUFW(buf,0) = 0x2b07; + WBUFL(buf,2) = account_id; + WBUFL(buf,6) = char_id; + mapif_sendall(buf, 10); +} + +// GMメッセージ送信 +int mapif_GMmessage(unsigned char *mes, int len, unsigned long color, int sfd) { + unsigned char buf[2048]; + + if (len > 2048) len = 2047; //Make it fit to avoid crashes. [Skotlex] + WBUFW(buf,0) = 0x3800; + WBUFW(buf,2) = len; + WBUFL(buf,4) = color; + memcpy(WBUFP(buf,8), mes, len - 8); + mapif_sendallwos(sfd, buf, len); +// printf("inter server: GM:%d %s\n", len, mes); + + return 0; +} + +// Wisp/page transmission to one map-server +int mapif_wis_message2(struct WisData *wd, int fd) { + WFIFOHEAD(fd, 56+wd->len); + WFIFOW(fd, 0) = 0x3801; + WFIFOW(fd, 2) = 56 + wd->len; + WFIFOL(fd, 4) = wd->id; + memcpy(WFIFOP(fd, 8), wd->src, NAME_LENGTH); + memcpy(WFIFOP(fd,32), wd->dst, NAME_LENGTH); + memcpy(WFIFOP(fd,56), wd->msg, wd->len); + wd->count = 1; + WFIFOSET(fd,WFIFOW(fd,2)); + return 1; +} + +// Wisp/page transmission to all map-server +int mapif_wis_message(struct WisData *wd) { + unsigned char buf[2048]; + if (wd->len > 2047-56) wd->len = 2047-56; //Force it to fit to avoid crashes. [Skotlex] + + WBUFW(buf, 0) = 0x3801; + WBUFW(buf, 2) = 56 + wd->len; + WBUFL(buf, 4) = wd->id; + memcpy(WBUFP(buf, 8), wd->src, NAME_LENGTH); + memcpy(WBUFP(buf,32), wd->dst, NAME_LENGTH); + memcpy(WBUFP(buf,56), wd->msg, wd->len); + wd->count = mapif_sendall(buf, WBUFW(buf,2)); + + return 0; +} + +int mapif_wis_fail(int fd, char *src) { + unsigned char buf[27]; + WBUFW(buf, 0) = 0x3802; + memcpy(WBUFP(buf, 2), src, NAME_LENGTH); + WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send(fd, buf, 27); + return 0; + +} + +// Wisp/page transmission result to map-server +int mapif_wis_end(struct WisData *wd, int flag) { + unsigned char buf[27]; + + WBUFW(buf, 0) = 0x3802; + memcpy(WBUFP(buf, 2), wd->src, 24); + WBUFB(buf,26) = flag; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send(wd->fd, buf, 27); +// printf("inter server wis_end: flag: %d\n", flag); + + return 0; +} + +// アカウント変数送信 +int mapif_account_reg(int fd, unsigned char *src) { + unsigned char *buf = aCalloc(1,WBUFW(src,2)); + + memcpy(WBUFP(buf,0),src,WBUFW(src,2)); + WBUFW(buf, 0) = 0x3804; + mapif_sendallwos(fd, buf, WBUFW(buf,2)); + + aFree(buf); + + return 0; +} + +// アカウント変数要求返信 +int mapif_account_reg_reply(int fd,int account_id, int char_id) { + struct accreg *reg = idb_get(accreg_db,account_id); + + WFIFOHEAD(fd, ACCOUNT_REG_NUM * 288+ 13); + WFIFOW(fd,0) = 0x3804; + WFIFOL(fd,4) = account_id; + WFIFOL(fd,8) = char_id; + WFIFOB(fd,12) = 2; //Acc Reg + if (reg == NULL) { + WFIFOW(fd,2) = 13; + } else { + int i, p; + for (p=13,i = 0; i < reg->reg_num; i++) { + p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].str)+1; //We add 1 to consider the '\0' in place. + p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].value)+1; + } + WFIFOW(fd,2)=p; + } + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} + +//Request to kick char from a certain map server. [Skotlex] +int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason) +{ + if (fd < 0) + return -1; + + WFIFOHEAD(fd, 7); + WFIFOW(fd,0) = 0x2b1f; + WFIFOL(fd,2) = account_id; + WFIFOB(fd,6) = reason; + WFIFOSET(fd,7); + + return 0; +} + +//-------------------------------------------------------- + +// Existence check of WISP data +int check_ttl_wisdata_sub(DBKey key, void *data, va_list ap) { + unsigned long tick; + struct WisData *wd = (struct WisData *)data; + tick = va_arg(ap, unsigned long); + + if (DIFF_TICK(tick, wd->tick) > WISDATA_TTL && wis_delnum < WISDELLIST_MAX) + wis_dellist[wis_delnum++] = wd->id; + + return 0; +} + +int check_ttl_wisdata(void) { + unsigned long tick = gettick(); + int i; + + do { + wis_delnum = 0; + wis_db->foreach(wis_db, check_ttl_wisdata_sub, tick); + for(i = 0; i < wis_delnum; i++) { + struct WisData *wd = idb_get(wis_db, wis_dellist[i]); + ShowWarning("inter: wis data id=%d time out : from %s to %s\n", wd->id, wd->src, wd->dst); + // removed. not send information after a timeout. Just no answer for the player + //mapif_wis_end(wd, 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + idb_remove(wis_db, wd->id); + } + } while(wis_delnum >= WISDELLIST_MAX); + + return 0; +} + +//-------------------------------------------------------- +// received packets from map-server + +// GMメッセージ送信 +int mapif_parse_GMmessage(int fd) { + RFIFOHEAD(fd); + mapif_GMmessage(RFIFOP(fd,8), RFIFOW(fd,2), RFIFOL(fd,4), fd); + + return 0; +} + +static struct WisData* mapif_create_whisper(int fd, char* src, char* dst, char* mes, int meslen) +{ + static int wisid = 0; + struct WisData* wd = (struct WisData *)aCalloc(sizeof(struct WisData), 1); + if (wd == NULL){ + ShowFatalError("inter: WisRequest: out of memory !\n"); + return NULL; + } + wd->id = ++wisid; + wd->fd = fd; + wd->len= meslen; + memcpy(wd->src, src, NAME_LENGTH); + memcpy(wd->dst, dst, NAME_LENGTH); + memcpy(wd->msg, mes, meslen); + wd->tick = gettick(); + return wd; +} + +// Wisp/page request to send +int mapif_parse_WisRequest(int fd) { + struct mmo_charstatus* char_status; + struct WisData* wd; + char name[NAME_LENGTH]; + int fd2; + + RFIFOHEAD(fd); + + if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) { + ShowWarning("inter: Wis message size too long.\n"); + return 0; + } else if (RFIFOW(fd,2)-52 <= 0) { // normaly, impossible, but who knows... + ShowError("inter: Wis message doesn't exist.\n"); + return 0; + } + + memcpy(name, RFIFOP(fd,28), NAME_LENGTH); //Received name may be too large and not contain \0! [Skotlex] + name[NAME_LENGTH-1]= '\0'; + // search if character exists before to ask all map-servers + char_status = search_character_byname(name); + if (char_status == NULL) + return mapif_wis_fail(fd, RFIFOP(fd, 4)); + + // Character exists. + // to be sure of the correct name, rewrite it + memset(name, 0, NAME_LENGTH); + strncpy(name, char_status->name, NAME_LENGTH); + // if source is destination, don't ask other servers. + if (strcmp((char*)RFIFOP(fd,4),name) == 0) + return mapif_wis_fail(fd, RFIFOP(fd, 4)); + + //Look for online character. + fd2 = search_character_online(char_status->account_id, char_status->char_id); + if (fd2 >= 0) { //Character online, send whisper. + wd = mapif_create_whisper(fd, RFIFOP(fd, 4), RFIFOP(fd,28), RFIFOP(fd,52), RFIFOW(fd,2)-52); + if (!wd) return 1; + idb_put(wis_db, wd->id, wd); + mapif_wis_message2(wd, fd2); + return 0; + } + //Not found. + return mapif_wis_fail(fd, RFIFOP(fd, 4)); + +/* Scrapped since now we know where characters are online in. [Skotlex] + wd = mapif_create_whisper(fd, RFIFOP(fd, 4), RFIFOP(fd,28), RFIFOP(fd,52), RFIFOW(fd,2)-52); + if (!wd) return 0; + idb_put(wis_db, wd->id, wd); + mapif_wis_message(wd); + return 0; +*/ +} + +// Wisp/page transmission result +int mapif_parse_WisReply(int fd) { + int id, flag; + struct WisData *wd; + RFIFOHEAD(fd); + id = RFIFOL(fd,2); + flag = RFIFOB(fd,6); + wd = idb_get(wis_db, id); + + if (wd == NULL) + return 0; // This wisp was probably suppress before, because it was timeout or because of target was found on another map-server + + if ((--wd->count) <= 0 || flag != 1) { + mapif_wis_end(wd, flag); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + idb_remove(wis_db, id); + } + + return 0; +} + +// Received wisp message from map-server for ALL gm (just copy the message and resends it to ALL map-servers) +int mapif_parse_WisToGM(int fd) { + unsigned char buf[2048]; // 0x3003/0x3803 .w .24B .w .?B + RFIFOHEAD(fd); + memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2)); + WBUFW(buf, 0) = 0x3803; + mapif_sendall(buf, RFIFOW(fd,2)); + + return 0; +} + +static void* create_accreg(DBKey key, va_list args) { + struct accreg *reg; + reg = (struct accreg*)aCalloc(sizeof(struct accreg), 1); + reg->account_id = key.i; + return reg; +} + +// アカウント変数保存要求 +int mapif_parse_Registry(int fd) { + int j, p, len; + struct accreg *reg; + RFIFOHEAD(fd); + + switch (RFIFOB(fd,12)) { + case 3: //Character registry + return char_parse_Registry(RFIFOL(fd,4), RFIFOL(fd,8), RFIFOP(fd,13), RFIFOW(fd,2)-13); + case 2: //Acc Reg + break; + case 1: //Acc Reg2, forward to login + return save_accreg2(RFIFOP(fd,4), RFIFOW(fd,2)-4); + default: //Error? + return 1; + } + reg = idb_ensure(accreg_db, RFIFOL(fd,4), create_accreg); + + for(j=0,p=13;jreg[j].str,&len); + reg->reg[j].str[len]='\0'; + p +=len+1; //+1 to skip the '\0' between strings. + sscanf(RFIFOP(fd,p), "%255c%n",reg->reg[j].value,&len); + reg->reg[j].value[len]='\0'; + p +=len+1; + } + reg->reg_num=j; + mapif_account_reg(fd, RFIFOP(fd,0)); // 他のMAPサーバーに送信 + + return 0; +} + +// Request the value of all registries. +int mapif_parse_RegistryRequest(int fd) +{ + RFIFOHEAD(fd); + //Load Char Registry + if (RFIFOB(fd,12)) + char_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6)); + //Load Account Registry + if (RFIFOB(fd,11)) + mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6)); + //Ask Login Server for Account2 values. + if (RFIFOB(fd,10)) + request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6)-2); + return 1; +} + +//-------------------------------------------------------- + +// map server からの通信(1パケットのみ解析すること) +// エラーなら0(false)、処理できたなら1、 +// パケット長が足りなければ2をかえさなければならない +int inter_parse_frommap(int fd) { + int cmd, len; + RFIFOHEAD(fd); + cmd = RFIFOW(fd,0); + len = 0; + + // inter鯖管轄かを調べる + if (cmd < 0x3000 || cmd >= 0x3000 + (sizeof(inter_recv_packet_length) / sizeof(inter_recv_packet_length[0]))) + return 0; + + if (inter_recv_packet_length[cmd-0x3000] == 0) //This is necessary, because otherwise we return 2 and the char server will just hang waiting for packets! [Skotlex] + return 0; + + // パケット長を調べる + if ((len = inter_check_length(fd, inter_recv_packet_length[cmd - 0x3000])) == 0) + return 2; + + switch(cmd) { + case 0x3000: mapif_parse_GMmessage(fd); break; + case 0x3001: mapif_parse_WisRequest(fd); break; + case 0x3002: mapif_parse_WisReply(fd); break; + case 0x3003: mapif_parse_WisToGM(fd); break; + case 0x3004: mapif_parse_Registry(fd); break; + case 0x3005: mapif_parse_RegistryRequest(fd); break; + default: + if (inter_party_parse_frommap(fd)) + break; + if (inter_guild_parse_frommap(fd)) + break; + if (inter_storage_parse_frommap(fd)) + break; + if (inter_pet_parse_frommap(fd)) + break; + if (inter_homun_parse_frommap(fd)) + break; + return 0; + } + RFIFOSKIP(fd, len); + return 1; +} + +// RFIFOのパケット長確認 +// 必要パケット長があればパケット長、まだ足りなければ0 +int inter_check_length(int fd, int length) { + if (length == -1) { // 可変パケット長 + RFIFOHEAD(fd); + if (RFIFOREST(fd) < 4) // パケット長が未着 + return 0; + length = RFIFOW(fd,2); + } + + if ((int)RFIFOREST(fd) < length) // パケットが未着 + return 0; + + return length; +} +#endif //TXT_SQL_CONVERT diff --git a/src/char/inter.h b/src/char/inter.h index 75489c387..1789848c5 100644 --- a/src/char/inter.h +++ b/src/char/inter.h @@ -1,28 +1,28 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INTER_H_ -#define _INTER_H_ - -int inter_init_txt(const char *file); -void inter_final(void); -int inter_save(void); -int inter_parse_frommap(int fd); -int inter_mapif_init(int fd); -void mapif_send_maxid(int, int); -int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason); - -int inter_check_length(int fd,int length); - -int inter_log(char *fmt,...); - -#define inter_cfgName "conf/inter_athena.conf" - -extern unsigned int party_share_level; -extern char inter_log_filename[1024]; -extern char main_chat_nick[16]; - -//For TXT->SQL conversion -extern char accreg_txt[]; -int inter_accreg_fromstr(const char *str, struct accreg *reg); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INTER_H_ +#define _INTER_H_ + +int inter_init_txt(const char *file); +void inter_final(void); +int inter_save(void); +int inter_parse_frommap(int fd); +int inter_mapif_init(int fd); +void mapif_send_maxid(int, int); +int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason); + +int inter_check_length(int fd,int length); + +int inter_log(char *fmt,...); + +#define inter_cfgName "conf/inter_athena.conf" + +extern unsigned int party_share_level; +extern char inter_log_filename[1024]; +extern char main_chat_nick[16]; + +//For TXT->SQL conversion +extern char accreg_txt[]; +int inter_accreg_fromstr(const char *str, struct accreg *reg); +#endif diff --git a/src/char_sql/char.c b/src/char_sql/char.c index 136453e3b..a6bc9dbea 100644 --- a/src/char_sql/char.c +++ b/src/char_sql/char.c @@ -1,4435 +1,4435 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -// original code from athena -// SQL conversion by Jioh L. Jung -// TXT 1.105 -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "char.h" -#include "../common/utils.h" -#include "../common/strlib.h" -#include "../common/showmsg.h" -#include "itemdb.h" -#include "inter.h" -#include "db.h" -#include "malloc.h" -#include "int_guild.h" - -#ifndef TXT_SQL_CONVERT -static struct dbt *char_db_; -#endif -char char_db[256] = "char"; -char scdata_db[256] = "sc_data"; -char cart_db[256] = "cart_inventory"; -char inventory_db[256] = "inventory"; -char charlog_db[256] = "charlog"; -char storage_db[256] = "storage"; -char interlog_db[256] = "interlog"; -char reg_db[256] = "global_reg_value"; -char skill_db[256] = "skill"; -char memo_db[256] = "memo"; -char guild_db[256] = "guild"; -char guild_alliance_db[256] = "guild_alliance"; -char guild_castle_db[256] = "guild_castle"; -char guild_expulsion_db[256] = "guild_expulsion"; -char guild_member_db[256] = "guild_member"; -char guild_position_db[256] = "guild_position"; -char guild_skill_db[256] = "guild_skill"; -char guild_storage_db[256] = "guild_storage"; -char party_db[256] = "party"; -char pet_db[256] = "pet"; -char friend_db[256] = "friends"; -#ifdef TXT_SQL_CONVERT -int save_log = 0; //Have the logs be off by default when converting -#else -int save_log = 1; -int db_use_sqldbs; -int connection_ping_interval = 0; - -char login_db[256] = "login"; -char login_db_account_id[32] = "account_id"; -char login_db_level[32] = "level"; - -int lowest_gm_level = 1; - -char *SQL_CONF_NAME = "conf/inter_athena.conf"; - -struct mmo_map_server{ - long ip; - short port; - int users; - unsigned short map[MAX_MAP_PER_SERVER]; -} server[MAX_MAP_SERVERS]; - -int server_fd[MAX_MAP_SERVERS]; - -int login_fd, char_fd; -char userid[24]; -char passwd[24]; -char server_name[20]; -char wisp_server_name[NAME_LENGTH] = "Server"; -char login_ip_str[128]; -in_addr_t login_ip = 0; -int login_port = 6900; -char char_ip_str[128]; -in_addr_t char_ip = 0; -char bind_ip_str[128]; -in_addr_t bind_ip = 0; -int char_port = 6121; -int char_maintenance = 0; -int char_new; -int char_new_display; -int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor] -int char_name_option = 0; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor] -char char_name_letters[1024] = ""; // list of letters/symbols used to authorise or not a name of a character. by [Yor] -//The following are characters that are trimmed regardless because they cause confusion and problems on the servers. [Skotlex] -#define TRIM_CHARS "\032\t\x0A\x0D " -int char_per_account = 0; //Maximum charas per account (default unlimited) [Sirius] -int char_del_level = 0; //From which level u can delete character [Lupus] - -int log_char = 1; // loggin char or not [devil] -int log_inter = 1; // loggin inter or not [devil] - -// Advanced subnet check [LuzZza] -struct _subnet { - long subnet; - long mask; - long char_ip; - long map_ip; -} subnet[16]; - -int subnet_count = 0; - -char unknown_char_name[NAME_LENGTH] = "Unknown"; -char db_path[1024]="db"; - -//These are used to aid the map server in identifying valid clients. [Skotlex] -static int max_account_id = DEFAULT_MAX_ACCOUNT_ID, max_char_id = DEFAULT_MAX_CHAR_ID; -static int online_check = 1; //If one, it won't let players connect when their account is already registered online and will send the relevant map server a kick user request. [Skotlex] - -struct char_session_data{ - int account_id, login_id1, login_id2,sex; - int found_char[9]; - char email[40]; // e-mail (default: a@a.com) by [Yor] - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) -}; - -#define AUTH_FIFO_SIZE 256 -struct { - int account_id, char_id, login_id1, login_id2, ip, char_pos, delflag,sex; - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) -} auth_fifo[AUTH_FIFO_SIZE]; -int auth_fifo_pos = 0; - -int check_ip_flag = 1; // It's to check IP of a player between char-server and other servers (part of anti-hacking system) - -struct mmo_charstatus char_dat; -int char_num,char_max; -int max_connect_user = 0; -int gm_allow_level = 99; -int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; -int start_zeny = 0; -int start_weapon = 1201; -int start_armor = 2301; -int guild_exp_rate = 100; - -//Custom limits for the fame lists. [Skotlex] -int fame_list_size_chemist = MAX_FAME_LIST; -int fame_list_size_smith = MAX_FAME_LIST; -int fame_list_size_taekwon = MAX_FAME_LIST; - -// Char-server-side stored fame lists [DracoRPG] -struct fame_list smith_fame_list[MAX_FAME_LIST]; -struct fame_list chemist_fame_list[MAX_FAME_LIST]; -struct fame_list taekwon_fame_list[MAX_FAME_LIST]; - -// check for exit signal -// 0 is saving complete -// other is char_id -unsigned int save_flag = 0; - -// start point (you can reset point on conf file) -struct point start_point = { 0, 53, 111}; - -bool char_gm_read = false; -struct gm_account *gm_account = NULL; -int GM_num = 0; - -int console = 0; - -//Structure for holding in memory which characters are online on the map servers connected. -struct online_char_data { - int account_id; - int char_id; - short server; - unsigned waiting_disconnect :1; -}; - -struct dbt *online_char_db; //Holds all online characters. - -#ifndef SQL_DEBUG - -#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] - -#else - -#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) - -#endif - -static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data); - -static void * create_online_char_data(DBKey key, va_list args) { - struct online_char_data* character; - character = aCalloc(1, sizeof(struct online_char_data)); - character->account_id = key.i; - character->char_id = -1; - character->server = -1; - return character; -} - -//------------------------------------------------- -// Set Character online/offline [Wizputer] -//------------------------------------------------- - -void set_char_online(int map_id, int char_id, int account_id) { - struct online_char_data* character; - if ( char_id != 99 ) { - sprintf(tmp_sql, "UPDATE `%s` SET `online`='1' WHERE `char_id`='%d'",char_db,char_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - if (max_account_id < account_id || max_char_id < char_id) - { //Notify map-server of the new max IDs [Skotlex] - if (account_id > max_account_id) - max_account_id = account_id; - if (char_id > max_char_id) - max_char_id = char_id; - mapif_send_maxid(max_account_id, max_char_id); - } - } - - character = idb_ensure(online_char_db, account_id, create_online_char_data); - if (online_check && character->char_id != -1 && character->server > -1 && character->server != map_id) - { - //char == 99 <- Character logging in, so someone has logged in while one - //char is still on map-server, so kick him out, but don't print "error" - //as this is normal behaviour. [Skotlex] - if (char_id != 99) - ShowNotice("set_char_online: Character %d:%d marked in map server %d, but map server %d claims to have (%d:%d) online!\n", - character->account_id, character->char_id, character->server, map_id, account_id, char_id); - mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); - } - character->char_id = (char_id==99)?-1:char_id; - character->server = (char_id==99)?-1:map_id; - character->waiting_disconnect = 0; - if (char_id != 99) - { //Set char online in guild cache. If char is in memory, use the guild id on it, otherwise seek it. - struct mmo_charstatus *cp; - cp = idb_get(char_db_,char_id); - inter_guild_CharOnline(char_id, cp?cp->guild_id:-1); - } - if (login_fd > 0 && !session[login_fd]->eof) - { - WFIFOHEAD(login_fd,6); - WFIFOW(login_fd,0) = 0x272b; - WFIFOL(login_fd,2) = account_id; - WFIFOSET(login_fd,6); - } -} - -void set_char_offline(int char_id, int account_id) { - struct mmo_charstatus *cp; - struct online_char_data* character; - - if ( char_id == 99 ) - sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `account_id`='%d'", char_db, account_id); - else { - cp = idb_get(char_db_,char_id); - inter_guild_CharOffline(char_id, cp?cp->guild_id:-1); - if (cp) - idb_remove(char_db_,char_id); - - sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `char_id`='%d'", char_db, char_id); - - if (mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - if ((character = idb_get(online_char_db, account_id)) != NULL) - { //We don't free yet to avoid aCalloc/aFree spamming during char change. [Skotlex] - character->char_id = -1; - character->server = -1; - character->waiting_disconnect = 0; - } - - if (login_fd > 0 && !session[login_fd]->eof) - { - WFIFOHEAD(login_fd,6); - WFIFOW(login_fd,0) = 0x272c; - WFIFOL(login_fd,2) = account_id; - WFIFOSET(login_fd,6); - } -} - -static int char_db_setoffline(DBKey key, void* data, va_list ap) { - struct online_char_data* character = (struct online_char_data*)data; - int server = va_arg(ap, int); - if (server == -1) { - character->char_id = -1; - character->server = -1; - character->waiting_disconnect = 0; - } else if (character->server == server) - character->server = -2; //In some map server that we aren't connected to. - return 0; -} - -static int char_db_kickoffline(DBKey key, void* data, va_list ap) { - struct online_char_data* character = (struct online_char_data*)data; - int server = va_arg(ap, int); - if (server > -1 && character->server != server) - return 0; - - //Kick out any connected characters, and set them offline as appropiate. - if (character->server > -1) - mapif_disconnectplayer(server_fd[character->server], - character->account_id, character->char_id, 1); - else if (!character->waiting_disconnect) - set_char_offline(character->char_id, character->account_id); - else return 0; - return 1; -} - -void set_all_offline(int id) { - if (id < 0) - ShowNotice("Sending all users offline.\n"); - else - ShowNotice("Sending users of map-server %d offline.\n",id); - online_char_db->foreach(online_char_db,char_db_kickoffline,id); - - if (id >= 0 || login_fd <= 0 || session[login_fd]->eof) - return; - //Tell login-server to also mark all our characters as offline. - WFIFOHEAD(login_fd, 2); - WFIFOW(login_fd,0) = 0x2737; - WFIFOSET(login_fd,2); -} - -void set_all_offline_sql(void) { - //Set all players to 'OFFLINE' - sprintf(tmp_sql, "UPDATE `%s` SET `online` = '0'", char_db); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sprintf(tmp_sql, "UPDATE `%s` SET `online` = '0'", guild_member_db); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sprintf(tmp_sql, "UPDATE `%s` SET `connect_member` = '0'", guild_db); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } -} - -//---------------------------------------------------------------------- -// Determine if an account (id) is a GM account -// and returns its level (or 0 if it isn't a GM account or if not found) -//---------------------------------------------------------------------- -// Removed since nothing GM related goes on in the char server [CLOWNISIUS] -int isGM(int account_id) { - int i; - - for(i = 0; i < GM_num; i++) - if (gm_account[i].account_id == account_id) - return gm_account[i].level; - return 0; -} - -void read_gm_account(void) { - if(!char_gm_read) - return; - - if (gm_account != NULL) - aFree(gm_account); - GM_num = 0; - - sprintf(tmp_sql, "SELECT `%s`,`%s` FROM `%s` WHERE `%s`>='%d'",login_db_account_id,login_db_level,login_db,login_db_level,lowest_gm_level); - if (mysql_query(&lmysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&lmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - lsql_res = mysql_store_result(&lmysql_handle); - if (lsql_res) { - gm_account = (struct gm_account*)aCalloc(sizeof(struct gm_account) * (size_t)mysql_num_rows(lsql_res), 1); - while ((lsql_row = mysql_fetch_row(lsql_res))) { - gm_account[GM_num].account_id = atoi(lsql_row[0]); - gm_account[GM_num].level = atoi(lsql_row[1]); - GM_num++; - } - } - - mysql_free_result(lsql_res); - mapif_send_gmaccounts(); -} -#endif //TXT_SQL_CONVERT -int compare_item(struct item *a, struct item *b) { - - if(a->id == b->id && - a->nameid == b->nameid && - a->amount == b->amount && - a->equip == b->equip && - a->identify == b->identify && - a->refine == b->refine && - a->attribute == b->attribute) - { - int i; - for (i=0; icard[i]==b->card[i]; i++); - return (i == MAX_SLOTS); - } - return 0; -} - -#ifndef TXT_SQL_CONVERT -static void* create_charstatus(DBKey key, va_list args) { - struct mmo_charstatus *cp; - cp = (struct mmo_charstatus *) aCalloc(1,sizeof(struct mmo_charstatus)); - cp->char_id = key.i; - return cp; -} -#endif //TXT_SQL_CONVERT -int mmo_char_tosql(int char_id, struct mmo_charstatus *p){ - int i=0,j; - int count = 0; - int diff = 0; - char *tmp_ptr; //Building a single query should be more efficient than running - //multiple queries for each thing about to be saved, right? [Skotlex] - char save_status[128]; //For displaying save information. [Skotlex] - struct mmo_charstatus *cp; - struct itemtmp mapitem[MAX_GUILD_STORAGE]; - - if (char_id!=p->char_id) return 0; - -#ifndef TXT_SQL_CONVERT - cp = idb_ensure(char_db_, char_id, create_charstatus); -#else - cp = aCalloc(1, sizeof(struct mmo_charstatus)); -#endif - - memset(save_status, 0, sizeof(save_status)); - diff = 0; - //map inventory data - for(i=0;iinventory[i], &cp->inventory[i])) - diff = 1; - if(p->inventory[i].nameid>0){ - mapitem[count].flag=0; - mapitem[count].id = p->inventory[i].id; - mapitem[count].nameid=p->inventory[i].nameid; - mapitem[count].amount = p->inventory[i].amount; - mapitem[count].equip = p->inventory[i].equip; - mapitem[count].identify = p->inventory[i].identify; - mapitem[count].refine = p->inventory[i].refine; - mapitem[count].attribute = p->inventory[i].attribute; - for (j=0; jinventory[i].card[j]; - count++; - } - } - //printf("- Save item data to MySQL!\n"); - if (diff) - if (!memitemdata_to_sql(mapitem, count, p->char_id,TABLE_INVENTORY)) - strcat(save_status, " inventory"); - - count = 0; - diff = 0; - - //map cart data - for(i=0;icart[i], &cp->cart[i])) - diff = 1; - if(p->cart[i].nameid>0){ - mapitem[count].flag=0; - mapitem[count].id = p->cart[i].id; - mapitem[count].nameid=p->cart[i].nameid; - mapitem[count].amount = p->cart[i].amount; - mapitem[count].equip = p->cart[i].equip; - mapitem[count].identify = p->cart[i].identify; - mapitem[count].refine = p->cart[i].refine; - mapitem[count].attribute = p->cart[i].attribute; - for (j=0; jcart[i].card[j]; - count++; - } - } - - if (diff) - if (!memitemdata_to_sql(mapitem, count, p->char_id,TABLE_CART)) - strcat(save_status, " cart"); -#ifdef TXT_SQL_CONVERT -{ //Insert the barebones to then update the rest. - char t_name[NAME_LENGTH*2]; - jstrescapecpy(t_name, p->name); - sprintf(tmp_sql, "REPLACE INTO `%s` (`account_id`, `char_num`, `name`) VALUES ('%d', '%d', '%s')", - char_db, p->account_id, p->char_num, t_name); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } else - strcat(save_status, " creation"); -} -#endif - - if ( - (p->base_exp != cp->base_exp) || (p->base_level != cp->base_level) || - (p->job_level != cp->job_level) || (p->job_exp != cp->job_exp) || - (p->zeny != cp->zeny) || - (p->last_point.x != cp->last_point.x) || (p->last_point.y != cp->last_point.y) || - (p->max_hp != cp->max_hp) || (p->hp != cp->hp) || - (p->max_sp != cp->max_sp) || (p->sp != cp->sp) || - (p->status_point != cp->status_point) || (p->skill_point != cp->skill_point) || - (p->str != cp->str) || (p->agi != cp->agi) || (p->vit != cp->vit) || - (p->int_ != cp->int_) || (p->dex != cp->dex) || (p->luk != cp->luk) || - (p->option != cp->option) || - (p->party_id != cp->party_id) || (p->guild_id != cp->guild_id) || - (p->pet_id != cp->pet_id) || (p->weapon != cp->weapon) || (p->hom_id != cp->hom_id) || - (p->shield != cp->shield) || (p->head_top != cp->head_top) || - (p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom) - ) - { //Save status - sprintf(tmp_sql ,"UPDATE `%s` SET `base_level`='%d', `job_level`='%d'," - "`base_exp`='%u', `job_exp`='%u', `zeny`='%d'," - "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d'," - "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d'," - "`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d'," //[orn] add homun_id (homunculus id) - "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," - "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d'" - " WHERE `account_id`='%d' AND `char_id` = '%d'", - char_db, p->base_level, p->job_level, - p->base_exp, p->job_exp, p->zeny, - p->max_hp, p->hp, p->max_sp, p->sp, p->status_point, p->skill_point, - p->str, p->agi, p->vit, p->int_, p->dex, p->luk, - p->option, p->party_id, p->guild_id, p->pet_id, p->hom_id, //[orn] add homun_id (homunculus id) - p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom, - mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y, - mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, - p->account_id, p->char_id - ); - - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } else - strcat(save_status, " status"); - } - - //Values that will seldom change (to speed up saving) - if ( - (p->hair != cp->hair) || (p->hair_color != cp->hair_color) || (p->clothes_color != cp->clothes_color) || - (p->class_ != cp->class_) || - (p->partner_id != cp->partner_id) || (p->father != cp->father) || - (p->mother != cp->mother) || (p->child != cp->child) || - (p->karma != cp->karma) || (p->manner != cp->manner) || - (p->fame != cp->fame) - ) - { - sprintf(tmp_sql ,"UPDATE `%s` SET `class`='%d'," - "`hair`='%d',`hair_color`='%d',`clothes_color`='%d'," - "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d'," - "`karma`='%d',`manner`='%d', `fame`='%d'" - " WHERE `account_id`='%d' AND `char_id` = '%d'", - char_db, p->class_, - p->hair, p->hair_color, p->clothes_color, - p->partner_id, p->father, p->mother, p->child, - p->karma, p->manner, p->fame, - p->account_id, p->char_id - ); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } else - strcat(save_status, " status2"); - } - - - diff = 0; - - for(i=0;imemo_point[i].map == cp->memo_point[i].map && p->memo_point[i].x == cp->memo_point[i].x && p->memo_point[i].y == cp->memo_point[i].y) - continue; - diff = 1; - break; - } - - if (diff) - { //Save memo - //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`) - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",memo_db, p->char_id); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - //insert here. - tmp_ptr = tmp_sql; - tmp_ptr += sprintf(tmp_ptr, "INSERT INTO `%s`(`char_id`,`map`,`x`,`y`) VALUES ", memo_db); - count = 0; - for(i=0;imemo_point[i].map){ - tmp_ptr += sprintf(tmp_ptr,"('%d', '%s', '%d', '%d'),", - char_id, mapindex_id2name(p->memo_point[i].map), p->memo_point[i].x, p->memo_point[i].y); - count++; - } - } - if (count) - { //Dangerous? Only if none of the above sprintf worked. [Skotlex] - tmp_ptr[-1] = '\0'; //Remove the trailing comma. - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } else - strcat(save_status, " memo"); - } else //Memo Points cleared (how is this possible?). - strcat(save_status, " memo"); - } - - diff = 0; - for(i=0;iskill[i].lv != 0) && (p->skill[i].id == 0)) - p->skill[i].id = i; // Fix skill tree - - if((p->skill[i].id != cp->skill[i].id) || (p->skill[i].lv != cp->skill[i].lv) || - (p->skill[i].flag != cp->skill[i].flag)) - { - diff = 1; - break; - } - } - - if (diff) - { //Save skills - - //`skill` (`char_id`, `id`, `lv`) - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",skill_db, p->char_id); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - tmp_ptr = tmp_sql; - tmp_ptr += sprintf(tmp_ptr,"INSERT INTO `%s`(`char_id`,`id`,`lv`) VALUES ", skill_db); - count = 0; - //insert here. - for(i=0;iskill[i].id && p->skill[i].flag!=1) - { - tmp_ptr += sprintf(tmp_ptr,"('%d','%d','%d'),", - char_id, p->skill[i].id, (p->skill[i].flag==0)?p->skill[i].lv:p->skill[i].flag-2); - count++; - } - } - - if (count) - { - tmp_ptr[-1] = '\0'; //Remove trailing comma. - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } else - strcat(save_status, " skills"); - } else //Skills removed (reset?) - strcat(save_status, " skills"); - } - diff = 0; - for(i = 0; i < MAX_FRIENDS; i++){ - if(p->friends[i].char_id != cp->friends[i].char_id || - p->friends[i].account_id != cp->friends[i].account_id){ - diff = 1; - break; - } - } - - if(diff == 1) - { //Save friends - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `char_id`='%d'", friend_db, char_id); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - tmp_ptr = tmp_sql; - tmp_ptr += sprintf(tmp_ptr, "INSERT INTO `%s` (`char_id`, `friend_account`, `friend_id`) VALUES ", friend_db); - count = 0; - for(i = 0; i < MAX_FRIENDS; i++){ - if(p->friends[i].char_id > 0) - { - tmp_ptr += sprintf(tmp_ptr, "('%d','%d','%d'),", char_id, p->friends[i].account_id, p->friends[i].char_id); - count++; - } - } - if (count) - { - tmp_ptr[-1] = '\0'; //Remove the last comma. [Skotlex] - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } else - strcat(save_status, " friends"); - } else //Friend list cleared. - strcat(save_status, " friends"); - - } - - if (save_status[0]!='\0' && save_log) - ShowInfo("Saved char %d - %s:%s.\n", char_id, p->name, save_status); -#ifndef TXT_SQL_CONVERT - memcpy(cp, p, sizeof(struct mmo_charstatus)); -#else - aFree(cp); -#endif - return 0; -} - -// [Ilpalazzo-sama] -int memitemdata_to_sql(struct itemtmp mapitem[], int count, int char_id, int tableswitch) -{ - int i,j, flag, id; - char *tablename; - char selectoption[16]; - char * str_p = tmp_sql; - - switch (tableswitch) { - case TABLE_INVENTORY: - tablename = inventory_db; // no need for sprintf here as *_db are char*. - sprintf(selectoption,"char_id"); - break; - case TABLE_CART: - tablename = cart_db; - sprintf(selectoption,"char_id"); - break; - case TABLE_STORAGE: - tablename = storage_db; - sprintf(selectoption,"account_id"); - break; - case TABLE_GUILD_STORAGE: - tablename = guild_storage_db; - sprintf(selectoption,"guild_id"); - break; - default: - ShowError("Invalid table name!\n"); - return 1; - } - - //=======================================mysql database data > memory=============================================== - - str_p += sprintf(str_p, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); - - for (j=0; j SQL =============================== -#ifndef TXT_SQL_CONVERT - if(!itemdb_isequip(mapitem[i].nameid)) - { //Quick update of stackable items. Update Qty and Equip should be enough, but in case we are also updating identify - sprintf(tmp_sql,"UPDATE `%s` SET `equip`='%d', `identify`='%d', `amount`='%d' WHERE `id`='%d' LIMIT 1", - tablename, mapitem[i].equip, mapitem[i].identify,mapitem[i].amount, id); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } else -#endif //TXT_SQL_CONVERT - { //Equipment or Misc item, just update all fields. - str_p = tmp_sql; - str_p += sprintf(str_p,"UPDATE `%s` SET `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d'", - tablename, mapitem[i].equip, mapitem[i].identify, mapitem[i].refine, mapitem[i].attribute); - - for(j=0; jchar_id = char_id; - if (save_log) - ShowInfo("Char load request (%d)\n", char_id); - //`char`( `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, //9 - //`str`,`agi`,`vit`,`int`,`dex`,`luk`, //15 - //`max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, //21 - //`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`, //27 - //`hair`,`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, //35 - //`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`) - //splite 2 parts. cause veeeery long SQL syntax - - sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`," - "`str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point` FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR - - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sql_res = mysql_store_result(&mysql_handle); - - if (sql_res) { - sql_row = mysql_fetch_row(sql_res); - if (!sql_row) - { //Just how does this happens? [Skotlex] - ShowError("Requested non-existant character id: %d!\n", char_id); - return 0; - } - - p->char_id = char_id; - p->account_id = atoi(sql_row[1]); - p->char_num = atoi(sql_row[2]); - strcpy(p->name, sql_row[3]); - p->class_ = atoi(sql_row[4]); - p->base_level = atoi(sql_row[5]); - p->job_level = atoi(sql_row[6]); - exp = atof(sql_row[7]); - p->base_exp = exp<0?0:(exp>UINT_MAX?UINT_MAX:(unsigned int)exp); - exp = atof(sql_row[8]); - p->job_exp = exp<0?0:(exp>UINT_MAX?UINT_MAX:(unsigned int)exp); - p->zeny = atoi(sql_row[9]); - p->str = atoi(sql_row[10]); - p->agi = atoi(sql_row[11]); - p->vit = atoi(sql_row[12]); - p->int_ = atoi(sql_row[13]); - p->dex = atoi(sql_row[14]); - p->luk = atoi(sql_row[15]); - p->max_hp = atoi(sql_row[16]); - p->hp = atoi(sql_row[17]); - p->max_sp = atoi(sql_row[18]); - p->sp = atoi(sql_row[19]); - p->status_point = atoi(sql_row[20]) > USHRT_MAX ? USHRT_MAX : atoi(sql_row[20]); - p->skill_point = atoi(sql_row[21]) > USHRT_MAX ? USHRT_MAX : atoi(sql_row[21]); - //free mysql result. - mysql_free_result(sql_res); - strcat (t_msg, " status"); - } else - ShowError("Load char failed (%d - table %s).\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!? - - sprintf(tmp_sql, "SELECT `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`," - "`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`," - "`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame`, `homun_id`" //[orn] homun_id - "FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sql_res = mysql_store_result(&mysql_handle); - sql_row = sql_res?mysql_fetch_row(sql_res):NULL; - if (sql_row) { - - p->option = atoi(sql_row[0]); p->karma = atoi(sql_row[1]); p->manner = atoi(sql_row[2]); - p->party_id = atoi(sql_row[3]); p->guild_id = atoi(sql_row[4]); p->pet_id = atoi(sql_row[5]); - - p->hair = atoi(sql_row[6]); p->hair_color = atoi(sql_row[7]); p->clothes_color = atoi(sql_row[8]); - p->weapon = atoi(sql_row[9]); p->shield = atoi(sql_row[10]); - p->head_top = atoi(sql_row[11]); p->head_mid = atoi(sql_row[12]); p->head_bottom = atoi(sql_row[13]); - p->last_point.map = mapindex_name2id(sql_row[14]); p->last_point.x = atoi(sql_row[15]); p->last_point.y = atoi(sql_row[16]); - p->save_point.map = mapindex_name2id(sql_row[17]); p->save_point.x = atoi(sql_row[18]); p->save_point.y = atoi(sql_row[19]); - p->partner_id = atoi(sql_row[20]); p->father = atoi(sql_row[21]); p->mother = atoi(sql_row[22]); p->child = atoi(sql_row[23]); - p->fame = atoi(sql_row[24]); - p->hom_id = atoi(sql_row[25]); //[orn] homunculus id - - strcat (t_msg, " status2"); - } else - ShowError("Char load failed (%d - table %s)\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!? - //free mysql result. - if (sql_res) - mysql_free_result(sql_res); - - //read memo data - //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`) - sprintf(tmp_sql, "SELECT `map`,`x`,`y` FROM `%s` WHERE `char_id`='%d' ORDER by `memo_id`",memo_db, char_id); // TBR - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - - if (sql_res) { - for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){ - p->memo_point[i].map = mapindex_name2id(sql_row[0]); - p->memo_point[i].x=atoi(sql_row[1]); - p->memo_point[i].y=atoi(sql_row[2]); - //i ++; - } - mysql_free_result(sql_res); - strcat (t_msg, " memo"); - } - - //read inventory - //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`) - str_p += sprintf(str_p, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); - - for (j=0; jinventory[i].id = atoi(sql_row[0]); - p->inventory[i].nameid = atoi(sql_row[1]); - p->inventory[i].amount = atoi(sql_row[2]); - p->inventory[i].equip = atoi(sql_row[3]); - p->inventory[i].identify = atoi(sql_row[4]); - p->inventory[i].refine = atoi(sql_row[5]); - p->inventory[i].attribute = atoi(sql_row[6]); - for (j=0; jinventory[i].card[j] = atoi(sql_row[7+j]); - } - mysql_free_result(sql_res); - strcat (t_msg, " inventory"); - } - - //read cart. - //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`) - str_p = tmp_sql; - str_p += sprintf(str_p, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); - - for (j=0; jcart[i].id = atoi(sql_row[0]); - p->cart[i].nameid = atoi(sql_row[1]); - p->cart[i].amount = atoi(sql_row[2]); - p->cart[i].equip = atoi(sql_row[3]); - p->cart[i].identify = atoi(sql_row[4]); - p->cart[i].refine = atoi(sql_row[5]); - p->cart[i].attribute = atoi(sql_row[6]); - for(j=0; jcart[i].card[j] = atoi(sql_row[7+j]); - } - mysql_free_result(sql_res); - strcat (t_msg, " cart"); - } - - //read skill - //`skill` (`char_id`, `id`, `lv`) - sprintf(tmp_sql, "SELECT `id`, `lv` FROM `%s` WHERE `char_id`='%d'",skill_db, char_id); // TBR - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){ - n = atoi(sql_row[0]); - p->skill[n].id = n; //memory!? shit!. - p->skill[n].lv = atoi(sql_row[1]); - } - mysql_free_result(sql_res); - strcat (t_msg, " skills"); - } -/* Global-reg loading is now handled by the inter-server. - //global_reg - //`global_reg_value` (`char_id`, `str`, `value`) - sprintf(tmp_sql, "SELECT `str`, `value` FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, char_id); // TBR - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - i = 0; - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){ - strcpy (p->global_reg[i].str, sql_row[0]); - strcpy (p->global_reg[i].value, sql_row[1]); - } - mysql_free_result(sql_res); - strcat (t_msg, " reg_values"); - } - p->global_reg_num=i; -*/ - //Shamelessly stolen from its_sparky (ie: thanks) and then assimilated by [Skotlex] - //Friend list - sprintf(tmp_sql, "SELECT f.friend_account, f.friend_id, c.name FROM `%s` f LEFT JOIN `%s` c ON f.friend_account=c.account_id AND f.friend_id=c.char_id WHERE f.char_id='%d'", friend_db, char_db, char_id); - - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sql_res = mysql_store_result(&mysql_handle); - if(sql_res) - { - for(i = 0; (sql_row = mysql_fetch_row(sql_res)) && ifriends[i].account_id = atoi(sql_row[0]); - p->friends[i].char_id = atoi(sql_row[1]); - strncpy(p->friends[i].name, sql_row[2], NAME_LENGTH-1); //The -1 is to avoid losing the ending \0 [Skotlex] - } - } - } - mysql_free_result(sql_res); - strcat (t_msg, " friends"); - } - - if (save_log) - ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfuly! - - cp = idb_ensure(char_db_, char_id, create_charstatus); - memcpy(cp, p, sizeof(struct mmo_charstatus)); - return 1; -} - -// For quick selection of data when displaying the char menu. [Skotlex] -// -int mmo_char_fromsql_short(int char_id, struct mmo_charstatus *p){ - char t_msg[128]; - double exp; - memset(p, 0, sizeof(struct mmo_charstatus)); - t_msg[0]= '\0'; - - p->char_id = char_id; -// ShowInfo("Quick Char load request (%d)\n", char_id); - //`char`( `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, //9 - //`str`,`agi`,`vit`,`int`,`dex`,`luk`, //15 - //`max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, //21 - //`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`, //27 - //`hair`,`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, //35 - //`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`) - //splite 2 parts. cause veeeery long SQL syntax - - sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`," - "`str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point` FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR - - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sql_res = mysql_store_result(&mysql_handle); - - if (sql_res) { - sql_row = mysql_fetch_row(sql_res); - if (!sql_row) - { //Just how does this happens? [Skotlex] - ShowError("Requested non-existant character id: %d!\n", char_id); - mysql_free_result(sql_res); - return 0; - } - p->char_id = char_id; - p->account_id = atoi(sql_row[1]); - p->char_num = atoi(sql_row[2]); - strcpy(p->name, sql_row[3]); - p->class_ = atoi(sql_row[4]); - p->base_level = atoi(sql_row[5]); - p->job_level = atoi(sql_row[6]); - exp = atof(sql_row[7]); - p->base_exp = exp<0?0:(exp>UINT_MAX?UINT_MAX:(unsigned int)exp); - exp = atof(sql_row[8]); - p->job_exp = exp<0?0:(exp>UINT_MAX?UINT_MAX:(unsigned int)exp); - p->zeny = atoi(sql_row[9]); - p->str = atoi(sql_row[10]); - p->agi = atoi(sql_row[11]); - p->vit = atoi(sql_row[12]); - p->int_ = atoi(sql_row[13]); - p->dex = atoi(sql_row[14]); - p->luk = atoi(sql_row[15]); - p->max_hp = atoi(sql_row[16]); - p->hp = atoi(sql_row[17]); - p->max_sp = atoi(sql_row[18]); - p->sp = atoi(sql_row[19]); - p->status_point = atoi(sql_row[20]) > USHRT_MAX ? USHRT_MAX : atoi(sql_row[20]); - p->skill_point = atoi(sql_row[21]) > USHRT_MAX ? USHRT_MAX : atoi(sql_row[21]); - //free mysql result. - mysql_free_result(sql_res); - strcat (t_msg, " status"); - } else - ShowError("Load char failed (%d - table %s).\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!? - - sprintf(tmp_sql, "SELECT `option`,`karma`,`manner`,`hair`,`hair_color`," - "`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`" - "FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sql_res = mysql_store_result(&mysql_handle); - sql_row = sql_res?mysql_fetch_row(sql_res):NULL; - if (sql_row) { - - p->option = atoi(sql_row[0]); p->karma = atoi(sql_row[1]); p->manner = atoi(sql_row[2]); - p->hair = atoi(sql_row[3]); p->hair_color = atoi(sql_row[4]); p->clothes_color = atoi(sql_row[5]); - p->weapon = atoi(sql_row[6]); p->shield = atoi(sql_row[7]); - p->head_top = atoi(sql_row[8]); p->head_mid = atoi(sql_row[9]); p->head_bottom = atoi(sql_row[10]); - - strcat (t_msg, " status2"); - } else - ShowError("Char load failed (%d - table %s)\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!? - //free mysql result. - if (sql_res) - mysql_free_result(sql_res); -// if (save_log) //Too much spam :/ -// ShowInfo("Quick Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfuly! - - return 1; -} -//========================================================================================================== -int mmo_char_sql_init(void) { - ShowInfo("Begin Initializing.......\n"); - char_db_= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA, sizeof(int)); - // memory initialize - memset(&char_dat, 0, sizeof(struct mmo_charstatus)); - if(char_per_account == 0){ - ShowStatus("Chars per Account: 'Unlimited'.......\n"); - }else{ - ShowStatus("Chars per Account: '%d'.......\n", char_per_account); - } - - //the 'set offline' part is now in check_login_conn ... - //if the server connects to loginserver - //it will dc all off players - //and send the loginserver the new state.... - - // Force all users offline in sql when starting char-server - // (useful when servers crashs and don't clean the database) - set_all_offline_sql(); - - ShowInfo("Finished initilizing.......\n"); - - return 0; -} - -//========================================================================================================== - -int make_new_char_sql(int fd, unsigned char *dat) { - struct char_session_data *sd; - char name[NAME_LENGTH]; - char t_name[NAME_LENGTH*2]; - unsigned int i; // Used in for loop and comparing with strlen, safe to be unsigned. [Lance] - int char_id, temp; - - strncpy(name, dat, NAME_LENGTH); - name[NAME_LENGTH-1] = '\0'; //Always terminate string. - trim(name,TRIM_CHARS); //Trim character name. [Skotlex] - jstrescapecpy(t_name, name); - - // disabled until fixed >.> - // Note: escape characters should be added to jstrescape()! - //mysql_real_escape_string(&mysql_handle, t_name, t_name_temp, sizeof(t_name_temp)); - - if (!session_isValid(fd) || !(sd = (struct char_session_data*)session[fd]->session_data)) - return -2; - - ShowInfo("New character request (%d)\n", sd->account_id); - - //check name != main chat nick [LuzZza] - if(strcmpi(name, main_chat_nick) == 0) { - ShowInfo("Create char failed (%d): this nick (%s) reserved for mainchat messages.\n", sd->account_id, name); - return -2; - } - - //check for charcount (maxchars) :) - if(char_per_account != 0){ - sprintf(tmp_sql, "SELECT `account_id` FROM `%s` WHERE `account_id` = '%d'", char_db, sd->account_id); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - if(sql_res){ - //ok - temp = (int)mysql_num_rows(sql_res); - if(temp >= char_per_account){ - //hehe .. limit exceeded :P - ShowInfo("Create char failed (%d): charlimit exceeded.\n", sd->account_id); - mysql_free_result(sql_res); - return -2; - } - mysql_free_result(sql_res); - } - } - - // Check Authorised letters/symbols in the name of the character - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) - return -2; - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) - return -2; - } // else, all letters/symbols are authorised (except control char removed before) - - //check stat error - if ((dat[24]+dat[25]+dat[26]+dat[27]+dat[28]+dat[29]!=6*5 ) || // stats - (dat[30] >= 9) || // slots (dat[30] can not be negativ) - (dat[33] <= 0) || (dat[33] >= 24) || // hair style - (dat[31] >= 9)) { // hair color (dat[31] can not be negativ) - if (log_char) { - // char.log to charlog - sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" - "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - charlog_db,"make new char error", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); - //query - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id); - return -2; - } // for now we have checked: stat points used <31, char slot is less then 9, hair style/color values are acceptable - - // check individual stat value - for(i = 24; i <= 29; i++) { - if (dat[i] < 1 || dat[i] > 9) { - if (log_char) { - // char.log to charlog - sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" - "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - charlog_db,"make new char error", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); - //query - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id); - return -2; - } - } // now we know that every stat has proper value but we have to check if str/int agi/luk vit/dex pairs are correct - - if( ((dat[24]+dat[27]) > 10) || ((dat[25]+dat[29]) > 10) || ((dat[26]+dat[28]) > 10) ) { - if (log_char) { - // char.log to charlog - sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" - "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - charlog_db,"make new char error", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); - //query - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id); - return -2; - } // now when we have passed all stat checks - - if (log_char) { - // char.log to charlog - sprintf(tmp_sql,"INSERT INTO `%s`(`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" - "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - charlog_db,"make new char", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); - //query - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - //printf("make new char %d-%d %s %d, %d, %d, %d, %d, %d - %d, %d" RETCODE, - // fd, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); - - //Check Name (already in use?) - sprintf(tmp_sql, "SELECT 1 FROM `%s` WHERE `name` = '%s'",char_db, t_name); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return -2; - } - sql_res = mysql_store_result(&mysql_handle); - if(sql_res){ - temp = (int)mysql_num_rows(sql_res); - mysql_free_result(sql_res); - if (temp > 0) { - ShowInfo("Create char failed: charname already in use\n"); - return -1; - } - } - - // check char slot. - sprintf(tmp_sql, "SELECT `account_id`, `char_num` FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d'",char_db, sd->account_id, dat[30]); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - - if(sql_res){ - temp = (int)mysql_num_rows(sql_res); - mysql_free_result(sql_res); - if (temp > 0) { - ShowWarning("Create char failed (%d, slot: %d), slot already in use\n", sd->account_id, dat[30]); - return -2; - } - } - - //New Querys [Sirius] - //Insert the char to the 'chardb' ^^ - sprintf(tmp_sql, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `zeny`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`," - "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`) VALUES (" - "'%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')", - char_db, sd->account_id , dat[30] , t_name, start_zeny, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], - (40 * (100 + dat[26])/100) , (40 * (100 + dat[26])/100 ), (11 * (100 + dat[27])/100), (11 * (100 + dat[27])/100), dat[33], dat[31], - mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return -2; //No, stop the procedure! - } - //Now we need the charid from sql! - if(mysql_field_count(&mysql_handle) == 0 && - mysql_insert_id(&mysql_handle) > 0) - char_id = (int)mysql_insert_id(&mysql_handle); - else { - //delete the char ..(no trash in DB!) but how is this possible? - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d' AND `name` = '%s'", char_db, sd->account_id, dat[30], t_name); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - return -2; //XD end of the (World? :P) .. charcreate (denied) - } - //Give the char the default items - //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`) - if (start_weapon > 0) { //add Start Weapon (Knife?) - sprintf(tmp_sql,"INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `equip`, `identify`) VALUES ('%d', '%d', '%d', '%d', '%d')", inventory_db, char_id, start_weapon,1,0x02,1); - if (mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - if (start_armor > 0) { //Add default armor (cotton shirt?) - sprintf(tmp_sql,"INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `equip`, `identify`) VALUES ('%d', '%d', '%d', '%d', '%d')", inventory_db, char_id, start_armor,1,0x10,1); - if (mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s\n", sd->account_id, char_id, dat[30], name); - return char_id; -} - -/*----------------------------------------------------------------------------------------------------------*/ -/* Delete char - davidsiaw */ -/*----------------------------------------------------------------------------------------------------------*/ -/* Returns 0 if successful - * Returns < 0 for error - */ -int delete_char_sql(int char_id, int partner_id) -{ - char char_name[NAME_LENGTH], t_name[NAME_LENGTH*2]; //Name needs be escaped. - int account_id=0, party_id=0, guild_id=0, char_base_level=0; - - sprintf(tmp_sql, "SELECT `name`,`account_id`,`party_id`,`guild_id`,`base_level` FROM `%s` WHERE `char_id`='%d'",char_db, char_id); - - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sql_res = mysql_store_result(&mysql_handle); - - if(sql_res) - sql_row = mysql_fetch_row(sql_res); - - if (sql_res == NULL || sql_row == NULL) - { - ShowError("delete_char_sql: Unable to fetch character data, deletion aborted.\n"); - if (sql_res) - mysql_free_result(sql_res); - return -1; - } - strncpy(char_name, sql_row[0], NAME_LENGTH); - char_name[NAME_LENGTH-1] = '\0'; - jstrescapecpy(t_name, char_name); //Escape string for sql use... [Skotlex] - account_id = atoi(sql_row[1]); - party_id = atoi(sql_row[2]); - guild_id = atoi(sql_row[3]); - char_base_level = atoi(sql_row[4]); - mysql_free_result(sql_res); //Let's free this as soon as possible to avoid problems later on. - - //check for config char del condition [Lupus] - if( ( char_del_level > 0 && char_base_level >= char_del_level ) - || ( char_del_level < 0 && char_base_level <= -char_del_level ) - ) { - ShowInfo("Char deletion aborted: %s, BaseLevel: %i\n",char_name,char_base_level); - return -1; - } - - /* Divorce [Wizputer] */ - if (partner_id) { - sprintf(tmp_sql,"UPDATE `%s` SET `partner_id`='0' WHERE `char_id`='%d'",char_db,partner_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sprintf(tmp_sql,"DELETE FROM `%s` WHERE (`nameid`='%d' OR `nameid`='%d') AND `char_id`='%d'",inventory_db,WEDDING_RING_M,WEDDING_RING_F,partner_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - //Make the character leave the party [Skotlex] - if (party_id) - inter_party_leave(party_id, account_id, char_id); - - /* delete char's pet */ - //Delete the hatched pet if you have one... - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d' AND `incuvate` = '0'",pet_db, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - // Komurka's suggested way to clear pets, modified by [Skotlex] (because I always personalize what I do :X) - //Removing pets that are in the char's inventory.... - { //NOTE: The syntax for multi-table deletes is a bit changed between 4.0 and 4.1 regarding aliases, so we have to consider the version... [Skotlex] - //Since we only care about the major and minor version, a double conversion is good enough. (4.1.20 -> 4.10000) - double mysql_version = atof(mysql_get_server_info(&mysql_handle)); - - sprintf(tmp_sql, - "delete FROM `%s` USING `%s` as c LEFT JOIN `%s` as i ON c.char_id = i.char_id, `%s` as p WHERE c.char_id = '%d' AND i.card0 = -256 AND p.pet_id = (i.card1|(i.card2<<2))", - (mysql_version<4.1?pet_db:"p"), char_db, inventory_db, pet_db, char_id); - - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - //Removing pets that are in the char's cart.... - sprintf(tmp_sql, - "delete FROM `%s` USING `%s` as c LEFT JOIN `%s` as i ON c.char_id = i.char_id, `%s` as p WHERE c.char_id = '%d' AND i.card0 = -256 AND p.pet_id = (i.card1|(i.card2<<2))", - (mysql_version<4.1?pet_db:"p"), char_db, cart_db, pet_db, char_id); - - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - /* delete char's friends list */ - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `char_id` = '%d'",friend_db, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - /* delete char from other's friend list */ - //NOTE: Won't this cause problems for people who are already online? [Skotlex] - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `friend_id` = '%d'",friend_db, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - /* delete inventory */ - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",inventory_db, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - /* delete cart inventory */ - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",cart_db, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - /* delete memo areas */ - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",memo_db, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - /* delete character registry */ - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - /* delete skills */ - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",skill_db, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - -#ifdef ENABLE_SC_SAVING - /* status changes */ - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", - scdata_db, account_id, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } -#endif - - if (log_char) { - sprintf(tmp_sql,"INSERT INTO `%s`(`time`, `account_id`,`char_num`,`char_msg`,`name`) VALUES (NOW(), '%d', '%d', 'Deleted char (CID %d)', '%s')", - charlog_db, account_id, 0, char_id, t_name); - //query - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - /* delete character */ - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",char_db, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - /* No need as we used inter_guild_leave [Skotlex] - // Also delete info from guildtables. - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",guild_member_db, char_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - */ - - sprintf(tmp_sql, "SELECT `guild_id` FROM `%s` WHERE `master` = '%s'", guild_db, t_name); - - if (mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } else { - sql_res = mysql_store_result(&mysql_handle); - - if (sql_res == NULL) { - if (mysql_errno(&mysql_handle) != 0) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - return -1; - } else { - int rows = (int)mysql_num_rows(sql_res); - mysql_free_result(sql_res); - if (rows > 0) { - mapif_parse_BreakGuild(0,guild_id); - } - else if (guild_id) //Leave your guild. - inter_guild_leave(guild_id, account_id, char_id); - } - } - return 0; -} - -//========================================================================================================== - -int count_users(void) { - int i, users; - - if (login_fd > 0 && session[login_fd]){ - users = 0; - for(i = 0; i < MAX_MAP_SERVERS; i++) { - if (server_fd[i] > 0) { - users += server[i].users; - } - } - return users; - } - return 0; -} - -int mmo_char_send006b(int fd, struct char_session_data *sd) { - int i, j, found_num = 0; - struct mmo_charstatus *p = NULL; - const int offset = 24; - WFIFOHEAD(fd, offset +9*106); - - set_char_online(-1, 99,sd->account_id); - - //search char. - sprintf(tmp_sql, "SELECT `char_id` FROM `%s` WHERE `account_id` = '%d'",char_db, sd->account_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - found_num = (int)mysql_num_rows(sql_res); -// ShowInfo("number of chars: %d\n", found_num); - i = 0; - while((sql_row = mysql_fetch_row(sql_res))) { - sd->found_char[i] = atoi(sql_row[0]); - i++; - } - mysql_free_result(sql_res); - } - - for(i = found_num; i < 9; i++) - sd->found_char[i] = -1; - - memset(WFIFOP(fd, 0), 0, offset + found_num * 106); - WFIFOW(fd, 0) = 0x6b; - WFIFOW(fd, 2) = offset + found_num * 106; - - if (save_log) - ShowInfo("Loading Char Data ("CL_BOLD"%d"CL_RESET")\n",sd->account_id); - - for(i = 0; i < found_num; i++) { - mmo_char_fromsql_short(sd->found_char[i], &char_dat); - - p = &char_dat; - - j = offset + (i * 106); // increase speed of code - - WFIFOL(fd,j) = p->char_id; - WFIFOL(fd,j+4) = p->base_exp>LONG_MAX?LONG_MAX:p->base_exp; - WFIFOL(fd,j+8) = p->zeny; - WFIFOL(fd,j+12) = p->job_exp>LONG_MAX?LONG_MAX:p->job_exp; - WFIFOL(fd,j+16) = p->job_level; - - WFIFOL(fd,j+20) = 0; - WFIFOL(fd,j+24) = 0; - WFIFOL(fd,j+28) = p->option; - - WFIFOL(fd,j+32) = p->karma; - WFIFOL(fd,j+36) = p->manner; - - WFIFOW(fd,j+40) = (p->status_point > SHRT_MAX) ? SHRT_MAX : p->status_point; - WFIFOW(fd,j+42) = (p->hp > SHRT_MAX) ? SHRT_MAX : p->hp; - WFIFOW(fd,j+44) = (p->max_hp > SHRT_MAX) ? SHRT_MAX : p->max_hp; - WFIFOW(fd,j+46) = (p->sp > SHRT_MAX) ? SHRT_MAX : p->sp; - WFIFOW(fd,j+48) = (p->max_sp > SHRT_MAX) ? SHRT_MAX : p->max_sp; - WFIFOW(fd,j+50) = DEFAULT_WALK_SPEED; // p->speed; - WFIFOW(fd,j+52) = p->class_; - WFIFOW(fd,j+54) = p->hair; - WFIFOW(fd,j+56) = p->option&0x20?0:p->weapon; //When the weapon is sent and your option is riding, the client crashes on login!? - WFIFOW(fd,j+58) = p->base_level; - WFIFOW(fd,j+60) = (p->skill_point > SHRT_MAX) ? SHRT_MAX : p->skill_point; - WFIFOW(fd,j+62) = p->head_bottom; - WFIFOW(fd,j+64) = p->shield; - WFIFOW(fd,j+66) = p->head_top; - WFIFOW(fd,j+68) = p->head_mid; - WFIFOW(fd,j+70) = p->hair_color; - WFIFOW(fd,j+72) = p->clothes_color; - - memcpy(WFIFOP(fd,j+74), p->name, NAME_LENGTH); - - WFIFOB(fd,j+98) = (p->str > UCHAR_MAX) ? UCHAR_MAX : p->str; - WFIFOB(fd,j+99) = (p->agi > UCHAR_MAX) ? UCHAR_MAX : p->agi; - WFIFOB(fd,j+100) = (p->vit > UCHAR_MAX) ? UCHAR_MAX : p->vit; - WFIFOB(fd,j+101) = (p->int_ > UCHAR_MAX) ? UCHAR_MAX : p->int_; - WFIFOB(fd,j+102) = (p->dex > UCHAR_MAX) ? UCHAR_MAX : p->dex; - WFIFOB(fd,j+103) = (p->luk > UCHAR_MAX) ? UCHAR_MAX : p->luk; - WFIFOB(fd,j+104) = p->char_num; - } - - WFIFOSET(fd,WFIFOW(fd,2)); -// printf("mmo_char_send006b end..\n"); - return 0; -} - -int send_accounts_tologin(int tid, unsigned int tick, int id, int data); - -int parse_tologin(int fd) { - int i; - struct char_session_data *sd; - RFIFOHEAD(fd); - // only login-server can have an access to here. - // so, if it isn't the login-server, we disconnect the session. - //session eof check! - if(fd != login_fd) - session[fd]->eof = 1; - if(session[fd]->eof) { - if (fd == login_fd) { - ShowWarning("Connection to login-server lost (connection #%d).\n", fd); - login_fd = -1; - } - do_close(fd); - return 0; - } - - sd = (struct char_session_data*)session[fd]->session_data; - - // hehe. no need to set user limit on SQL version. :P - // but char limitation is good way to maintain server. :D - while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { -// printf("parse_tologin : %d %d %x\n", fd, RFIFOREST(fd), RFIFOW(fd, 0)); - - switch(RFIFOW(fd, 0)){ - case 0x2711: - if (RFIFOREST(fd) < 3) - return 0; - if (RFIFOB(fd, 2)) { - //printf("connect login server error : %d\n", RFIFOB(fd, 2)); - ShowError("Can not connect to login-server.\n"); - ShowError("The server communication passwords (default s1/p1) are probably invalid.\n"); - ShowError("Also, please make sure your login db has the correct coounication username/passwords and the gender of the account is S.\n"); - ShowError("The communication passwords are set in map_athena.conf and char_athena.conf\n"); - return 0; - //exit(1); //fixed for server shutdown. - }else { - ShowStatus("Connected to login-server (connection #%d).\n", fd); - - //Send online accounts to login server. - send_accounts_tologin(-1, gettick(), 0, 0); - - // if no map-server already connected, display a message... - for(i = 0; i < MAX_MAP_SERVERS; i++) - if (server_fd[i] > 0 && server[i].map[0]) // if map-server online and at least 1 map - break; - if (i == MAX_MAP_SERVERS) - ShowStatus("Awaiting maps from map-server.\n"); - } - RFIFOSKIP(fd, 3); - break; - - case 0x2713: - if(RFIFOREST(fd)<51) - return 0; - for(i = 0; i < fd_max; i++) { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2)) { - WFIFOHEAD(i,3); - if (RFIFOB(fd,6) != 0) { - WFIFOW(i,0) = 0x6c; - WFIFOB(i,2) = 0x42; - WFIFOSET(i,3); - } else if (max_connect_user == 0 || count_users() < max_connect_user) { -// if (max_connect_user == 0) -// printf("max_connect_user (unlimited) -> accepted.\n"); -// else -// printf("count_users(): %d < max_connect_user (%d) -> accepted.\n", count_users(), max_connect_user); - sd->connect_until_time = (time_t)RFIFOL(fd,47); - memcpy(sd->email, RFIFOP(fd, 7), 40); - // send characters to player - mmo_char_send006b(i, sd); - } else if(isGM(sd->account_id) >= gm_allow_level) { - sd->connect_until_time = (time_t)RFIFOL(fd,47); - memcpy(sd->email, RFIFOP(fd, 7), 40); - // send characters to player - mmo_char_send006b(i, sd); - } else { - // refuse connection: too much online players -// printf("count_users(): %d < max_connect_use (%d) -> fail...\n", count_users(), max_connect_user); - WFIFOW(i,0) = 0x6c; - WFIFOW(i,2) = 0; - WFIFOSET(i,3); - } - } - } - RFIFOSKIP(fd,51); - break; - - case 0x2717: - if (RFIFOREST(fd) < 50) - return 0; - for(i = 0; i < fd_max; i++) { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { - if (sd->account_id == RFIFOL(fd,2)) { - memcpy(sd->email, RFIFOP(fd, 6), 40); - sd->connect_until_time = (time_t)RFIFOL(fd,46); - break; - } - } - } - RFIFOSKIP(fd,50); - break; - - // login-server alive packet - case 0x2718: - if (RFIFOREST(fd) < 2) - return 0; - RFIFOSKIP(fd,2); - break; - - // Receiving authentification from Freya-type login server (to avoid char->login->char) - case 0x2719: - if (RFIFOREST(fd) < 18) - return 0; - // to conserv a maximum of authentification, search if account is already authentified and replace it - // that will reduce multiple connection too - for(i = 0; i < AUTH_FIFO_SIZE; i++) - if (auth_fifo[i].account_id == RFIFOL(fd,2)) - break; - // if not found, use next value - if (i == AUTH_FIFO_SIZE) { - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - i = auth_fifo_pos; - auth_fifo_pos++; - } - //printf("auth_fifo set (auth #%d) - account: %d, secure: %08x-%08x\n", i, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); - auth_fifo[i].account_id = RFIFOL(fd,2); - auth_fifo[i].char_id = 0; - auth_fifo[i].login_id1 = RFIFOL(fd,6); - auth_fifo[i].login_id2 = RFIFOL(fd,10); - auth_fifo[i].delflag = 2; // 0: auth_fifo canceled/void, 2: auth_fifo received from login/map server in memory, 1: connection authentified - auth_fifo[i].char_pos = 0; - auth_fifo[i].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) - auth_fifo[i].ip = RFIFOL(fd,14); - //auth_fifo[i].map_auth = 0; - RFIFOSKIP(fd,18); - break; - - case 0x2721: // gm reply. I don't want to support this function. - if (RFIFOREST(fd) < 10) - return 0; - RFIFOSKIP(fd, 10); -/* Note that this is the code from char-txt! Even uncommenting it will not work. - printf("0x2721:GM reply\n"); - { - int oldacc, newacc; - unsigned char buf[64]; - if (RFIFOREST(fd) < 10) - return 0; - oldacc = RFIFOL(fd, 2); - newacc = RFIFOL(fd, 6); - RFIFOSKIP(fd, 10); - if (newacc > 0) { - for(i=0;i map\n"); - } -*/ - break; - case 0x2723: // changesex reply (modified by [Yor]) - if (RFIFOREST(fd) < 7) - return 0; - { - int acc, sex; - unsigned char buf[16]; - MYSQL_RES* sql_res2; - acc = RFIFOL(fd,2); - sex = RFIFOB(fd,6); - RFIFOSKIP(fd, 7); - if (acc > 0) { - sprintf(tmp_sql, "SELECT `char_id`,`class`,`skill_point`,`guild_id` FROM `%s` WHERE `account_id` = '%d'",char_db, acc); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res2 = mysql_store_result(&mysql_handle); - - while(sql_res2 && (sql_row = mysql_fetch_row(sql_res2))) { - int char_id, guild_id, jobclass, skill_point, class_; - char_id = atoi(sql_row[0]); - jobclass = atoi(sql_row[1]); - skill_point = atoi(sql_row[2]); - guild_id = atoi(sql_row[3]); - class_ = jobclass; - if (jobclass == JOB_BARD || jobclass == JOB_DANCER || - jobclass == JOB_CLOWN || jobclass == JOB_GYPSY || - jobclass == JOB_BABY_BARD || jobclass == JOB_BABY_DANCER) { - // job modification - if (jobclass == JOB_BARD || jobclass == JOB_DANCER) { - class_ = (sex) ? JOB_BARD : JOB_DANCER; - } else if (jobclass == JOB_CLOWN || jobclass == JOB_GYPSY) { - class_ = (sex) ? JOB_CLOWN : JOB_GYPSY; - } else if (jobclass == JOB_BABY_BARD || jobclass == JOB_BABY_DANCER) { - class_ = (sex) ? JOB_BABY_BARD : JOB_BABY_DANCER; - } - // remove specifical skills of classes 19,20 4020,4021 and 4042,4043 - sprintf(tmp_sql, "SELECT `lv` FROM `%s` WHERE `char_id` = '%d' AND `id` >= '315' AND `id` <= '330'",skill_db, char_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - while(( sql_row = mysql_fetch_row(sql_res))) { - skill_point += atoi(sql_row[0]); - } - mysql_free_result(sql_res); - } - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `char_id` = '%d' AND `id` >= '315' AND `id` <= '330'",skill_db, char_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - // to avoid any problem with equipment and invalid sex, equipment is unequiped. - sprintf(tmp_sql, "UPDATE `%s` SET `equip` = '0' WHERE `char_id` = '%d'",inventory_db, char_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sprintf(tmp_sql, "UPDATE `%s` SET `class`='%d' , `skill_point`='%d' , `weapon`='0' , `shield`='0' , `head_top`='0' , `head_mid`='0' , `head_bottom`='0' WHERE `char_id` = '%d'",char_db, class_, skill_point, char_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - if (guild_id) //If there is a guild, update the guild_member data [Skotlex] - inter_guild_sex_changed(guild_id, acc, char_id, sex); - } - if (sql_res2) - mysql_free_result(sql_res2); - } - // disconnect player if online on char-server - for(i = 0; i < fd_max; i++) { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { - if (sd->account_id == acc) { - session[i]->eof = 1; - break; - } - } - } - - WBUFW(buf,0) = 0x2b0d; - WBUFL(buf,2) = acc; - WBUFB(buf,6) = sex; - - mapif_sendall(buf, 7); - } - break; - - // account_reg2変更通知 - case 0x2729: - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { //Receive account_reg2 registry, forward to map servers. - unsigned char buf[ACCOUNT_REG2_NUM*(256+32+2)+16]; - memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2)); -// WBUFW(buf,0) = 0x2b11; - WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex] - mapif_sendall(buf, WBUFW(buf,2)); - RFIFOSKIP(fd, RFIFOW(fd,2)); - } - break; - - // State change of account/ban notification (from login-server) by [Yor] - case 0x2731: - if (RFIFOREST(fd) < 11) - return 0; - // send to all map-servers to disconnect the player - { - unsigned char buf[16]; - WBUFW(buf,0) = 0x2b14; - WBUFL(buf,2) = RFIFOL(fd,2); - WBUFB(buf,6) = RFIFOB(fd,6); // 0: change of statut, 1: ban - WBUFL(buf,7) = RFIFOL(fd,7); // status or final date of a banishment - mapif_sendall(buf, 11); - } - // disconnect player if online on char-server - for(i = 0; i < fd_max; i++) { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { - if (sd->account_id == RFIFOL(fd,2)) { - session[i]->eof = 1; - break; - } - } - } - RFIFOSKIP(fd,11); - break; - - case 0x2732: - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - if(!char_gm_read) { - unsigned char buf[32000]; - if (gm_account != NULL) - aFree(gm_account); - gm_account = (struct gm_account*)aCalloc(sizeof(struct gm_account) * ((RFIFOW(fd,2) - 4) / 5), 1); - GM_num = 0; - for (i = 4; i < RFIFOW(fd,2); i = i + 5) { - gm_account[GM_num].account_id = RFIFOL(fd,i); - gm_account[GM_num].level = (int)RFIFOB(fd,i+4); - //printf("GM account: %d -> level %d\n", gm_account[GM_num].account_id, gm_account[GM_num].level); - GM_num++; - } - ShowStatus("From login-server: receiving information of %d GM accounts.\n", GM_num); - // send new gm acccounts level to map-servers - memcpy(buf, RFIFOP(fd,0), RFIFOW(fd,2)); - WBUFW(buf,0) = 0x2b15; - mapif_sendall(buf, RFIFOW(fd,2)); - } - RFIFOSKIP(fd,RFIFOW(fd,2)); - break; - - // Receive GM accounts [Freya login server packet by Yor] - case 0x2733: - // add test here to remember that the login-server is Freya-type - // sprintf (login_server_type, "Freya"); - if (RFIFOREST(fd) < 7) - return 0; - { - int new_level = 0; - for(i = 0; i < GM_num; i++) - if (gm_account[i].account_id == RFIFOL(fd,2)) { - if (gm_account[i].level != (int)RFIFOB(fd,6)) { - gm_account[i].level = (int)RFIFOB(fd,6); - new_level = 1; - } - break; - } - // if not found, add it - if (i == GM_num) { - // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???) - // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) - if (((int)RFIFOB(fd,6)) > 0 && GM_num < 4000) { - if (GM_num == 0) { - gm_account = (struct gm_account*)aMalloc(sizeof(struct gm_account)); - } else { - gm_account = (struct gm_account*)aRealloc(gm_account, sizeof(struct gm_account) * (GM_num + 1)); - } - gm_account[GM_num].account_id = RFIFOL(fd,2); - gm_account[GM_num].level = (int)RFIFOB(fd,6); - new_level = 1; - GM_num++; - if (GM_num >= 4000) - ShowWarning("4000 GM accounts found. Next GM accounts are not readed.\n"); - } - } - if (new_level == 1) { - ShowStatus("From login-server: receiving GM account information (%d: level %d).\n", RFIFOL(fd,2), (int)RFIFOB(fd,6)); - mapif_send_gmaccounts(); - - //create_online_files(); // not change online file for only 1 player (in next timer, that will be done - // send gm acccounts level to map-servers - } - } - RFIFOSKIP(fd,7); - break; - - //Login server request to kick a character out. [Skotlex] - case 0x2734: - if (RFIFOREST(fd) < 6) - return 0; - { - struct online_char_data* character; - int aid = RFIFOL(fd,2); - if ((character = idb_get(online_char_db, aid)) != NULL) - { //Kick out this player. - if (character->server > -1) - { //Kick it from the map server it is on. - mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); - if (!character->waiting_disconnect) - add_timer(gettick()+15000, chardb_waiting_disconnect, character->account_id, 0); - character->waiting_disconnect = 1; - } else { //Manual kick from char server. - struct char_session_data *tsd; - int i; - for(i = 0; i < fd_max; i++) { - if (session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid) - { - WFIFOHEAD(i,3); - WFIFOW(i,0) = 0x81; - WFIFOB(i,2) = 2; - WFIFOSET(i,3); - break; - } - } - if (i == fd_max) //Shouldn't happen, but just in case. - set_char_offline(99, aid); - } - } - RFIFOSKIP(fd,6); - } - break; - - case 0x2735: - { - unsigned char buf[2]; - in_addr_t new_ip = 0; - RFIFOSKIP(fd,2); - - WBUFW(buf,0) = 0x2b1e; - mapif_sendall(buf, 2); - - new_ip = resolve_hostbyname(login_ip_str, NULL, NULL); - if (new_ip && new_ip != login_ip) //Update login ip, too. - login_ip = new_ip; - - new_ip = resolve_hostbyname(char_ip_str, NULL, NULL); - if (new_ip && new_ip != char_ip) - { //Update ip. - char_ip = new_ip; - ShowInfo("Updating IP for [%s].\n",char_ip_str); - WFIFOHEAD(fd,6); - WFIFOW(fd,0) = 0x2736; - WFIFOL(fd,2) = char_ip; - WFIFOSET(fd,6); - } - break; - } - default: - ShowError("Unknown packet 0x%04x from login server, disconnecting.\n", RFIFOW(fd, 0)); - session[fd]->eof = 1; - return 0; - } - } - - RFIFOFLUSH(fd); - - return 0; -} - -int request_accreg2(int account_id, int char_id) { - if (login_fd > 0) { - WFIFOHEAD(login_fd, 10); - WFIFOW(login_fd, 0) = 0x272e; - WFIFOL(login_fd, 2) = account_id; - WFIFOL(login_fd, 6) = char_id; - WFIFOSET(login_fd, 10); - return 1; - } - return 0; -} -//Send packet forward to login-server for account saving -int save_accreg2(unsigned char* buf, int len) { - if (login_fd > 0) { - WFIFOHEAD(login_fd, len+4); - memcpy(WFIFOP(login_fd,4), buf, len); - WFIFOW(login_fd,0) = 0x2728; - WFIFOW(login_fd,2) = len+4; - WFIFOSET(login_fd,len+4); - return 1; - } - return 0; -} - -void char_read_fame_list(void) -{ - int i; - struct fame_list fame_item; - - // Empty ranking lists - memset(smith_fame_list, 0, sizeof(smith_fame_list)); - memset(chemist_fame_list, 0, sizeof(chemist_fame_list)); - memset(taekwon_fame_list, 0, sizeof(taekwon_fame_list)); - // Build Blacksmith ranking list - sprintf(tmp_sql, "SELECT `char_id`,`fame`, `name` FROM `%s` WHERE `fame`>0 AND (`class`='%d' OR `class`='%d' OR `class`='%d') ORDER BY `fame` DESC LIMIT 0,%d", char_db, JOB_BLACKSMITH, JOB_WHITESMITH, JOB_BABY_BLACKSMITH, fame_list_size_smith); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - i = 0; - while((sql_row = mysql_fetch_row(sql_res))) { - fame_item.id = atoi(sql_row[0]); - fame_item.fame = atoi(sql_row[1]); - strncpy(fame_item.name, sql_row[2], NAME_LENGTH); - memcpy(&smith_fame_list[i], &fame_item, sizeof(struct fame_list)); - - if (++i == fame_list_size_smith) - break; - } - mysql_free_result(sql_res); - } - // Build Alchemist ranking list - sprintf(tmp_sql, "SELECT `char_id`,`fame`, `name` FROM `%s` WHERE `fame`>0 AND (`class`='%d' OR `class`='%d' OR `class`='%d') ORDER BY `fame` DESC LIMIT 0,%d", char_db, JOB_ALCHEMIST, JOB_CREATOR, JOB_BABY_ALCHEMIST, fame_list_size_chemist); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - i = 0; - while((sql_row = mysql_fetch_row(sql_res))) { - fame_item.id = atoi(sql_row[0]); - fame_item.fame = atoi(sql_row[1]); - strncpy(fame_item.name, sql_row[2], NAME_LENGTH); - - memcpy(&chemist_fame_list[i], &fame_item, sizeof(struct fame_list)); - - if (++i == fame_list_size_chemist) - break; - } - mysql_free_result(sql_res); - } - // Build Taekwon ranking list - sprintf(tmp_sql, "SELECT `char_id`,`fame`, `name` FROM `%s` WHERE `fame`>0 AND (`class`='%d') ORDER BY `fame` DESC LIMIT 0,%d", char_db, JOB_TAEKWON, fame_list_size_taekwon); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - i = 0; - while((sql_row = mysql_fetch_row(sql_res))) { - fame_item.id = atoi(sql_row[0]); - fame_item.fame = atoi(sql_row[1]); - strncpy(fame_item.name, sql_row[2], NAME_LENGTH); - - memcpy(&taekwon_fame_list[i], &fame_item, sizeof(struct fame_list)); - - if (++i == fame_list_size_taekwon) - break; - } - mysql_free_result(sql_res); - } -} - -// Send map-servers the fame ranking lists -int char_send_fame_list(int fd) { - int i, len = 8; - unsigned char buf[32000]; - - WBUFW(buf,0) = 0x2b1b; - - for(i = 0; i < fame_list_size_smith && smith_fame_list[i].id; i++) { - memcpy(WBUFP(buf, len), &smith_fame_list[i], sizeof(struct fame_list)); - len += sizeof(struct fame_list); - } - // add blacksmith's block length - WBUFW(buf, 6) = len; - - for(i = 0; i < fame_list_size_chemist && chemist_fame_list[i].id; i++) { - memcpy(WBUFP(buf, len), &chemist_fame_list[i], sizeof(struct fame_list)); - len += sizeof(struct fame_list); - } - // add alchemist's block length - WBUFW(buf, 4) = len; - - for(i = 0; i < fame_list_size_taekwon && taekwon_fame_list[i].id; i++) { - memcpy(WBUFP(buf, len), &taekwon_fame_list[i], sizeof(struct fame_list)); - len += sizeof(struct fame_list); - } - // add total packet length - WBUFW(buf, 2) = len; - - if (fd != -1) - mapif_send(fd, buf, len); - else - mapif_sendall(buf, len); - return 0; -} - -int search_mapserver(unsigned short map, long ip, short port); - -//Loads a character's name and stores it in the buffer given (must be NAME_LENGTH in size) -//Returns 1 on found, 0 on not found (buffer is filled with Unknown char name) -int char_loadName(int char_id, char* name) -{ - sprintf(tmp_sql, "SELECT `name` FROM `%s` WHERE `char_id`='%d'", char_db, char_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sql_res = mysql_store_result(&mysql_handle); - sql_row = sql_res?mysql_fetch_row(sql_res):NULL; - - if (sql_row) - memcpy(name, sql_row[0], NAME_LENGTH); - else - memcpy(name, unknown_char_name, NAME_LENGTH); - if (sql_res) mysql_free_result(sql_res); - return sql_row?1:0; -} - - -int parse_frommap(int fd) { - int i = 0, j = 0; - int id; - RFIFOHEAD(fd); - - // Sometimes fd=0, and it will cause server crash. Don't know why. :( - if (fd <= 0) { - ShowError("parse_frommap error fd=%d\n", fd); - return 0; - } - - for(id = 0; id < MAX_MAP_SERVERS; id++) - if (server_fd[id] == fd) - break; - if(id == MAX_MAP_SERVERS) - session[fd]->eof = 1; - if(session[fd]->eof) { - if (id < MAX_MAP_SERVERS) { - unsigned char buf[16384]; - ShowStatus("Map-server %d (session #%d) has disconnected.\n", id, fd); - //Notify other map servers that this one is gone. [Skotlex] - WBUFW(buf,0) = 0x2b20; - WBUFL(buf,4) = server[id].ip; - WBUFW(buf,8) = server[id].port; - j = 0; - for(i = 0; i < MAX_MAP_PER_SERVER; i++) - if (server[id].map[i]) - WBUFW(buf,10+(j++)*4) = server[id].map[i]; - if (j > 0) { - WBUFW(buf,2) = j * 4 + 10; - mapif_sendallwos(fd, buf, WBUFW(buf,2)); - } - memset(&server[id], 0, sizeof(struct mmo_map_server)); - sprintf(tmp_sql, "DELETE FROM `ragsrvinfo` WHERE `index`='%d'", server_fd[id]); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - server_fd[id] = -1; - online_char_db->foreach(online_char_db,char_db_setoffline,id); //Tag relevant chars as 'in disconnected' server. - } - do_close(fd); - return 0; - } - - while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { - switch(RFIFOW(fd, 0)) { - - // map-server alive packet - case 0x2718: - RFIFOSKIP(fd,2); - break; - - case 0x2af7: - RFIFOSKIP(fd,2); - if(char_gm_read) //Re-read gm accounts. - read_gm_account(); - //Send to login request to reload gm accounts. - else if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 2); - WFIFOW(login_fd,0) = 0x2709; - WFIFOSET(login_fd, 2); - } - break; - - // mapserver -> map names recv. - case 0x2afa: - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { - unsigned char *p = (unsigned char *)&server[id].ip; - unsigned char buf[16384]; - int x; - WFIFOHEAD(fd,3+NAME_LENGTH); - - memset(server[id].map, 0, sizeof(server[id].map)); - j = 0; - for(i = 4; i < RFIFOW(fd,2); i += 4) { - server[id].map[j] = RFIFOW(fd,i); - j++; - } - - ShowStatus("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n", - id, j, p[0], p[1], p[2], p[3], server[id].port); - ShowStatus("Map-server %d loading complete.\n", id); - - if (max_account_id != DEFAULT_MAX_ACCOUNT_ID || max_char_id != DEFAULT_MAX_CHAR_ID) - mapif_send_maxid(max_account_id, max_char_id); //Send the current max ids to the server to keep in sync [Skotlex] - - WFIFOW(fd,0) = 0x2afb; - WFIFOB(fd,2) = 0; - // name for wisp to player - memcpy(WFIFOP(fd,3), wisp_server_name, NAME_LENGTH); - WFIFOSET(fd,3+NAME_LENGTH); - - char_send_fame_list(fd); //Send fame list. - - if (j == 0) - ShowWarning("Map-Server %d have NO maps.\n", id); - else { - // Transmitting maps information to the other map-servers - WBUFW(buf,0) = 0x2b04; - WBUFW(buf,2) = j * 4 + 10; - WBUFL(buf,4) = server[id].ip; - WBUFW(buf,8) = server[id].port; - memcpy(WBUFP(buf,10), RFIFOP(fd,4), j * 4); - mapif_sendallwos(fd, buf, WBUFW(buf,2)); - } - // Transmitting the maps of the other map-servers to the new map-server - for(x = 0; x < MAX_MAP_SERVERS; x++) { - if (server_fd[x] > 0 && x != id) { - WFIFOHEAD(fd, 10 +4*MAX_MAP_PER_SERVER); - WFIFOW(fd,0) = 0x2b04; - WFIFOL(fd,4) = server[x].ip; - WFIFOW(fd,8) = server[x].port; - j = 0; - for(i = 0; i < MAX_MAP_PER_SERVER; i++) - if (server[x].map[i]) - WFIFOW(fd,10+(j++)*4) = server[x].map[i]; - if (j > 0) { - WFIFOW(fd,2) = j * 4 + 10; - WFIFOSET(fd,WFIFOW(fd,2)); - } - } - } - RFIFOSKIP(fd,RFIFOW(fd,2)); - } - break; - //Packet command is now used for sc_data request. [Skotlex] - case 0x2afc: - if (RFIFOREST(fd) < 10) - return 0; - { - int aid, cid; - aid = RFIFOL(fd,2); - cid = RFIFOL(fd,6); - RFIFOSKIP(fd, 10); -#ifdef ENABLE_SC_SAVING - sprintf(tmp_sql, "SELECT type, tick, val1, val2, val3, val4 from `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", - scdata_db, aid, cid); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - break; - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - struct status_change_data data; - int count = 0; - WFIFOHEAD(fd, 14+50*sizeof(struct status_change_data)); - WFIFOW(fd, 0) = 0x2b1d; - WFIFOL(fd, 4) = aid; - WFIFOL(fd, 8) = cid; - while((sql_row = mysql_fetch_row(sql_res)) && count < 50) - { - data.type = atoi(sql_row[0]); - data.tick = atoi(sql_row[1]); - data.val1 = atoi(sql_row[2]); - data.val2 = atoi(sql_row[3]); - data.val3 = atoi(sql_row[4]); - data.val4 = atoi(sql_row[5]); - memcpy(WFIFOP(fd, 14+count*sizeof(struct status_change_data)), &data, sizeof(struct status_change_data)); - count++; - } - if (count >= 50) - ShowWarning("Too many status changes for %d:%d, some of them were not loaded.\n", aid, cid); - mysql_free_result(sql_res); - if (count > 0) - { - WFIFOW(fd, 2) = 14 + count*sizeof(struct status_change_data); - WFIFOW(fd, 12) = count; - WFIFOSET(fd, WFIFOW(fd,2)); - - //Clear the data once loaded. - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, aid, cid); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - } -#endif - break; - } - //set MAP user count - case 0x2afe: - if (RFIFOREST(fd) < 4) - return 0; - if (RFIFOW(fd,2) != server[id].users) { - server[id].users = RFIFOW(fd,2); - ShowInfo("User Count: %d (Server: %d)\n", server[id].users, id); - } - RFIFOSKIP(fd, 4); - break; - // set MAP user - case 0x2aff: - if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { - //TODO: When data mismatches memory, update guild/party online/offline states. - int i, aid, cid; - struct online_char_data* character; - - online_char_db->foreach(online_char_db,char_db_setoffline,id); //Set all chars from this server as 'unknown' - server[id].users = RFIFOW(fd,4); - for(i = 0; i < server[id].users; i++) { - aid = RFIFOL(fd,6+i*8); - cid = RFIFOL(fd,6+i*8+4); - character = idb_ensure(online_char_db, aid, create_online_char_data); - if (character->server > -1 && character->server != id) - { - ShowNotice("Set map user: Character (%d:%d) marked on map server %d, but map server %d claims to have (%d:%d) online!\n", - character->account_id, character->char_id, character->server, id, aid, cid); - mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); - } - character->server = id; - character->char_id = cid; - } - //If any chars remain in -2, they will be cleaned in the cleanup timer. - RFIFOSKIP(fd,RFIFOW(fd,2)); - break; - } - // char saving - case 0x2b01: - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { - int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8), size = RFIFOW(fd,2); - struct online_char_data* character; - if (size - 13 != sizeof(struct mmo_charstatus)) - { - ShowError("parse_from_map (save-char): Size mismatch! %d != %d\n", size-13, sizeof(struct mmo_charstatus)); - RFIFOSKIP(fd,size); - break; - } - //Check account only if this ain't final save. Final-save goes through because of the char-map reconnect - if (RFIFOB(fd,12) || ( - (character = idb_get(online_char_db, aid)) != NULL && - character->char_id == cid)) - { - memcpy(&char_dat, RFIFOP(fd,13), sizeof(struct mmo_charstatus)); - mmo_char_tosql(cid, &char_dat); - } else { //This may be valid on char-server reconnection, when re-sending characters that already logged off. - ShowError("parse_from_map (save-char): Received data for non-existant/offline character (%d:%d).\n", aid, cid); - set_char_online(id, cid, aid); - } - - if (RFIFOB(fd,12)) - { //Flag? Set character offline after saving [Skotlex] - set_char_offline(cid, aid); - WFIFOHEAD(fd, 10); - WFIFOW(fd, 0) = 0x2b21; //Save ack only needed on final save. - WFIFOL(fd, 2) = aid; - WFIFOL(fd, 6) = cid; - WFIFOSET(fd, 10); - } - RFIFOSKIP(fd,size); - break; - } - // req char selection - case 0x2b02: - if (RFIFOREST(fd) < 18) - return 0; - - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - - auth_fifo[auth_fifo_pos].account_id = RFIFOL(fd, 2); - auth_fifo[auth_fifo_pos].char_id = 0; - auth_fifo[auth_fifo_pos].login_id1 = RFIFOL(fd, 6); - auth_fifo[auth_fifo_pos].login_id2 = RFIFOL(fd,10); - auth_fifo[auth_fifo_pos].delflag = 2; - auth_fifo[auth_fifo_pos].char_pos = 0; - auth_fifo[auth_fifo_pos].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) - auth_fifo[auth_fifo_pos].ip = RFIFOL(fd,14); - auth_fifo_pos++; - { - WFIFOHEAD(fd, 7); - WFIFOW(fd, 0) = 0x2b03; - WFIFOL(fd, 2) = RFIFOL(fd, 2); - WFIFOB(fd, 6) = 0; - WFIFOSET(fd, 7); - } - RFIFOSKIP(fd, 18); - break; - - // request "change map server" - case 0x2b05: - if (RFIFOREST(fd) < 35) - return 0; - { - unsigned short name; - int map_id, map_fd = -1; - struct online_char_data* data; - struct mmo_charstatus* char_data; - name = RFIFOW(fd,18); - map_id = search_mapserver(name, RFIFOL(fd,24), RFIFOW(fd,28)); //Locate mapserver by ip and port. - if (map_id >= 0) - map_fd = server_fd[map_id]; - //Char should just had been saved before this packet, so this should be safe. [Skotlex] - char_data = uidb_get(char_db_,RFIFOL(fd,14)); - if (char_data == NULL) - { //Really shouldn't happen. - mmo_char_fromsql(RFIFOL(fd,14), &char_dat); - char_data = &char_dat; - } - //Tell the new map server about this player using Kevin's new auth packet. [Skotlex] - if (map_fd>=0 && session[map_fd] && char_data) - { //Send the map server the auth of this player. - //Update the "last map" as this is where the player must be spawned on the new map server. - WFIFOHEAD(fd, 30); - WFIFOHEAD(map_fd, 20 + sizeof(struct mmo_charstatus)); - char_data->last_point.map = RFIFOW(fd,18); - char_data->last_point.x = RFIFOW(fd,20); - char_data->last_point.y = RFIFOW(fd,22); - char_data->sex = RFIFOB(fd,30); // Buuyo^ - - WFIFOW(map_fd,0) = 0x2afd; - WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus); - WFIFOL(map_fd,4) = RFIFOL(fd, 2); //Account ID - WFIFOL(map_fd,8) = RFIFOL(fd, 6); //Login1 - WFIFOL(map_fd,16) = RFIFOL(fd,10); //Login2 - WFIFOL(map_fd,12) = (unsigned long)0; //TODO: connect_until_time, how do I figure it out right now? - memcpy(WFIFOP(map_fd,20), char_data, sizeof(struct mmo_charstatus)); - WFIFOSET(map_fd, WFIFOW(map_fd,2)); - data = idb_ensure(online_char_db, RFIFOL(fd, 2), create_online_char_data); - data->char_id = char_data->char_id; - data->server = map_id; //Update server where char is. - - //Reply with an ack. - WFIFOW(fd, 0) = 0x2b06; - memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28); - WFIFOSET(fd, 30); - } else { //Reply with nak - WFIFOHEAD(fd, 30); - WFIFOW(fd, 0) = 0x2b06; - memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28); - WFIFOL(fd, 6) = 0; //Set login1 to 0. - WFIFOSET(fd, 30); - } - RFIFOSKIP(fd, 35); - } - break; - - // char name check - case 0x2b08: - if (RFIFOREST(fd) < 6) - return 0; - { - char name[NAME_LENGTH]; - WFIFOHEAD(fd,30); - char_loadName((int)RFIFOL(fd,2), name); - WFIFOW(fd,0) = 0x2b09; - WFIFOL(fd,2) = RFIFOL(fd,2); - memcpy(WFIFOP(fd,6), name, NAME_LENGTH); - WFIFOSET(fd,30); - RFIFOSKIP(fd,6); - } - break; - - // I want become GM - fuck! - case 0x2b0a: - if(RFIFOREST(fd)<4) - return 0; - if(RFIFOREST(fd) login %d %s %d\n", RFIFOL(fd, 4), RFIFOP(fd, 8), RFIFOW(fd, 2)); - */ - ShowWarning("packet 0x2ba (become GM) is not supported by the Char-Server.\n"); - RFIFOSKIP(fd, RFIFOW(fd, 2)); - break; - - // Map server send information to change an email of an account -> login-server - case 0x2b0c: - if (RFIFOREST(fd) < 86) - return 0; - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 86); - memcpy(WFIFOP(login_fd,0), RFIFOP(fd,0), 86); // 0x2722 .L .40B .40B - WFIFOW(login_fd,0) = 0x2722; - WFIFOSET(login_fd, 86); - } - RFIFOSKIP(fd, 86); - break; - - // Receiving from map-server a status change resquest. Transmission to login-server (by Yor) - case 0x2b0e: - if (RFIFOREST(fd) < 44) - return 0; - { - char character_name[NAME_LENGTH], t_name[NAME_LENGTH*2]; - int acc = RFIFOL(fd,2); // account_id of who ask (-1 if nobody) - WFIFOHEAD(fd, 34); - memcpy(character_name, RFIFOP(fd,6), NAME_LENGTH); - character_name[NAME_LENGTH-1] = '\0'; - jstrescapecpy(t_name, character_name); //Escape string for sql use... [Skotlex] - // prepare answer - WFIFOW(fd,0) = 0x2b0f; // answer - WFIFOL(fd,2) = acc; // who want do operation - WFIFOW(fd,30) = RFIFOW(fd, 30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban - sprintf(tmp_sql, "SELECT `account_id`,`name` FROM `%s` WHERE `name` = '%s'",char_db, t_name); - - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sql_res = mysql_store_result(&mysql_handle); - - if (sql_res) { - if (mysql_num_rows(sql_res)) { - sql_row = mysql_fetch_row(sql_res); - memcpy(WFIFOP(fd,6), sql_row[1], NAME_LENGTH); // put correct name if found - WFIFOW(fd,32) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - switch(RFIFOW(fd, 30)) { - case 1: // block - if (acc == -1 || isGM(acc) >= isGM(atoi(sql_row[0]))) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 10); - WFIFOW(login_fd,0) = 0x2724; - WFIFOL(login_fd,2) = atoi(sql_row[0]); // account value - WFIFOL(login_fd,6) = 5; // status of the account - WFIFOSET(login_fd, 10); - } else - WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } else - WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 2: // ban - if (acc == -1 || isGM(acc) >= isGM(atoi(sql_row[0]))) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 18); - WFIFOW(login_fd, 0) = 0x2725; - WFIFOL(login_fd, 2) = atoi(sql_row[0]); // account value - WFIFOW(login_fd, 6) = RFIFOW(fd,32); // year - WFIFOW(login_fd, 8) = RFIFOW(fd,34); // month - WFIFOW(login_fd,10) = RFIFOW(fd,36); // day - WFIFOW(login_fd,12) = RFIFOW(fd,38); // hour - WFIFOW(login_fd,14) = RFIFOW(fd,40); // minute - WFIFOW(login_fd,16) = RFIFOW(fd,42); // second - WFIFOSET(login_fd,18); - } else - WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } else - WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 3: // unblock - if (acc == -1 || isGM(acc) >= isGM(atoi(sql_row[0]))) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 10); - WFIFOW(login_fd,0) = 0x2724; - WFIFOL(login_fd,2) = atoi(sql_row[0]); // account value - WFIFOL(login_fd,6) = 0; // status of the account - WFIFOSET(login_fd, 10); - } else - WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } else - WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 4: // unban - if (acc == -1 || isGM(acc) >= isGM(atoi(sql_row[0]))) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 6); - WFIFOW(login_fd, 0) = 0x272a; - WFIFOL(login_fd, 2) = atoi(sql_row[0]); // account value - WFIFOSET(login_fd, 6); - } else - WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } else - WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - case 5: // changesex - if (acc == -1 || isGM(acc) >= isGM(atoi(sql_row[0]))) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd, 6); - WFIFOW(login_fd, 0) = 0x2727; - WFIFOL(login_fd, 2) = atoi(sql_row[0]); // account value - WFIFOSET(login_fd, 6); - } else - WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } else - WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - break; - } - } else { - // character name not found - memcpy(WFIFOP(fd,6), character_name, NAME_LENGTH); - WFIFOW(fd,32) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline - } - // send answer if a player ask, not if the server ask - if (acc != -1) { - WFIFOSET(fd, 34); - } - mysql_free_result(sql_res); - } - } - RFIFOSKIP(fd, 44); - break; - -// case 0x2b0f: Not used anymore, available for future use - - // Update and send fame ranking list [DracoRPG] - case 0x2b10: - if (RFIFOREST(fd) < 12) - return 0; - { - int cid = RFIFOL(fd, 2); - int fame = RFIFOL(fd, 6); - char type = RFIFOB(fd, 10); - char pos = RFIFOB(fd, 11); - int size = 0; - struct fame_list *list = NULL; - RFIFOSKIP(fd,12); - - switch(type) { - case 1: - size = fame_list_size_smith; - list = smith_fame_list; - break; - case 2: - size = fame_list_size_chemist; - list = chemist_fame_list; - break; - case 3: - size = fame_list_size_taekwon; - list = taekwon_fame_list; - break; - } - if(!size) break; //No list. - if(pos) - { - pos--; //Convert from pos to index. - if( - (pos == 0 || fame < list[pos-1].fame) && - (pos == size-1 || fame > list[pos+1].fame) - ) { //No change in order. - list[(int)pos].fame = fame; - char_send_fame_list(fd); - break; - } - // If the player's already in the list, remove the entry and shift the following ones 1 step up - memmove(list+pos, list+pos+1, (size-pos-1) * sizeof(struct fame_list)); - //Clear out last entry. - list[size-1].id = 0; - list[size-1].fame = 0; - } - - // Find the position where the player has to be inserted - for(i = 0; i < size && fame < list[i].fame; i++); - if(i>=size) break; //Out of ranking. - // When found someone with less or as much fame, insert just above - memmove(list+i+1, list+i, (size-i-1) * sizeof(struct fame_list)); - list[i].id = cid; - list[i].fame = fame; - // Look for the player's name - char_loadName(list[i].id, list[i].name); - char_send_fame_list(-1); - } - - break; - - // Receive rates [Wizputer] - case 0x2b16: - if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,8)) - return 0; - { - char motd[256], t_name[512]; //Required for jstrescapecpy [Skotlex] - strncpy(motd, RFIFOP(fd,10), 255); //First copy it to make sure the motd fits. - motd[255]='\0'; - jstrescapecpy(t_name,motd); - - sprintf(tmp_sql, "INSERT INTO `ragsrvinfo` SET `index`='%d',`name`='%s',`exp`='%d',`jexp`='%d',`drop`='%d',`motd`='%s'", - fd, server_name, RFIFOW(fd,2), RFIFOW(fd,4), RFIFOW(fd,6), t_name); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - RFIFOSKIP(fd,RFIFOW(fd,8)); - break; - } - - // Character disconnected set online 0 [Wizputer] - case 0x2b17: - if (RFIFOREST(fd) < 6 ) - return 0; - //printf("Setting %d char offline\n",RFIFOL(fd,2)); - set_char_offline(RFIFOL(fd,2),RFIFOL(fd,6)); - RFIFOSKIP(fd,10); - break; - // Reset all chars to offline [Wizputer] - case 0x2b18: - set_all_offline(id); - RFIFOSKIP(fd,2); - break; - // Character set online [Wizputer] - case 0x2b19: - if (RFIFOREST(fd) < 6 ) - return 0; - set_char_online(id, RFIFOL(fd,2),RFIFOL(fd,6)); - RFIFOSKIP(fd,10); - break; - - // Build and send fame ranking lists [DracoRPG] - case 0x2b1a: - if (RFIFOREST(fd) < 2) - return 0; - char_read_fame_list(); - char_send_fame_list(-1); - RFIFOSKIP(fd,2); - break; - - //Request saving sc_data of a player. [Skotlex] - case 0x2b1c: - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { -#ifdef ENABLE_SC_SAVING - int count, aid, cid, i; - struct status_change_data data; - char *p = tmp_sql; - - aid = RFIFOL(fd, 4); - cid = RFIFOL(fd, 8); - count = RFIFOW(fd, 12); - - p+= sprintf(p, "INSERT INTO `%s` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ", scdata_db); - - for (i = 0; i < count; i++) - { - memcpy (&data, RFIFOP(fd, 14+i*sizeof(struct status_change_data)), sizeof(struct status_change_data)); - p += sprintf (p, " ('%d','%d','%hu','%d','%d','%d','%d','%d'),", aid, cid, - data.type, data.tick, data.val1, data.val2, data.val3, data.val4); - } - if (count > 0) - { - *--p = '\0'; //Remove final comma. - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } -#endif - RFIFOSKIP(fd, RFIFOW(fd, 2)); - break; - } - - case 0x2736: - if (RFIFOREST(fd) < 6) return 0; - ShowInfo("Updated IP address of Server #%d to %d.%d.%d.%d.\n",id, - (int)RFIFOB(fd,2),(int)RFIFOB(fd,3), - (int)RFIFOB(fd,4),(int)RFIFOB(fd,5)); - server[id].ip = RFIFOL(fd, 2); - RFIFOSKIP(fd,6); - break; - - default: - // inter server - packet - { - int r = inter_parse_frommap(fd); - if (r == 1) break; // processed - if (r == 2) return 0; // need more packet - } - - // no inter server packet. no char server packet -> disconnect - ShowError("Unknown packet 0x%04x from map server, disconnecting.\n", RFIFOW(fd,0)); - session[fd]->eof = 1; - return 0; - } - } - return 0; -} - -int search_mapserver(unsigned short map, long ip, short port) { - int i, j; - - if (!map) - return -1; - - for(i = 0; i < MAX_MAP_SERVERS; i++) - if (server_fd[i] > 0) - for (j = 0; server[i].map[j]; j++) - if (server[i].map[j] == map) { - if (ip > 0 && server[i].ip != ip) - continue; - if (port > 0 && server[i].port != port) - continue; - return i; - } - - return -1; -} - -int char_mapif_init(int fd) { - return inter_mapif_init(fd); -} - -//-------------------------------------------- -// Test to know if an IP come from LAN or WAN. -// Rewrote: Adnvanced subnet check [LuzZza] -//-------------------------------------------- -int lan_subnetcheck(long *p) { - - int i; - unsigned char *sbn, *msk, *src = (unsigned char *)p; - - for(i=0; iclient_addr.sin_addr; - long subnet_map_ip; - RFIFOHEAD(fd); - - sd = (struct char_session_data*)session[fd]->session_data; - - if(login_fd < 0) - session[fd]->eof = 1; - if(session[fd]->eof) { - if (fd == login_fd) - login_fd = -1; - if (sd != NULL) - { - struct online_char_data* data = idb_get(online_char_db, sd->account_id); - if (!data || data->server== -1) //If it is not in any server, send it offline. [Skotlex] - set_char_offline(99,sd->account_id); - } - do_close(fd); - return 0; - } - - while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { - cmd = RFIFOW(fd,0); - // crc32のスキップ用 - if( sd==NULL && // 未ログインor管理パケット - RFIFOREST(fd)>=4 && // 最低バイト数制限 & 0x7530,0x7532管理パケ除去 - RFIFOREST(fd)<=21 && // 最大バイト数制限 & サーバーログイン除去 - cmd!=0x20b && // md5通知パケット除去 - (RFIFOREST(fd)<6 || RFIFOW(fd,4)==0x65) ){ // 次に何かパケットが来てるなら、接続でないとだめ - RFIFOSKIP(fd,4); - cmd = RFIFOW(fd,0); - ShowDebug("parse_char : %d crc32 skipped\n",fd); - if(RFIFOREST(fd)==0) - return 0; - } - -//For use in packets that depend on an sd being present [Skotlex] -#define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL) { RFIFOSKIP(fd,rest); return 0; } } - - switch(cmd){ - case 0x20b: //20040622 encryption ragexe correspondence - if (RFIFOREST(fd) < 19) - return 0; - RFIFOSKIP(fd,19); - break; - - case 0x65: // request to connect - ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOL(fd, 10)); - if (RFIFOREST(fd) < 17) - return 0; - { - WFIFOHEAD(fd, 4); - - if (sd) { - //Received again auth packet for already authentified account?? Discard it. - //TODO: Perhaps log this as a hack attempt? - RFIFOSKIP(fd,17); - break; - } - CREATE(session[fd]->session_data, struct char_session_data, 1); - sd = (struct char_session_data*)session[fd]->session_data; - sd->connect_until_time = 0; // unknow or illimited (not displaying on map-server) - sd->account_id = RFIFOL(fd, 2); - sd->login_id1 = RFIFOL(fd, 6); - sd->login_id2 = RFIFOL(fd, 10); - sd->sex = RFIFOB(fd, 16); - - WFIFOL(fd, 0) = RFIFOL(fd, 2); - WFIFOSET(fd, 4); - - for(i = 0; i < AUTH_FIFO_SIZE; i++) { - if (auth_fifo[i].account_id == sd->account_id && - auth_fifo[i].login_id1 == sd->login_id1 && -#if CMP_AUTHFIFO_LOGIN2 != 0 - auth_fifo[i].login_id2 == sd->login_id2 && // relate to the versions higher than 18 -#endif - (!check_ip_flag || auth_fifo[i].ip == session[fd]->client_addr.sin_addr.s_addr) && - auth_fifo[i].delflag == 2) { - auth_fifo[i].delflag = 1; - - if (online_check) - { // check if character is not online already. [Skotlex] - struct online_char_data* character; - character = idb_get(online_char_db, sd->account_id); - - if (character) - { - if (character->server > -1) - { //Character already online. KICK KICK KICK - mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); - if (!character->waiting_disconnect) - add_timer(gettick()+20000, chardb_waiting_disconnect, character->account_id, 0); - character->waiting_disconnect = 1; - /* Not a good idea because this would trigger when you do a char-change from the map server! [Skotlex] - } else { //Kick from char server. - struct char_session_data *tsd; - int i; - for(i = 0; i < fd_max; i++) { - if (session[i] && i != fd && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == sd->account_id) - { - WFIFOW(i,0) = 0x81; - WFIFOB(i,2) = 2; - WFIFOSET(i,3); - break; - } - if (i == fd_max) //Shouldn't happen, but just in case. - set_char_offline(99, sd->account_id); - } - */ - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 8; - WFIFOSET(fd,3); - break; - } - } - } - - if (max_connect_user == 0 || count_users() < max_connect_user) { - if (login_fd > 0) { // don't send request if no login-server - // request to login-server to obtain e-mail/time limit - WFIFOHEAD(login_fd, 6); - WFIFOW(login_fd,0) = 0x2716; - WFIFOL(login_fd,2) = sd->account_id; - WFIFOSET(login_fd,6); - } - // send characters to player - mmo_char_send006b(fd, sd); - } else { - // refuse connection (over populated) - WFIFOW(fd,0) = 0x6c; - WFIFOW(fd,2) = 0; - WFIFOSET(fd,3); - } -// printf("connection request> set delflag 1(o:2)- account_id:%d/login_id1:%d(fifo_id:%d)\n", sd->account_id, sd->login_id1, i); - break; - } - } - if (i == AUTH_FIFO_SIZE) { - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd,19); - WFIFOW(login_fd,0) = 0x2712; // ask login-server to authentify an account - WFIFOL(login_fd,2) = sd->account_id; - WFIFOL(login_fd,6) = sd->login_id1; - WFIFOL(login_fd,10) = sd->login_id2; - WFIFOB(login_fd,14) = sd->sex; - WFIFOL(login_fd,15) = session[fd]->client_addr.sin_addr.s_addr; - WFIFOSET(login_fd,19); - } else { // if no login-server, we must refuse connection - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x6c; - WFIFOB(fd,2) = 0; - WFIFOSET(fd,3); - } - } - } - RFIFOSKIP(fd, 17); - break; - - case 0x66: // char select - FIFOSD_CHECK(3); - - sprintf(tmp_sql, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d'",char_db, sd->account_id, RFIFOB(fd, 2)); - RFIFOSKIP(fd, 3); - - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - sql_row = sql_res?mysql_fetch_row(sql_res):NULL; - - if (sql_row) - { - int char_id = atoi(sql_row[0]); - mysql_free_result(sql_res); //Free'd as soon as possible - mmo_char_fromsql(char_id, &char_dat); - char_dat.sex = sd->sex; - } else { - mysql_free_result(sql_res); - break; - } - - if (log_char) { - char escaped_name[NAME_LENGTH*2]; - sprintf(tmp_sql,"INSERT INTO `%s`(`time`, `account_id`,`char_num`,`name`) VALUES (NOW(), '%d', '%d', '%s')", - charlog_db, sd->account_id, RFIFOB(fd, 2), jstrescapecpy(escaped_name, char_dat.name)); - //query - if(mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - ShowInfo("Selected char: (Account %d: %d - %s)" RETCODE, sd->account_id, RFIFOB(fd, 2), char_dat.name); - - i = search_mapserver(char_dat.last_point.map, -1, -1); - - // if map is not found, we check major cities - if (i < 0) { - unsigned short j; - //First check that there's actually a map server online. - for(j = 0; j < MAX_MAP_SERVERS; j++) - if (server_fd[j] >= 0 && server[j].map[0]) - break; - if (j == MAX_MAP_SERVERS) { - ShowInfo("Connection Closed. No map servers available.\n"); - WFIFOHEAD(fd, 3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - break; - } - if ((i = search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) { - char_dat.last_point.x = 273; - char_dat.last_point.y = 354; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) { - char_dat.last_point.x = 120; - char_dat.last_point.y = 100; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) { - char_dat.last_point.x = 160; - char_dat.last_point.y = 94; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) { - char_dat.last_point.x = 116; - char_dat.last_point.y = 57; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) { - char_dat.last_point.x = 87; - char_dat.last_point.y = 117; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) { - char_dat.last_point.x = 94; - char_dat.last_point.y = 103; - } else { - ShowInfo("Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", mapindex_id2name(char_dat.last_point.map)); - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - break; - } - ShowWarning("Unable to find map-server for '%s', sending to major city '%s'.\n", mapindex_id2name(char_dat.last_point.map), mapindex_id2name(j)); - char_dat.last_point.map = j; - } - { //Send player to map. - WFIFOHEAD(fd,28); - WFIFOW(fd, 0) =0x71; - WFIFOL(fd, 2) =char_dat.char_id; - memcpy(WFIFOP(fd,6), mapindex_id2name(char_dat.last_point.map), MAP_NAME_LENGTH); - - // Advanced subnet check [LuzZza] - if((subnet_map_ip = lan_subnetcheck((long *)p))) - WFIFOL(fd,22) = subnet_map_ip; - else - WFIFOL(fd,22) = server[i].ip; - - WFIFOW(fd,26) = server[i].port; - WFIFOSET(fd,28); - } - if (auth_fifo_pos >= AUTH_FIFO_SIZE) { - auth_fifo_pos = 0; - } - auth_fifo[auth_fifo_pos].account_id = sd->account_id; - auth_fifo[auth_fifo_pos].char_id = char_dat.char_id; - auth_fifo[auth_fifo_pos].login_id1 = sd->login_id1; - auth_fifo[auth_fifo_pos].login_id2 = sd->login_id2; - auth_fifo[auth_fifo_pos].delflag = 0; - auth_fifo[auth_fifo_pos].char_pos = 0; - auth_fifo[auth_fifo_pos].sex = sd->sex; - auth_fifo[auth_fifo_pos].connect_until_time = sd->connect_until_time; - auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr.sin_addr.s_addr; - - //Send NEW auth packet [Kevin] - if ((map_fd = server_fd[i]) < 1 || session[map_fd] == NULL) - { - ShowError("parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i); - server_fd[i] = -1; - memset(&server[i], 0, sizeof(struct mmo_map_server)); - break; - } - { //Send auth ok to map server - WFIFOHEAD(map_fd, 20 + sizeof(struct mmo_charstatus)); - WFIFOW(map_fd,0) = 0x2afd; - WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus); - WFIFOL(map_fd,4) = auth_fifo[auth_fifo_pos].account_id; - WFIFOL(map_fd,8) = auth_fifo[auth_fifo_pos].login_id1; - WFIFOL(map_fd,16) = auth_fifo[auth_fifo_pos].login_id2; - WFIFOL(map_fd,12) = (unsigned long)auth_fifo[auth_fifo_pos].connect_until_time; - memcpy(WFIFOP(map_fd,20), &char_dat, sizeof(struct mmo_charstatus)); - WFIFOSET(map_fd, WFIFOW(map_fd,2)); - } - - set_char_online(i, auth_fifo[auth_fifo_pos].char_id, auth_fifo[auth_fifo_pos].account_id); - //Checks to see if the even share setting of the party must be broken. - inter_party_logged(char_dat.party_id, char_dat.account_id, char_dat.char_id); - auth_fifo_pos++; - break; - - case 0x67: // make new - FIFOSD_CHECK(37); - - if(char_new == 0) //turn character creation on/off [Kevin] - i = -2; - else - i = make_new_char_sql(fd, RFIFOP(fd, 2)); - - //'Charname already exists' (-1), 'Char creation denied' (-2) - //And 'You are underaged' (-3) (XD) [Sirius] - if (i < 0) - { - WFIFOHEAD(fd, 3); - WFIFOW(fd, 0) = 0x6e; - switch (i) { - case -1: WFIFOB(fd, 2) = 0x00; break; - case -2: WFIFOB(fd, 2) = 0x02; break; - case -3: WFIFOB(fd, 2) = 0x01; break; - } - WFIFOSET(fd, 3); - RFIFOSKIP(fd, 37); - break; - } - { //Send data. - WFIFOHEAD(fd, 108); - WFIFOW(fd, 0) = 0x6d; - memset(WFIFOP(fd, 2), 0x00, 106); - - mmo_char_fromsql_short(i, &char_dat); //Only the short data is needed. - WFIFOL(fd, 2) = char_dat.char_id; - WFIFOL(fd,2+4) = char_dat.base_exp>LONG_MAX?LONG_MAX:char_dat.base_exp; - WFIFOL(fd,2+8) = char_dat.zeny; - WFIFOL(fd,2+12) = char_dat.job_exp>LONG_MAX?LONG_MAX:char_dat.job_exp; - WFIFOL(fd,2+16) = char_dat.job_level; - - WFIFOL(fd,2+28) = char_dat.karma; - WFIFOL(fd,2+32) = char_dat.manner; - - WFIFOW(fd,2+40) = 0x30; - WFIFOW(fd,2+42) = (char_dat.hp > SHRT_MAX) ? SHRT_MAX : char_dat.hp; - WFIFOW(fd,2+44) = (char_dat.max_hp > SHRT_MAX) ? SHRT_MAX : char_dat.max_hp; - WFIFOW(fd,2+46) = (char_dat.sp > SHRT_MAX) ? SHRT_MAX : char_dat.sp; - WFIFOW(fd,2+48) = (char_dat.max_sp > SHRT_MAX) ? SHRT_MAX : char_dat.max_sp; - WFIFOW(fd,2+50) = DEFAULT_WALK_SPEED; // char_dat[i].speed; - WFIFOW(fd,2+52) = char_dat.class_; - WFIFOW(fd,2+54) = char_dat.hair; - - WFIFOW(fd,2+58) = char_dat.base_level; - WFIFOW(fd,2+60) = (char_dat.skill_point > SHRT_MAX) ? SHRT_MAX : char_dat.skill_point; - - WFIFOW(fd,2+64) = char_dat.shield; - WFIFOW(fd,2+66) = char_dat.head_top; - WFIFOW(fd,2+68) = char_dat.head_mid; - WFIFOW(fd,2+70) = char_dat.hair_color; - - memcpy(WFIFOP(fd,2+74), char_dat.name, NAME_LENGTH); - - WFIFOB(fd,2+98) = char_dat.str>UCHAR_MAX?UCHAR_MAX:char_dat.str; - WFIFOB(fd,2+99) = char_dat.agi>UCHAR_MAX?UCHAR_MAX:char_dat.agi; - WFIFOB(fd,2+100) = char_dat.vit>UCHAR_MAX?UCHAR_MAX:char_dat.vit; - WFIFOB(fd,2+101) = char_dat.int_>UCHAR_MAX?UCHAR_MAX:char_dat.int_; - WFIFOB(fd,2+102) = char_dat.dex>UCHAR_MAX?UCHAR_MAX:char_dat.dex; - WFIFOB(fd,2+103) = char_dat.luk>UCHAR_MAX?UCHAR_MAX:char_dat.luk; - WFIFOB(fd,2+104) = char_dat.char_num; - - WFIFOSET(fd, 108); - RFIFOSKIP(fd, 37); - } - //to do - for(ch = 0; ch < 9; ch++) { - if (sd->found_char[ch] == -1) { - sd->found_char[ch] = char_dat.char_id; - break; - } - } - break; - case 0x68: /* delete char */ - FIFOSD_CHECK(46); - { - int cid = RFIFOL(fd,2); - WFIFOHEAD(fd, 46); - ShowInfo(CL_RED" Request Char Deletion:"CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, cid); - memcpy(email, RFIFOP(fd,6), 40); - - /* Check if e-mail is correct */ - if(strcmpi(email, sd->email)){ - if(strcmp("a@a.com", sd->email) == 0){ - if(strcmp("a@a.com", email) == 0 || strcmp("", email) == 0){ - //ignore - }else{ - //del fail - WFIFOW(fd, 0) = 0x70; - WFIFOB(fd, 2) = 0; - WFIFOSET(fd, 3); - RFIFOSKIP(fd, 46); - break; - } - }else{ - //del fail - WFIFOW(fd, 0) = 0x70; - WFIFOB(fd, 2) = 0; - WFIFOSET(fd, 3); - RFIFOSKIP(fd, 46); - break; - } - } - - for(i = 0; i < 9; i++) { - /* Debug: - printf("Checking if char to be deleted: %d - %d (%d)\n", sd->found_char[i], RFIFOL(fd, 2), sd->account_id); - */ - if (sd->found_char[i] == cid) { - for(ch = i; ch < 9-1; ch++) - sd->found_char[ch] = sd->found_char[ch+1]; - sd->found_char[8] = -1; - break; - } - } - /* Such a character does not exist in the account */ - /* If so, you are so screwed. */ - if (i == 9) { - WFIFOW(fd, 0) = 0x70; - WFIFOB(fd, 2) = 0; - WFIFOSET(fd, 3); - break; - } - - /* Grab the partner id */ - sprintf(tmp_sql, "SELECT `partner_id` FROM `%s` WHERE `char_id`='%d'",char_db, cid); - - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sql_res = mysql_store_result(&mysql_handle); - - if(sql_res) - { - int char_pid=0; - sql_row = mysql_fetch_row(sql_res); - if (sql_row) - char_pid = atoi(sql_row[0]); - mysql_free_result(sql_res); - - /* Delete character and partner (if any) */ - if(delete_char_sql(cid, char_pid)<0){ - //can't delete the char - //either SQL error or can't delete by some CONFIG conditions - //del fail - WFIFOW(fd, 0) = 0x70; - WFIFOB(fd, 2) = 0; - WFIFOSET(fd, 3); - RFIFOSKIP(fd, 46); - break; - } - if (char_pid != 0) - { /* If there is partner, tell map server to do divorce */ - WBUFW(buf,0) = 0x2b12; - WBUFL(buf,2) = RFIFOL(fd,2); - WBUFL(buf,6) = char_pid; - mapif_sendall(buf,10); - } - } - /* Char successfully deleted.*/ - WFIFOW(fd, 0) = 0x6f; - WFIFOSET(fd, 2); - - RFIFOSKIP(fd, 46); - break; - } - case 0x2af8: // login as map-server - if (RFIFOREST(fd) < 60) - return 0; - { - char *l_userid = RFIFOP(fd,2); - char *l_password = RFIFOP(fd,26); - WFIFOHEAD(fd, 4+5*GM_num); - - l_userid[23] = '\0'; - l_password[23] = '\0'; - WFIFOW(fd, 0) = 0x2af9; - for(i = 0; i < MAX_MAP_SERVERS; i++) { - if (server_fd[i] <= 0) - break; - } - if (i == MAX_MAP_SERVERS || - strcmp(l_userid, userid) || - strcmp(l_password, passwd)) { - WFIFOB(fd,2) = 3; - WFIFOSET(fd, 3); - } else { - int len; - WFIFOB(fd,2) = 0; - WFIFOSET(fd, 3); - session[fd]->func_parse = parse_frommap; - server_fd[i] = fd; - server[i].ip = RFIFOL(fd, 54); - server[i].port = RFIFOW(fd, 58); - server[i].users = 0; - memset(server[i].map, 0, sizeof(server[i].map)); - realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); - char_mapif_init(fd); - // send gm acccounts level to map-servers - len = 4; - WFIFOW(fd,0) = 0x2b15; - for(i = 0; i < GM_num; i++) { - WFIFOL(fd,len) = gm_account[i].account_id; - WFIFOB(fd,len+4) = (unsigned char)gm_account[i].level; - len += 5; - } - WFIFOW(fd,2) = len; - WFIFOSET(fd,len); - } - RFIFOSKIP(fd,60); - break; - } - case 0x187: // Alive? - if (RFIFOREST(fd) < 6) { - return 0; - } - RFIFOSKIP(fd, 6); - break; - - case 0x7530: // Athena info get - { - WFIFOHEAD(fd, 10); - WFIFOW(fd, 0) = 0x7531; - WFIFOB(fd, 2) = ATHENA_MAJOR_VERSION; - WFIFOB(fd, 3) = ATHENA_MINOR_VERSION; - WFIFOB(fd, 4) = ATHENA_REVISION; - WFIFOB(fd, 5) = ATHENA_RELEASE_FLAG; - WFIFOB(fd, 6) = ATHENA_OFFICIAL_FLAG; - WFIFOB(fd, 7) = ATHENA_SERVER_INTER | ATHENA_SERVER_CHAR; - WFIFOW(fd, 8) = ATHENA_MOD_VERSION; - WFIFOSET(fd, 10); - RFIFOSKIP(fd, 2); - return 0; - } - case 0x7532: // disconnect(default also disconnect) - default: - session[fd]->eof = 1; - return 0; - } - } - RFIFOFLUSH(fd); - - return 0; -} - -// Console Command Parser [Wizputer] -int parse_console(char *buf) { - char *type,*command; - - type = (char *)aMalloc(64); - command = (char *)aMalloc(64); - - memset(type,0,64); - memset(command,0,64); - - ShowNotice("Console: %s\n",buf); - - if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 ) - sscanf(buf,"%[^\n]",type); - - ShowNotice("Type of command: %s || Command: %s \n",type,command); - - if(buf) aFree(buf); - if(type) aFree(type); - if(command) aFree(command); - - return 0; -} - -// MAP send all -int mapif_sendall(unsigned char *buf, unsigned int len) { - int i, c; - int fd; - - c = 0; - for(i = 0; i < MAX_MAP_SERVERS; i++) { - if ((fd = server_fd[i]) > 0) { //0 Should not be a valid server_fd [Skotlex] - WFIFOHEAD(fd,len); -#if 0 //This seems to have been fixed long long ago. - if (session[fd] == NULL) - { //Could this be the crash's source? [Skotlex] - ShowError("mapif_sendall: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", fd, i); - server_fd[i] = -1; - memset(&server[i], 0, sizeof(struct mmo_map_server)); - continue; - } -#endif - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - c++; - } - } - - return c; -} - -int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { - int i, c; - int fd; - - c = 0; - for(i=0, c=0;i 0 && fd != sfd) { - WFIFOHEAD(fd, len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd, len); - c++; - } - } - - return c; -} - -int mapif_send(int fd, unsigned char *buf, unsigned int len) { - int i; - - if (fd >= 0) { - for(i = 0; i < MAX_MAP_SERVERS; i++) { - if (fd == server_fd[i]) { - WFIFOHEAD(fd,len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - return 1; - } - } - } - return 0; -} - -int send_users_tologin(int tid, unsigned int tick, int id, int data) { - int users = count_users(); - unsigned char buf[16]; - - if (login_fd > 0 && session[login_fd]) { - // send number of user to login server - WFIFOHEAD(login_fd,6); - WFIFOW(login_fd,0) = 0x2714; - WFIFOL(login_fd,2) = users; - WFIFOSET(login_fd,6); - } - // send number of players to all map-servers - WBUFW(buf,0) = 0x2b00; - WBUFL(buf,2) = users; - mapif_sendall(buf, 6); - - return 0; -} - -static int send_accounts_tologin_sub(DBKey key, void* data, va_list ap) { - struct online_char_data* character = (struct online_char_data*)data; - int *i = va_arg(ap, int*); - int count = va_arg(ap, int); - if ((*i) >= count) - return 0; //This is an error that shouldn't happen.... - if(character->server > -1) { - WFIFOHEAD(login_fd, 8+count*4); - WFIFOL(login_fd, 8+(*i)*4) =character->account_id; - (*i)++; - return 1; - } - return 0; -} - -int send_accounts_tologin(int tid, unsigned int tick, int id, int data) { - int users = count_users(), i=0; - - if (login_fd > 0 && session[login_fd]) { - // send account list to login server - WFIFOHEAD(login_fd, 8+users*4); - WFIFOW(login_fd,0) = 0x272d; - WFIFOL(login_fd,4) = users; - online_char_db->foreach(online_char_db, send_accounts_tologin_sub, &i, users); - WFIFOW(login_fd,2) = 8+ i*4; - WFIFOSET(login_fd,WFIFOW(login_fd,2)); - } - return 0; -} - -int check_connect_login_server(int tid, unsigned int tick, int id, int data) { - if (login_fd > 0 && session[login_fd] != NULL) - return 0; - - ShowInfo("Attempt to connect to login-server...\n"); - login_fd = make_connection(login_ip, login_port); - if (login_fd == -1) - { //Try again later. [Skotlex] - login_fd = 0; - return 0; - } - session[login_fd]->func_parse = parse_tologin; - realloc_fifo(login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); - { - WFIFOHEAD(login_fd, 86); - WFIFOW(login_fd,0) = 0x2710; - memcpy(WFIFOP(login_fd,2), userid, 24); - memcpy(WFIFOP(login_fd,26), passwd, 24); - WFIFOL(login_fd,50) = 0; - WFIFOL(login_fd,54) = char_ip; - WFIFOL(login_fd,58) = char_port; - memcpy(WFIFOP(login_fd,60), server_name, 20); - WFIFOW(login_fd,80) = 0; - WFIFOW(login_fd,82) = char_maintenance; - WFIFOW(login_fd,84) = char_new_display; //only display (New) if they want to [Kevin] - WFIFOSET(login_fd,86); - } - return 0; -} - -//------------------------------------------------ -//Invoked 15 seconds after mapif_disconnectplayer in case the map server doesn't -//replies/disconnect the player we tried to kick. [Skotlex] -//------------------------------------------------ -static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data) -{ - struct online_char_data* character; - if ((character = idb_get(online_char_db, id)) != NULL && character->waiting_disconnect) - { //Mark it offline due to timeout. - set_char_offline(character->char_id, character->account_id); - } - return 0; -} - -//---------------------------------------------------------- -// Return numerical value of a switch configuration by [Yor] -// on/off, english, fran軋is, deutsch, espaol -//---------------------------------------------------------- -int config_switch(const char *str) { - if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) - return 1; - if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) - return 0; - - return atoi(str); -} - -//---------------------------------- -// Reading Lan Support configuration -// Rewrote: Anvanced subnet check [LuzZza] -//---------------------------------- -int char_lan_config_read(const char *lancfgName) { - - FILE *fp; - int line_num = 0; - char line[1024], w1[64], w2[64], w3[64], w4[64]; - - if((fp = fopen(lancfgName, "r")) == NULL) { - ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName); - return 1; - } - - ShowInfo("Reading the configuration file %s...\n", lancfgName); - - while(fgets(line, sizeof(line)-1, fp)) { - - line_num++; - if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n') - continue; - - line[sizeof(line)-1] = '\0'; - if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4) { - - ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num); - continue; - } - - remove_control_chars((unsigned char *)w1); - remove_control_chars((unsigned char *)w2); - remove_control_chars((unsigned char *)w3); - remove_control_chars((unsigned char *)w4); - - if(strcmpi(w1, "subnet") == 0) { - - subnet[subnet_count].mask = inet_addr(w2); - subnet[subnet_count].char_ip = inet_addr(w3); - subnet[subnet_count].map_ip = inet_addr(w4); - subnet[subnet_count].subnet = subnet[subnet_count].char_ip&subnet[subnet_count].mask; - if (subnet[subnet_count].subnet != (subnet[subnet_count].map_ip&subnet[subnet_count].mask)) { - ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4); - continue; - } - - subnet_count++; - } - - ShowStatus("Read information about %d subnetworks.\n", subnet_count); - } - - fclose(fp); - return 0; -} - -void do_final(void) { - ShowInfo("Doing final stage...\n"); - //inter_save(); - do_final_itemdb(); - //check SQL save progress. - //wait until save char complete - - set_all_offline(-1); - set_all_offline_sql(); - - inter_final(); - - flush_fifos(); - - mapindex_final(); - - sprintf(tmp_sql,"DELETE FROM `ragsrvinfo"); - if (mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - if(gm_account) { - aFree(gm_account); - gm_account = 0; - } - - delete_session(login_fd); - delete_session(char_fd); - char_db_->destroy(char_db_, NULL); - online_char_db->destroy(online_char_db, NULL); - - mysql_close(&mysql_handle); - if(char_gm_read) - mysql_close(&lmysql_handle); - - ShowInfo("ok! all done...\n"); -} -#endif //TXT_SQL_CONVERT -void sql_config_read(const char *cfgName){ /* Kalaspuff, to get login_db */ - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - ShowInfo("Reading file %s...\n", cfgName); - - if ((fp = fopen(cfgName, "r")) == NULL) { - ShowFatalError("file not found: %s\n", cfgName); - exit(1); - } - - while(fgets(line, sizeof(line)-1, fp)){ - if(line[0] == '/' && line[1] == '/') - continue; - - if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) - continue; - - if(strcmpi(w1,"char_db")==0){ - strcpy(char_db,w2); -#ifndef TXT_SQL_CONVERT - } else if(strcmpi(w1, "gm_read_method") == 0) { - if(atoi(w2) != 0) - char_gm_read = true; - else - char_gm_read = false; - //custom columns for login database - }else if(strcmpi(w1,"login_db")==0){ - strcpy(login_db, w2); - }else if(strcmpi(w1,"login_db_level")==0){ - strcpy(login_db_level,w2); - }else if(strcmpi(w1,"login_db_account_id")==0){ - strcpy(login_db_account_id,w2); - }else if(strcmpi(w1,"lowest_gm_level")==0){ - lowest_gm_level = atoi(w2); - ShowStatus("set lowest_gm_level : %s\n",w2); -#endif - }else if(strcmpi(w1,"scdata_db")==0){ - strcpy(scdata_db,w2); - }else if(strcmpi(w1,"cart_db")==0){ - strcpy(cart_db,w2); - }else if(strcmpi(w1,"inventory_db")==0){ - strcpy(inventory_db,w2); - }else if(strcmpi(w1,"charlog_db")==0){ - strcpy(charlog_db,w2); - }else if(strcmpi(w1,"storage_db")==0){ - strcpy(storage_db,w2); - }else if(strcmpi(w1,"reg_db")==0){ - strcpy(reg_db,w2); - }else if(strcmpi(w1,"skill_db")==0){ - strcpy(skill_db,w2); - }else if(strcmpi(w1,"interlog_db")==0){ - strcpy(interlog_db,w2); - }else if(strcmpi(w1,"memo_db")==0){ - strcpy(memo_db,w2); - }else if(strcmpi(w1,"guild_db")==0){ - strcpy(guild_db,w2); - }else if(strcmpi(w1,"guild_alliance_db")==0){ - strcpy(guild_alliance_db,w2); - }else if(strcmpi(w1,"guild_castle_db")==0){ - strcpy(guild_castle_db,w2); - }else if(strcmpi(w1,"guild_expulsion_db")==0){ - strcpy(guild_expulsion_db,w2); - }else if(strcmpi(w1,"guild_member_db")==0){ - strcpy(guild_member_db,w2); - }else if(strcmpi(w1,"guild_skill_db")==0){ - strcpy(guild_skill_db,w2); - }else if(strcmpi(w1,"guild_position_db")==0){ - strcpy(guild_position_db,w2); - }else if(strcmpi(w1,"guild_storage_db")==0){ - strcpy(guild_storage_db,w2); - }else if(strcmpi(w1,"party_db")==0){ - strcpy(party_db,w2); - }else if(strcmpi(w1,"pet_db")==0){ - strcpy(pet_db,w2); - }else if(strcmpi(w1,"friend_db")==0){ - strcpy(friend_db,w2); -#ifndef TXT_SQL_CONVERT - }else if(strcmpi(w1,"db_path")==0){ - strcpy(db_path,w2); - //Map server option to use SQL db or not - }else if(strcmpi(w1,"use_sql_db")==0){ // added for sql item_db read for char server [Valaris] - db_use_sqldbs = config_switch(w2); - ShowStatus("Using SQL dbs: %s\n",w2); - }else if(strcmpi(w1,"item_db_db")==0){ - strcpy(item_db_db,w2); - }else if(strcmpi(w1,"item_db2_db")==0){ - strcpy(item_db2_db,w2); - } else if(strcmpi(w1,"connection_ping_interval")==0) { - connection_ping_interval = config_switch(w2); -#endif - //support the import command, just like any other config - }else if(strcmpi(w1,"import")==0){ - sql_config_read(w2); - } - - } - fclose(fp); - ShowInfo("done reading %s.\n", cfgName); -} -#ifndef TXT_SQL_CONVERT - -int char_config_read(const char *cfgName) { - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - if ((fp = fopen(cfgName, "r")) == NULL) { - ShowFatalError("Configuration file not found: %s.\n", cfgName); - exit(1); - } - - ShowInfo("Reading file %s...\n", cfgName); - while(fgets(line, sizeof(line)-1, fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - - line[sizeof(line)-1] = '\0'; - if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2) - continue; - - remove_control_chars((unsigned char *) w1); - remove_control_chars((unsigned char *) w2); - if(strcmpi(w1,"timestamp_format")==0) { - strncpy(timestamp_format, w2, 20); - } else if(strcmpi(w1,"console_silent")==0){ - msg_silent = 0; //To always allow the next line to show up. - ShowInfo("Console Silent Setting: %d\n", atoi(w2)); - msg_silent = atoi(w2); - } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ - stdout_with_ansisequence = config_switch(w2); - } else if (strcmpi(w1, "userid") == 0) { - strncpy(userid, w2, 24); - } else if (strcmpi(w1, "passwd") == 0) { - strncpy(passwd, w2, 24); - } else if (strcmpi(w1, "server_name") == 0) { - strncpy(server_name, w2, 20); - server_name[sizeof(server_name) - 1] = '\0'; - ShowStatus("%s server has been initialized\n", w2); - } else if (strcmpi(w1, "wisp_server_name") == 0) { - if (strlen(w2) >= 4) { - memcpy(wisp_server_name, w2, sizeof(wisp_server_name)); - wisp_server_name[sizeof(wisp_server_name) - 1] = '\0'; - } - } else if (strcmpi(w1, "login_ip") == 0) { - unsigned char ip_str[16]; - login_ip = resolve_hostbyname(w2, NULL, ip_str); - if (login_ip) { - strncpy(login_ip_str, w2, sizeof(login_ip_str)); - ShowStatus("Login server IP address : %s -> %s\n", w2, ip_str); - } - } else if (strcmpi(w1, "login_port") == 0) { - login_port=atoi(w2); - } else if (strcmpi(w1, "char_ip") == 0) { - unsigned char ip_str[16]; - char_ip = resolve_hostbyname(w2, NULL, ip_str); - if (char_ip){ - strncpy(char_ip_str, w2, sizeof(char_ip_str)); - ShowStatus("Character server IP address : %s -> %s\n", w2, ip_str); - } - } else if (strcmpi(w1, "bind_ip") == 0) { - unsigned char ip_str[16]; - bind_ip = resolve_hostbyname(w2, NULL, ip_str); - if (bind_ip) { - strncpy(bind_ip_str, w2, sizeof(bind_ip_str)); - ShowStatus("Character server binding IP address : %s -> %s\n", w2, ip_str); - } - } else if (strcmpi(w1, "char_port") == 0) { - char_port = atoi(w2); - } else if (strcmpi(w1, "char_maintenance") == 0) { - char_maintenance = atoi(w2); - } else if (strcmpi(w1, "char_new")==0){ - char_new = atoi(w2); - } else if (strcmpi(w1, "char_new_display")==0){ - char_new_display = atoi(w2); - } else if (strcmpi(w1, "max_connect_user") == 0) { - max_connect_user = atoi(w2); - if (max_connect_user < 0) - max_connect_user = 0; // unlimited online players - } else if(strcmpi(w1, "gm_allow_level") == 0) { - gm_allow_level = atoi(w2); - if(gm_allow_level < 0) - gm_allow_level = 99; - } else if (strcmpi(w1, "check_ip_flag") == 0) { - check_ip_flag = config_switch(w2); - } else if (strcmpi(w1, "online_check") == 0) { - online_check = config_switch(w2); - } else if (strcmpi(w1, "autosave_time") == 0) { - autosave_interval = atoi(w2)*1000; - if (autosave_interval <= 0) - autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; - } else if (strcmpi(w1, "save_log") == 0) { - save_log = config_switch(w2); - } else if (strcmpi(w1, "start_point") == 0) { - char map[MAP_NAME_LENGTH]; - int x, y; - if (sscanf(w2,"%16[^,],%d,%d", map, &x, &y) < 3) - continue; - if (strstr(map, ".gat") != NULL) { // Verify at least if '.gat' is in the map name - start_point.map = mapindex_name2id(map); - if (!start_point.map) - ShowError("Specified start_point %s not found in map-index cache.\n", map); - start_point.x = x; - start_point.y = y; - } - } else if (strcmpi(w1, "start_zeny") == 0) { - start_zeny = atoi(w2); - if (start_zeny < 0) - start_zeny = 0; - } else if (strcmpi(w1, "start_weapon") == 0) { - start_weapon = atoi(w2); - if (start_weapon < 0) - start_weapon = 0; - } else if (strcmpi(w1, "start_armor") == 0) { - start_armor = atoi(w2); - if (start_armor < 0) - start_armor = 0; - } else if(strcmpi(w1,"log_char")==0){ //log char or not [devil] - log_char = atoi(w2); - } else if (strcmpi(w1, "unknown_char_name") == 0) { - strcpy(unknown_char_name, w2); - unknown_char_name[NAME_LENGTH-1] = 0; - } else if (strcmpi(w1, "name_ignoring_case") == 0) { - name_ignoring_case = config_switch(w2); - } else if (strcmpi(w1, "char_name_option") == 0) { - char_name_option = atoi(w2); - } else if (strcmpi(w1, "char_name_letters") == 0) { - strcpy(char_name_letters, w2); - } else if (strcmpi(w1, "check_ip_flag") == 0) { - check_ip_flag = config_switch(w2); - } else if (strcmpi(w1, "chars_per_account") == 0) { //maxchars per account [Sirius] - char_per_account = atoi(w2); - } else if (strcmpi(w1, "char_del_level") == 0) { //disable/enable char deletion by its level condition [Lupus] - char_del_level = atoi(w2); - } else if (strcmpi(w1, "console") == 0) { - if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) - console = 1; - } else if (strcmpi(w1, "fame_list_alchemist") == 0) { - fame_list_size_chemist = atoi(w2); - if (fame_list_size_chemist > MAX_FAME_LIST) { - ShowWarning("Max fame list size is %d (fame_list_alchemist)\n", MAX_FAME_LIST); - fame_list_size_chemist = MAX_FAME_LIST; - } - } else if (strcmpi(w1, "fame_list_blacksmith") == 0) { - fame_list_size_smith = atoi(w2); - if (fame_list_size_smith > MAX_FAME_LIST) { - ShowWarning("Max fame list size is %d (fame_list_blacksmith)\n", MAX_FAME_LIST); - fame_list_size_smith = MAX_FAME_LIST; - } - } else if (strcmpi(w1, "fame_list_taekwon") == 0) { - fame_list_size_taekwon = atoi(w2); - if (fame_list_size_taekwon > MAX_FAME_LIST) { - ShowWarning("Max fame list size is %d (fame_list_taekwon)\n", MAX_FAME_LIST); - fame_list_size_taekwon = MAX_FAME_LIST; - } - } else if (strcmpi(w1, "guild_exp_rate") == 0) { - guild_exp_rate = atoi(w2); - } else if (strcmpi(w1, "import") == 0) { - char_config_read(w2); - } - } - fclose(fp); - - ShowInfo("Done reading %s.\n", cfgName); - - return 0; -} - -void set_server_type(void) -{ - SERVER_TYPE = ATHENA_SERVER_CHAR; -} - -static int online_data_cleanup_sub(DBKey key, void *data, va_list ap) -{ - struct online_char_data *character= (struct online_char_data*)data; - if (character->server == -2) //Unknown server.. set them offline - set_char_offline(character->char_id, character->account_id); - if (character->server < 0) - //Free data from players that have not been online for a while. - db_remove(online_char_db, key); - return 0; -} - -static int online_data_cleanup(int tid, unsigned int tick, int id, int data) -{ - online_char_db->foreach(online_char_db, online_data_cleanup_sub); - return 0; -} - -int do_init(int argc, char **argv){ - int i; - - for(i = 0; i < MAX_MAP_SERVERS; i++) { - memset(&server[i], 0, sizeof(struct mmo_map_server)); - server_fd[i] = -1; - } - - //Read map indexes - mapindex_init(); - start_point.map = mapindex_name2id("new_zone01.gat"); - - char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]); - char_lan_config_read((argc > 3) ? argv[3] : LAN_CONF_NAME); - sql_config_read(SQL_CONF_NAME); - - if (strcmp(userid, "s1")==0 && strcmp(passwd, "p1")==0) { - ShowError("Using the default user/password s1/p1 is NOT RECOMMENDED.\n"); - ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n"); - ShowNotice("And then change the user/password to use in conf/char_athena.conf (or conf/import/char_conf.txt)\n"); - } - - ShowInfo("Finished reading the char-server configuration.\n"); - - inter_init_sql((argc > 2) ? argv[2] : inter_cfgName); // inter server テハア篳ュ - ShowInfo("Finished reading the inter-server configuration.\n"); - - //Read ItemDB - do_init_itemdb(); - - ShowInfo("Initializing char server.\n"); - online_char_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - mmo_char_sql_init(); - ShowInfo("char server initialized.\n"); - -// ShowDebug("set parser -> parse_char()...\n"); - set_defaultparse(parse_char); - -// ShowDebug("set terminate function -> do_final().....\n"); - - if ((naddr_ != 0) && (!login_ip || !char_ip)) { - // The char server should know what IP address it is running on - // - MouseJstr - int localaddr = ntohl(addr_[0]); - unsigned char *ptr = (unsigned char *) &localaddr; - char buf[16]; - sprintf(buf, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]); - if (naddr_ != 1) - ShowStatus("Multiple interfaces detected.. using %s as our IP address\n", buf); - else - ShowStatus("Defaulting to %s as our IP address\n", buf); - if (!login_ip) { - strcpy(login_ip_str, buf); - login_ip = inet_addr(login_ip_str); - } - if (!char_ip) { - strcpy(char_ip_str, buf); - char_ip = inet_addr(char_ip_str); - } - if (ptr[0] == 192 && ptr[1] == 168) - ShowWarning("Firewall detected.. edit subnet_athena.conf and char_athena.conf\n"); - } - - ShowInfo("open port %d.....\n",char_port); - char_fd = make_listen_bind(bind_ip?bind_ip:INADDR_ANY,char_port); - - add_timer_func_list(check_connect_login_server, "check_connect_login_server"); - add_timer_func_list(send_users_tologin, "send_users_tologin"); - add_timer_func_list(send_accounts_tologin, "send_accounts_tologin"); - add_timer_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect"); - - add_timer_func_list(online_data_cleanup, "online_data_cleanup"); - add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600 * 1000); - - // send ALIVE PING to login server. - add_timer_interval(gettick() + 10, check_connect_login_server, 0, 0, 10 * 1000); - // send USER COUNT PING to login server. - add_timer_interval(gettick() + 10, send_users_tologin, 0, 0, 5 * 1000); - add_timer_interval(gettick() + 3600*1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour. - - char_read_fame_list(); //Read fame lists. - - if(char_gm_read) - read_gm_account(); - - - if ( console ) { - set_defaultconsoleparse(parse_console); - start_console(); - } - - //Cleaning the tables for NULL entrys @ startup [Sirius] - //Chardb clean - ShowInfo("Cleaning the '%s' table...\n", char_db); - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `account_id` = '0'", char_db); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - //guilddb clean - ShowInfo("Cleaning the '%s' table...\n", guild_db); - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_lv` = '0' AND `max_member` = '0' AND `exp` = '0' AND `next_exp` = '0' AND `average_lv` = '0'", guild_db); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - //guildmemberdb clean - ShowInfo("Cleaning the '%s' table...\n", guild_member_db); - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '0' AND `account_id` = '0' AND `char_id` = '0'", guild_member_db); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - ShowInfo("End of char server initilization function.\n"); - ShowStatus("The char-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", char_port); - return 0; -} - -#undef mysql_query - -int debug_mysql_query(char *file, int line, void *mysql, const char *q) { -#ifdef TWILIGHT - ShowDebug("sql: %s:%d# %s\n", file, line, q); -#endif - return mysql_query((MYSQL *) mysql, q); -} - -int char_child(int parent_id, int child_id) { - int tmp_id = 0; - sprintf (tmp_sql, "SELECT `child` FROM `%s` WHERE `char_id` = '%d'", char_db, parent_id); - if (mysql_query (&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result (&mysql_handle); - sql_row = sql_res?mysql_fetch_row (sql_res):NULL; - if (sql_row) - tmp_id = atoi (sql_row[0]); - else - ShowError("CHAR: child Failed!\n"); - if (sql_res) mysql_free_result (sql_res); - if ( tmp_id == child_id ) - return 1; - else - return 0; -} - -int char_married(int pl1,int pl2) { - int tmp_id = 0; - sprintf (tmp_sql, "SELECT `partner_id` FROM `%s` WHERE `char_id` = '%d'", char_db, pl1); - if (mysql_query (&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result (&mysql_handle); - sql_row = sql_res?mysql_fetch_row (sql_res):NULL; - if (sql_row) - tmp_id = atoi (sql_row[0]); - else - ShowError("CHAR: married Failed!\n"); - if (sql_res) mysql_free_result (sql_res); - if ( tmp_id == pl2 ) - return 1; - else - return 0; -} - -int char_family(int pl1,int pl2,int pl3) { - int charid, partnerid, childid; - sprintf (tmp_sql, "SELECT `char_id`,`partner_id`,`child` FROM `%s` WHERE `char_id` IN ('%d','%d','%d')", char_db, pl1, pl2, pl3); - if (mysql_query (&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - sql_res = mysql_store_result (&mysql_handle); - if (!sql_res) return 0; - - while((sql_row = mysql_fetch_row(sql_res))) - { - charid = atoi(sql_row[0]); - partnerid = atoi(sql_row[1]); - childid = atoi(sql_row[2]); - if (charid == pl1) { - if ((pl2 == partnerid && pl3 == childid) || - (pl3 == partnerid && pl2 == childid) - ) { - mysql_free_result (sql_res); - return childid; - } - } - if(charid == pl2) { - if ((pl1 == partnerid && pl3 == childid) || - (pl3 == partnerid && pl1 == childid) - ) { - mysql_free_result (sql_res); - return childid; - } - } - if(charid == pl3) { - if ((pl1 == partnerid && pl2 == childid) || - (pl2 == partnerid && pl1 == childid) - ) { - mysql_free_result (sql_res); - return childid; - } - } - } - mysql_free_result (sql_res); - return 0; -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +// original code from athena +// SQL conversion by Jioh L. Jung +// TXT 1.105 +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "char.h" +#include "../common/utils.h" +#include "../common/strlib.h" +#include "../common/showmsg.h" +#include "itemdb.h" +#include "inter.h" +#include "db.h" +#include "malloc.h" +#include "int_guild.h" + +#ifndef TXT_SQL_CONVERT +static struct dbt *char_db_; +#endif +char char_db[256] = "char"; +char scdata_db[256] = "sc_data"; +char cart_db[256] = "cart_inventory"; +char inventory_db[256] = "inventory"; +char charlog_db[256] = "charlog"; +char storage_db[256] = "storage"; +char interlog_db[256] = "interlog"; +char reg_db[256] = "global_reg_value"; +char skill_db[256] = "skill"; +char memo_db[256] = "memo"; +char guild_db[256] = "guild"; +char guild_alliance_db[256] = "guild_alliance"; +char guild_castle_db[256] = "guild_castle"; +char guild_expulsion_db[256] = "guild_expulsion"; +char guild_member_db[256] = "guild_member"; +char guild_position_db[256] = "guild_position"; +char guild_skill_db[256] = "guild_skill"; +char guild_storage_db[256] = "guild_storage"; +char party_db[256] = "party"; +char pet_db[256] = "pet"; +char friend_db[256] = "friends"; +#ifdef TXT_SQL_CONVERT +int save_log = 0; //Have the logs be off by default when converting +#else +int save_log = 1; +int db_use_sqldbs; +int connection_ping_interval = 0; + +char login_db[256] = "login"; +char login_db_account_id[32] = "account_id"; +char login_db_level[32] = "level"; + +int lowest_gm_level = 1; + +char *SQL_CONF_NAME = "conf/inter_athena.conf"; + +struct mmo_map_server{ + long ip; + short port; + int users; + unsigned short map[MAX_MAP_PER_SERVER]; +} server[MAX_MAP_SERVERS]; + +int server_fd[MAX_MAP_SERVERS]; + +int login_fd, char_fd; +char userid[24]; +char passwd[24]; +char server_name[20]; +char wisp_server_name[NAME_LENGTH] = "Server"; +char login_ip_str[128]; +in_addr_t login_ip = 0; +int login_port = 6900; +char char_ip_str[128]; +in_addr_t char_ip = 0; +char bind_ip_str[128]; +in_addr_t bind_ip = 0; +int char_port = 6121; +int char_maintenance = 0; +int char_new; +int char_new_display; +int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor] +int char_name_option = 0; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor] +char char_name_letters[1024] = ""; // list of letters/symbols used to authorise or not a name of a character. by [Yor] +//The following are characters that are trimmed regardless because they cause confusion and problems on the servers. [Skotlex] +#define TRIM_CHARS "\032\t\x0A\x0D " +int char_per_account = 0; //Maximum charas per account (default unlimited) [Sirius] +int char_del_level = 0; //From which level u can delete character [Lupus] + +int log_char = 1; // loggin char or not [devil] +int log_inter = 1; // loggin inter or not [devil] + +// Advanced subnet check [LuzZza] +struct _subnet { + long subnet; + long mask; + long char_ip; + long map_ip; +} subnet[16]; + +int subnet_count = 0; + +char unknown_char_name[NAME_LENGTH] = "Unknown"; +char db_path[1024]="db"; + +//These are used to aid the map server in identifying valid clients. [Skotlex] +static int max_account_id = DEFAULT_MAX_ACCOUNT_ID, max_char_id = DEFAULT_MAX_CHAR_ID; +static int online_check = 1; //If one, it won't let players connect when their account is already registered online and will send the relevant map server a kick user request. [Skotlex] + +struct char_session_data{ + int account_id, login_id1, login_id2,sex; + int found_char[9]; + char email[40]; // e-mail (default: a@a.com) by [Yor] + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) +}; + +#define AUTH_FIFO_SIZE 256 +struct { + int account_id, char_id, login_id1, login_id2, ip, char_pos, delflag,sex; + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) +} auth_fifo[AUTH_FIFO_SIZE]; +int auth_fifo_pos = 0; + +int check_ip_flag = 1; // It's to check IP of a player between char-server and other servers (part of anti-hacking system) + +struct mmo_charstatus char_dat; +int char_num,char_max; +int max_connect_user = 0; +int gm_allow_level = 99; +int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; +int start_zeny = 0; +int start_weapon = 1201; +int start_armor = 2301; +int guild_exp_rate = 100; + +//Custom limits for the fame lists. [Skotlex] +int fame_list_size_chemist = MAX_FAME_LIST; +int fame_list_size_smith = MAX_FAME_LIST; +int fame_list_size_taekwon = MAX_FAME_LIST; + +// Char-server-side stored fame lists [DracoRPG] +struct fame_list smith_fame_list[MAX_FAME_LIST]; +struct fame_list chemist_fame_list[MAX_FAME_LIST]; +struct fame_list taekwon_fame_list[MAX_FAME_LIST]; + +// check for exit signal +// 0 is saving complete +// other is char_id +unsigned int save_flag = 0; + +// start point (you can reset point on conf file) +struct point start_point = { 0, 53, 111}; + +bool char_gm_read = false; +struct gm_account *gm_account = NULL; +int GM_num = 0; + +int console = 0; + +//Structure for holding in memory which characters are online on the map servers connected. +struct online_char_data { + int account_id; + int char_id; + short server; + unsigned waiting_disconnect :1; +}; + +struct dbt *online_char_db; //Holds all online characters. + +#ifndef SQL_DEBUG + +#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] + +#else + +#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) + +#endif + +static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data); + +static void * create_online_char_data(DBKey key, va_list args) { + struct online_char_data* character; + character = aCalloc(1, sizeof(struct online_char_data)); + character->account_id = key.i; + character->char_id = -1; + character->server = -1; + return character; +} + +//------------------------------------------------- +// Set Character online/offline [Wizputer] +//------------------------------------------------- + +void set_char_online(int map_id, int char_id, int account_id) { + struct online_char_data* character; + if ( char_id != 99 ) { + sprintf(tmp_sql, "UPDATE `%s` SET `online`='1' WHERE `char_id`='%d'",char_db,char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + if (max_account_id < account_id || max_char_id < char_id) + { //Notify map-server of the new max IDs [Skotlex] + if (account_id > max_account_id) + max_account_id = account_id; + if (char_id > max_char_id) + max_char_id = char_id; + mapif_send_maxid(max_account_id, max_char_id); + } + } + + character = idb_ensure(online_char_db, account_id, create_online_char_data); + if (online_check && character->char_id != -1 && character->server > -1 && character->server != map_id) + { + //char == 99 <- Character logging in, so someone has logged in while one + //char is still on map-server, so kick him out, but don't print "error" + //as this is normal behaviour. [Skotlex] + if (char_id != 99) + ShowNotice("set_char_online: Character %d:%d marked in map server %d, but map server %d claims to have (%d:%d) online!\n", + character->account_id, character->char_id, character->server, map_id, account_id, char_id); + mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); + } + character->char_id = (char_id==99)?-1:char_id; + character->server = (char_id==99)?-1:map_id; + character->waiting_disconnect = 0; + if (char_id != 99) + { //Set char online in guild cache. If char is in memory, use the guild id on it, otherwise seek it. + struct mmo_charstatus *cp; + cp = idb_get(char_db_,char_id); + inter_guild_CharOnline(char_id, cp?cp->guild_id:-1); + } + if (login_fd > 0 && !session[login_fd]->eof) + { + WFIFOHEAD(login_fd,6); + WFIFOW(login_fd,0) = 0x272b; + WFIFOL(login_fd,2) = account_id; + WFIFOSET(login_fd,6); + } +} + +void set_char_offline(int char_id, int account_id) { + struct mmo_charstatus *cp; + struct online_char_data* character; + + if ( char_id == 99 ) + sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `account_id`='%d'", char_db, account_id); + else { + cp = idb_get(char_db_,char_id); + inter_guild_CharOffline(char_id, cp?cp->guild_id:-1); + if (cp) + idb_remove(char_db_,char_id); + + sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `char_id`='%d'", char_db, char_id); + + if (mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + if ((character = idb_get(online_char_db, account_id)) != NULL) + { //We don't free yet to avoid aCalloc/aFree spamming during char change. [Skotlex] + character->char_id = -1; + character->server = -1; + character->waiting_disconnect = 0; + } + + if (login_fd > 0 && !session[login_fd]->eof) + { + WFIFOHEAD(login_fd,6); + WFIFOW(login_fd,0) = 0x272c; + WFIFOL(login_fd,2) = account_id; + WFIFOSET(login_fd,6); + } +} + +static int char_db_setoffline(DBKey key, void* data, va_list ap) { + struct online_char_data* character = (struct online_char_data*)data; + int server = va_arg(ap, int); + if (server == -1) { + character->char_id = -1; + character->server = -1; + character->waiting_disconnect = 0; + } else if (character->server == server) + character->server = -2; //In some map server that we aren't connected to. + return 0; +} + +static int char_db_kickoffline(DBKey key, void* data, va_list ap) { + struct online_char_data* character = (struct online_char_data*)data; + int server = va_arg(ap, int); + if (server > -1 && character->server != server) + return 0; + + //Kick out any connected characters, and set them offline as appropiate. + if (character->server > -1) + mapif_disconnectplayer(server_fd[character->server], + character->account_id, character->char_id, 1); + else if (!character->waiting_disconnect) + set_char_offline(character->char_id, character->account_id); + else return 0; + return 1; +} + +void set_all_offline(int id) { + if (id < 0) + ShowNotice("Sending all users offline.\n"); + else + ShowNotice("Sending users of map-server %d offline.\n",id); + online_char_db->foreach(online_char_db,char_db_kickoffline,id); + + if (id >= 0 || login_fd <= 0 || session[login_fd]->eof) + return; + //Tell login-server to also mark all our characters as offline. + WFIFOHEAD(login_fd, 2); + WFIFOW(login_fd,0) = 0x2737; + WFIFOSET(login_fd,2); +} + +void set_all_offline_sql(void) { + //Set all players to 'OFFLINE' + sprintf(tmp_sql, "UPDATE `%s` SET `online` = '0'", char_db); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sprintf(tmp_sql, "UPDATE `%s` SET `online` = '0'", guild_member_db); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sprintf(tmp_sql, "UPDATE `%s` SET `connect_member` = '0'", guild_db); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } +} + +//---------------------------------------------------------------------- +// Determine if an account (id) is a GM account +// and returns its level (or 0 if it isn't a GM account or if not found) +//---------------------------------------------------------------------- +// Removed since nothing GM related goes on in the char server [CLOWNISIUS] +int isGM(int account_id) { + int i; + + for(i = 0; i < GM_num; i++) + if (gm_account[i].account_id == account_id) + return gm_account[i].level; + return 0; +} + +void read_gm_account(void) { + if(!char_gm_read) + return; + + if (gm_account != NULL) + aFree(gm_account); + GM_num = 0; + + sprintf(tmp_sql, "SELECT `%s`,`%s` FROM `%s` WHERE `%s`>='%d'",login_db_account_id,login_db_level,login_db,login_db_level,lowest_gm_level); + if (mysql_query(&lmysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&lmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + lsql_res = mysql_store_result(&lmysql_handle); + if (lsql_res) { + gm_account = (struct gm_account*)aCalloc(sizeof(struct gm_account) * (size_t)mysql_num_rows(lsql_res), 1); + while ((lsql_row = mysql_fetch_row(lsql_res))) { + gm_account[GM_num].account_id = atoi(lsql_row[0]); + gm_account[GM_num].level = atoi(lsql_row[1]); + GM_num++; + } + } + + mysql_free_result(lsql_res); + mapif_send_gmaccounts(); +} +#endif //TXT_SQL_CONVERT +int compare_item(struct item *a, struct item *b) { + + if(a->id == b->id && + a->nameid == b->nameid && + a->amount == b->amount && + a->equip == b->equip && + a->identify == b->identify && + a->refine == b->refine && + a->attribute == b->attribute) + { + int i; + for (i=0; icard[i]==b->card[i]; i++); + return (i == MAX_SLOTS); + } + return 0; +} + +#ifndef TXT_SQL_CONVERT +static void* create_charstatus(DBKey key, va_list args) { + struct mmo_charstatus *cp; + cp = (struct mmo_charstatus *) aCalloc(1,sizeof(struct mmo_charstatus)); + cp->char_id = key.i; + return cp; +} +#endif //TXT_SQL_CONVERT +int mmo_char_tosql(int char_id, struct mmo_charstatus *p){ + int i=0,j; + int count = 0; + int diff = 0; + char *tmp_ptr; //Building a single query should be more efficient than running + //multiple queries for each thing about to be saved, right? [Skotlex] + char save_status[128]; //For displaying save information. [Skotlex] + struct mmo_charstatus *cp; + struct itemtmp mapitem[MAX_GUILD_STORAGE]; + + if (char_id!=p->char_id) return 0; + +#ifndef TXT_SQL_CONVERT + cp = idb_ensure(char_db_, char_id, create_charstatus); +#else + cp = aCalloc(1, sizeof(struct mmo_charstatus)); +#endif + + memset(save_status, 0, sizeof(save_status)); + diff = 0; + //map inventory data + for(i=0;iinventory[i], &cp->inventory[i])) + diff = 1; + if(p->inventory[i].nameid>0){ + mapitem[count].flag=0; + mapitem[count].id = p->inventory[i].id; + mapitem[count].nameid=p->inventory[i].nameid; + mapitem[count].amount = p->inventory[i].amount; + mapitem[count].equip = p->inventory[i].equip; + mapitem[count].identify = p->inventory[i].identify; + mapitem[count].refine = p->inventory[i].refine; + mapitem[count].attribute = p->inventory[i].attribute; + for (j=0; jinventory[i].card[j]; + count++; + } + } + //printf("- Save item data to MySQL!\n"); + if (diff) + if (!memitemdata_to_sql(mapitem, count, p->char_id,TABLE_INVENTORY)) + strcat(save_status, " inventory"); + + count = 0; + diff = 0; + + //map cart data + for(i=0;icart[i], &cp->cart[i])) + diff = 1; + if(p->cart[i].nameid>0){ + mapitem[count].flag=0; + mapitem[count].id = p->cart[i].id; + mapitem[count].nameid=p->cart[i].nameid; + mapitem[count].amount = p->cart[i].amount; + mapitem[count].equip = p->cart[i].equip; + mapitem[count].identify = p->cart[i].identify; + mapitem[count].refine = p->cart[i].refine; + mapitem[count].attribute = p->cart[i].attribute; + for (j=0; jcart[i].card[j]; + count++; + } + } + + if (diff) + if (!memitemdata_to_sql(mapitem, count, p->char_id,TABLE_CART)) + strcat(save_status, " cart"); +#ifdef TXT_SQL_CONVERT +{ //Insert the barebones to then update the rest. + char t_name[NAME_LENGTH*2]; + jstrescapecpy(t_name, p->name); + sprintf(tmp_sql, "REPLACE INTO `%s` (`account_id`, `char_num`, `name`) VALUES ('%d', '%d', '%s')", + char_db, p->account_id, p->char_num, t_name); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } else + strcat(save_status, " creation"); +} +#endif + + if ( + (p->base_exp != cp->base_exp) || (p->base_level != cp->base_level) || + (p->job_level != cp->job_level) || (p->job_exp != cp->job_exp) || + (p->zeny != cp->zeny) || + (p->last_point.x != cp->last_point.x) || (p->last_point.y != cp->last_point.y) || + (p->max_hp != cp->max_hp) || (p->hp != cp->hp) || + (p->max_sp != cp->max_sp) || (p->sp != cp->sp) || + (p->status_point != cp->status_point) || (p->skill_point != cp->skill_point) || + (p->str != cp->str) || (p->agi != cp->agi) || (p->vit != cp->vit) || + (p->int_ != cp->int_) || (p->dex != cp->dex) || (p->luk != cp->luk) || + (p->option != cp->option) || + (p->party_id != cp->party_id) || (p->guild_id != cp->guild_id) || + (p->pet_id != cp->pet_id) || (p->weapon != cp->weapon) || (p->hom_id != cp->hom_id) || + (p->shield != cp->shield) || (p->head_top != cp->head_top) || + (p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom) + ) + { //Save status + sprintf(tmp_sql ,"UPDATE `%s` SET `base_level`='%d', `job_level`='%d'," + "`base_exp`='%u', `job_exp`='%u', `zeny`='%d'," + "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d'," + "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d'," + "`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d'," //[orn] add homun_id (homunculus id) + "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," + "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d'" + " WHERE `account_id`='%d' AND `char_id` = '%d'", + char_db, p->base_level, p->job_level, + p->base_exp, p->job_exp, p->zeny, + p->max_hp, p->hp, p->max_sp, p->sp, p->status_point, p->skill_point, + p->str, p->agi, p->vit, p->int_, p->dex, p->luk, + p->option, p->party_id, p->guild_id, p->pet_id, p->hom_id, //[orn] add homun_id (homunculus id) + p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom, + mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y, + mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, + p->account_id, p->char_id + ); + + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } else + strcat(save_status, " status"); + } + + //Values that will seldom change (to speed up saving) + if ( + (p->hair != cp->hair) || (p->hair_color != cp->hair_color) || (p->clothes_color != cp->clothes_color) || + (p->class_ != cp->class_) || + (p->partner_id != cp->partner_id) || (p->father != cp->father) || + (p->mother != cp->mother) || (p->child != cp->child) || + (p->karma != cp->karma) || (p->manner != cp->manner) || + (p->fame != cp->fame) + ) + { + sprintf(tmp_sql ,"UPDATE `%s` SET `class`='%d'," + "`hair`='%d',`hair_color`='%d',`clothes_color`='%d'," + "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d'," + "`karma`='%d',`manner`='%d', `fame`='%d'" + " WHERE `account_id`='%d' AND `char_id` = '%d'", + char_db, p->class_, + p->hair, p->hair_color, p->clothes_color, + p->partner_id, p->father, p->mother, p->child, + p->karma, p->manner, p->fame, + p->account_id, p->char_id + ); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } else + strcat(save_status, " status2"); + } + + + diff = 0; + + for(i=0;imemo_point[i].map == cp->memo_point[i].map && p->memo_point[i].x == cp->memo_point[i].x && p->memo_point[i].y == cp->memo_point[i].y) + continue; + diff = 1; + break; + } + + if (diff) + { //Save memo + //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`) + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",memo_db, p->char_id); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + //insert here. + tmp_ptr = tmp_sql; + tmp_ptr += sprintf(tmp_ptr, "INSERT INTO `%s`(`char_id`,`map`,`x`,`y`) VALUES ", memo_db); + count = 0; + for(i=0;imemo_point[i].map){ + tmp_ptr += sprintf(tmp_ptr,"('%d', '%s', '%d', '%d'),", + char_id, mapindex_id2name(p->memo_point[i].map), p->memo_point[i].x, p->memo_point[i].y); + count++; + } + } + if (count) + { //Dangerous? Only if none of the above sprintf worked. [Skotlex] + tmp_ptr[-1] = '\0'; //Remove the trailing comma. + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } else + strcat(save_status, " memo"); + } else //Memo Points cleared (how is this possible?). + strcat(save_status, " memo"); + } + + diff = 0; + for(i=0;iskill[i].lv != 0) && (p->skill[i].id == 0)) + p->skill[i].id = i; // Fix skill tree + + if((p->skill[i].id != cp->skill[i].id) || (p->skill[i].lv != cp->skill[i].lv) || + (p->skill[i].flag != cp->skill[i].flag)) + { + diff = 1; + break; + } + } + + if (diff) + { //Save skills + + //`skill` (`char_id`, `id`, `lv`) + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",skill_db, p->char_id); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + tmp_ptr = tmp_sql; + tmp_ptr += sprintf(tmp_ptr,"INSERT INTO `%s`(`char_id`,`id`,`lv`) VALUES ", skill_db); + count = 0; + //insert here. + for(i=0;iskill[i].id && p->skill[i].flag!=1) + { + tmp_ptr += sprintf(tmp_ptr,"('%d','%d','%d'),", + char_id, p->skill[i].id, (p->skill[i].flag==0)?p->skill[i].lv:p->skill[i].flag-2); + count++; + } + } + + if (count) + { + tmp_ptr[-1] = '\0'; //Remove trailing comma. + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } else + strcat(save_status, " skills"); + } else //Skills removed (reset?) + strcat(save_status, " skills"); + } + diff = 0; + for(i = 0; i < MAX_FRIENDS; i++){ + if(p->friends[i].char_id != cp->friends[i].char_id || + p->friends[i].account_id != cp->friends[i].account_id){ + diff = 1; + break; + } + } + + if(diff == 1) + { //Save friends + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `char_id`='%d'", friend_db, char_id); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + tmp_ptr = tmp_sql; + tmp_ptr += sprintf(tmp_ptr, "INSERT INTO `%s` (`char_id`, `friend_account`, `friend_id`) VALUES ", friend_db); + count = 0; + for(i = 0; i < MAX_FRIENDS; i++){ + if(p->friends[i].char_id > 0) + { + tmp_ptr += sprintf(tmp_ptr, "('%d','%d','%d'),", char_id, p->friends[i].account_id, p->friends[i].char_id); + count++; + } + } + if (count) + { + tmp_ptr[-1] = '\0'; //Remove the last comma. [Skotlex] + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } else + strcat(save_status, " friends"); + } else //Friend list cleared. + strcat(save_status, " friends"); + + } + + if (save_status[0]!='\0' && save_log) + ShowInfo("Saved char %d - %s:%s.\n", char_id, p->name, save_status); +#ifndef TXT_SQL_CONVERT + memcpy(cp, p, sizeof(struct mmo_charstatus)); +#else + aFree(cp); +#endif + return 0; +} + +// [Ilpalazzo-sama] +int memitemdata_to_sql(struct itemtmp mapitem[], int count, int char_id, int tableswitch) +{ + int i,j, flag, id; + char *tablename; + char selectoption[16]; + char * str_p = tmp_sql; + + switch (tableswitch) { + case TABLE_INVENTORY: + tablename = inventory_db; // no need for sprintf here as *_db are char*. + sprintf(selectoption,"char_id"); + break; + case TABLE_CART: + tablename = cart_db; + sprintf(selectoption,"char_id"); + break; + case TABLE_STORAGE: + tablename = storage_db; + sprintf(selectoption,"account_id"); + break; + case TABLE_GUILD_STORAGE: + tablename = guild_storage_db; + sprintf(selectoption,"guild_id"); + break; + default: + ShowError("Invalid table name!\n"); + return 1; + } + + //=======================================mysql database data > memory=============================================== + + str_p += sprintf(str_p, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); + + for (j=0; j SQL =============================== +#ifndef TXT_SQL_CONVERT + if(!itemdb_isequip(mapitem[i].nameid)) + { //Quick update of stackable items. Update Qty and Equip should be enough, but in case we are also updating identify + sprintf(tmp_sql,"UPDATE `%s` SET `equip`='%d', `identify`='%d', `amount`='%d' WHERE `id`='%d' LIMIT 1", + tablename, mapitem[i].equip, mapitem[i].identify,mapitem[i].amount, id); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } else +#endif //TXT_SQL_CONVERT + { //Equipment or Misc item, just update all fields. + str_p = tmp_sql; + str_p += sprintf(str_p,"UPDATE `%s` SET `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d'", + tablename, mapitem[i].equip, mapitem[i].identify, mapitem[i].refine, mapitem[i].attribute); + + for(j=0; jchar_id = char_id; + if (save_log) + ShowInfo("Char load request (%d)\n", char_id); + //`char`( `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, //9 + //`str`,`agi`,`vit`,`int`,`dex`,`luk`, //15 + //`max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, //21 + //`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`, //27 + //`hair`,`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, //35 + //`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`) + //splite 2 parts. cause veeeery long SQL syntax + + sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`," + "`str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point` FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR + + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sql_res = mysql_store_result(&mysql_handle); + + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); + if (!sql_row) + { //Just how does this happens? [Skotlex] + ShowError("Requested non-existant character id: %d!\n", char_id); + return 0; + } + + p->char_id = char_id; + p->account_id = atoi(sql_row[1]); + p->char_num = atoi(sql_row[2]); + strcpy(p->name, sql_row[3]); + p->class_ = atoi(sql_row[4]); + p->base_level = atoi(sql_row[5]); + p->job_level = atoi(sql_row[6]); + exp = atof(sql_row[7]); + p->base_exp = exp<0?0:(exp>UINT_MAX?UINT_MAX:(unsigned int)exp); + exp = atof(sql_row[8]); + p->job_exp = exp<0?0:(exp>UINT_MAX?UINT_MAX:(unsigned int)exp); + p->zeny = atoi(sql_row[9]); + p->str = atoi(sql_row[10]); + p->agi = atoi(sql_row[11]); + p->vit = atoi(sql_row[12]); + p->int_ = atoi(sql_row[13]); + p->dex = atoi(sql_row[14]); + p->luk = atoi(sql_row[15]); + p->max_hp = atoi(sql_row[16]); + p->hp = atoi(sql_row[17]); + p->max_sp = atoi(sql_row[18]); + p->sp = atoi(sql_row[19]); + p->status_point = atoi(sql_row[20]) > USHRT_MAX ? USHRT_MAX : atoi(sql_row[20]); + p->skill_point = atoi(sql_row[21]) > USHRT_MAX ? USHRT_MAX : atoi(sql_row[21]); + //free mysql result. + mysql_free_result(sql_res); + strcat (t_msg, " status"); + } else + ShowError("Load char failed (%d - table %s).\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!? + + sprintf(tmp_sql, "SELECT `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`," + "`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`," + "`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame`, `homun_id`" //[orn] homun_id + "FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sql_res = mysql_store_result(&mysql_handle); + sql_row = sql_res?mysql_fetch_row(sql_res):NULL; + if (sql_row) { + + p->option = atoi(sql_row[0]); p->karma = atoi(sql_row[1]); p->manner = atoi(sql_row[2]); + p->party_id = atoi(sql_row[3]); p->guild_id = atoi(sql_row[4]); p->pet_id = atoi(sql_row[5]); + + p->hair = atoi(sql_row[6]); p->hair_color = atoi(sql_row[7]); p->clothes_color = atoi(sql_row[8]); + p->weapon = atoi(sql_row[9]); p->shield = atoi(sql_row[10]); + p->head_top = atoi(sql_row[11]); p->head_mid = atoi(sql_row[12]); p->head_bottom = atoi(sql_row[13]); + p->last_point.map = mapindex_name2id(sql_row[14]); p->last_point.x = atoi(sql_row[15]); p->last_point.y = atoi(sql_row[16]); + p->save_point.map = mapindex_name2id(sql_row[17]); p->save_point.x = atoi(sql_row[18]); p->save_point.y = atoi(sql_row[19]); + p->partner_id = atoi(sql_row[20]); p->father = atoi(sql_row[21]); p->mother = atoi(sql_row[22]); p->child = atoi(sql_row[23]); + p->fame = atoi(sql_row[24]); + p->hom_id = atoi(sql_row[25]); //[orn] homunculus id + + strcat (t_msg, " status2"); + } else + ShowError("Char load failed (%d - table %s)\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!? + //free mysql result. + if (sql_res) + mysql_free_result(sql_res); + + //read memo data + //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`) + sprintf(tmp_sql, "SELECT `map`,`x`,`y` FROM `%s` WHERE `char_id`='%d' ORDER by `memo_id`",memo_db, char_id); // TBR + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + + if (sql_res) { + for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){ + p->memo_point[i].map = mapindex_name2id(sql_row[0]); + p->memo_point[i].x=atoi(sql_row[1]); + p->memo_point[i].y=atoi(sql_row[2]); + //i ++; + } + mysql_free_result(sql_res); + strcat (t_msg, " memo"); + } + + //read inventory + //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`) + str_p += sprintf(str_p, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); + + for (j=0; jinventory[i].id = atoi(sql_row[0]); + p->inventory[i].nameid = atoi(sql_row[1]); + p->inventory[i].amount = atoi(sql_row[2]); + p->inventory[i].equip = atoi(sql_row[3]); + p->inventory[i].identify = atoi(sql_row[4]); + p->inventory[i].refine = atoi(sql_row[5]); + p->inventory[i].attribute = atoi(sql_row[6]); + for (j=0; jinventory[i].card[j] = atoi(sql_row[7+j]); + } + mysql_free_result(sql_res); + strcat (t_msg, " inventory"); + } + + //read cart. + //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`) + str_p = tmp_sql; + str_p += sprintf(str_p, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); + + for (j=0; jcart[i].id = atoi(sql_row[0]); + p->cart[i].nameid = atoi(sql_row[1]); + p->cart[i].amount = atoi(sql_row[2]); + p->cart[i].equip = atoi(sql_row[3]); + p->cart[i].identify = atoi(sql_row[4]); + p->cart[i].refine = atoi(sql_row[5]); + p->cart[i].attribute = atoi(sql_row[6]); + for(j=0; jcart[i].card[j] = atoi(sql_row[7+j]); + } + mysql_free_result(sql_res); + strcat (t_msg, " cart"); + } + + //read skill + //`skill` (`char_id`, `id`, `lv`) + sprintf(tmp_sql, "SELECT `id`, `lv` FROM `%s` WHERE `char_id`='%d'",skill_db, char_id); // TBR + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){ + n = atoi(sql_row[0]); + p->skill[n].id = n; //memory!? shit!. + p->skill[n].lv = atoi(sql_row[1]); + } + mysql_free_result(sql_res); + strcat (t_msg, " skills"); + } +/* Global-reg loading is now handled by the inter-server. + //global_reg + //`global_reg_value` (`char_id`, `str`, `value`) + sprintf(tmp_sql, "SELECT `str`, `value` FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, char_id); // TBR + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + i = 0; + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){ + strcpy (p->global_reg[i].str, sql_row[0]); + strcpy (p->global_reg[i].value, sql_row[1]); + } + mysql_free_result(sql_res); + strcat (t_msg, " reg_values"); + } + p->global_reg_num=i; +*/ + //Shamelessly stolen from its_sparky (ie: thanks) and then assimilated by [Skotlex] + //Friend list + sprintf(tmp_sql, "SELECT f.friend_account, f.friend_id, c.name FROM `%s` f LEFT JOIN `%s` c ON f.friend_account=c.account_id AND f.friend_id=c.char_id WHERE f.char_id='%d'", friend_db, char_db, char_id); + + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sql_res = mysql_store_result(&mysql_handle); + if(sql_res) + { + for(i = 0; (sql_row = mysql_fetch_row(sql_res)) && ifriends[i].account_id = atoi(sql_row[0]); + p->friends[i].char_id = atoi(sql_row[1]); + strncpy(p->friends[i].name, sql_row[2], NAME_LENGTH-1); //The -1 is to avoid losing the ending \0 [Skotlex] + } + } + } + mysql_free_result(sql_res); + strcat (t_msg, " friends"); + } + + if (save_log) + ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfuly! + + cp = idb_ensure(char_db_, char_id, create_charstatus); + memcpy(cp, p, sizeof(struct mmo_charstatus)); + return 1; +} + +// For quick selection of data when displaying the char menu. [Skotlex] +// +int mmo_char_fromsql_short(int char_id, struct mmo_charstatus *p){ + char t_msg[128]; + double exp; + memset(p, 0, sizeof(struct mmo_charstatus)); + t_msg[0]= '\0'; + + p->char_id = char_id; +// ShowInfo("Quick Char load request (%d)\n", char_id); + //`char`( `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, //9 + //`str`,`agi`,`vit`,`int`,`dex`,`luk`, //15 + //`max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, //21 + //`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`, //27 + //`hair`,`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, //35 + //`last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`) + //splite 2 parts. cause veeeery long SQL syntax + + sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`," + "`str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point` FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR + + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sql_res = mysql_store_result(&mysql_handle); + + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); + if (!sql_row) + { //Just how does this happens? [Skotlex] + ShowError("Requested non-existant character id: %d!\n", char_id); + mysql_free_result(sql_res); + return 0; + } + p->char_id = char_id; + p->account_id = atoi(sql_row[1]); + p->char_num = atoi(sql_row[2]); + strcpy(p->name, sql_row[3]); + p->class_ = atoi(sql_row[4]); + p->base_level = atoi(sql_row[5]); + p->job_level = atoi(sql_row[6]); + exp = atof(sql_row[7]); + p->base_exp = exp<0?0:(exp>UINT_MAX?UINT_MAX:(unsigned int)exp); + exp = atof(sql_row[8]); + p->job_exp = exp<0?0:(exp>UINT_MAX?UINT_MAX:(unsigned int)exp); + p->zeny = atoi(sql_row[9]); + p->str = atoi(sql_row[10]); + p->agi = atoi(sql_row[11]); + p->vit = atoi(sql_row[12]); + p->int_ = atoi(sql_row[13]); + p->dex = atoi(sql_row[14]); + p->luk = atoi(sql_row[15]); + p->max_hp = atoi(sql_row[16]); + p->hp = atoi(sql_row[17]); + p->max_sp = atoi(sql_row[18]); + p->sp = atoi(sql_row[19]); + p->status_point = atoi(sql_row[20]) > USHRT_MAX ? USHRT_MAX : atoi(sql_row[20]); + p->skill_point = atoi(sql_row[21]) > USHRT_MAX ? USHRT_MAX : atoi(sql_row[21]); + //free mysql result. + mysql_free_result(sql_res); + strcat (t_msg, " status"); + } else + ShowError("Load char failed (%d - table %s).\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!? + + sprintf(tmp_sql, "SELECT `option`,`karma`,`manner`,`hair`,`hair_color`," + "`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`" + "FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); // TBR + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sql_res = mysql_store_result(&mysql_handle); + sql_row = sql_res?mysql_fetch_row(sql_res):NULL; + if (sql_row) { + + p->option = atoi(sql_row[0]); p->karma = atoi(sql_row[1]); p->manner = atoi(sql_row[2]); + p->hair = atoi(sql_row[3]); p->hair_color = atoi(sql_row[4]); p->clothes_color = atoi(sql_row[5]); + p->weapon = atoi(sql_row[6]); p->shield = atoi(sql_row[7]); + p->head_top = atoi(sql_row[8]); p->head_mid = atoi(sql_row[9]); p->head_bottom = atoi(sql_row[10]); + + strcat (t_msg, " status2"); + } else + ShowError("Char load failed (%d - table %s)\n", char_id, char_db); //Error?! ERRRRRR WHAT THAT SAY!? + //free mysql result. + if (sql_res) + mysql_free_result(sql_res); +// if (save_log) //Too much spam :/ +// ShowInfo("Quick Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfuly! + + return 1; +} +//========================================================================================================== +int mmo_char_sql_init(void) { + ShowInfo("Begin Initializing.......\n"); + char_db_= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA, sizeof(int)); + // memory initialize + memset(&char_dat, 0, sizeof(struct mmo_charstatus)); + if(char_per_account == 0){ + ShowStatus("Chars per Account: 'Unlimited'.......\n"); + }else{ + ShowStatus("Chars per Account: '%d'.......\n", char_per_account); + } + + //the 'set offline' part is now in check_login_conn ... + //if the server connects to loginserver + //it will dc all off players + //and send the loginserver the new state.... + + // Force all users offline in sql when starting char-server + // (useful when servers crashs and don't clean the database) + set_all_offline_sql(); + + ShowInfo("Finished initilizing.......\n"); + + return 0; +} + +//========================================================================================================== + +int make_new_char_sql(int fd, unsigned char *dat) { + struct char_session_data *sd; + char name[NAME_LENGTH]; + char t_name[NAME_LENGTH*2]; + unsigned int i; // Used in for loop and comparing with strlen, safe to be unsigned. [Lance] + int char_id, temp; + + strncpy(name, dat, NAME_LENGTH); + name[NAME_LENGTH-1] = '\0'; //Always terminate string. + trim(name,TRIM_CHARS); //Trim character name. [Skotlex] + jstrescapecpy(t_name, name); + + // disabled until fixed >.> + // Note: escape characters should be added to jstrescape()! + //mysql_real_escape_string(&mysql_handle, t_name, t_name_temp, sizeof(t_name_temp)); + + if (!session_isValid(fd) || !(sd = (struct char_session_data*)session[fd]->session_data)) + return -2; + + ShowInfo("New character request (%d)\n", sd->account_id); + + //check name != main chat nick [LuzZza] + if(strcmpi(name, main_chat_nick) == 0) { + ShowInfo("Create char failed (%d): this nick (%s) reserved for mainchat messages.\n", sd->account_id, name); + return -2; + } + + //check for charcount (maxchars) :) + if(char_per_account != 0){ + sprintf(tmp_sql, "SELECT `account_id` FROM `%s` WHERE `account_id` = '%d'", char_db, sd->account_id); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + if(sql_res){ + //ok + temp = (int)mysql_num_rows(sql_res); + if(temp >= char_per_account){ + //hehe .. limit exceeded :P + ShowInfo("Create char failed (%d): charlimit exceeded.\n", sd->account_id); + mysql_free_result(sql_res); + return -2; + } + mysql_free_result(sql_res); + } + } + + // Check Authorised letters/symbols in the name of the character + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) == NULL) + return -2; + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) != NULL) + return -2; + } // else, all letters/symbols are authorised (except control char removed before) + + //check stat error + if ((dat[24]+dat[25]+dat[26]+dat[27]+dat[28]+dat[29]!=6*5 ) || // stats + (dat[30] >= 9) || // slots (dat[30] can not be negativ) + (dat[33] <= 0) || (dat[33] >= 24) || // hair style + (dat[31] >= 9)) { // hair color (dat[31] can not be negativ) + if (log_char) { + // char.log to charlog + sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" + "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + charlog_db,"make new char error", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); + //query + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id); + return -2; + } // for now we have checked: stat points used <31, char slot is less then 9, hair style/color values are acceptable + + // check individual stat value + for(i = 24; i <= 29; i++) { + if (dat[i] < 1 || dat[i] > 9) { + if (log_char) { + // char.log to charlog + sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" + "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + charlog_db,"make new char error", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); + //query + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id); + return -2; + } + } // now we know that every stat has proper value but we have to check if str/int agi/luk vit/dex pairs are correct + + if( ((dat[24]+dat[27]) > 10) || ((dat[25]+dat[29]) > 10) || ((dat[26]+dat[28]) > 10) ) { + if (log_char) { + // char.log to charlog + sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" + "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + charlog_db,"make new char error", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); + //query + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id); + return -2; + } // now when we have passed all stat checks + + if (log_char) { + // char.log to charlog + sprintf(tmp_sql,"INSERT INTO `%s`(`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" + "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + charlog_db,"make new char", sd->account_id, dat[30], t_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); + //query + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + //printf("make new char %d-%d %s %d, %d, %d, %d, %d, %d - %d, %d" RETCODE, + // fd, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); + + //Check Name (already in use?) + sprintf(tmp_sql, "SELECT 1 FROM `%s` WHERE `name` = '%s'",char_db, t_name); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return -2; + } + sql_res = mysql_store_result(&mysql_handle); + if(sql_res){ + temp = (int)mysql_num_rows(sql_res); + mysql_free_result(sql_res); + if (temp > 0) { + ShowInfo("Create char failed: charname already in use\n"); + return -1; + } + } + + // check char slot. + sprintf(tmp_sql, "SELECT `account_id`, `char_num` FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d'",char_db, sd->account_id, dat[30]); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + + if(sql_res){ + temp = (int)mysql_num_rows(sql_res); + mysql_free_result(sql_res); + if (temp > 0) { + ShowWarning("Create char failed (%d, slot: %d), slot already in use\n", sd->account_id, dat[30]); + return -2; + } + } + + //New Querys [Sirius] + //Insert the char to the 'chardb' ^^ + sprintf(tmp_sql, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `zeny`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`," + "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`) VALUES (" + "'%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')", + char_db, sd->account_id , dat[30] , t_name, start_zeny, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], + (40 * (100 + dat[26])/100) , (40 * (100 + dat[26])/100 ), (11 * (100 + dat[27])/100), (11 * (100 + dat[27])/100), dat[33], dat[31], + mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return -2; //No, stop the procedure! + } + //Now we need the charid from sql! + if(mysql_field_count(&mysql_handle) == 0 && + mysql_insert_id(&mysql_handle) > 0) + char_id = (int)mysql_insert_id(&mysql_handle); + else { + //delete the char ..(no trash in DB!) but how is this possible? + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d' AND `name` = '%s'", char_db, sd->account_id, dat[30], t_name); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + return -2; //XD end of the (World? :P) .. charcreate (denied) + } + //Give the char the default items + //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`) + if (start_weapon > 0) { //add Start Weapon (Knife?) + sprintf(tmp_sql,"INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `equip`, `identify`) VALUES ('%d', '%d', '%d', '%d', '%d')", inventory_db, char_id, start_weapon,1,0x02,1); + if (mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + if (start_armor > 0) { //Add default armor (cotton shirt?) + sprintf(tmp_sql,"INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `equip`, `identify`) VALUES ('%d', '%d', '%d', '%d', '%d')", inventory_db, char_id, start_armor,1,0x10,1); + if (mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s\n", sd->account_id, char_id, dat[30], name); + return char_id; +} + +/*----------------------------------------------------------------------------------------------------------*/ +/* Delete char - davidsiaw */ +/*----------------------------------------------------------------------------------------------------------*/ +/* Returns 0 if successful + * Returns < 0 for error + */ +int delete_char_sql(int char_id, int partner_id) +{ + char char_name[NAME_LENGTH], t_name[NAME_LENGTH*2]; //Name needs be escaped. + int account_id=0, party_id=0, guild_id=0, char_base_level=0; + + sprintf(tmp_sql, "SELECT `name`,`account_id`,`party_id`,`guild_id`,`base_level` FROM `%s` WHERE `char_id`='%d'",char_db, char_id); + + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sql_res = mysql_store_result(&mysql_handle); + + if(sql_res) + sql_row = mysql_fetch_row(sql_res); + + if (sql_res == NULL || sql_row == NULL) + { + ShowError("delete_char_sql: Unable to fetch character data, deletion aborted.\n"); + if (sql_res) + mysql_free_result(sql_res); + return -1; + } + strncpy(char_name, sql_row[0], NAME_LENGTH); + char_name[NAME_LENGTH-1] = '\0'; + jstrescapecpy(t_name, char_name); //Escape string for sql use... [Skotlex] + account_id = atoi(sql_row[1]); + party_id = atoi(sql_row[2]); + guild_id = atoi(sql_row[3]); + char_base_level = atoi(sql_row[4]); + mysql_free_result(sql_res); //Let's free this as soon as possible to avoid problems later on. + + //check for config char del condition [Lupus] + if( ( char_del_level > 0 && char_base_level >= char_del_level ) + || ( char_del_level < 0 && char_base_level <= -char_del_level ) + ) { + ShowInfo("Char deletion aborted: %s, BaseLevel: %i\n",char_name,char_base_level); + return -1; + } + + /* Divorce [Wizputer] */ + if (partner_id) { + sprintf(tmp_sql,"UPDATE `%s` SET `partner_id`='0' WHERE `char_id`='%d'",char_db,partner_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sprintf(tmp_sql,"DELETE FROM `%s` WHERE (`nameid`='%d' OR `nameid`='%d') AND `char_id`='%d'",inventory_db,WEDDING_RING_M,WEDDING_RING_F,partner_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + //Make the character leave the party [Skotlex] + if (party_id) + inter_party_leave(party_id, account_id, char_id); + + /* delete char's pet */ + //Delete the hatched pet if you have one... + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d' AND `incuvate` = '0'",pet_db, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + // Komurka's suggested way to clear pets, modified by [Skotlex] (because I always personalize what I do :X) + //Removing pets that are in the char's inventory.... + { //NOTE: The syntax for multi-table deletes is a bit changed between 4.0 and 4.1 regarding aliases, so we have to consider the version... [Skotlex] + //Since we only care about the major and minor version, a double conversion is good enough. (4.1.20 -> 4.10000) + double mysql_version = atof(mysql_get_server_info(&mysql_handle)); + + sprintf(tmp_sql, + "delete FROM `%s` USING `%s` as c LEFT JOIN `%s` as i ON c.char_id = i.char_id, `%s` as p WHERE c.char_id = '%d' AND i.card0 = -256 AND p.pet_id = (i.card1|(i.card2<<2))", + (mysql_version<4.1?pet_db:"p"), char_db, inventory_db, pet_db, char_id); + + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + //Removing pets that are in the char's cart.... + sprintf(tmp_sql, + "delete FROM `%s` USING `%s` as c LEFT JOIN `%s` as i ON c.char_id = i.char_id, `%s` as p WHERE c.char_id = '%d' AND i.card0 = -256 AND p.pet_id = (i.card1|(i.card2<<2))", + (mysql_version<4.1?pet_db:"p"), char_db, cart_db, pet_db, char_id); + + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + /* delete char's friends list */ + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `char_id` = '%d'",friend_db, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + /* delete char from other's friend list */ + //NOTE: Won't this cause problems for people who are already online? [Skotlex] + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `friend_id` = '%d'",friend_db, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + /* delete inventory */ + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",inventory_db, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + /* delete cart inventory */ + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",cart_db, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + /* delete memo areas */ + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",memo_db, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + /* delete character registry */ + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + /* delete skills */ + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",skill_db, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + +#ifdef ENABLE_SC_SAVING + /* status changes */ + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", + scdata_db, account_id, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } +#endif + + if (log_char) { + sprintf(tmp_sql,"INSERT INTO `%s`(`time`, `account_id`,`char_num`,`char_msg`,`name`) VALUES (NOW(), '%d', '%d', 'Deleted char (CID %d)', '%s')", + charlog_db, account_id, 0, char_id, t_name); + //query + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + /* delete character */ + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",char_db, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + /* No need as we used inter_guild_leave [Skotlex] + // Also delete info from guildtables. + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",guild_member_db, char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + */ + + sprintf(tmp_sql, "SELECT `guild_id` FROM `%s` WHERE `master` = '%s'", guild_db, t_name); + + if (mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } else { + sql_res = mysql_store_result(&mysql_handle); + + if (sql_res == NULL) { + if (mysql_errno(&mysql_handle) != 0) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + return -1; + } else { + int rows = (int)mysql_num_rows(sql_res); + mysql_free_result(sql_res); + if (rows > 0) { + mapif_parse_BreakGuild(0,guild_id); + } + else if (guild_id) //Leave your guild. + inter_guild_leave(guild_id, account_id, char_id); + } + } + return 0; +} + +//========================================================================================================== + +int count_users(void) { + int i, users; + + if (login_fd > 0 && session[login_fd]){ + users = 0; + for(i = 0; i < MAX_MAP_SERVERS; i++) { + if (server_fd[i] > 0) { + users += server[i].users; + } + } + return users; + } + return 0; +} + +int mmo_char_send006b(int fd, struct char_session_data *sd) { + int i, j, found_num = 0; + struct mmo_charstatus *p = NULL; + const int offset = 24; + WFIFOHEAD(fd, offset +9*106); + + set_char_online(-1, 99,sd->account_id); + + //search char. + sprintf(tmp_sql, "SELECT `char_id` FROM `%s` WHERE `account_id` = '%d'",char_db, sd->account_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + found_num = (int)mysql_num_rows(sql_res); +// ShowInfo("number of chars: %d\n", found_num); + i = 0; + while((sql_row = mysql_fetch_row(sql_res))) { + sd->found_char[i] = atoi(sql_row[0]); + i++; + } + mysql_free_result(sql_res); + } + + for(i = found_num; i < 9; i++) + sd->found_char[i] = -1; + + memset(WFIFOP(fd, 0), 0, offset + found_num * 106); + WFIFOW(fd, 0) = 0x6b; + WFIFOW(fd, 2) = offset + found_num * 106; + + if (save_log) + ShowInfo("Loading Char Data ("CL_BOLD"%d"CL_RESET")\n",sd->account_id); + + for(i = 0; i < found_num; i++) { + mmo_char_fromsql_short(sd->found_char[i], &char_dat); + + p = &char_dat; + + j = offset + (i * 106); // increase speed of code + + WFIFOL(fd,j) = p->char_id; + WFIFOL(fd,j+4) = p->base_exp>LONG_MAX?LONG_MAX:p->base_exp; + WFIFOL(fd,j+8) = p->zeny; + WFIFOL(fd,j+12) = p->job_exp>LONG_MAX?LONG_MAX:p->job_exp; + WFIFOL(fd,j+16) = p->job_level; + + WFIFOL(fd,j+20) = 0; + WFIFOL(fd,j+24) = 0; + WFIFOL(fd,j+28) = p->option; + + WFIFOL(fd,j+32) = p->karma; + WFIFOL(fd,j+36) = p->manner; + + WFIFOW(fd,j+40) = (p->status_point > SHRT_MAX) ? SHRT_MAX : p->status_point; + WFIFOW(fd,j+42) = (p->hp > SHRT_MAX) ? SHRT_MAX : p->hp; + WFIFOW(fd,j+44) = (p->max_hp > SHRT_MAX) ? SHRT_MAX : p->max_hp; + WFIFOW(fd,j+46) = (p->sp > SHRT_MAX) ? SHRT_MAX : p->sp; + WFIFOW(fd,j+48) = (p->max_sp > SHRT_MAX) ? SHRT_MAX : p->max_sp; + WFIFOW(fd,j+50) = DEFAULT_WALK_SPEED; // p->speed; + WFIFOW(fd,j+52) = p->class_; + WFIFOW(fd,j+54) = p->hair; + WFIFOW(fd,j+56) = p->option&0x20?0:p->weapon; //When the weapon is sent and your option is riding, the client crashes on login!? + WFIFOW(fd,j+58) = p->base_level; + WFIFOW(fd,j+60) = (p->skill_point > SHRT_MAX) ? SHRT_MAX : p->skill_point; + WFIFOW(fd,j+62) = p->head_bottom; + WFIFOW(fd,j+64) = p->shield; + WFIFOW(fd,j+66) = p->head_top; + WFIFOW(fd,j+68) = p->head_mid; + WFIFOW(fd,j+70) = p->hair_color; + WFIFOW(fd,j+72) = p->clothes_color; + + memcpy(WFIFOP(fd,j+74), p->name, NAME_LENGTH); + + WFIFOB(fd,j+98) = (p->str > UCHAR_MAX) ? UCHAR_MAX : p->str; + WFIFOB(fd,j+99) = (p->agi > UCHAR_MAX) ? UCHAR_MAX : p->agi; + WFIFOB(fd,j+100) = (p->vit > UCHAR_MAX) ? UCHAR_MAX : p->vit; + WFIFOB(fd,j+101) = (p->int_ > UCHAR_MAX) ? UCHAR_MAX : p->int_; + WFIFOB(fd,j+102) = (p->dex > UCHAR_MAX) ? UCHAR_MAX : p->dex; + WFIFOB(fd,j+103) = (p->luk > UCHAR_MAX) ? UCHAR_MAX : p->luk; + WFIFOB(fd,j+104) = p->char_num; + } + + WFIFOSET(fd,WFIFOW(fd,2)); +// printf("mmo_char_send006b end..\n"); + return 0; +} + +int send_accounts_tologin(int tid, unsigned int tick, int id, int data); + +int parse_tologin(int fd) { + int i; + struct char_session_data *sd; + RFIFOHEAD(fd); + // only login-server can have an access to here. + // so, if it isn't the login-server, we disconnect the session. + //session eof check! + if(fd != login_fd) + session[fd]->eof = 1; + if(session[fd]->eof) { + if (fd == login_fd) { + ShowWarning("Connection to login-server lost (connection #%d).\n", fd); + login_fd = -1; + } + do_close(fd); + return 0; + } + + sd = (struct char_session_data*)session[fd]->session_data; + + // hehe. no need to set user limit on SQL version. :P + // but char limitation is good way to maintain server. :D + while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { +// printf("parse_tologin : %d %d %x\n", fd, RFIFOREST(fd), RFIFOW(fd, 0)); + + switch(RFIFOW(fd, 0)){ + case 0x2711: + if (RFIFOREST(fd) < 3) + return 0; + if (RFIFOB(fd, 2)) { + //printf("connect login server error : %d\n", RFIFOB(fd, 2)); + ShowError("Can not connect to login-server.\n"); + ShowError("The server communication passwords (default s1/p1) are probably invalid.\n"); + ShowError("Also, please make sure your login db has the correct coounication username/passwords and the gender of the account is S.\n"); + ShowError("The communication passwords are set in map_athena.conf and char_athena.conf\n"); + return 0; + //exit(1); //fixed for server shutdown. + }else { + ShowStatus("Connected to login-server (connection #%d).\n", fd); + + //Send online accounts to login server. + send_accounts_tologin(-1, gettick(), 0, 0); + + // if no map-server already connected, display a message... + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] > 0 && server[i].map[0]) // if map-server online and at least 1 map + break; + if (i == MAX_MAP_SERVERS) + ShowStatus("Awaiting maps from map-server.\n"); + } + RFIFOSKIP(fd, 3); + break; + + case 0x2713: + if(RFIFOREST(fd)<51) + return 0; + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2)) { + WFIFOHEAD(i,3); + if (RFIFOB(fd,6) != 0) { + WFIFOW(i,0) = 0x6c; + WFIFOB(i,2) = 0x42; + WFIFOSET(i,3); + } else if (max_connect_user == 0 || count_users() < max_connect_user) { +// if (max_connect_user == 0) +// printf("max_connect_user (unlimited) -> accepted.\n"); +// else +// printf("count_users(): %d < max_connect_user (%d) -> accepted.\n", count_users(), max_connect_user); + sd->connect_until_time = (time_t)RFIFOL(fd,47); + memcpy(sd->email, RFIFOP(fd, 7), 40); + // send characters to player + mmo_char_send006b(i, sd); + } else if(isGM(sd->account_id) >= gm_allow_level) { + sd->connect_until_time = (time_t)RFIFOL(fd,47); + memcpy(sd->email, RFIFOP(fd, 7), 40); + // send characters to player + mmo_char_send006b(i, sd); + } else { + // refuse connection: too much online players +// printf("count_users(): %d < max_connect_use (%d) -> fail...\n", count_users(), max_connect_user); + WFIFOW(i,0) = 0x6c; + WFIFOW(i,2) = 0; + WFIFOSET(i,3); + } + } + } + RFIFOSKIP(fd,51); + break; + + case 0x2717: + if (RFIFOREST(fd) < 50) + return 0; + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { + if (sd->account_id == RFIFOL(fd,2)) { + memcpy(sd->email, RFIFOP(fd, 6), 40); + sd->connect_until_time = (time_t)RFIFOL(fd,46); + break; + } + } + } + RFIFOSKIP(fd,50); + break; + + // login-server alive packet + case 0x2718: + if (RFIFOREST(fd) < 2) + return 0; + RFIFOSKIP(fd,2); + break; + + // Receiving authentification from Freya-type login server (to avoid char->login->char) + case 0x2719: + if (RFIFOREST(fd) < 18) + return 0; + // to conserv a maximum of authentification, search if account is already authentified and replace it + // that will reduce multiple connection too + for(i = 0; i < AUTH_FIFO_SIZE; i++) + if (auth_fifo[i].account_id == RFIFOL(fd,2)) + break; + // if not found, use next value + if (i == AUTH_FIFO_SIZE) { + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + i = auth_fifo_pos; + auth_fifo_pos++; + } + //printf("auth_fifo set (auth #%d) - account: %d, secure: %08x-%08x\n", i, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); + auth_fifo[i].account_id = RFIFOL(fd,2); + auth_fifo[i].char_id = 0; + auth_fifo[i].login_id1 = RFIFOL(fd,6); + auth_fifo[i].login_id2 = RFIFOL(fd,10); + auth_fifo[i].delflag = 2; // 0: auth_fifo canceled/void, 2: auth_fifo received from login/map server in memory, 1: connection authentified + auth_fifo[i].char_pos = 0; + auth_fifo[i].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) + auth_fifo[i].ip = RFIFOL(fd,14); + //auth_fifo[i].map_auth = 0; + RFIFOSKIP(fd,18); + break; + + case 0x2721: // gm reply. I don't want to support this function. + if (RFIFOREST(fd) < 10) + return 0; + RFIFOSKIP(fd, 10); +/* Note that this is the code from char-txt! Even uncommenting it will not work. + printf("0x2721:GM reply\n"); + { + int oldacc, newacc; + unsigned char buf[64]; + if (RFIFOREST(fd) < 10) + return 0; + oldacc = RFIFOL(fd, 2); + newacc = RFIFOL(fd, 6); + RFIFOSKIP(fd, 10); + if (newacc > 0) { + for(i=0;i map\n"); + } +*/ + break; + case 0x2723: // changesex reply (modified by [Yor]) + if (RFIFOREST(fd) < 7) + return 0; + { + int acc, sex; + unsigned char buf[16]; + MYSQL_RES* sql_res2; + acc = RFIFOL(fd,2); + sex = RFIFOB(fd,6); + RFIFOSKIP(fd, 7); + if (acc > 0) { + sprintf(tmp_sql, "SELECT `char_id`,`class`,`skill_point`,`guild_id` FROM `%s` WHERE `account_id` = '%d'",char_db, acc); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res2 = mysql_store_result(&mysql_handle); + + while(sql_res2 && (sql_row = mysql_fetch_row(sql_res2))) { + int char_id, guild_id, jobclass, skill_point, class_; + char_id = atoi(sql_row[0]); + jobclass = atoi(sql_row[1]); + skill_point = atoi(sql_row[2]); + guild_id = atoi(sql_row[3]); + class_ = jobclass; + if (jobclass == JOB_BARD || jobclass == JOB_DANCER || + jobclass == JOB_CLOWN || jobclass == JOB_GYPSY || + jobclass == JOB_BABY_BARD || jobclass == JOB_BABY_DANCER) { + // job modification + if (jobclass == JOB_BARD || jobclass == JOB_DANCER) { + class_ = (sex) ? JOB_BARD : JOB_DANCER; + } else if (jobclass == JOB_CLOWN || jobclass == JOB_GYPSY) { + class_ = (sex) ? JOB_CLOWN : JOB_GYPSY; + } else if (jobclass == JOB_BABY_BARD || jobclass == JOB_BABY_DANCER) { + class_ = (sex) ? JOB_BABY_BARD : JOB_BABY_DANCER; + } + // remove specifical skills of classes 19,20 4020,4021 and 4042,4043 + sprintf(tmp_sql, "SELECT `lv` FROM `%s` WHERE `char_id` = '%d' AND `id` >= '315' AND `id` <= '330'",skill_db, char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + while(( sql_row = mysql_fetch_row(sql_res))) { + skill_point += atoi(sql_row[0]); + } + mysql_free_result(sql_res); + } + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `char_id` = '%d' AND `id` >= '315' AND `id` <= '330'",skill_db, char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + // to avoid any problem with equipment and invalid sex, equipment is unequiped. + sprintf(tmp_sql, "UPDATE `%s` SET `equip` = '0' WHERE `char_id` = '%d'",inventory_db, char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sprintf(tmp_sql, "UPDATE `%s` SET `class`='%d' , `skill_point`='%d' , `weapon`='0' , `shield`='0' , `head_top`='0' , `head_mid`='0' , `head_bottom`='0' WHERE `char_id` = '%d'",char_db, class_, skill_point, char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + if (guild_id) //If there is a guild, update the guild_member data [Skotlex] + inter_guild_sex_changed(guild_id, acc, char_id, sex); + } + if (sql_res2) + mysql_free_result(sql_res2); + } + // disconnect player if online on char-server + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { + if (sd->account_id == acc) { + session[i]->eof = 1; + break; + } + } + } + + WBUFW(buf,0) = 0x2b0d; + WBUFL(buf,2) = acc; + WBUFB(buf,6) = sex; + + mapif_sendall(buf, 7); + } + break; + + // account_reg2変更通知 + case 0x2729: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { //Receive account_reg2 registry, forward to map servers. + unsigned char buf[ACCOUNT_REG2_NUM*(256+32+2)+16]; + memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2)); +// WBUFW(buf,0) = 0x2b11; + WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex] + mapif_sendall(buf, WBUFW(buf,2)); + RFIFOSKIP(fd, RFIFOW(fd,2)); + } + break; + + // State change of account/ban notification (from login-server) by [Yor] + case 0x2731: + if (RFIFOREST(fd) < 11) + return 0; + // send to all map-servers to disconnect the player + { + unsigned char buf[16]; + WBUFW(buf,0) = 0x2b14; + WBUFL(buf,2) = RFIFOL(fd,2); + WBUFB(buf,6) = RFIFOB(fd,6); // 0: change of statut, 1: ban + WBUFL(buf,7) = RFIFOL(fd,7); // status or final date of a banishment + mapif_sendall(buf, 11); + } + // disconnect player if online on char-server + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { + if (sd->account_id == RFIFOL(fd,2)) { + session[i]->eof = 1; + break; + } + } + } + RFIFOSKIP(fd,11); + break; + + case 0x2732: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + if(!char_gm_read) { + unsigned char buf[32000]; + if (gm_account != NULL) + aFree(gm_account); + gm_account = (struct gm_account*)aCalloc(sizeof(struct gm_account) * ((RFIFOW(fd,2) - 4) / 5), 1); + GM_num = 0; + for (i = 4; i < RFIFOW(fd,2); i = i + 5) { + gm_account[GM_num].account_id = RFIFOL(fd,i); + gm_account[GM_num].level = (int)RFIFOB(fd,i+4); + //printf("GM account: %d -> level %d\n", gm_account[GM_num].account_id, gm_account[GM_num].level); + GM_num++; + } + ShowStatus("From login-server: receiving information of %d GM accounts.\n", GM_num); + // send new gm acccounts level to map-servers + memcpy(buf, RFIFOP(fd,0), RFIFOW(fd,2)); + WBUFW(buf,0) = 0x2b15; + mapif_sendall(buf, RFIFOW(fd,2)); + } + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + + // Receive GM accounts [Freya login server packet by Yor] + case 0x2733: + // add test here to remember that the login-server is Freya-type + // sprintf (login_server_type, "Freya"); + if (RFIFOREST(fd) < 7) + return 0; + { + int new_level = 0; + for(i = 0; i < GM_num; i++) + if (gm_account[i].account_id == RFIFOL(fd,2)) { + if (gm_account[i].level != (int)RFIFOB(fd,6)) { + gm_account[i].level = (int)RFIFOB(fd,6); + new_level = 1; + } + break; + } + // if not found, add it + if (i == GM_num) { + // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???) + // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) + if (((int)RFIFOB(fd,6)) > 0 && GM_num < 4000) { + if (GM_num == 0) { + gm_account = (struct gm_account*)aMalloc(sizeof(struct gm_account)); + } else { + gm_account = (struct gm_account*)aRealloc(gm_account, sizeof(struct gm_account) * (GM_num + 1)); + } + gm_account[GM_num].account_id = RFIFOL(fd,2); + gm_account[GM_num].level = (int)RFIFOB(fd,6); + new_level = 1; + GM_num++; + if (GM_num >= 4000) + ShowWarning("4000 GM accounts found. Next GM accounts are not readed.\n"); + } + } + if (new_level == 1) { + ShowStatus("From login-server: receiving GM account information (%d: level %d).\n", RFIFOL(fd,2), (int)RFIFOB(fd,6)); + mapif_send_gmaccounts(); + + //create_online_files(); // not change online file for only 1 player (in next timer, that will be done + // send gm acccounts level to map-servers + } + } + RFIFOSKIP(fd,7); + break; + + //Login server request to kick a character out. [Skotlex] + case 0x2734: + if (RFIFOREST(fd) < 6) + return 0; + { + struct online_char_data* character; + int aid = RFIFOL(fd,2); + if ((character = idb_get(online_char_db, aid)) != NULL) + { //Kick out this player. + if (character->server > -1) + { //Kick it from the map server it is on. + mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); + if (!character->waiting_disconnect) + add_timer(gettick()+15000, chardb_waiting_disconnect, character->account_id, 0); + character->waiting_disconnect = 1; + } else { //Manual kick from char server. + struct char_session_data *tsd; + int i; + for(i = 0; i < fd_max; i++) { + if (session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid) + { + WFIFOHEAD(i,3); + WFIFOW(i,0) = 0x81; + WFIFOB(i,2) = 2; + WFIFOSET(i,3); + break; + } + } + if (i == fd_max) //Shouldn't happen, but just in case. + set_char_offline(99, aid); + } + } + RFIFOSKIP(fd,6); + } + break; + + case 0x2735: + { + unsigned char buf[2]; + in_addr_t new_ip = 0; + RFIFOSKIP(fd,2); + + WBUFW(buf,0) = 0x2b1e; + mapif_sendall(buf, 2); + + new_ip = resolve_hostbyname(login_ip_str, NULL, NULL); + if (new_ip && new_ip != login_ip) //Update login ip, too. + login_ip = new_ip; + + new_ip = resolve_hostbyname(char_ip_str, NULL, NULL); + if (new_ip && new_ip != char_ip) + { //Update ip. + char_ip = new_ip; + ShowInfo("Updating IP for [%s].\n",char_ip_str); + WFIFOHEAD(fd,6); + WFIFOW(fd,0) = 0x2736; + WFIFOL(fd,2) = char_ip; + WFIFOSET(fd,6); + } + break; + } + default: + ShowError("Unknown packet 0x%04x from login server, disconnecting.\n", RFIFOW(fd, 0)); + session[fd]->eof = 1; + return 0; + } + } + + RFIFOFLUSH(fd); + + return 0; +} + +int request_accreg2(int account_id, int char_id) { + if (login_fd > 0) { + WFIFOHEAD(login_fd, 10); + WFIFOW(login_fd, 0) = 0x272e; + WFIFOL(login_fd, 2) = account_id; + WFIFOL(login_fd, 6) = char_id; + WFIFOSET(login_fd, 10); + return 1; + } + return 0; +} +//Send packet forward to login-server for account saving +int save_accreg2(unsigned char* buf, int len) { + if (login_fd > 0) { + WFIFOHEAD(login_fd, len+4); + memcpy(WFIFOP(login_fd,4), buf, len); + WFIFOW(login_fd,0) = 0x2728; + WFIFOW(login_fd,2) = len+4; + WFIFOSET(login_fd,len+4); + return 1; + } + return 0; +} + +void char_read_fame_list(void) +{ + int i; + struct fame_list fame_item; + + // Empty ranking lists + memset(smith_fame_list, 0, sizeof(smith_fame_list)); + memset(chemist_fame_list, 0, sizeof(chemist_fame_list)); + memset(taekwon_fame_list, 0, sizeof(taekwon_fame_list)); + // Build Blacksmith ranking list + sprintf(tmp_sql, "SELECT `char_id`,`fame`, `name` FROM `%s` WHERE `fame`>0 AND (`class`='%d' OR `class`='%d' OR `class`='%d') ORDER BY `fame` DESC LIMIT 0,%d", char_db, JOB_BLACKSMITH, JOB_WHITESMITH, JOB_BABY_BLACKSMITH, fame_list_size_smith); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + i = 0; + while((sql_row = mysql_fetch_row(sql_res))) { + fame_item.id = atoi(sql_row[0]); + fame_item.fame = atoi(sql_row[1]); + strncpy(fame_item.name, sql_row[2], NAME_LENGTH); + memcpy(&smith_fame_list[i], &fame_item, sizeof(struct fame_list)); + + if (++i == fame_list_size_smith) + break; + } + mysql_free_result(sql_res); + } + // Build Alchemist ranking list + sprintf(tmp_sql, "SELECT `char_id`,`fame`, `name` FROM `%s` WHERE `fame`>0 AND (`class`='%d' OR `class`='%d' OR `class`='%d') ORDER BY `fame` DESC LIMIT 0,%d", char_db, JOB_ALCHEMIST, JOB_CREATOR, JOB_BABY_ALCHEMIST, fame_list_size_chemist); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + i = 0; + while((sql_row = mysql_fetch_row(sql_res))) { + fame_item.id = atoi(sql_row[0]); + fame_item.fame = atoi(sql_row[1]); + strncpy(fame_item.name, sql_row[2], NAME_LENGTH); + + memcpy(&chemist_fame_list[i], &fame_item, sizeof(struct fame_list)); + + if (++i == fame_list_size_chemist) + break; + } + mysql_free_result(sql_res); + } + // Build Taekwon ranking list + sprintf(tmp_sql, "SELECT `char_id`,`fame`, `name` FROM `%s` WHERE `fame`>0 AND (`class`='%d') ORDER BY `fame` DESC LIMIT 0,%d", char_db, JOB_TAEKWON, fame_list_size_taekwon); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + i = 0; + while((sql_row = mysql_fetch_row(sql_res))) { + fame_item.id = atoi(sql_row[0]); + fame_item.fame = atoi(sql_row[1]); + strncpy(fame_item.name, sql_row[2], NAME_LENGTH); + + memcpy(&taekwon_fame_list[i], &fame_item, sizeof(struct fame_list)); + + if (++i == fame_list_size_taekwon) + break; + } + mysql_free_result(sql_res); + } +} + +// Send map-servers the fame ranking lists +int char_send_fame_list(int fd) { + int i, len = 8; + unsigned char buf[32000]; + + WBUFW(buf,0) = 0x2b1b; + + for(i = 0; i < fame_list_size_smith && smith_fame_list[i].id; i++) { + memcpy(WBUFP(buf, len), &smith_fame_list[i], sizeof(struct fame_list)); + len += sizeof(struct fame_list); + } + // add blacksmith's block length + WBUFW(buf, 6) = len; + + for(i = 0; i < fame_list_size_chemist && chemist_fame_list[i].id; i++) { + memcpy(WBUFP(buf, len), &chemist_fame_list[i], sizeof(struct fame_list)); + len += sizeof(struct fame_list); + } + // add alchemist's block length + WBUFW(buf, 4) = len; + + for(i = 0; i < fame_list_size_taekwon && taekwon_fame_list[i].id; i++) { + memcpy(WBUFP(buf, len), &taekwon_fame_list[i], sizeof(struct fame_list)); + len += sizeof(struct fame_list); + } + // add total packet length + WBUFW(buf, 2) = len; + + if (fd != -1) + mapif_send(fd, buf, len); + else + mapif_sendall(buf, len); + return 0; +} + +int search_mapserver(unsigned short map, long ip, short port); + +//Loads a character's name and stores it in the buffer given (must be NAME_LENGTH in size) +//Returns 1 on found, 0 on not found (buffer is filled with Unknown char name) +int char_loadName(int char_id, char* name) +{ + sprintf(tmp_sql, "SELECT `name` FROM `%s` WHERE `char_id`='%d'", char_db, char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sql_res = mysql_store_result(&mysql_handle); + sql_row = sql_res?mysql_fetch_row(sql_res):NULL; + + if (sql_row) + memcpy(name, sql_row[0], NAME_LENGTH); + else + memcpy(name, unknown_char_name, NAME_LENGTH); + if (sql_res) mysql_free_result(sql_res); + return sql_row?1:0; +} + + +int parse_frommap(int fd) { + int i = 0, j = 0; + int id; + RFIFOHEAD(fd); + + // Sometimes fd=0, and it will cause server crash. Don't know why. :( + if (fd <= 0) { + ShowError("parse_frommap error fd=%d\n", fd); + return 0; + } + + for(id = 0; id < MAX_MAP_SERVERS; id++) + if (server_fd[id] == fd) + break; + if(id == MAX_MAP_SERVERS) + session[fd]->eof = 1; + if(session[fd]->eof) { + if (id < MAX_MAP_SERVERS) { + unsigned char buf[16384]; + ShowStatus("Map-server %d (session #%d) has disconnected.\n", id, fd); + //Notify other map servers that this one is gone. [Skotlex] + WBUFW(buf,0) = 0x2b20; + WBUFL(buf,4) = server[id].ip; + WBUFW(buf,8) = server[id].port; + j = 0; + for(i = 0; i < MAX_MAP_PER_SERVER; i++) + if (server[id].map[i]) + WBUFW(buf,10+(j++)*4) = server[id].map[i]; + if (j > 0) { + WBUFW(buf,2) = j * 4 + 10; + mapif_sendallwos(fd, buf, WBUFW(buf,2)); + } + memset(&server[id], 0, sizeof(struct mmo_map_server)); + sprintf(tmp_sql, "DELETE FROM `ragsrvinfo` WHERE `index`='%d'", server_fd[id]); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + server_fd[id] = -1; + online_char_db->foreach(online_char_db,char_db_setoffline,id); //Tag relevant chars as 'in disconnected' server. + } + do_close(fd); + return 0; + } + + while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { + switch(RFIFOW(fd, 0)) { + + // map-server alive packet + case 0x2718: + RFIFOSKIP(fd,2); + break; + + case 0x2af7: + RFIFOSKIP(fd,2); + if(char_gm_read) //Re-read gm accounts. + read_gm_account(); + //Send to login request to reload gm accounts. + else if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 2); + WFIFOW(login_fd,0) = 0x2709; + WFIFOSET(login_fd, 2); + } + break; + + // mapserver -> map names recv. + case 0x2afa: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + unsigned char *p = (unsigned char *)&server[id].ip; + unsigned char buf[16384]; + int x; + WFIFOHEAD(fd,3+NAME_LENGTH); + + memset(server[id].map, 0, sizeof(server[id].map)); + j = 0; + for(i = 4; i < RFIFOW(fd,2); i += 4) { + server[id].map[j] = RFIFOW(fd,i); + j++; + } + + ShowStatus("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n", + id, j, p[0], p[1], p[2], p[3], server[id].port); + ShowStatus("Map-server %d loading complete.\n", id); + + if (max_account_id != DEFAULT_MAX_ACCOUNT_ID || max_char_id != DEFAULT_MAX_CHAR_ID) + mapif_send_maxid(max_account_id, max_char_id); //Send the current max ids to the server to keep in sync [Skotlex] + + WFIFOW(fd,0) = 0x2afb; + WFIFOB(fd,2) = 0; + // name for wisp to player + memcpy(WFIFOP(fd,3), wisp_server_name, NAME_LENGTH); + WFIFOSET(fd,3+NAME_LENGTH); + + char_send_fame_list(fd); //Send fame list. + + if (j == 0) + ShowWarning("Map-Server %d have NO maps.\n", id); + else { + // Transmitting maps information to the other map-servers + WBUFW(buf,0) = 0x2b04; + WBUFW(buf,2) = j * 4 + 10; + WBUFL(buf,4) = server[id].ip; + WBUFW(buf,8) = server[id].port; + memcpy(WBUFP(buf,10), RFIFOP(fd,4), j * 4); + mapif_sendallwos(fd, buf, WBUFW(buf,2)); + } + // Transmitting the maps of the other map-servers to the new map-server + for(x = 0; x < MAX_MAP_SERVERS; x++) { + if (server_fd[x] > 0 && x != id) { + WFIFOHEAD(fd, 10 +4*MAX_MAP_PER_SERVER); + WFIFOW(fd,0) = 0x2b04; + WFIFOL(fd,4) = server[x].ip; + WFIFOW(fd,8) = server[x].port; + j = 0; + for(i = 0; i < MAX_MAP_PER_SERVER; i++) + if (server[x].map[i]) + WFIFOW(fd,10+(j++)*4) = server[x].map[i]; + if (j > 0) { + WFIFOW(fd,2) = j * 4 + 10; + WFIFOSET(fd,WFIFOW(fd,2)); + } + } + } + RFIFOSKIP(fd,RFIFOW(fd,2)); + } + break; + //Packet command is now used for sc_data request. [Skotlex] + case 0x2afc: + if (RFIFOREST(fd) < 10) + return 0; + { + int aid, cid; + aid = RFIFOL(fd,2); + cid = RFIFOL(fd,6); + RFIFOSKIP(fd, 10); +#ifdef ENABLE_SC_SAVING + sprintf(tmp_sql, "SELECT type, tick, val1, val2, val3, val4 from `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", + scdata_db, aid, cid); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + break; + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + struct status_change_data data; + int count = 0; + WFIFOHEAD(fd, 14+50*sizeof(struct status_change_data)); + WFIFOW(fd, 0) = 0x2b1d; + WFIFOL(fd, 4) = aid; + WFIFOL(fd, 8) = cid; + while((sql_row = mysql_fetch_row(sql_res)) && count < 50) + { + data.type = atoi(sql_row[0]); + data.tick = atoi(sql_row[1]); + data.val1 = atoi(sql_row[2]); + data.val2 = atoi(sql_row[3]); + data.val3 = atoi(sql_row[4]); + data.val4 = atoi(sql_row[5]); + memcpy(WFIFOP(fd, 14+count*sizeof(struct status_change_data)), &data, sizeof(struct status_change_data)); + count++; + } + if (count >= 50) + ShowWarning("Too many status changes for %d:%d, some of them were not loaded.\n", aid, cid); + mysql_free_result(sql_res); + if (count > 0) + { + WFIFOW(fd, 2) = 14 + count*sizeof(struct status_change_data); + WFIFOW(fd, 12) = count; + WFIFOSET(fd, WFIFOW(fd,2)); + + //Clear the data once loaded. + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, aid, cid); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + } +#endif + break; + } + //set MAP user count + case 0x2afe: + if (RFIFOREST(fd) < 4) + return 0; + if (RFIFOW(fd,2) != server[id].users) { + server[id].users = RFIFOW(fd,2); + ShowInfo("User Count: %d (Server: %d)\n", server[id].users, id); + } + RFIFOSKIP(fd, 4); + break; + // set MAP user + case 0x2aff: + if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + //TODO: When data mismatches memory, update guild/party online/offline states. + int i, aid, cid; + struct online_char_data* character; + + online_char_db->foreach(online_char_db,char_db_setoffline,id); //Set all chars from this server as 'unknown' + server[id].users = RFIFOW(fd,4); + for(i = 0; i < server[id].users; i++) { + aid = RFIFOL(fd,6+i*8); + cid = RFIFOL(fd,6+i*8+4); + character = idb_ensure(online_char_db, aid, create_online_char_data); + if (character->server > -1 && character->server != id) + { + ShowNotice("Set map user: Character (%d:%d) marked on map server %d, but map server %d claims to have (%d:%d) online!\n", + character->account_id, character->char_id, character->server, id, aid, cid); + mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); + } + character->server = id; + character->char_id = cid; + } + //If any chars remain in -2, they will be cleaned in the cleanup timer. + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + } + // char saving + case 0x2b01: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8), size = RFIFOW(fd,2); + struct online_char_data* character; + if (size - 13 != sizeof(struct mmo_charstatus)) + { + ShowError("parse_from_map (save-char): Size mismatch! %d != %d\n", size-13, sizeof(struct mmo_charstatus)); + RFIFOSKIP(fd,size); + break; + } + //Check account only if this ain't final save. Final-save goes through because of the char-map reconnect + if (RFIFOB(fd,12) || ( + (character = idb_get(online_char_db, aid)) != NULL && + character->char_id == cid)) + { + memcpy(&char_dat, RFIFOP(fd,13), sizeof(struct mmo_charstatus)); + mmo_char_tosql(cid, &char_dat); + } else { //This may be valid on char-server reconnection, when re-sending characters that already logged off. + ShowError("parse_from_map (save-char): Received data for non-existant/offline character (%d:%d).\n", aid, cid); + set_char_online(id, cid, aid); + } + + if (RFIFOB(fd,12)) + { //Flag? Set character offline after saving [Skotlex] + set_char_offline(cid, aid); + WFIFOHEAD(fd, 10); + WFIFOW(fd, 0) = 0x2b21; //Save ack only needed on final save. + WFIFOL(fd, 2) = aid; + WFIFOL(fd, 6) = cid; + WFIFOSET(fd, 10); + } + RFIFOSKIP(fd,size); + break; + } + // req char selection + case 0x2b02: + if (RFIFOREST(fd) < 18) + return 0; + + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + + auth_fifo[auth_fifo_pos].account_id = RFIFOL(fd, 2); + auth_fifo[auth_fifo_pos].char_id = 0; + auth_fifo[auth_fifo_pos].login_id1 = RFIFOL(fd, 6); + auth_fifo[auth_fifo_pos].login_id2 = RFIFOL(fd,10); + auth_fifo[auth_fifo_pos].delflag = 2; + auth_fifo[auth_fifo_pos].char_pos = 0; + auth_fifo[auth_fifo_pos].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) + auth_fifo[auth_fifo_pos].ip = RFIFOL(fd,14); + auth_fifo_pos++; + { + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) = 0x2b03; + WFIFOL(fd, 2) = RFIFOL(fd, 2); + WFIFOB(fd, 6) = 0; + WFIFOSET(fd, 7); + } + RFIFOSKIP(fd, 18); + break; + + // request "change map server" + case 0x2b05: + if (RFIFOREST(fd) < 35) + return 0; + { + unsigned short name; + int map_id, map_fd = -1; + struct online_char_data* data; + struct mmo_charstatus* char_data; + name = RFIFOW(fd,18); + map_id = search_mapserver(name, RFIFOL(fd,24), RFIFOW(fd,28)); //Locate mapserver by ip and port. + if (map_id >= 0) + map_fd = server_fd[map_id]; + //Char should just had been saved before this packet, so this should be safe. [Skotlex] + char_data = uidb_get(char_db_,RFIFOL(fd,14)); + if (char_data == NULL) + { //Really shouldn't happen. + mmo_char_fromsql(RFIFOL(fd,14), &char_dat); + char_data = &char_dat; + } + //Tell the new map server about this player using Kevin's new auth packet. [Skotlex] + if (map_fd>=0 && session[map_fd] && char_data) + { //Send the map server the auth of this player. + //Update the "last map" as this is where the player must be spawned on the new map server. + WFIFOHEAD(fd, 30); + WFIFOHEAD(map_fd, 20 + sizeof(struct mmo_charstatus)); + char_data->last_point.map = RFIFOW(fd,18); + char_data->last_point.x = RFIFOW(fd,20); + char_data->last_point.y = RFIFOW(fd,22); + char_data->sex = RFIFOB(fd,30); // Buuyo^ + + WFIFOW(map_fd,0) = 0x2afd; + WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus); + WFIFOL(map_fd,4) = RFIFOL(fd, 2); //Account ID + WFIFOL(map_fd,8) = RFIFOL(fd, 6); //Login1 + WFIFOL(map_fd,16) = RFIFOL(fd,10); //Login2 + WFIFOL(map_fd,12) = (unsigned long)0; //TODO: connect_until_time, how do I figure it out right now? + memcpy(WFIFOP(map_fd,20), char_data, sizeof(struct mmo_charstatus)); + WFIFOSET(map_fd, WFIFOW(map_fd,2)); + data = idb_ensure(online_char_db, RFIFOL(fd, 2), create_online_char_data); + data->char_id = char_data->char_id; + data->server = map_id; //Update server where char is. + + //Reply with an ack. + WFIFOW(fd, 0) = 0x2b06; + memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28); + WFIFOSET(fd, 30); + } else { //Reply with nak + WFIFOHEAD(fd, 30); + WFIFOW(fd, 0) = 0x2b06; + memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 28); + WFIFOL(fd, 6) = 0; //Set login1 to 0. + WFIFOSET(fd, 30); + } + RFIFOSKIP(fd, 35); + } + break; + + // char name check + case 0x2b08: + if (RFIFOREST(fd) < 6) + return 0; + { + char name[NAME_LENGTH]; + WFIFOHEAD(fd,30); + char_loadName((int)RFIFOL(fd,2), name); + WFIFOW(fd,0) = 0x2b09; + WFIFOL(fd,2) = RFIFOL(fd,2); + memcpy(WFIFOP(fd,6), name, NAME_LENGTH); + WFIFOSET(fd,30); + RFIFOSKIP(fd,6); + } + break; + + // I want become GM - fuck! + case 0x2b0a: + if(RFIFOREST(fd)<4) + return 0; + if(RFIFOREST(fd) login %d %s %d\n", RFIFOL(fd, 4), RFIFOP(fd, 8), RFIFOW(fd, 2)); + */ + ShowWarning("packet 0x2ba (become GM) is not supported by the Char-Server.\n"); + RFIFOSKIP(fd, RFIFOW(fd, 2)); + break; + + // Map server send information to change an email of an account -> login-server + case 0x2b0c: + if (RFIFOREST(fd) < 86) + return 0; + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 86); + memcpy(WFIFOP(login_fd,0), RFIFOP(fd,0), 86); // 0x2722 .L .40B .40B + WFIFOW(login_fd,0) = 0x2722; + WFIFOSET(login_fd, 86); + } + RFIFOSKIP(fd, 86); + break; + + // Receiving from map-server a status change resquest. Transmission to login-server (by Yor) + case 0x2b0e: + if (RFIFOREST(fd) < 44) + return 0; + { + char character_name[NAME_LENGTH], t_name[NAME_LENGTH*2]; + int acc = RFIFOL(fd,2); // account_id of who ask (-1 if nobody) + WFIFOHEAD(fd, 34); + memcpy(character_name, RFIFOP(fd,6), NAME_LENGTH); + character_name[NAME_LENGTH-1] = '\0'; + jstrescapecpy(t_name, character_name); //Escape string for sql use... [Skotlex] + // prepare answer + WFIFOW(fd,0) = 0x2b0f; // answer + WFIFOL(fd,2) = acc; // who want do operation + WFIFOW(fd,30) = RFIFOW(fd, 30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban + sprintf(tmp_sql, "SELECT `account_id`,`name` FROM `%s` WHERE `name` = '%s'",char_db, t_name); + + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sql_res = mysql_store_result(&mysql_handle); + + if (sql_res) { + if (mysql_num_rows(sql_res)) { + sql_row = mysql_fetch_row(sql_res); + memcpy(WFIFOP(fd,6), sql_row[1], NAME_LENGTH); // put correct name if found + WFIFOW(fd,32) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + switch(RFIFOW(fd, 30)) { + case 1: // block + if (acc == -1 || isGM(acc) >= isGM(atoi(sql_row[0]))) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 10); + WFIFOW(login_fd,0) = 0x2724; + WFIFOL(login_fd,2) = atoi(sql_row[0]); // account value + WFIFOL(login_fd,6) = 5; // status of the account + WFIFOSET(login_fd, 10); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 2: // ban + if (acc == -1 || isGM(acc) >= isGM(atoi(sql_row[0]))) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 18); + WFIFOW(login_fd, 0) = 0x2725; + WFIFOL(login_fd, 2) = atoi(sql_row[0]); // account value + WFIFOW(login_fd, 6) = RFIFOW(fd,32); // year + WFIFOW(login_fd, 8) = RFIFOW(fd,34); // month + WFIFOW(login_fd,10) = RFIFOW(fd,36); // day + WFIFOW(login_fd,12) = RFIFOW(fd,38); // hour + WFIFOW(login_fd,14) = RFIFOW(fd,40); // minute + WFIFOW(login_fd,16) = RFIFOW(fd,42); // second + WFIFOSET(login_fd,18); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 3: // unblock + if (acc == -1 || isGM(acc) >= isGM(atoi(sql_row[0]))) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 10); + WFIFOW(login_fd,0) = 0x2724; + WFIFOL(login_fd,2) = atoi(sql_row[0]); // account value + WFIFOL(login_fd,6) = 0; // status of the account + WFIFOSET(login_fd, 10); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 4: // unban + if (acc == -1 || isGM(acc) >= isGM(atoi(sql_row[0]))) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 6); + WFIFOW(login_fd, 0) = 0x272a; + WFIFOL(login_fd, 2) = atoi(sql_row[0]); // account value + WFIFOSET(login_fd, 6); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 5: // changesex + if (acc == -1 || isGM(acc) >= isGM(atoi(sql_row[0]))) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd, 6); + WFIFOW(login_fd, 0) = 0x2727; + WFIFOL(login_fd, 2) = atoi(sql_row[0]); // account value + WFIFOSET(login_fd, 6); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + } + } else { + // character name not found + memcpy(WFIFOP(fd,6), character_name, NAME_LENGTH); + WFIFOW(fd,32) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } + // send answer if a player ask, not if the server ask + if (acc != -1) { + WFIFOSET(fd, 34); + } + mysql_free_result(sql_res); + } + } + RFIFOSKIP(fd, 44); + break; + +// case 0x2b0f: Not used anymore, available for future use + + // Update and send fame ranking list [DracoRPG] + case 0x2b10: + if (RFIFOREST(fd) < 12) + return 0; + { + int cid = RFIFOL(fd, 2); + int fame = RFIFOL(fd, 6); + char type = RFIFOB(fd, 10); + char pos = RFIFOB(fd, 11); + int size = 0; + struct fame_list *list = NULL; + RFIFOSKIP(fd,12); + + switch(type) { + case 1: + size = fame_list_size_smith; + list = smith_fame_list; + break; + case 2: + size = fame_list_size_chemist; + list = chemist_fame_list; + break; + case 3: + size = fame_list_size_taekwon; + list = taekwon_fame_list; + break; + } + if(!size) break; //No list. + if(pos) + { + pos--; //Convert from pos to index. + if( + (pos == 0 || fame < list[pos-1].fame) && + (pos == size-1 || fame > list[pos+1].fame) + ) { //No change in order. + list[(int)pos].fame = fame; + char_send_fame_list(fd); + break; + } + // If the player's already in the list, remove the entry and shift the following ones 1 step up + memmove(list+pos, list+pos+1, (size-pos-1) * sizeof(struct fame_list)); + //Clear out last entry. + list[size-1].id = 0; + list[size-1].fame = 0; + } + + // Find the position where the player has to be inserted + for(i = 0; i < size && fame < list[i].fame; i++); + if(i>=size) break; //Out of ranking. + // When found someone with less or as much fame, insert just above + memmove(list+i+1, list+i, (size-i-1) * sizeof(struct fame_list)); + list[i].id = cid; + list[i].fame = fame; + // Look for the player's name + char_loadName(list[i].id, list[i].name); + char_send_fame_list(-1); + } + + break; + + // Receive rates [Wizputer] + case 0x2b16: + if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,8)) + return 0; + { + char motd[256], t_name[512]; //Required for jstrescapecpy [Skotlex] + strncpy(motd, RFIFOP(fd,10), 255); //First copy it to make sure the motd fits. + motd[255]='\0'; + jstrescapecpy(t_name,motd); + + sprintf(tmp_sql, "INSERT INTO `ragsrvinfo` SET `index`='%d',`name`='%s',`exp`='%d',`jexp`='%d',`drop`='%d',`motd`='%s'", + fd, server_name, RFIFOW(fd,2), RFIFOW(fd,4), RFIFOW(fd,6), t_name); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + RFIFOSKIP(fd,RFIFOW(fd,8)); + break; + } + + // Character disconnected set online 0 [Wizputer] + case 0x2b17: + if (RFIFOREST(fd) < 6 ) + return 0; + //printf("Setting %d char offline\n",RFIFOL(fd,2)); + set_char_offline(RFIFOL(fd,2),RFIFOL(fd,6)); + RFIFOSKIP(fd,10); + break; + // Reset all chars to offline [Wizputer] + case 0x2b18: + set_all_offline(id); + RFIFOSKIP(fd,2); + break; + // Character set online [Wizputer] + case 0x2b19: + if (RFIFOREST(fd) < 6 ) + return 0; + set_char_online(id, RFIFOL(fd,2),RFIFOL(fd,6)); + RFIFOSKIP(fd,10); + break; + + // Build and send fame ranking lists [DracoRPG] + case 0x2b1a: + if (RFIFOREST(fd) < 2) + return 0; + char_read_fame_list(); + char_send_fame_list(-1); + RFIFOSKIP(fd,2); + break; + + //Request saving sc_data of a player. [Skotlex] + case 0x2b1c: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { +#ifdef ENABLE_SC_SAVING + int count, aid, cid, i; + struct status_change_data data; + char *p = tmp_sql; + + aid = RFIFOL(fd, 4); + cid = RFIFOL(fd, 8); + count = RFIFOW(fd, 12); + + p+= sprintf(p, "INSERT INTO `%s` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ", scdata_db); + + for (i = 0; i < count; i++) + { + memcpy (&data, RFIFOP(fd, 14+i*sizeof(struct status_change_data)), sizeof(struct status_change_data)); + p += sprintf (p, " ('%d','%d','%hu','%d','%d','%d','%d','%d'),", aid, cid, + data.type, data.tick, data.val1, data.val2, data.val3, data.val4); + } + if (count > 0) + { + *--p = '\0'; //Remove final comma. + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } +#endif + RFIFOSKIP(fd, RFIFOW(fd, 2)); + break; + } + + case 0x2736: + if (RFIFOREST(fd) < 6) return 0; + ShowInfo("Updated IP address of Server #%d to %d.%d.%d.%d.\n",id, + (int)RFIFOB(fd,2),(int)RFIFOB(fd,3), + (int)RFIFOB(fd,4),(int)RFIFOB(fd,5)); + server[id].ip = RFIFOL(fd, 2); + RFIFOSKIP(fd,6); + break; + + default: + // inter server - packet + { + int r = inter_parse_frommap(fd); + if (r == 1) break; // processed + if (r == 2) return 0; // need more packet + } + + // no inter server packet. no char server packet -> disconnect + ShowError("Unknown packet 0x%04x from map server, disconnecting.\n", RFIFOW(fd,0)); + session[fd]->eof = 1; + return 0; + } + } + return 0; +} + +int search_mapserver(unsigned short map, long ip, short port) { + int i, j; + + if (!map) + return -1; + + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] > 0) + for (j = 0; server[i].map[j]; j++) + if (server[i].map[j] == map) { + if (ip > 0 && server[i].ip != ip) + continue; + if (port > 0 && server[i].port != port) + continue; + return i; + } + + return -1; +} + +int char_mapif_init(int fd) { + return inter_mapif_init(fd); +} + +//-------------------------------------------- +// Test to know if an IP come from LAN or WAN. +// Rewrote: Adnvanced subnet check [LuzZza] +//-------------------------------------------- +int lan_subnetcheck(long *p) { + + int i; + unsigned char *sbn, *msk, *src = (unsigned char *)p; + + for(i=0; iclient_addr.sin_addr; + long subnet_map_ip; + RFIFOHEAD(fd); + + sd = (struct char_session_data*)session[fd]->session_data; + + if(login_fd < 0) + session[fd]->eof = 1; + if(session[fd]->eof) { + if (fd == login_fd) + login_fd = -1; + if (sd != NULL) + { + struct online_char_data* data = idb_get(online_char_db, sd->account_id); + if (!data || data->server== -1) //If it is not in any server, send it offline. [Skotlex] + set_char_offline(99,sd->account_id); + } + do_close(fd); + return 0; + } + + while(RFIFOREST(fd) >= 2 && !session[fd]->eof) { + cmd = RFIFOW(fd,0); + // crc32のスキップ用 + if( sd==NULL && // 未ログインor管理パケット + RFIFOREST(fd)>=4 && // 最低バイト数制限 & 0x7530,0x7532管理パケ除去 + RFIFOREST(fd)<=21 && // 最大バイト数制限 & サーバーログイン除去 + cmd!=0x20b && // md5通知パケット除去 + (RFIFOREST(fd)<6 || RFIFOW(fd,4)==0x65) ){ // 次に何かパケットが来てるなら、接続でないとだめ + RFIFOSKIP(fd,4); + cmd = RFIFOW(fd,0); + ShowDebug("parse_char : %d crc32 skipped\n",fd); + if(RFIFOREST(fd)==0) + return 0; + } + +//For use in packets that depend on an sd being present [Skotlex] +#define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL) { RFIFOSKIP(fd,rest); return 0; } } + + switch(cmd){ + case 0x20b: //20040622 encryption ragexe correspondence + if (RFIFOREST(fd) < 19) + return 0; + RFIFOSKIP(fd,19); + break; + + case 0x65: // request to connect + ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOL(fd, 10)); + if (RFIFOREST(fd) < 17) + return 0; + { + WFIFOHEAD(fd, 4); + + if (sd) { + //Received again auth packet for already authentified account?? Discard it. + //TODO: Perhaps log this as a hack attempt? + RFIFOSKIP(fd,17); + break; + } + CREATE(session[fd]->session_data, struct char_session_data, 1); + sd = (struct char_session_data*)session[fd]->session_data; + sd->connect_until_time = 0; // unknow or illimited (not displaying on map-server) + sd->account_id = RFIFOL(fd, 2); + sd->login_id1 = RFIFOL(fd, 6); + sd->login_id2 = RFIFOL(fd, 10); + sd->sex = RFIFOB(fd, 16); + + WFIFOL(fd, 0) = RFIFOL(fd, 2); + WFIFOSET(fd, 4); + + for(i = 0; i < AUTH_FIFO_SIZE; i++) { + if (auth_fifo[i].account_id == sd->account_id && + auth_fifo[i].login_id1 == sd->login_id1 && +#if CMP_AUTHFIFO_LOGIN2 != 0 + auth_fifo[i].login_id2 == sd->login_id2 && // relate to the versions higher than 18 +#endif + (!check_ip_flag || auth_fifo[i].ip == session[fd]->client_addr.sin_addr.s_addr) && + auth_fifo[i].delflag == 2) { + auth_fifo[i].delflag = 1; + + if (online_check) + { // check if character is not online already. [Skotlex] + struct online_char_data* character; + character = idb_get(online_char_db, sd->account_id); + + if (character) + { + if (character->server > -1) + { //Character already online. KICK KICK KICK + mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); + if (!character->waiting_disconnect) + add_timer(gettick()+20000, chardb_waiting_disconnect, character->account_id, 0); + character->waiting_disconnect = 1; + /* Not a good idea because this would trigger when you do a char-change from the map server! [Skotlex] + } else { //Kick from char server. + struct char_session_data *tsd; + int i; + for(i = 0; i < fd_max; i++) { + if (session[i] && i != fd && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == sd->account_id) + { + WFIFOW(i,0) = 0x81; + WFIFOB(i,2) = 2; + WFIFOSET(i,3); + break; + } + if (i == fd_max) //Shouldn't happen, but just in case. + set_char_offline(99, sd->account_id); + } + */ + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 8; + WFIFOSET(fd,3); + break; + } + } + } + + if (max_connect_user == 0 || count_users() < max_connect_user) { + if (login_fd > 0) { // don't send request if no login-server + // request to login-server to obtain e-mail/time limit + WFIFOHEAD(login_fd, 6); + WFIFOW(login_fd,0) = 0x2716; + WFIFOL(login_fd,2) = sd->account_id; + WFIFOSET(login_fd,6); + } + // send characters to player + mmo_char_send006b(fd, sd); + } else { + // refuse connection (over populated) + WFIFOW(fd,0) = 0x6c; + WFIFOW(fd,2) = 0; + WFIFOSET(fd,3); + } +// printf("connection request> set delflag 1(o:2)- account_id:%d/login_id1:%d(fifo_id:%d)\n", sd->account_id, sd->login_id1, i); + break; + } + } + if (i == AUTH_FIFO_SIZE) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd,19); + WFIFOW(login_fd,0) = 0x2712; // ask login-server to authentify an account + WFIFOL(login_fd,2) = sd->account_id; + WFIFOL(login_fd,6) = sd->login_id1; + WFIFOL(login_fd,10) = sd->login_id2; + WFIFOB(login_fd,14) = sd->sex; + WFIFOL(login_fd,15) = session[fd]->client_addr.sin_addr.s_addr; + WFIFOSET(login_fd,19); + } else { // if no login-server, we must refuse connection + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6c; + WFIFOB(fd,2) = 0; + WFIFOSET(fd,3); + } + } + } + RFIFOSKIP(fd, 17); + break; + + case 0x66: // char select + FIFOSD_CHECK(3); + + sprintf(tmp_sql, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d'",char_db, sd->account_id, RFIFOB(fd, 2)); + RFIFOSKIP(fd, 3); + + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + sql_row = sql_res?mysql_fetch_row(sql_res):NULL; + + if (sql_row) + { + int char_id = atoi(sql_row[0]); + mysql_free_result(sql_res); //Free'd as soon as possible + mmo_char_fromsql(char_id, &char_dat); + char_dat.sex = sd->sex; + } else { + mysql_free_result(sql_res); + break; + } + + if (log_char) { + char escaped_name[NAME_LENGTH*2]; + sprintf(tmp_sql,"INSERT INTO `%s`(`time`, `account_id`,`char_num`,`name`) VALUES (NOW(), '%d', '%d', '%s')", + charlog_db, sd->account_id, RFIFOB(fd, 2), jstrescapecpy(escaped_name, char_dat.name)); + //query + if(mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + ShowInfo("Selected char: (Account %d: %d - %s)" RETCODE, sd->account_id, RFIFOB(fd, 2), char_dat.name); + + i = search_mapserver(char_dat.last_point.map, -1, -1); + + // if map is not found, we check major cities + if (i < 0) { + unsigned short j; + //First check that there's actually a map server online. + for(j = 0; j < MAX_MAP_SERVERS; j++) + if (server_fd[j] >= 0 && server[j].map[0]) + break; + if (j == MAX_MAP_SERVERS) { + ShowInfo("Connection Closed. No map servers available.\n"); + WFIFOHEAD(fd, 3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + break; + } + if ((i = search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) { + char_dat.last_point.x = 273; + char_dat.last_point.y = 354; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) { + char_dat.last_point.x = 120; + char_dat.last_point.y = 100; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) { + char_dat.last_point.x = 160; + char_dat.last_point.y = 94; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) { + char_dat.last_point.x = 116; + char_dat.last_point.y = 57; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) { + char_dat.last_point.x = 87; + char_dat.last_point.y = 117; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) { + char_dat.last_point.x = 94; + char_dat.last_point.y = 103; + } else { + ShowInfo("Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", mapindex_id2name(char_dat.last_point.map)); + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + break; + } + ShowWarning("Unable to find map-server for '%s', sending to major city '%s'.\n", mapindex_id2name(char_dat.last_point.map), mapindex_id2name(j)); + char_dat.last_point.map = j; + } + { //Send player to map. + WFIFOHEAD(fd,28); + WFIFOW(fd, 0) =0x71; + WFIFOL(fd, 2) =char_dat.char_id; + memcpy(WFIFOP(fd,6), mapindex_id2name(char_dat.last_point.map), MAP_NAME_LENGTH); + + // Advanced subnet check [LuzZza] + if((subnet_map_ip = lan_subnetcheck((long *)p))) + WFIFOL(fd,22) = subnet_map_ip; + else + WFIFOL(fd,22) = server[i].ip; + + WFIFOW(fd,26) = server[i].port; + WFIFOSET(fd,28); + } + if (auth_fifo_pos >= AUTH_FIFO_SIZE) { + auth_fifo_pos = 0; + } + auth_fifo[auth_fifo_pos].account_id = sd->account_id; + auth_fifo[auth_fifo_pos].char_id = char_dat.char_id; + auth_fifo[auth_fifo_pos].login_id1 = sd->login_id1; + auth_fifo[auth_fifo_pos].login_id2 = sd->login_id2; + auth_fifo[auth_fifo_pos].delflag = 0; + auth_fifo[auth_fifo_pos].char_pos = 0; + auth_fifo[auth_fifo_pos].sex = sd->sex; + auth_fifo[auth_fifo_pos].connect_until_time = sd->connect_until_time; + auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr.sin_addr.s_addr; + + //Send NEW auth packet [Kevin] + if ((map_fd = server_fd[i]) < 1 || session[map_fd] == NULL) + { + ShowError("parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i); + server_fd[i] = -1; + memset(&server[i], 0, sizeof(struct mmo_map_server)); + break; + } + { //Send auth ok to map server + WFIFOHEAD(map_fd, 20 + sizeof(struct mmo_charstatus)); + WFIFOW(map_fd,0) = 0x2afd; + WFIFOW(map_fd,2) = 20 + sizeof(struct mmo_charstatus); + WFIFOL(map_fd,4) = auth_fifo[auth_fifo_pos].account_id; + WFIFOL(map_fd,8) = auth_fifo[auth_fifo_pos].login_id1; + WFIFOL(map_fd,16) = auth_fifo[auth_fifo_pos].login_id2; + WFIFOL(map_fd,12) = (unsigned long)auth_fifo[auth_fifo_pos].connect_until_time; + memcpy(WFIFOP(map_fd,20), &char_dat, sizeof(struct mmo_charstatus)); + WFIFOSET(map_fd, WFIFOW(map_fd,2)); + } + + set_char_online(i, auth_fifo[auth_fifo_pos].char_id, auth_fifo[auth_fifo_pos].account_id); + //Checks to see if the even share setting of the party must be broken. + inter_party_logged(char_dat.party_id, char_dat.account_id, char_dat.char_id); + auth_fifo_pos++; + break; + + case 0x67: // make new + FIFOSD_CHECK(37); + + if(char_new == 0) //turn character creation on/off [Kevin] + i = -2; + else + i = make_new_char_sql(fd, RFIFOP(fd, 2)); + + //'Charname already exists' (-1), 'Char creation denied' (-2) + //And 'You are underaged' (-3) (XD) [Sirius] + if (i < 0) + { + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x6e; + switch (i) { + case -1: WFIFOB(fd, 2) = 0x00; break; + case -2: WFIFOB(fd, 2) = 0x02; break; + case -3: WFIFOB(fd, 2) = 0x01; break; + } + WFIFOSET(fd, 3); + RFIFOSKIP(fd, 37); + break; + } + { //Send data. + WFIFOHEAD(fd, 108); + WFIFOW(fd, 0) = 0x6d; + memset(WFIFOP(fd, 2), 0x00, 106); + + mmo_char_fromsql_short(i, &char_dat); //Only the short data is needed. + WFIFOL(fd, 2) = char_dat.char_id; + WFIFOL(fd,2+4) = char_dat.base_exp>LONG_MAX?LONG_MAX:char_dat.base_exp; + WFIFOL(fd,2+8) = char_dat.zeny; + WFIFOL(fd,2+12) = char_dat.job_exp>LONG_MAX?LONG_MAX:char_dat.job_exp; + WFIFOL(fd,2+16) = char_dat.job_level; + + WFIFOL(fd,2+28) = char_dat.karma; + WFIFOL(fd,2+32) = char_dat.manner; + + WFIFOW(fd,2+40) = 0x30; + WFIFOW(fd,2+42) = (char_dat.hp > SHRT_MAX) ? SHRT_MAX : char_dat.hp; + WFIFOW(fd,2+44) = (char_dat.max_hp > SHRT_MAX) ? SHRT_MAX : char_dat.max_hp; + WFIFOW(fd,2+46) = (char_dat.sp > SHRT_MAX) ? SHRT_MAX : char_dat.sp; + WFIFOW(fd,2+48) = (char_dat.max_sp > SHRT_MAX) ? SHRT_MAX : char_dat.max_sp; + WFIFOW(fd,2+50) = DEFAULT_WALK_SPEED; // char_dat[i].speed; + WFIFOW(fd,2+52) = char_dat.class_; + WFIFOW(fd,2+54) = char_dat.hair; + + WFIFOW(fd,2+58) = char_dat.base_level; + WFIFOW(fd,2+60) = (char_dat.skill_point > SHRT_MAX) ? SHRT_MAX : char_dat.skill_point; + + WFIFOW(fd,2+64) = char_dat.shield; + WFIFOW(fd,2+66) = char_dat.head_top; + WFIFOW(fd,2+68) = char_dat.head_mid; + WFIFOW(fd,2+70) = char_dat.hair_color; + + memcpy(WFIFOP(fd,2+74), char_dat.name, NAME_LENGTH); + + WFIFOB(fd,2+98) = char_dat.str>UCHAR_MAX?UCHAR_MAX:char_dat.str; + WFIFOB(fd,2+99) = char_dat.agi>UCHAR_MAX?UCHAR_MAX:char_dat.agi; + WFIFOB(fd,2+100) = char_dat.vit>UCHAR_MAX?UCHAR_MAX:char_dat.vit; + WFIFOB(fd,2+101) = char_dat.int_>UCHAR_MAX?UCHAR_MAX:char_dat.int_; + WFIFOB(fd,2+102) = char_dat.dex>UCHAR_MAX?UCHAR_MAX:char_dat.dex; + WFIFOB(fd,2+103) = char_dat.luk>UCHAR_MAX?UCHAR_MAX:char_dat.luk; + WFIFOB(fd,2+104) = char_dat.char_num; + + WFIFOSET(fd, 108); + RFIFOSKIP(fd, 37); + } + //to do + for(ch = 0; ch < 9; ch++) { + if (sd->found_char[ch] == -1) { + sd->found_char[ch] = char_dat.char_id; + break; + } + } + break; + case 0x68: /* delete char */ + FIFOSD_CHECK(46); + { + int cid = RFIFOL(fd,2); + WFIFOHEAD(fd, 46); + ShowInfo(CL_RED" Request Char Deletion:"CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, cid); + memcpy(email, RFIFOP(fd,6), 40); + + /* Check if e-mail is correct */ + if(strcmpi(email, sd->email)){ + if(strcmp("a@a.com", sd->email) == 0){ + if(strcmp("a@a.com", email) == 0 || strcmp("", email) == 0){ + //ignore + }else{ + //del fail + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; + WFIFOSET(fd, 3); + RFIFOSKIP(fd, 46); + break; + } + }else{ + //del fail + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; + WFIFOSET(fd, 3); + RFIFOSKIP(fd, 46); + break; + } + } + + for(i = 0; i < 9; i++) { + /* Debug: + printf("Checking if char to be deleted: %d - %d (%d)\n", sd->found_char[i], RFIFOL(fd, 2), sd->account_id); + */ + if (sd->found_char[i] == cid) { + for(ch = i; ch < 9-1; ch++) + sd->found_char[ch] = sd->found_char[ch+1]; + sd->found_char[8] = -1; + break; + } + } + /* Such a character does not exist in the account */ + /* If so, you are so screwed. */ + if (i == 9) { + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; + WFIFOSET(fd, 3); + break; + } + + /* Grab the partner id */ + sprintf(tmp_sql, "SELECT `partner_id` FROM `%s` WHERE `char_id`='%d'",char_db, cid); + + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sql_res = mysql_store_result(&mysql_handle); + + if(sql_res) + { + int char_pid=0; + sql_row = mysql_fetch_row(sql_res); + if (sql_row) + char_pid = atoi(sql_row[0]); + mysql_free_result(sql_res); + + /* Delete character and partner (if any) */ + if(delete_char_sql(cid, char_pid)<0){ + //can't delete the char + //either SQL error or can't delete by some CONFIG conditions + //del fail + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; + WFIFOSET(fd, 3); + RFIFOSKIP(fd, 46); + break; + } + if (char_pid != 0) + { /* If there is partner, tell map server to do divorce */ + WBUFW(buf,0) = 0x2b12; + WBUFL(buf,2) = RFIFOL(fd,2); + WBUFL(buf,6) = char_pid; + mapif_sendall(buf,10); + } + } + /* Char successfully deleted.*/ + WFIFOW(fd, 0) = 0x6f; + WFIFOSET(fd, 2); + + RFIFOSKIP(fd, 46); + break; + } + case 0x2af8: // login as map-server + if (RFIFOREST(fd) < 60) + return 0; + { + char *l_userid = RFIFOP(fd,2); + char *l_password = RFIFOP(fd,26); + WFIFOHEAD(fd, 4+5*GM_num); + + l_userid[23] = '\0'; + l_password[23] = '\0'; + WFIFOW(fd, 0) = 0x2af9; + for(i = 0; i < MAX_MAP_SERVERS; i++) { + if (server_fd[i] <= 0) + break; + } + if (i == MAX_MAP_SERVERS || + strcmp(l_userid, userid) || + strcmp(l_password, passwd)) { + WFIFOB(fd,2) = 3; + WFIFOSET(fd, 3); + } else { + int len; + WFIFOB(fd,2) = 0; + WFIFOSET(fd, 3); + session[fd]->func_parse = parse_frommap; + server_fd[i] = fd; + server[i].ip = RFIFOL(fd, 54); + server[i].port = RFIFOW(fd, 58); + server[i].users = 0; + memset(server[i].map, 0, sizeof(server[i].map)); + realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + char_mapif_init(fd); + // send gm acccounts level to map-servers + len = 4; + WFIFOW(fd,0) = 0x2b15; + for(i = 0; i < GM_num; i++) { + WFIFOL(fd,len) = gm_account[i].account_id; + WFIFOB(fd,len+4) = (unsigned char)gm_account[i].level; + len += 5; + } + WFIFOW(fd,2) = len; + WFIFOSET(fd,len); + } + RFIFOSKIP(fd,60); + break; + } + case 0x187: // Alive? + if (RFIFOREST(fd) < 6) { + return 0; + } + RFIFOSKIP(fd, 6); + break; + + case 0x7530: // Athena info get + { + WFIFOHEAD(fd, 10); + WFIFOW(fd, 0) = 0x7531; + WFIFOB(fd, 2) = ATHENA_MAJOR_VERSION; + WFIFOB(fd, 3) = ATHENA_MINOR_VERSION; + WFIFOB(fd, 4) = ATHENA_REVISION; + WFIFOB(fd, 5) = ATHENA_RELEASE_FLAG; + WFIFOB(fd, 6) = ATHENA_OFFICIAL_FLAG; + WFIFOB(fd, 7) = ATHENA_SERVER_INTER | ATHENA_SERVER_CHAR; + WFIFOW(fd, 8) = ATHENA_MOD_VERSION; + WFIFOSET(fd, 10); + RFIFOSKIP(fd, 2); + return 0; + } + case 0x7532: // disconnect(default also disconnect) + default: + session[fd]->eof = 1; + return 0; + } + } + RFIFOFLUSH(fd); + + return 0; +} + +// Console Command Parser [Wizputer] +int parse_console(char *buf) { + char *type,*command; + + type = (char *)aMalloc(64); + command = (char *)aMalloc(64); + + memset(type,0,64); + memset(command,0,64); + + ShowNotice("Console: %s\n",buf); + + if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 ) + sscanf(buf,"%[^\n]",type); + + ShowNotice("Type of command: %s || Command: %s \n",type,command); + + if(buf) aFree(buf); + if(type) aFree(type); + if(command) aFree(command); + + return 0; +} + +// MAP send all +int mapif_sendall(unsigned char *buf, unsigned int len) { + int i, c; + int fd; + + c = 0; + for(i = 0; i < MAX_MAP_SERVERS; i++) { + if ((fd = server_fd[i]) > 0) { //0 Should not be a valid server_fd [Skotlex] + WFIFOHEAD(fd,len); +#if 0 //This seems to have been fixed long long ago. + if (session[fd] == NULL) + { //Could this be the crash's source? [Skotlex] + ShowError("mapif_sendall: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", fd, i); + server_fd[i] = -1; + memset(&server[i], 0, sizeof(struct mmo_map_server)); + continue; + } +#endif + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd,len); + c++; + } + } + + return c; +} + +int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { + int i, c; + int fd; + + c = 0; + for(i=0, c=0;i 0 && fd != sfd) { + WFIFOHEAD(fd, len); + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd, len); + c++; + } + } + + return c; +} + +int mapif_send(int fd, unsigned char *buf, unsigned int len) { + int i; + + if (fd >= 0) { + for(i = 0; i < MAX_MAP_SERVERS; i++) { + if (fd == server_fd[i]) { + WFIFOHEAD(fd,len); + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd,len); + return 1; + } + } + } + return 0; +} + +int send_users_tologin(int tid, unsigned int tick, int id, int data) { + int users = count_users(); + unsigned char buf[16]; + + if (login_fd > 0 && session[login_fd]) { + // send number of user to login server + WFIFOHEAD(login_fd,6); + WFIFOW(login_fd,0) = 0x2714; + WFIFOL(login_fd,2) = users; + WFIFOSET(login_fd,6); + } + // send number of players to all map-servers + WBUFW(buf,0) = 0x2b00; + WBUFL(buf,2) = users; + mapif_sendall(buf, 6); + + return 0; +} + +static int send_accounts_tologin_sub(DBKey key, void* data, va_list ap) { + struct online_char_data* character = (struct online_char_data*)data; + int *i = va_arg(ap, int*); + int count = va_arg(ap, int); + if ((*i) >= count) + return 0; //This is an error that shouldn't happen.... + if(character->server > -1) { + WFIFOHEAD(login_fd, 8+count*4); + WFIFOL(login_fd, 8+(*i)*4) =character->account_id; + (*i)++; + return 1; + } + return 0; +} + +int send_accounts_tologin(int tid, unsigned int tick, int id, int data) { + int users = count_users(), i=0; + + if (login_fd > 0 && session[login_fd]) { + // send account list to login server + WFIFOHEAD(login_fd, 8+users*4); + WFIFOW(login_fd,0) = 0x272d; + WFIFOL(login_fd,4) = users; + online_char_db->foreach(online_char_db, send_accounts_tologin_sub, &i, users); + WFIFOW(login_fd,2) = 8+ i*4; + WFIFOSET(login_fd,WFIFOW(login_fd,2)); + } + return 0; +} + +int check_connect_login_server(int tid, unsigned int tick, int id, int data) { + if (login_fd > 0 && session[login_fd] != NULL) + return 0; + + ShowInfo("Attempt to connect to login-server...\n"); + login_fd = make_connection(login_ip, login_port); + if (login_fd == -1) + { //Try again later. [Skotlex] + login_fd = 0; + return 0; + } + session[login_fd]->func_parse = parse_tologin; + realloc_fifo(login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + { + WFIFOHEAD(login_fd, 86); + WFIFOW(login_fd,0) = 0x2710; + memcpy(WFIFOP(login_fd,2), userid, 24); + memcpy(WFIFOP(login_fd,26), passwd, 24); + WFIFOL(login_fd,50) = 0; + WFIFOL(login_fd,54) = char_ip; + WFIFOL(login_fd,58) = char_port; + memcpy(WFIFOP(login_fd,60), server_name, 20); + WFIFOW(login_fd,80) = 0; + WFIFOW(login_fd,82) = char_maintenance; + WFIFOW(login_fd,84) = char_new_display; //only display (New) if they want to [Kevin] + WFIFOSET(login_fd,86); + } + return 0; +} + +//------------------------------------------------ +//Invoked 15 seconds after mapif_disconnectplayer in case the map server doesn't +//replies/disconnect the player we tried to kick. [Skotlex] +//------------------------------------------------ +static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, int data) +{ + struct online_char_data* character; + if ((character = idb_get(online_char_db, id)) != NULL && character->waiting_disconnect) + { //Mark it offline due to timeout. + set_char_offline(character->char_id, character->account_id); + } + return 0; +} + +//---------------------------------------------------------- +// Return numerical value of a switch configuration by [Yor] +// on/off, english, fran軋is, deutsch, espaol +//---------------------------------------------------------- +int config_switch(const char *str) { + if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) + return 1; + if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) + return 0; + + return atoi(str); +} + +//---------------------------------- +// Reading Lan Support configuration +// Rewrote: Anvanced subnet check [LuzZza] +//---------------------------------- +int char_lan_config_read(const char *lancfgName) { + + FILE *fp; + int line_num = 0; + char line[1024], w1[64], w2[64], w3[64], w4[64]; + + if((fp = fopen(lancfgName, "r")) == NULL) { + ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName); + return 1; + } + + ShowInfo("Reading the configuration file %s...\n", lancfgName); + + while(fgets(line, sizeof(line)-1, fp)) { + + line_num++; + if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n') + continue; + + line[sizeof(line)-1] = '\0'; + if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4) { + + ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num); + continue; + } + + remove_control_chars((unsigned char *)w1); + remove_control_chars((unsigned char *)w2); + remove_control_chars((unsigned char *)w3); + remove_control_chars((unsigned char *)w4); + + if(strcmpi(w1, "subnet") == 0) { + + subnet[subnet_count].mask = inet_addr(w2); + subnet[subnet_count].char_ip = inet_addr(w3); + subnet[subnet_count].map_ip = inet_addr(w4); + subnet[subnet_count].subnet = subnet[subnet_count].char_ip&subnet[subnet_count].mask; + if (subnet[subnet_count].subnet != (subnet[subnet_count].map_ip&subnet[subnet_count].mask)) { + ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4); + continue; + } + + subnet_count++; + } + + ShowStatus("Read information about %d subnetworks.\n", subnet_count); + } + + fclose(fp); + return 0; +} + +void do_final(void) { + ShowInfo("Doing final stage...\n"); + //inter_save(); + do_final_itemdb(); + //check SQL save progress. + //wait until save char complete + + set_all_offline(-1); + set_all_offline_sql(); + + inter_final(); + + flush_fifos(); + + mapindex_final(); + + sprintf(tmp_sql,"DELETE FROM `ragsrvinfo"); + if (mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + if(gm_account) { + aFree(gm_account); + gm_account = 0; + } + + delete_session(login_fd); + delete_session(char_fd); + char_db_->destroy(char_db_, NULL); + online_char_db->destroy(online_char_db, NULL); + + mysql_close(&mysql_handle); + if(char_gm_read) + mysql_close(&lmysql_handle); + + ShowInfo("ok! all done...\n"); +} +#endif //TXT_SQL_CONVERT +void sql_config_read(const char *cfgName){ /* Kalaspuff, to get login_db */ + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + ShowInfo("Reading file %s...\n", cfgName); + + if ((fp = fopen(cfgName, "r")) == NULL) { + ShowFatalError("file not found: %s\n", cfgName); + exit(1); + } + + while(fgets(line, sizeof(line)-1, fp)){ + if(line[0] == '/' && line[1] == '/') + continue; + + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + if(strcmpi(w1,"char_db")==0){ + strcpy(char_db,w2); +#ifndef TXT_SQL_CONVERT + } else if(strcmpi(w1, "gm_read_method") == 0) { + if(atoi(w2) != 0) + char_gm_read = true; + else + char_gm_read = false; + //custom columns for login database + }else if(strcmpi(w1,"login_db")==0){ + strcpy(login_db, w2); + }else if(strcmpi(w1,"login_db_level")==0){ + strcpy(login_db_level,w2); + }else if(strcmpi(w1,"login_db_account_id")==0){ + strcpy(login_db_account_id,w2); + }else if(strcmpi(w1,"lowest_gm_level")==0){ + lowest_gm_level = atoi(w2); + ShowStatus("set lowest_gm_level : %s\n",w2); +#endif + }else if(strcmpi(w1,"scdata_db")==0){ + strcpy(scdata_db,w2); + }else if(strcmpi(w1,"cart_db")==0){ + strcpy(cart_db,w2); + }else if(strcmpi(w1,"inventory_db")==0){ + strcpy(inventory_db,w2); + }else if(strcmpi(w1,"charlog_db")==0){ + strcpy(charlog_db,w2); + }else if(strcmpi(w1,"storage_db")==0){ + strcpy(storage_db,w2); + }else if(strcmpi(w1,"reg_db")==0){ + strcpy(reg_db,w2); + }else if(strcmpi(w1,"skill_db")==0){ + strcpy(skill_db,w2); + }else if(strcmpi(w1,"interlog_db")==0){ + strcpy(interlog_db,w2); + }else if(strcmpi(w1,"memo_db")==0){ + strcpy(memo_db,w2); + }else if(strcmpi(w1,"guild_db")==0){ + strcpy(guild_db,w2); + }else if(strcmpi(w1,"guild_alliance_db")==0){ + strcpy(guild_alliance_db,w2); + }else if(strcmpi(w1,"guild_castle_db")==0){ + strcpy(guild_castle_db,w2); + }else if(strcmpi(w1,"guild_expulsion_db")==0){ + strcpy(guild_expulsion_db,w2); + }else if(strcmpi(w1,"guild_member_db")==0){ + strcpy(guild_member_db,w2); + }else if(strcmpi(w1,"guild_skill_db")==0){ + strcpy(guild_skill_db,w2); + }else if(strcmpi(w1,"guild_position_db")==0){ + strcpy(guild_position_db,w2); + }else if(strcmpi(w1,"guild_storage_db")==0){ + strcpy(guild_storage_db,w2); + }else if(strcmpi(w1,"party_db")==0){ + strcpy(party_db,w2); + }else if(strcmpi(w1,"pet_db")==0){ + strcpy(pet_db,w2); + }else if(strcmpi(w1,"friend_db")==0){ + strcpy(friend_db,w2); +#ifndef TXT_SQL_CONVERT + }else if(strcmpi(w1,"db_path")==0){ + strcpy(db_path,w2); + //Map server option to use SQL db or not + }else if(strcmpi(w1,"use_sql_db")==0){ // added for sql item_db read for char server [Valaris] + db_use_sqldbs = config_switch(w2); + ShowStatus("Using SQL dbs: %s\n",w2); + }else if(strcmpi(w1,"item_db_db")==0){ + strcpy(item_db_db,w2); + }else if(strcmpi(w1,"item_db2_db")==0){ + strcpy(item_db2_db,w2); + } else if(strcmpi(w1,"connection_ping_interval")==0) { + connection_ping_interval = config_switch(w2); +#endif + //support the import command, just like any other config + }else if(strcmpi(w1,"import")==0){ + sql_config_read(w2); + } + + } + fclose(fp); + ShowInfo("done reading %s.\n", cfgName); +} +#ifndef TXT_SQL_CONVERT + +int char_config_read(const char *cfgName) { + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + if ((fp = fopen(cfgName, "r")) == NULL) { + ShowFatalError("Configuration file not found: %s.\n", cfgName); + exit(1); + } + + ShowInfo("Reading file %s...\n", cfgName); + while(fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof(line)-1] = '\0'; + if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + remove_control_chars((unsigned char *) w1); + remove_control_chars((unsigned char *) w2); + if(strcmpi(w1,"timestamp_format")==0) { + strncpy(timestamp_format, w2, 20); + } else if(strcmpi(w1,"console_silent")==0){ + msg_silent = 0; //To always allow the next line to show up. + ShowInfo("Console Silent Setting: %d\n", atoi(w2)); + msg_silent = atoi(w2); + } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ + stdout_with_ansisequence = config_switch(w2); + } else if (strcmpi(w1, "userid") == 0) { + strncpy(userid, w2, 24); + } else if (strcmpi(w1, "passwd") == 0) { + strncpy(passwd, w2, 24); + } else if (strcmpi(w1, "server_name") == 0) { + strncpy(server_name, w2, 20); + server_name[sizeof(server_name) - 1] = '\0'; + ShowStatus("%s server has been initialized\n", w2); + } else if (strcmpi(w1, "wisp_server_name") == 0) { + if (strlen(w2) >= 4) { + memcpy(wisp_server_name, w2, sizeof(wisp_server_name)); + wisp_server_name[sizeof(wisp_server_name) - 1] = '\0'; + } + } else if (strcmpi(w1, "login_ip") == 0) { + unsigned char ip_str[16]; + login_ip = resolve_hostbyname(w2, NULL, ip_str); + if (login_ip) { + strncpy(login_ip_str, w2, sizeof(login_ip_str)); + ShowStatus("Login server IP address : %s -> %s\n", w2, ip_str); + } + } else if (strcmpi(w1, "login_port") == 0) { + login_port=atoi(w2); + } else if (strcmpi(w1, "char_ip") == 0) { + unsigned char ip_str[16]; + char_ip = resolve_hostbyname(w2, NULL, ip_str); + if (char_ip){ + strncpy(char_ip_str, w2, sizeof(char_ip_str)); + ShowStatus("Character server IP address : %s -> %s\n", w2, ip_str); + } + } else if (strcmpi(w1, "bind_ip") == 0) { + unsigned char ip_str[16]; + bind_ip = resolve_hostbyname(w2, NULL, ip_str); + if (bind_ip) { + strncpy(bind_ip_str, w2, sizeof(bind_ip_str)); + ShowStatus("Character server binding IP address : %s -> %s\n", w2, ip_str); + } + } else if (strcmpi(w1, "char_port") == 0) { + char_port = atoi(w2); + } else if (strcmpi(w1, "char_maintenance") == 0) { + char_maintenance = atoi(w2); + } else if (strcmpi(w1, "char_new")==0){ + char_new = atoi(w2); + } else if (strcmpi(w1, "char_new_display")==0){ + char_new_display = atoi(w2); + } else if (strcmpi(w1, "max_connect_user") == 0) { + max_connect_user = atoi(w2); + if (max_connect_user < 0) + max_connect_user = 0; // unlimited online players + } else if(strcmpi(w1, "gm_allow_level") == 0) { + gm_allow_level = atoi(w2); + if(gm_allow_level < 0) + gm_allow_level = 99; + } else if (strcmpi(w1, "check_ip_flag") == 0) { + check_ip_flag = config_switch(w2); + } else if (strcmpi(w1, "online_check") == 0) { + online_check = config_switch(w2); + } else if (strcmpi(w1, "autosave_time") == 0) { + autosave_interval = atoi(w2)*1000; + if (autosave_interval <= 0) + autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; + } else if (strcmpi(w1, "save_log") == 0) { + save_log = config_switch(w2); + } else if (strcmpi(w1, "start_point") == 0) { + char map[MAP_NAME_LENGTH]; + int x, y; + if (sscanf(w2,"%16[^,],%d,%d", map, &x, &y) < 3) + continue; + if (strstr(map, ".gat") != NULL) { // Verify at least if '.gat' is in the map name + start_point.map = mapindex_name2id(map); + if (!start_point.map) + ShowError("Specified start_point %s not found in map-index cache.\n", map); + start_point.x = x; + start_point.y = y; + } + } else if (strcmpi(w1, "start_zeny") == 0) { + start_zeny = atoi(w2); + if (start_zeny < 0) + start_zeny = 0; + } else if (strcmpi(w1, "start_weapon") == 0) { + start_weapon = atoi(w2); + if (start_weapon < 0) + start_weapon = 0; + } else if (strcmpi(w1, "start_armor") == 0) { + start_armor = atoi(w2); + if (start_armor < 0) + start_armor = 0; + } else if(strcmpi(w1,"log_char")==0){ //log char or not [devil] + log_char = atoi(w2); + } else if (strcmpi(w1, "unknown_char_name") == 0) { + strcpy(unknown_char_name, w2); + unknown_char_name[NAME_LENGTH-1] = 0; + } else if (strcmpi(w1, "name_ignoring_case") == 0) { + name_ignoring_case = config_switch(w2); + } else if (strcmpi(w1, "char_name_option") == 0) { + char_name_option = atoi(w2); + } else if (strcmpi(w1, "char_name_letters") == 0) { + strcpy(char_name_letters, w2); + } else if (strcmpi(w1, "check_ip_flag") == 0) { + check_ip_flag = config_switch(w2); + } else if (strcmpi(w1, "chars_per_account") == 0) { //maxchars per account [Sirius] + char_per_account = atoi(w2); + } else if (strcmpi(w1, "char_del_level") == 0) { //disable/enable char deletion by its level condition [Lupus] + char_del_level = atoi(w2); + } else if (strcmpi(w1, "console") == 0) { + if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) + console = 1; + } else if (strcmpi(w1, "fame_list_alchemist") == 0) { + fame_list_size_chemist = atoi(w2); + if (fame_list_size_chemist > MAX_FAME_LIST) { + ShowWarning("Max fame list size is %d (fame_list_alchemist)\n", MAX_FAME_LIST); + fame_list_size_chemist = MAX_FAME_LIST; + } + } else if (strcmpi(w1, "fame_list_blacksmith") == 0) { + fame_list_size_smith = atoi(w2); + if (fame_list_size_smith > MAX_FAME_LIST) { + ShowWarning("Max fame list size is %d (fame_list_blacksmith)\n", MAX_FAME_LIST); + fame_list_size_smith = MAX_FAME_LIST; + } + } else if (strcmpi(w1, "fame_list_taekwon") == 0) { + fame_list_size_taekwon = atoi(w2); + if (fame_list_size_taekwon > MAX_FAME_LIST) { + ShowWarning("Max fame list size is %d (fame_list_taekwon)\n", MAX_FAME_LIST); + fame_list_size_taekwon = MAX_FAME_LIST; + } + } else if (strcmpi(w1, "guild_exp_rate") == 0) { + guild_exp_rate = atoi(w2); + } else if (strcmpi(w1, "import") == 0) { + char_config_read(w2); + } + } + fclose(fp); + + ShowInfo("Done reading %s.\n", cfgName); + + return 0; +} + +void set_server_type(void) +{ + SERVER_TYPE = ATHENA_SERVER_CHAR; +} + +static int online_data_cleanup_sub(DBKey key, void *data, va_list ap) +{ + struct online_char_data *character= (struct online_char_data*)data; + if (character->server == -2) //Unknown server.. set them offline + set_char_offline(character->char_id, character->account_id); + if (character->server < 0) + //Free data from players that have not been online for a while. + db_remove(online_char_db, key); + return 0; +} + +static int online_data_cleanup(int tid, unsigned int tick, int id, int data) +{ + online_char_db->foreach(online_char_db, online_data_cleanup_sub); + return 0; +} + +int do_init(int argc, char **argv){ + int i; + + for(i = 0; i < MAX_MAP_SERVERS; i++) { + memset(&server[i], 0, sizeof(struct mmo_map_server)); + server_fd[i] = -1; + } + + //Read map indexes + mapindex_init(); + start_point.map = mapindex_name2id("new_zone01.gat"); + + char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]); + char_lan_config_read((argc > 3) ? argv[3] : LAN_CONF_NAME); + sql_config_read(SQL_CONF_NAME); + + if (strcmp(userid, "s1")==0 && strcmp(passwd, "p1")==0) { + ShowError("Using the default user/password s1/p1 is NOT RECOMMENDED.\n"); + ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n"); + ShowNotice("And then change the user/password to use in conf/char_athena.conf (or conf/import/char_conf.txt)\n"); + } + + ShowInfo("Finished reading the char-server configuration.\n"); + + inter_init_sql((argc > 2) ? argv[2] : inter_cfgName); // inter server テハア篳ュ + ShowInfo("Finished reading the inter-server configuration.\n"); + + //Read ItemDB + do_init_itemdb(); + + ShowInfo("Initializing char server.\n"); + online_char_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + mmo_char_sql_init(); + ShowInfo("char server initialized.\n"); + +// ShowDebug("set parser -> parse_char()...\n"); + set_defaultparse(parse_char); + +// ShowDebug("set terminate function -> do_final().....\n"); + + if ((naddr_ != 0) && (!login_ip || !char_ip)) { + // The char server should know what IP address it is running on + // - MouseJstr + int localaddr = ntohl(addr_[0]); + unsigned char *ptr = (unsigned char *) &localaddr; + char buf[16]; + sprintf(buf, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]); + if (naddr_ != 1) + ShowStatus("Multiple interfaces detected.. using %s as our IP address\n", buf); + else + ShowStatus("Defaulting to %s as our IP address\n", buf); + if (!login_ip) { + strcpy(login_ip_str, buf); + login_ip = inet_addr(login_ip_str); + } + if (!char_ip) { + strcpy(char_ip_str, buf); + char_ip = inet_addr(char_ip_str); + } + if (ptr[0] == 192 && ptr[1] == 168) + ShowWarning("Firewall detected.. edit subnet_athena.conf and char_athena.conf\n"); + } + + ShowInfo("open port %d.....\n",char_port); + char_fd = make_listen_bind(bind_ip?bind_ip:INADDR_ANY,char_port); + + add_timer_func_list(check_connect_login_server, "check_connect_login_server"); + add_timer_func_list(send_users_tologin, "send_users_tologin"); + add_timer_func_list(send_accounts_tologin, "send_accounts_tologin"); + add_timer_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect"); + + add_timer_func_list(online_data_cleanup, "online_data_cleanup"); + add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600 * 1000); + + // send ALIVE PING to login server. + add_timer_interval(gettick() + 10, check_connect_login_server, 0, 0, 10 * 1000); + // send USER COUNT PING to login server. + add_timer_interval(gettick() + 10, send_users_tologin, 0, 0, 5 * 1000); + add_timer_interval(gettick() + 3600*1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour. + + char_read_fame_list(); //Read fame lists. + + if(char_gm_read) + read_gm_account(); + + + if ( console ) { + set_defaultconsoleparse(parse_console); + start_console(); + } + + //Cleaning the tables for NULL entrys @ startup [Sirius] + //Chardb clean + ShowInfo("Cleaning the '%s' table...\n", char_db); + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `account_id` = '0'", char_db); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + //guilddb clean + ShowInfo("Cleaning the '%s' table...\n", guild_db); + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_lv` = '0' AND `max_member` = '0' AND `exp` = '0' AND `next_exp` = '0' AND `average_lv` = '0'", guild_db); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + //guildmemberdb clean + ShowInfo("Cleaning the '%s' table...\n", guild_member_db); + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '0' AND `account_id` = '0' AND `char_id` = '0'", guild_member_db); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + ShowInfo("End of char server initilization function.\n"); + ShowStatus("The char-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", char_port); + return 0; +} + +#undef mysql_query + +int debug_mysql_query(char *file, int line, void *mysql, const char *q) { +#ifdef TWILIGHT + ShowDebug("sql: %s:%d# %s\n", file, line, q); +#endif + return mysql_query((MYSQL *) mysql, q); +} + +int char_child(int parent_id, int child_id) { + int tmp_id = 0; + sprintf (tmp_sql, "SELECT `child` FROM `%s` WHERE `char_id` = '%d'", char_db, parent_id); + if (mysql_query (&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result (&mysql_handle); + sql_row = sql_res?mysql_fetch_row (sql_res):NULL; + if (sql_row) + tmp_id = atoi (sql_row[0]); + else + ShowError("CHAR: child Failed!\n"); + if (sql_res) mysql_free_result (sql_res); + if ( tmp_id == child_id ) + return 1; + else + return 0; +} + +int char_married(int pl1,int pl2) { + int tmp_id = 0; + sprintf (tmp_sql, "SELECT `partner_id` FROM `%s` WHERE `char_id` = '%d'", char_db, pl1); + if (mysql_query (&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result (&mysql_handle); + sql_row = sql_res?mysql_fetch_row (sql_res):NULL; + if (sql_row) + tmp_id = atoi (sql_row[0]); + else + ShowError("CHAR: married Failed!\n"); + if (sql_res) mysql_free_result (sql_res); + if ( tmp_id == pl2 ) + return 1; + else + return 0; +} + +int char_family(int pl1,int pl2,int pl3) { + int charid, partnerid, childid; + sprintf (tmp_sql, "SELECT `char_id`,`partner_id`,`child` FROM `%s` WHERE `char_id` IN ('%d','%d','%d')", char_db, pl1, pl2, pl3); + if (mysql_query (&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + sql_res = mysql_store_result (&mysql_handle); + if (!sql_res) return 0; + + while((sql_row = mysql_fetch_row(sql_res))) + { + charid = atoi(sql_row[0]); + partnerid = atoi(sql_row[1]); + childid = atoi(sql_row[2]); + if (charid == pl1) { + if ((pl2 == partnerid && pl3 == childid) || + (pl3 == partnerid && pl2 == childid) + ) { + mysql_free_result (sql_res); + return childid; + } + } + if(charid == pl2) { + if ((pl1 == partnerid && pl3 == childid) || + (pl3 == partnerid && pl1 == childid) + ) { + mysql_free_result (sql_res); + return childid; + } + } + if(charid == pl3) { + if ((pl1 == partnerid && pl2 == childid) || + (pl2 == partnerid && pl1 == childid) + ) { + mysql_free_result (sql_res); + return childid; + } + } + } + mysql_free_result (sql_res); + return 0; +} +#endif //TXT_SQL_CONVERT diff --git a/src/char_sql/char.h b/src/char_sql/char.h index 70d8e3b13..1ad48fc9b 100644 --- a/src/char_sql/char.h +++ b/src/char_sql/char.h @@ -1,110 +1,110 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder -#ifndef _CHARSQL_H_ -#define _CHARSQL_H_ - -#include "../common/core.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/mmo.h" -#include "../common/version.h" -#include "../common/db.h" -#include "../common/mapindex.h" - -#include "inter.h" -#include "int_pet.h" -#include "int_guild.h" -#include "int_party.h" -#include "int_storage.h" -#include "itemdb.h" - -#define START_CHAR_NUM 150000 -#define MAX_MAP_SERVERS 30 - -#define LAN_CONF_NAME "conf/subnet_athena.conf" - -#define DEFAULT_AUTOSAVE_INTERVAL 300*1000 - -struct itemtmp { - int flag;//checked = 1 else 0 - int id; - short nameid; - short amount; - unsigned short equip; - char identify; - char refine; - char attribute; - short card[4]; -}; -enum { - TABLE_INVENTORY, - TABLE_CART, - TABLE_STORAGE, - TABLE_GUILD_STORAGE, -}; -struct itemtemp{ - struct itemtmp equip[MAX_GUILD_STORAGE],notequip[MAX_GUILD_STORAGE]; -}; -int memitemdata_to_sql(struct itemtmp mapitem[], int count, int char_id,int tableswitch); - -//int memitemdataNEW_to_sql(struct itemtmp mapitem[], int count, int char_id,int tableswitch); -int mapif_sendall(unsigned char *buf,unsigned int len); -int mapif_sendallwos(int fd,unsigned char *buf,unsigned int len); -int mapif_send(int fd,unsigned char *buf,unsigned int len); - -int char_married(int pl1,int pl2); -int char_child(int parent_id, int child_id); -int char_family(int pl1,int pl2,int pl3); - -int char_loadName(int char_id, char* name); - -int request_accreg2(int account_id, int char_id); -int save_accreg2(unsigned char* buf, int len); - -extern int char_name_option; -extern char char_name_letters[]; -extern bool char_gm_read; -extern int autosave_interval; -extern int save_log; -extern int charsave_method; -extern char db_path[]; -extern char char_db[256]; -extern char scdata_db[256]; -extern char cart_db[256]; -extern char inventory_db[256]; -extern char charlog_db[256]; -extern char storage_db[256]; -extern char interlog_db[256]; -extern char reg_db[256]; -extern char skill_db[256]; -extern char memo_db[256]; -extern char guild_db[256]; -extern char guild_alliance_db[256]; -extern char guild_castle_db[256]; -extern char guild_expulsion_db[256]; -extern char guild_member_db[256]; -extern char guild_position_db[256]; -extern char guild_skill_db[256]; -extern char guild_storage_db[256]; -extern char party_db[256]; -extern char pet_db[256]; - -extern int db_use_sqldbs; // added for sql item_db read for char server [Valaris] -extern int connection_ping_interval; - -extern char login_db_level[32]; -extern char login_db_account_id[32]; - -extern int lowest_gm_level; -extern int GM_num; -extern struct gm_account *gm_account; - -extern int guild_exp_rate; -extern int log_inter; - -extern int debug_mysql_query(char *file, int line, void *mysql, const char *q); - -//Exported for use in the TXT-SQL converter. -int mmo_char_tosql(int char_id, struct mmo_charstatus *p); -void sql_config_read(const char *cfgName); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder +#ifndef _CHARSQL_H_ +#define _CHARSQL_H_ + +#include "../common/core.h" +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/mmo.h" +#include "../common/version.h" +#include "../common/db.h" +#include "../common/mapindex.h" + +#include "inter.h" +#include "int_pet.h" +#include "int_guild.h" +#include "int_party.h" +#include "int_storage.h" +#include "itemdb.h" + +#define START_CHAR_NUM 150000 +#define MAX_MAP_SERVERS 30 + +#define LAN_CONF_NAME "conf/subnet_athena.conf" + +#define DEFAULT_AUTOSAVE_INTERVAL 300*1000 + +struct itemtmp { + int flag;//checked = 1 else 0 + int id; + short nameid; + short amount; + unsigned short equip; + char identify; + char refine; + char attribute; + short card[4]; +}; +enum { + TABLE_INVENTORY, + TABLE_CART, + TABLE_STORAGE, + TABLE_GUILD_STORAGE, +}; +struct itemtemp{ + struct itemtmp equip[MAX_GUILD_STORAGE],notequip[MAX_GUILD_STORAGE]; +}; +int memitemdata_to_sql(struct itemtmp mapitem[], int count, int char_id,int tableswitch); + +//int memitemdataNEW_to_sql(struct itemtmp mapitem[], int count, int char_id,int tableswitch); +int mapif_sendall(unsigned char *buf,unsigned int len); +int mapif_sendallwos(int fd,unsigned char *buf,unsigned int len); +int mapif_send(int fd,unsigned char *buf,unsigned int len); + +int char_married(int pl1,int pl2); +int char_child(int parent_id, int child_id); +int char_family(int pl1,int pl2,int pl3); + +int char_loadName(int char_id, char* name); + +int request_accreg2(int account_id, int char_id); +int save_accreg2(unsigned char* buf, int len); + +extern int char_name_option; +extern char char_name_letters[]; +extern bool char_gm_read; +extern int autosave_interval; +extern int save_log; +extern int charsave_method; +extern char db_path[]; +extern char char_db[256]; +extern char scdata_db[256]; +extern char cart_db[256]; +extern char inventory_db[256]; +extern char charlog_db[256]; +extern char storage_db[256]; +extern char interlog_db[256]; +extern char reg_db[256]; +extern char skill_db[256]; +extern char memo_db[256]; +extern char guild_db[256]; +extern char guild_alliance_db[256]; +extern char guild_castle_db[256]; +extern char guild_expulsion_db[256]; +extern char guild_member_db[256]; +extern char guild_position_db[256]; +extern char guild_skill_db[256]; +extern char guild_storage_db[256]; +extern char party_db[256]; +extern char pet_db[256]; + +extern int db_use_sqldbs; // added for sql item_db read for char server [Valaris] +extern int connection_ping_interval; + +extern char login_db_level[32]; +extern char login_db_account_id[32]; + +extern int lowest_gm_level; +extern int GM_num; +extern struct gm_account *gm_account; + +extern int guild_exp_rate; +extern int log_inter; + +extern int debug_mysql_query(char *file, int line, void *mysql, const char *q); + +//Exported for use in the TXT-SQL converter. +int mmo_char_tosql(int char_id, struct mmo_charstatus *p); +void sql_config_read(const char *cfgName); +#endif diff --git a/src/char_sql/int_guild.c b/src/char_sql/int_guild.c index 8920ed948..dbb9bc1ad 100644 --- a/src/char_sql/int_guild.c +++ b/src/char_sql/int_guild.c @@ -1,2106 +1,2106 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -// original code from athena -// SQL conversion by hack - -#include -#include -#include -#include - -#include "char.h" -#include "../common/strlib.h" -#include "../common/showmsg.h" -// #include "int_storage.h" -#include "inter.h" -#include "int_guild.h" -#include "mmo.h" -#include "socket.h" -#include "db.h" -#include "malloc.h" - -#define GS_MEMBER_UNMODIFIED 0x00 -#define GS_MEMBER_MODIFIED 0x01 -#define GS_MEMBER_NEW 0x02 - -#define GS_POSITION_UNMODIFIED 0x00 -#define GS_POSITION_MODIFIED 0x01 - -// LSB = 0 => Alliance, LSB = 1 => Opposition -#define GUILD_ALLIANCE_TYPE_MASK 0x01 -#define GUILD_ALLIANCE_REMOVE 0x08 - -static char dataToHex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - -#ifndef TXT_SQL_CONVERT -//Guild cache -static struct dbt *guild_db_; - -struct guild_castle castles[MAX_GUILDCASTLE]; - -static unsigned int guild_exp[100]; - -int mapif_parse_GuildLeave(int fd,int guild_id,int account_id,int char_id,int flag,const char *mes); -int mapif_guild_broken(int guild_id,int flag); -int guild_check_empty(struct guild *g); -int guild_calcinfo(struct guild *g); -int mapif_guild_basicinfochanged(int guild_id,int type,const void *data,int len); -int mapif_guild_info(int fd,struct guild *g); -int guild_break_sub(int key,void *data,va_list ap); -int inter_guild_tosql(struct guild *g,int flag); - -#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) - -static int guild_save(DBKey key, void *data, va_list ap) { - struct guild *g = (struct guild*) data; - int *last_id = va_arg(ap, int *); - int *state = va_arg(ap, int *); - - if ((*state) == 0 && g->guild_id == (*last_id)) - (*state)++; //Save next guild in the list. - else if (g->save_flag&GS_MASK && (*state) == 1) { - inter_guild_tosql(g, g->save_flag&GS_MASK); - g->save_flag &= ~GS_MASK; - - //Some guild saved. - (*last_id) = g->guild_id; - (*state)++; - } - - if(g->save_flag == GS_REMOVE) { //Nothing to save, guild is ready for removal. - if (save_log) - ShowInfo("Guild Unloaded (%d - %s)\n", g->guild_id, g->name); - db_remove(guild_db_, key); - } - return 0; -} - -static int guild_save_timer(int tid, unsigned int tick, int id, int data) { - static int last_id = 0; //To know in which guild we were. - int state = 0; //0: Have not reached last guild. 1: Reached last guild, ready for save. 2: Some guild saved, don't do further saving. - if (!last_id) //Save the first guild in the list. - state = 1; - guild_db_->foreach(guild_db_, guild_save, &last_id, &state); - if (state != 2) //Reached the end of the guild db without saving. - last_id = 0; //Reset guild saved, return to beginning. - - state = guild_db_->size(guild_db_); - if (state < 1) state = 1; //Calculate the time slot for the next save. - add_timer(tick + autosave_interval/state, guild_save_timer, 0, 0); - return 0; -} - -int inter_guild_removemember_tosql(int account_id, int char_id) -{ - sprintf(tmp_sql,"DELETE from `%s` where `account_id` = '%d' and `char_id` = '%d'", guild_member_db, account_id, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sprintf(tmp_sql,"UPDATE `%s` SET `guild_id` = '0' WHERE `char_id` = '%d'", char_db, char_id); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - return 0; -} -#endif //TXT_SQL_CONVERT -// Save guild into sql -int inter_guild_tosql(struct guild *g,int flag) -{ - // Table guild (GS_BASIC_MASK) - // GS_EMBLEM `emblem_len`,`emblem_id`,`emblem_data` - // GS_CONNECT `connect_member`,`average_lv` - // GS_MES `mes1`,`mes2` - // GS_LEVEL `guild_lv`,`max_member`,`exp`,`next_exp`,`skill_point` - // GS_BASIC `name`,`master`,`char_id` - - // GS_MEMBER `guild_member` (`guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`rsv1`,`rsv2`,`name`) - // GS_POSITION `guild_position` (`guild_id`,`position`,`name`,`mode`,`exp_mode`) - // GS_ALLIANCE `guild_alliance` (`guild_id`,`opposition`,`alliance_id`,`name`) - // GS_EXPULSION `guild_expulsion` (`guild_id`,`name`,`mes`,`acc`,`account_id`,`rsv1`,`rsv2`,`rsv3`) - // GS_SKILL `guild_skill` (`guild_id`,`id`,`lv`) - - // temporary storage for str convertion. They must be twice the size of the - // original string to ensure no overflows will occur. [Skotlex] - char t_name[NAME_LENGTH*2], - t_master[NAME_LENGTH*2], - t_mes1[120], - t_mes2[240], - t_member[NAME_LENGTH*2], - t_position[NAME_LENGTH*2], - t_alliance[NAME_LENGTH*2], - t_ename[NAME_LENGTH*2], - t_emes[80], - t_info[240]; - char emblem_data[4096]; - char new_guild = 0; - int i=0, sql_index; - - if (g->guild_id<=0 && g->guild_id != -1) return 0; - -#ifdef NOISY - ShowInfo("Save guild request ("CL_BOLD"%d"CL_RESET" - flag 0x%x).",g->guild_id, flag); -#endif - - jstrescapecpy(t_name, g->name); - - t_info[0]='\0'; - -#ifndef TXT_SQL_CONVERT - // Insert a new guild the guild - if (flag&GS_BASIC && g->guild_id == -1) - { - strcat(t_info, " guild_create"); - - // Create a new guild - sprintf(tmp_sql,"INSERT INTO `%s` " - "(`name`,`master`,`guild_lv`,`max_member`,`average_lv`,`char_id`) " - "VALUES ('%s', '%s', '%d', '%d', '%d', '%d')", - guild_db,t_name,jstrescapecpy(t_master,g->master),g->guild_lv,g->max_member,g->average_lv,g->member[0].char_id); - if(mysql_query(&mysql_handle, tmp_sql) ) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - if (g->guild_id == -1) - return 0; //Failed to create guild! - } - else - { - //New guild, catch id - if(mysql_field_count(&mysql_handle) == 0 && mysql_insert_id(&mysql_handle) != 0) - { - g->guild_id = (int)mysql_insert_id(&mysql_handle); - new_guild = 1; - } - else - return 0; //Failed to get ID?? - } - } -#else - // Insert a new guild the guild - if (flag&GS_BASIC) - { - strcat(t_info, " guild_create"); - // Since the PK is guild id + master id, a replace will not be enough if we are overwriting data, we need to wipe the previous guild. - sprintf(tmp_sql,"DELETE FROM `%s` where `guild_id` = '%d'", guild_db,g->guild_id); - if(mysql_query(&mysql_handle, tmp_sql) ) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - // Create a new guild - sprintf(tmp_sql,"REPLACE INTO `%s` " - "(`guild_id`,`name`,`master`,`guild_lv`,`max_member`,`average_lv`,`char_id`) " - "VALUES ('%d', '%s', '%s', '%d', '%d', '%d', '%d')", - guild_db,g->guild_id,t_name,jstrescapecpy(t_master,g->master),g->guild_lv,g->max_member,g->average_lv,g->member[0].char_id); - if(mysql_query(&mysql_handle, tmp_sql) ) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; //Failed to create guild. - } - } -#endif //TXT_SQL_CONVERT - // If we need an update on an existing guild or more update on the new guild - if (((flag & GS_BASIC_MASK) && !new_guild) || ((flag & (GS_BASIC_MASK & ~GS_BASIC)) && new_guild)) - { - sql_index = sprintf(tmp_sql,"UPDATE `%s` SET ", guild_db); - - if (flag & GS_EMBLEM) - { - char * pData = emblem_data; - strcat(t_info, " emblem"); - // Convert emblem_data to hex - for(i=0; iemblem_len; i++){ - *pData++ = dataToHex[(g->emblem_data[i] >> 4) & 0x0F]; - *pData++ = dataToHex[g->emblem_data[i] & 0x0F]; - } - *pData = 0; - sql_index += sprintf(tmp_sql + sql_index,"`emblem_len`=%d,`emblem_id`=%d,`emblem_data`='%s',",g->emblem_len,g->emblem_id,emblem_data); - } - if (flag & GS_BASIC) - { - strcat(t_info, " basic"); - sql_index += sprintf(tmp_sql + sql_index,"`name`='%s', `master`='%s', `char_id`=%d,",t_name,jstrescapecpy(t_master,g->master),g->member[0].char_id); - } - if (flag & GS_CONNECT) - { - strcat(t_info, " connect"); - sql_index += sprintf(tmp_sql + sql_index,"`connect_member`=%d,`average_lv`=%d,",g->connect_member, g->average_lv); - } - if (flag & GS_MES) - { - strcat(t_info, " mes"); - sql_index += sprintf(tmp_sql + sql_index,"`mes1`='%s',`mes2`='%s',",jstrescapecpy(t_mes1,g->mes1),jstrescapecpy(t_mes2,g->mes2)); - } - if (flag & GS_LEVEL) - { - strcat(t_info, " level"); - sql_index += sprintf(tmp_sql + sql_index,"`guild_lv`=%d,`skill_point`=%d,`exp`=%u,`next_exp`=%u,`max_member`=%d,",g->guild_lv, g->skill_point, g->exp, g->next_exp, g->max_member); - } - sprintf(tmp_sql + sql_index -1," WHERE `guild_id`=%d", g->guild_id); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - if (flag&GS_MEMBER) - { - struct guild_member *m; - strcat(t_info, " members"); - // Update only needed players - for(i=0;imax_member;i++){ - m = &g->member[i]; -#ifndef TXT_SQL_CONVERT - if (!m->modified) - continue; -#endif - if(m->account_id) { - //Since nothing references guild member table as foreign keys, it's safe to use REPLACE INTO - sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`name`) " - "VALUES ('%d','%d','%d','%d','%d','%d','%d','%d','%u','%d','%d','%d','%s')", - guild_member_db, g->guild_id, m->account_id,m->char_id, - m->hair,m->hair_color,m->gender, - m->class_,m->lv,m->exp,m->exp_payper,m->online,m->position, - jstrescapecpy(t_member,m->name)); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - if (m->modified & GS_MEMBER_NEW) - { - sprintf(tmp_sql,"UPDATE `%s` SET `guild_id` = '%d' WHERE `char_id` = '%d'", - char_db, g->guild_id, m->char_id); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - m->modified = GS_MEMBER_UNMODIFIED; - } - } - } - - if (flag&GS_POSITION){ - strcat(t_info, " positions"); - //printf("- Insert guild %d to guild_position\n",g->guild_id); - for(i=0;iposition[i]; -#ifndef TXT_SQL_CONVERT - if (!p->modified) - continue; -#endif - sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`position`,`name`,`mode`,`exp_mode`) VALUES ('%d','%d', '%s','%d','%d')", - guild_position_db, g->guild_id, i, jstrescapecpy(t_position,p->name),p->mode,p->exp_mode); - //printf(" %s\n",tmp_sql); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - p->modified = GS_POSITION_UNMODIFIED; - } - } - - if (flag&GS_ALLIANCE) - { - // Delete current alliances - // NOTE: no need to do it on both sides since both guilds in memory had - // their info changed, not to mention this would also mess up oppositions! - // [Skotlex] -// sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d' OR `alliance_id`='%d'",guild_alliance_db, g->guild_id,g->guild_id); - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_alliance_db, g->guild_id); - if(mysql_query(&mysql_handle, tmp_sql) ) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - else - { - //printf("- Insert guild %d to guild_alliance\n",g->guild_id); - for(i=0;ialliance[i]; - if(a->guild_id>0) - { - sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`opposition`,`alliance_id`,`name`) " - "VALUES ('%d','%d','%d','%s')", - guild_alliance_db, g->guild_id,a->opposition,a->guild_id,jstrescapecpy(t_alliance,a->name)); - //printf(" %s\n",tmp_sql); - if(mysql_query(&mysql_handle, tmp_sql) ) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - } - } - } - - if (flag&GS_EXPULSION){ - strcat(t_info, " expulsions"); - //printf("- Insert guild %d to guild_expulsion\n",g->guild_id); - for(i=0;iexpulsion[i]; - if(e->account_id>0){ - sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`name`,`mes`,`acc`,`account_id`,`rsv1`,`rsv2`,`rsv3`) " - "VALUES ('%d','%s','%s','%s','%d','%d','%d','%d')", - guild_expulsion_db, g->guild_id, - jstrescapecpy(t_ename,e->name),jstrescapecpy(t_emes,e->mes),e->acc,e->account_id,e->rsv1,e->rsv2,e->rsv3 ); - //printf(" %s\n",tmp_sql); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - } - } - - if (flag&GS_SKILL){ - strcat(t_info, " skills"); - //printf("- Insert guild %d to guild_skill\n",g->guild_id); - for(i=0;iskill[i].id>0 && g->skill[i].lv>0){ - sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`id`,`lv`) VALUES ('%d','%d','%d')", - guild_skill_db, g->guild_id,g->skill[i].id,g->skill[i].lv); - //printf("%s\n",tmp_sql); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - } - } - - if (save_log) - ShowInfo("Saved guild (%d - %s):%s\n",g->guild_id,g->name,t_info); - return 1; -} -#ifndef TXT_SQL_CONVERT -// Read guild from sql -struct guild * inter_guild_fromsql(int guild_id) -{ - int i; - char * pstr, * pEmblemData; - struct guild *g; - - if (guild_id<=0) return NULL; - - g = idb_get(guild_db_,guild_id); - if (g) return g; - - g = (struct guild*)aCalloc(sizeof(struct guild), 1); - -#ifdef NOISY - ShowInfo("Guild load request (%d)...\n", guild_id); -#endif - - sprintf(tmp_sql,"SELECT `name`,`master`,`guild_lv`,`connect_member`,`max_member`,`average_lv`,`exp`,`next_exp`,`skill_point`,`mes1`,`mes2`,`emblem_len`,`emblem_id`,`emblem_data` " - "FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id); - //printf(" %s\n",tmp_sql); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(g); - return NULL; - } - - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res==NULL || mysql_num_rows(sql_res)<1) { - //Guild does not exists. - if (sql_res) mysql_free_result(sql_res); - aFree(g); - return NULL; - } - - sql_row = mysql_fetch_row(sql_res); - if (sql_row==NULL) { - mysql_free_result(sql_res); - aFree(g); - return NULL; - } - - g->guild_id=guild_id; - strncpy(g->name,sql_row[0],NAME_LENGTH-1); - strncpy(g->master,sql_row[1],NAME_LENGTH-1); - g->guild_lv=atoi(sql_row[2]); - g->connect_member=atoi(sql_row[3]); - g->max_member = atoi(sql_row[4]); - if (g->max_member > MAX_GUILD) - { // Fix reduction of MAX_GUILD [PoW] - ShowWarning("Guild %d:%s specifies higher capacity (%d) than MAX_GUILD (%d)\n", guild_id, g->name, g->max_member, MAX_GUILD); - g->max_member = MAX_GUILD; - } - g->average_lv=atoi(sql_row[5]); - g->exp=(unsigned int)atof(sql_row[6]); - g->next_exp=(unsigned int)atof(sql_row[7]); - g->skill_point=atoi(sql_row[8]); - //There shouldn't be a need to copy the very last char, as it's the \0 [Skotlex] - strncpy(g->mes1,sql_row[9],59); - strncpy(g->mes2,sql_row[10],119); - g->emblem_len=atoi(sql_row[11]); - g->emblem_id=atoi(sql_row[12]); - for(i=0,pstr=sql_row[13],pEmblemData=g->emblem_data; i < g->emblem_len; i++,pstr+=2){ - int c1=pstr[0],c2=pstr[1],x1=0,x2=0; - if(c1>='0' && c1<='9') - x1=c1-'0'; - else if(c1>='a' && c1<='f') - x1=c1-'a'+10; - else if(c1>='A' && c1<='F') - x1=c1-'A'+10; - if(c2>='0' && c2<='9') - x2=c2-'0'; - else if(c2>='a' && c2<='f') - x2=c2-'a'+10; - else if(c2>='A' && c2<='F') - x2=c2-'A'+10; - *pEmblemData++=(x1<<4)|x2; - } - mysql_free_result(sql_res); - - //printf("- Read guild_member %d from sql \n",guild_id); - sprintf(tmp_sql,"SELECT `guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`rsv1`,`rsv2`,`name` " - "FROM `%s` WHERE `guild_id`='%d' ORDER BY `position`", guild_member_db, guild_id); - //printf(" %s\n",tmp_sql); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(g); - return NULL; - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res) { - for(i=0;((sql_row = mysql_fetch_row(sql_res))&&imax_member);i++){ - struct guild_member *m = &g->member[i]; - m->account_id=atoi(sql_row[1]); - m->char_id=atoi(sql_row[2]); - m->hair=atoi(sql_row[3]); - m->hair_color=atoi(sql_row[4]); - m->gender=atoi(sql_row[5]); - m->class_=atoi(sql_row[6]); - m->lv=atoi(sql_row[7]); - m->exp=strtoul(sql_row[8],NULL,10); - m->exp_payper=atoi(sql_row[9]); - m->online=atoi(sql_row[10]); - m->position = atoi(sql_row[11]); - if (m->position >= MAX_GUILDPOSITION) // Fix reduction of MAX_GUILDPOSITION [PoW] - m->position = MAX_GUILDPOSITION - 1; - - strncpy(m->name,sql_row[14],NAME_LENGTH-1); - m->modified = GS_MEMBER_UNMODIFIED; - } - mysql_free_result(sql_res); - } - - //printf("- Read guild_position %d from sql \n",guild_id); - sprintf(tmp_sql,"SELECT `guild_id`,`position`,`name`,`mode`,`exp_mode` FROM `%s` WHERE `guild_id`='%d'",guild_position_db, guild_id); - //printf(" %s\n",tmp_sql); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(g); - return NULL; - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res) { - for(i=0;((sql_row = mysql_fetch_row(sql_res))&&iposition[position]; - strncpy(p->name,sql_row[2],NAME_LENGTH-1); - p->mode=atoi(sql_row[3]); - p->exp_mode=atoi(sql_row[4]); - p->modified = GS_POSITION_UNMODIFIED; - } - mysql_free_result(sql_res); - } - - //printf("- Read guild_alliance %d from sql \n",guild_id); - sprintf(tmp_sql,"SELECT `guild_id`,`opposition`,`alliance_id`,`name` FROM `%s` WHERE `guild_id`='%d'",guild_alliance_db, guild_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(g); - return NULL; - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res) { - for(i=0;((sql_row = mysql_fetch_row(sql_res))&&ialliance[i]; - a->opposition=atoi(sql_row[1]); - a->guild_id=atoi(sql_row[2]); - strncpy(a->name,sql_row[3],NAME_LENGTH-1); - } - mysql_free_result(sql_res); - } - - //printf("- Read guild_expulsion %d from sql \n",guild_id); - sprintf(tmp_sql,"SELECT `guild_id`,`name`,`mes`,`acc`,`account_id`,`rsv1`,`rsv2`,`rsv3` FROM `%s` WHERE `guild_id`='%d'",guild_expulsion_db, guild_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(g); - return NULL; - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res) { - for(i=0;((sql_row = mysql_fetch_row(sql_res))&&iexpulsion[i]; - - strncpy(e->name,sql_row[1],NAME_LENGTH-1); - //No need to copy char 40, the null terminator. [Skotlex] - strncpy(e->mes,sql_row[2],39); - strncpy(e->acc,sql_row[3],39); - e->account_id=atoi(sql_row[4]); - e->rsv1=atoi(sql_row[5]); - e->rsv2=atoi(sql_row[6]); - e->rsv3=atoi(sql_row[7]); - } - mysql_free_result(sql_res); - } - - //printf("- Read guild_skill %d from sql \n",guild_id); - sprintf(tmp_sql,"SELECT `guild_id`,`id`,`lv` FROM `%s` WHERE `guild_id`='%d' ORDER BY `id`",guild_skill_db, guild_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(g); - return NULL; - } - - for(i = 0; i < MAX_GUILDSKILL; i++) - { //Skill IDs must always be initialized. [Skotlex] - g->skill[i].id = i + GD_SKILLBASE; - } - - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res) { - while ((sql_row = mysql_fetch_row(sql_res))){ - int id = atoi(sql_row[1])-GD_SKILLBASE; - if (id >= 0 && id < MAX_GUILDSKILL) - //I know this seems ridiculous, but the skills HAVE to be placed on their 'correct' array slot or things break x.x [Skotlex] - g->skill[id].lv=atoi(sql_row[2]); - } - mysql_free_result(sql_res); - } - - idb_put(guild_db_, guild_id, g); //Add to cache - g->save_flag |= GS_REMOVE; //But set it to be removed, in case it is not needed for long. - - if (save_log) - ShowInfo("Guild loaded (%d - %s)\n", guild_id, g->name); - - return g; -} - -#endif //TXT_SQL_CONVERT -int inter_guildcastle_tosql(struct guild_castle *gc){ - // `guild_castle` (`castle_id`, `guild_id`, `economy`, `defense`, `triggerE`, `triggerD`, `nextTime`, `payTime`, `createTime`, `visibleC`, `visibleG0`, `visibleG1`, `visibleG2`, `visibleG3`, `visibleG4`, `visibleG5`, `visibleG6`, `visibleG7`) - - if (gc==NULL) return 0; - #ifdef GUILD_DEBUG -ShowDebug("Save guild_castle (%d)\n", gc->castle_id); - #endif - -// sql_query("DELETE FROM `%s` WHERE `castle_id`='%d'",guild_castle_db, gc->castle_id); - - sprintf(tmp_sql,"REPLACE INTO `%s` " - "(`castle_id`, `guild_id`, `economy`, `defense`, `triggerE`, `triggerD`, `nextTime`, `payTime`, `createTime`," - "`visibleC`, `visibleG0`, `visibleG1`, `visibleG2`, `visibleG3`, `visibleG4`, `visibleG5`, `visibleG6`, `visibleG7`," - "`Ghp0`, `Ghp1`, `Ghp2`, `Ghp3`, `Ghp4`, `Ghp5`, `Ghp6`, `Ghp7`)" - "VALUES ('%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d')", - guild_castle_db, gc->castle_id, gc->guild_id, gc->economy, gc->defense, gc->triggerE, gc->triggerD, gc->nextTime, gc->payTime, - gc->createTime, gc->visibleC, - gc->guardian[0].visible, gc->guardian[1].visible, gc->guardian[2].visible, gc->guardian[3].visible, gc->guardian[4].visible, gc->guardian[5].visible, gc->guardian[6].visible, gc->guardian[7].visible, - gc->guardian[0].hp, gc->guardian[1].hp, gc->guardian[2].hp, gc->guardian[3].hp, gc->guardian[4].hp, gc->guardian[5].hp, gc->guardian[6].hp, gc->guardian[7].hp); - - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - -#ifndef TXT_SQL_CONVERT - memcpy(&castles[gc->castle_id],gc,sizeof(struct guild_castle)); -#endif //TXT_SQL_CONVERT - return 0; -} -#ifndef TXT_SQL_CONVERT - -// Read guild_castle from sql -int inter_guildcastle_fromsql(int castle_id,struct guild_castle *gc) -{ - static int castles_init=0; - if (gc==NULL) return 0; - if (castle_id==-1) return 0; - - if(!castles_init) - { - int i; - for(i=0;icastle_id=castle_id; - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res!=NULL && mysql_num_rows(sql_res)>0) { - sql_row = mysql_fetch_row(sql_res); - if (sql_row==NULL){ - mysql_free_result(sql_res); - return 1; //Assume empty castle. - } - gc->guild_id = atoi (sql_row[1]); - gc->economy = atoi (sql_row[2]); - gc->defense = atoi (sql_row[3]); - gc->triggerE = atoi (sql_row[4]); - gc->triggerD = atoi (sql_row[5]); - gc->nextTime = atoi (sql_row[6]); - gc->payTime = atoi (sql_row[7]); - gc->createTime = atoi (sql_row[8]); - gc->visibleC = atoi (sql_row[9]); - gc->guardian[0].visible = atoi (sql_row[10]); - gc->guardian[1].visible = atoi (sql_row[11]); - gc->guardian[2].visible = atoi (sql_row[12]); - gc->guardian[3].visible = atoi (sql_row[13]); - gc->guardian[4].visible = atoi (sql_row[14]); - gc->guardian[5].visible = atoi (sql_row[15]); - gc->guardian[6].visible = atoi (sql_row[16]); - gc->guardian[7].visible = atoi (sql_row[17]); - gc->guardian[0].hp = atoi (sql_row[18]); - gc->guardian[1].hp = atoi (sql_row[19]); - gc->guardian[2].hp = atoi (sql_row[20]); - gc->guardian[3].hp = atoi (sql_row[21]); - gc->guardian[4].hp = atoi (sql_row[22]); - gc->guardian[5].hp = atoi (sql_row[23]); - gc->guardian[6].hp = atoi (sql_row[24]); - gc->guardian[7].hp = atoi (sql_row[25]); - - if (save_log) - ShowInfo("Loaded Castle %d (guild %d)\n",castle_id,gc->guild_id); - - } - mysql_free_result(sql_res) ; //resource free - - memcpy(&castles[castle_id],gc,sizeof(struct guild_castle)); - - return 1; -} - - -// Read exp_guild.txt -int inter_guild_ReadEXP(void) -{ - int i; - FILE *fp; - char line[1024]; - for (i=0;i<100;i++) guild_exp[i]=0; - - sprintf(line, "%s/exp_guild.txt", db_path); - fp=fopen(line,"r"); - if(fp==NULL){ - ShowError("can't read %s\n", line); - return 1; - } - i=0; - while(fgets(line,256,fp) && i<100){ - if(line[0]=='/' && line[1]=='/') - continue; - guild_exp[i]=(unsigned int)atof(line); - i++; - } - fclose(fp); - - return 0; -} - - -int inter_guild_CharOnline(int char_id, int guild_id) { - - struct guild *g; - int i; - - if (guild_id == -1) { - //Get guild_id from the database - sprintf (tmp_sql , "SELECT guild_id FROM `%s` WHERE char_id='%d'",char_db,char_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - - sql_res = mysql_store_result(&mysql_handle) ; - if(sql_res == NULL) - return 0; //Eh? No guild? - - sql_row = mysql_fetch_row(sql_res); - guild_id = sql_row?atoi(sql_row[0]):0; - mysql_free_result(sql_res); - } - if (guild_id == 0) - return 0; //No guild... - - g = inter_guild_fromsql(guild_id); - if(!g) { - ShowError("Character %d's guild %d not found!\n", char_id, guild_id); - return 0; - } - - //Member has logged in before saving, tell saver not to delete - if(g->save_flag & GS_REMOVE) - g->save_flag &= ~GS_REMOVE; - - //Set member online - for(i=0; imax_member; i++) { - if (g->member[i].char_id == char_id) { - g->member[i].online = 1; - g->member[i].modified = GS_MEMBER_MODIFIED; - break; - } - } - return 1; -} - -int inter_guild_CharOffline(int char_id, int guild_id) { - struct guild *g=NULL; - int online_count=0, i; - - if (guild_id == -1) { - //Get guild_id from the database - sprintf (tmp_sql , "SELECT guild_id FROM `%s` WHERE char_id='%d'",char_db,char_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - - sql_res = mysql_store_result(&mysql_handle) ; - if(sql_res == NULL) - return 0; //Eh? No guild? - - sql_row = mysql_fetch_row(sql_res); - guild_id = sql_row?atoi(sql_row[0]):0; - mysql_free_result(sql_res); - } - if (guild_id == 0) - return 0; //No guild... - - //Character has a guild, set character offline and check if they were the only member online - g = inter_guild_fromsql(guild_id); - if (g == NULL) //Guild not found? - return 0; - - //Set member offline - for(i=0; imax_member; i++) { - if(g->member[i].char_id == char_id) - { - g->member[i].online = 0; - g->member[i].modified = GS_MEMBER_MODIFIED; - } - if(g->member[i].online && !online_count) - online_count++; - } - - // Remove guild from memory if no players online - if(online_count == 0) - g->save_flag |= GS_REMOVE; - - return 1; -} - -// Initialize guild sql -int inter_guild_sql_init(void) -{ - //Initialize the guild cache - guild_db_= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - //Read exp file - inter_guild_ReadEXP(); - - add_timer_func_list(guild_save_timer, "guild_save_timer"); - add_timer(gettick() + 10000, guild_save_timer, 0, 0); - return 0; -} - -static int guild_db_final(DBKey key, void *data, va_list ap) -{ - struct guild *g = (struct guild*)data; - if (g->save_flag&GS_MASK) { - inter_guild_tosql(g, g->save_flag&GS_MASK); - return 1; - } - return 0; -} - -void inter_guild_sql_final(void) -{ - guild_db_->destroy(guild_db_, guild_db_final); - return; -} - -// Get guild_id by its name. Returns 0 if not found, -1 on error. -int search_guildname(char *str) -{ - int guild_id; - char t_name[NAME_LENGTH*2]; - - jstrescapecpy(t_name, str); - //Lookup guilds with the same name - sprintf (tmp_sql , "SELECT guild_id FROM `%s` WHERE name='%s'", guild_db, t_name); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return -1; - } - - sql_res = mysql_store_result(&mysql_handle) ; - if(sql_res) - sql_row = mysql_fetch_row(sql_res); - - guild_id = (sql_row&&sql_res&&sql_row[0])?atoi(sql_row[0]):0; - mysql_free_result(sql_res); - return guild_id; -} - -// Check if guild is empty -int guild_check_empty(struct guild *g) -{ - int i; - for(i=0;imax_member;i++){ - if(g->member[i].account_id>0){ - return 0; - } - } - //Let the calling function handle the guild removal in case they need - //to do something else with it before freeing the data. [Skotlex] - return 1; -} - -unsigned int guild_nextexp(int level) -{ - if (level == 0) - return 1; - if (level < 100 && level > 0) // Change by hack - return guild_exp[level-1]; - - return 0; -} - -int guild_checkskill(struct guild *g,int id) { - - int idx = id - GD_SKILLBASE; - - if(idx < 0 || idx >= MAX_GUILDSKILL) - return 0; - - return g->skill[idx].lv; -} - -int guild_calcinfo(struct guild *g) -{ - int i,c; - unsigned int nextexp; - struct guild before = *g; // Save guild current values - - if(g->guild_lv<=0) - g->guild_lv = 1; - nextexp = guild_nextexp(g->guild_lv); - - // Consume guild exp and increase guild level - while(g->exp >= nextexp && nextexp > 0){ //fixed guild exp overflow [Kevin] - g->exp-=nextexp; - g->guild_lv++; - g->skill_point++; - nextexp = guild_nextexp(g->guild_lv); - } - - // Save next exp step - g->next_exp = nextexp; - - // Set the max number of members, Guild Extention skill - currently adds 6 to max per skill lv. - g->max_member = 16 + guild_checkskill(g, GD_EXTENSION) * 6; - if(g->max_member > MAX_GUILD) - { - ShowError("Guild %d:%s has capacity for too many guild members (%d), max supported is %d\n", g->guild_id, g->name, g->max_member, MAX_GUILD); - g->max_member = MAX_GUILD; - } - - // Compute the guild average level level - g->average_lv=0; - g->connect_member=0; - for(i=c=0;imax_member;i++) - { - if(g->member[i].account_id>0) - { - if (g->member[i].lv >= 0) - { - g->average_lv+=g->member[i].lv; - c++; - } - else - { - ShowWarning("Guild %d:%s, member %d:%s has an invalid level %d\n", g->guild_id, g->name, g->member[i].char_id, g->member[i].name, g->member[i].lv); - } - - if(g->member[i].online) - g->connect_member++; - } - } - if(c) - g->average_lv /= c; - - // Check if guild stats has change - if(g->max_member != before.max_member || g->guild_lv != before.guild_lv || g->skill_point != before.skill_point ) - { - g->save_flag |= GS_LEVEL; - mapif_guild_info(-1,g); - return 1; - } - - return 0; -} - -//------------------------------------------------------------------- -// Packet sent to map server - -int mapif_guild_created(int fd,int account_id,struct guild *g) -{ - WFIFOHEAD(fd, 10); - WFIFOW(fd,0)=0x3830; - WFIFOL(fd,2)=account_id; - if(g != NULL) - { - WFIFOL(fd,6)=g->guild_id; - ShowInfo("int_guild: Guild created (%d - %s)\n",g->guild_id,g->name); - } else - WFIFOL(fd,6)=0; - - WFIFOSET(fd,10); - return 0; -} -// Guild not found -int mapif_guild_noinfo(int fd,int guild_id) -{ - unsigned char buf[12]; - WBUFW(buf,0)=0x3831; - WBUFW(buf,2)=8; - WBUFL(buf,4)=guild_id; - ShowWarning("int_guild: info not found %d\n",guild_id); - if(fd<0) - mapif_sendall(buf,8); - else - mapif_send(fd,buf,8); - return 0; -} - -// Send guild info -int mapif_guild_info(int fd,struct guild *g) -{ - unsigned char buf[8+sizeof(struct guild)]; - WBUFW(buf,0)=0x3831; - WBUFW(buf,2)=4+sizeof(struct guild); - memcpy(buf+4,g,sizeof(struct guild)); - if(fd<0) - mapif_sendall(buf,WBUFW(buf,2)); - else - mapif_send(fd,buf,WBUFW(buf,2)); - return 0; -} - -// ACK member add -int mapif_guild_memberadded(int fd,int guild_id,int account_id,int char_id,int flag) -{ - WFIFOHEAD(fd, 15); - WFIFOW(fd,0)=0x3832; - WFIFOL(fd,2)=guild_id; - WFIFOL(fd,6)=account_id; - WFIFOL(fd,10)=char_id; - WFIFOB(fd,14)=flag; - WFIFOSET(fd,15); - return 0; -} - -// ACK member leave -int mapif_guild_leaved(int guild_id,int account_id,int char_id,int flag, const char *name, const char *mes) -{ - unsigned char buf[55+NAME_LENGTH]; - WBUFW(buf, 0)=0x3834; - WBUFL(buf, 2)=guild_id; - WBUFL(buf, 6)=account_id; - WBUFL(buf,10)=char_id; - WBUFB(buf,14)=flag; - memcpy(WBUFP(buf,15),mes,40); - memcpy(WBUFP(buf,55),name,NAME_LENGTH); - mapif_sendall(buf,55+NAME_LENGTH); - ShowInfo("int_guild: guild leaved (%d - %d: %s - %s)\n",guild_id,account_id,name,mes); - return 0; -} - -// Send short member's info -int mapif_guild_memberinfoshort(struct guild *g,int idx) -{ - unsigned char buf[19]; - WBUFW(buf, 0)=0x3835; - WBUFL(buf, 2)=g->guild_id; - WBUFL(buf, 6)=g->member[idx].account_id; - WBUFL(buf,10)=g->member[idx].char_id; - WBUFB(buf,14)=(unsigned char)g->member[idx].online; - WBUFW(buf,15)=g->member[idx].lv; - WBUFW(buf,17)=g->member[idx].class_; - mapif_sendall(buf,19); - return 0; -} - -// Send guild broken -int mapif_guild_broken(int guild_id,int flag) -{ - unsigned char buf[7]; - WBUFW(buf,0)=0x3836; - WBUFL(buf,2)=guild_id; - WBUFB(buf,6)=flag; - mapif_sendall(buf,7); - ShowInfo("int_guild: Guild broken (%d)\n",guild_id); - return 0; -} - -// Send guild message -int mapif_guild_message(int guild_id,int account_id,char *mes,int len, int sfd) -{ - unsigned char buf[512]; - if (len > 500) - len = 500; - WBUFW(buf,0)=0x3837; - WBUFW(buf,2)=len+12; - WBUFL(buf,4)=guild_id; - WBUFL(buf,8)=account_id; - memcpy(WBUFP(buf,12),mes,len); - mapif_sendallwos(sfd, buf,len+12); - return 0; -} - -// Send basic info -int mapif_guild_basicinfochanged(int guild_id,int type,const void *data,int len) -{ - unsigned char buf[2048]; - if (len > 2038) - len = 2038; - WBUFW(buf, 0)=0x3839; - WBUFW(buf, 2)=len+10; - WBUFL(buf, 4)=guild_id; - WBUFW(buf, 8)=type; - memcpy(WBUFP(buf,10),data,len); - mapif_sendall(buf,len+10); - return 0; -} - -// Send member info -int mapif_guild_memberinfochanged(int guild_id,int account_id,int char_id, int type,const void *data,int len) -{ - unsigned char buf[2048]; - if (len > 2030) - len = 2030; - WBUFW(buf, 0)=0x383a; - WBUFW(buf, 2)=len+18; - WBUFL(buf, 4)=guild_id; - WBUFL(buf, 8)=account_id; - WBUFL(buf,12)=char_id; - WBUFW(buf,16)=type; - memcpy(WBUFP(buf,18),data,len); - mapif_sendall(buf,len+18); - return 0; -} - -// ACK guild skill up -int mapif_guild_skillupack(int guild_id,int skill_num,int account_id) -{ - unsigned char buf[14]; - WBUFW(buf, 0)=0x383c; - WBUFL(buf, 2)=guild_id; - WBUFL(buf, 6)=skill_num; - WBUFL(buf,10)=account_id; - mapif_sendall(buf,14); - return 0; -} - -// ACK guild alliance -int mapif_guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2, - int flag,const char *name1,const char *name2) -{ - unsigned char buf[19+2*NAME_LENGTH]; - WBUFW(buf, 0)=0x383d; - WBUFL(buf, 2)=guild_id1; - WBUFL(buf, 6)=guild_id2; - WBUFL(buf,10)=account_id1; - WBUFL(buf,14)=account_id2; - WBUFB(buf,18)=flag; - memcpy(WBUFP(buf,19),name1,NAME_LENGTH); - memcpy(WBUFP(buf,19+NAME_LENGTH),name2,NAME_LENGTH); - mapif_sendall(buf,19+2*NAME_LENGTH); - return 0; -} - -// Send a guild position desc -int mapif_guild_position(struct guild *g,int idx) -{ - unsigned char buf[12 + sizeof(struct guild_position)]; - WBUFW(buf,0)=0x383b; - WBUFW(buf,2)=sizeof(struct guild_position)+12; - WBUFL(buf,4)=g->guild_id; - WBUFL(buf,8)=idx; - memcpy(WBUFP(buf,12),&g->position[idx],sizeof(struct guild_position)); - mapif_sendall(buf,WBUFW(buf,2)); - return 0; -} - -// Send the guild notice -int mapif_guild_notice(struct guild *g) -{ - unsigned char buf[256]; - WBUFW(buf,0)=0x383e; - WBUFL(buf,2)=g->guild_id; - memcpy(WBUFP(buf,6),g->mes1,60); - memcpy(WBUFP(buf,66),g->mes2,120); - mapif_sendall(buf,186); - return 0; -} - -// Send emblem data -int mapif_guild_emblem(struct guild *g) -{ - unsigned char buf[12 + sizeof(g->emblem_data)]; - WBUFW(buf,0)=0x383f; - WBUFW(buf,2)=g->emblem_len+12; - WBUFL(buf,4)=g->guild_id; - WBUFL(buf,8)=g->emblem_id; - memcpy(WBUFP(buf,12),g->emblem_data,g->emblem_len); - mapif_sendall(buf,WBUFW(buf,2)); - return 0; -} - -int mapif_guild_master_changed(struct guild *g, int position) -{ - unsigned char buf[10]; - WBUFW(buf,0)=0x3843; - WBUFL(buf,2)=g->guild_id; - WBUFL(buf,6)=position; - mapif_sendall(buf,10); - return 0; -} - -int mapif_guild_castle_dataload(int castle_id,int index,int value) // -{ - unsigned char buf[9]; - WBUFW(buf, 0)=0x3840; - WBUFW(buf, 2)=castle_id; - WBUFB(buf, 4)=index; - WBUFL(buf, 5)=value; - mapif_sendall(buf,9); - return 0; -} - -int mapif_guild_castle_datasave(int castle_id,int index,int value) // -{ - unsigned char buf[9]; - WBUFW(buf, 0)=0x3841; - WBUFW(buf, 2)=castle_id; - WBUFB(buf, 4)=index; - WBUFL(buf, 5)=value; - mapif_sendall(buf,9); - return 0; -} - -int mapif_guild_castle_alldataload(int fd) { - struct guild_castle* gc = (struct guild_castle *)aMalloc(sizeof(struct guild_castle)); - int i, len = 4; - WFIFOHEAD(fd, len + MAX_GUILDCASTLE*sizeof(struct guild_castle)); - WFIFOW(fd,0) = 0x3842; - sprintf(tmp_sql,"SELECT * FROM `%s` ORDER BY `castle_id`", guild_castle_db); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - for(i = 0; ((sql_row = mysql_fetch_row(sql_res)) && i < MAX_GUILDCASTLE); i++) { - memset(gc, 0, sizeof(struct guild_castle)); - gc->castle_id = atoi(sql_row[0]); - gc->guild_id = atoi(sql_row[1]); - gc->economy = atoi(sql_row[2]); - gc->defense = atoi(sql_row[3]); - gc->triggerE = atoi(sql_row[4]); - gc->triggerD = atoi(sql_row[5]); - gc->nextTime = atoi(sql_row[6]); - gc->payTime = atoi(sql_row[7]); - gc->createTime = atoi(sql_row[8]); - gc->visibleC = atoi(sql_row[9]); - gc->guardian[0].visible = atoi(sql_row[10]); - gc->guardian[1].visible = atoi(sql_row[11]); - gc->guardian[2].visible = atoi(sql_row[12]); - gc->guardian[3].visible = atoi(sql_row[13]); - gc->guardian[4].visible = atoi(sql_row[14]); - gc->guardian[5].visible = atoi(sql_row[15]); - gc->guardian[6].visible = atoi(sql_row[16]); - gc->guardian[7].visible = atoi(sql_row[17]); - gc->guardian[0].visible = atoi(sql_row[18]); - gc->guardian[1].visible = atoi(sql_row[19]); - gc->guardian[2].visible = atoi(sql_row[20]); - gc->guardian[3].visible = atoi(sql_row[21]); - gc->guardian[4].visible = atoi(sql_row[22]); - gc->guardian[5].visible = atoi(sql_row[23]); - gc->guardian[6].visible = atoi(sql_row[24]); - gc->guardian[7].visible = atoi(sql_row[25]); - memcpy(WFIFOP(fd,len), gc, sizeof(struct guild_castle)); - len += sizeof(struct guild_castle); - } - mysql_free_result(sql_res); - } - WFIFOW(fd,2) = len; - WFIFOSET(fd,len); - - aFree(gc); - - return 0; -} - - -//------------------------------------------------------------------- -// Packet received from map server - - -// ギルド作成要求 -int mapif_parse_CreateGuild(int fd,int account_id,char *name,struct guild_member *master) -{ - struct guild *g; - int i=0; -#ifdef NOISY - ShowInfo("Creating Guild (%s)\n", name); -#endif - if(search_guildname(name) != 0){ - ShowInfo("int_guild: guild with same name exists [%s]\n",name); - mapif_guild_created(fd,account_id,NULL); - return 0; - } - // Check Authorised letters/symbols in the name of the character - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) { - mapif_guild_created(fd,account_id,NULL); - return 0; - } - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) { - mapif_guild_created(fd,account_id,NULL); - return 0; - } - } - - g = (struct guild *)aMalloc(sizeof(struct guild)); - memset(g,0,sizeof(struct guild)); - - memcpy(g->name,name,NAME_LENGTH); - memcpy(g->master,master->name,NAME_LENGTH); - memcpy(&g->member[0],master,sizeof(struct guild_member)); - g->member[0].modified = GS_MEMBER_MODIFIED; - - // Set default positions - g->position[0].mode=0x11; - strcpy(g->position[0].name,"GuildMaster"); - strcpy(g->position[MAX_GUILDPOSITION-1].name,"Newbie"); - for(i=1;iposition[i].name,"Position %d",i+1); - - // Initialize guild property - g->max_member=16; - g->average_lv=master->lv; - for(i=0;iskill[i].id=i + GD_SKILLBASE; - g->guild_id= -1; //Request to create guild. - - // Create the guild - if (!inter_guild_tosql(g,GS_BASIC|GS_POSITION|GS_SKILL)) { - //Failed to Create guild.... - ShowError("Failed to create Guild %s (Guild Master: %s)\n", g->name, g->master); - mapif_guild_created(fd,account_id,NULL); - aFree(g); - return 0; - } - ShowInfo("Created Guild %d - %s (Guild Master: %s)\n", g->guild_id, g->name, g->master); - - //Add to cache - idb_put(guild_db_, g->guild_id, g); - - // Report to client - mapif_guild_created(fd,account_id,g); - mapif_guild_info(fd,g); - - if(log_inter) - inter_log("guild %s (id=%d) created by master %s (id=%d)" RETCODE, - name, g->guild_id, master->name, master->account_id ); - - return 0; -} - -// Return guild info to client -int mapif_parse_GuildInfo(int fd,int guild_id) -{ - struct guild * g = inter_guild_fromsql(guild_id); //We use this because on start-up the info of castle-owned guilds is requied. [Skotlex] - if(g) - { - if (!guild_calcinfo(g)) - mapif_guild_info(fd,g); - } - else - mapif_guild_noinfo(fd,guild_id); // Failed to load info - return 0; -} - -// Add member to guild -int mapif_parse_GuildAddMember(int fd,int guild_id,struct guild_member *m) -{ - struct guild * g; - int i; - - g = inter_guild_fromsql(guild_id); - if(g==NULL){ - // Failed to add - mapif_guild_memberadded(fd,guild_id,m->account_id,m->char_id,1); - return 0; - } - - // Find an empty slot - for(i=0;imax_member;i++) - { - if(g->member[i].account_id==0) - { - memcpy(&g->member[i],m,sizeof(struct guild_member)); - g->member[i].modified = (GS_MEMBER_NEW | GS_MEMBER_MODIFIED); - mapif_guild_memberadded(fd,guild_id,m->account_id,m->char_id,0); - if (!guild_calcinfo(g)) //Send members if it was not invoked. - mapif_guild_info(fd,g); - - g->save_flag |= GS_MEMBER; - if (g->save_flag&GS_REMOVE) - g->save_flag&=~GS_REMOVE; - return 0; - } - } - - // Failed to add - mapif_guild_memberadded(fd,guild_id,m->account_id,m->char_id,1); - return 0; -} - -// Delete member from guild -int mapif_parse_GuildLeave(int fd,int guild_id,int account_id,int char_id,int flag,const char *mes) -{ - struct guild * g = inter_guild_fromsql(guild_id); - - if(g){ - int i; - // Find the member - for(i=0;imax_member;i++){ - if( g->member[i].account_id==account_id && g->member[i].char_id==char_id) - { - if(flag) - { - // Write expulsion reason - int j; - // Find an empty slot - for(j=0;jexpulsion[j].account_id==0) - break; - } - // Expulsion list is full, flush the oldest one - if(j==MAX_GUILDEXPULSION){ - for(j=0;jexpulsion[j]=g->expulsion[j+1]; - j=MAX_GUILDEXPULSION-1; - } - // Save the expulsion - g->expulsion[j].account_id=account_id; - memcpy(g->expulsion[j].acc,"dummy",NAME_LENGTH-1); - memcpy(g->expulsion[j].name,g->member[i].name,NAME_LENGTH-1); - memcpy(g->expulsion[j].mes,mes,40); - } - - mapif_guild_leaved(guild_id,account_id,char_id,flag,g->member[i].name,mes); - inter_guild_removemember_tosql(g->member[i].account_id,g->member[i].char_id); - - memset(&g->member[i],0,sizeof(struct guild_member)); - - if(!guild_check_empty(g)) { - break; - } - //Guild empty? break it. - mapif_parse_BreakGuild(-1,guild_id); //Break the guild. - return 0; - } - } - //Update member info. - if (!guild_calcinfo(g)) - mapif_guild_info(fd,g); - g->save_flag |= GS_EXPULSION; - }else{ - // Unknown guild, just update the player - sprintf(tmp_sql, "UPDATE `%s` SET `guild_id`='0' WHERE `account_id`='%d' AND `char_id`='%d'",char_db, account_id,char_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - /* mapif_guild_leaved(guild_id,account_id,char_id,flag,g->member[i].name,mes); */ - } - - return 0; -} - -// Change member info -int mapif_parse_GuildChangeMemberInfoShort(int fd,int guild_id, - int account_id,int char_id,int online,int lv,int class_) -{ - // Could speed up by manipulating only guild_member - struct guild * g; - int i,alv,c; - int prev_count; - - g = inter_guild_fromsql(guild_id); - if(g==NULL) - return 0; - - prev_count = g->connect_member; - g->connect_member=0; - - for(i=0,alv=0,c=0;imax_member;i++) - { - // Found the member - if(g->member[i].account_id==account_id && g->member[i].char_id==char_id) - { - g->member[i].online=online; - g->member[i].lv=lv; - g->member[i].class_=class_; - g->member[i].modified = GS_MEMBER_MODIFIED; - mapif_guild_memberinfoshort(g,i); - } - if( g->member[i].account_id>0 ) - { - if (g->member[i].lv > 0) - { - alv+=g->member[i].lv; - c++; - } - else - { - ShowWarning("Guild %d:%s, member %d:%s has an invalid level %d\n", g->guild_id, g->name, g->member[i].char_id, g->member[i].name, g->member[i].lv); - } - } - if( g->member[i].online ) - g->connect_member++; - } - - if (c) - { - alv = alv/c; - if (g->connect_member != prev_count || g->average_lv != alv) - { - g->average_lv=alv; - g->save_flag |= GS_CONNECT; - } - if (g->save_flag & GS_REMOVE) - g->save_flag &= ~GS_REMOVE; - } - g->save_flag |= GS_MEMBER; //Update guild member data - return 0; -} - -// BreakGuild -int mapif_parse_BreakGuild(int fd,int guild_id) -{ - struct guild * g; - - g = inter_guild_fromsql(guild_id); - if(g==NULL) - return 0; - - // Delete guild from sql - //printf("- Delete guild %d from guild\n",guild_id); - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_member_db, guild_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_castle_db, guild_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_storage_db, guild_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d' OR `alliance_id` = '%d'", guild_alliance_db, guild_id, guild_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_position_db, guild_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_skill_db, guild_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_expulsion_db, guild_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - //printf("- Update guild %d of char\n",guild_id); - sprintf(tmp_sql, "UPDATE `%s` SET `guild_id`='0' WHERE `guild_id`='%d'",char_db, guild_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - mapif_guild_broken(guild_id,0); - - if(log_inter) - inter_log("guild %s (id=%d) broken" RETCODE,g->name,guild_id); - - //Remove the guild from memory. [Skotlex] - idb_remove(guild_db_, guild_id); - return 0; -} - -// Forward Guild message to others map servers -int mapif_parse_GuildMessage(int fd,int guild_id,int account_id,char *mes,int len) -{ - return mapif_guild_message(guild_id,account_id,mes,len, fd); -} - -// Modification of the guild -int mapif_parse_GuildBasicInfoChange(int fd,int guild_id,int type,const char *data,int len) -{ - struct guild * g; - short dw=*((short *)data); - g = inter_guild_fromsql(guild_id); - if(g==NULL) - return 0; - - switch(type) - { - case GBI_GUILDLV: - ShowDebug("GBI_GUILDLV\n"); - if(dw>0 && g->guild_lv+dw<=50) - { - g->guild_lv+=dw; - g->skill_point+=dw; - } - else if(dw<0 && g->guild_lv+dw>=1) - g->guild_lv+=dw; - mapif_guild_info(-1,g); - g->save_flag |= GS_LEVEL; - return 0; - default: - ShowError("int_guild: GuildBasicInfoChange: Unknown type %d\n",type); - break; - } - mapif_guild_basicinfochanged(guild_id,type,data,len); - return 0; -} - -// Modification of the guild -int mapif_parse_GuildMemberInfoChange(int fd,int guild_id,int account_id,int char_id, - int type,const char *data,int len) -{ - // Could make some improvement in speed, because only change guild_member - int i; - struct guild * g; - - g = inter_guild_fromsql(guild_id); - if(g==NULL) - return 0; - - // Search the member - for(i=0;imax_member;i++) - if( g->member[i].account_id==account_id && - g->member[i].char_id==char_id ) - break; - - // Not Found - if(i==g->max_member){ - ShowWarning("int_guild: GuildMemberChange: Not found %d,%d in guild (%d - %s)\n", - account_id,char_id,guild_id,g->name); - return 0; - } - - switch(type) - { - case GMI_POSITION: - { - g->member[i].position=*((int *)data); - g->member[i].modified = GS_MEMBER_MODIFIED; - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - g->save_flag |= GS_MEMBER; - break; - } - case GMI_EXP: - { // EXP - unsigned int exp, old_exp=g->member[i].exp; - g->member[i].exp=*((unsigned int *)data); - g->member[i].modified = GS_MEMBER_MODIFIED; - if (g->member[i].exp > old_exp) - { - exp = g->member[i].exp - old_exp; - - // Compute gained exp - if (guild_exp_rate != 100) - exp = exp*guild_exp_rate/100; - - // Update guild exp - if (exp > UINT_MAX - g->exp) - g->exp = UINT_MAX; - else - g->exp+=exp; - - guild_calcinfo(g); - mapif_guild_basicinfochanged(guild_id,GBI_EXP,&g->exp,4); - g->save_flag |= GS_LEVEL; - } - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - g->save_flag |= GS_MEMBER; - break; - } - case GMI_HAIR: - { - g->member[i].hair=*((int *)data); - g->member[i].modified = GS_MEMBER_MODIFIED; - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - g->save_flag |= GS_MEMBER; //Save new data. - break; - } - case GMI_HAIR_COLOR: - { - g->member[i].hair_color=*((int *)data); - g->member[i].modified = GS_MEMBER_MODIFIED; - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - g->save_flag |= GS_MEMBER; //Save new data. - break; - } - case GMI_GENDER: - { - g->member[i].gender=*((int *)data); - g->member[i].modified = GS_MEMBER_MODIFIED; - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - g->save_flag |= GS_MEMBER; //Save new data. - break; - } - case GMI_CLASS: - { - g->member[i].class_=*((int *)data); - g->member[i].modified = GS_MEMBER_MODIFIED; - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - g->save_flag |= GS_MEMBER; //Save new data. - break; - } - case GMI_LEVEL: - { - g->member[i].lv=*((int *)data); - g->member[i].modified = GS_MEMBER_MODIFIED; - mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); - g->save_flag |= GS_MEMBER; //Save new data. - break; - } - default: - ShowError("int_guild: GuildMemberInfoChange: Unknown type %d\n",type); - break; - } - return 0; -} - -int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender) -{ - return mapif_parse_GuildMemberInfoChange(0, guild_id, account_id, char_id, GMI_GENDER, (const char*)&gender, sizeof(gender)); -} - -// Change a position desc -int mapif_parse_GuildPosition(int fd,int guild_id,int idx,struct guild_position *p) -{ - // Could make some improvement in speed, because only change guild_position - struct guild * g; - - g = inter_guild_fromsql(guild_id); - if(g==NULL || idx<0 || idx>=MAX_GUILDPOSITION) - return 0; - - memcpy(&g->position[idx],p,sizeof(struct guild_position)); - mapif_guild_position(g,idx); - ShowInfo("int_guild: position data changed (Guild %d, position %d)\n",guild_id, idx); - g->position[idx].modified = GS_POSITION_MODIFIED; - g->save_flag |= GS_POSITION; // Change guild_position - return 0; -} - -// Guild Skill UP -int mapif_parse_GuildSkillUp(int fd,int guild_id,int skill_num,int account_id) -{ - struct guild * g; - int idx = skill_num - GD_SKILLBASE; - - g = inter_guild_fromsql(guild_id); - if(g == NULL || idx < 0 || idx >= MAX_GUILDSKILL) - return 0; - - if(g->skill_point>0 && g->skill[idx].id>0 && g->skill[idx].lv<10 ) - { - g->skill[idx].lv++; - g->skill_point--; - if (!guild_calcinfo(g)) - mapif_guild_info(-1,g); - mapif_guild_skillupack(guild_id,skill_num,account_id); - g->save_flag |= (GS_LEVEL|GS_SKILL); // Change guild & guild_skill - } - return 0; -} - -//Manual deletion of an alliance when partnering guild does not exists. [Skotlex] -static int mapif_parse_GuildDeleteAlliance(struct guild *g, int guild_id, int account_id1, int account_id2, int flag) -{ - int i; - char name[NAME_LENGTH]; - for(i=0;ialliance[i].guild_id == guild_id) - { - strcpy(name, g->alliance[i].name); - g->alliance[i].guild_id=0; - break; - } - if (i == MAX_GUILDALLIANCE) - return -1; - - mapif_guild_alliance(g->guild_id,guild_id,account_id1,account_id2,flag,g->name,name); - g->save_flag |= GS_ALLIANCE; - return 0; -} - -// Alliance modification -int mapif_parse_GuildAlliance(int fd,int guild_id1,int guild_id2, - int account_id1,int account_id2,int flag) -{ - // Could speed up - struct guild *g[2]; - int j,i; - g[0] = inter_guild_fromsql(guild_id1); - g[1] = inter_guild_fromsql(guild_id2); - - if(g[0] && g[1]==NULL && (flag & GUILD_ALLIANCE_REMOVE)) //Requested to remove an alliance with a not found guild. - return mapif_parse_GuildDeleteAlliance(g[0], guild_id2, account_id1, account_id2, flag); //Try to do a manual removal of said guild. - - if(g[0]==NULL || g[1]==NULL) - return 0; - - if(flag&GUILD_ALLIANCE_REMOVE) - { - // Remove alliance/opposition, in case of alliance, remove on both side - for(i=0;i<2-(flag&GUILD_ALLIANCE_TYPE_MASK);i++) - { - for(j=0;jalliance[j].guild_id == g[1-i]->guild_id && g[i]->alliance[j].opposition == (flag&GUILD_ALLIANCE_TYPE_MASK)) - { - g[i]->alliance[j].guild_id=0; - break; - } - } - } - } - else - { - // Add alliance, in case of alliance, add on both side - for(i=0;i<2-(flag&GUILD_ALLIANCE_TYPE_MASK);i++) - { - // Search an empty slot - for(j=0;jalliance[j].guild_id==0) - { - g[i]->alliance[j].guild_id=g[1-i]->guild_id; - memcpy(g[i]->alliance[j].name,g[1-i]->name,NAME_LENGTH-1); - // Set alliance type - g[i]->alliance[j].opposition = flag&GUILD_ALLIANCE_TYPE_MASK; - break; - } - } - } - } - - // Send on all map the new alliance/opposition - mapif_guild_alliance(guild_id1,guild_id2,account_id1,account_id2,flag,g[0]->name,g[1]->name); - - // Mark the two guild to be saved - g[0]->save_flag |= GS_ALLIANCE; - g[1]->save_flag |= GS_ALLIANCE; - return 0; -} - -// Change guild message -int mapif_parse_GuildNotice(int fd,int guild_id,const char *mes1,const char *mes2) -{ - struct guild *g; - - g = inter_guild_fromsql(guild_id); - if(g==NULL) - return 0; - - memcpy(g->mes1,mes1,60); - memcpy(g->mes2,mes2,120); - g->save_flag |= GS_MES; //Change mes of guild - return mapif_guild_notice(g); -} - -int mapif_parse_GuildEmblem(int fd,int len,int guild_id,int dummy,const char *data) -{ - struct guild * g; - - g = inter_guild_fromsql(guild_id); - if(g==NULL) - return 0; - - if (len > sizeof(g->emblem_data)) - len = sizeof(g->emblem_data); - - memcpy(g->emblem_data,data,len); - g->emblem_len=len; - g->emblem_id++; - g->save_flag |= GS_EMBLEM; //Change guild - return mapif_guild_emblem(g); -} - -int mapif_parse_GuildCastleDataLoad(int fd,int castle_id,int index) // -{ - struct guild_castle gc; - if (!inter_guildcastle_fromsql(castle_id, &gc)) { - return mapif_guild_castle_dataload(castle_id,0,0); - } - switch(index){ - case 1: return mapif_guild_castle_dataload(gc.castle_id,index,gc.guild_id); break; - case 2: return mapif_guild_castle_dataload(gc.castle_id,index,gc.economy); break; - case 3: return mapif_guild_castle_dataload(gc.castle_id,index,gc.defense); break; - case 4: return mapif_guild_castle_dataload(gc.castle_id,index,gc.triggerE); break; - case 5: return mapif_guild_castle_dataload(gc.castle_id,index,gc.triggerD); break; - case 6: return mapif_guild_castle_dataload(gc.castle_id,index,gc.nextTime); break; - case 7: return mapif_guild_castle_dataload(gc.castle_id,index,gc.payTime); break; - case 8: return mapif_guild_castle_dataload(gc.castle_id,index,gc.createTime); break; - case 9: return mapif_guild_castle_dataload(gc.castle_id,index,gc.visibleC); break; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - return mapif_guild_castle_dataload(gc.castle_id,index,gc.guardian[index-10].visible); break; - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - case 24: - case 25: - return mapif_guild_castle_dataload(gc.castle_id,index,gc.guardian[index-18].hp); break; - default: - ShowError("mapif_parse_GuildCastleDataLoad ERROR!! (Not found index=%d)\n", index); - return 0; - } -} - -int mapif_parse_GuildCastleDataSave(int fd,int castle_id,int index,int value) // -{ - struct guild_castle gc; - if(!inter_guildcastle_fromsql(castle_id, &gc)) - return mapif_guild_castle_datasave(castle_id,index,value); - - switch(index){ - case 1: - if( gc.guild_id!=value ){ - int gid=(value)?value:gc.guild_id; - struct guild *g=idb_get(guild_db_, gid); - if(log_inter) - inter_log("guild %s (id=%d) %s castle id=%d" RETCODE, - (g)?g->name:"??" ,gid, (value)?"occupy":"abandon", castle_id); - } - gc.guild_id = value; - if(gc.guild_id == 0) { - //Delete guardians. - memset(&gc.guardian, 0, sizeof(gc.guardian)); - } - break; - case 2: gc.economy = value; break; - case 3: gc.defense = value; break; - case 4: gc.triggerE = value; break; - case 5: gc.triggerD = value; break; - case 6: gc.nextTime = value; break; - case 7: gc.payTime = value; break; - case 8: gc.createTime = value; break; - case 9: gc.visibleC = value; break; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - gc.guardian[index-10].visible = value; break; - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - case 24: - case 25: - gc.guardian[index-18].hp = value; break; // end additions [Valaris] - default: - ShowError("mapif_parse_GuildCastleDataSave ERROR!! (Not found index=%d)\n", index); - return 0; - } - inter_guildcastle_tosql(&gc); - mapif_guild_castle_datasave(gc.castle_id,index,value); - return 0; -} - -int mapif_parse_GuildMasterChange(int fd, int guild_id, const char* name, int len) -{ - struct guild * g; - struct guild_member gm; - int pos; - - g = inter_guild_fromsql(guild_id); - - if(g==NULL || len > NAME_LENGTH) - return 0; - - // Find member (name) - for (pos = 0; pos < g->max_member && strncmp(g->member[pos].name, name, len); pos++); - - if (pos == g->max_member) - return 0; //Character not found?? - - // Switch current and old GM - memcpy(&gm, &g->member[pos], sizeof (struct guild_member)); - memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member)); - memcpy(&g->member[0], &gm, sizeof(struct guild_member)); - - // Switch positions - g->member[pos].position = g->member[0].position; - g->member[pos].modified = GS_MEMBER_MODIFIED; - g->member[0].position = 0; //Position 0: guild Master. - g->member[0].modified = GS_MEMBER_MODIFIED; - - strncpy(g->master, name, len); - if (len < NAME_LENGTH) - g->master[len] = '\0'; - - ShowInfo("int_guild: Guildmaster Changed to %s (Guild %d - %s)\n",g->master, guild_id, g->name); - g->save_flag |= (GS_BASIC|GS_MEMBER); //Save main data and member data. - return mapif_guild_master_changed(g, pos); -} - -// ギルドチェック要求 -int mapif_parse_GuildCheck(int fd,int guild_id,int account_id,int char_id) -{ - // What does this mean? Check if belong to another guild? - return 0; -} - -// map server からの通信 -// ・1パケットのみ解析すること -// ・パケット長データはinter.cにセットしておくこと -// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない -// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない -int inter_guild_parse_frommap(int fd) -{ - RFIFOHEAD(fd); - switch(RFIFOW(fd,0)){ - case 0x3030: mapif_parse_CreateGuild(fd,RFIFOL(fd,4),(char*)RFIFOP(fd,8),(struct guild_member *)RFIFOP(fd,32)); break; - case 0x3031: mapif_parse_GuildInfo(fd,RFIFOL(fd,2)); break; - case 0x3032: mapif_parse_GuildAddMember(fd,RFIFOL(fd,4),(struct guild_member *)RFIFOP(fd,8)); break; - case 0x3033: mapif_parse_GuildMasterChange(fd,RFIFOL(fd,4),(const char*)RFIFOP(fd,8),RFIFOW(fd,2)-8); break; - case 0x3034: mapif_parse_GuildLeave(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),(const char*)RFIFOP(fd,15)); break; - case 0x3035: mapif_parse_GuildChangeMemberInfoShort(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),RFIFOW(fd,15),RFIFOW(fd,17)); break; - case 0x3036: mapif_parse_BreakGuild(fd,RFIFOL(fd,2)); break; - case 0x3037: mapif_parse_GuildMessage(fd,RFIFOL(fd,4),RFIFOL(fd,8),(char*)RFIFOP(fd,12),RFIFOW(fd,2)-12); break; - case 0x3038: mapif_parse_GuildCheck(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; - case 0x3039: mapif_parse_GuildBasicInfoChange(fd,RFIFOL(fd,4),RFIFOW(fd,8),(const char*)RFIFOP(fd,10),RFIFOW(fd,2)-10); break; - case 0x303A: mapif_parse_GuildMemberInfoChange(fd,RFIFOL(fd,4),RFIFOL(fd,8),RFIFOL(fd,12),RFIFOW(fd,16),(const char*)RFIFOP(fd,18),RFIFOW(fd,2)-18); break; - case 0x303B: mapif_parse_GuildPosition(fd,RFIFOL(fd,4),RFIFOL(fd,8),(struct guild_position *)RFIFOP(fd,12)); break; - case 0x303C: mapif_parse_GuildSkillUp(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10)); break; - case 0x303D: mapif_parse_GuildAlliance(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOL(fd,14),RFIFOB(fd,18)); break; - case 0x303E: mapif_parse_GuildNotice(fd,RFIFOL(fd,2),(const char*)RFIFOP(fd,6),(const char*)RFIFOP(fd,66)); break; - case 0x303F: mapif_parse_GuildEmblem(fd,RFIFOW(fd,2)-12,RFIFOL(fd,4),RFIFOL(fd,8),(const char*)RFIFOP(fd,12)); break; - case 0x3040: mapif_parse_GuildCastleDataLoad(fd,RFIFOW(fd,2),RFIFOB(fd,4)); break; - case 0x3041: mapif_parse_GuildCastleDataSave(fd,RFIFOW(fd,2),RFIFOB(fd,4),RFIFOL(fd,5)); break; - - default: - return 0; - } - return 1; -} - -int inter_guild_mapif_init(int fd) -{ - return mapif_guild_castle_alldataload(fd); -} - -// サーバーから脱退要求(キャラ削除用) -int inter_guild_leave(int guild_id,int account_id,int char_id) -{ - return mapif_parse_GuildLeave(-1,guild_id,account_id,char_id,0,"** Character Deleted **"); -} - -int inter_guild_broken(int guild_id) -{ - return mapif_guild_broken(guild_id, 0); -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +// original code from athena +// SQL conversion by hack + +#include +#include +#include +#include + +#include "char.h" +#include "../common/strlib.h" +#include "../common/showmsg.h" +// #include "int_storage.h" +#include "inter.h" +#include "int_guild.h" +#include "mmo.h" +#include "socket.h" +#include "db.h" +#include "malloc.h" + +#define GS_MEMBER_UNMODIFIED 0x00 +#define GS_MEMBER_MODIFIED 0x01 +#define GS_MEMBER_NEW 0x02 + +#define GS_POSITION_UNMODIFIED 0x00 +#define GS_POSITION_MODIFIED 0x01 + +// LSB = 0 => Alliance, LSB = 1 => Opposition +#define GUILD_ALLIANCE_TYPE_MASK 0x01 +#define GUILD_ALLIANCE_REMOVE 0x08 + +static char dataToHex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + +#ifndef TXT_SQL_CONVERT +//Guild cache +static struct dbt *guild_db_; + +struct guild_castle castles[MAX_GUILDCASTLE]; + +static unsigned int guild_exp[100]; + +int mapif_parse_GuildLeave(int fd,int guild_id,int account_id,int char_id,int flag,const char *mes); +int mapif_guild_broken(int guild_id,int flag); +int guild_check_empty(struct guild *g); +int guild_calcinfo(struct guild *g); +int mapif_guild_basicinfochanged(int guild_id,int type,const void *data,int len); +int mapif_guild_info(int fd,struct guild *g); +int guild_break_sub(int key,void *data,va_list ap); +int inter_guild_tosql(struct guild *g,int flag); + +#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) + +static int guild_save(DBKey key, void *data, va_list ap) { + struct guild *g = (struct guild*) data; + int *last_id = va_arg(ap, int *); + int *state = va_arg(ap, int *); + + if ((*state) == 0 && g->guild_id == (*last_id)) + (*state)++; //Save next guild in the list. + else if (g->save_flag&GS_MASK && (*state) == 1) { + inter_guild_tosql(g, g->save_flag&GS_MASK); + g->save_flag &= ~GS_MASK; + + //Some guild saved. + (*last_id) = g->guild_id; + (*state)++; + } + + if(g->save_flag == GS_REMOVE) { //Nothing to save, guild is ready for removal. + if (save_log) + ShowInfo("Guild Unloaded (%d - %s)\n", g->guild_id, g->name); + db_remove(guild_db_, key); + } + return 0; +} + +static int guild_save_timer(int tid, unsigned int tick, int id, int data) { + static int last_id = 0; //To know in which guild we were. + int state = 0; //0: Have not reached last guild. 1: Reached last guild, ready for save. 2: Some guild saved, don't do further saving. + if (!last_id) //Save the first guild in the list. + state = 1; + guild_db_->foreach(guild_db_, guild_save, &last_id, &state); + if (state != 2) //Reached the end of the guild db without saving. + last_id = 0; //Reset guild saved, return to beginning. + + state = guild_db_->size(guild_db_); + if (state < 1) state = 1; //Calculate the time slot for the next save. + add_timer(tick + autosave_interval/state, guild_save_timer, 0, 0); + return 0; +} + +int inter_guild_removemember_tosql(int account_id, int char_id) +{ + sprintf(tmp_sql,"DELETE from `%s` where `account_id` = '%d' and `char_id` = '%d'", guild_member_db, account_id, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sprintf(tmp_sql,"UPDATE `%s` SET `guild_id` = '0' WHERE `char_id` = '%d'", char_db, char_id); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + return 0; +} +#endif //TXT_SQL_CONVERT +// Save guild into sql +int inter_guild_tosql(struct guild *g,int flag) +{ + // Table guild (GS_BASIC_MASK) + // GS_EMBLEM `emblem_len`,`emblem_id`,`emblem_data` + // GS_CONNECT `connect_member`,`average_lv` + // GS_MES `mes1`,`mes2` + // GS_LEVEL `guild_lv`,`max_member`,`exp`,`next_exp`,`skill_point` + // GS_BASIC `name`,`master`,`char_id` + + // GS_MEMBER `guild_member` (`guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`rsv1`,`rsv2`,`name`) + // GS_POSITION `guild_position` (`guild_id`,`position`,`name`,`mode`,`exp_mode`) + // GS_ALLIANCE `guild_alliance` (`guild_id`,`opposition`,`alliance_id`,`name`) + // GS_EXPULSION `guild_expulsion` (`guild_id`,`name`,`mes`,`acc`,`account_id`,`rsv1`,`rsv2`,`rsv3`) + // GS_SKILL `guild_skill` (`guild_id`,`id`,`lv`) + + // temporary storage for str convertion. They must be twice the size of the + // original string to ensure no overflows will occur. [Skotlex] + char t_name[NAME_LENGTH*2], + t_master[NAME_LENGTH*2], + t_mes1[120], + t_mes2[240], + t_member[NAME_LENGTH*2], + t_position[NAME_LENGTH*2], + t_alliance[NAME_LENGTH*2], + t_ename[NAME_LENGTH*2], + t_emes[80], + t_info[240]; + char emblem_data[4096]; + char new_guild = 0; + int i=0, sql_index; + + if (g->guild_id<=0 && g->guild_id != -1) return 0; + +#ifdef NOISY + ShowInfo("Save guild request ("CL_BOLD"%d"CL_RESET" - flag 0x%x).",g->guild_id, flag); +#endif + + jstrescapecpy(t_name, g->name); + + t_info[0]='\0'; + +#ifndef TXT_SQL_CONVERT + // Insert a new guild the guild + if (flag&GS_BASIC && g->guild_id == -1) + { + strcat(t_info, " guild_create"); + + // Create a new guild + sprintf(tmp_sql,"INSERT INTO `%s` " + "(`name`,`master`,`guild_lv`,`max_member`,`average_lv`,`char_id`) " + "VALUES ('%s', '%s', '%d', '%d', '%d', '%d')", + guild_db,t_name,jstrescapecpy(t_master,g->master),g->guild_lv,g->max_member,g->average_lv,g->member[0].char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + if (g->guild_id == -1) + return 0; //Failed to create guild! + } + else + { + //New guild, catch id + if(mysql_field_count(&mysql_handle) == 0 && mysql_insert_id(&mysql_handle) != 0) + { + g->guild_id = (int)mysql_insert_id(&mysql_handle); + new_guild = 1; + } + else + return 0; //Failed to get ID?? + } + } +#else + // Insert a new guild the guild + if (flag&GS_BASIC) + { + strcat(t_info, " guild_create"); + // Since the PK is guild id + master id, a replace will not be enough if we are overwriting data, we need to wipe the previous guild. + sprintf(tmp_sql,"DELETE FROM `%s` where `guild_id` = '%d'", guild_db,g->guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + // Create a new guild + sprintf(tmp_sql,"REPLACE INTO `%s` " + "(`guild_id`,`name`,`master`,`guild_lv`,`max_member`,`average_lv`,`char_id`) " + "VALUES ('%d', '%s', '%s', '%d', '%d', '%d', '%d')", + guild_db,g->guild_id,t_name,jstrescapecpy(t_master,g->master),g->guild_lv,g->max_member,g->average_lv,g->member[0].char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; //Failed to create guild. + } + } +#endif //TXT_SQL_CONVERT + // If we need an update on an existing guild or more update on the new guild + if (((flag & GS_BASIC_MASK) && !new_guild) || ((flag & (GS_BASIC_MASK & ~GS_BASIC)) && new_guild)) + { + sql_index = sprintf(tmp_sql,"UPDATE `%s` SET ", guild_db); + + if (flag & GS_EMBLEM) + { + char * pData = emblem_data; + strcat(t_info, " emblem"); + // Convert emblem_data to hex + for(i=0; iemblem_len; i++){ + *pData++ = dataToHex[(g->emblem_data[i] >> 4) & 0x0F]; + *pData++ = dataToHex[g->emblem_data[i] & 0x0F]; + } + *pData = 0; + sql_index += sprintf(tmp_sql + sql_index,"`emblem_len`=%d,`emblem_id`=%d,`emblem_data`='%s',",g->emblem_len,g->emblem_id,emblem_data); + } + if (flag & GS_BASIC) + { + strcat(t_info, " basic"); + sql_index += sprintf(tmp_sql + sql_index,"`name`='%s', `master`='%s', `char_id`=%d,",t_name,jstrescapecpy(t_master,g->master),g->member[0].char_id); + } + if (flag & GS_CONNECT) + { + strcat(t_info, " connect"); + sql_index += sprintf(tmp_sql + sql_index,"`connect_member`=%d,`average_lv`=%d,",g->connect_member, g->average_lv); + } + if (flag & GS_MES) + { + strcat(t_info, " mes"); + sql_index += sprintf(tmp_sql + sql_index,"`mes1`='%s',`mes2`='%s',",jstrescapecpy(t_mes1,g->mes1),jstrescapecpy(t_mes2,g->mes2)); + } + if (flag & GS_LEVEL) + { + strcat(t_info, " level"); + sql_index += sprintf(tmp_sql + sql_index,"`guild_lv`=%d,`skill_point`=%d,`exp`=%u,`next_exp`=%u,`max_member`=%d,",g->guild_lv, g->skill_point, g->exp, g->next_exp, g->max_member); + } + sprintf(tmp_sql + sql_index -1," WHERE `guild_id`=%d", g->guild_id); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + if (flag&GS_MEMBER) + { + struct guild_member *m; + strcat(t_info, " members"); + // Update only needed players + for(i=0;imax_member;i++){ + m = &g->member[i]; +#ifndef TXT_SQL_CONVERT + if (!m->modified) + continue; +#endif + if(m->account_id) { + //Since nothing references guild member table as foreign keys, it's safe to use REPLACE INTO + sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`name`) " + "VALUES ('%d','%d','%d','%d','%d','%d','%d','%d','%u','%d','%d','%d','%s')", + guild_member_db, g->guild_id, m->account_id,m->char_id, + m->hair,m->hair_color,m->gender, + m->class_,m->lv,m->exp,m->exp_payper,m->online,m->position, + jstrescapecpy(t_member,m->name)); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + if (m->modified & GS_MEMBER_NEW) + { + sprintf(tmp_sql,"UPDATE `%s` SET `guild_id` = '%d' WHERE `char_id` = '%d'", + char_db, g->guild_id, m->char_id); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + m->modified = GS_MEMBER_UNMODIFIED; + } + } + } + + if (flag&GS_POSITION){ + strcat(t_info, " positions"); + //printf("- Insert guild %d to guild_position\n",g->guild_id); + for(i=0;iposition[i]; +#ifndef TXT_SQL_CONVERT + if (!p->modified) + continue; +#endif + sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`position`,`name`,`mode`,`exp_mode`) VALUES ('%d','%d', '%s','%d','%d')", + guild_position_db, g->guild_id, i, jstrescapecpy(t_position,p->name),p->mode,p->exp_mode); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + p->modified = GS_POSITION_UNMODIFIED; + } + } + + if (flag&GS_ALLIANCE) + { + // Delete current alliances + // NOTE: no need to do it on both sides since both guilds in memory had + // their info changed, not to mention this would also mess up oppositions! + // [Skotlex] +// sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d' OR `alliance_id`='%d'",guild_alliance_db, g->guild_id,g->guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_alliance_db, g->guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + else + { + //printf("- Insert guild %d to guild_alliance\n",g->guild_id); + for(i=0;ialliance[i]; + if(a->guild_id>0) + { + sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`opposition`,`alliance_id`,`name`) " + "VALUES ('%d','%d','%d','%s')", + guild_alliance_db, g->guild_id,a->opposition,a->guild_id,jstrescapecpy(t_alliance,a->name)); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + } + } + } + + if (flag&GS_EXPULSION){ + strcat(t_info, " expulsions"); + //printf("- Insert guild %d to guild_expulsion\n",g->guild_id); + for(i=0;iexpulsion[i]; + if(e->account_id>0){ + sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`name`,`mes`,`acc`,`account_id`,`rsv1`,`rsv2`,`rsv3`) " + "VALUES ('%d','%s','%s','%s','%d','%d','%d','%d')", + guild_expulsion_db, g->guild_id, + jstrescapecpy(t_ename,e->name),jstrescapecpy(t_emes,e->mes),e->acc,e->account_id,e->rsv1,e->rsv2,e->rsv3 ); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + } + } + + if (flag&GS_SKILL){ + strcat(t_info, " skills"); + //printf("- Insert guild %d to guild_skill\n",g->guild_id); + for(i=0;iskill[i].id>0 && g->skill[i].lv>0){ + sprintf(tmp_sql,"REPLACE INTO `%s` (`guild_id`,`id`,`lv`) VALUES ('%d','%d','%d')", + guild_skill_db, g->guild_id,g->skill[i].id,g->skill[i].lv); + //printf("%s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + } + } + + if (save_log) + ShowInfo("Saved guild (%d - %s):%s\n",g->guild_id,g->name,t_info); + return 1; +} +#ifndef TXT_SQL_CONVERT +// Read guild from sql +struct guild * inter_guild_fromsql(int guild_id) +{ + int i; + char * pstr, * pEmblemData; + struct guild *g; + + if (guild_id<=0) return NULL; + + g = idb_get(guild_db_,guild_id); + if (g) return g; + + g = (struct guild*)aCalloc(sizeof(struct guild), 1); + +#ifdef NOISY + ShowInfo("Guild load request (%d)...\n", guild_id); +#endif + + sprintf(tmp_sql,"SELECT `name`,`master`,`guild_lv`,`connect_member`,`max_member`,`average_lv`,`exp`,`next_exp`,`skill_point`,`mes1`,`mes2`,`emblem_len`,`emblem_id`,`emblem_data` " + "FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(g); + return NULL; + } + + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res==NULL || mysql_num_rows(sql_res)<1) { + //Guild does not exists. + if (sql_res) mysql_free_result(sql_res); + aFree(g); + return NULL; + } + + sql_row = mysql_fetch_row(sql_res); + if (sql_row==NULL) { + mysql_free_result(sql_res); + aFree(g); + return NULL; + } + + g->guild_id=guild_id; + strncpy(g->name,sql_row[0],NAME_LENGTH-1); + strncpy(g->master,sql_row[1],NAME_LENGTH-1); + g->guild_lv=atoi(sql_row[2]); + g->connect_member=atoi(sql_row[3]); + g->max_member = atoi(sql_row[4]); + if (g->max_member > MAX_GUILD) + { // Fix reduction of MAX_GUILD [PoW] + ShowWarning("Guild %d:%s specifies higher capacity (%d) than MAX_GUILD (%d)\n", guild_id, g->name, g->max_member, MAX_GUILD); + g->max_member = MAX_GUILD; + } + g->average_lv=atoi(sql_row[5]); + g->exp=(unsigned int)atof(sql_row[6]); + g->next_exp=(unsigned int)atof(sql_row[7]); + g->skill_point=atoi(sql_row[8]); + //There shouldn't be a need to copy the very last char, as it's the \0 [Skotlex] + strncpy(g->mes1,sql_row[9],59); + strncpy(g->mes2,sql_row[10],119); + g->emblem_len=atoi(sql_row[11]); + g->emblem_id=atoi(sql_row[12]); + for(i=0,pstr=sql_row[13],pEmblemData=g->emblem_data; i < g->emblem_len; i++,pstr+=2){ + int c1=pstr[0],c2=pstr[1],x1=0,x2=0; + if(c1>='0' && c1<='9') + x1=c1-'0'; + else if(c1>='a' && c1<='f') + x1=c1-'a'+10; + else if(c1>='A' && c1<='F') + x1=c1-'A'+10; + if(c2>='0' && c2<='9') + x2=c2-'0'; + else if(c2>='a' && c2<='f') + x2=c2-'a'+10; + else if(c2>='A' && c2<='F') + x2=c2-'A'+10; + *pEmblemData++=(x1<<4)|x2; + } + mysql_free_result(sql_res); + + //printf("- Read guild_member %d from sql \n",guild_id); + sprintf(tmp_sql,"SELECT `guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`rsv1`,`rsv2`,`name` " + "FROM `%s` WHERE `guild_id`='%d' ORDER BY `position`", guild_member_db, guild_id); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(g); + return NULL; + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res) { + for(i=0;((sql_row = mysql_fetch_row(sql_res))&&imax_member);i++){ + struct guild_member *m = &g->member[i]; + m->account_id=atoi(sql_row[1]); + m->char_id=atoi(sql_row[2]); + m->hair=atoi(sql_row[3]); + m->hair_color=atoi(sql_row[4]); + m->gender=atoi(sql_row[5]); + m->class_=atoi(sql_row[6]); + m->lv=atoi(sql_row[7]); + m->exp=strtoul(sql_row[8],NULL,10); + m->exp_payper=atoi(sql_row[9]); + m->online=atoi(sql_row[10]); + m->position = atoi(sql_row[11]); + if (m->position >= MAX_GUILDPOSITION) // Fix reduction of MAX_GUILDPOSITION [PoW] + m->position = MAX_GUILDPOSITION - 1; + + strncpy(m->name,sql_row[14],NAME_LENGTH-1); + m->modified = GS_MEMBER_UNMODIFIED; + } + mysql_free_result(sql_res); + } + + //printf("- Read guild_position %d from sql \n",guild_id); + sprintf(tmp_sql,"SELECT `guild_id`,`position`,`name`,`mode`,`exp_mode` FROM `%s` WHERE `guild_id`='%d'",guild_position_db, guild_id); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(g); + return NULL; + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res) { + for(i=0;((sql_row = mysql_fetch_row(sql_res))&&iposition[position]; + strncpy(p->name,sql_row[2],NAME_LENGTH-1); + p->mode=atoi(sql_row[3]); + p->exp_mode=atoi(sql_row[4]); + p->modified = GS_POSITION_UNMODIFIED; + } + mysql_free_result(sql_res); + } + + //printf("- Read guild_alliance %d from sql \n",guild_id); + sprintf(tmp_sql,"SELECT `guild_id`,`opposition`,`alliance_id`,`name` FROM `%s` WHERE `guild_id`='%d'",guild_alliance_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(g); + return NULL; + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res) { + for(i=0;((sql_row = mysql_fetch_row(sql_res))&&ialliance[i]; + a->opposition=atoi(sql_row[1]); + a->guild_id=atoi(sql_row[2]); + strncpy(a->name,sql_row[3],NAME_LENGTH-1); + } + mysql_free_result(sql_res); + } + + //printf("- Read guild_expulsion %d from sql \n",guild_id); + sprintf(tmp_sql,"SELECT `guild_id`,`name`,`mes`,`acc`,`account_id`,`rsv1`,`rsv2`,`rsv3` FROM `%s` WHERE `guild_id`='%d'",guild_expulsion_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(g); + return NULL; + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res) { + for(i=0;((sql_row = mysql_fetch_row(sql_res))&&iexpulsion[i]; + + strncpy(e->name,sql_row[1],NAME_LENGTH-1); + //No need to copy char 40, the null terminator. [Skotlex] + strncpy(e->mes,sql_row[2],39); + strncpy(e->acc,sql_row[3],39); + e->account_id=atoi(sql_row[4]); + e->rsv1=atoi(sql_row[5]); + e->rsv2=atoi(sql_row[6]); + e->rsv3=atoi(sql_row[7]); + } + mysql_free_result(sql_res); + } + + //printf("- Read guild_skill %d from sql \n",guild_id); + sprintf(tmp_sql,"SELECT `guild_id`,`id`,`lv` FROM `%s` WHERE `guild_id`='%d' ORDER BY `id`",guild_skill_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(g); + return NULL; + } + + for(i = 0; i < MAX_GUILDSKILL; i++) + { //Skill IDs must always be initialized. [Skotlex] + g->skill[i].id = i + GD_SKILLBASE; + } + + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res) { + while ((sql_row = mysql_fetch_row(sql_res))){ + int id = atoi(sql_row[1])-GD_SKILLBASE; + if (id >= 0 && id < MAX_GUILDSKILL) + //I know this seems ridiculous, but the skills HAVE to be placed on their 'correct' array slot or things break x.x [Skotlex] + g->skill[id].lv=atoi(sql_row[2]); + } + mysql_free_result(sql_res); + } + + idb_put(guild_db_, guild_id, g); //Add to cache + g->save_flag |= GS_REMOVE; //But set it to be removed, in case it is not needed for long. + + if (save_log) + ShowInfo("Guild loaded (%d - %s)\n", guild_id, g->name); + + return g; +} + +#endif //TXT_SQL_CONVERT +int inter_guildcastle_tosql(struct guild_castle *gc){ + // `guild_castle` (`castle_id`, `guild_id`, `economy`, `defense`, `triggerE`, `triggerD`, `nextTime`, `payTime`, `createTime`, `visibleC`, `visibleG0`, `visibleG1`, `visibleG2`, `visibleG3`, `visibleG4`, `visibleG5`, `visibleG6`, `visibleG7`) + + if (gc==NULL) return 0; + #ifdef GUILD_DEBUG +ShowDebug("Save guild_castle (%d)\n", gc->castle_id); + #endif + +// sql_query("DELETE FROM `%s` WHERE `castle_id`='%d'",guild_castle_db, gc->castle_id); + + sprintf(tmp_sql,"REPLACE INTO `%s` " + "(`castle_id`, `guild_id`, `economy`, `defense`, `triggerE`, `triggerD`, `nextTime`, `payTime`, `createTime`," + "`visibleC`, `visibleG0`, `visibleG1`, `visibleG2`, `visibleG3`, `visibleG4`, `visibleG5`, `visibleG6`, `visibleG7`," + "`Ghp0`, `Ghp1`, `Ghp2`, `Ghp3`, `Ghp4`, `Ghp5`, `Ghp6`, `Ghp7`)" + "VALUES ('%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d')", + guild_castle_db, gc->castle_id, gc->guild_id, gc->economy, gc->defense, gc->triggerE, gc->triggerD, gc->nextTime, gc->payTime, + gc->createTime, gc->visibleC, + gc->guardian[0].visible, gc->guardian[1].visible, gc->guardian[2].visible, gc->guardian[3].visible, gc->guardian[4].visible, gc->guardian[5].visible, gc->guardian[6].visible, gc->guardian[7].visible, + gc->guardian[0].hp, gc->guardian[1].hp, gc->guardian[2].hp, gc->guardian[3].hp, gc->guardian[4].hp, gc->guardian[5].hp, gc->guardian[6].hp, gc->guardian[7].hp); + + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + +#ifndef TXT_SQL_CONVERT + memcpy(&castles[gc->castle_id],gc,sizeof(struct guild_castle)); +#endif //TXT_SQL_CONVERT + return 0; +} +#ifndef TXT_SQL_CONVERT + +// Read guild_castle from sql +int inter_guildcastle_fromsql(int castle_id,struct guild_castle *gc) +{ + static int castles_init=0; + if (gc==NULL) return 0; + if (castle_id==-1) return 0; + + if(!castles_init) + { + int i; + for(i=0;icastle_id=castle_id; + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res!=NULL && mysql_num_rows(sql_res)>0) { + sql_row = mysql_fetch_row(sql_res); + if (sql_row==NULL){ + mysql_free_result(sql_res); + return 1; //Assume empty castle. + } + gc->guild_id = atoi (sql_row[1]); + gc->economy = atoi (sql_row[2]); + gc->defense = atoi (sql_row[3]); + gc->triggerE = atoi (sql_row[4]); + gc->triggerD = atoi (sql_row[5]); + gc->nextTime = atoi (sql_row[6]); + gc->payTime = atoi (sql_row[7]); + gc->createTime = atoi (sql_row[8]); + gc->visibleC = atoi (sql_row[9]); + gc->guardian[0].visible = atoi (sql_row[10]); + gc->guardian[1].visible = atoi (sql_row[11]); + gc->guardian[2].visible = atoi (sql_row[12]); + gc->guardian[3].visible = atoi (sql_row[13]); + gc->guardian[4].visible = atoi (sql_row[14]); + gc->guardian[5].visible = atoi (sql_row[15]); + gc->guardian[6].visible = atoi (sql_row[16]); + gc->guardian[7].visible = atoi (sql_row[17]); + gc->guardian[0].hp = atoi (sql_row[18]); + gc->guardian[1].hp = atoi (sql_row[19]); + gc->guardian[2].hp = atoi (sql_row[20]); + gc->guardian[3].hp = atoi (sql_row[21]); + gc->guardian[4].hp = atoi (sql_row[22]); + gc->guardian[5].hp = atoi (sql_row[23]); + gc->guardian[6].hp = atoi (sql_row[24]); + gc->guardian[7].hp = atoi (sql_row[25]); + + if (save_log) + ShowInfo("Loaded Castle %d (guild %d)\n",castle_id,gc->guild_id); + + } + mysql_free_result(sql_res) ; //resource free + + memcpy(&castles[castle_id],gc,sizeof(struct guild_castle)); + + return 1; +} + + +// Read exp_guild.txt +int inter_guild_ReadEXP(void) +{ + int i; + FILE *fp; + char line[1024]; + for (i=0;i<100;i++) guild_exp[i]=0; + + sprintf(line, "%s/exp_guild.txt", db_path); + fp=fopen(line,"r"); + if(fp==NULL){ + ShowError("can't read %s\n", line); + return 1; + } + i=0; + while(fgets(line,256,fp) && i<100){ + if(line[0]=='/' && line[1]=='/') + continue; + guild_exp[i]=(unsigned int)atof(line); + i++; + } + fclose(fp); + + return 0; +} + + +int inter_guild_CharOnline(int char_id, int guild_id) { + + struct guild *g; + int i; + + if (guild_id == -1) { + //Get guild_id from the database + sprintf (tmp_sql , "SELECT guild_id FROM `%s` WHERE char_id='%d'",char_db,char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + + sql_res = mysql_store_result(&mysql_handle) ; + if(sql_res == NULL) + return 0; //Eh? No guild? + + sql_row = mysql_fetch_row(sql_res); + guild_id = sql_row?atoi(sql_row[0]):0; + mysql_free_result(sql_res); + } + if (guild_id == 0) + return 0; //No guild... + + g = inter_guild_fromsql(guild_id); + if(!g) { + ShowError("Character %d's guild %d not found!\n", char_id, guild_id); + return 0; + } + + //Member has logged in before saving, tell saver not to delete + if(g->save_flag & GS_REMOVE) + g->save_flag &= ~GS_REMOVE; + + //Set member online + for(i=0; imax_member; i++) { + if (g->member[i].char_id == char_id) { + g->member[i].online = 1; + g->member[i].modified = GS_MEMBER_MODIFIED; + break; + } + } + return 1; +} + +int inter_guild_CharOffline(int char_id, int guild_id) { + struct guild *g=NULL; + int online_count=0, i; + + if (guild_id == -1) { + //Get guild_id from the database + sprintf (tmp_sql , "SELECT guild_id FROM `%s` WHERE char_id='%d'",char_db,char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + + sql_res = mysql_store_result(&mysql_handle) ; + if(sql_res == NULL) + return 0; //Eh? No guild? + + sql_row = mysql_fetch_row(sql_res); + guild_id = sql_row?atoi(sql_row[0]):0; + mysql_free_result(sql_res); + } + if (guild_id == 0) + return 0; //No guild... + + //Character has a guild, set character offline and check if they were the only member online + g = inter_guild_fromsql(guild_id); + if (g == NULL) //Guild not found? + return 0; + + //Set member offline + for(i=0; imax_member; i++) { + if(g->member[i].char_id == char_id) + { + g->member[i].online = 0; + g->member[i].modified = GS_MEMBER_MODIFIED; + } + if(g->member[i].online && !online_count) + online_count++; + } + + // Remove guild from memory if no players online + if(online_count == 0) + g->save_flag |= GS_REMOVE; + + return 1; +} + +// Initialize guild sql +int inter_guild_sql_init(void) +{ + //Initialize the guild cache + guild_db_= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + //Read exp file + inter_guild_ReadEXP(); + + add_timer_func_list(guild_save_timer, "guild_save_timer"); + add_timer(gettick() + 10000, guild_save_timer, 0, 0); + return 0; +} + +static int guild_db_final(DBKey key, void *data, va_list ap) +{ + struct guild *g = (struct guild*)data; + if (g->save_flag&GS_MASK) { + inter_guild_tosql(g, g->save_flag&GS_MASK); + return 1; + } + return 0; +} + +void inter_guild_sql_final(void) +{ + guild_db_->destroy(guild_db_, guild_db_final); + return; +} + +// Get guild_id by its name. Returns 0 if not found, -1 on error. +int search_guildname(char *str) +{ + int guild_id; + char t_name[NAME_LENGTH*2]; + + jstrescapecpy(t_name, str); + //Lookup guilds with the same name + sprintf (tmp_sql , "SELECT guild_id FROM `%s` WHERE name='%s'", guild_db, t_name); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return -1; + } + + sql_res = mysql_store_result(&mysql_handle) ; + if(sql_res) + sql_row = mysql_fetch_row(sql_res); + + guild_id = (sql_row&&sql_res&&sql_row[0])?atoi(sql_row[0]):0; + mysql_free_result(sql_res); + return guild_id; +} + +// Check if guild is empty +int guild_check_empty(struct guild *g) +{ + int i; + for(i=0;imax_member;i++){ + if(g->member[i].account_id>0){ + return 0; + } + } + //Let the calling function handle the guild removal in case they need + //to do something else with it before freeing the data. [Skotlex] + return 1; +} + +unsigned int guild_nextexp(int level) +{ + if (level == 0) + return 1; + if (level < 100 && level > 0) // Change by hack + return guild_exp[level-1]; + + return 0; +} + +int guild_checkskill(struct guild *g,int id) { + + int idx = id - GD_SKILLBASE; + + if(idx < 0 || idx >= MAX_GUILDSKILL) + return 0; + + return g->skill[idx].lv; +} + +int guild_calcinfo(struct guild *g) +{ + int i,c; + unsigned int nextexp; + struct guild before = *g; // Save guild current values + + if(g->guild_lv<=0) + g->guild_lv = 1; + nextexp = guild_nextexp(g->guild_lv); + + // Consume guild exp and increase guild level + while(g->exp >= nextexp && nextexp > 0){ //fixed guild exp overflow [Kevin] + g->exp-=nextexp; + g->guild_lv++; + g->skill_point++; + nextexp = guild_nextexp(g->guild_lv); + } + + // Save next exp step + g->next_exp = nextexp; + + // Set the max number of members, Guild Extention skill - currently adds 6 to max per skill lv. + g->max_member = 16 + guild_checkskill(g, GD_EXTENSION) * 6; + if(g->max_member > MAX_GUILD) + { + ShowError("Guild %d:%s has capacity for too many guild members (%d), max supported is %d\n", g->guild_id, g->name, g->max_member, MAX_GUILD); + g->max_member = MAX_GUILD; + } + + // Compute the guild average level level + g->average_lv=0; + g->connect_member=0; + for(i=c=0;imax_member;i++) + { + if(g->member[i].account_id>0) + { + if (g->member[i].lv >= 0) + { + g->average_lv+=g->member[i].lv; + c++; + } + else + { + ShowWarning("Guild %d:%s, member %d:%s has an invalid level %d\n", g->guild_id, g->name, g->member[i].char_id, g->member[i].name, g->member[i].lv); + } + + if(g->member[i].online) + g->connect_member++; + } + } + if(c) + g->average_lv /= c; + + // Check if guild stats has change + if(g->max_member != before.max_member || g->guild_lv != before.guild_lv || g->skill_point != before.skill_point ) + { + g->save_flag |= GS_LEVEL; + mapif_guild_info(-1,g); + return 1; + } + + return 0; +} + +//------------------------------------------------------------------- +// Packet sent to map server + +int mapif_guild_created(int fd,int account_id,struct guild *g) +{ + WFIFOHEAD(fd, 10); + WFIFOW(fd,0)=0x3830; + WFIFOL(fd,2)=account_id; + if(g != NULL) + { + WFIFOL(fd,6)=g->guild_id; + ShowInfo("int_guild: Guild created (%d - %s)\n",g->guild_id,g->name); + } else + WFIFOL(fd,6)=0; + + WFIFOSET(fd,10); + return 0; +} +// Guild not found +int mapif_guild_noinfo(int fd,int guild_id) +{ + unsigned char buf[12]; + WBUFW(buf,0)=0x3831; + WBUFW(buf,2)=8; + WBUFL(buf,4)=guild_id; + ShowWarning("int_guild: info not found %d\n",guild_id); + if(fd<0) + mapif_sendall(buf,8); + else + mapif_send(fd,buf,8); + return 0; +} + +// Send guild info +int mapif_guild_info(int fd,struct guild *g) +{ + unsigned char buf[8+sizeof(struct guild)]; + WBUFW(buf,0)=0x3831; + WBUFW(buf,2)=4+sizeof(struct guild); + memcpy(buf+4,g,sizeof(struct guild)); + if(fd<0) + mapif_sendall(buf,WBUFW(buf,2)); + else + mapif_send(fd,buf,WBUFW(buf,2)); + return 0; +} + +// ACK member add +int mapif_guild_memberadded(int fd,int guild_id,int account_id,int char_id,int flag) +{ + WFIFOHEAD(fd, 15); + WFIFOW(fd,0)=0x3832; + WFIFOL(fd,2)=guild_id; + WFIFOL(fd,6)=account_id; + WFIFOL(fd,10)=char_id; + WFIFOB(fd,14)=flag; + WFIFOSET(fd,15); + return 0; +} + +// ACK member leave +int mapif_guild_leaved(int guild_id,int account_id,int char_id,int flag, const char *name, const char *mes) +{ + unsigned char buf[55+NAME_LENGTH]; + WBUFW(buf, 0)=0x3834; + WBUFL(buf, 2)=guild_id; + WBUFL(buf, 6)=account_id; + WBUFL(buf,10)=char_id; + WBUFB(buf,14)=flag; + memcpy(WBUFP(buf,15),mes,40); + memcpy(WBUFP(buf,55),name,NAME_LENGTH); + mapif_sendall(buf,55+NAME_LENGTH); + ShowInfo("int_guild: guild leaved (%d - %d: %s - %s)\n",guild_id,account_id,name,mes); + return 0; +} + +// Send short member's info +int mapif_guild_memberinfoshort(struct guild *g,int idx) +{ + unsigned char buf[19]; + WBUFW(buf, 0)=0x3835; + WBUFL(buf, 2)=g->guild_id; + WBUFL(buf, 6)=g->member[idx].account_id; + WBUFL(buf,10)=g->member[idx].char_id; + WBUFB(buf,14)=(unsigned char)g->member[idx].online; + WBUFW(buf,15)=g->member[idx].lv; + WBUFW(buf,17)=g->member[idx].class_; + mapif_sendall(buf,19); + return 0; +} + +// Send guild broken +int mapif_guild_broken(int guild_id,int flag) +{ + unsigned char buf[7]; + WBUFW(buf,0)=0x3836; + WBUFL(buf,2)=guild_id; + WBUFB(buf,6)=flag; + mapif_sendall(buf,7); + ShowInfo("int_guild: Guild broken (%d)\n",guild_id); + return 0; +} + +// Send guild message +int mapif_guild_message(int guild_id,int account_id,char *mes,int len, int sfd) +{ + unsigned char buf[512]; + if (len > 500) + len = 500; + WBUFW(buf,0)=0x3837; + WBUFW(buf,2)=len+12; + WBUFL(buf,4)=guild_id; + WBUFL(buf,8)=account_id; + memcpy(WBUFP(buf,12),mes,len); + mapif_sendallwos(sfd, buf,len+12); + return 0; +} + +// Send basic info +int mapif_guild_basicinfochanged(int guild_id,int type,const void *data,int len) +{ + unsigned char buf[2048]; + if (len > 2038) + len = 2038; + WBUFW(buf, 0)=0x3839; + WBUFW(buf, 2)=len+10; + WBUFL(buf, 4)=guild_id; + WBUFW(buf, 8)=type; + memcpy(WBUFP(buf,10),data,len); + mapif_sendall(buf,len+10); + return 0; +} + +// Send member info +int mapif_guild_memberinfochanged(int guild_id,int account_id,int char_id, int type,const void *data,int len) +{ + unsigned char buf[2048]; + if (len > 2030) + len = 2030; + WBUFW(buf, 0)=0x383a; + WBUFW(buf, 2)=len+18; + WBUFL(buf, 4)=guild_id; + WBUFL(buf, 8)=account_id; + WBUFL(buf,12)=char_id; + WBUFW(buf,16)=type; + memcpy(WBUFP(buf,18),data,len); + mapif_sendall(buf,len+18); + return 0; +} + +// ACK guild skill up +int mapif_guild_skillupack(int guild_id,int skill_num,int account_id) +{ + unsigned char buf[14]; + WBUFW(buf, 0)=0x383c; + WBUFL(buf, 2)=guild_id; + WBUFL(buf, 6)=skill_num; + WBUFL(buf,10)=account_id; + mapif_sendall(buf,14); + return 0; +} + +// ACK guild alliance +int mapif_guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2, + int flag,const char *name1,const char *name2) +{ + unsigned char buf[19+2*NAME_LENGTH]; + WBUFW(buf, 0)=0x383d; + WBUFL(buf, 2)=guild_id1; + WBUFL(buf, 6)=guild_id2; + WBUFL(buf,10)=account_id1; + WBUFL(buf,14)=account_id2; + WBUFB(buf,18)=flag; + memcpy(WBUFP(buf,19),name1,NAME_LENGTH); + memcpy(WBUFP(buf,19+NAME_LENGTH),name2,NAME_LENGTH); + mapif_sendall(buf,19+2*NAME_LENGTH); + return 0; +} + +// Send a guild position desc +int mapif_guild_position(struct guild *g,int idx) +{ + unsigned char buf[12 + sizeof(struct guild_position)]; + WBUFW(buf,0)=0x383b; + WBUFW(buf,2)=sizeof(struct guild_position)+12; + WBUFL(buf,4)=g->guild_id; + WBUFL(buf,8)=idx; + memcpy(WBUFP(buf,12),&g->position[idx],sizeof(struct guild_position)); + mapif_sendall(buf,WBUFW(buf,2)); + return 0; +} + +// Send the guild notice +int mapif_guild_notice(struct guild *g) +{ + unsigned char buf[256]; + WBUFW(buf,0)=0x383e; + WBUFL(buf,2)=g->guild_id; + memcpy(WBUFP(buf,6),g->mes1,60); + memcpy(WBUFP(buf,66),g->mes2,120); + mapif_sendall(buf,186); + return 0; +} + +// Send emblem data +int mapif_guild_emblem(struct guild *g) +{ + unsigned char buf[12 + sizeof(g->emblem_data)]; + WBUFW(buf,0)=0x383f; + WBUFW(buf,2)=g->emblem_len+12; + WBUFL(buf,4)=g->guild_id; + WBUFL(buf,8)=g->emblem_id; + memcpy(WBUFP(buf,12),g->emblem_data,g->emblem_len); + mapif_sendall(buf,WBUFW(buf,2)); + return 0; +} + +int mapif_guild_master_changed(struct guild *g, int position) +{ + unsigned char buf[10]; + WBUFW(buf,0)=0x3843; + WBUFL(buf,2)=g->guild_id; + WBUFL(buf,6)=position; + mapif_sendall(buf,10); + return 0; +} + +int mapif_guild_castle_dataload(int castle_id,int index,int value) // +{ + unsigned char buf[9]; + WBUFW(buf, 0)=0x3840; + WBUFW(buf, 2)=castle_id; + WBUFB(buf, 4)=index; + WBUFL(buf, 5)=value; + mapif_sendall(buf,9); + return 0; +} + +int mapif_guild_castle_datasave(int castle_id,int index,int value) // +{ + unsigned char buf[9]; + WBUFW(buf, 0)=0x3841; + WBUFW(buf, 2)=castle_id; + WBUFB(buf, 4)=index; + WBUFL(buf, 5)=value; + mapif_sendall(buf,9); + return 0; +} + +int mapif_guild_castle_alldataload(int fd) { + struct guild_castle* gc = (struct guild_castle *)aMalloc(sizeof(struct guild_castle)); + int i, len = 4; + WFIFOHEAD(fd, len + MAX_GUILDCASTLE*sizeof(struct guild_castle)); + WFIFOW(fd,0) = 0x3842; + sprintf(tmp_sql,"SELECT * FROM `%s` ORDER BY `castle_id`", guild_castle_db); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + for(i = 0; ((sql_row = mysql_fetch_row(sql_res)) && i < MAX_GUILDCASTLE); i++) { + memset(gc, 0, sizeof(struct guild_castle)); + gc->castle_id = atoi(sql_row[0]); + gc->guild_id = atoi(sql_row[1]); + gc->economy = atoi(sql_row[2]); + gc->defense = atoi(sql_row[3]); + gc->triggerE = atoi(sql_row[4]); + gc->triggerD = atoi(sql_row[5]); + gc->nextTime = atoi(sql_row[6]); + gc->payTime = atoi(sql_row[7]); + gc->createTime = atoi(sql_row[8]); + gc->visibleC = atoi(sql_row[9]); + gc->guardian[0].visible = atoi(sql_row[10]); + gc->guardian[1].visible = atoi(sql_row[11]); + gc->guardian[2].visible = atoi(sql_row[12]); + gc->guardian[3].visible = atoi(sql_row[13]); + gc->guardian[4].visible = atoi(sql_row[14]); + gc->guardian[5].visible = atoi(sql_row[15]); + gc->guardian[6].visible = atoi(sql_row[16]); + gc->guardian[7].visible = atoi(sql_row[17]); + gc->guardian[0].visible = atoi(sql_row[18]); + gc->guardian[1].visible = atoi(sql_row[19]); + gc->guardian[2].visible = atoi(sql_row[20]); + gc->guardian[3].visible = atoi(sql_row[21]); + gc->guardian[4].visible = atoi(sql_row[22]); + gc->guardian[5].visible = atoi(sql_row[23]); + gc->guardian[6].visible = atoi(sql_row[24]); + gc->guardian[7].visible = atoi(sql_row[25]); + memcpy(WFIFOP(fd,len), gc, sizeof(struct guild_castle)); + len += sizeof(struct guild_castle); + } + mysql_free_result(sql_res); + } + WFIFOW(fd,2) = len; + WFIFOSET(fd,len); + + aFree(gc); + + return 0; +} + + +//------------------------------------------------------------------- +// Packet received from map server + + +// ギルド作成要求 +int mapif_parse_CreateGuild(int fd,int account_id,char *name,struct guild_member *master) +{ + struct guild *g; + int i=0; +#ifdef NOISY + ShowInfo("Creating Guild (%s)\n", name); +#endif + if(search_guildname(name) != 0){ + ShowInfo("int_guild: guild with same name exists [%s]\n",name); + mapif_guild_created(fd,account_id,NULL); + return 0; + } + // Check Authorised letters/symbols in the name of the character + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) == NULL) { + mapif_guild_created(fd,account_id,NULL); + return 0; + } + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) != NULL) { + mapif_guild_created(fd,account_id,NULL); + return 0; + } + } + + g = (struct guild *)aMalloc(sizeof(struct guild)); + memset(g,0,sizeof(struct guild)); + + memcpy(g->name,name,NAME_LENGTH); + memcpy(g->master,master->name,NAME_LENGTH); + memcpy(&g->member[0],master,sizeof(struct guild_member)); + g->member[0].modified = GS_MEMBER_MODIFIED; + + // Set default positions + g->position[0].mode=0x11; + strcpy(g->position[0].name,"GuildMaster"); + strcpy(g->position[MAX_GUILDPOSITION-1].name,"Newbie"); + for(i=1;iposition[i].name,"Position %d",i+1); + + // Initialize guild property + g->max_member=16; + g->average_lv=master->lv; + for(i=0;iskill[i].id=i + GD_SKILLBASE; + g->guild_id= -1; //Request to create guild. + + // Create the guild + if (!inter_guild_tosql(g,GS_BASIC|GS_POSITION|GS_SKILL)) { + //Failed to Create guild.... + ShowError("Failed to create Guild %s (Guild Master: %s)\n", g->name, g->master); + mapif_guild_created(fd,account_id,NULL); + aFree(g); + return 0; + } + ShowInfo("Created Guild %d - %s (Guild Master: %s)\n", g->guild_id, g->name, g->master); + + //Add to cache + idb_put(guild_db_, g->guild_id, g); + + // Report to client + mapif_guild_created(fd,account_id,g); + mapif_guild_info(fd,g); + + if(log_inter) + inter_log("guild %s (id=%d) created by master %s (id=%d)" RETCODE, + name, g->guild_id, master->name, master->account_id ); + + return 0; +} + +// Return guild info to client +int mapif_parse_GuildInfo(int fd,int guild_id) +{ + struct guild * g = inter_guild_fromsql(guild_id); //We use this because on start-up the info of castle-owned guilds is requied. [Skotlex] + if(g) + { + if (!guild_calcinfo(g)) + mapif_guild_info(fd,g); + } + else + mapif_guild_noinfo(fd,guild_id); // Failed to load info + return 0; +} + +// Add member to guild +int mapif_parse_GuildAddMember(int fd,int guild_id,struct guild_member *m) +{ + struct guild * g; + int i; + + g = inter_guild_fromsql(guild_id); + if(g==NULL){ + // Failed to add + mapif_guild_memberadded(fd,guild_id,m->account_id,m->char_id,1); + return 0; + } + + // Find an empty slot + for(i=0;imax_member;i++) + { + if(g->member[i].account_id==0) + { + memcpy(&g->member[i],m,sizeof(struct guild_member)); + g->member[i].modified = (GS_MEMBER_NEW | GS_MEMBER_MODIFIED); + mapif_guild_memberadded(fd,guild_id,m->account_id,m->char_id,0); + if (!guild_calcinfo(g)) //Send members if it was not invoked. + mapif_guild_info(fd,g); + + g->save_flag |= GS_MEMBER; + if (g->save_flag&GS_REMOVE) + g->save_flag&=~GS_REMOVE; + return 0; + } + } + + // Failed to add + mapif_guild_memberadded(fd,guild_id,m->account_id,m->char_id,1); + return 0; +} + +// Delete member from guild +int mapif_parse_GuildLeave(int fd,int guild_id,int account_id,int char_id,int flag,const char *mes) +{ + struct guild * g = inter_guild_fromsql(guild_id); + + if(g){ + int i; + // Find the member + for(i=0;imax_member;i++){ + if( g->member[i].account_id==account_id && g->member[i].char_id==char_id) + { + if(flag) + { + // Write expulsion reason + int j; + // Find an empty slot + for(j=0;jexpulsion[j].account_id==0) + break; + } + // Expulsion list is full, flush the oldest one + if(j==MAX_GUILDEXPULSION){ + for(j=0;jexpulsion[j]=g->expulsion[j+1]; + j=MAX_GUILDEXPULSION-1; + } + // Save the expulsion + g->expulsion[j].account_id=account_id; + memcpy(g->expulsion[j].acc,"dummy",NAME_LENGTH-1); + memcpy(g->expulsion[j].name,g->member[i].name,NAME_LENGTH-1); + memcpy(g->expulsion[j].mes,mes,40); + } + + mapif_guild_leaved(guild_id,account_id,char_id,flag,g->member[i].name,mes); + inter_guild_removemember_tosql(g->member[i].account_id,g->member[i].char_id); + + memset(&g->member[i],0,sizeof(struct guild_member)); + + if(!guild_check_empty(g)) { + break; + } + //Guild empty? break it. + mapif_parse_BreakGuild(-1,guild_id); //Break the guild. + return 0; + } + } + //Update member info. + if (!guild_calcinfo(g)) + mapif_guild_info(fd,g); + g->save_flag |= GS_EXPULSION; + }else{ + // Unknown guild, just update the player + sprintf(tmp_sql, "UPDATE `%s` SET `guild_id`='0' WHERE `account_id`='%d' AND `char_id`='%d'",char_db, account_id,char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + /* mapif_guild_leaved(guild_id,account_id,char_id,flag,g->member[i].name,mes); */ + } + + return 0; +} + +// Change member info +int mapif_parse_GuildChangeMemberInfoShort(int fd,int guild_id, + int account_id,int char_id,int online,int lv,int class_) +{ + // Could speed up by manipulating only guild_member + struct guild * g; + int i,alv,c; + int prev_count; + + g = inter_guild_fromsql(guild_id); + if(g==NULL) + return 0; + + prev_count = g->connect_member; + g->connect_member=0; + + for(i=0,alv=0,c=0;imax_member;i++) + { + // Found the member + if(g->member[i].account_id==account_id && g->member[i].char_id==char_id) + { + g->member[i].online=online; + g->member[i].lv=lv; + g->member[i].class_=class_; + g->member[i].modified = GS_MEMBER_MODIFIED; + mapif_guild_memberinfoshort(g,i); + } + if( g->member[i].account_id>0 ) + { + if (g->member[i].lv > 0) + { + alv+=g->member[i].lv; + c++; + } + else + { + ShowWarning("Guild %d:%s, member %d:%s has an invalid level %d\n", g->guild_id, g->name, g->member[i].char_id, g->member[i].name, g->member[i].lv); + } + } + if( g->member[i].online ) + g->connect_member++; + } + + if (c) + { + alv = alv/c; + if (g->connect_member != prev_count || g->average_lv != alv) + { + g->average_lv=alv; + g->save_flag |= GS_CONNECT; + } + if (g->save_flag & GS_REMOVE) + g->save_flag &= ~GS_REMOVE; + } + g->save_flag |= GS_MEMBER; //Update guild member data + return 0; +} + +// BreakGuild +int mapif_parse_BreakGuild(int fd,int guild_id) +{ + struct guild * g; + + g = inter_guild_fromsql(guild_id); + if(g==NULL) + return 0; + + // Delete guild from sql + //printf("- Delete guild %d from guild\n",guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_member_db, guild_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_castle_db, guild_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_storage_db, guild_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d' OR `alliance_id` = '%d'", guild_alliance_db, guild_id, guild_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_position_db, guild_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_skill_db, guild_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_expulsion_db, guild_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + //printf("- Update guild %d of char\n",guild_id); + sprintf(tmp_sql, "UPDATE `%s` SET `guild_id`='0' WHERE `guild_id`='%d'",char_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + mapif_guild_broken(guild_id,0); + + if(log_inter) + inter_log("guild %s (id=%d) broken" RETCODE,g->name,guild_id); + + //Remove the guild from memory. [Skotlex] + idb_remove(guild_db_, guild_id); + return 0; +} + +// Forward Guild message to others map servers +int mapif_parse_GuildMessage(int fd,int guild_id,int account_id,char *mes,int len) +{ + return mapif_guild_message(guild_id,account_id,mes,len, fd); +} + +// Modification of the guild +int mapif_parse_GuildBasicInfoChange(int fd,int guild_id,int type,const char *data,int len) +{ + struct guild * g; + short dw=*((short *)data); + g = inter_guild_fromsql(guild_id); + if(g==NULL) + return 0; + + switch(type) + { + case GBI_GUILDLV: + ShowDebug("GBI_GUILDLV\n"); + if(dw>0 && g->guild_lv+dw<=50) + { + g->guild_lv+=dw; + g->skill_point+=dw; + } + else if(dw<0 && g->guild_lv+dw>=1) + g->guild_lv+=dw; + mapif_guild_info(-1,g); + g->save_flag |= GS_LEVEL; + return 0; + default: + ShowError("int_guild: GuildBasicInfoChange: Unknown type %d\n",type); + break; + } + mapif_guild_basicinfochanged(guild_id,type,data,len); + return 0; +} + +// Modification of the guild +int mapif_parse_GuildMemberInfoChange(int fd,int guild_id,int account_id,int char_id, + int type,const char *data,int len) +{ + // Could make some improvement in speed, because only change guild_member + int i; + struct guild * g; + + g = inter_guild_fromsql(guild_id); + if(g==NULL) + return 0; + + // Search the member + for(i=0;imax_member;i++) + if( g->member[i].account_id==account_id && + g->member[i].char_id==char_id ) + break; + + // Not Found + if(i==g->max_member){ + ShowWarning("int_guild: GuildMemberChange: Not found %d,%d in guild (%d - %s)\n", + account_id,char_id,guild_id,g->name); + return 0; + } + + switch(type) + { + case GMI_POSITION: + { + g->member[i].position=*((int *)data); + g->member[i].modified = GS_MEMBER_MODIFIED; + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + g->save_flag |= GS_MEMBER; + break; + } + case GMI_EXP: + { // EXP + unsigned int exp, old_exp=g->member[i].exp; + g->member[i].exp=*((unsigned int *)data); + g->member[i].modified = GS_MEMBER_MODIFIED; + if (g->member[i].exp > old_exp) + { + exp = g->member[i].exp - old_exp; + + // Compute gained exp + if (guild_exp_rate != 100) + exp = exp*guild_exp_rate/100; + + // Update guild exp + if (exp > UINT_MAX - g->exp) + g->exp = UINT_MAX; + else + g->exp+=exp; + + guild_calcinfo(g); + mapif_guild_basicinfochanged(guild_id,GBI_EXP,&g->exp,4); + g->save_flag |= GS_LEVEL; + } + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + g->save_flag |= GS_MEMBER; + break; + } + case GMI_HAIR: + { + g->member[i].hair=*((int *)data); + g->member[i].modified = GS_MEMBER_MODIFIED; + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + g->save_flag |= GS_MEMBER; //Save new data. + break; + } + case GMI_HAIR_COLOR: + { + g->member[i].hair_color=*((int *)data); + g->member[i].modified = GS_MEMBER_MODIFIED; + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + g->save_flag |= GS_MEMBER; //Save new data. + break; + } + case GMI_GENDER: + { + g->member[i].gender=*((int *)data); + g->member[i].modified = GS_MEMBER_MODIFIED; + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + g->save_flag |= GS_MEMBER; //Save new data. + break; + } + case GMI_CLASS: + { + g->member[i].class_=*((int *)data); + g->member[i].modified = GS_MEMBER_MODIFIED; + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + g->save_flag |= GS_MEMBER; //Save new data. + break; + } + case GMI_LEVEL: + { + g->member[i].lv=*((int *)data); + g->member[i].modified = GS_MEMBER_MODIFIED; + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + g->save_flag |= GS_MEMBER; //Save new data. + break; + } + default: + ShowError("int_guild: GuildMemberInfoChange: Unknown type %d\n",type); + break; + } + return 0; +} + +int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender) +{ + return mapif_parse_GuildMemberInfoChange(0, guild_id, account_id, char_id, GMI_GENDER, (const char*)&gender, sizeof(gender)); +} + +// Change a position desc +int mapif_parse_GuildPosition(int fd,int guild_id,int idx,struct guild_position *p) +{ + // Could make some improvement in speed, because only change guild_position + struct guild * g; + + g = inter_guild_fromsql(guild_id); + if(g==NULL || idx<0 || idx>=MAX_GUILDPOSITION) + return 0; + + memcpy(&g->position[idx],p,sizeof(struct guild_position)); + mapif_guild_position(g,idx); + ShowInfo("int_guild: position data changed (Guild %d, position %d)\n",guild_id, idx); + g->position[idx].modified = GS_POSITION_MODIFIED; + g->save_flag |= GS_POSITION; // Change guild_position + return 0; +} + +// Guild Skill UP +int mapif_parse_GuildSkillUp(int fd,int guild_id,int skill_num,int account_id) +{ + struct guild * g; + int idx = skill_num - GD_SKILLBASE; + + g = inter_guild_fromsql(guild_id); + if(g == NULL || idx < 0 || idx >= MAX_GUILDSKILL) + return 0; + + if(g->skill_point>0 && g->skill[idx].id>0 && g->skill[idx].lv<10 ) + { + g->skill[idx].lv++; + g->skill_point--; + if (!guild_calcinfo(g)) + mapif_guild_info(-1,g); + mapif_guild_skillupack(guild_id,skill_num,account_id); + g->save_flag |= (GS_LEVEL|GS_SKILL); // Change guild & guild_skill + } + return 0; +} + +//Manual deletion of an alliance when partnering guild does not exists. [Skotlex] +static int mapif_parse_GuildDeleteAlliance(struct guild *g, int guild_id, int account_id1, int account_id2, int flag) +{ + int i; + char name[NAME_LENGTH]; + for(i=0;ialliance[i].guild_id == guild_id) + { + strcpy(name, g->alliance[i].name); + g->alliance[i].guild_id=0; + break; + } + if (i == MAX_GUILDALLIANCE) + return -1; + + mapif_guild_alliance(g->guild_id,guild_id,account_id1,account_id2,flag,g->name,name); + g->save_flag |= GS_ALLIANCE; + return 0; +} + +// Alliance modification +int mapif_parse_GuildAlliance(int fd,int guild_id1,int guild_id2, + int account_id1,int account_id2,int flag) +{ + // Could speed up + struct guild *g[2]; + int j,i; + g[0] = inter_guild_fromsql(guild_id1); + g[1] = inter_guild_fromsql(guild_id2); + + if(g[0] && g[1]==NULL && (flag & GUILD_ALLIANCE_REMOVE)) //Requested to remove an alliance with a not found guild. + return mapif_parse_GuildDeleteAlliance(g[0], guild_id2, account_id1, account_id2, flag); //Try to do a manual removal of said guild. + + if(g[0]==NULL || g[1]==NULL) + return 0; + + if(flag&GUILD_ALLIANCE_REMOVE) + { + // Remove alliance/opposition, in case of alliance, remove on both side + for(i=0;i<2-(flag&GUILD_ALLIANCE_TYPE_MASK);i++) + { + for(j=0;jalliance[j].guild_id == g[1-i]->guild_id && g[i]->alliance[j].opposition == (flag&GUILD_ALLIANCE_TYPE_MASK)) + { + g[i]->alliance[j].guild_id=0; + break; + } + } + } + } + else + { + // Add alliance, in case of alliance, add on both side + for(i=0;i<2-(flag&GUILD_ALLIANCE_TYPE_MASK);i++) + { + // Search an empty slot + for(j=0;jalliance[j].guild_id==0) + { + g[i]->alliance[j].guild_id=g[1-i]->guild_id; + memcpy(g[i]->alliance[j].name,g[1-i]->name,NAME_LENGTH-1); + // Set alliance type + g[i]->alliance[j].opposition = flag&GUILD_ALLIANCE_TYPE_MASK; + break; + } + } + } + } + + // Send on all map the new alliance/opposition + mapif_guild_alliance(guild_id1,guild_id2,account_id1,account_id2,flag,g[0]->name,g[1]->name); + + // Mark the two guild to be saved + g[0]->save_flag |= GS_ALLIANCE; + g[1]->save_flag |= GS_ALLIANCE; + return 0; +} + +// Change guild message +int mapif_parse_GuildNotice(int fd,int guild_id,const char *mes1,const char *mes2) +{ + struct guild *g; + + g = inter_guild_fromsql(guild_id); + if(g==NULL) + return 0; + + memcpy(g->mes1,mes1,60); + memcpy(g->mes2,mes2,120); + g->save_flag |= GS_MES; //Change mes of guild + return mapif_guild_notice(g); +} + +int mapif_parse_GuildEmblem(int fd,int len,int guild_id,int dummy,const char *data) +{ + struct guild * g; + + g = inter_guild_fromsql(guild_id); + if(g==NULL) + return 0; + + if (len > sizeof(g->emblem_data)) + len = sizeof(g->emblem_data); + + memcpy(g->emblem_data,data,len); + g->emblem_len=len; + g->emblem_id++; + g->save_flag |= GS_EMBLEM; //Change guild + return mapif_guild_emblem(g); +} + +int mapif_parse_GuildCastleDataLoad(int fd,int castle_id,int index) // +{ + struct guild_castle gc; + if (!inter_guildcastle_fromsql(castle_id, &gc)) { + return mapif_guild_castle_dataload(castle_id,0,0); + } + switch(index){ + case 1: return mapif_guild_castle_dataload(gc.castle_id,index,gc.guild_id); break; + case 2: return mapif_guild_castle_dataload(gc.castle_id,index,gc.economy); break; + case 3: return mapif_guild_castle_dataload(gc.castle_id,index,gc.defense); break; + case 4: return mapif_guild_castle_dataload(gc.castle_id,index,gc.triggerE); break; + case 5: return mapif_guild_castle_dataload(gc.castle_id,index,gc.triggerD); break; + case 6: return mapif_guild_castle_dataload(gc.castle_id,index,gc.nextTime); break; + case 7: return mapif_guild_castle_dataload(gc.castle_id,index,gc.payTime); break; + case 8: return mapif_guild_castle_dataload(gc.castle_id,index,gc.createTime); break; + case 9: return mapif_guild_castle_dataload(gc.castle_id,index,gc.visibleC); break; + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + return mapif_guild_castle_dataload(gc.castle_id,index,gc.guardian[index-10].visible); break; + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + return mapif_guild_castle_dataload(gc.castle_id,index,gc.guardian[index-18].hp); break; + default: + ShowError("mapif_parse_GuildCastleDataLoad ERROR!! (Not found index=%d)\n", index); + return 0; + } +} + +int mapif_parse_GuildCastleDataSave(int fd,int castle_id,int index,int value) // +{ + struct guild_castle gc; + if(!inter_guildcastle_fromsql(castle_id, &gc)) + return mapif_guild_castle_datasave(castle_id,index,value); + + switch(index){ + case 1: + if( gc.guild_id!=value ){ + int gid=(value)?value:gc.guild_id; + struct guild *g=idb_get(guild_db_, gid); + if(log_inter) + inter_log("guild %s (id=%d) %s castle id=%d" RETCODE, + (g)?g->name:"??" ,gid, (value)?"occupy":"abandon", castle_id); + } + gc.guild_id = value; + if(gc.guild_id == 0) { + //Delete guardians. + memset(&gc.guardian, 0, sizeof(gc.guardian)); + } + break; + case 2: gc.economy = value; break; + case 3: gc.defense = value; break; + case 4: gc.triggerE = value; break; + case 5: gc.triggerD = value; break; + case 6: gc.nextTime = value; break; + case 7: gc.payTime = value; break; + case 8: gc.createTime = value; break; + case 9: gc.visibleC = value; break; + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + gc.guardian[index-10].visible = value; break; + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + gc.guardian[index-18].hp = value; break; // end additions [Valaris] + default: + ShowError("mapif_parse_GuildCastleDataSave ERROR!! (Not found index=%d)\n", index); + return 0; + } + inter_guildcastle_tosql(&gc); + mapif_guild_castle_datasave(gc.castle_id,index,value); + return 0; +} + +int mapif_parse_GuildMasterChange(int fd, int guild_id, const char* name, int len) +{ + struct guild * g; + struct guild_member gm; + int pos; + + g = inter_guild_fromsql(guild_id); + + if(g==NULL || len > NAME_LENGTH) + return 0; + + // Find member (name) + for (pos = 0; pos < g->max_member && strncmp(g->member[pos].name, name, len); pos++); + + if (pos == g->max_member) + return 0; //Character not found?? + + // Switch current and old GM + memcpy(&gm, &g->member[pos], sizeof (struct guild_member)); + memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member)); + memcpy(&g->member[0], &gm, sizeof(struct guild_member)); + + // Switch positions + g->member[pos].position = g->member[0].position; + g->member[pos].modified = GS_MEMBER_MODIFIED; + g->member[0].position = 0; //Position 0: guild Master. + g->member[0].modified = GS_MEMBER_MODIFIED; + + strncpy(g->master, name, len); + if (len < NAME_LENGTH) + g->master[len] = '\0'; + + ShowInfo("int_guild: Guildmaster Changed to %s (Guild %d - %s)\n",g->master, guild_id, g->name); + g->save_flag |= (GS_BASIC|GS_MEMBER); //Save main data and member data. + return mapif_guild_master_changed(g, pos); +} + +// ギルドチェック要求 +int mapif_parse_GuildCheck(int fd,int guild_id,int account_id,int char_id) +{ + // What does this mean? Check if belong to another guild? + return 0; +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_guild_parse_frommap(int fd) +{ + RFIFOHEAD(fd); + switch(RFIFOW(fd,0)){ + case 0x3030: mapif_parse_CreateGuild(fd,RFIFOL(fd,4),(char*)RFIFOP(fd,8),(struct guild_member *)RFIFOP(fd,32)); break; + case 0x3031: mapif_parse_GuildInfo(fd,RFIFOL(fd,2)); break; + case 0x3032: mapif_parse_GuildAddMember(fd,RFIFOL(fd,4),(struct guild_member *)RFIFOP(fd,8)); break; + case 0x3033: mapif_parse_GuildMasterChange(fd,RFIFOL(fd,4),(const char*)RFIFOP(fd,8),RFIFOW(fd,2)-8); break; + case 0x3034: mapif_parse_GuildLeave(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),(const char*)RFIFOP(fd,15)); break; + case 0x3035: mapif_parse_GuildChangeMemberInfoShort(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),RFIFOW(fd,15),RFIFOW(fd,17)); break; + case 0x3036: mapif_parse_BreakGuild(fd,RFIFOL(fd,2)); break; + case 0x3037: mapif_parse_GuildMessage(fd,RFIFOL(fd,4),RFIFOL(fd,8),(char*)RFIFOP(fd,12),RFIFOW(fd,2)-12); break; + case 0x3038: mapif_parse_GuildCheck(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; + case 0x3039: mapif_parse_GuildBasicInfoChange(fd,RFIFOL(fd,4),RFIFOW(fd,8),(const char*)RFIFOP(fd,10),RFIFOW(fd,2)-10); break; + case 0x303A: mapif_parse_GuildMemberInfoChange(fd,RFIFOL(fd,4),RFIFOL(fd,8),RFIFOL(fd,12),RFIFOW(fd,16),(const char*)RFIFOP(fd,18),RFIFOW(fd,2)-18); break; + case 0x303B: mapif_parse_GuildPosition(fd,RFIFOL(fd,4),RFIFOL(fd,8),(struct guild_position *)RFIFOP(fd,12)); break; + case 0x303C: mapif_parse_GuildSkillUp(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10)); break; + case 0x303D: mapif_parse_GuildAlliance(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOL(fd,14),RFIFOB(fd,18)); break; + case 0x303E: mapif_parse_GuildNotice(fd,RFIFOL(fd,2),(const char*)RFIFOP(fd,6),(const char*)RFIFOP(fd,66)); break; + case 0x303F: mapif_parse_GuildEmblem(fd,RFIFOW(fd,2)-12,RFIFOL(fd,4),RFIFOL(fd,8),(const char*)RFIFOP(fd,12)); break; + case 0x3040: mapif_parse_GuildCastleDataLoad(fd,RFIFOW(fd,2),RFIFOB(fd,4)); break; + case 0x3041: mapif_parse_GuildCastleDataSave(fd,RFIFOW(fd,2),RFIFOB(fd,4),RFIFOL(fd,5)); break; + + default: + return 0; + } + return 1; +} + +int inter_guild_mapif_init(int fd) +{ + return mapif_guild_castle_alldataload(fd); +} + +// サーバーから脱退要求(キャラ削除用) +int inter_guild_leave(int guild_id,int account_id,int char_id) +{ + return mapif_parse_GuildLeave(-1,guild_id,account_id,char_id,0,"** Character Deleted **"); +} + +int inter_guild_broken(int guild_id) +{ + return mapif_guild_broken(guild_id, 0); +} +#endif //TXT_SQL_CONVERT diff --git a/src/char_sql/int_guild.h b/src/char_sql/int_guild.h index 88836a2a1..4b153574b 100644 --- a/src/char_sql/int_guild.h +++ b/src/char_sql/int_guild.h @@ -1,35 +1,35 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INT_GUILD_SQL_H_ -#define _INT_GUILD_SQL_H_ - -int inter_guild_parse_frommap(int fd); -int inter_guild_sql_init(void); -void inter_guild_sql_final(void); -int inter_guild_mapif_init(int fd); -int inter_guild_leave(int guild_id,int account_id,int char_id); -int mapif_parse_BreakGuild(int fd,int guild_id); -int inter_guild_broken(int guild_id); -int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender); -int inter_guild_CharOnline(int char_id, int guild_id); -int inter_guild_CharOffline(int char_id, int guild_id); - -#define GS_BASIC 0x0001 -#define GS_MEMBER 0x0002 -#define GS_POSITION 0x0004 -#define GS_ALLIANCE 0x0008 -#define GS_EXPULSION 0x0010 -#define GS_SKILL 0x0020 -#define GS_EMBLEM 0x0040 -#define GS_CONNECT 0x0080 -#define GS_LEVEL 0x0100 -#define GS_MES 0x0200 -#define GS_MASK 0x03FF -#define GS_BASIC_MASK (GS_BASIC | GS_EMBLEM | GS_CONNECT | GS_LEVEL | GS_MES) -#define GS_REMOVE 0x8000 - -//For the TXT->SQL converter. -int inter_guild_tosql(struct guild *g,int flag); -int inter_guildcastle_tosql(struct guild_castle *gc); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INT_GUILD_SQL_H_ +#define _INT_GUILD_SQL_H_ + +int inter_guild_parse_frommap(int fd); +int inter_guild_sql_init(void); +void inter_guild_sql_final(void); +int inter_guild_mapif_init(int fd); +int inter_guild_leave(int guild_id,int account_id,int char_id); +int mapif_parse_BreakGuild(int fd,int guild_id); +int inter_guild_broken(int guild_id); +int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender); +int inter_guild_CharOnline(int char_id, int guild_id); +int inter_guild_CharOffline(int char_id, int guild_id); + +#define GS_BASIC 0x0001 +#define GS_MEMBER 0x0002 +#define GS_POSITION 0x0004 +#define GS_ALLIANCE 0x0008 +#define GS_EXPULSION 0x0010 +#define GS_SKILL 0x0020 +#define GS_EMBLEM 0x0040 +#define GS_CONNECT 0x0080 +#define GS_LEVEL 0x0100 +#define GS_MES 0x0200 +#define GS_MASK 0x03FF +#define GS_BASIC_MASK (GS_BASIC | GS_EMBLEM | GS_CONNECT | GS_LEVEL | GS_MES) +#define GS_REMOVE 0x8000 + +//For the TXT->SQL converter. +int inter_guild_tosql(struct guild *g,int flag); +int inter_guildcastle_tosql(struct guild_castle *gc); +#endif diff --git a/src/char_sql/int_homun.c b/src/char_sql/int_homun.c index d39114602..e726b15d3 100644 --- a/src/char_sql/int_homun.c +++ b/src/char_sql/int_homun.c @@ -1,296 +1,296 @@ -// Homunculus saving by Albator and Orn for eAthena. -// GNU/GPL rulez ! - -#include -#include -#include - -#include "char.h" -#include "../common/strlib.h" -#include "../common/showmsg.h" - -struct s_homunculus *homun_pt; - -#ifndef SQL_DEBUG - -#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] - -#else - -#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) - -#endif - - -int inter_homunculus_sql_init(void){ - //memory alloc - homun_pt = (struct s_homunculus*)aCalloc(sizeof(struct s_homunculus), 1); - return 0; -} -void inter_homunculus_sql_final(void){ - if (homun_pt) aFree(homun_pt); - return; -} - -int mapif_saved_homunculus(int fd, int account_id, unsigned char flag) -{ - WFIFOHEAD(fd, 7); - WFIFOW(fd,0) = 0x3892; - WFIFOL(fd,2) = account_id; - WFIFOB(fd,6) = flag; - WFIFOSET(fd, 7); - return 0; -} -int mapif_info_homunculus(int fd, int account_id, struct s_homunculus *hd) -{ - WFIFOHEAD(fd, sizeof(struct s_homunculus)+9); - WFIFOW(fd,0) = 0x3891; - WFIFOW(fd,2) = sizeof(struct s_homunculus)+9; - WFIFOL(fd,4) = account_id; - WFIFOB(fd,8) = 1; // account loaded with success - - memcpy(WFIFOP(fd,9), hd, sizeof(struct s_homunculus)); - WFIFOSET(fd, sizeof(struct s_homunculus)+9); - return 0; -} - -int mapif_homunculus_deleted(int fd, int flag) -{ - WFIFOHEAD(fd, 3); - WFIFOW(fd, 0) = 0x3893; - WFIFOB(fd,2) = flag; //Flag 1 = success - WFIFOSET(fd, 3); - return 0; - -} -int mapif_homunculus_created(int fd, int account_id, struct s_homunculus *sh, unsigned char flag) -{ - WFIFOHEAD(fd, sizeof(struct s_homunculus)+9); - WFIFOW(fd,0) = 0x3890; - WFIFOW(fd,2) = sizeof(struct s_homunculus)+9; - WFIFOL(fd,4) = account_id; - WFIFOB(fd,8)= flag; - memcpy(WFIFOP(fd,9),sh,sizeof(struct s_homunculus)); - WFIFOSET(fd, WFIFOW(fd,2)); - return 0; -} - -// Save/Update Homunculus Skills -int mapif_save_homunculus_skills(struct s_homunculus *hd) -{ - int i; - - for(i=0; ihskill[i].id != 0 && hd->hskill[i].lv != 0 ) - { - sprintf(tmp_sql,"REPLACE INTO `skill_homunculus` (`homun_id`, `id`, `lv`) VALUES (%d, %d, %d)", - hd->hom_id, hd->hskill[i].id, hd->hskill[i].lv); - - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - } - } - - return 1; -} - -int mapif_save_homunculus(int fd, int account_id, struct s_homunculus *hd) -{ - int flag =1; - char t_name[NAME_LENGTH*2]; - jstrescapecpy(t_name, hd->name); - - if(hd->hom_id==0) // new homunculus - { - ShowInfo("New homunculus name : %s\n",hd->name); - - sprintf(tmp_sql, "INSERT INTO `homunculus` " - "(`char_id`, `class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`, `rename_flag`, `vaporize`) " - "VALUES ('%d', '%d', '%s', '%d', '%u', '%u', '%d', '%d', %d, '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - hd->char_id, hd->class_,t_name,hd->level,hd->exp,hd->intimacy,hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk, - hd->hp,hd->max_hp,hd->sp,hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize); - - } - else - { - sprintf(tmp_sql, "UPDATE `homunculus` SET `char_id`='%d', `class`='%d',`name`='%s',`level`='%d',`exp`='%u',`intimacy`='%u',`hunger`='%d', `str`='%d', `agi`='%d', `vit`='%d', `int`='%d', `dex`='%d', `luk`='%d', `hp`='%d',`max_hp`='%d',`sp`='%d',`max_sp`='%d',`skill_point`='%d', `rename_flag`='%d', `vaporize`='%d' WHERE `homun_id`='%d'", - hd->char_id, hd->class_,t_name,hd->level,hd->exp,hd->intimacy,hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk, - hd->hp,hd->max_hp,hd->sp,hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize, hd->hom_id); - } - - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - flag = 0; - } - - if(hd->hom_id==0 && flag!=0) - hd->hom_id = (int)mysql_insert_id(&mysql_handle); // new homunculus - else - { - flag = mapif_save_homunculus_skills(hd); - mapif_saved_homunculus(fd, account_id, flag); - } - return flag; -} - - - -// Load an homunculus -int mapif_load_homunculus(int fd){ - int i; - RFIFOHEAD(fd); - memset(homun_pt, 0, sizeof(struct s_homunculus)); - - sprintf(tmp_sql,"SELECT `homun_id`,`char_id`,`class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`,`rename_flag`, `vaporize` FROM `homunculus` WHERE `homun_id`='%lu'", RFIFOL(fd,6)); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - - return 0; - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res!=NULL && mysql_num_rows(sql_res)>0) { - sql_row = mysql_fetch_row(sql_res); - - homun_pt->hom_id = RFIFOL(fd,6) ; //RFIFOL(fd,2); - homun_pt->class_ = atoi(sql_row[2]); - memcpy(homun_pt->name, sql_row[3],NAME_LENGTH-1); - homun_pt->char_id = atoi(sql_row[1]); - homun_pt->level = atoi(sql_row[4]); - homun_pt->exp = atoi(sql_row[5]); - homun_pt->intimacy = atoi(sql_row[6]); - homun_pt->hunger = atoi(sql_row[7]); - homun_pt->str = atoi(sql_row[8]); - homun_pt->agi = atoi(sql_row[9]); - homun_pt->vit = atoi(sql_row[10]); - homun_pt->int_ = atoi(sql_row[11]); - homun_pt->dex = atoi(sql_row[12]); - homun_pt->luk = atoi(sql_row[13]); - homun_pt->hp = atoi(sql_row[14]); - homun_pt->max_hp = atoi(sql_row[15]); - homun_pt->sp = atoi(sql_row[16]); - homun_pt->max_sp = atoi(sql_row[17]); - homun_pt->skillpts = atoi(sql_row[18]); - homun_pt->rename_flag = atoi(sql_row[19]); - homun_pt->vaporize = atoi(sql_row[20]); - } - if(homun_pt->hunger < 0) - homun_pt->hunger = 0; - else if(homun_pt->hunger > 100) - homun_pt->hunger = 100; - if(homun_pt->intimacy < 0) - homun_pt->intimacy = 0; - else if(homun_pt->intimacy > 100000) - homun_pt->intimacy = 100000; - - mysql_free_result(sql_res); - - // Load Homunculus Skill - memset(homun_pt->hskill, 0, sizeof(homun_pt->hskill)); - - sprintf(tmp_sql,"SELECT `id`,`lv` FROM `skill_homunculus` WHERE `homun_id`=%d",homun_pt->hom_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - sql_res = mysql_store_result(&mysql_handle); - if(sql_res){ - while((sql_row = mysql_fetch_row(sql_res))){ - i = (atoi(sql_row[0])-HM_SKILLBASE-1); - homun_pt->hskill[i].id = atoi(sql_row[0]); - homun_pt->hskill[i].lv = atoi(sql_row[1]); - } - } - - mysql_free_result(sql_res); - - ShowInfo("Homunculus loaded (%d - %s).\n", homun_pt->hom_id, homun_pt->name); - return mapif_info_homunculus(fd, RFIFOL(fd,2), homun_pt); -} - - - -int mapif_delete_homunculus(int fd) -{ - RFIFOHEAD(fd); - sprintf(tmp_sql, "DELETE FROM `homunculus` WHERE `homun_id` = '%lu'", RFIFOL(fd,2)); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return mapif_homunculus_deleted(fd, 0); - } - - sprintf(tmp_sql, "DELETE FROM `skill_homunculus` WHERE `homun_id` = '%lu'", RFIFOL(fd,2)); - if(mysql_query(&mysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return mapif_homunculus_deleted(fd, 0); - } - return mapif_homunculus_deleted(fd, 1); -} - -int mapif_rename_homun_ack(int fd, int account_id, int char_id, unsigned char flag, char *name){ - WFIFOHEAD(fd, NAME_LENGTH+12); - WFIFOW(fd, 0) =0x3894; - WFIFOL(fd, 2) =account_id; - WFIFOL(fd, 6) =char_id; - WFIFOB(fd, 10) =flag; - memcpy(WFIFOP(fd, 11), name, NAME_LENGTH); - WFIFOSET(fd, NAME_LENGTH+12); - - return 0; -} - -int mapif_rename_homun(int fd, int account_id, int char_id, char *name){ - int i; - - // Check Authorised letters/symbols in the name of the homun - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) { - mapif_rename_homun_ack(fd, account_id, char_id, 0, name); - return 0; - } - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) { - mapif_rename_homun_ack(fd, account_id, char_id, 0, name); - return 0; - } - } - - mapif_rename_homun_ack(fd, account_id, char_id, 1, name); - return 0; -} - -int mapif_parse_CreateHomunculus(int fd) -{ - RFIFOHEAD(fd); - memcpy(homun_pt, RFIFOP(fd,8), sizeof(struct s_homunculus)); - // Save in sql db - if(mapif_save_homunculus(fd,RFIFOL(fd,4), homun_pt)) - return mapif_homunculus_created(fd, RFIFOL(fd,4), homun_pt, 1); // send homun_id - return mapif_homunculus_created(fd, RFIFOL(fd,4), homun_pt, 0); // fail -} - -int inter_homunculus_parse_frommap(int fd){ - RFIFOHEAD(fd); - switch(RFIFOW(fd, 0)){ - case 0x3090: mapif_parse_CreateHomunculus(fd); break; - case 0x3091: mapif_load_homunculus(fd); break; - case 0x3092: mapif_save_homunculus(fd, RFIFOW(fd,4), (struct s_homunculus*) RFIFOP(fd, 8)); break; - case 0x3093: mapif_delete_homunculus(fd); break; // doesn't need to be parse, very simple packet... - case 0x3094: mapif_rename_homun(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOP(fd, 10)); break; - default: - return 0; - } - return 1; -} +// Homunculus saving by Albator and Orn for eAthena. +// GNU/GPL rulez ! + +#include +#include +#include + +#include "char.h" +#include "../common/strlib.h" +#include "../common/showmsg.h" + +struct s_homunculus *homun_pt; + +#ifndef SQL_DEBUG + +#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] + +#else + +#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) + +#endif + + +int inter_homunculus_sql_init(void){ + //memory alloc + homun_pt = (struct s_homunculus*)aCalloc(sizeof(struct s_homunculus), 1); + return 0; +} +void inter_homunculus_sql_final(void){ + if (homun_pt) aFree(homun_pt); + return; +} + +int mapif_saved_homunculus(int fd, int account_id, unsigned char flag) +{ + WFIFOHEAD(fd, 7); + WFIFOW(fd,0) = 0x3892; + WFIFOL(fd,2) = account_id; + WFIFOB(fd,6) = flag; + WFIFOSET(fd, 7); + return 0; +} +int mapif_info_homunculus(int fd, int account_id, struct s_homunculus *hd) +{ + WFIFOHEAD(fd, sizeof(struct s_homunculus)+9); + WFIFOW(fd,0) = 0x3891; + WFIFOW(fd,2) = sizeof(struct s_homunculus)+9; + WFIFOL(fd,4) = account_id; + WFIFOB(fd,8) = 1; // account loaded with success + + memcpy(WFIFOP(fd,9), hd, sizeof(struct s_homunculus)); + WFIFOSET(fd, sizeof(struct s_homunculus)+9); + return 0; +} + +int mapif_homunculus_deleted(int fd, int flag) +{ + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = 0x3893; + WFIFOB(fd,2) = flag; //Flag 1 = success + WFIFOSET(fd, 3); + return 0; + +} +int mapif_homunculus_created(int fd, int account_id, struct s_homunculus *sh, unsigned char flag) +{ + WFIFOHEAD(fd, sizeof(struct s_homunculus)+9); + WFIFOW(fd,0) = 0x3890; + WFIFOW(fd,2) = sizeof(struct s_homunculus)+9; + WFIFOL(fd,4) = account_id; + WFIFOB(fd,8)= flag; + memcpy(WFIFOP(fd,9),sh,sizeof(struct s_homunculus)); + WFIFOSET(fd, WFIFOW(fd,2)); + return 0; +} + +// Save/Update Homunculus Skills +int mapif_save_homunculus_skills(struct s_homunculus *hd) +{ + int i; + + for(i=0; ihskill[i].id != 0 && hd->hskill[i].lv != 0 ) + { + sprintf(tmp_sql,"REPLACE INTO `skill_homunculus` (`homun_id`, `id`, `lv`) VALUES (%d, %d, %d)", + hd->hom_id, hd->hskill[i].id, hd->hskill[i].lv); + + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + } + } + + return 1; +} + +int mapif_save_homunculus(int fd, int account_id, struct s_homunculus *hd) +{ + int flag =1; + char t_name[NAME_LENGTH*2]; + jstrescapecpy(t_name, hd->name); + + if(hd->hom_id==0) // new homunculus + { + ShowInfo("New homunculus name : %s\n",hd->name); + + sprintf(tmp_sql, "INSERT INTO `homunculus` " + "(`char_id`, `class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`, `rename_flag`, `vaporize`) " + "VALUES ('%d', '%d', '%s', '%d', '%u', '%u', '%d', '%d', %d, '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + hd->char_id, hd->class_,t_name,hd->level,hd->exp,hd->intimacy,hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk, + hd->hp,hd->max_hp,hd->sp,hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize); + + } + else + { + sprintf(tmp_sql, "UPDATE `homunculus` SET `char_id`='%d', `class`='%d',`name`='%s',`level`='%d',`exp`='%u',`intimacy`='%u',`hunger`='%d', `str`='%d', `agi`='%d', `vit`='%d', `int`='%d', `dex`='%d', `luk`='%d', `hp`='%d',`max_hp`='%d',`sp`='%d',`max_sp`='%d',`skill_point`='%d', `rename_flag`='%d', `vaporize`='%d' WHERE `homun_id`='%d'", + hd->char_id, hd->class_,t_name,hd->level,hd->exp,hd->intimacy,hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk, + hd->hp,hd->max_hp,hd->sp,hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize, hd->hom_id); + } + + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + flag = 0; + } + + if(hd->hom_id==0 && flag!=0) + hd->hom_id = (int)mysql_insert_id(&mysql_handle); // new homunculus + else + { + flag = mapif_save_homunculus_skills(hd); + mapif_saved_homunculus(fd, account_id, flag); + } + return flag; +} + + + +// Load an homunculus +int mapif_load_homunculus(int fd){ + int i; + RFIFOHEAD(fd); + memset(homun_pt, 0, sizeof(struct s_homunculus)); + + sprintf(tmp_sql,"SELECT `homun_id`,`char_id`,`class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`,`rename_flag`, `vaporize` FROM `homunculus` WHERE `homun_id`='%lu'", RFIFOL(fd,6)); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + + return 0; + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res!=NULL && mysql_num_rows(sql_res)>0) { + sql_row = mysql_fetch_row(sql_res); + + homun_pt->hom_id = RFIFOL(fd,6) ; //RFIFOL(fd,2); + homun_pt->class_ = atoi(sql_row[2]); + memcpy(homun_pt->name, sql_row[3],NAME_LENGTH-1); + homun_pt->char_id = atoi(sql_row[1]); + homun_pt->level = atoi(sql_row[4]); + homun_pt->exp = atoi(sql_row[5]); + homun_pt->intimacy = atoi(sql_row[6]); + homun_pt->hunger = atoi(sql_row[7]); + homun_pt->str = atoi(sql_row[8]); + homun_pt->agi = atoi(sql_row[9]); + homun_pt->vit = atoi(sql_row[10]); + homun_pt->int_ = atoi(sql_row[11]); + homun_pt->dex = atoi(sql_row[12]); + homun_pt->luk = atoi(sql_row[13]); + homun_pt->hp = atoi(sql_row[14]); + homun_pt->max_hp = atoi(sql_row[15]); + homun_pt->sp = atoi(sql_row[16]); + homun_pt->max_sp = atoi(sql_row[17]); + homun_pt->skillpts = atoi(sql_row[18]); + homun_pt->rename_flag = atoi(sql_row[19]); + homun_pt->vaporize = atoi(sql_row[20]); + } + if(homun_pt->hunger < 0) + homun_pt->hunger = 0; + else if(homun_pt->hunger > 100) + homun_pt->hunger = 100; + if(homun_pt->intimacy < 0) + homun_pt->intimacy = 0; + else if(homun_pt->intimacy > 100000) + homun_pt->intimacy = 100000; + + mysql_free_result(sql_res); + + // Load Homunculus Skill + memset(homun_pt->hskill, 0, sizeof(homun_pt->hskill)); + + sprintf(tmp_sql,"SELECT `id`,`lv` FROM `skill_homunculus` WHERE `homun_id`=%d",homun_pt->hom_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + sql_res = mysql_store_result(&mysql_handle); + if(sql_res){ + while((sql_row = mysql_fetch_row(sql_res))){ + i = (atoi(sql_row[0])-HM_SKILLBASE-1); + homun_pt->hskill[i].id = atoi(sql_row[0]); + homun_pt->hskill[i].lv = atoi(sql_row[1]); + } + } + + mysql_free_result(sql_res); + + ShowInfo("Homunculus loaded (%d - %s).\n", homun_pt->hom_id, homun_pt->name); + return mapif_info_homunculus(fd, RFIFOL(fd,2), homun_pt); +} + + + +int mapif_delete_homunculus(int fd) +{ + RFIFOHEAD(fd); + sprintf(tmp_sql, "DELETE FROM `homunculus` WHERE `homun_id` = '%lu'", RFIFOL(fd,2)); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return mapif_homunculus_deleted(fd, 0); + } + + sprintf(tmp_sql, "DELETE FROM `skill_homunculus` WHERE `homun_id` = '%lu'", RFIFOL(fd,2)); + if(mysql_query(&mysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return mapif_homunculus_deleted(fd, 0); + } + return mapif_homunculus_deleted(fd, 1); +} + +int mapif_rename_homun_ack(int fd, int account_id, int char_id, unsigned char flag, char *name){ + WFIFOHEAD(fd, NAME_LENGTH+12); + WFIFOW(fd, 0) =0x3894; + WFIFOL(fd, 2) =account_id; + WFIFOL(fd, 6) =char_id; + WFIFOB(fd, 10) =flag; + memcpy(WFIFOP(fd, 11), name, NAME_LENGTH); + WFIFOSET(fd, NAME_LENGTH+12); + + return 0; +} + +int mapif_rename_homun(int fd, int account_id, int char_id, char *name){ + int i; + + // Check Authorised letters/symbols in the name of the homun + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) == NULL) { + mapif_rename_homun_ack(fd, account_id, char_id, 0, name); + return 0; + } + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) != NULL) { + mapif_rename_homun_ack(fd, account_id, char_id, 0, name); + return 0; + } + } + + mapif_rename_homun_ack(fd, account_id, char_id, 1, name); + return 0; +} + +int mapif_parse_CreateHomunculus(int fd) +{ + RFIFOHEAD(fd); + memcpy(homun_pt, RFIFOP(fd,8), sizeof(struct s_homunculus)); + // Save in sql db + if(mapif_save_homunculus(fd,RFIFOL(fd,4), homun_pt)) + return mapif_homunculus_created(fd, RFIFOL(fd,4), homun_pt, 1); // send homun_id + return mapif_homunculus_created(fd, RFIFOL(fd,4), homun_pt, 0); // fail +} + +int inter_homunculus_parse_frommap(int fd){ + RFIFOHEAD(fd); + switch(RFIFOW(fd, 0)){ + case 0x3090: mapif_parse_CreateHomunculus(fd); break; + case 0x3091: mapif_load_homunculus(fd); break; + case 0x3092: mapif_save_homunculus(fd, RFIFOW(fd,4), (struct s_homunculus*) RFIFOP(fd, 8)); break; + case 0x3093: mapif_delete_homunculus(fd); break; // doesn't need to be parse, very simple packet... + case 0x3094: mapif_rename_homun(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOP(fd, 10)); break; + default: + return 0; + } + return 1; +} diff --git a/src/char_sql/int_homun.h b/src/char_sql/int_homun.h index e274c9ee1..cfc46ca24 100644 --- a/src/char_sql/int_homun.h +++ b/src/char_sql/int_homun.h @@ -1,14 +1,14 @@ -// Homunculus saving by Albator and Orn for eAthena. -// GNU/GPL rulez ! - -#ifndef _INT_HOMUN_H_ -#define _INT_HOMUN_H_ - -int inter_homunculus_sql_init(void); -void inter_homunculus_sql_final(void); -int mapif_save_homunculus(struct s_homunculus *hd); -int mapif_load_homunculus(int fd); -int mapif_delete_homunculus(int fd); -int inter_homunculus_parse_frommap(int fd); - -#endif +// Homunculus saving by Albator and Orn for eAthena. +// GNU/GPL rulez ! + +#ifndef _INT_HOMUN_H_ +#define _INT_HOMUN_H_ + +int inter_homunculus_sql_init(void); +void inter_homunculus_sql_final(void); +int mapif_save_homunculus(struct s_homunculus *hd); +int mapif_load_homunculus(int fd); +int mapif_delete_homunculus(int fd); +int inter_homunculus_parse_frommap(int fd); + +#endif diff --git a/src/char_sql/int_party.c b/src/char_sql/int_party.c index da5506903..e39c826d4 100644 --- a/src/char_sql/int_party.c +++ b/src/char_sql/int_party.c @@ -1,937 +1,937 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -// original code from athena -// SQL conversion by hack - -#include -#include -#include -#include -#include "char.h" -#include "../common/db.h" -#include "../common/strlib.h" -#include "../common/socket.h" -#include "../common/showmsg.h" - -#ifndef TXT_SQL_CONVERT -struct party_data { - struct party party; - unsigned int min_lv, max_lv; - int family; //Is this party a family? if so, this holds the child id. - unsigned char size; //Total size of party. -}; - -static struct party_data *party_pt; -static struct dbt *party_db_; - -int mapif_party_broken(int party_id,int flag); -int party_check_empty(struct party_data *p); -int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id); -int party_check_exp_share(struct party_data *p); -int mapif_party_optionchanged(int fd,struct party *p, int account_id, int flag); - -#ifndef SQL_DEBUG - -#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] - -#else - -#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) - -#endif - -//Updates party's level range and unsets even share if broken. -static int int_party_check_lv(struct party_data *p) { - int i; - unsigned int lv; - p->min_lv = UINT_MAX; - p->max_lv = 0; - for(i=0;iparty.member[i].online) - continue; - - lv=p->party.member[i].lv; - if (lv < p->min_lv) p->min_lv = lv; - if (lv > p->max_lv) p->max_lv = lv; - } - - if (p->party.exp && !party_check_exp_share(p)) { - p->party.exp = 0; - mapif_party_optionchanged(0, &p->party, 0, 0); - return 0; - } - return 1; -} -//Calculates the state of a party. -static void int_party_calc_state(struct party_data *p) -{ - int i; - unsigned int lv; - p->min_lv = UINT_MAX; - p->max_lv = 0; - p->party.count = - p->size = - p->family = 0; - - //Check party size - for(i=0;iparty.member[i].lv) continue; - p->size++; - if(p->party.member[i].online) - p->party.count++; - } - if(p->size == 3) { - //Check Family State. - p->family = char_family( - p->party.member[0].char_id, - p->party.member[1].char_id, - p->party.member[2].char_id - ); - } - //max/min levels. - for(i=0;iparty.member[i].lv; - if (!lv) continue; - if(p->party.member[i].online && - //On families, the kid is not counted towards exp share rules. - p->party.member[i].char_id != p->family) - { - if( lv < p->min_lv ) p->min_lv=lv; - if( p->max_lv < lv ) p->max_lv=lv; - } - } - - if (p->party.exp && !party_check_exp_share(p)) { - p->party.exp = 0; //Set off even share. - mapif_party_optionchanged(0, &p->party, 0, 0); - } - return; -} -#endif //TXT_SQL_CONVERT -// Save party to mysql -int inter_party_tosql(struct party *p, int flag, int index) -{ - // 'party' ('party_id','name','exp','item','leader_id','leader_char') - char t_name[NAME_LENGTH*2]; //Required for jstrescapecpy [Skotlex] - int party_id; - if (p == NULL || p->party_id == 0) - return 0; - party_id = p->party_id; - -#ifdef NOISY - ShowInfo("Save party request ("CL_BOLD"%d"CL_RESET" - %s).\n", party_id, p->name); -#endif - jstrescapecpy(t_name, p->name); - -#ifndef TXT_SQL_CONVERT - if (flag&PS_BREAK) { //Break the party - // we'll skip name-checking and just reset everyone with the same party id [celest] - sprintf (tmp_sql, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", char_db, party_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `party_id`='%d'", party_db, party_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - //Remove from memory - idb_remove(party_db_, party_id); - return 1; - } -#endif //TXT_SQL_CONVERT - if(flag&PS_CREATE) { //Create party -#ifndef TXT_SQL_CONVERT - sprintf(tmp_sql, "INSERT INTO `%s` " - "(`name`, `exp`, `item`, `leader_id`, `leader_char`) " - "VALUES ('%s', '%d', '%d', '%d', '%d')", - party_db, t_name, p->exp, p->item, p->member[index].account_id, p->member[index].char_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - if(mysql_field_count(&mysql_handle) == 0 && - mysql_insert_id(&mysql_handle) != 0) - party_id = p->party_id = (int)mysql_insert_id(&mysql_handle); - else //Failed to retrieve ID?? - return 0; -#else - //During conversion, you want to specify the id, and allow overwriting - //(in case someone is re-running the process. - sprintf(tmp_sql, "REPLACE INTO `%s` " - "(`party_id`, `name`, `exp`, `item`, `leader_id`, `leader_char`) " - "VALUES ('%d', '%s', '%d', '%d', '%d', '%d')", - party_db, p->party_id, t_name, p->exp, p->item, p->member[index].account_id, p->member[index].char_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } -#endif - } - -#ifndef TXT_SQL_CONVERT - if (flag&PS_BASIC) { - //Update party info. - sprintf(tmp_sql, "UPDATE `%s` SET `name`='%s', `exp`='%d', `item`='%d' WHERE `party_id`='%d'", - party_db, t_name, p->exp, p->item, party_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - if (flag&PS_LEADER) { - //Update leader - sprintf(tmp_sql, "UPDATE `%s` SET `leader_id`='%d', `leader_char`='%d' WHERE `party_id`='%d'", - party_db, p->member[index].account_id, p->member[index].char_id, party_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - if (flag&PS_ADDMEMBER) { - //Add one party member. - sprintf (tmp_sql, "UPDATE `%s` SET `party_id`='%d' WHERE `account_id`='%d' AND `char_id`='%d'", - char_db, party_id, p->member[index].account_id, p->member[index].char_id); - if (mysql_query (&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - if (flag&PS_DELMEMBER) { - //Remove one party member. - sprintf (tmp_sql, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d' AND `account_id`='%d' AND `char_id`='%d'", - char_db, party_id, p->member[index].account_id, p->member[index].char_id); - if (mysql_query (&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } -#endif //TXT_SQL_CONVERT - if (save_log) - ShowInfo("Party Saved (%d - %s)\n", party_id, p->name); - return 1; -} -#ifndef TXT_SQL_CONVERT -// Read party from mysql -struct party_data *inter_party_fromsql(int party_id) -{ - int leader_id = 0, leader_char = 0; - struct party_data *p; -#ifdef NOISY - ShowInfo("Load party request ("CL_BOLD"%d"CL_RESET")\n", party_id); -#endif - if (party_id <=0) - return NULL; - - //Load from memory - if ((p = idb_get(party_db_, party_id)) != NULL) - return p; - - p = party_pt; - memset(p, 0, sizeof(struct party_data)); - - sprintf(tmp_sql, "SELECT `party_id`, `name`,`exp`,`item`, `leader_id`, `leader_char` FROM `%s` WHERE `party_id`='%d'", - party_db, party_id); // TBR - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return NULL; - } - - sql_res = mysql_store_result(&mysql_handle) ; - if (!sql_res) - return NULL; - sql_row = mysql_fetch_row(sql_res); - if (!sql_row) { - mysql_free_result(sql_res); - return NULL; - } - p->party.party_id = party_id; - memcpy(&p->party.name, sql_row[1], NAME_LENGTH-1); - p->party.exp = atoi(sql_row[2])?1:0; - p->party.item = atoi(sql_row[3]); - leader_id = atoi(sql_row[4]); - leader_char = atoi(sql_row[5]); - mysql_free_result(sql_res); - - // Load members - sprintf(tmp_sql,"SELECT `account_id`,`char_id`,`name`,`base_level`,`last_map`,`online`,`class` FROM `%s` WHERE `party_id`='%d'", - char_db, party_id); // TBR - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return NULL; - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - int i; - for (i = 0; iparty.member[i]; - m->account_id = atoi(sql_row[0]); - m->char_id = atoi(sql_row[1]); - m->leader = (m->account_id == leader_id && m->char_id == leader_char)?1:0; - memcpy(m->name, sql_row[2], NAME_LENGTH); - m->lv = atoi(sql_row[3]); - m->map = mapindex_name2id(sql_row[4]); - m->online = atoi(sql_row[5])?1:0; - m->class_ = atoi(sql_row[6]); - } - mysql_free_result(sql_res); - } - - if (save_log) - ShowInfo("Party loaded (%d - %s).\n",party_id, p->party.name); - //Add party to memory. - p = aCalloc(1, sizeof(struct party_data)); - memcpy(p, party_pt, sizeof(struct party_data)); - //init state - int_party_calc_state(p); - idb_put(party_db_, party_id, p); - return p; -} - -int inter_party_sql_init(void){ - //memory alloc - party_db_ = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - party_pt = (struct party_data*)aCalloc(sizeof(struct party_data), 1); - if (!party_pt) { - ShowFatalError("inter_party_sql_init: Out of Memory!\n"); - exit(1); - } - - /* Uncomment the following if you want to do a party_db cleanup (remove parties with no members) on startup.[Skotlex] - ShowStatus("cleaning party table...\n"); - sprintf (tmp_sql, - "DELETE FROM `%s` USING `%s` LEFT JOIN `%s` ON `%s`.leader_id =`%s`.account_id AND `%s`.leader_char = `%s`.char_id WHERE `%s`.account_id IS NULL", - party_db, party_db, char_db, party_db, char_db, party_db, char_db, char_db); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - */ - return 0; -} - -void inter_party_sql_final(void) -{ - party_db_->destroy(party_db_, NULL); - aFree(party_pt); - return; -} - -// Search for the party according to its name -struct party_data* search_partyname(char *str) -{ - char t_name[NAME_LENGTH*2]; - int party_id; - - sprintf(tmp_sql,"SELECT `party_id` FROM `%s` WHERE `name`='%s'",party_db, jstrescapecpy(t_name,str)); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res==NULL || mysql_num_rows(sql_res)<=0) - { - if (sql_res) mysql_free_result(sql_res); - return NULL; - } - sql_row = mysql_fetch_row(sql_res); - party_id = sql_row?atoi(sql_row[0]):0; - mysql_free_result(sql_res); - - return inter_party_fromsql(party_id); -} - -// Returns whether this party can keep having exp share or not. -int party_check_exp_share(struct party_data *p) -{ - return (p->party.count < 2 || p->max_lv - p->min_lv <= party_share_level); -} - -// Is there any member in the party? -int party_check_empty(struct party_data *p) -{ - int i; - if (p==NULL||p->party.party_id==0) return 1; - for(i=0;iparty.member[i].account_id;i++); - if (i < MAX_PARTY) return 0; - // If there is no member, then break the party - mapif_party_broken(p->party.party_id,0); - inter_party_tosql(&p->party, PS_BREAK, 0); - return 1; -} - - -// Check if a member is in two party, not necessary :) -int party_check_conflict(int party_id,int account_id,int char_id) -{ - return 0; -} - -//------------------------------------------------------------------- -// map serverへの通信 - -// パーティ作成可否 -int mapif_party_created(int fd,int account_id,int char_id,struct party *p) -{ - WFIFOHEAD(fd, 39); - WFIFOW(fd,0)=0x3820; - WFIFOL(fd,2)=account_id; - WFIFOL(fd,6)=char_id; - if(p!=NULL){ - WFIFOB(fd,10)=0; - WFIFOL(fd,11)=p->party_id; - memcpy(WFIFOP(fd,15),p->name,NAME_LENGTH); - ShowInfo("int_party: Party created (%d - %s)\n",p->party_id,p->name); - }else{ - WFIFOB(fd,10)=1; - WFIFOL(fd,11)=0; - memset(WFIFOP(fd,15),0,NAME_LENGTH); - } - WFIFOSET(fd,39); - - return 0; -} - -// パーティ情報見つからず -int mapif_party_noinfo(int fd,int party_id) -{ - WFIFOHEAD(fd,8); - WFIFOW(fd,0)=0x3821; - WFIFOW(fd,2)=8; - WFIFOL(fd,4)=party_id; - WFIFOSET(fd,8); - ShowWarning("int_party: info not found %d\n",party_id); - return 0; -} -// パーティ情報まとめ送り -int mapif_party_info(int fd,struct party *p) -{ - unsigned char buf[5+sizeof(struct party)]; - WBUFW(buf,0)=0x3821; - WBUFW(buf,2)=4+sizeof(struct party); - memcpy(buf+4,p,sizeof(struct party)); - - if(fd<0) - mapif_sendall(buf,WBUFW(buf,2)); - else - mapif_send(fd,buf,WBUFW(buf,2)); - return 0; -} -// パーティメンバ追加可否 -int mapif_party_memberadded(int fd, int party_id, int account_id, int char_id, int flag) { - WFIFOHEAD(fd, 15); - WFIFOW(fd,0) = 0x3822; - WFIFOL(fd,2) = party_id; - WFIFOL(fd,6) = account_id; - WFIFOL(fd,10) = char_id; - WFIFOB(fd,14) = flag; - WFIFOSET(fd,15); - - return 0; -} - -// パーティ設定変更通知 -int mapif_party_optionchanged(int fd,struct party *p,int account_id,int flag) -{ - unsigned char buf[16]; - WBUFW(buf,0)=0x3823; - WBUFL(buf,2)=p->party_id; - WBUFL(buf,6)=account_id; - WBUFW(buf,10)=p->exp; - WBUFW(buf,12)=p->item; - WBUFB(buf,14)=flag; - if(flag==0) - mapif_sendall(buf,15); - else - mapif_send(fd,buf,15); - return 0; -} - -//Checks whether the even-share setting of a party is broken when a character logs in. [Skotlex] -int inter_party_logged(int party_id, int account_id, int char_id) -{ - struct party_data *p; - int i; - - if (party_id <= 0) - return 0; - - if (!party_id) - return 0; - p = inter_party_fromsql(party_id); - if(!p) //Non existant party? - return 0; - - for(i = 0; i < MAX_PARTY; i++) - if(p->party.member[i].account_id==account_id && - p->party.member[i].char_id==char_id) - { - if (!p->party.member[i].online) { - p->party.member[i].online = 1; - p->party.count++; - if(p->party.member[i].lv < p->min_lv || - p->party.member[i].lv > p->max_lv) - int_party_check_lv(p); - } - break; - } - - return 0; -} - -// パーティ脱退通知 -int mapif_party_leaved(int party_id,int account_id, int char_id) { - unsigned char buf[16]; - - WBUFW(buf,0) = 0x3824; - WBUFL(buf,2) = party_id; - WBUFL(buf,6) = account_id; - WBUFL(buf,10) = char_id; - mapif_sendall(buf, 14); - return 0; -} - -// パーティマップ更新通知 -int mapif_party_membermoved(struct party *p,int idx) -{ - unsigned char buf[20]; - - WBUFW(buf,0) = 0x3825; - WBUFL(buf,2) = p->party_id; - WBUFL(buf,6) = p->member[idx].account_id; - WBUFL(buf,10) = p->member[idx].char_id; - WBUFW(buf,14) = p->member[idx].map; - WBUFB(buf,16) = p->member[idx].online; - WBUFW(buf,17) = p->member[idx].lv; - mapif_sendall(buf, 19); - return 0; -} - -// パーティ解散通知 -int mapif_party_broken(int party_id,int flag) -{ - unsigned char buf[16]; - WBUFW(buf,0)=0x3826; - WBUFL(buf,2)=party_id; - WBUFB(buf,6)=flag; - mapif_sendall(buf,7); - //printf("int_party: broken %d\n",party_id); - return 0; -} -// パーティ内発言 -int mapif_party_message(int party_id,int account_id,char *mes,int len, int sfd) -{ - unsigned char buf[512]; - WBUFW(buf,0)=0x3827; - WBUFW(buf,2)=len+12; - WBUFL(buf,4)=party_id; - WBUFL(buf,8)=account_id; - memcpy(WBUFP(buf,12),mes,len); - mapif_sendallwos(sfd, buf,len+12); - return 0; -} - -//------------------------------------------------------------------- -// map serverからの通信 - - -// Create Party -int mapif_parse_CreateParty(int fd, char *name, int item, int item2, struct party_member *leader) -{ - struct party_data *p; - int i; - if( (p=search_partyname(name))!=NULL){ - mapif_party_created(fd,leader->account_id,leader->char_id,NULL); - return 0; - } - // Check Authorised letters/symbols in the name of the character - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) { - mapif_party_created(fd,leader->account_id,leader->char_id,NULL); - return 0; - } - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) { - mapif_party_created(fd,leader->account_id,leader->char_id,NULL); - return 0; - } - } - - p= aCalloc(1, sizeof(struct party_data)); - - memcpy(p->party.name,name,NAME_LENGTH); - p->party.exp=0; - p->party.item=(item?1:0)|(item2?2:0); - - memcpy(&p->party.member[0], leader, sizeof(struct party_member)); - p->party.member[0].leader=1; - p->party.member[0].online=1; - - p->party.party_id=-1;//New party. - if (inter_party_tosql(&p->party,PS_CREATE|PS_ADDMEMBER,0)) { - //Add party to db - int_party_calc_state(p); - idb_put(party_db_, p->party.party_id, p); - mapif_party_created(fd,leader->account_id,leader->char_id,&p->party); - mapif_party_info(fd,&p->party); - } else { //Failed to create party. - aFree(p); - mapif_party_created(fd,leader->account_id,leader->char_id,NULL); - } - - return 0; -} -// パーティ情報要求 -int mapif_parse_PartyInfo(int fd,int party_id) -{ - struct party_data *p; - p = inter_party_fromsql(party_id); - - if (p) - mapif_party_info(fd,&p->party); - else - mapif_party_noinfo(fd,party_id); - return 0; -} -// パーティ追加要求 -int mapif_parse_PartyAddMember(int fd, int party_id, struct party_member *member) { - struct party_data *p; - int i; - - p = inter_party_fromsql(party_id); - - if(!p || p->size == MAX_PARTY){ - mapif_party_memberadded(fd,party_id,member->account_id,member->char_id,1); - return 0; - } - - for(i=0;iparty.member[i].account_id) - continue; - - memcpy(&p->party.member[i], member, sizeof(struct party_member)); - p->party.member[i].leader=0; - if (p->party.member[i].online) p->party.count++; - p->size++; - if (p->size == 3) //Check family state. - int_party_calc_state(p); - else //Check even share range. - if (member->lv < p->min_lv || member->lv > p->max_lv || p->family) { - if (p->family) p->family = 0; //Family state broken. - int_party_check_lv(p); - } - - mapif_party_memberadded(fd,party_id,member->account_id,member->char_id,0); - mapif_party_info(-1,&p->party); - inter_party_tosql(&p->party, PS_ADDMEMBER, i); - return 0; - } - //Party full - mapif_party_memberadded(fd,party_id,member->account_id,member->char_id,1); - return 0; -} -// パーティー設定変更要求 -int mapif_parse_PartyChangeOption(int fd,int party_id,int account_id,int exp,int item) -{ - struct party_data *p; - int flag = 0; - p = inter_party_fromsql(party_id); - - if(!p) - return 0; - - p->party.exp=exp; - if( exp && !party_check_exp_share(p) ){ - flag|=0x01; - p->party.exp=0; - } - p->party.item = item&0x3; //Filter out invalid values. - mapif_party_optionchanged(fd,&p->party,account_id,flag); - inter_party_tosql(&p->party, PS_BASIC, 0); - return 0; -} -// パーティ脱退要求 -int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) -{ - struct party_data *p; - int i,j=-1; - - p = inter_party_fromsql(party_id); - if (!p) { //Party does not exists? - sprintf(tmp_sql, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", char_db, party_id); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - return 0; - } - - for (i = 0; i < MAX_PARTY; i++) { - if(p->party.member[i].account_id == account_id && - p->party.member[i].char_id == char_id) { - break; - } - } - if (i >= MAX_PARTY) - return 0; //Member not found? - - mapif_party_leaved(party_id, account_id, char_id); - - if (p->party.member[i].leader){ - p->party.member[i].account_id = 0; - for (j = 0; j < MAX_PARTY; j++) { - if (!p->party.member[j].account_id) - continue; - mapif_party_leaved(party_id, p->party.member[j].account_id, p->party.member[j].char_id); - p->party.member[j].account_id = 0; - } - //Party gets deleted on the check_empty call below. - } else { - inter_party_tosql(&p->party,PS_DELMEMBER,i); - j = p->party.member[i].lv; - if(p->party.member[i].online) p->party.count--; - memset(&p->party.member[i], 0, sizeof(struct party_member)); - p->size--; - if (j == p->min_lv || j == p->max_lv || p->family) - { - if(p->family) p->family = 0; //Family state broken. - int_party_check_lv(p); - } - } - - if (party_check_empty(p) == 0) - mapif_party_info(-1,&p->party); - return 0; -} -// When member goes to other map -int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id, unsigned short map, int online, unsigned int lv) -{ - struct party_data *p; - int i; - - p = inter_party_fromsql(party_id); - if (p == NULL) - return 0; - - for(i = 0; i < MAX_PARTY; i++) { - if(p->party.member[i].account_id == account_id && - p->party.member[i].char_id == char_id) - { - p->party.member[i].map = map; - if (p->party.member[i].online != online) - { - p->party.member[i].online = online; - if (online) - p->party.count++; - else - p->party.count--; - // Even share check situations: Family state (always breaks) - // character logging on/off is max/min level (update level range) - // or character logging on/off has a different level (update level range using new level) - if (p->family || - (p->party.member[i].lv <= p->min_lv || p->party.member[i].lv >= p->max_lv) || - (p->party.member[i].lv != lv && (lv <= p->min_lv || lv >= p->max_lv)) - ) - { - p->party.member[i].lv = lv; - int_party_check_lv(p); - } - } - if (p->party.member[i].lv != lv) { - if(p->party.member[i].lv == p->min_lv || - p->party.member[i].lv == p->max_lv) - { - p->party.member[i].lv = lv; - int_party_check_lv(p); - } else - p->party.member[i].lv = lv; - } - mapif_party_membermoved(&p->party, i); - break; - } - } - return 0; -} -// パーティ解散要求 -int mapif_parse_BreakParty(int fd,int party_id) -{ - struct party_data *p; - - p = inter_party_fromsql(party_id); - - if(!p) - return 0; - inter_party_tosql(&p->party,PS_BREAK,0); - mapif_party_broken(fd,party_id); - return 0; -} -// パーティメッセージ送信 -int mapif_parse_PartyMessage(int fd,int party_id,int account_id,char *mes,int len) -{ - return mapif_party_message(party_id,account_id,mes,len, fd); -} -// パーティチェック要求 -int mapif_parse_PartyCheck(int fd,int party_id,int account_id,int char_id) -{ - return party_check_conflict(party_id,account_id,char_id); -} - -int mapif_parse_PartyLeaderChange(int fd,int party_id,int account_id,int char_id) -{ - struct party_data *p; - int i; - - p = inter_party_fromsql(party_id); - - if(!p) - return 0; - - for (i = 0; i < MAX_PARTY; i++) - { - if(p->party.member[i].leader) - p->party.member[i].leader = 0; - if(p->party.member[i].account_id == account_id && - p->party.member[i].char_id == char_id) - { - p->party.member[i].leader = 1; - inter_party_tosql(&p->party,PS_LEADER, i); - } - } - return 1; -} - -// map server からの通信 -// ・1パケットのみ解析すること -// ・パケット長データはinter.cにセットしておくこと -// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない -// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない -int inter_party_parse_frommap(int fd) -{ - RFIFOHEAD(fd); - switch(RFIFOW(fd,0)) { - case 0x3020: mapif_parse_CreateParty(fd, (char*)RFIFOP(fd,4), RFIFOB(fd,28), RFIFOB(fd,29), (struct party_member*)RFIFOP(fd,30)); break; - case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2)); break; - case 0x3022: mapif_parse_PartyAddMember(fd, RFIFOL(fd,4), (struct party_member*)RFIFOP(fd,8)); break; - case 0x3023: mapif_parse_PartyChangeOption(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOW(fd,10), RFIFOW(fd,12)); break; - case 0x3024: mapif_parse_PartyLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; - case 0x3025: mapif_parse_PartyChangeMap(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOW(fd,14), RFIFOB(fd,16), RFIFOW(fd,17)); break; - case 0x3026: mapif_parse_BreakParty(fd, RFIFOL(fd,2)); break; - case 0x3027: mapif_parse_PartyMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), (char*)RFIFOP(fd,12), RFIFOW(fd,2)-12); break; - case 0x3028: mapif_parse_PartyCheck(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; - case 0x3029: mapif_parse_PartyLeaderChange(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; - default: - return 0; - } - return 1; -} - -// サーバーから脱退要求(キャラ削除用) -int inter_party_leave(int party_id,int account_id, int char_id) -{ - return mapif_parse_PartyLeave(-1,party_id,account_id, char_id); -} - -int inter_party_CharOnline(int char_id, int party_id) { - struct party_data *p; - int i; - - if (party_id == -1) { - //Get party_id from the database - sprintf (tmp_sql , "SELECT party_id FROM `%s` WHERE char_id='%d'",char_db,char_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - - sql_res = mysql_store_result(&mysql_handle) ; - if(sql_res == NULL) - return 0; //Eh? No party? - - sql_row = mysql_fetch_row(sql_res); - party_id = sql_row?atoi(sql_row[0]):0; - mysql_free_result(sql_res); - } - if (party_id == 0) - return 0; //No party... - - p = inter_party_fromsql(party_id); - if(!p) { - ShowError("Character %d's party %d not found!\n", char_id, party_id); - return 0; - } - - //Set member online - for(i=0; iparty.member[i].char_id == char_id) { - if (!p->party.member[i].online) { - p->party.member[i].online = 1; - p->party.count++; - if (p->party.member[i].lv < p->min_lv || - p->party.member[i].lv > p->max_lv) - int_party_check_lv(p); - } - break; - } - } - return 1; -} - -int inter_party_CharOffline(int char_id, int party_id) { - struct party_data *p=NULL; - int i; - - if (party_id == -1) { - //Get guild_id from the database - sprintf (tmp_sql , "SELECT party_id FROM `%s` WHERE char_id='%d'",char_db,char_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - - sql_res = mysql_store_result(&mysql_handle) ; - if(sql_res == NULL) - return 0; //Eh? No party? - - sql_row = mysql_fetch_row(sql_res); - party_id = sql_row?atoi(sql_row[0]):0; - mysql_free_result(sql_res); - } - if (party_id == 0) - return 0; //No party... - - //Character has a party, set character offline and check if they were the only member online - if ((p = inter_party_fromsql(party_id)) == NULL) - return 0; - - //Set member offline - for(i=0; i< MAX_PARTY; i++) { - if(p->party.member[i].char_id == char_id) - { - p->party.member[i].online = 0; - p->party.count--; - if(p->party.member[i].lv == p->min_lv || - p->party.member[i].lv == p->max_lv) - int_party_check_lv(p); - break; - } - } - - if(!p->party.count) - //Parties don't have any data that needs be saved at this point... so just remove it from memory. - idb_remove(party_db_, party_id); - return 1; -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +// original code from athena +// SQL conversion by hack + +#include +#include +#include +#include +#include "char.h" +#include "../common/db.h" +#include "../common/strlib.h" +#include "../common/socket.h" +#include "../common/showmsg.h" + +#ifndef TXT_SQL_CONVERT +struct party_data { + struct party party; + unsigned int min_lv, max_lv; + int family; //Is this party a family? if so, this holds the child id. + unsigned char size; //Total size of party. +}; + +static struct party_data *party_pt; +static struct dbt *party_db_; + +int mapif_party_broken(int party_id,int flag); +int party_check_empty(struct party_data *p); +int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id); +int party_check_exp_share(struct party_data *p); +int mapif_party_optionchanged(int fd,struct party *p, int account_id, int flag); + +#ifndef SQL_DEBUG + +#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] + +#else + +#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) + +#endif + +//Updates party's level range and unsets even share if broken. +static int int_party_check_lv(struct party_data *p) { + int i; + unsigned int lv; + p->min_lv = UINT_MAX; + p->max_lv = 0; + for(i=0;iparty.member[i].online) + continue; + + lv=p->party.member[i].lv; + if (lv < p->min_lv) p->min_lv = lv; + if (lv > p->max_lv) p->max_lv = lv; + } + + if (p->party.exp && !party_check_exp_share(p)) { + p->party.exp = 0; + mapif_party_optionchanged(0, &p->party, 0, 0); + return 0; + } + return 1; +} +//Calculates the state of a party. +static void int_party_calc_state(struct party_data *p) +{ + int i; + unsigned int lv; + p->min_lv = UINT_MAX; + p->max_lv = 0; + p->party.count = + p->size = + p->family = 0; + + //Check party size + for(i=0;iparty.member[i].lv) continue; + p->size++; + if(p->party.member[i].online) + p->party.count++; + } + if(p->size == 3) { + //Check Family State. + p->family = char_family( + p->party.member[0].char_id, + p->party.member[1].char_id, + p->party.member[2].char_id + ); + } + //max/min levels. + for(i=0;iparty.member[i].lv; + if (!lv) continue; + if(p->party.member[i].online && + //On families, the kid is not counted towards exp share rules. + p->party.member[i].char_id != p->family) + { + if( lv < p->min_lv ) p->min_lv=lv; + if( p->max_lv < lv ) p->max_lv=lv; + } + } + + if (p->party.exp && !party_check_exp_share(p)) { + p->party.exp = 0; //Set off even share. + mapif_party_optionchanged(0, &p->party, 0, 0); + } + return; +} +#endif //TXT_SQL_CONVERT +// Save party to mysql +int inter_party_tosql(struct party *p, int flag, int index) +{ + // 'party' ('party_id','name','exp','item','leader_id','leader_char') + char t_name[NAME_LENGTH*2]; //Required for jstrescapecpy [Skotlex] + int party_id; + if (p == NULL || p->party_id == 0) + return 0; + party_id = p->party_id; + +#ifdef NOISY + ShowInfo("Save party request ("CL_BOLD"%d"CL_RESET" - %s).\n", party_id, p->name); +#endif + jstrescapecpy(t_name, p->name); + +#ifndef TXT_SQL_CONVERT + if (flag&PS_BREAK) { //Break the party + // we'll skip name-checking and just reset everyone with the same party id [celest] + sprintf (tmp_sql, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", char_db, party_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `party_id`='%d'", party_db, party_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + //Remove from memory + idb_remove(party_db_, party_id); + return 1; + } +#endif //TXT_SQL_CONVERT + if(flag&PS_CREATE) { //Create party +#ifndef TXT_SQL_CONVERT + sprintf(tmp_sql, "INSERT INTO `%s` " + "(`name`, `exp`, `item`, `leader_id`, `leader_char`) " + "VALUES ('%s', '%d', '%d', '%d', '%d')", + party_db, t_name, p->exp, p->item, p->member[index].account_id, p->member[index].char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + if(mysql_field_count(&mysql_handle) == 0 && + mysql_insert_id(&mysql_handle) != 0) + party_id = p->party_id = (int)mysql_insert_id(&mysql_handle); + else //Failed to retrieve ID?? + return 0; +#else + //During conversion, you want to specify the id, and allow overwriting + //(in case someone is re-running the process. + sprintf(tmp_sql, "REPLACE INTO `%s` " + "(`party_id`, `name`, `exp`, `item`, `leader_id`, `leader_char`) " + "VALUES ('%d', '%s', '%d', '%d', '%d', '%d')", + party_db, p->party_id, t_name, p->exp, p->item, p->member[index].account_id, p->member[index].char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } +#endif + } + +#ifndef TXT_SQL_CONVERT + if (flag&PS_BASIC) { + //Update party info. + sprintf(tmp_sql, "UPDATE `%s` SET `name`='%s', `exp`='%d', `item`='%d' WHERE `party_id`='%d'", + party_db, t_name, p->exp, p->item, party_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + if (flag&PS_LEADER) { + //Update leader + sprintf(tmp_sql, "UPDATE `%s` SET `leader_id`='%d', `leader_char`='%d' WHERE `party_id`='%d'", + party_db, p->member[index].account_id, p->member[index].char_id, party_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + if (flag&PS_ADDMEMBER) { + //Add one party member. + sprintf (tmp_sql, "UPDATE `%s` SET `party_id`='%d' WHERE `account_id`='%d' AND `char_id`='%d'", + char_db, party_id, p->member[index].account_id, p->member[index].char_id); + if (mysql_query (&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + if (flag&PS_DELMEMBER) { + //Remove one party member. + sprintf (tmp_sql, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d' AND `account_id`='%d' AND `char_id`='%d'", + char_db, party_id, p->member[index].account_id, p->member[index].char_id); + if (mysql_query (&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } +#endif //TXT_SQL_CONVERT + if (save_log) + ShowInfo("Party Saved (%d - %s)\n", party_id, p->name); + return 1; +} +#ifndef TXT_SQL_CONVERT +// Read party from mysql +struct party_data *inter_party_fromsql(int party_id) +{ + int leader_id = 0, leader_char = 0; + struct party_data *p; +#ifdef NOISY + ShowInfo("Load party request ("CL_BOLD"%d"CL_RESET")\n", party_id); +#endif + if (party_id <=0) + return NULL; + + //Load from memory + if ((p = idb_get(party_db_, party_id)) != NULL) + return p; + + p = party_pt; + memset(p, 0, sizeof(struct party_data)); + + sprintf(tmp_sql, "SELECT `party_id`, `name`,`exp`,`item`, `leader_id`, `leader_char` FROM `%s` WHERE `party_id`='%d'", + party_db, party_id); // TBR + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return NULL; + } + + sql_res = mysql_store_result(&mysql_handle) ; + if (!sql_res) + return NULL; + sql_row = mysql_fetch_row(sql_res); + if (!sql_row) { + mysql_free_result(sql_res); + return NULL; + } + p->party.party_id = party_id; + memcpy(&p->party.name, sql_row[1], NAME_LENGTH-1); + p->party.exp = atoi(sql_row[2])?1:0; + p->party.item = atoi(sql_row[3]); + leader_id = atoi(sql_row[4]); + leader_char = atoi(sql_row[5]); + mysql_free_result(sql_res); + + // Load members + sprintf(tmp_sql,"SELECT `account_id`,`char_id`,`name`,`base_level`,`last_map`,`online`,`class` FROM `%s` WHERE `party_id`='%d'", + char_db, party_id); // TBR + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return NULL; + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + int i; + for (i = 0; iparty.member[i]; + m->account_id = atoi(sql_row[0]); + m->char_id = atoi(sql_row[1]); + m->leader = (m->account_id == leader_id && m->char_id == leader_char)?1:0; + memcpy(m->name, sql_row[2], NAME_LENGTH); + m->lv = atoi(sql_row[3]); + m->map = mapindex_name2id(sql_row[4]); + m->online = atoi(sql_row[5])?1:0; + m->class_ = atoi(sql_row[6]); + } + mysql_free_result(sql_res); + } + + if (save_log) + ShowInfo("Party loaded (%d - %s).\n",party_id, p->party.name); + //Add party to memory. + p = aCalloc(1, sizeof(struct party_data)); + memcpy(p, party_pt, sizeof(struct party_data)); + //init state + int_party_calc_state(p); + idb_put(party_db_, party_id, p); + return p; +} + +int inter_party_sql_init(void){ + //memory alloc + party_db_ = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + party_pt = (struct party_data*)aCalloc(sizeof(struct party_data), 1); + if (!party_pt) { + ShowFatalError("inter_party_sql_init: Out of Memory!\n"); + exit(1); + } + + /* Uncomment the following if you want to do a party_db cleanup (remove parties with no members) on startup.[Skotlex] + ShowStatus("cleaning party table...\n"); + sprintf (tmp_sql, + "DELETE FROM `%s` USING `%s` LEFT JOIN `%s` ON `%s`.leader_id =`%s`.account_id AND `%s`.leader_char = `%s`.char_id WHERE `%s`.account_id IS NULL", + party_db, party_db, char_db, party_db, char_db, party_db, char_db, char_db); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + */ + return 0; +} + +void inter_party_sql_final(void) +{ + party_db_->destroy(party_db_, NULL); + aFree(party_pt); + return; +} + +// Search for the party according to its name +struct party_data* search_partyname(char *str) +{ + char t_name[NAME_LENGTH*2]; + int party_id; + + sprintf(tmp_sql,"SELECT `party_id` FROM `%s` WHERE `name`='%s'",party_db, jstrescapecpy(t_name,str)); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res==NULL || mysql_num_rows(sql_res)<=0) + { + if (sql_res) mysql_free_result(sql_res); + return NULL; + } + sql_row = mysql_fetch_row(sql_res); + party_id = sql_row?atoi(sql_row[0]):0; + mysql_free_result(sql_res); + + return inter_party_fromsql(party_id); +} + +// Returns whether this party can keep having exp share or not. +int party_check_exp_share(struct party_data *p) +{ + return (p->party.count < 2 || p->max_lv - p->min_lv <= party_share_level); +} + +// Is there any member in the party? +int party_check_empty(struct party_data *p) +{ + int i; + if (p==NULL||p->party.party_id==0) return 1; + for(i=0;iparty.member[i].account_id;i++); + if (i < MAX_PARTY) return 0; + // If there is no member, then break the party + mapif_party_broken(p->party.party_id,0); + inter_party_tosql(&p->party, PS_BREAK, 0); + return 1; +} + + +// Check if a member is in two party, not necessary :) +int party_check_conflict(int party_id,int account_id,int char_id) +{ + return 0; +} + +//------------------------------------------------------------------- +// map serverへの通信 + +// パーティ作成可否 +int mapif_party_created(int fd,int account_id,int char_id,struct party *p) +{ + WFIFOHEAD(fd, 39); + WFIFOW(fd,0)=0x3820; + WFIFOL(fd,2)=account_id; + WFIFOL(fd,6)=char_id; + if(p!=NULL){ + WFIFOB(fd,10)=0; + WFIFOL(fd,11)=p->party_id; + memcpy(WFIFOP(fd,15),p->name,NAME_LENGTH); + ShowInfo("int_party: Party created (%d - %s)\n",p->party_id,p->name); + }else{ + WFIFOB(fd,10)=1; + WFIFOL(fd,11)=0; + memset(WFIFOP(fd,15),0,NAME_LENGTH); + } + WFIFOSET(fd,39); + + return 0; +} + +// パーティ情報見つからず +int mapif_party_noinfo(int fd,int party_id) +{ + WFIFOHEAD(fd,8); + WFIFOW(fd,0)=0x3821; + WFIFOW(fd,2)=8; + WFIFOL(fd,4)=party_id; + WFIFOSET(fd,8); + ShowWarning("int_party: info not found %d\n",party_id); + return 0; +} +// パーティ情報まとめ送り +int mapif_party_info(int fd,struct party *p) +{ + unsigned char buf[5+sizeof(struct party)]; + WBUFW(buf,0)=0x3821; + WBUFW(buf,2)=4+sizeof(struct party); + memcpy(buf+4,p,sizeof(struct party)); + + if(fd<0) + mapif_sendall(buf,WBUFW(buf,2)); + else + mapif_send(fd,buf,WBUFW(buf,2)); + return 0; +} +// パーティメンバ追加可否 +int mapif_party_memberadded(int fd, int party_id, int account_id, int char_id, int flag) { + WFIFOHEAD(fd, 15); + WFIFOW(fd,0) = 0x3822; + WFIFOL(fd,2) = party_id; + WFIFOL(fd,6) = account_id; + WFIFOL(fd,10) = char_id; + WFIFOB(fd,14) = flag; + WFIFOSET(fd,15); + + return 0; +} + +// パーティ設定変更通知 +int mapif_party_optionchanged(int fd,struct party *p,int account_id,int flag) +{ + unsigned char buf[16]; + WBUFW(buf,0)=0x3823; + WBUFL(buf,2)=p->party_id; + WBUFL(buf,6)=account_id; + WBUFW(buf,10)=p->exp; + WBUFW(buf,12)=p->item; + WBUFB(buf,14)=flag; + if(flag==0) + mapif_sendall(buf,15); + else + mapif_send(fd,buf,15); + return 0; +} + +//Checks whether the even-share setting of a party is broken when a character logs in. [Skotlex] +int inter_party_logged(int party_id, int account_id, int char_id) +{ + struct party_data *p; + int i; + + if (party_id <= 0) + return 0; + + if (!party_id) + return 0; + p = inter_party_fromsql(party_id); + if(!p) //Non existant party? + return 0; + + for(i = 0; i < MAX_PARTY; i++) + if(p->party.member[i].account_id==account_id && + p->party.member[i].char_id==char_id) + { + if (!p->party.member[i].online) { + p->party.member[i].online = 1; + p->party.count++; + if(p->party.member[i].lv < p->min_lv || + p->party.member[i].lv > p->max_lv) + int_party_check_lv(p); + } + break; + } + + return 0; +} + +// パーティ脱退通知 +int mapif_party_leaved(int party_id,int account_id, int char_id) { + unsigned char buf[16]; + + WBUFW(buf,0) = 0x3824; + WBUFL(buf,2) = party_id; + WBUFL(buf,6) = account_id; + WBUFL(buf,10) = char_id; + mapif_sendall(buf, 14); + return 0; +} + +// パーティマップ更新通知 +int mapif_party_membermoved(struct party *p,int idx) +{ + unsigned char buf[20]; + + WBUFW(buf,0) = 0x3825; + WBUFL(buf,2) = p->party_id; + WBUFL(buf,6) = p->member[idx].account_id; + WBUFL(buf,10) = p->member[idx].char_id; + WBUFW(buf,14) = p->member[idx].map; + WBUFB(buf,16) = p->member[idx].online; + WBUFW(buf,17) = p->member[idx].lv; + mapif_sendall(buf, 19); + return 0; +} + +// パーティ解散通知 +int mapif_party_broken(int party_id,int flag) +{ + unsigned char buf[16]; + WBUFW(buf,0)=0x3826; + WBUFL(buf,2)=party_id; + WBUFB(buf,6)=flag; + mapif_sendall(buf,7); + //printf("int_party: broken %d\n",party_id); + return 0; +} +// パーティ内発言 +int mapif_party_message(int party_id,int account_id,char *mes,int len, int sfd) +{ + unsigned char buf[512]; + WBUFW(buf,0)=0x3827; + WBUFW(buf,2)=len+12; + WBUFL(buf,4)=party_id; + WBUFL(buf,8)=account_id; + memcpy(WBUFP(buf,12),mes,len); + mapif_sendallwos(sfd, buf,len+12); + return 0; +} + +//------------------------------------------------------------------- +// map serverからの通信 + + +// Create Party +int mapif_parse_CreateParty(int fd, char *name, int item, int item2, struct party_member *leader) +{ + struct party_data *p; + int i; + if( (p=search_partyname(name))!=NULL){ + mapif_party_created(fd,leader->account_id,leader->char_id,NULL); + return 0; + } + // Check Authorised letters/symbols in the name of the character + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) == NULL) { + mapif_party_created(fd,leader->account_id,leader->char_id,NULL); + return 0; + } + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) != NULL) { + mapif_party_created(fd,leader->account_id,leader->char_id,NULL); + return 0; + } + } + + p= aCalloc(1, sizeof(struct party_data)); + + memcpy(p->party.name,name,NAME_LENGTH); + p->party.exp=0; + p->party.item=(item?1:0)|(item2?2:0); + + memcpy(&p->party.member[0], leader, sizeof(struct party_member)); + p->party.member[0].leader=1; + p->party.member[0].online=1; + + p->party.party_id=-1;//New party. + if (inter_party_tosql(&p->party,PS_CREATE|PS_ADDMEMBER,0)) { + //Add party to db + int_party_calc_state(p); + idb_put(party_db_, p->party.party_id, p); + mapif_party_created(fd,leader->account_id,leader->char_id,&p->party); + mapif_party_info(fd,&p->party); + } else { //Failed to create party. + aFree(p); + mapif_party_created(fd,leader->account_id,leader->char_id,NULL); + } + + return 0; +} +// パーティ情報要求 +int mapif_parse_PartyInfo(int fd,int party_id) +{ + struct party_data *p; + p = inter_party_fromsql(party_id); + + if (p) + mapif_party_info(fd,&p->party); + else + mapif_party_noinfo(fd,party_id); + return 0; +} +// パーティ追加要求 +int mapif_parse_PartyAddMember(int fd, int party_id, struct party_member *member) { + struct party_data *p; + int i; + + p = inter_party_fromsql(party_id); + + if(!p || p->size == MAX_PARTY){ + mapif_party_memberadded(fd,party_id,member->account_id,member->char_id,1); + return 0; + } + + for(i=0;iparty.member[i].account_id) + continue; + + memcpy(&p->party.member[i], member, sizeof(struct party_member)); + p->party.member[i].leader=0; + if (p->party.member[i].online) p->party.count++; + p->size++; + if (p->size == 3) //Check family state. + int_party_calc_state(p); + else //Check even share range. + if (member->lv < p->min_lv || member->lv > p->max_lv || p->family) { + if (p->family) p->family = 0; //Family state broken. + int_party_check_lv(p); + } + + mapif_party_memberadded(fd,party_id,member->account_id,member->char_id,0); + mapif_party_info(-1,&p->party); + inter_party_tosql(&p->party, PS_ADDMEMBER, i); + return 0; + } + //Party full + mapif_party_memberadded(fd,party_id,member->account_id,member->char_id,1); + return 0; +} +// パーティー設定変更要求 +int mapif_parse_PartyChangeOption(int fd,int party_id,int account_id,int exp,int item) +{ + struct party_data *p; + int flag = 0; + p = inter_party_fromsql(party_id); + + if(!p) + return 0; + + p->party.exp=exp; + if( exp && !party_check_exp_share(p) ){ + flag|=0x01; + p->party.exp=0; + } + p->party.item = item&0x3; //Filter out invalid values. + mapif_party_optionchanged(fd,&p->party,account_id,flag); + inter_party_tosql(&p->party, PS_BASIC, 0); + return 0; +} +// パーティ脱退要求 +int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) +{ + struct party_data *p; + int i,j=-1; + + p = inter_party_fromsql(party_id); + if (!p) { //Party does not exists? + sprintf(tmp_sql, "UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d'", char_db, party_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + return 0; + } + + for (i = 0; i < MAX_PARTY; i++) { + if(p->party.member[i].account_id == account_id && + p->party.member[i].char_id == char_id) { + break; + } + } + if (i >= MAX_PARTY) + return 0; //Member not found? + + mapif_party_leaved(party_id, account_id, char_id); + + if (p->party.member[i].leader){ + p->party.member[i].account_id = 0; + for (j = 0; j < MAX_PARTY; j++) { + if (!p->party.member[j].account_id) + continue; + mapif_party_leaved(party_id, p->party.member[j].account_id, p->party.member[j].char_id); + p->party.member[j].account_id = 0; + } + //Party gets deleted on the check_empty call below. + } else { + inter_party_tosql(&p->party,PS_DELMEMBER,i); + j = p->party.member[i].lv; + if(p->party.member[i].online) p->party.count--; + memset(&p->party.member[i], 0, sizeof(struct party_member)); + p->size--; + if (j == p->min_lv || j == p->max_lv || p->family) + { + if(p->family) p->family = 0; //Family state broken. + int_party_check_lv(p); + } + } + + if (party_check_empty(p) == 0) + mapif_party_info(-1,&p->party); + return 0; +} +// When member goes to other map +int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, int char_id, unsigned short map, int online, unsigned int lv) +{ + struct party_data *p; + int i; + + p = inter_party_fromsql(party_id); + if (p == NULL) + return 0; + + for(i = 0; i < MAX_PARTY; i++) { + if(p->party.member[i].account_id == account_id && + p->party.member[i].char_id == char_id) + { + p->party.member[i].map = map; + if (p->party.member[i].online != online) + { + p->party.member[i].online = online; + if (online) + p->party.count++; + else + p->party.count--; + // Even share check situations: Family state (always breaks) + // character logging on/off is max/min level (update level range) + // or character logging on/off has a different level (update level range using new level) + if (p->family || + (p->party.member[i].lv <= p->min_lv || p->party.member[i].lv >= p->max_lv) || + (p->party.member[i].lv != lv && (lv <= p->min_lv || lv >= p->max_lv)) + ) + { + p->party.member[i].lv = lv; + int_party_check_lv(p); + } + } + if (p->party.member[i].lv != lv) { + if(p->party.member[i].lv == p->min_lv || + p->party.member[i].lv == p->max_lv) + { + p->party.member[i].lv = lv; + int_party_check_lv(p); + } else + p->party.member[i].lv = lv; + } + mapif_party_membermoved(&p->party, i); + break; + } + } + return 0; +} +// パーティ解散要求 +int mapif_parse_BreakParty(int fd,int party_id) +{ + struct party_data *p; + + p = inter_party_fromsql(party_id); + + if(!p) + return 0; + inter_party_tosql(&p->party,PS_BREAK,0); + mapif_party_broken(fd,party_id); + return 0; +} +// パーティメッセージ送信 +int mapif_parse_PartyMessage(int fd,int party_id,int account_id,char *mes,int len) +{ + return mapif_party_message(party_id,account_id,mes,len, fd); +} +// パーティチェック要求 +int mapif_parse_PartyCheck(int fd,int party_id,int account_id,int char_id) +{ + return party_check_conflict(party_id,account_id,char_id); +} + +int mapif_parse_PartyLeaderChange(int fd,int party_id,int account_id,int char_id) +{ + struct party_data *p; + int i; + + p = inter_party_fromsql(party_id); + + if(!p) + return 0; + + for (i = 0; i < MAX_PARTY; i++) + { + if(p->party.member[i].leader) + p->party.member[i].leader = 0; + if(p->party.member[i].account_id == account_id && + p->party.member[i].char_id == char_id) + { + p->party.member[i].leader = 1; + inter_party_tosql(&p->party,PS_LEADER, i); + } + } + return 1; +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_party_parse_frommap(int fd) +{ + RFIFOHEAD(fd); + switch(RFIFOW(fd,0)) { + case 0x3020: mapif_parse_CreateParty(fd, (char*)RFIFOP(fd,4), RFIFOB(fd,28), RFIFOB(fd,29), (struct party_member*)RFIFOP(fd,30)); break; + case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2)); break; + case 0x3022: mapif_parse_PartyAddMember(fd, RFIFOL(fd,4), (struct party_member*)RFIFOP(fd,8)); break; + case 0x3023: mapif_parse_PartyChangeOption(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOW(fd,10), RFIFOW(fd,12)); break; + case 0x3024: mapif_parse_PartyLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; + case 0x3025: mapif_parse_PartyChangeMap(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOW(fd,14), RFIFOB(fd,16), RFIFOW(fd,17)); break; + case 0x3026: mapif_parse_BreakParty(fd, RFIFOL(fd,2)); break; + case 0x3027: mapif_parse_PartyMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), (char*)RFIFOP(fd,12), RFIFOW(fd,2)-12); break; + case 0x3028: mapif_parse_PartyCheck(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; + case 0x3029: mapif_parse_PartyLeaderChange(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; + default: + return 0; + } + return 1; +} + +// サーバーから脱退要求(キャラ削除用) +int inter_party_leave(int party_id,int account_id, int char_id) +{ + return mapif_parse_PartyLeave(-1,party_id,account_id, char_id); +} + +int inter_party_CharOnline(int char_id, int party_id) { + struct party_data *p; + int i; + + if (party_id == -1) { + //Get party_id from the database + sprintf (tmp_sql , "SELECT party_id FROM `%s` WHERE char_id='%d'",char_db,char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + + sql_res = mysql_store_result(&mysql_handle) ; + if(sql_res == NULL) + return 0; //Eh? No party? + + sql_row = mysql_fetch_row(sql_res); + party_id = sql_row?atoi(sql_row[0]):0; + mysql_free_result(sql_res); + } + if (party_id == 0) + return 0; //No party... + + p = inter_party_fromsql(party_id); + if(!p) { + ShowError("Character %d's party %d not found!\n", char_id, party_id); + return 0; + } + + //Set member online + for(i=0; iparty.member[i].char_id == char_id) { + if (!p->party.member[i].online) { + p->party.member[i].online = 1; + p->party.count++; + if (p->party.member[i].lv < p->min_lv || + p->party.member[i].lv > p->max_lv) + int_party_check_lv(p); + } + break; + } + } + return 1; +} + +int inter_party_CharOffline(int char_id, int party_id) { + struct party_data *p=NULL; + int i; + + if (party_id == -1) { + //Get guild_id from the database + sprintf (tmp_sql , "SELECT party_id FROM `%s` WHERE char_id='%d'",char_db,char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + + sql_res = mysql_store_result(&mysql_handle) ; + if(sql_res == NULL) + return 0; //Eh? No party? + + sql_row = mysql_fetch_row(sql_res); + party_id = sql_row?atoi(sql_row[0]):0; + mysql_free_result(sql_res); + } + if (party_id == 0) + return 0; //No party... + + //Character has a party, set character offline and check if they were the only member online + if ((p = inter_party_fromsql(party_id)) == NULL) + return 0; + + //Set member offline + for(i=0; i< MAX_PARTY; i++) { + if(p->party.member[i].char_id == char_id) + { + p->party.member[i].online = 0; + p->party.count--; + if(p->party.member[i].lv == p->min_lv || + p->party.member[i].lv == p->max_lv) + int_party_check_lv(p); + break; + } + } + + if(!p->party.count) + //Parties don't have any data that needs be saved at this point... so just remove it from memory. + idb_remove(party_db_, party_id); + return 1; +} +#endif //TXT_SQL_CONVERT diff --git a/src/char_sql/int_party.h b/src/char_sql/int_party.h index 7e6219f9c..4691447e9 100644 --- a/src/char_sql/int_party.h +++ b/src/char_sql/int_party.h @@ -1,30 +1,30 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INT_PARTY_SQL_H_ -#define _INT_PARTY_SQL_H_ - -//Party Flags on what to save/delete. -//Create a new party entry (index holds leader's info) -#define PS_CREATE 0x01 -//Update basic party info. -#define PS_BASIC 0x02 -//Update party's leader -#define PS_LEADER 0x04 -//Specify new party member (index specifies which party member) -#define PS_ADDMEMBER 0x08 -//Specify member that left (index specifies which party member) -#define PS_DELMEMBER 0x10 -//Specify that this party must be deleted. -#define PS_BREAK 0x20 - -int inter_party_parse_frommap(int fd); -int inter_party_sql_init(void); -void inter_party_sql_final(void); -int inter_party_leave(int party_id,int account_id, int char_id); -int inter_party_logged(int party_id, int account_id, int char_id); -int inter_party_CharOnline(int char_id, int party_id); -int inter_party_CharOffline(int char_id, int party_id); -//Required for the TXT->SQL converter -int inter_party_tosql(struct party *p, int flag, int index); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INT_PARTY_SQL_H_ +#define _INT_PARTY_SQL_H_ + +//Party Flags on what to save/delete. +//Create a new party entry (index holds leader's info) +#define PS_CREATE 0x01 +//Update basic party info. +#define PS_BASIC 0x02 +//Update party's leader +#define PS_LEADER 0x04 +//Specify new party member (index specifies which party member) +#define PS_ADDMEMBER 0x08 +//Specify member that left (index specifies which party member) +#define PS_DELMEMBER 0x10 +//Specify that this party must be deleted. +#define PS_BREAK 0x20 + +int inter_party_parse_frommap(int fd); +int inter_party_sql_init(void); +void inter_party_sql_final(void); +int inter_party_leave(int party_id,int account_id, int char_id); +int inter_party_logged(int party_id, int account_id, int char_id); +int inter_party_CharOnline(int char_id, int party_id); +int inter_party_CharOffline(int char_id, int party_id); +//Required for the TXT->SQL converter +int inter_party_tosql(struct party *p, int flag, int index); +#endif diff --git a/src/char_sql/int_pet.c b/src/char_sql/int_pet.c index 69eb4ec02..ff4f0edb9 100644 --- a/src/char_sql/int_pet.c +++ b/src/char_sql/int_pet.c @@ -1,372 +1,372 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -// original code from athena -// SQL conversion by Jioh L. Jung - -#include -#include -#include - -#include "char.h" -#include "../common/strlib.h" -#include "../common/showmsg.h" - -struct s_pet *pet_pt; - -#ifndef SQL_DEBUG - -#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] - -#else - -#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) - -#endif - -//--------------------------------------------------------- -int inter_pet_tosql(int pet_id, struct s_pet *p) { - //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`) - char t_name[NAME_LENGTH*2]; - - jstrescapecpy(t_name, p->name); - - if(p->hungry < 0) - p->hungry = 0; - else if(p->hungry > 100) - p->hungry = 100; - if(p->intimate < 0) - p->intimate = 0; - else if(p->intimate > 1000) - p->intimate = 1000; - if (pet_id == -1) //New pet. - sprintf(tmp_sql,"INSERT INTO `%s` " - "(`class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`) " - "VALUES ('%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - pet_db, p->class_, t_name, p->account_id, p->char_id, p->level, p->egg_id, - p->equip, p->intimate, p->hungry, p->rename_flag, p->incuvate); - - else //Update pet. - sprintf(tmp_sql, "UPDATE `%s` SET `class`='%d',`name`='%s',`account_id`='%d',`char_id`='%d',`level`='%d',`egg_id`='%d',`equip`='%d',`intimate`='%d',`hungry`='%d',`rename_flag`='%d',`incuvate`='%d' WHERE `pet_id`='%d'", - pet_db, p->class_, t_name, p->account_id, p->char_id, p->level, p->egg_id, - p->equip, p->intimate, p->hungry, p->rename_flag, p->incuvate, p->pet_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } else if (pet_id == -1) { //New pet inserted. - if(mysql_field_count(&mysql_handle) == 0 && - mysql_insert_id(&mysql_handle) != 0) { - p->pet_id = pet_id = (int)mysql_insert_id(&mysql_handle); - } else { - ShowError("inter_pet_tosql: Failed to retrieve new pet_id for '%s'. Pet creation aborted.\n", p->name); - return 0; - } - } - - if (save_log) - ShowInfo("Pet saved %d - %s.\n", pet_id, p->name); - return 1; -} -#ifndef TXT_SQL_CONVERT -int inter_pet_fromsql(int pet_id, struct s_pet *p){ - -#ifdef NOISY - ShowInfo("Loading pet (%d)...\n",pet_id); -#endif - memset(p, 0, sizeof(struct s_pet)); - - //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`) - - sprintf(tmp_sql,"SELECT `pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate` FROM `%s` WHERE `pet_id`='%d'",pet_db, pet_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res!=NULL && mysql_num_rows(sql_res)>0) { - sql_row = mysql_fetch_row(sql_res); - - p->pet_id = pet_id; - p->class_ = atoi(sql_row[1]); - memcpy(p->name, sql_row[2],NAME_LENGTH-1); - p->account_id = atoi(sql_row[3]); - p->char_id = atoi(sql_row[4]); - p->level = atoi(sql_row[5]); - p->egg_id = atoi(sql_row[6]); - p->equip = atoi(sql_row[7]); - p->intimate = atoi(sql_row[8]); - p->hungry = atoi(sql_row[9]); - p->rename_flag = atoi(sql_row[10]); - p->incuvate = atoi(sql_row[11]); - } - if(p->hungry < 0) - p->hungry = 0; - else if(p->hungry > 100) - p->hungry = 100; - if(p->intimate < 0) - p->intimate = 0; - else if(p->intimate > 1000) - p->intimate = 1000; - - mysql_free_result(sql_res); - - if (save_log) - ShowInfo("Pet loaded (%d - %s).\n", pet_id, p->name); - return 0; -} -//---------------------------------------------- - -int inter_pet_sql_init(void){ - //memory alloc - pet_pt = (struct s_pet*)aCalloc(sizeof(struct s_pet), 1); - return 0; -} -void inter_pet_sql_final(void){ - if (pet_pt) aFree(pet_pt); - return; -} -//---------------------------------- -int inter_pet_delete(int pet_id){ - ShowInfo("delete pet request: %d...\n",pet_id); - - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `pet_id`='%d'",pet_db, pet_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - return 0; -} -//------------------------------------------------------ -int mapif_pet_created(int fd, int account_id, struct s_pet *p) -{ - WFIFOHEAD(fd, 11); - WFIFOW(fd, 0) =0x3880; - WFIFOL(fd, 2) =account_id; - if(p!=NULL){ - WFIFOB(fd, 6)=0; - WFIFOL(fd, 7) =p->pet_id; - ShowInfo("int_pet: created pet %d - %s\n", p->pet_id, p->name); - }else{ - WFIFOB(fd, 6)=1; - WFIFOL(fd, 7)=0; - } - WFIFOSET(fd, 11); - - return 0; -} - -int mapif_pet_info(int fd, int account_id, struct s_pet *p){ - WFIFOHEAD(fd, sizeof(struct s_pet) + 9); - WFIFOW(fd, 0) =0x3881; - WFIFOW(fd, 2) =sizeof(struct s_pet) + 9; - WFIFOL(fd, 4) =account_id; - WFIFOB(fd, 8)=0; - memcpy(WFIFOP(fd, 9), p, sizeof(struct s_pet)); - WFIFOSET(fd, WFIFOW(fd, 2)); - - return 0; -} - -int mapif_pet_noinfo(int fd, int account_id){ - WFIFOHEAD(fd, sizeof(struct s_pet) + 9); - WFIFOW(fd, 0) =0x3881; - WFIFOW(fd, 2) =sizeof(struct s_pet) + 9; - WFIFOL(fd, 4) =account_id; - WFIFOB(fd, 8)=1; - memset(WFIFOP(fd, 9), 0, sizeof(struct s_pet)); - WFIFOSET(fd, WFIFOW(fd, 2)); - - return 0; -} - -int mapif_save_pet_ack(int fd, int account_id, int flag){ - WFIFOHEAD(fd, 7); - WFIFOW(fd, 0) =0x3882; - WFIFOL(fd, 2) =account_id; - WFIFOB(fd, 6) =flag; - WFIFOSET(fd, 7); - - return 0; -} - -int mapif_delete_pet_ack(int fd, int flag){ - WFIFOHEAD(fd, 3); - WFIFOW(fd, 0) =0x3883; - WFIFOB(fd, 2) =flag; - WFIFOSET(fd, 3); - - return 0; -} - -int mapif_rename_pet_ack(int fd, int account_id, int char_id, int flag, char *name){ - WFIFOHEAD(fd, NAME_LENGTH+12); - WFIFOW(fd, 0) =0x3884; - WFIFOL(fd, 2) =account_id; - WFIFOL(fd, 6) =char_id; - WFIFOB(fd, 10) =flag; - memcpy(WFIFOP(fd, 11), name, NAME_LENGTH); - WFIFOSET(fd, NAME_LENGTH+12); - - return 0; -} - - -int mapif_create_pet(int fd, int account_id, int char_id, short pet_class, short pet_lv, short pet_egg_id, - short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name){ - - memset(pet_pt, 0, sizeof(struct s_pet)); - memcpy(pet_pt->name, pet_name, NAME_LENGTH-1); - if(incuvate == 1) - pet_pt->account_id = pet_pt->char_id = 0; - else { - pet_pt->account_id = account_id; - pet_pt->char_id = char_id; - } - pet_pt->class_ = pet_class; - pet_pt->level = pet_lv; - pet_pt->egg_id = pet_egg_id; - pet_pt->equip = pet_equip; - pet_pt->intimate = intimate; - pet_pt->hungry = hungry; - pet_pt->rename_flag = rename_flag; - pet_pt->incuvate = incuvate; - - if(pet_pt->hungry < 0) - pet_pt->hungry = 0; - else if(pet_pt->hungry > 100) - pet_pt->hungry = 100; - if(pet_pt->intimate < 0) - pet_pt->intimate = 0; - else if(pet_pt->intimate > 1000) - pet_pt->intimate = 1000; - - pet_pt->pet_id = -1; //Signal NEW pet. - if (inter_pet_tosql(pet_pt->pet_id,pet_pt)) - mapif_pet_created(fd, account_id, pet_pt); - else //Failed... - mapif_pet_created(fd, account_id, NULL); - - return 0; -} - -int mapif_load_pet(int fd, int account_id, int char_id, int pet_id){ - memset(pet_pt, 0, sizeof(struct s_pet)); - - inter_pet_fromsql(pet_id, pet_pt); - - if(pet_pt!=NULL) { - if(pet_pt->incuvate == 1) { - pet_pt->account_id = pet_pt->char_id = 0; - mapif_pet_info(fd, account_id, pet_pt); - } - else if(account_id == pet_pt->account_id && char_id == pet_pt->char_id) - mapif_pet_info(fd, account_id, pet_pt); - else - mapif_pet_noinfo(fd, account_id); - } - else - mapif_pet_noinfo(fd, account_id); - - return 0; -} - -int mapif_save_pet(int fd, int account_id, struct s_pet *data) { - //here process pet save request. - int len; - RFIFOHEAD(fd); - len=RFIFOW(fd, 2); - if(sizeof(struct s_pet)!=len-8) { - ShowError("inter pet: data size error %d %d\n", sizeof(struct s_pet), len-8); - } - - else{ - if(data->hungry < 0) - data->hungry = 0; - else if(data->hungry > 100) - data->hungry = 100; - if(data->intimate < 0) - data->intimate = 0; - else if(data->intimate > 1000) - data->intimate = 1000; - inter_pet_tosql(data->pet_id,data); - mapif_save_pet_ack(fd, account_id, 0); - } - - return 0; -} - -int mapif_delete_pet(int fd, int pet_id){ - mapif_delete_pet_ack(fd, inter_pet_delete(pet_id)); - - return 0; -} - -int mapif_rename_pet(int fd, int account_id, int char_id, char *name){ - int i; - - // Check Authorised letters/symbols in the name of the pet - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) { - mapif_rename_pet_ack(fd, account_id, char_id, 0, name); - return 0; - } - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) { - mapif_rename_pet_ack(fd, account_id, char_id, 0, name); - return 0; - } - } - - mapif_rename_pet_ack(fd, account_id, char_id, 1, name); - return 0; -} - -int mapif_parse_CreatePet(int fd){ - RFIFOHEAD(fd); - mapif_create_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOW(fd, 10), RFIFOW(fd, 12), RFIFOW(fd, 14), RFIFOW(fd, 16), RFIFOW(fd, 18), - RFIFOW(fd, 20), RFIFOB(fd, 22), RFIFOB(fd, 23), (char*)RFIFOP(fd, 24)); - return 0; -} - -int mapif_parse_LoadPet(int fd){ - RFIFOHEAD(fd); - mapif_load_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOL(fd, 10)); - return 0; -} - -int mapif_parse_SavePet(int fd){ - RFIFOHEAD(fd); - mapif_save_pet(fd, RFIFOL(fd, 4), (struct s_pet *) RFIFOP(fd, 8)); - return 0; -} - -int mapif_parse_DeletePet(int fd){ - RFIFOHEAD(fd); - mapif_delete_pet(fd, RFIFOL(fd, 2)); - return 0; -} - -int mapif_parse_RenamePet(int fd){ - RFIFOHEAD(fd); - mapif_rename_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOP(fd, 10)); - return 0; -} - -int inter_pet_parse_frommap(int fd){ - RFIFOHEAD(fd); - switch(RFIFOW(fd, 0)){ - case 0x3080: mapif_parse_CreatePet(fd); break; - case 0x3081: mapif_parse_LoadPet(fd); break; - case 0x3082: mapif_parse_SavePet(fd); break; - case 0x3083: mapif_parse_DeletePet(fd); break; - case 0x3084: mapif_parse_RenamePet(fd); break; - default: - return 0; - } - return 1; -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +// original code from athena +// SQL conversion by Jioh L. Jung + +#include +#include +#include + +#include "char.h" +#include "../common/strlib.h" +#include "../common/showmsg.h" + +struct s_pet *pet_pt; + +#ifndef SQL_DEBUG + +#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] + +#else + +#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) + +#endif + +//--------------------------------------------------------- +int inter_pet_tosql(int pet_id, struct s_pet *p) { + //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`) + char t_name[NAME_LENGTH*2]; + + jstrescapecpy(t_name, p->name); + + if(p->hungry < 0) + p->hungry = 0; + else if(p->hungry > 100) + p->hungry = 100; + if(p->intimate < 0) + p->intimate = 0; + else if(p->intimate > 1000) + p->intimate = 1000; + if (pet_id == -1) //New pet. + sprintf(tmp_sql,"INSERT INTO `%s` " + "(`class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`) " + "VALUES ('%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + pet_db, p->class_, t_name, p->account_id, p->char_id, p->level, p->egg_id, + p->equip, p->intimate, p->hungry, p->rename_flag, p->incuvate); + + else //Update pet. + sprintf(tmp_sql, "UPDATE `%s` SET `class`='%d',`name`='%s',`account_id`='%d',`char_id`='%d',`level`='%d',`egg_id`='%d',`equip`='%d',`intimate`='%d',`hungry`='%d',`rename_flag`='%d',`incuvate`='%d' WHERE `pet_id`='%d'", + pet_db, p->class_, t_name, p->account_id, p->char_id, p->level, p->egg_id, + p->equip, p->intimate, p->hungry, p->rename_flag, p->incuvate, p->pet_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } else if (pet_id == -1) { //New pet inserted. + if(mysql_field_count(&mysql_handle) == 0 && + mysql_insert_id(&mysql_handle) != 0) { + p->pet_id = pet_id = (int)mysql_insert_id(&mysql_handle); + } else { + ShowError("inter_pet_tosql: Failed to retrieve new pet_id for '%s'. Pet creation aborted.\n", p->name); + return 0; + } + } + + if (save_log) + ShowInfo("Pet saved %d - %s.\n", pet_id, p->name); + return 1; +} +#ifndef TXT_SQL_CONVERT +int inter_pet_fromsql(int pet_id, struct s_pet *p){ + +#ifdef NOISY + ShowInfo("Loading pet (%d)...\n",pet_id); +#endif + memset(p, 0, sizeof(struct s_pet)); + + //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate`) + + sprintf(tmp_sql,"SELECT `pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incuvate` FROM `%s` WHERE `pet_id`='%d'",pet_db, pet_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res!=NULL && mysql_num_rows(sql_res)>0) { + sql_row = mysql_fetch_row(sql_res); + + p->pet_id = pet_id; + p->class_ = atoi(sql_row[1]); + memcpy(p->name, sql_row[2],NAME_LENGTH-1); + p->account_id = atoi(sql_row[3]); + p->char_id = atoi(sql_row[4]); + p->level = atoi(sql_row[5]); + p->egg_id = atoi(sql_row[6]); + p->equip = atoi(sql_row[7]); + p->intimate = atoi(sql_row[8]); + p->hungry = atoi(sql_row[9]); + p->rename_flag = atoi(sql_row[10]); + p->incuvate = atoi(sql_row[11]); + } + if(p->hungry < 0) + p->hungry = 0; + else if(p->hungry > 100) + p->hungry = 100; + if(p->intimate < 0) + p->intimate = 0; + else if(p->intimate > 1000) + p->intimate = 1000; + + mysql_free_result(sql_res); + + if (save_log) + ShowInfo("Pet loaded (%d - %s).\n", pet_id, p->name); + return 0; +} +//---------------------------------------------- + +int inter_pet_sql_init(void){ + //memory alloc + pet_pt = (struct s_pet*)aCalloc(sizeof(struct s_pet), 1); + return 0; +} +void inter_pet_sql_final(void){ + if (pet_pt) aFree(pet_pt); + return; +} +//---------------------------------- +int inter_pet_delete(int pet_id){ + ShowInfo("delete pet request: %d...\n",pet_id); + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `pet_id`='%d'",pet_db, pet_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + return 0; +} +//------------------------------------------------------ +int mapif_pet_created(int fd, int account_id, struct s_pet *p) +{ + WFIFOHEAD(fd, 11); + WFIFOW(fd, 0) =0x3880; + WFIFOL(fd, 2) =account_id; + if(p!=NULL){ + WFIFOB(fd, 6)=0; + WFIFOL(fd, 7) =p->pet_id; + ShowInfo("int_pet: created pet %d - %s\n", p->pet_id, p->name); + }else{ + WFIFOB(fd, 6)=1; + WFIFOL(fd, 7)=0; + } + WFIFOSET(fd, 11); + + return 0; +} + +int mapif_pet_info(int fd, int account_id, struct s_pet *p){ + WFIFOHEAD(fd, sizeof(struct s_pet) + 9); + WFIFOW(fd, 0) =0x3881; + WFIFOW(fd, 2) =sizeof(struct s_pet) + 9; + WFIFOL(fd, 4) =account_id; + WFIFOB(fd, 8)=0; + memcpy(WFIFOP(fd, 9), p, sizeof(struct s_pet)); + WFIFOSET(fd, WFIFOW(fd, 2)); + + return 0; +} + +int mapif_pet_noinfo(int fd, int account_id){ + WFIFOHEAD(fd, sizeof(struct s_pet) + 9); + WFIFOW(fd, 0) =0x3881; + WFIFOW(fd, 2) =sizeof(struct s_pet) + 9; + WFIFOL(fd, 4) =account_id; + WFIFOB(fd, 8)=1; + memset(WFIFOP(fd, 9), 0, sizeof(struct s_pet)); + WFIFOSET(fd, WFIFOW(fd, 2)); + + return 0; +} + +int mapif_save_pet_ack(int fd, int account_id, int flag){ + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) =0x3882; + WFIFOL(fd, 2) =account_id; + WFIFOB(fd, 6) =flag; + WFIFOSET(fd, 7); + + return 0; +} + +int mapif_delete_pet_ack(int fd, int flag){ + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) =0x3883; + WFIFOB(fd, 2) =flag; + WFIFOSET(fd, 3); + + return 0; +} + +int mapif_rename_pet_ack(int fd, int account_id, int char_id, int flag, char *name){ + WFIFOHEAD(fd, NAME_LENGTH+12); + WFIFOW(fd, 0) =0x3884; + WFIFOL(fd, 2) =account_id; + WFIFOL(fd, 6) =char_id; + WFIFOB(fd, 10) =flag; + memcpy(WFIFOP(fd, 11), name, NAME_LENGTH); + WFIFOSET(fd, NAME_LENGTH+12); + + return 0; +} + + +int mapif_create_pet(int fd, int account_id, int char_id, short pet_class, short pet_lv, short pet_egg_id, + short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name){ + + memset(pet_pt, 0, sizeof(struct s_pet)); + memcpy(pet_pt->name, pet_name, NAME_LENGTH-1); + if(incuvate == 1) + pet_pt->account_id = pet_pt->char_id = 0; + else { + pet_pt->account_id = account_id; + pet_pt->char_id = char_id; + } + pet_pt->class_ = pet_class; + pet_pt->level = pet_lv; + pet_pt->egg_id = pet_egg_id; + pet_pt->equip = pet_equip; + pet_pt->intimate = intimate; + pet_pt->hungry = hungry; + pet_pt->rename_flag = rename_flag; + pet_pt->incuvate = incuvate; + + if(pet_pt->hungry < 0) + pet_pt->hungry = 0; + else if(pet_pt->hungry > 100) + pet_pt->hungry = 100; + if(pet_pt->intimate < 0) + pet_pt->intimate = 0; + else if(pet_pt->intimate > 1000) + pet_pt->intimate = 1000; + + pet_pt->pet_id = -1; //Signal NEW pet. + if (inter_pet_tosql(pet_pt->pet_id,pet_pt)) + mapif_pet_created(fd, account_id, pet_pt); + else //Failed... + mapif_pet_created(fd, account_id, NULL); + + return 0; +} + +int mapif_load_pet(int fd, int account_id, int char_id, int pet_id){ + memset(pet_pt, 0, sizeof(struct s_pet)); + + inter_pet_fromsql(pet_id, pet_pt); + + if(pet_pt!=NULL) { + if(pet_pt->incuvate == 1) { + pet_pt->account_id = pet_pt->char_id = 0; + mapif_pet_info(fd, account_id, pet_pt); + } + else if(account_id == pet_pt->account_id && char_id == pet_pt->char_id) + mapif_pet_info(fd, account_id, pet_pt); + else + mapif_pet_noinfo(fd, account_id); + } + else + mapif_pet_noinfo(fd, account_id); + + return 0; +} + +int mapif_save_pet(int fd, int account_id, struct s_pet *data) { + //here process pet save request. + int len; + RFIFOHEAD(fd); + len=RFIFOW(fd, 2); + if(sizeof(struct s_pet)!=len-8) { + ShowError("inter pet: data size error %d %d\n", sizeof(struct s_pet), len-8); + } + + else{ + if(data->hungry < 0) + data->hungry = 0; + else if(data->hungry > 100) + data->hungry = 100; + if(data->intimate < 0) + data->intimate = 0; + else if(data->intimate > 1000) + data->intimate = 1000; + inter_pet_tosql(data->pet_id,data); + mapif_save_pet_ack(fd, account_id, 0); + } + + return 0; +} + +int mapif_delete_pet(int fd, int pet_id){ + mapif_delete_pet_ack(fd, inter_pet_delete(pet_id)); + + return 0; +} + +int mapif_rename_pet(int fd, int account_id, int char_id, char *name){ + int i; + + // Check Authorised letters/symbols in the name of the pet + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) == NULL) { + mapif_rename_pet_ack(fd, account_id, char_id, 0, name); + return 0; + } + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < NAME_LENGTH && name[i]; i++) + if (strchr(char_name_letters, name[i]) != NULL) { + mapif_rename_pet_ack(fd, account_id, char_id, 0, name); + return 0; + } + } + + mapif_rename_pet_ack(fd, account_id, char_id, 1, name); + return 0; +} + +int mapif_parse_CreatePet(int fd){ + RFIFOHEAD(fd); + mapif_create_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOW(fd, 10), RFIFOW(fd, 12), RFIFOW(fd, 14), RFIFOW(fd, 16), RFIFOW(fd, 18), + RFIFOW(fd, 20), RFIFOB(fd, 22), RFIFOB(fd, 23), (char*)RFIFOP(fd, 24)); + return 0; +} + +int mapif_parse_LoadPet(int fd){ + RFIFOHEAD(fd); + mapif_load_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOL(fd, 10)); + return 0; +} + +int mapif_parse_SavePet(int fd){ + RFIFOHEAD(fd); + mapif_save_pet(fd, RFIFOL(fd, 4), (struct s_pet *) RFIFOP(fd, 8)); + return 0; +} + +int mapif_parse_DeletePet(int fd){ + RFIFOHEAD(fd); + mapif_delete_pet(fd, RFIFOL(fd, 2)); + return 0; +} + +int mapif_parse_RenamePet(int fd){ + RFIFOHEAD(fd); + mapif_rename_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOP(fd, 10)); + return 0; +} + +int inter_pet_parse_frommap(int fd){ + RFIFOHEAD(fd); + switch(RFIFOW(fd, 0)){ + case 0x3080: mapif_parse_CreatePet(fd); break; + case 0x3081: mapif_parse_LoadPet(fd); break; + case 0x3082: mapif_parse_SavePet(fd); break; + case 0x3083: mapif_parse_DeletePet(fd); break; + case 0x3084: mapif_parse_RenamePet(fd); break; + default: + return 0; + } + return 1; +} +#endif //TXT_SQL_CONVERT diff --git a/src/char_sql/int_pet.h b/src/char_sql/int_pet.h index fa5cbac0d..1b03c9768 100644 --- a/src/char_sql/int_pet.h +++ b/src/char_sql/int_pet.h @@ -1,18 +1,18 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INT_PET_SQL_H_ -#define _INT_PET_SQL_H_ - -int inter_pet_init(void); -void inter_pet_sql_final(void); -int inter_pet_save(void); -int inter_pet_delete(int pet_id); - -int inter_pet_parse_frommap(int fd); -int inter_pet_sql_init(void); -//extern char pet_txt[256]; - -//Exported for use in the TXT-SQL converter. -int inter_pet_tosql(int pet_id, struct s_pet *p); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INT_PET_SQL_H_ +#define _INT_PET_SQL_H_ + +int inter_pet_init(void); +void inter_pet_sql_final(void); +int inter_pet_save(void); +int inter_pet_delete(int pet_id); + +int inter_pet_parse_frommap(int fd); +int inter_pet_sql_init(void); +//extern char pet_txt[256]; + +//Exported for use in the TXT-SQL converter. +int inter_pet_tosql(int pet_id, struct s_pet *p); +#endif diff --git a/src/char_sql/int_storage.c b/src/char_sql/int_storage.c index 578de6bc8..5ca2c25bb 100644 --- a/src/char_sql/int_storage.c +++ b/src/char_sql/int_storage.c @@ -1,379 +1,379 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -// original code from athena -// SQL conversion by Jioh L. Jung - -#include -#include - -#include "char.h" -#include "itemdb.h" -#include "../common/showmsg.h" - -#define STORAGE_MEMINC 16 - -#ifndef TXT_SQL_CONVERT -// reset by inter_config_read() -struct storage *storage_pt=NULL; -struct guild_storage *guild_storage_pt=NULL; - -#ifndef SQL_DEBUG - -#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] - -#else - -#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) - -#endif - -#endif //TXT_SQL_CONVERT -// storage data -> DB conversion -int storage_tosql(int account_id,struct storage *p){ - int i,j; -// int eqcount=1; -// int noteqcount=1; - int count=0; - struct itemtmp mapitem[MAX_STORAGE]; - for(i=0;istorage_[i].nameid>0){ - mapitem[count].flag=0; - mapitem[count].id = p->storage_[i].id; - mapitem[count].nameid=p->storage_[i].nameid; - mapitem[count].amount = p->storage_[i].amount; - mapitem[count].equip = p->storage_[i].equip; - mapitem[count].identify = p->storage_[i].identify; - mapitem[count].refine = p->storage_[i].refine; - mapitem[count].attribute = p->storage_[i].attribute; - for(j=0; jstorage_[i].card[j]; - count++; - } - } - - memitemdata_to_sql(mapitem, count, account_id,TABLE_STORAGE); - - //printf ("storage dump to DB - id: %d (total: %d)\n", account_id, j); - return 0; -} -#ifndef TXT_SQL_CONVERT - -// DB -> storage data conversion -int storage_fromsql(int account_id, struct storage *p){ - int i=0,j; - char * str_p = tmp_sql; - - memset(p,0,sizeof(struct storage)); //clean up memory - p->storage_amount = 0; - p->account_id = account_id; - - // storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} - str_p += sprintf(str_p,"SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`"); - - for (j=0; jstorage_[i].id= atoi(sql_row[0]); - p->storage_[i].nameid= atoi(sql_row[1]); - p->storage_[i].amount= atoi(sql_row[2]); - p->storage_[i].equip= atoi(sql_row[3]); - p->storage_[i].identify= atoi(sql_row[4]); - p->storage_[i].refine= atoi(sql_row[5]); - p->storage_[i].attribute= atoi(sql_row[6]); - for (j=0; jstorage_[i].card[j]= atoi(sql_row[7+j]); - i++; - } - p->storage_amount = i; - mysql_free_result(sql_res); - } - - ShowInfo ("storage load complete from DB - id: %d (total: %d)\n", account_id, p->storage_amount); - return 1; -} -#endif //TXT_SQL_CONVERT -// Save guild_storage data to sql -int guild_storage_tosql(int guild_id, struct guild_storage *p){ - int i,j; -// int eqcount=1; -// int noteqcount=1; - int count=0; - struct itemtmp mapitem[MAX_GUILD_STORAGE]; - for(i=0;istorage_[i].nameid>0){ - mapitem[count].flag=0; - mapitem[count].id = p->storage_[i].id; - mapitem[count].nameid=p->storage_[i].nameid; - mapitem[count].amount = p->storage_[i].amount; - mapitem[count].equip = p->storage_[i].equip; - mapitem[count].identify = p->storage_[i].identify; - mapitem[count].refine = p->storage_[i].refine; - mapitem[count].attribute = p->storage_[i].attribute; - for (j=0; jstorage_[i].card[j]; - count++; - } - } - - memitemdata_to_sql(mapitem, count, guild_id,TABLE_GUILD_STORAGE); - - ShowInfo ("guild storage save to DB - id: %d (total: %d)\n", guild_id,i); - return 0; -} -#ifndef TXT_SQL_CONVERT -// Load guild_storage data to mem -int guild_storage_fromsql(int guild_id, struct guild_storage *p){ - int i=0,j; - struct guild_storage *gs=guild_storage_pt; - char * str_p = tmp_sql; - p=gs; - - memset(p,0,sizeof(struct guild_storage)); //clean up memory - p->storage_amount = 0; - p->guild_id = guild_id; - - // storage {`guild_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} - str_p += sprintf(str_p,"SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`"); - - for (j=0; jstorage_[i].id= atoi(sql_row[0]); - p->storage_[i].nameid= atoi(sql_row[1]); - p->storage_[i].amount= atoi(sql_row[2]); - p->storage_[i].equip= atoi(sql_row[3]); - p->storage_[i].identify= atoi(sql_row[4]); - p->storage_[i].refine= atoi(sql_row[5]); - p->storage_[i].attribute= atoi(sql_row[6]); - for (j=0; jstorage_[i].card[j] = atoi(sql_row[7+j]); - i++; - } - p->storage_amount = i; - mysql_free_result(sql_res); - } - ShowInfo ("guild storage load complete from DB - id: %d (total: %d)\n", guild_id, p->storage_amount); - return 0; -} - -//--------------------------------------------------------- -// storage data initialize -int inter_storage_sql_init(void){ - - //memory alloc - ShowDebug("interserver storage memory initialize....(%d byte)\n",sizeof(struct storage)); - storage_pt = (struct storage*)aCalloc(sizeof(struct storage), 1); - guild_storage_pt = (struct guild_storage*)aCalloc(sizeof(struct guild_storage), 1); -// memset(storage_pt,0,sizeof(struct storage)); //Calloc sets stuff to 0 already. [Skotlex] -// memset(guild_storage_pt,0,sizeof(struct guild_storage)); - - return 1; -} -// storage data finalize -void inter_storage_sql_final(void) -{ - if (storage_pt) aFree(storage_pt); - if (guild_storage_pt) aFree(guild_storage_pt); - return; -} -// q?f[^? -int inter_storage_delete(int account_id) -{ - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id`='%d'",storage_db, account_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - return 0; -} -int inter_guild_storage_delete(int guild_id) -{ - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_storage_db, guild_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - return 0; -} - -//--------------------------------------------------------- -// packet from map server - -// recive packet about storage data -int mapif_load_storage(int fd,int account_id){ - //load from DB - WFIFOHEAD(fd, sizeof(struct storage)+8); - storage_fromsql(account_id, storage_pt); - WFIFOW(fd,0)=0x3810; - WFIFOW(fd,2)=sizeof(struct storage)+8; - WFIFOL(fd,4)=account_id; - memcpy(WFIFOP(fd,8),storage_pt,sizeof(struct storage)); - WFIFOSET(fd,WFIFOW(fd,2)); - return 0; -} -// send ack to map server which is "storage data save ok." -int mapif_save_storage_ack(int fd,int account_id){ - WFIFOHEAD(fd, 7); - WFIFOW(fd,0)=0x3811; - WFIFOL(fd,2)=account_id; - WFIFOB(fd,6)=0; - WFIFOSET(fd,7); - return 0; -} - -int mapif_load_guild_storage(int fd,int account_id,int guild_id) -{ - int guild_exist=1; - WFIFOHEAD(fd, sizeof(struct guild_storage)+12); - WFIFOW(fd,0)=0x3818; - -#if 0 // innodb guilds should render this check unnecessary [Aru] - // Check if guild exists, I may write a function for this later, coz I use it several times. - //printf("- Check if guild %d exists\n",g->guild_id); - sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res!=NULL && mysql_num_rows(sql_res)>0) { - sql_row = mysql_fetch_row(sql_res); - guild_exist = atoi (sql_row[0]); - //printf("- Check if guild %d exists : %s\n",g->guild_id,((guild_exist==0)?"No":"Yes")); - } - mysql_free_result(sql_res) ; //resource free -#endif - if(guild_exist==1) { - guild_storage_fromsql(guild_id,guild_storage_pt); - WFIFOW(fd,2)=sizeof(struct guild_storage)+12; - WFIFOL(fd,4)=account_id; - WFIFOL(fd,8)=guild_id; - memcpy(WFIFOP(fd,12),guild_storage_pt,sizeof(struct guild_storage)); - } - else { - WFIFOW(fd,2)=12; - WFIFOL(fd,4)=account_id; - WFIFOL(fd,8)=0; - } - WFIFOSET(fd,WFIFOW(fd,2)); - - return 0; -} -int mapif_save_guild_storage_ack(int fd,int account_id,int guild_id,int fail) -{ - WFIFOHEAD(fd,11); - WFIFOW(fd,0)=0x3819; - WFIFOL(fd,2)=account_id; - WFIFOL(fd,6)=guild_id; - WFIFOB(fd,10)=fail; - WFIFOSET(fd,11); - return 0; -} - -//--------------------------------------------------------- -// packet from map server - -// recive request about storage data -int mapif_parse_LoadStorage(int fd){ - RFIFOHEAD(fd); - mapif_load_storage(fd,RFIFOL(fd,2)); - return 0; -} -// storage data recive and save -int mapif_parse_SaveStorage(int fd){ - int account_id; - int len; - RFIFOHEAD(fd); - account_id=RFIFOL(fd,4); - len=RFIFOW(fd,2); - if(sizeof(struct storage)!=len-8){ - ShowError("inter storage: data size error %d %d\n",sizeof(struct storage),len-8); - }else{ - memcpy(&storage_pt[0],RFIFOP(fd,8),sizeof(struct storage)); - storage_tosql(account_id, storage_pt); - mapif_save_storage_ack(fd,account_id); - } - return 0; -} - -int mapif_parse_LoadGuildStorage(int fd) -{ - RFIFOHEAD(fd); - mapif_load_guild_storage(fd,RFIFOL(fd,2),RFIFOL(fd,6)); - return 0; -} - -int mapif_parse_SaveGuildStorage(int fd) -{ - int guild_exist=1; - int guild_id; - int len; - RFIFOHEAD(fd); - guild_id=RFIFOL(fd,8); - len=RFIFOW(fd,2); - if(sizeof(struct guild_storage)!=len-12){ - ShowError("inter storage: data size error %d %d\n",sizeof(struct guild_storage),len-12); - } - else { -#if 0 // Again, innodb key checks make the check pointless - // Check if guild exists, I may write a function for this later, coz I use it several times. - //printf("- Check if guild %d exists\n",g->guild_id); - sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res!=NULL && mysql_num_rows(sql_res)>0) { - sql_row = mysql_fetch_row(sql_res); - guild_exist = atoi (sql_row[0]); - //printf("- Check if guild %d exists : %s\n",g->guild_id,((guild_exist==0)?"No":"Yes")); - } - mysql_free_result(sql_res) ; //resource free -#endif - if(guild_exist==1) { - memcpy(guild_storage_pt,RFIFOP(fd,12),sizeof(struct guild_storage)); - guild_storage_tosql(guild_id,guild_storage_pt); - mapif_save_guild_storage_ack(fd,RFIFOL(fd,4),guild_id,0); - } - else - mapif_save_guild_storage_ack(fd,RFIFOL(fd,4),guild_id,1); - } - return 0; -} - - -int inter_storage_parse_frommap(int fd){ - RFIFOHEAD(fd); - switch(RFIFOW(fd,0)){ - case 0x3010: mapif_parse_LoadStorage(fd); break; - case 0x3011: mapif_parse_SaveStorage(fd); break; - case 0x3018: mapif_parse_LoadGuildStorage(fd); break; - case 0x3019: mapif_parse_SaveGuildStorage(fd); break; - default: - return 0; - } - return 1; -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +// original code from athena +// SQL conversion by Jioh L. Jung + +#include +#include + +#include "char.h" +#include "itemdb.h" +#include "../common/showmsg.h" + +#define STORAGE_MEMINC 16 + +#ifndef TXT_SQL_CONVERT +// reset by inter_config_read() +struct storage *storage_pt=NULL; +struct guild_storage *guild_storage_pt=NULL; + +#ifndef SQL_DEBUG + +#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] + +#else + +#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) + +#endif + +#endif //TXT_SQL_CONVERT +// storage data -> DB conversion +int storage_tosql(int account_id,struct storage *p){ + int i,j; +// int eqcount=1; +// int noteqcount=1; + int count=0; + struct itemtmp mapitem[MAX_STORAGE]; + for(i=0;istorage_[i].nameid>0){ + mapitem[count].flag=0; + mapitem[count].id = p->storage_[i].id; + mapitem[count].nameid=p->storage_[i].nameid; + mapitem[count].amount = p->storage_[i].amount; + mapitem[count].equip = p->storage_[i].equip; + mapitem[count].identify = p->storage_[i].identify; + mapitem[count].refine = p->storage_[i].refine; + mapitem[count].attribute = p->storage_[i].attribute; + for(j=0; jstorage_[i].card[j]; + count++; + } + } + + memitemdata_to_sql(mapitem, count, account_id,TABLE_STORAGE); + + //printf ("storage dump to DB - id: %d (total: %d)\n", account_id, j); + return 0; +} +#ifndef TXT_SQL_CONVERT + +// DB -> storage data conversion +int storage_fromsql(int account_id, struct storage *p){ + int i=0,j; + char * str_p = tmp_sql; + + memset(p,0,sizeof(struct storage)); //clean up memory + p->storage_amount = 0; + p->account_id = account_id; + + // storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} + str_p += sprintf(str_p,"SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`"); + + for (j=0; jstorage_[i].id= atoi(sql_row[0]); + p->storage_[i].nameid= atoi(sql_row[1]); + p->storage_[i].amount= atoi(sql_row[2]); + p->storage_[i].equip= atoi(sql_row[3]); + p->storage_[i].identify= atoi(sql_row[4]); + p->storage_[i].refine= atoi(sql_row[5]); + p->storage_[i].attribute= atoi(sql_row[6]); + for (j=0; jstorage_[i].card[j]= atoi(sql_row[7+j]); + i++; + } + p->storage_amount = i; + mysql_free_result(sql_res); + } + + ShowInfo ("storage load complete from DB - id: %d (total: %d)\n", account_id, p->storage_amount); + return 1; +} +#endif //TXT_SQL_CONVERT +// Save guild_storage data to sql +int guild_storage_tosql(int guild_id, struct guild_storage *p){ + int i,j; +// int eqcount=1; +// int noteqcount=1; + int count=0; + struct itemtmp mapitem[MAX_GUILD_STORAGE]; + for(i=0;istorage_[i].nameid>0){ + mapitem[count].flag=0; + mapitem[count].id = p->storage_[i].id; + mapitem[count].nameid=p->storage_[i].nameid; + mapitem[count].amount = p->storage_[i].amount; + mapitem[count].equip = p->storage_[i].equip; + mapitem[count].identify = p->storage_[i].identify; + mapitem[count].refine = p->storage_[i].refine; + mapitem[count].attribute = p->storage_[i].attribute; + for (j=0; jstorage_[i].card[j]; + count++; + } + } + + memitemdata_to_sql(mapitem, count, guild_id,TABLE_GUILD_STORAGE); + + ShowInfo ("guild storage save to DB - id: %d (total: %d)\n", guild_id,i); + return 0; +} +#ifndef TXT_SQL_CONVERT +// Load guild_storage data to mem +int guild_storage_fromsql(int guild_id, struct guild_storage *p){ + int i=0,j; + struct guild_storage *gs=guild_storage_pt; + char * str_p = tmp_sql; + p=gs; + + memset(p,0,sizeof(struct guild_storage)); //clean up memory + p->storage_amount = 0; + p->guild_id = guild_id; + + // storage {`guild_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} + str_p += sprintf(str_p,"SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`"); + + for (j=0; jstorage_[i].id= atoi(sql_row[0]); + p->storage_[i].nameid= atoi(sql_row[1]); + p->storage_[i].amount= atoi(sql_row[2]); + p->storage_[i].equip= atoi(sql_row[3]); + p->storage_[i].identify= atoi(sql_row[4]); + p->storage_[i].refine= atoi(sql_row[5]); + p->storage_[i].attribute= atoi(sql_row[6]); + for (j=0; jstorage_[i].card[j] = atoi(sql_row[7+j]); + i++; + } + p->storage_amount = i; + mysql_free_result(sql_res); + } + ShowInfo ("guild storage load complete from DB - id: %d (total: %d)\n", guild_id, p->storage_amount); + return 0; +} + +//--------------------------------------------------------- +// storage data initialize +int inter_storage_sql_init(void){ + + //memory alloc + ShowDebug("interserver storage memory initialize....(%d byte)\n",sizeof(struct storage)); + storage_pt = (struct storage*)aCalloc(sizeof(struct storage), 1); + guild_storage_pt = (struct guild_storage*)aCalloc(sizeof(struct guild_storage), 1); +// memset(storage_pt,0,sizeof(struct storage)); //Calloc sets stuff to 0 already. [Skotlex] +// memset(guild_storage_pt,0,sizeof(struct guild_storage)); + + return 1; +} +// storage data finalize +void inter_storage_sql_final(void) +{ + if (storage_pt) aFree(storage_pt); + if (guild_storage_pt) aFree(guild_storage_pt); + return; +} +// q?f[^? +int inter_storage_delete(int account_id) +{ + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `account_id`='%d'",storage_db, account_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + return 0; +} +int inter_guild_storage_delete(int guild_id) +{ + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_storage_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + return 0; +} + +//--------------------------------------------------------- +// packet from map server + +// recive packet about storage data +int mapif_load_storage(int fd,int account_id){ + //load from DB + WFIFOHEAD(fd, sizeof(struct storage)+8); + storage_fromsql(account_id, storage_pt); + WFIFOW(fd,0)=0x3810; + WFIFOW(fd,2)=sizeof(struct storage)+8; + WFIFOL(fd,4)=account_id; + memcpy(WFIFOP(fd,8),storage_pt,sizeof(struct storage)); + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} +// send ack to map server which is "storage data save ok." +int mapif_save_storage_ack(int fd,int account_id){ + WFIFOHEAD(fd, 7); + WFIFOW(fd,0)=0x3811; + WFIFOL(fd,2)=account_id; + WFIFOB(fd,6)=0; + WFIFOSET(fd,7); + return 0; +} + +int mapif_load_guild_storage(int fd,int account_id,int guild_id) +{ + int guild_exist=1; + WFIFOHEAD(fd, sizeof(struct guild_storage)+12); + WFIFOW(fd,0)=0x3818; + +#if 0 // innodb guilds should render this check unnecessary [Aru] + // Check if guild exists, I may write a function for this later, coz I use it several times. + //printf("- Check if guild %d exists\n",g->guild_id); + sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res!=NULL && mysql_num_rows(sql_res)>0) { + sql_row = mysql_fetch_row(sql_res); + guild_exist = atoi (sql_row[0]); + //printf("- Check if guild %d exists : %s\n",g->guild_id,((guild_exist==0)?"No":"Yes")); + } + mysql_free_result(sql_res) ; //resource free +#endif + if(guild_exist==1) { + guild_storage_fromsql(guild_id,guild_storage_pt); + WFIFOW(fd,2)=sizeof(struct guild_storage)+12; + WFIFOL(fd,4)=account_id; + WFIFOL(fd,8)=guild_id; + memcpy(WFIFOP(fd,12),guild_storage_pt,sizeof(struct guild_storage)); + } + else { + WFIFOW(fd,2)=12; + WFIFOL(fd,4)=account_id; + WFIFOL(fd,8)=0; + } + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} +int mapif_save_guild_storage_ack(int fd,int account_id,int guild_id,int fail) +{ + WFIFOHEAD(fd,11); + WFIFOW(fd,0)=0x3819; + WFIFOL(fd,2)=account_id; + WFIFOL(fd,6)=guild_id; + WFIFOB(fd,10)=fail; + WFIFOSET(fd,11); + return 0; +} + +//--------------------------------------------------------- +// packet from map server + +// recive request about storage data +int mapif_parse_LoadStorage(int fd){ + RFIFOHEAD(fd); + mapif_load_storage(fd,RFIFOL(fd,2)); + return 0; +} +// storage data recive and save +int mapif_parse_SaveStorage(int fd){ + int account_id; + int len; + RFIFOHEAD(fd); + account_id=RFIFOL(fd,4); + len=RFIFOW(fd,2); + if(sizeof(struct storage)!=len-8){ + ShowError("inter storage: data size error %d %d\n",sizeof(struct storage),len-8); + }else{ + memcpy(&storage_pt[0],RFIFOP(fd,8),sizeof(struct storage)); + storage_tosql(account_id, storage_pt); + mapif_save_storage_ack(fd,account_id); + } + return 0; +} + +int mapif_parse_LoadGuildStorage(int fd) +{ + RFIFOHEAD(fd); + mapif_load_guild_storage(fd,RFIFOL(fd,2),RFIFOL(fd,6)); + return 0; +} + +int mapif_parse_SaveGuildStorage(int fd) +{ + int guild_exist=1; + int guild_id; + int len; + RFIFOHEAD(fd); + guild_id=RFIFOL(fd,8); + len=RFIFOW(fd,2); + if(sizeof(struct guild_storage)!=len-12){ + ShowError("inter storage: data size error %d %d\n",sizeof(struct guild_storage),len-12); + } + else { +#if 0 // Again, innodb key checks make the check pointless + // Check if guild exists, I may write a function for this later, coz I use it several times. + //printf("- Check if guild %d exists\n",g->guild_id); + sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `guild_id`='%d'",guild_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res!=NULL && mysql_num_rows(sql_res)>0) { + sql_row = mysql_fetch_row(sql_res); + guild_exist = atoi (sql_row[0]); + //printf("- Check if guild %d exists : %s\n",g->guild_id,((guild_exist==0)?"No":"Yes")); + } + mysql_free_result(sql_res) ; //resource free +#endif + if(guild_exist==1) { + memcpy(guild_storage_pt,RFIFOP(fd,12),sizeof(struct guild_storage)); + guild_storage_tosql(guild_id,guild_storage_pt); + mapif_save_guild_storage_ack(fd,RFIFOL(fd,4),guild_id,0); + } + else + mapif_save_guild_storage_ack(fd,RFIFOL(fd,4),guild_id,1); + } + return 0; +} + + +int inter_storage_parse_frommap(int fd){ + RFIFOHEAD(fd); + switch(RFIFOW(fd,0)){ + case 0x3010: mapif_parse_LoadStorage(fd); break; + case 0x3011: mapif_parse_SaveStorage(fd); break; + case 0x3018: mapif_parse_LoadGuildStorage(fd); break; + case 0x3019: mapif_parse_SaveGuildStorage(fd); break; + default: + return 0; + } + return 1; +} +#endif //TXT_SQL_CONVERT diff --git a/src/char_sql/int_storage.h b/src/char_sql/int_storage.h index c886a45e2..3cba41e64 100644 --- a/src/char_sql/int_storage.h +++ b/src/char_sql/int_storage.h @@ -1,17 +1,17 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INT_STORAGE_SQL_H_ -#define _INT_STORAGE_SQL_H_ - -int inter_storage_sql_init(void); -void inter_storage_sql_final(void); -int inter_storage_delete(int account_id); -int inter_guild_storage_delete(int guild_id); - -int inter_storage_parse_frommap(int fd); - -//Exported for use in the TXT-SQL converter. -int storage_tosql(int account_id,struct storage *p); -int guild_storage_tosql(int guild_id, struct guild_storage *p); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INT_STORAGE_SQL_H_ +#define _INT_STORAGE_SQL_H_ + +int inter_storage_sql_init(void); +void inter_storage_sql_final(void); +int inter_storage_delete(int account_id); +int inter_guild_storage_delete(int guild_id); + +int inter_storage_parse_frommap(int fd); + +//Exported for use in the TXT-SQL converter. +int storage_tosql(int account_id,struct storage *p); +int guild_storage_tosql(int guild_id, struct guild_storage *p); +#endif diff --git a/src/char_sql/inter.c b/src/char_sql/inter.c index 5ce452717..64983cfc6 100644 --- a/src/char_sql/inter.c +++ b/src/char_sql/inter.c @@ -1,822 +1,822 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -// original code from athena -// SQL conversion by Jioh L. Jung - -#include -#include - -#include "char.h" -#include "../common/strlib.h" -#include "../common/showmsg.h" -#include "inter.h" -#include "int_party.h" -#include "int_guild.h" -#include "int_storage.h" -#include "int_pet.h" -#include "int_homun.h" //albator - -#define WISDATA_TTL (60*1000) // Wisデータの生存時間(60秒) -#define WISDELLIST_MAX 256 // Wisデータ削除リストの要素数 - - -MYSQL mysql_handle; -MYSQL_RES* sql_res ; -MYSQL_ROW sql_row ; -int sql_fields, sql_cnt; -char tmp_sql[65535]; - -MYSQL lmysql_handle; -MYSQL_RES* lsql_res ; -MYSQL_ROW lsql_row ; - -int char_server_port = 3306; -char char_server_ip[32] = "127.0.0.1"; -char char_server_id[32] = "ragnarok"; -char char_server_pw[32] = "ragnarok"; -char char_server_db[32] = "ragnarok"; -char default_codepage[32] = ""; //Feature by irmin. - -int login_server_port = 3306; -char login_server_ip[32] = "127.0.0.1"; -char login_server_id[32] = "ragnarok"; -char login_server_pw[32] = "ragnarok"; -char login_server_db[32] = "ragnarok"; - -#ifndef TXT_SQL_CONVERT - -static struct accreg *accreg_pt; -unsigned int party_share_level = 10; -char main_chat_nick[16] = "Main"; - -// sending packet list -// NOTE: This variable ain't used at all! And it's confusing.. where do I add that the length of packet 0x2b07 is 10? x.x [Skotlex] -int inter_send_packet_length[]={ - -1,-1,27,-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - -1, 7, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, - 35,-1,11,15, 34,29, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, - 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, - 9, 9,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 11,-1, 7, 3, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; -// recv. packet list -int inter_recv_packet_length[]={ - -1,-1, 7,-1, -1,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3000-0x300f - 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, //0x3010-0x301f - -1, 6,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, //0x3020-0x302f - -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 14,19,186,-1, //0x3030-0x303f - 5, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 48,14,-1, 6, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3080-0x308f - -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3090 - 0x309f Homunculus packets [albator] -}; - -struct WisData { - int id, fd, count,len; - unsigned long tick; - unsigned char src[24], dst[24], msg[512]; -}; -static struct dbt * wis_db = NULL; -static int wis_dellist[WISDELLIST_MAX], wis_delnum; - -int inter_sql_test (void); - -#endif //TXT_SQL_CONVERT -//-------------------------------------------------------- -// Save registry to sql -int inter_accreg_tosql(int account_id, int char_id, struct accreg *reg, int type){ - - int j; - char temp_str[64]; //Needs be twice the source to ensure it fits [Skotlex] - char temp_str2[512]; - if (account_id<=0) return 0; - reg->account_id=account_id; - reg->char_id = char_id; - - switch (type) { - case 3: //Char Reg - //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`) - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, char_id); - break; - case 2: //Account Reg - //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`) - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'",reg_db, account_id); - break; - case 1: //Account2 Reg - ShowError("inter_accreg_tosql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n"); - return 0; - default: - ShowError("inter_accreg_tosql: Invalid type %d\n", type); - return 0; - - } - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - if (reg->reg_num<=0) return 0; - - for(j=0;jreg_num;j++){ - if(reg->reg[j].str != NULL){ - sprintf(tmp_sql,"INSERT INTO `%s` (`type`, `account_id`, `char_id`, `str`, `value`) VALUES ('%d','%d','%d','%s','%s')", - reg_db, type, type!=3?reg->account_id:0, type==3?reg->char_id:0, - jstrescapecpy(temp_str,reg->reg[j].str), jstrescapecpy(temp_str2,reg->reg[j].value)); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - } - return 1; -} -#ifndef TXT_SQL_CONVERT - -// Load account_reg from sql (type=2) -int inter_accreg_fromsql(int account_id,int char_id, struct accreg *reg, int type) -{ - int j=0; - if (reg==NULL) return 0; - memset(reg, 0, sizeof(struct accreg)); - reg->account_id=account_id; - reg->char_id=char_id; - - //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`) - switch (type) { - case 3: //char reg - sprintf (tmp_sql, "SELECT `str`, `value` FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, reg->char_id); - break; - case 2: //account reg - sprintf (tmp_sql, "SELECT `str`, `value` FROM `%s` WHERE `type`=2 AND `account_id`='%d'",reg_db, reg->account_id); - break; - case 1: //account2 reg - ShowError("inter_accreg_fromsql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n"); - return 0; - } - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - - if (sql_res) { - for(j=0;(sql_row = mysql_fetch_row(sql_res));j++){ - strcpy(reg->reg[j].str, sql_row[0]); - strcpy(reg->reg[j].value, sql_row[1]); - } - mysql_free_result(sql_res); - } - reg->reg_num=j; - return 1; -} - -// Initialize -int inter_accreg_sql_init(void) -{ - CREATE(accreg_pt, struct accreg, 1); - return 0; - -} -#endif //TXT_SQL_CONVERT - -/*========================================== - * read config file - *------------------------------------------ - */ -static int inter_config_read(const char *cfgName) { - int i; - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - fp=fopen(cfgName,"r"); - if(fp==NULL){ - ShowError("file not found: %s\n", cfgName); - return 1; - } - - ShowInfo("reading file %s...\n",cfgName); - - while(fgets(line, 1020, fp)){ - i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2); - if(i!=2) - continue; - - if(strcmpi(w1,"char_server_ip")==0){ - strcpy(char_server_ip, w2); - ShowStatus ("set char_server_ip : %s\n",w2); - } - else if(strcmpi(w1,"char_server_port")==0){ - char_server_port=atoi(w2); - ShowStatus ("set char_server_port : %s\n",w2); - } - else if(strcmpi(w1,"char_server_id")==0){ - strcpy(char_server_id, w2); - ShowStatus ("set char_server_id : %s\n",w2); - } - else if(strcmpi(w1,"char_server_pw")==0){ - strcpy(char_server_pw, w2); - ShowStatus ("set char_server_pw : %s\n",w2); - } - else if(strcmpi(w1,"char_server_db")==0){ - strcpy(char_server_db, w2); - ShowStatus ("set char_server_db : %s\n",w2); - } - else if(strcmpi(w1,"default_codepage")==0){ - strcpy(default_codepage, w2); - ShowStatus ("set default_codepage : %s\n",w2); - } - //Logins information to be read from the inter_athena.conf - //for character deletion (checks email in the loginDB) - else if(strcmpi(w1,"login_server_ip")==0){ - strcpy(login_server_ip, w2); - ShowStatus ("set login_server_ip : %s\n",w2); - } - else if(strcmpi(w1,"login_server_port")==0){ - login_server_port=atoi(w2); - ShowStatus ("set login_server_port : %s\n",w2); - } - else if(strcmpi(w1,"login_server_id")==0){ - strcpy(login_server_id, w2); - ShowStatus ("set login_server_id : %s\n",w2); - } - else if(strcmpi(w1,"login_server_pw")==0){ - strcpy(login_server_pw, w2); - ShowStatus ("set login_server_pw : %s\n",w2); - } - else if(strcmpi(w1,"login_server_db")==0){ - strcpy(login_server_db, w2); - ShowStatus ("set login_server_db : %s\n",w2); - } -#ifndef TXT_SQL_CONVERT - else if(strcmpi(w1,"party_share_level")==0){ - party_share_level=(unsigned int)atof(w2); - } - else if(strcmpi(w1,"log_inter")==0){ - log_inter = atoi(w2); - } - else if(strcmpi(w1, "main_chat_nick")==0){ // Main chat nick [LuzZza] - strcpy(main_chat_nick, w2); // - } -#endif //TXT_SQL_CONVERT - else if(strcmpi(w1,"import")==0){ - inter_config_read(w2); - } - } - fclose(fp); - - ShowInfo ("done reading %s.\n", cfgName); - - return 0; -} -#ifndef TXT_SQL_CONVERT - -// Save interlog into sql -int inter_log(char *fmt,...) -{ - char str[255]; - char temp_str[510]; //Needs be twice as long as str[] //Skotlex - va_list ap; - va_start(ap,fmt); - - vsprintf(str,fmt,ap); - sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `log`) VALUES (NOW(), '%s')",interlog_db, jstrescapecpy(temp_str,str)); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - va_end(ap); - return 0; -} - -/*====================================================== - * Does a mysql_ping to all connection handles. [Skotlex] - *------------------------------------------------------ - */ -int inter_sql_ping(int tid, unsigned int tick, int id, int data) -{ - ShowInfo("Pinging SQL server to keep connection alive...\n"); - mysql_ping(&mysql_handle); - if(char_gm_read) - mysql_ping(&lmysql_handle); - return 0; -} -#endif //TXT_SQL_CONVERT - -// initialize -int inter_init_sql(const char *file) -{ - //int i; - - ShowInfo ("interserver initialize...\n"); - inter_config_read(file); - - //DB connection initialized - mysql_init(&mysql_handle); - ShowInfo("Connect Character DB server.... (Character Server)\n"); - if(!mysql_real_connect(&mysql_handle, char_server_ip, char_server_id, char_server_pw, - char_server_db ,char_server_port, (char *)NULL, 0)) { - //pointer check - ShowFatalError("%s\n",mysql_error(&mysql_handle)); - exit(1); - } -#ifndef TXT_SQL_CONVERT - else if (inter_sql_test()) { - ShowStatus("Connect Success! (Character Server)\n"); - } - - if(char_gm_read) { - mysql_init(&lmysql_handle); - ShowInfo("Connect Character DB server.... (login server)\n"); - if(!mysql_real_connect(&lmysql_handle, login_server_ip, login_server_id, login_server_pw, - login_server_db ,login_server_port, (char *)NULL, 0)) { - //pointer check - ShowFatalError("%s\n",mysql_error(&lmysql_handle)); - exit(1); - }else { - ShowStatus ("Connect Success! (Login Server)\n"); - } - } -#endif //TXT_SQL_CONVERT - if(strlen(default_codepage) > 0 ) { - sprintf( tmp_sql, "SET NAMES %s", default_codepage ); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } -#ifndef TXT_SQL_CONVERT - if(char_gm_read) - if (mysql_query(&lmysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&lmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } -#endif //TXT_SQL_CONVERT - } - -#ifndef TXT_SQL_CONVERT - wis_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - inter_guild_sql_init(); - inter_storage_sql_init(); - inter_party_sql_init(); - inter_pet_sql_init(); - inter_homunculus_sql_init(); // albator - inter_accreg_sql_init(); - - if (connection_ping_interval) { - add_timer_func_list(inter_sql_ping, "inter_sql_ping"); - add_timer_interval(gettick()+connection_ping_interval*60*60*1000, - inter_sql_ping, 0, 0, connection_ping_interval*60*60*1000); - } -#endif //TXT_SQL_CONVERT - return 0; -} -#ifndef TXT_SQL_CONVERT - -int inter_sql_test (void) -{ - const char fields[][24] = { - "father", // version 1363 - "fame", // version 1491 - }; - char buf[1024] = ""; - int i; - - sprintf(tmp_sql, "EXPLAIN `%s`",char_db); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - // store DB fields - if (sql_res) { - while((sql_row = mysql_fetch_row(sql_res))) { - strcat (buf, sql_row[0]); - strcat (buf, " "); - } - } - - // check DB strings - for (i = 0; i < (int)(sizeof(fields) / sizeof(fields[0])); i++) { - if(!strstr(buf, fields[i])) { - ShowSQL ("Field `%s` not be found in `%s`. Consider updating your database!\n", fields[i], char_db); - exit(1); - } - } - - mysql_free_result(sql_res); - - return 1; -} - -// finalize -void inter_final(void) { - wis_db->destroy(wis_db, NULL); - - inter_guild_sql_final(); - inter_storage_sql_final(); - inter_party_sql_final(); - inter_pet_sql_final(); - inter_homunculus_sql_final(); //[orn] - - if (accreg_pt) aFree(accreg_pt); - return; -} - -int inter_mapif_init(int fd) { - inter_guild_mapif_init(fd); - - return 0; -} - - -//-------------------------------------------------------- - -// GM message sending -int mapif_GMmessage(unsigned char *mes, int len, unsigned long color, int sfd) { - unsigned char buf[2048]; - - if (len > 2048) len = 2047; //Make it fit to avoid crashes. [Skotlex] - WBUFW(buf, 0) = 0x3800; - WBUFW(buf, 2) = len; - WBUFL(buf, 4) = color; - memcpy(WBUFP(buf, 8), mes, len-8); - mapif_sendallwos(sfd, buf, len); - return 0; -} - -// Wis sending -int mapif_wis_message(struct WisData *wd) { - unsigned char buf[2048]; - if (wd->len > 2047-56) wd->len = 2047-56; //Force it to fit to avoid crashes. [Skotlex] - - WBUFW(buf, 0) = 0x3801; - WBUFW(buf, 2) = 56 +wd->len; - WBUFL(buf, 4) = wd->id; - memcpy(WBUFP(buf, 8), wd->src, NAME_LENGTH); - memcpy(WBUFP(buf,32), wd->dst, NAME_LENGTH); - memcpy(WBUFP(buf,56), wd->msg, wd->len); - wd->count = mapif_sendall(buf,WBUFW(buf,2)); - - return 0; -} -// Wis sending result -int mapif_wis_end(struct WisData *wd,int flag) -{ - unsigned char buf[27]; - - WBUFW(buf, 0)=0x3802; - memcpy(WBUFP(buf, 2),wd->src,24); - WBUFB(buf,26)=flag; - mapif_send(wd->fd,buf,27); -// printf("inter server wis_end %d\n",flag); - return 0; -} - -int mapif_account_reg(int fd,unsigned char *src) -{ -// unsigned char buf[WBUFW(src,2)]; <- Hey, can this really be done? [Skotlex] - unsigned char *buf = aCalloc(1,WBUFW(src,2)); // [Lance] - Skot... Dynamic allocation is better :D - memcpy(WBUFP(buf,0),src,WBUFW(src,2)); - WBUFW(buf, 0)=0x3804; - mapif_sendallwos(fd, buf, WBUFW(buf,2)); - aFree(buf); - return 0; -} - -// Send the requested account_reg -int mapif_account_reg_reply(int fd,int account_id,int char_id, int type) -{ - struct accreg *reg=accreg_pt; - WFIFOHEAD(fd, 13 + 5000); - inter_accreg_fromsql(account_id,char_id,reg,type); - - WFIFOW(fd,0)=0x3804; - WFIFOL(fd,4)=account_id; - WFIFOL(fd,8)=char_id; - WFIFOB(fd,12)=type; - if(reg->reg_num==0){ - WFIFOW(fd,2)=13; - }else{ - int i,p; - for (p=13,i = 0; i < reg->reg_num && p < 5000; i++) { - p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].str)+1; //We add 1 to consider the '\0' in place. - p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].value)+1; - } - WFIFOW(fd,2)=p; - if (p>= 5000) - ShowWarning("Too many acc regs for %d:%d, not all values were loaded.\n", account_id, char_id); - } - WFIFOSET(fd,WFIFOW(fd,2)); - return 0; -} - -int mapif_send_gmaccounts() -{ - int i, len = 4; - unsigned char buf[32000]; - - // forward the gm accounts to the map server - len = 4; - WBUFW(buf,0) = 0x2b15; - - for(i = 0; i < GM_num; i++) { - WBUFL(buf, len) = gm_account[i].account_id; - WBUFB(buf, len+4) = (unsigned char)gm_account[i].level; - len += 5; - } - WBUFW(buf, 2) = len; - mapif_sendall(buf, len); - - return 0; -} - -//Sends to map server the current max Account/Char id [Skotlex] -void mapif_send_maxid(int account_id, int char_id) -{ - unsigned char buf[12]; - - WBUFW(buf,0) = 0x2b07; - WBUFL(buf,2) = account_id; - WBUFL(buf,6) = char_id; - mapif_sendall(buf, 10); -} - -//Request to kick char from a certain map server. [Skotlex] -int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason) -{ - if (fd >= 0) - { - WFIFOHEAD(fd,7); - WFIFOW(fd,0) = 0x2b1f; - WFIFOL(fd,2) = account_id; - WFIFOB(fd,6) = reason; - WFIFOSET(fd,7); - return 0; - } - return -1; -} - -//-------------------------------------------------------- - -// Existence check of WISP data -int check_ttl_wisdata_sub(DBKey key, void *data, va_list ap) { - unsigned long tick; - struct WisData *wd = (struct WisData *)data; - tick = va_arg(ap, unsigned long); - - if (DIFF_TICK(tick, wd->tick) > WISDATA_TTL && wis_delnum < WISDELLIST_MAX) - wis_dellist[wis_delnum++] = wd->id; - - return 0; -} - -int check_ttl_wisdata(void) { - unsigned long tick = gettick(); - int i; - - do { - wis_delnum = 0; - wis_db->foreach(wis_db, check_ttl_wisdata_sub, tick); - for(i = 0; i < wis_delnum; i++) { - struct WisData *wd = idb_get(wis_db, wis_dellist[i]); - ShowWarning("inter: wis data id=%d time out : from %s to %s\n", wd->id, wd->src, wd->dst); - // removed. not send information after a timeout. Just no answer for the player - //mapif_wis_end(wd, 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - idb_remove(wis_db, wd->id); - } - } while(wis_delnum >= WISDELLIST_MAX); - - return 0; -} - -//-------------------------------------------------------- - -// GM message sending -int mapif_parse_GMmessage(int fd) -{ - RFIFOHEAD(fd); - mapif_GMmessage(RFIFOP(fd, 8), RFIFOW(fd, 2), RFIFOL(fd, 4), fd); - return 0; -} - - -// Wisp/page request to send -int mapif_parse_WisRequest(int fd) { - struct WisData* wd; - static int wisid = 0; - char name[NAME_LENGTH], t_name[NAME_LENGTH*2]; //Needs space to allocate names with escaped chars [Skotlex] - RFIFOHEAD(fd); - if ( fd <= 0 ) {return 0;} // check if we have a valid fd - - if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) { - ShowWarning("inter: Wis message size too long.\n"); - return 0; - } else if (RFIFOW(fd,2)-52 <= 0) { // normaly, impossible, but who knows... - ShowError("inter: Wis message doesn't exist.\n"); - return 0; - } - memcpy(name, RFIFOP(fd,28), NAME_LENGTH); //Received name may be too large and not contain \0! [Skotlex] - name[NAME_LENGTH-1]= '\0'; - - sprintf (tmp_sql, "SELECT `name` FROM `%s` WHERE `name`='%s'", - char_db, jstrescapecpy(t_name, name)); - if(mysql_query(&mysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - sql_res = mysql_store_result(&mysql_handle); - - // search if character exists before to ask all map-servers - if (!(sql_row = mysql_fetch_row(sql_res))) { - unsigned char buf[27]; - WBUFW(buf, 0) = 0x3802; - memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH); - WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - mapif_send(fd, buf, 27); - // Character exists. So, ask all map-servers - } else { - // to be sure of the correct name, rewrite it - memset(name, 0, NAME_LENGTH); - strncpy(name, sql_row[0], NAME_LENGTH); - // if source is destination, don't ask other servers. - if (strcmp((char*)RFIFOP(fd,4),name) == 0) { - unsigned char buf[27]; - WBUFW(buf, 0) = 0x3802; - memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH); - WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - mapif_send(fd, buf, 27); - } else { - - CREATE(wd, struct WisData, 1); - - // Whether the failure of previous wisp/page transmission (timeout) - check_ttl_wisdata(); - - wd->id = ++wisid; - wd->fd = fd; - wd->len= RFIFOW(fd,2)-52; - memcpy(wd->src, RFIFOP(fd, 4), NAME_LENGTH); - memcpy(wd->dst, RFIFOP(fd,28), NAME_LENGTH); - memcpy(wd->msg, RFIFOP(fd,52), wd->len); - wd->tick = gettick(); - idb_put(wis_db, wd->id, wd); - mapif_wis_message(wd); - } - } - - //Freeing ... O.o - if(sql_res){ - mysql_free_result(sql_res); - } - - return 0; -} - - -// Wisp/page transmission result -int mapif_parse_WisReply(int fd) { - int id, flag; - struct WisData *wd; - RFIFOHEAD(fd); - id = RFIFOL(fd,2); - flag = RFIFOB(fd,6); - wd = idb_get(wis_db, id); - if (wd == NULL) - return 0; // This wisp was probably suppress before, because it was timeout of because of target was found on another map-server - - if ((--wd->count) <= 0 || flag != 1) { - mapif_wis_end(wd, flag); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target - idb_remove(wis_db, id); - } - - return 0; -} - -// Received wisp message from map-server for ALL gm (just copy the message and resends it to ALL map-servers) -int mapif_parse_WisToGM(int fd) { - unsigned char buf[2048]; // 0x3003/0x3803 .w .24B .w .?B - RFIFOHEAD(fd); - ShowDebug("Sent packet back!\n"); - memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2)); - WBUFW(buf, 0) = 0x3803; - mapif_sendall(buf, RFIFOW(fd,2)); - - return 0; -} - -// Save account_reg into sql (type=2) -int mapif_parse_Registry(int fd) -{ - int j,p,len, max; - struct accreg *reg=accreg_pt; - RFIFOHEAD(fd); - memset(accreg_pt,0,sizeof(struct accreg)); - switch (RFIFOB(fd, 12)) { - case 3: //Character registry - max = GLOBAL_REG_NUM; - break; - case 2: //Account Registry - max = ACCOUNT_REG_NUM; - break; - case 1: //Account2 registry, must be sent over to login server. - return save_accreg2(RFIFOP(fd,4), RFIFOW(fd,2)-4); - default: - return 1; - } - for(j=0,p=13;jreg[j].str,&len); - reg->reg[j].str[len]='\0'; - p +=len+1; //+1 to skip the '\0' between strings. - sscanf(RFIFOP(fd,p), "%255c%n",reg->reg[j].value,&len); - reg->reg[j].value[len]='\0'; - p +=len+1; - } - reg->reg_num=j; - - inter_accreg_tosql(RFIFOL(fd,4),RFIFOL(fd,8),reg, RFIFOB(fd,12)); - mapif_account_reg(fd,RFIFOP(fd,0)); // Send updated accounts to other map servers. - return 0; -} - -// Request the value of all registries. -int mapif_parse_RegistryRequest(int fd) -{ - RFIFOHEAD(fd); - //Load Char Registry - if (RFIFOB(fd,12)) - mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6),3); - //Load Account Registry - if (RFIFOB(fd,11)) - mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6),2); - //Ask Login Server for Account2 values. - if (RFIFOB(fd,10)) - request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6)-2); - return 1; -} - -//-------------------------------------------------------- -int inter_parse_frommap(int fd) -{ - int cmd; - int len=0; - RFIFOHEAD(fd); - cmd=RFIFOW(fd,0); - // inter鯖管轄かを調べ - if(cmd < 0x3000 || cmd >= 0x3000 + (sizeof(inter_recv_packet_length)/ - sizeof(inter_recv_packet_length[0]) ) ) - return 0; - - if (inter_recv_packet_length[cmd-0x3000] == 0) //This is necessary, because otherwise we return 2 and the char server will just hang waiting for packets! [Skotlex] - return 0; - - // パケット長を調べる - if((len = inter_check_length(fd, inter_recv_packet_length[cmd - 0x3000])) == 0) - return 2; - - switch(cmd){ - case 0x3000: mapif_parse_GMmessage(fd); break; - case 0x3001: mapif_parse_WisRequest(fd); break; - case 0x3002: mapif_parse_WisReply(fd); break; - case 0x3003: mapif_parse_WisToGM(fd); break; - case 0x3004: mapif_parse_Registry(fd); break; - case 0x3005: mapif_parse_RegistryRequest(fd); break; - default: - if(inter_party_parse_frommap(fd)) - break; - if(inter_guild_parse_frommap(fd)) - break; - if(inter_storage_parse_frommap(fd)) - break; - if(inter_pet_parse_frommap(fd)) - break; - if(inter_homunculus_parse_frommap(fd)) //albator - break; - return 0; - } - - RFIFOSKIP(fd, len); - return 1; -} - -// RFIFO check -int inter_check_length(int fd, int length) -{ - RFIFOHEAD(fd); - if(length==-1){ // v-len packet - if(RFIFOREST(fd)<4) // packet not yet - return 0; - length = RFIFOW(fd, 2); - } - - if((int)RFIFOREST(fd) < length) // packet not yet - return 0; - - return length; -} -#endif //TXT_SQL_CONVERT +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +// original code from athena +// SQL conversion by Jioh L. Jung + +#include +#include + +#include "char.h" +#include "../common/strlib.h" +#include "../common/showmsg.h" +#include "inter.h" +#include "int_party.h" +#include "int_guild.h" +#include "int_storage.h" +#include "int_pet.h" +#include "int_homun.h" //albator + +#define WISDATA_TTL (60*1000) // Wisデータの生存時間(60秒) +#define WISDELLIST_MAX 256 // Wisデータ削除リストの要素数 + + +MYSQL mysql_handle; +MYSQL_RES* sql_res ; +MYSQL_ROW sql_row ; +int sql_fields, sql_cnt; +char tmp_sql[65535]; + +MYSQL lmysql_handle; +MYSQL_RES* lsql_res ; +MYSQL_ROW lsql_row ; + +int char_server_port = 3306; +char char_server_ip[32] = "127.0.0.1"; +char char_server_id[32] = "ragnarok"; +char char_server_pw[32] = "ragnarok"; +char char_server_db[32] = "ragnarok"; +char default_codepage[32] = ""; //Feature by irmin. + +int login_server_port = 3306; +char login_server_ip[32] = "127.0.0.1"; +char login_server_id[32] = "ragnarok"; +char login_server_pw[32] = "ragnarok"; +char login_server_db[32] = "ragnarok"; + +#ifndef TXT_SQL_CONVERT + +static struct accreg *accreg_pt; +unsigned int party_share_level = 10; +char main_chat_nick[16] = "Main"; + +// sending packet list +// NOTE: This variable ain't used at all! And it's confusing.. where do I add that the length of packet 0x2b07 is 10? x.x [Skotlex] +int inter_send_packet_length[]={ + -1,-1,27,-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 7, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, + 35,-1,11,15, 34,29, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, + 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, + 9, 9,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 11,-1, 7, 3, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +// recv. packet list +int inter_recv_packet_length[]={ + -1,-1, 7,-1, -1,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3000-0x300f + 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, //0x3010-0x301f + -1, 6,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, //0x3020-0x302f + -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 14,19,186,-1, //0x3030-0x303f + 5, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 48,14,-1, 6, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3080-0x308f + -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3090 - 0x309f Homunculus packets [albator] +}; + +struct WisData { + int id, fd, count,len; + unsigned long tick; + unsigned char src[24], dst[24], msg[512]; +}; +static struct dbt * wis_db = NULL; +static int wis_dellist[WISDELLIST_MAX], wis_delnum; + +int inter_sql_test (void); + +#endif //TXT_SQL_CONVERT +//-------------------------------------------------------- +// Save registry to sql +int inter_accreg_tosql(int account_id, int char_id, struct accreg *reg, int type){ + + int j; + char temp_str[64]; //Needs be twice the source to ensure it fits [Skotlex] + char temp_str2[512]; + if (account_id<=0) return 0; + reg->account_id=account_id; + reg->char_id = char_id; + + switch (type) { + case 3: //Char Reg + //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`) + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, char_id); + break; + case 2: //Account Reg + //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`) + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'",reg_db, account_id); + break; + case 1: //Account2 Reg + ShowError("inter_accreg_tosql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n"); + return 0; + default: + ShowError("inter_accreg_tosql: Invalid type %d\n", type); + return 0; + + } + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + if (reg->reg_num<=0) return 0; + + for(j=0;jreg_num;j++){ + if(reg->reg[j].str != NULL){ + sprintf(tmp_sql,"INSERT INTO `%s` (`type`, `account_id`, `char_id`, `str`, `value`) VALUES ('%d','%d','%d','%s','%s')", + reg_db, type, type!=3?reg->account_id:0, type==3?reg->char_id:0, + jstrescapecpy(temp_str,reg->reg[j].str), jstrescapecpy(temp_str2,reg->reg[j].value)); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + } + return 1; +} +#ifndef TXT_SQL_CONVERT + +// Load account_reg from sql (type=2) +int inter_accreg_fromsql(int account_id,int char_id, struct accreg *reg, int type) +{ + int j=0; + if (reg==NULL) return 0; + memset(reg, 0, sizeof(struct accreg)); + reg->account_id=account_id; + reg->char_id=char_id; + + //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`) + switch (type) { + case 3: //char reg + sprintf (tmp_sql, "SELECT `str`, `value` FROM `%s` WHERE `type`=3 AND `char_id`='%d'",reg_db, reg->char_id); + break; + case 2: //account reg + sprintf (tmp_sql, "SELECT `str`, `value` FROM `%s` WHERE `type`=2 AND `account_id`='%d'",reg_db, reg->account_id); + break; + case 1: //account2 reg + ShowError("inter_accreg_fromsql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n"); + return 0; + } + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + + if (sql_res) { + for(j=0;(sql_row = mysql_fetch_row(sql_res));j++){ + strcpy(reg->reg[j].str, sql_row[0]); + strcpy(reg->reg[j].value, sql_row[1]); + } + mysql_free_result(sql_res); + } + reg->reg_num=j; + return 1; +} + +// Initialize +int inter_accreg_sql_init(void) +{ + CREATE(accreg_pt, struct accreg, 1); + return 0; + +} +#endif //TXT_SQL_CONVERT + +/*========================================== + * read config file + *------------------------------------------ + */ +static int inter_config_read(const char *cfgName) { + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp=fopen(cfgName,"r"); + if(fp==NULL){ + ShowError("file not found: %s\n", cfgName); + return 1; + } + + ShowInfo("reading file %s...\n",cfgName); + + while(fgets(line, 1020, fp)){ + i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2); + if(i!=2) + continue; + + if(strcmpi(w1,"char_server_ip")==0){ + strcpy(char_server_ip, w2); + ShowStatus ("set char_server_ip : %s\n",w2); + } + else if(strcmpi(w1,"char_server_port")==0){ + char_server_port=atoi(w2); + ShowStatus ("set char_server_port : %s\n",w2); + } + else if(strcmpi(w1,"char_server_id")==0){ + strcpy(char_server_id, w2); + ShowStatus ("set char_server_id : %s\n",w2); + } + else if(strcmpi(w1,"char_server_pw")==0){ + strcpy(char_server_pw, w2); + ShowStatus ("set char_server_pw : %s\n",w2); + } + else if(strcmpi(w1,"char_server_db")==0){ + strcpy(char_server_db, w2); + ShowStatus ("set char_server_db : %s\n",w2); + } + else if(strcmpi(w1,"default_codepage")==0){ + strcpy(default_codepage, w2); + ShowStatus ("set default_codepage : %s\n",w2); + } + //Logins information to be read from the inter_athena.conf + //for character deletion (checks email in the loginDB) + else if(strcmpi(w1,"login_server_ip")==0){ + strcpy(login_server_ip, w2); + ShowStatus ("set login_server_ip : %s\n",w2); + } + else if(strcmpi(w1,"login_server_port")==0){ + login_server_port=atoi(w2); + ShowStatus ("set login_server_port : %s\n",w2); + } + else if(strcmpi(w1,"login_server_id")==0){ + strcpy(login_server_id, w2); + ShowStatus ("set login_server_id : %s\n",w2); + } + else if(strcmpi(w1,"login_server_pw")==0){ + strcpy(login_server_pw, w2); + ShowStatus ("set login_server_pw : %s\n",w2); + } + else if(strcmpi(w1,"login_server_db")==0){ + strcpy(login_server_db, w2); + ShowStatus ("set login_server_db : %s\n",w2); + } +#ifndef TXT_SQL_CONVERT + else if(strcmpi(w1,"party_share_level")==0){ + party_share_level=(unsigned int)atof(w2); + } + else if(strcmpi(w1,"log_inter")==0){ + log_inter = atoi(w2); + } + else if(strcmpi(w1, "main_chat_nick")==0){ // Main chat nick [LuzZza] + strcpy(main_chat_nick, w2); // + } +#endif //TXT_SQL_CONVERT + else if(strcmpi(w1,"import")==0){ + inter_config_read(w2); + } + } + fclose(fp); + + ShowInfo ("done reading %s.\n", cfgName); + + return 0; +} +#ifndef TXT_SQL_CONVERT + +// Save interlog into sql +int inter_log(char *fmt,...) +{ + char str[255]; + char temp_str[510]; //Needs be twice as long as str[] //Skotlex + va_list ap; + va_start(ap,fmt); + + vsprintf(str,fmt,ap); + sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `log`) VALUES (NOW(), '%s')",interlog_db, jstrescapecpy(temp_str,str)); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + va_end(ap); + return 0; +} + +/*====================================================== + * Does a mysql_ping to all connection handles. [Skotlex] + *------------------------------------------------------ + */ +int inter_sql_ping(int tid, unsigned int tick, int id, int data) +{ + ShowInfo("Pinging SQL server to keep connection alive...\n"); + mysql_ping(&mysql_handle); + if(char_gm_read) + mysql_ping(&lmysql_handle); + return 0; +} +#endif //TXT_SQL_CONVERT + +// initialize +int inter_init_sql(const char *file) +{ + //int i; + + ShowInfo ("interserver initialize...\n"); + inter_config_read(file); + + //DB connection initialized + mysql_init(&mysql_handle); + ShowInfo("Connect Character DB server.... (Character Server)\n"); + if(!mysql_real_connect(&mysql_handle, char_server_ip, char_server_id, char_server_pw, + char_server_db ,char_server_port, (char *)NULL, 0)) { + //pointer check + ShowFatalError("%s\n",mysql_error(&mysql_handle)); + exit(1); + } +#ifndef TXT_SQL_CONVERT + else if (inter_sql_test()) { + ShowStatus("Connect Success! (Character Server)\n"); + } + + if(char_gm_read) { + mysql_init(&lmysql_handle); + ShowInfo("Connect Character DB server.... (login server)\n"); + if(!mysql_real_connect(&lmysql_handle, login_server_ip, login_server_id, login_server_pw, + login_server_db ,login_server_port, (char *)NULL, 0)) { + //pointer check + ShowFatalError("%s\n",mysql_error(&lmysql_handle)); + exit(1); + }else { + ShowStatus ("Connect Success! (Login Server)\n"); + } + } +#endif //TXT_SQL_CONVERT + if(strlen(default_codepage) > 0 ) { + sprintf( tmp_sql, "SET NAMES %s", default_codepage ); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } +#ifndef TXT_SQL_CONVERT + if(char_gm_read) + if (mysql_query(&lmysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&lmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } +#endif //TXT_SQL_CONVERT + } + +#ifndef TXT_SQL_CONVERT + wis_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + inter_guild_sql_init(); + inter_storage_sql_init(); + inter_party_sql_init(); + inter_pet_sql_init(); + inter_homunculus_sql_init(); // albator + inter_accreg_sql_init(); + + if (connection_ping_interval) { + add_timer_func_list(inter_sql_ping, "inter_sql_ping"); + add_timer_interval(gettick()+connection_ping_interval*60*60*1000, + inter_sql_ping, 0, 0, connection_ping_interval*60*60*1000); + } +#endif //TXT_SQL_CONVERT + return 0; +} +#ifndef TXT_SQL_CONVERT + +int inter_sql_test (void) +{ + const char fields[][24] = { + "father", // version 1363 + "fame", // version 1491 + }; + char buf[1024] = ""; + int i; + + sprintf(tmp_sql, "EXPLAIN `%s`",char_db); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + // store DB fields + if (sql_res) { + while((sql_row = mysql_fetch_row(sql_res))) { + strcat (buf, sql_row[0]); + strcat (buf, " "); + } + } + + // check DB strings + for (i = 0; i < (int)(sizeof(fields) / sizeof(fields[0])); i++) { + if(!strstr(buf, fields[i])) { + ShowSQL ("Field `%s` not be found in `%s`. Consider updating your database!\n", fields[i], char_db); + exit(1); + } + } + + mysql_free_result(sql_res); + + return 1; +} + +// finalize +void inter_final(void) { + wis_db->destroy(wis_db, NULL); + + inter_guild_sql_final(); + inter_storage_sql_final(); + inter_party_sql_final(); + inter_pet_sql_final(); + inter_homunculus_sql_final(); //[orn] + + if (accreg_pt) aFree(accreg_pt); + return; +} + +int inter_mapif_init(int fd) { + inter_guild_mapif_init(fd); + + return 0; +} + + +//-------------------------------------------------------- + +// GM message sending +int mapif_GMmessage(unsigned char *mes, int len, unsigned long color, int sfd) { + unsigned char buf[2048]; + + if (len > 2048) len = 2047; //Make it fit to avoid crashes. [Skotlex] + WBUFW(buf, 0) = 0x3800; + WBUFW(buf, 2) = len; + WBUFL(buf, 4) = color; + memcpy(WBUFP(buf, 8), mes, len-8); + mapif_sendallwos(sfd, buf, len); + return 0; +} + +// Wis sending +int mapif_wis_message(struct WisData *wd) { + unsigned char buf[2048]; + if (wd->len > 2047-56) wd->len = 2047-56; //Force it to fit to avoid crashes. [Skotlex] + + WBUFW(buf, 0) = 0x3801; + WBUFW(buf, 2) = 56 +wd->len; + WBUFL(buf, 4) = wd->id; + memcpy(WBUFP(buf, 8), wd->src, NAME_LENGTH); + memcpy(WBUFP(buf,32), wd->dst, NAME_LENGTH); + memcpy(WBUFP(buf,56), wd->msg, wd->len); + wd->count = mapif_sendall(buf,WBUFW(buf,2)); + + return 0; +} +// Wis sending result +int mapif_wis_end(struct WisData *wd,int flag) +{ + unsigned char buf[27]; + + WBUFW(buf, 0)=0x3802; + memcpy(WBUFP(buf, 2),wd->src,24); + WBUFB(buf,26)=flag; + mapif_send(wd->fd,buf,27); +// printf("inter server wis_end %d\n",flag); + return 0; +} + +int mapif_account_reg(int fd,unsigned char *src) +{ +// unsigned char buf[WBUFW(src,2)]; <- Hey, can this really be done? [Skotlex] + unsigned char *buf = aCalloc(1,WBUFW(src,2)); // [Lance] - Skot... Dynamic allocation is better :D + memcpy(WBUFP(buf,0),src,WBUFW(src,2)); + WBUFW(buf, 0)=0x3804; + mapif_sendallwos(fd, buf, WBUFW(buf,2)); + aFree(buf); + return 0; +} + +// Send the requested account_reg +int mapif_account_reg_reply(int fd,int account_id,int char_id, int type) +{ + struct accreg *reg=accreg_pt; + WFIFOHEAD(fd, 13 + 5000); + inter_accreg_fromsql(account_id,char_id,reg,type); + + WFIFOW(fd,0)=0x3804; + WFIFOL(fd,4)=account_id; + WFIFOL(fd,8)=char_id; + WFIFOB(fd,12)=type; + if(reg->reg_num==0){ + WFIFOW(fd,2)=13; + }else{ + int i,p; + for (p=13,i = 0; i < reg->reg_num && p < 5000; i++) { + p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].str)+1; //We add 1 to consider the '\0' in place. + p+= sprintf(WFIFOP(fd,p), "%s", reg->reg[i].value)+1; + } + WFIFOW(fd,2)=p; + if (p>= 5000) + ShowWarning("Too many acc regs for %d:%d, not all values were loaded.\n", account_id, char_id); + } + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} + +int mapif_send_gmaccounts() +{ + int i, len = 4; + unsigned char buf[32000]; + + // forward the gm accounts to the map server + len = 4; + WBUFW(buf,0) = 0x2b15; + + for(i = 0; i < GM_num; i++) { + WBUFL(buf, len) = gm_account[i].account_id; + WBUFB(buf, len+4) = (unsigned char)gm_account[i].level; + len += 5; + } + WBUFW(buf, 2) = len; + mapif_sendall(buf, len); + + return 0; +} + +//Sends to map server the current max Account/Char id [Skotlex] +void mapif_send_maxid(int account_id, int char_id) +{ + unsigned char buf[12]; + + WBUFW(buf,0) = 0x2b07; + WBUFL(buf,2) = account_id; + WBUFL(buf,6) = char_id; + mapif_sendall(buf, 10); +} + +//Request to kick char from a certain map server. [Skotlex] +int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason) +{ + if (fd >= 0) + { + WFIFOHEAD(fd,7); + WFIFOW(fd,0) = 0x2b1f; + WFIFOL(fd,2) = account_id; + WFIFOB(fd,6) = reason; + WFIFOSET(fd,7); + return 0; + } + return -1; +} + +//-------------------------------------------------------- + +// Existence check of WISP data +int check_ttl_wisdata_sub(DBKey key, void *data, va_list ap) { + unsigned long tick; + struct WisData *wd = (struct WisData *)data; + tick = va_arg(ap, unsigned long); + + if (DIFF_TICK(tick, wd->tick) > WISDATA_TTL && wis_delnum < WISDELLIST_MAX) + wis_dellist[wis_delnum++] = wd->id; + + return 0; +} + +int check_ttl_wisdata(void) { + unsigned long tick = gettick(); + int i; + + do { + wis_delnum = 0; + wis_db->foreach(wis_db, check_ttl_wisdata_sub, tick); + for(i = 0; i < wis_delnum; i++) { + struct WisData *wd = idb_get(wis_db, wis_dellist[i]); + ShowWarning("inter: wis data id=%d time out : from %s to %s\n", wd->id, wd->src, wd->dst); + // removed. not send information after a timeout. Just no answer for the player + //mapif_wis_end(wd, 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + idb_remove(wis_db, wd->id); + } + } while(wis_delnum >= WISDELLIST_MAX); + + return 0; +} + +//-------------------------------------------------------- + +// GM message sending +int mapif_parse_GMmessage(int fd) +{ + RFIFOHEAD(fd); + mapif_GMmessage(RFIFOP(fd, 8), RFIFOW(fd, 2), RFIFOL(fd, 4), fd); + return 0; +} + + +// Wisp/page request to send +int mapif_parse_WisRequest(int fd) { + struct WisData* wd; + static int wisid = 0; + char name[NAME_LENGTH], t_name[NAME_LENGTH*2]; //Needs space to allocate names with escaped chars [Skotlex] + RFIFOHEAD(fd); + if ( fd <= 0 ) {return 0;} // check if we have a valid fd + + if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) { + ShowWarning("inter: Wis message size too long.\n"); + return 0; + } else if (RFIFOW(fd,2)-52 <= 0) { // normaly, impossible, but who knows... + ShowError("inter: Wis message doesn't exist.\n"); + return 0; + } + memcpy(name, RFIFOP(fd,28), NAME_LENGTH); //Received name may be too large and not contain \0! [Skotlex] + name[NAME_LENGTH-1]= '\0'; + + sprintf (tmp_sql, "SELECT `name` FROM `%s` WHERE `name`='%s'", + char_db, jstrescapecpy(t_name, name)); + if(mysql_query(&mysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + sql_res = mysql_store_result(&mysql_handle); + + // search if character exists before to ask all map-servers + if (!(sql_row = mysql_fetch_row(sql_res))) { + unsigned char buf[27]; + WBUFW(buf, 0) = 0x3802; + memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH); + WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send(fd, buf, 27); + // Character exists. So, ask all map-servers + } else { + // to be sure of the correct name, rewrite it + memset(name, 0, NAME_LENGTH); + strncpy(name, sql_row[0], NAME_LENGTH); + // if source is destination, don't ask other servers. + if (strcmp((char*)RFIFOP(fd,4),name) == 0) { + unsigned char buf[27]; + WBUFW(buf, 0) = 0x3802; + memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH); + WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send(fd, buf, 27); + } else { + + CREATE(wd, struct WisData, 1); + + // Whether the failure of previous wisp/page transmission (timeout) + check_ttl_wisdata(); + + wd->id = ++wisid; + wd->fd = fd; + wd->len= RFIFOW(fd,2)-52; + memcpy(wd->src, RFIFOP(fd, 4), NAME_LENGTH); + memcpy(wd->dst, RFIFOP(fd,28), NAME_LENGTH); + memcpy(wd->msg, RFIFOP(fd,52), wd->len); + wd->tick = gettick(); + idb_put(wis_db, wd->id, wd); + mapif_wis_message(wd); + } + } + + //Freeing ... O.o + if(sql_res){ + mysql_free_result(sql_res); + } + + return 0; +} + + +// Wisp/page transmission result +int mapif_parse_WisReply(int fd) { + int id, flag; + struct WisData *wd; + RFIFOHEAD(fd); + id = RFIFOL(fd,2); + flag = RFIFOB(fd,6); + wd = idb_get(wis_db, id); + if (wd == NULL) + return 0; // This wisp was probably suppress before, because it was timeout of because of target was found on another map-server + + if ((--wd->count) <= 0 || flag != 1) { + mapif_wis_end(wd, flag); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + idb_remove(wis_db, id); + } + + return 0; +} + +// Received wisp message from map-server for ALL gm (just copy the message and resends it to ALL map-servers) +int mapif_parse_WisToGM(int fd) { + unsigned char buf[2048]; // 0x3003/0x3803 .w .24B .w .?B + RFIFOHEAD(fd); + ShowDebug("Sent packet back!\n"); + memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2)); + WBUFW(buf, 0) = 0x3803; + mapif_sendall(buf, RFIFOW(fd,2)); + + return 0; +} + +// Save account_reg into sql (type=2) +int mapif_parse_Registry(int fd) +{ + int j,p,len, max; + struct accreg *reg=accreg_pt; + RFIFOHEAD(fd); + memset(accreg_pt,0,sizeof(struct accreg)); + switch (RFIFOB(fd, 12)) { + case 3: //Character registry + max = GLOBAL_REG_NUM; + break; + case 2: //Account Registry + max = ACCOUNT_REG_NUM; + break; + case 1: //Account2 registry, must be sent over to login server. + return save_accreg2(RFIFOP(fd,4), RFIFOW(fd,2)-4); + default: + return 1; + } + for(j=0,p=13;jreg[j].str,&len); + reg->reg[j].str[len]='\0'; + p +=len+1; //+1 to skip the '\0' between strings. + sscanf(RFIFOP(fd,p), "%255c%n",reg->reg[j].value,&len); + reg->reg[j].value[len]='\0'; + p +=len+1; + } + reg->reg_num=j; + + inter_accreg_tosql(RFIFOL(fd,4),RFIFOL(fd,8),reg, RFIFOB(fd,12)); + mapif_account_reg(fd,RFIFOP(fd,0)); // Send updated accounts to other map servers. + return 0; +} + +// Request the value of all registries. +int mapif_parse_RegistryRequest(int fd) +{ + RFIFOHEAD(fd); + //Load Char Registry + if (RFIFOB(fd,12)) + mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6),3); + //Load Account Registry + if (RFIFOB(fd,11)) + mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6),2); + //Ask Login Server for Account2 values. + if (RFIFOB(fd,10)) + request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6)-2); + return 1; +} + +//-------------------------------------------------------- +int inter_parse_frommap(int fd) +{ + int cmd; + int len=0; + RFIFOHEAD(fd); + cmd=RFIFOW(fd,0); + // inter鯖管轄かを調べ + if(cmd < 0x3000 || cmd >= 0x3000 + (sizeof(inter_recv_packet_length)/ + sizeof(inter_recv_packet_length[0]) ) ) + return 0; + + if (inter_recv_packet_length[cmd-0x3000] == 0) //This is necessary, because otherwise we return 2 and the char server will just hang waiting for packets! [Skotlex] + return 0; + + // パケット長を調べる + if((len = inter_check_length(fd, inter_recv_packet_length[cmd - 0x3000])) == 0) + return 2; + + switch(cmd){ + case 0x3000: mapif_parse_GMmessage(fd); break; + case 0x3001: mapif_parse_WisRequest(fd); break; + case 0x3002: mapif_parse_WisReply(fd); break; + case 0x3003: mapif_parse_WisToGM(fd); break; + case 0x3004: mapif_parse_Registry(fd); break; + case 0x3005: mapif_parse_RegistryRequest(fd); break; + default: + if(inter_party_parse_frommap(fd)) + break; + if(inter_guild_parse_frommap(fd)) + break; + if(inter_storage_parse_frommap(fd)) + break; + if(inter_pet_parse_frommap(fd)) + break; + if(inter_homunculus_parse_frommap(fd)) //albator + break; + return 0; + } + + RFIFOSKIP(fd, len); + return 1; +} + +// RFIFO check +int inter_check_length(int fd, int length) +{ + RFIFOHEAD(fd); + if(length==-1){ // v-len packet + if(RFIFOREST(fd)<4) // packet not yet + return 0; + length = RFIFOW(fd, 2); + } + + if((int)RFIFOREST(fd) < length) // packet not yet + return 0; + + return length; +} +#endif //TXT_SQL_CONVERT diff --git a/src/char_sql/inter.h b/src/char_sql/inter.h index 8dc6f3668..ecfde71c0 100644 --- a/src/char_sql/inter.h +++ b/src/char_sql/inter.h @@ -1,56 +1,56 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INTER_SQL_H_ -#define _INTER_SQL_H_ - -int inter_init_sql(const char *file); -void inter_final(void); -int inter_parse_frommap(int fd); -int inter_mapif_init(int fd); -int mapif_send_gmaccounts(void); -void mapif_send_maxid(int, int); -int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason); - -int inter_check_length(int fd,int length); - -int inter_log(char *fmt,...); - -#define inter_cfgName "conf/inter_athena.conf" - -extern unsigned int party_share_level; -extern char inter_log_filename[1024]; - -#ifdef __WIN32 -//Windows.h need to be included before mysql.h -#include -#endif -//add include for DBMS(mysql) -#include - -extern MYSQL mysql_handle; -extern char tmp_sql[65535]; -extern MYSQL_RES* sql_res ; -extern MYSQL_ROW sql_row ; -extern int sql_cnt; - -extern MYSQL lmysql_handle; -extern MYSQL_RES* lsql_res ; -extern MYSQL_ROW lsql_row ; - -extern int char_server_port; -extern char char_server_ip[32]; -extern char char_server_id[32]; -extern char char_server_pw[32]; -extern char char_server_db[32]; - -extern int login_db_server_port; -extern char login_db_server_ip[32]; -extern char login_db_server_id[32]; -extern char login_db_server_pw[32]; -extern char login_db_server_db[32]; - -extern char main_chat_nick[16]; - -int inter_accreg_tosql(int account_id, int char_id, struct accreg *reg, int type); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INTER_SQL_H_ +#define _INTER_SQL_H_ + +int inter_init_sql(const char *file); +void inter_final(void); +int inter_parse_frommap(int fd); +int inter_mapif_init(int fd); +int mapif_send_gmaccounts(void); +void mapif_send_maxid(int, int); +int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason); + +int inter_check_length(int fd,int length); + +int inter_log(char *fmt,...); + +#define inter_cfgName "conf/inter_athena.conf" + +extern unsigned int party_share_level; +extern char inter_log_filename[1024]; + +#ifdef __WIN32 +//Windows.h need to be included before mysql.h +#include +#endif +//add include for DBMS(mysql) +#include + +extern MYSQL mysql_handle; +extern char tmp_sql[65535]; +extern MYSQL_RES* sql_res ; +extern MYSQL_ROW sql_row ; +extern int sql_cnt; + +extern MYSQL lmysql_handle; +extern MYSQL_RES* lsql_res ; +extern MYSQL_ROW lsql_row ; + +extern int char_server_port; +extern char char_server_ip[32]; +extern char char_server_id[32]; +extern char char_server_pw[32]; +extern char char_server_db[32]; + +extern int login_db_server_port; +extern char login_db_server_ip[32]; +extern char login_db_server_id[32]; +extern char login_db_server_pw[32]; +extern char login_db_server_db[32]; + +extern char main_chat_nick[16]; + +int inter_accreg_tosql(int account_id, int char_id, struct accreg *reg, int type); +#endif diff --git a/src/char_sql/itemdb.c b/src/char_sql/itemdb.c index e3d37ca85..9f76eb21c 100644 --- a/src/char_sql/itemdb.c +++ b/src/char_sql/itemdb.c @@ -1,222 +1,222 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include - -#include "itemdb.h" -#include "db.h" -#include "inter.h" -#include "char.h" -#include "utils.h" -#include "../common/showmsg.h" - -#define MAX_RANDITEM 2000 - -// ** ITEMDB_OVERRIDE_NAME_VERBOSE ** -// 定義すると、itemdb.txtとgrfで名前が異なる場合、表示します. -//#define ITEMDB_OVERRIDE_NAME_VERBOSE 1 - -char item_db_db[256]="item_db"; // added to specify item_db sql table [Valaris] -char item_db2_db[256]="item_db2"; - -static struct dbt* item_db; - -static void* create_item(DBKey key, va_list args) { - struct item_data *id; - int nameid = key.i; - CREATE(id, struct item_data, 1); - id->nameid = nameid; - id->type = IT_ETC; - return id; -} -/*========================================== - * DBの検索 - *------------------------------------------ - */ -struct item_data* itemdb_search(int nameid) -{ - return idb_ensure(item_db,nameid,create_item); -} - -/*========================================== - * - *------------------------------------------ - */ -int itemdb_isequip(int nameid) -{ - int type=itemdb_type(nameid); - if(type==IT_HEALING || type==IT_USABLE || type==IT_ETC || type==IT_CARD || type==IT_AMMO) - return 0; - return 1; -} -/*========================================== - * - *------------------------------------------ - */ -int itemdb_isequip2(struct item_data *data) -{ - if(data) { - int type=data->type; - if(type==IT_HEALING || type==IT_USABLE || type==IT_ETC || type==IT_CARD || type==IT_AMMO) - return 0; - else - return 1; - } - return 0; -} - - - -/*========================================== - * アイテムデータベースの読み込み - *------------------------------------------ - */ -static int itemdb_readdb(void) -{ - FILE *fp; - char line[1024]; - int ln=0,lines=0; - int nameid,j; - char *str[32],*p,*np; - struct item_data *id; - int i=0; - char *filename[]={ "item_db.txt","item_db2.txt" }; - - for(i=0;i<2;i++){ - sprintf(line, "%s/%s", db_path, filename[i]); - fp=fopen(line,"r"); - if(fp==NULL){ - if(i>0) - continue; - ShowFatalError("can't read %s\n",line); - exit(1); - } - - lines=0; - while(fgets(line,1020,fp)){ - lines++; - if(line[0]=='/' && line[1]=='/') - continue; - malloc_tsetdword(str,0,sizeof(str)); - for(j=0,np=p=line;j<4 && p;j++){ - str[j]=p; - p=strchr(p,','); - if(p){ *p++=0; np=p; } - } - if(str[0]==NULL) - continue; - - nameid=atoi(str[0]); - if(nameid<=0) - continue; - if (j < 4) - { //Crash-fix on broken item lines. [Skotlex] - ShowWarning("Reading %s: Insufficient fields for item with id %d, skipping.\n", filename[i], nameid); - continue; - } - ln++; - - //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Job Upper,Gender,Loc,wLV,eLV,refineable,View - id=itemdb_search(nameid); - strncpy(id->name, str[1], ITEM_NAME_LENGTH-1); - strncpy(id->jname, str[2], ITEM_NAME_LENGTH-1); - id->type=atoi(str[3]); - if (id->type == IT_DELAYCONSUME) - id->type = IT_USABLE; - } - fclose(fp); - if (ln > 0) { - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,filename[i]); - } - ln=0; // reset to 0 - } - return 0; -} - -static int itemdb_read_sqldb(void) // sql item_db read, shortened version of map-server item_db read [Valaris] -{ - unsigned short nameid; - struct item_data *id; - char *item_db_name[] = { item_db_db, item_db2_db }; - long unsigned int ln = 0; - int i; - - // ---------- - - for (i = 0; i < 2; i++) { - sprintf(tmp_sql, "SELECT * FROM `%s`", item_db_name[i]); - - // Execute the query; if the query execution succeeded... - if (mysql_query(&mysql_handle, tmp_sql) == 0) { - sql_res = mysql_store_result(&mysql_handle); - - // If the storage of the query result succeeded... - if (sql_res) { - // Parse each row in the query result into sql_row - while ((sql_row = mysql_fetch_row(sql_res))) - { /*Table structure is: - 00 id - 01 name_english - 02 name_japanese - 03 type - ... - */ - nameid = atoi(sql_row[0]); - - // If the identifier is not within the valid range, process the next row - if (nameid == 0) - continue; - - ln++; - - // ---------- - id=itemdb_search(nameid); - - strncpy(id->name, sql_row[1], ITEM_NAME_LENGTH-1); - strncpy(id->jname, sql_row[2], ITEM_NAME_LENGTH-1); - - id->type = atoi(sql_row[3]); - if (id->type == IT_DELAYCONSUME) - id->type = IT_USABLE; - } - ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, item_db_name[i]); - ln = 0; - } else { - ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - // Free the query result - mysql_free_result(sql_res); - } else { - ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -void do_final_itemdb(void) -{ - if(item_db){ - item_db->destroy(item_db,NULL); - item_db=NULL; - } -} -int do_init_itemdb(void) -{ - item_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - if (db_use_sqldbs) // it db_use_sqldbs in inter config are yes, will read from item_db for char server display [Valaris] - itemdb_read_sqldb(); - else - itemdb_readdb(); - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include + +#include "itemdb.h" +#include "db.h" +#include "inter.h" +#include "char.h" +#include "utils.h" +#include "../common/showmsg.h" + +#define MAX_RANDITEM 2000 + +// ** ITEMDB_OVERRIDE_NAME_VERBOSE ** +// 定義すると、itemdb.txtとgrfで名前が異なる場合、表示します. +//#define ITEMDB_OVERRIDE_NAME_VERBOSE 1 + +char item_db_db[256]="item_db"; // added to specify item_db sql table [Valaris] +char item_db2_db[256]="item_db2"; + +static struct dbt* item_db; + +static void* create_item(DBKey key, va_list args) { + struct item_data *id; + int nameid = key.i; + CREATE(id, struct item_data, 1); + id->nameid = nameid; + id->type = IT_ETC; + return id; +} +/*========================================== + * DBの検索 + *------------------------------------------ + */ +struct item_data* itemdb_search(int nameid) +{ + return idb_ensure(item_db,nameid,create_item); +} + +/*========================================== + * + *------------------------------------------ + */ +int itemdb_isequip(int nameid) +{ + int type=itemdb_type(nameid); + if(type==IT_HEALING || type==IT_USABLE || type==IT_ETC || type==IT_CARD || type==IT_AMMO) + return 0; + return 1; +} +/*========================================== + * + *------------------------------------------ + */ +int itemdb_isequip2(struct item_data *data) +{ + if(data) { + int type=data->type; + if(type==IT_HEALING || type==IT_USABLE || type==IT_ETC || type==IT_CARD || type==IT_AMMO) + return 0; + else + return 1; + } + return 0; +} + + + +/*========================================== + * アイテムデータベースの読み込み + *------------------------------------------ + */ +static int itemdb_readdb(void) +{ + FILE *fp; + char line[1024]; + int ln=0,lines=0; + int nameid,j; + char *str[32],*p,*np; + struct item_data *id; + int i=0; + char *filename[]={ "item_db.txt","item_db2.txt" }; + + for(i=0;i<2;i++){ + sprintf(line, "%s/%s", db_path, filename[i]); + fp=fopen(line,"r"); + if(fp==NULL){ + if(i>0) + continue; + ShowFatalError("can't read %s\n",line); + exit(1); + } + + lines=0; + while(fgets(line,1020,fp)){ + lines++; + if(line[0]=='/' && line[1]=='/') + continue; + malloc_tsetdword(str,0,sizeof(str)); + for(j=0,np=p=line;j<4 && p;j++){ + str[j]=p; + p=strchr(p,','); + if(p){ *p++=0; np=p; } + } + if(str[0]==NULL) + continue; + + nameid=atoi(str[0]); + if(nameid<=0) + continue; + if (j < 4) + { //Crash-fix on broken item lines. [Skotlex] + ShowWarning("Reading %s: Insufficient fields for item with id %d, skipping.\n", filename[i], nameid); + continue; + } + ln++; + + //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Job Upper,Gender,Loc,wLV,eLV,refineable,View + id=itemdb_search(nameid); + strncpy(id->name, str[1], ITEM_NAME_LENGTH-1); + strncpy(id->jname, str[2], ITEM_NAME_LENGTH-1); + id->type=atoi(str[3]); + if (id->type == IT_DELAYCONSUME) + id->type = IT_USABLE; + } + fclose(fp); + if (ln > 0) { + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,filename[i]); + } + ln=0; // reset to 0 + } + return 0; +} + +static int itemdb_read_sqldb(void) // sql item_db read, shortened version of map-server item_db read [Valaris] +{ + unsigned short nameid; + struct item_data *id; + char *item_db_name[] = { item_db_db, item_db2_db }; + long unsigned int ln = 0; + int i; + + // ---------- + + for (i = 0; i < 2; i++) { + sprintf(tmp_sql, "SELECT * FROM `%s`", item_db_name[i]); + + // Execute the query; if the query execution succeeded... + if (mysql_query(&mysql_handle, tmp_sql) == 0) { + sql_res = mysql_store_result(&mysql_handle); + + // If the storage of the query result succeeded... + if (sql_res) { + // Parse each row in the query result into sql_row + while ((sql_row = mysql_fetch_row(sql_res))) + { /*Table structure is: + 00 id + 01 name_english + 02 name_japanese + 03 type + ... + */ + nameid = atoi(sql_row[0]); + + // If the identifier is not within the valid range, process the next row + if (nameid == 0) + continue; + + ln++; + + // ---------- + id=itemdb_search(nameid); + + strncpy(id->name, sql_row[1], ITEM_NAME_LENGTH-1); + strncpy(id->jname, sql_row[2], ITEM_NAME_LENGTH-1); + + id->type = atoi(sql_row[3]); + if (id->type == IT_DELAYCONSUME) + id->type = IT_USABLE; + } + ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, item_db_name[i]); + ln = 0; + } else { + ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + // Free the query result + mysql_free_result(sql_res); + } else { + ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +void do_final_itemdb(void) +{ + if(item_db){ + item_db->destroy(item_db,NULL); + item_db=NULL; + } +} +int do_init_itemdb(void) +{ + item_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + if (db_use_sqldbs) // it db_use_sqldbs in inter config are yes, will read from item_db for char server display [Valaris] + itemdb_read_sqldb(); + else + itemdb_readdb(); + return 0; +} diff --git a/src/char_sql/itemdb.h b/src/char_sql/itemdb.h index d19dd498c..53b1d3e02 100644 --- a/src/char_sql/itemdb.h +++ b/src/char_sql/itemdb.h @@ -1,43 +1,43 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _ITEMDB_H_ -#define _ITEMDB_H_ -#include "mmo.h" - -//FIXME: Maybe it would be better to move this enum to mmo.h, -//instead of having it twice on the map server and here? [Skotlex] -enum { - IT_HEALING = 0, - IT_UNKNOWN, //1 - IT_USABLE, //2 - IT_ETC, //3 - IT_WEAPON, //4 - IT_ARMOR, //5 - IT_CARD, //6 - IT_PETEGG, //7 - IT_PETARMOR,//8 - IT_UNKNOWN2,//9 - IT_AMMO, //10 - IT_DELAYCONSUME,//11 - IT_MAX -} item_types; - -struct item_data { - int nameid; - char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH]; - int type; -}; - -extern char item_db_db[256]; -extern char item_db2_db[256]; -struct item_data* itemdb_search(int nameid); -#define itemdb_type(n) itemdb_search(n)->type - -int itemdb_isequip(int); -int itemdb_isequip2(struct item_data *); - -void do_final_itemdb(void); -int do_init_itemdb(void); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _ITEMDB_H_ +#define _ITEMDB_H_ +#include "mmo.h" + +//FIXME: Maybe it would be better to move this enum to mmo.h, +//instead of having it twice on the map server and here? [Skotlex] +enum { + IT_HEALING = 0, + IT_UNKNOWN, //1 + IT_USABLE, //2 + IT_ETC, //3 + IT_WEAPON, //4 + IT_ARMOR, //5 + IT_CARD, //6 + IT_PETEGG, //7 + IT_PETARMOR,//8 + IT_UNKNOWN2,//9 + IT_AMMO, //10 + IT_DELAYCONSUME,//11 + IT_MAX +} item_types; + +struct item_data { + int nameid; + char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH]; + int type; +}; + +extern char item_db_db[256]; +extern char item_db2_db[256]; +struct item_data* itemdb_search(int nameid); +#define itemdb_type(n) itemdb_search(n)->type + +int itemdb_isequip(int); +int itemdb_isequip2(struct item_data *); + +void do_final_itemdb(void); +int do_init_itemdb(void); + +#endif diff --git a/src/common/core.c b/src/common/core.c index 06ee6b2b8..7b977632d 100644 --- a/src/common/core.c +++ b/src/common/core.c @@ -1,303 +1,303 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#ifndef _WIN32 -#include -#endif -#include -#include -#include - -#include "core.h" -#include "../common/db.h" -#include "../common/mmo.h" -#include "../common/malloc.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/graph.h" -#include "../common/plugins.h" -#include "../common/version.h" -#include "../common/showmsg.h" - -#ifndef _WIN32 - #include "svnversion.h" -#endif - -int runflag = 1; -int arg_c = 0; -char **arg_v = NULL; - -char *SERVER_NAME = NULL; -char SERVER_TYPE = ATHENA_SERVER_NONE; -static void (*term_func)(void) = NULL; -#ifndef SVNVERSION - static char eA_svn_version[10]; -#endif -/*====================================== - * CORE : Set function - *-------------------------------------- - */ -void set_termfunc(void (*termfunc)(void)) -{ - term_func = termfunc; -} - -#ifndef MINICORE // minimalist Core -// Added by Gabuzomeu -// -// This is an implementation of signal() using sigaction() for portability. -// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced -// Programming in the UNIX Environment_. -// -#ifdef WIN32 // windows don't have SIGPIPE -#define SIGPIPE SIGINT -#endif - -#ifndef POSIX -#define compat_signal(signo, func) signal(signo, func) -#else -sigfunc *compat_signal(int signo, sigfunc *func) -{ - struct sigaction sact, oact; - - sact.sa_handler = func; - sigemptyset(&sact.sa_mask); - sact.sa_flags = 0; -#ifdef SA_INTERRUPT - sact.sa_flags |= SA_INTERRUPT; /* SunOS */ -#endif - - if (sigaction(signo, &sact, &oact) < 0) - return (SIG_ERR); - - return (oact.sa_handler); -} -#endif - -/*====================================== - * CORE : Signal Sub Function - *-------------------------------------- - */ -static void sig_proc(int sn) -{ - static int is_called = 0; - - switch (sn) { - case SIGINT: - case SIGTERM: - if (++is_called > 3) - exit(0); - runflag = 0; - break; -#ifndef _WIN32 - case SIGXFSZ: - // ignore and allow it to set errno to EFBIG - ShowWarning ("Max file size reached!\n"); - //run_flag = 0; // should we quit? - break; - case SIGPIPE: - ShowMessage ("Broken pipe found... closing socket\n"); // set to eof in socket.c - break; // does nothing here -#endif - } -} - -void signals_init (void) -{ - compat_signal(SIGTERM, sig_proc); - compat_signal(SIGINT, sig_proc); - - // Signal to create coredumps by system when necessary (crash) - compat_signal(SIGSEGV, SIG_DFL); - compat_signal(SIGFPE, SIG_DFL); - compat_signal(SIGILL, SIG_DFL); - #ifndef _WIN32 - compat_signal(SIGXFSZ, sig_proc); - compat_signal(SIGPIPE, sig_proc); - compat_signal(SIGBUS, SIG_DFL); - compat_signal(SIGTRAP, SIG_DFL); - #endif -} -#endif - -#ifdef SVNVERSION - #define xstringify(x) stringify(x) - #define stringify(x) #x - const char *get_svn_revision(void) - { - return xstringify(SVNVERSION); - } -#else -const char* get_svn_revision(void) -{ - FILE *fp; - - if(*eA_svn_version) - return eA_svn_version; - - if ((fp = fopen(".svn/entries", "r"))) - { - char line[1024]; - int rev; - // Check the version - if (fgets(line,sizeof(line),fp)) - { - if(!isdigit(line[0])) - { - // XML File format - while (fgets(line,sizeof(line),fp)) - if (strstr(line,"revision=")) break; - fclose(fp); - if (sscanf(line," %*[^\"]\"%d%*[^\n]", &rev) == 1) { - snprintf(eA_svn_version, sizeof(eA_svn_version), "%d", rev); - } - } - else - { - // Bin File format - fgets(line,sizeof(line),fp); // Get the name - fgets(line,sizeof(line),fp); // Get the entries kind - if(fgets(line,sizeof(line),fp)) // Get the rev numver - { - snprintf(eA_svn_version, sizeof(eA_svn_version), "%d", atoi(line)); - } - } - } - } - - if(!(*eA_svn_version)) - snprintf(eA_svn_version, sizeof(eA_svn_version), "Unknown"); - - return eA_svn_version; -} -#endif - -/*====================================== - * CORE : Display title - *-------------------------------------- - */ - -static void display_title(void) -{ - //The clearscreeen is usually more of an annoyance than anything else... [Skotlex] -// ClearScreen(); // clear screen and go up/left (0, 0 position in text) - //ShowMessage("\n"); //A blank message?? - printf("\n"); - ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n"); // white writing (37) on blue background (44), \033[K clean until end of file - ShowMessage(""CL_XXBL" ("CL_BT_YELLOW" (c)2005 eAthena Development Team presents "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33) - ShowMessage(""CL_XXBL" ("CL_BOLD" ______ __ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char - ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ _ \\/\\ \\__/\\ \\ v%2d.%02d.%02d "CL_XXBL")"CL_CLL""CL_NORMAL"\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION); // 1: bold char, 0: normal char - ShowMessage(""CL_XXBL" ("CL_BOLD" __\\ \\ \\_\\ \\ \\ ,_\\ \\ \\___ __ ___ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char - ShowMessage(""CL_XXBL" ("CL_BOLD" /'__`\\ \\ __ \\ \\ \\/\\ \\ _ `\\ /'__`\\/' _ `\\ /'__`\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char - ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ __/\\ \\ \\/\\ \\ \\ \\_\\ \\ \\ \\ \\/\\ __//\\ \\/\\ \\/\\ \\_\\.\\_ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char - ShowMessage(""CL_XXBL" ("CL_BOLD" \\ \\____\\\\ \\_\\ \\_\\ \\__\\\\ \\_\\ \\_\\ \\____\\ \\_\\ \\_\\ \\__/.\\_\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char - ShowMessage(""CL_XXBL" ("CL_BOLD" \\/____/ \\/_/\\/_/\\/__/ \\/_/\\/_/\\/____/\\/_/\\/_/\\/__/\\/_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char - ShowMessage(""CL_XXBL" ("CL_BOLD" _ _ _ _ _ _ _ _ _ _ _ _ _ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char - ShowMessage(""CL_XXBL" ("CL_BOLD" / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char - ShowMessage(""CL_XXBL" ("CL_BOLD" ( e | n | g | l | i | s | h ) ( A | t | h | e | n | a ) "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char - ShowMessage(""CL_XXBL" ("CL_BOLD" \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char - ShowMessage(""CL_XXBL" ("CL_BOLD" "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33) - ShowMessage(""CL_XXBL" ("CL_BT_YELLOW" Advanced Fusion Maps (c) 2003-2005 The Fusion Project "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33) - ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n\n"); // reset color - - ShowInfo("SVN Revision: '"CL_WHITE"%s"CL_RESET"'.\n", get_svn_revision()); -} - -// Warning if logged in as superuser (root) -void usercheck(void){ -#ifndef _WIN32 - if ((getuid() == 0) && (getgid() == 0)) { - ShowWarning ("You are running eAthena as the root superuser.\n"); - ShowWarning ("It is unnecessary and unsafe to run eAthena with root privileges.\n"); - sleep(3); - } -#endif -} - -/*====================================== - * CORE : MAINROUTINE - *-------------------------------------- - */ -#ifndef MINICORE // minimalist Core -int main (int argc, char **argv) -{ - int next; - - // initialise program arguments - { - char *p1 = SERVER_NAME = argv[0]; - char *p2 = p1; - while ((p1 = strchr(p2, '/')) != NULL || (p1 = strchr(p2, '\\')) != NULL) - { - SERVER_NAME = ++p1; - p2 = p1; - } - arg_c = argc; - arg_v = argv; - #ifndef SVNVERSION - *eA_svn_version = '\0'; - #endif - } - - set_server_type(); - display_title(); - usercheck(); - - malloc_init(); /* 一番最初に実行する必要がある */ - db_init(); - signals_init(); - - timer_init(); - socket_init(); - plugins_init(); - - do_init(argc,argv); - graph_init(); - plugin_event_trigger("Athena_Init"); - - while (runflag) { - next = do_timer(gettick_nocache()); - do_sendrecv(next); -#ifndef TURBO - do_parsepacket(); -#endif - } - - plugin_event_trigger("Athena_Final"); - graph_final(); - do_final(); - - timer_final(); - plugins_final(); - socket_final(); - db_final(); - malloc_final(); - - return 0; -} -#else -int main (int argc, char **argv) -{ - // initialise program arguments - { - char *p = SERVER_NAME = argv[0]; - while ((p = strchr(p, '/')) != NULL) - SERVER_NAME = ++p; - arg_c = argc; - arg_v = argv; - } - - display_title(); - usercheck(); - do_init(argc,argv); - do_final(); - - return 0; -} -#endif - -#ifdef BCHECK -unsigned int __invalid_size_argument_for_IOC; -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include + +#include "core.h" +#include "../common/db.h" +#include "../common/mmo.h" +#include "../common/malloc.h" +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/graph.h" +#include "../common/plugins.h" +#include "../common/version.h" +#include "../common/showmsg.h" + +#ifndef _WIN32 + #include "svnversion.h" +#endif + +int runflag = 1; +int arg_c = 0; +char **arg_v = NULL; + +char *SERVER_NAME = NULL; +char SERVER_TYPE = ATHENA_SERVER_NONE; +static void (*term_func)(void) = NULL; +#ifndef SVNVERSION + static char eA_svn_version[10]; +#endif +/*====================================== + * CORE : Set function + *-------------------------------------- + */ +void set_termfunc(void (*termfunc)(void)) +{ + term_func = termfunc; +} + +#ifndef MINICORE // minimalist Core +// Added by Gabuzomeu +// +// This is an implementation of signal() using sigaction() for portability. +// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced +// Programming in the UNIX Environment_. +// +#ifdef WIN32 // windows don't have SIGPIPE +#define SIGPIPE SIGINT +#endif + +#ifndef POSIX +#define compat_signal(signo, func) signal(signo, func) +#else +sigfunc *compat_signal(int signo, sigfunc *func) +{ + struct sigaction sact, oact; + + sact.sa_handler = func; + sigemptyset(&sact.sa_mask); + sact.sa_flags = 0; +#ifdef SA_INTERRUPT + sact.sa_flags |= SA_INTERRUPT; /* SunOS */ +#endif + + if (sigaction(signo, &sact, &oact) < 0) + return (SIG_ERR); + + return (oact.sa_handler); +} +#endif + +/*====================================== + * CORE : Signal Sub Function + *-------------------------------------- + */ +static void sig_proc(int sn) +{ + static int is_called = 0; + + switch (sn) { + case SIGINT: + case SIGTERM: + if (++is_called > 3) + exit(0); + runflag = 0; + break; +#ifndef _WIN32 + case SIGXFSZ: + // ignore and allow it to set errno to EFBIG + ShowWarning ("Max file size reached!\n"); + //run_flag = 0; // should we quit? + break; + case SIGPIPE: + ShowMessage ("Broken pipe found... closing socket\n"); // set to eof in socket.c + break; // does nothing here +#endif + } +} + +void signals_init (void) +{ + compat_signal(SIGTERM, sig_proc); + compat_signal(SIGINT, sig_proc); + + // Signal to create coredumps by system when necessary (crash) + compat_signal(SIGSEGV, SIG_DFL); + compat_signal(SIGFPE, SIG_DFL); + compat_signal(SIGILL, SIG_DFL); + #ifndef _WIN32 + compat_signal(SIGXFSZ, sig_proc); + compat_signal(SIGPIPE, sig_proc); + compat_signal(SIGBUS, SIG_DFL); + compat_signal(SIGTRAP, SIG_DFL); + #endif +} +#endif + +#ifdef SVNVERSION + #define xstringify(x) stringify(x) + #define stringify(x) #x + const char *get_svn_revision(void) + { + return xstringify(SVNVERSION); + } +#else +const char* get_svn_revision(void) +{ + FILE *fp; + + if(*eA_svn_version) + return eA_svn_version; + + if ((fp = fopen(".svn/entries", "r"))) + { + char line[1024]; + int rev; + // Check the version + if (fgets(line,sizeof(line),fp)) + { + if(!isdigit(line[0])) + { + // XML File format + while (fgets(line,sizeof(line),fp)) + if (strstr(line,"revision=")) break; + fclose(fp); + if (sscanf(line," %*[^\"]\"%d%*[^\n]", &rev) == 1) { + snprintf(eA_svn_version, sizeof(eA_svn_version), "%d", rev); + } + } + else + { + // Bin File format + fgets(line,sizeof(line),fp); // Get the name + fgets(line,sizeof(line),fp); // Get the entries kind + if(fgets(line,sizeof(line),fp)) // Get the rev numver + { + snprintf(eA_svn_version, sizeof(eA_svn_version), "%d", atoi(line)); + } + } + } + } + + if(!(*eA_svn_version)) + snprintf(eA_svn_version, sizeof(eA_svn_version), "Unknown"); + + return eA_svn_version; +} +#endif + +/*====================================== + * CORE : Display title + *-------------------------------------- + */ + +static void display_title(void) +{ + //The clearscreeen is usually more of an annoyance than anything else... [Skotlex] +// ClearScreen(); // clear screen and go up/left (0, 0 position in text) + //ShowMessage("\n"); //A blank message?? + printf("\n"); + ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n"); // white writing (37) on blue background (44), \033[K clean until end of file + ShowMessage(""CL_XXBL" ("CL_BT_YELLOW" (c)2005 eAthena Development Team presents "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33) + ShowMessage(""CL_XXBL" ("CL_BOLD" ______ __ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char + ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ _ \\/\\ \\__/\\ \\ v%2d.%02d.%02d "CL_XXBL")"CL_CLL""CL_NORMAL"\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION); // 1: bold char, 0: normal char + ShowMessage(""CL_XXBL" ("CL_BOLD" __\\ \\ \\_\\ \\ \\ ,_\\ \\ \\___ __ ___ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char + ShowMessage(""CL_XXBL" ("CL_BOLD" /'__`\\ \\ __ \\ \\ \\/\\ \\ _ `\\ /'__`\\/' _ `\\ /'__`\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char + ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ __/\\ \\ \\/\\ \\ \\ \\_\\ \\ \\ \\ \\/\\ __//\\ \\/\\ \\/\\ \\_\\.\\_ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char + ShowMessage(""CL_XXBL" ("CL_BOLD" \\ \\____\\\\ \\_\\ \\_\\ \\__\\\\ \\_\\ \\_\\ \\____\\ \\_\\ \\_\\ \\__/.\\_\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char + ShowMessage(""CL_XXBL" ("CL_BOLD" \\/____/ \\/_/\\/_/\\/__/ \\/_/\\/_/\\/____/\\/_/\\/_/\\/__/\\/_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char + ShowMessage(""CL_XXBL" ("CL_BOLD" _ _ _ _ _ _ _ _ _ _ _ _ _ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char + ShowMessage(""CL_XXBL" ("CL_BOLD" / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char + ShowMessage(""CL_XXBL" ("CL_BOLD" ( e | n | g | l | i | s | h ) ( A | t | h | e | n | a ) "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char + ShowMessage(""CL_XXBL" ("CL_BOLD" \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // 1: bold char, 0: normal char + ShowMessage(""CL_XXBL" ("CL_BOLD" "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33) + ShowMessage(""CL_XXBL" ("CL_BT_YELLOW" Advanced Fusion Maps (c) 2003-2005 The Fusion Project "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); // yellow writing (33) + ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n\n"); // reset color + + ShowInfo("SVN Revision: '"CL_WHITE"%s"CL_RESET"'.\n", get_svn_revision()); +} + +// Warning if logged in as superuser (root) +void usercheck(void){ +#ifndef _WIN32 + if ((getuid() == 0) && (getgid() == 0)) { + ShowWarning ("You are running eAthena as the root superuser.\n"); + ShowWarning ("It is unnecessary and unsafe to run eAthena with root privileges.\n"); + sleep(3); + } +#endif +} + +/*====================================== + * CORE : MAINROUTINE + *-------------------------------------- + */ +#ifndef MINICORE // minimalist Core +int main (int argc, char **argv) +{ + int next; + + // initialise program arguments + { + char *p1 = SERVER_NAME = argv[0]; + char *p2 = p1; + while ((p1 = strchr(p2, '/')) != NULL || (p1 = strchr(p2, '\\')) != NULL) + { + SERVER_NAME = ++p1; + p2 = p1; + } + arg_c = argc; + arg_v = argv; + #ifndef SVNVERSION + *eA_svn_version = '\0'; + #endif + } + + set_server_type(); + display_title(); + usercheck(); + + malloc_init(); /* 一番最初に実行する必要がある */ + db_init(); + signals_init(); + + timer_init(); + socket_init(); + plugins_init(); + + do_init(argc,argv); + graph_init(); + plugin_event_trigger("Athena_Init"); + + while (runflag) { + next = do_timer(gettick_nocache()); + do_sendrecv(next); +#ifndef TURBO + do_parsepacket(); +#endif + } + + plugin_event_trigger("Athena_Final"); + graph_final(); + do_final(); + + timer_final(); + plugins_final(); + socket_final(); + db_final(); + malloc_final(); + + return 0; +} +#else +int main (int argc, char **argv) +{ + // initialise program arguments + { + char *p = SERVER_NAME = argv[0]; + while ((p = strchr(p, '/')) != NULL) + SERVER_NAME = ++p; + arg_c = argc; + arg_v = argv; + } + + display_title(); + usercheck(); + do_init(argc,argv); + do_final(); + + return 0; +} +#endif + +#ifdef BCHECK +unsigned int __invalid_size_argument_for_IOC; +#endif diff --git a/src/common/core.h b/src/common/core.h index ce57c28a6..fffd44028 100644 --- a/src/common/core.h +++ b/src/common/core.h @@ -1,22 +1,22 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _CORE_H_ -#define _CORE_H_ - -//#define SQL_DEBUG //uncomment for debug_mysql_query instead of mysql_real_query - -extern int arg_c; -extern char **arg_v; - -extern int runflag; -extern char *SERVER_NAME; -extern char SERVER_TYPE; - -extern const char *get_svn_revision(void); -extern int do_init(int,char**); -extern void set_server_type(void); -extern void set_termfunc(void (*termfunc)(void)); -extern void do_final(void); - -#endif // _CORE_H_ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _CORE_H_ +#define _CORE_H_ + +//#define SQL_DEBUG //uncomment for debug_mysql_query instead of mysql_real_query + +extern int arg_c; +extern char **arg_v; + +extern int runflag; +extern char *SERVER_NAME; +extern char SERVER_TYPE; + +extern const char *get_svn_revision(void); +extern int do_init(int,char**); +extern void set_server_type(void); +extern void set_termfunc(void (*termfunc)(void)); +extern void do_final(void); + +#endif // _CORE_H_ diff --git a/src/common/db.c b/src/common/db.c index ebb5b9a30..28b5721e9 100644 --- a/src/common/db.c +++ b/src/common/db.c @@ -1,2448 +1,2448 @@ -/*****************************************************************************\ - * Copyright (c) Athena Dev Teams - Licensed under GNU GPL * - * For more information, see LICENCE in the main folder * - * * - * This file is separated in five sections: * - * (1) Private typedefs, enums, structures, defines and gblobal variables * - * (2) Private functions * - * (3) Protected functions used internally * - * (4) Protected functions used in the interface of the database * - * (5) Public functions * - * * - * The databases are structured as a hashtable of RED-BLACK trees. * - * * - * Properties of the RED-BLACK trees being used: * - * 1. The value of any node is greater than the value of its left child and * - * less than the value of its right child. * - * 2. Every node is colored either RED or BLACK. * - * 3. Every red node that is not a leaf has only black children. * - * 4. Every path from the root to a leaf contains the same number of black * - * nodes. * - * 5. The root node is black. * - * An n node in a RED-BLACK tree has the property that its * - * height is O(lg(n)). * - * Another important property is that after adding a node to a RED-BLACK * - * tree, the tree can be readjusted in O(lg(n)) time. * - * Similarly, after deleting a node from a RED-BLACK tree, the tree can be * - * readjusted in O(lg(n)) time. * - * {@link http://www.cs.mcgill.ca/~cs251/OldCourses/1997/topic18/} * - * * - * How to add new database types: * - * 1. Add the identifier of the new database type to the enum DBType * - * 2. If not already there, add the data type of the key to the union DBKey * - * 3. If the key can be considered NULL, update the function db_is_key_null * - * 4. If the key can be duplicated, update the functions db_dup_key and * - * db_dup_key_free * - * 5. Create a comparator and update the function db_default_cmp * - * 6. Create a hasher and update the function db_default_hash * - * 7. If the new database type requires or does not support some options, * - * update the function db_fix_options * - * * - * TODO: * - * - create test cases to test the database system thoroughly * - * - make data an enumeration * - * - finish this header describing the database system * - * - create custom database allocator * - * - make the system thread friendly * - * - change the structure of the database to T-Trees * - * - create a db that organizes itself by splaying * - * * - * HISTORY: * - * 2.1 (Athena build #???#) - Portability fix * - * - Fixed the portability of casting to union and added the functions * - * {@link DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)} and * - * {@link DBInterface#clear(DBInterface,DBApply,...)}. * - * 2.0 (Athena build 4859) - Transition version * - * - Almost everything recoded with a strategy similar to objects, * - * database structure is maintained. * - * 1.0 (up to Athena build 4706) * - * - Previous database system. * - * * - * @version 2.1 (Athena build #???#) - Portability fix * - * @author (Athena build 4859) Flavio @ Amazon Project * - * @author (up to Athena build 4706) Athena Dev Teams * - * @encoding US-ASCII * - * @see common#db.h * -\*****************************************************************************/ -#include -#include -#include - -#include "db.h" -#include "../common/mmo.h" -#include "../common/utils.h" -#include "../common/malloc.h" -#include "../common/showmsg.h" -#include "../common/ers.h" - -/*****************************************************************************\ - * (1) Private typedefs, enums, structures, defines and global variables of * - * the database system. * - * DB_ENABLE_STATS - Define to enable database statistics. * - * HASH_SIZE - Define with the size of the hashtable. * - * DBNColor - Enumeration of colors of the nodes. * - * DBNode - Structure of a node in RED-BLACK trees. * - * struct db_free - Structure that holds a deleted node to be freed. * - * Database - Struture of the database. * - * stats - Statistics about the database system. * -\*****************************************************************************/ - -/** - * If defined statistics about database nodes, database creating/destruction - * and function usage are keept and displayed when finalizing the database - * system. - * WARNING: This adds overhead to every database operation (not shure how much). - * @private - * @see #DBStats - * @see #stats - * @see #db_final(void) - */ -//#define DB_ENABLE_STATS - -/** - * Size of the hashtable in the database. - * @private - * @see Database#ht - */ -#define HASH_SIZE (256+27) - -/** - * A node in a RED-BLACK tree of the database. - * @param parent Parent node - * @param left Left child node - * @param right Right child node - * @param key Key of this database entry - * @param data Data of this database entry - * @param deleted If the node is deleted - * @param color Color of the node - * @private - * @see Database#ht - */ -typedef struct dbn { - // Tree structure - struct dbn *parent; - struct dbn *left; - struct dbn *right; - // Node data - DBKey key; - void *data; - // Other - enum {RED, BLACK} color; - unsigned deleted : 1; -} *DBNode; - -/** - * Structure that holds a deleted node. - * @param node Deleted node - * @param root Address to the root of the tree - * @private - * @see Database#free_list - */ -struct db_free { - DBNode node; - DBNode *root; -}; - -/** - * Complete database structure. - * @param dbi Interface of the database - * @param alloc_file File where the database was allocated - * @param alloc_line Line in the file where the database was allocated - * @param free_list Array of deleted nodes to be freed - * @param free_count Number of deleted nodes in free_list - * @param free_max Current maximum capacity of free_list - * @param free_lock Lock for freeing the nodes - * @param nodes Manager of reusable tree nodes - * @param cmp Comparator of the database - * @param hash Hasher of the database - * @param release Releaser of the database - * @param ht Hashtable of RED-BLACK trees - * @param type Type of the database - * @param options Options of the database - * @param item_count Number of items in the database - * @param maxlen Maximum length of strings in DB_STRING and DB_ISTRING databases - * @param global_lock Global lock of the database - * @private - * @see common\db.h#DBInterface - * @see #HASH_SIZE - * @see #DBNode - * @see #struct db_free - * @see common\db.h#DBComparator(void *,void *) - * @see common\db.h#DBHasher(void *) - * @see common\db.h#DBReleaser(void *,void *,DBRelease) - * @see common\db.h#DBOptions - * @see common\db.h#DBType - * @see #db_alloc(const char *,int,DBOptions,DBType,...) - */ -typedef struct db { - // Database interface - struct dbt dbi; - // File and line of allocation - const char *alloc_file; - int alloc_line; - // Lock system - struct db_free *free_list; - unsigned int free_count; - unsigned int free_max; - unsigned int free_lock; - // Other - ERInterface nodes; - DBComparator cmp; - DBHasher hash; - DBReleaser release; - DBNode ht[HASH_SIZE]; - DBType type; - DBOptions options; - unsigned int item_count; - unsigned short maxlen; - unsigned global_lock : 1; -} *Database; - -#ifdef DB_ENABLE_STATS -/** - * Structure with what is counted when the database estatistics are enabled. - * @private - * @see #DB_ENABLE_STATS - * @see #stats - */ -static struct { - // Node alloc/free - unsigned int db_node_alloc; - unsigned int db_node_free; - // Database creating/destruction counters - unsigned int db_int_alloc; - unsigned int db_uint_alloc; - unsigned int db_string_alloc; - unsigned int db_istring_alloc; - unsigned int db_int_destroy; - unsigned int db_uint_destroy; - unsigned int db_string_destroy; - unsigned int db_istring_destroy; - // Function usage counters - unsigned int db_rotate_left; - unsigned int db_rotate_right; - unsigned int db_rebalance; - unsigned int db_rebalance_erase; - unsigned int db_is_key_null; - unsigned int db_dup_key; - unsigned int db_dup_key_free; - unsigned int db_free_add; - unsigned int db_free_remove; - unsigned int db_free_lock; - unsigned int db_free_unlock; - unsigned int db_int_cmp; - unsigned int db_uint_cmp; - unsigned int db_string_cmp; - unsigned int db_istring_cmp; - unsigned int db_int_hash; - unsigned int db_uint_hash; - unsigned int db_string_hash; - unsigned int db_istring_hash; - unsigned int db_release_nothing; - unsigned int db_release_key; - unsigned int db_release_data; - unsigned int db_release_both; - unsigned int db_get; - unsigned int db_getall; - unsigned int db_vgetall; - unsigned int db_ensure; - unsigned int db_vensure; - unsigned int db_put; - unsigned int db_remove; - unsigned int db_foreach; - unsigned int db_vforeach; - unsigned int db_clear; - unsigned int db_vclear; - unsigned int db_destroy; - unsigned int db_vdestroy; - unsigned int db_size; - unsigned int db_type; - unsigned int db_options; - unsigned int db_fix_options; - unsigned int db_default_cmp; - unsigned int db_default_hash; - unsigned int db_default_release; - unsigned int db_custom_release; - unsigned int db_alloc; - unsigned int db_i2key; - unsigned int db_ui2key; - unsigned int db_str2key; - unsigned int db_init; - unsigned int db_final; -} stats = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 -}; -#endif /* DB_ENABLE_STATS */ - -/*****************************************************************************\ - * (2) Section of private functions used by the database system. * - * db_rotate_left - Rotate a tree node to the left. * - * db_rotate_right - Rotate a tree node to the right. * - * db_rebalance - Rebalance the tree. * - * db_rebalance_erase - Rebalance the tree after a BLACK node was erased. * - * db_is_key_null - Returns not 0 if the key is considered NULL. * - * db_dup_key - Duplicate a key for internal use. * - * db_dup_key_free - Free the duplicated key. * - * db_free_add - Add a node to the free_list of a database. * - * db_free_remove - Remove a node from the free_list of a database. * - * db_free_lock - Increment the free_lock of a database. * - * db_free_unlock - Decrement the free_lock of a database. * - * If it was the last lock, frees the nodes in free_list. * - * NOTE: Keeps the database trees balanced. * -\*****************************************************************************/ - -/** - * Rotate a node to the left. - * @param node Node to be rotated - * @param root Pointer to the root of the tree - * @private - * @see #db_rebalance(DBNode,DBNode *) - * @see #db_rebalance_erase(DBNode,DBNode *) - */ -static void db_rotate_left(DBNode node, DBNode *root) -{ - DBNode y = node->right; - -#ifdef DB_ENABLE_STATS - if (stats.db_rotate_left != (unsigned int)~0) stats.db_rotate_left++; -#endif /* DB_ENABLE_STATS */ - // put the left of y at the right of node - node->right = y->left; - if (y->left) - y->left->parent = node; - y->parent = node->parent; - // link y and node's parent - if (node == *root) { - *root = y; // node was root - } else if (node == node->parent->left) { - node->parent->left = y; // node was at the left - } else { - node->parent->right = y; // node was at the right - } - // put node at the left of y - y->left = node; - node->parent = y; -} - -/** - * Rotate a node to the right - * @param node Node to be rotated - * @param root Pointer to the root of the tree - * @private - * @see #db_rebalance(DBNode,DBNode *) - * @see #db_rebalance_erase(DBNode,DBNode *) - */ -static void db_rotate_right(DBNode node, DBNode *root) -{ - DBNode y = node->left; - -#ifdef DB_ENABLE_STATS - if (stats.db_rotate_right != (unsigned int)~0) stats.db_rotate_right++; -#endif /* DB_ENABLE_STATS */ - // put the right of y at the left of node - node->left = y->right; - if (y->right != 0) - y->right->parent = node; - y->parent = node->parent; - // link y and node's parent - if (node == *root) { - *root = y; // node was root - } else if (node == node->parent->right) { - node->parent->right = y; // node was at the right - } else { - node->parent->left = y; // node was at the left - } - // put node at the right of y - y->right = node; - node->parent = y; -} - -/** - * Rebalance the RED-BLACK tree. - * Called when the node and it's parent are both RED. - * @param node Node to be rebalanced - * @param root Pointer to the root of the tree - * @private - * @see #db_rotate_left(DBNode,DBNode *) - * @see #db_rotate_right(DBNode,DBNode *) - * @see #db_put(DBInterface,DBKey,void *) - */ -static void db_rebalance(DBNode node, DBNode *root) -{ - DBNode y; - -#ifdef DB_ENABLE_STATS - if (stats.db_rebalance != (unsigned int)~0) stats.db_rebalance++; -#endif /* DB_ENABLE_STATS */ - // Restore the RED-BLACK properties - node->color = RED; - while (node != *root && node->parent->color == RED) { - if (node->parent == node->parent->parent->left) { - // If node's parent is a left, y is node's right 'uncle' - y = node->parent->parent->right; - if (y && y->color == RED) { // case 1 - // change the colors and move up the tree - node->parent->color = BLACK; - y->color = BLACK; - node->parent->parent->color = RED; - node = node->parent->parent; - } else { - if (node == node->parent->right) { // case 2 - // move up and rotate - node = node->parent; - db_rotate_left(node, root); - } - // case 3 - node->parent->color = BLACK; - node->parent->parent->color = RED; - db_rotate_right(node->parent->parent, root); - } - } else { - // If node's parent is a right, y is node's left 'uncle' - y = node->parent->parent->left; - if (y && y->color == RED) { // case 1 - // change the colors and move up the tree - node->parent->color = BLACK; - y->color = BLACK; - node->parent->parent->color = RED; - node = node->parent->parent; - } else { - if (node == node->parent->left) { // case 2 - // move up and rotate - node = node->parent; - db_rotate_right(node, root); - } - // case 3 - node->parent->color = BLACK; - node->parent->parent->color = RED; - db_rotate_left(node->parent->parent, root); - } - } - } - (*root)->color = BLACK; // the root can and should always be black -} - -/** - * Erase a node from the RED-BLACK tree, keeping the tree balanced. - * @param node Node to be erased from the tree - * @param root Root of the tree - * @private - * @see #db_rotate_left(DBNode,DBNode *) - * @see #db_rotate_right(DBNode,DBNode *) - * @see #db_free_unlock(Database) - */ -static void db_rebalance_erase(DBNode node, DBNode *root) -{ - DBNode y = node; - DBNode x = NULL; - DBNode x_parent = NULL; - DBNode w; - -#ifdef DB_ENABLE_STATS - if (stats.db_rebalance_erase != (unsigned int)~0) stats.db_rebalance_erase++; -#endif /* DB_ENABLE_STATS */ - // Select where to change the tree - if (y->left == NULL) { // no left - x = y->right; - } else if (y->right == NULL) { // no right - x = y->left; - } else { // both exist, go to the leftmost node of the right sub-tree - y = y->right; - while (y->left != NULL) - y = y->left; - x = y->right; - } - - // Remove the node from the tree - if (y != node) { // both childs existed - // put the left of 'node' in the left of 'y' - node->left->parent = y; - y->left = node->left; - - // 'y' is not the direct child of 'node' - if (y != node->right) { - // put 'x' in the old position of 'y' - x_parent = y->parent; - if (x) x->parent = y->parent; - y->parent->left = x; - // put the right of 'node' in 'y' - y->right = node->right; - node->right->parent = y; - // 'y' is a direct child of 'node' - } else { - x_parent = y; - } - - // link 'y' and the parent of 'node' - if (*root == node) { - *root = y; // 'node' was the root - } else if (node->parent->left == node) { - node->parent->left = y; // 'node' was at the left - } else { - node->parent->right = y; // 'node' was at the right - } - y->parent = node->parent; - // switch colors - { - int tmp = y->color; - y->color = node->color; - node->color = tmp; - } - y = node; - } else { // one child did not exist - // put x in node's position - x_parent = y->parent; - if (x) x->parent = y->parent; - // link x and node's parent - if (*root == node) { - *root = x; // node was the root - } else if (node->parent->left == node) { - node->parent->left = x; // node was at the left - } else { - node->parent->right = x; // node was at the right - } - } - - // Restore the RED-BLACK properties - if (y->color != RED) { - while (x != *root && (x == NULL || x->color == BLACK)) { - if (x == x_parent->left) { - w = x_parent->right; - if (w->color == RED) { - w->color = BLACK; - x_parent->color = RED; - db_rotate_left(x_parent, root); - w = x_parent->right; - } - if ((w->left == NULL || w->left->color == BLACK) && - (w->right == NULL || w->right->color == BLACK)) { - w->color = RED; - x = x_parent; - x_parent = x_parent->parent; - } else { - if (w->right == NULL || w->right->color == BLACK) { - if (w->left) w->left->color = BLACK; - w->color = RED; - db_rotate_right(w, root); - w = x_parent->right; - } - w->color = x_parent->color; - x_parent->color = BLACK; - if (w->right) w->right->color = BLACK; - db_rotate_left(x_parent, root); - break; - } - } else { - w = x_parent->left; - if (w->color == RED) { - w->color = BLACK; - x_parent->color = RED; - db_rotate_right(x_parent, root); - w = x_parent->left; - } - if ((w->right == NULL || w->right->color == BLACK) && - (w->left == NULL || w->left->color == BLACK)) { - w->color = RED; - x = x_parent; - x_parent = x_parent->parent; - } else { - if (w->left == NULL || w->left->color == BLACK) { - if (w->right) w->right->color = BLACK; - w->color = RED; - db_rotate_left(w, root); - w = x_parent->left; - } - w->color = x_parent->color; - x_parent->color = BLACK; - if (w->left) w->left->color = BLACK; - db_rotate_right(x_parent, root); - break; - } - } - } - if (x) x->color = BLACK; - } -} - -/** - * Returns not 0 if the key is considerd to be NULL. - * @param type Type of database - * @param key Key being tested - * @return not 0 if considered NULL, 0 otherwise - * @private - * @see common\db.h#DBType - * @see common\db.h#DBKey - * @see #db_get(DBInterface,DBKey) - * @see #db_put(DBInterface,DBKey,void *) - * @see #db_remove(DBInterface,DBKey) - */ -static int db_is_key_null(DBType type, DBKey key) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_is_key_null != (unsigned int)~0) stats.db_is_key_null++; -#endif /* DB_ENABLE_STATS */ - switch (type) { - case DB_STRING: - case DB_ISTRING: - return (key.str == NULL); - - default: // Not a pointer - return 0; - } -} - -/** - * Duplicate the key used in the database. - * @param db Database the key is being used in - * @param key Key to be duplicated - * @param Duplicated key - * @private - * @see #db_free_add(Database,DBNode,DBNode *) - * @see #db_free_remove(Database,DBNode) - * @see #db_put(DBInterface,DBKey,void *) - * @see #db_dup_key_free(Database,DBKey) - */ -static DBKey db_dup_key(Database db, DBKey key) -{ - unsigned char *str; - -#ifdef DB_ENABLE_STATS - if (stats.db_dup_key != (unsigned int)~0) stats.db_dup_key++; -#endif /* DB_ENABLE_STATS */ - switch (db->type) { - case DB_STRING: - case DB_ISTRING: - if (db->maxlen) { - CREATE(str, unsigned char, db->maxlen +1); - memcpy(str, key.str, db->maxlen); - str[db->maxlen] = '\0'; - key.str = str; - } else { - key.str = (unsigned char *)aStrdup((const char *)key.str); - } - return key; - - default: - return key; - } -} - -/** - * Free a key duplicated by db_dup_key. - * @param db Database the key is being used in - * @param key Key to be freed - * @private - * @see #db_dup_key(Database,DBKey) - */ -static void db_dup_key_free(Database db, DBKey key) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_dup_key_free != (unsigned int)~0) stats.db_dup_key_free++; -#endif /* DB_ENABLE_STATS */ - switch (db->type) { - case DB_STRING: - case DB_ISTRING: - aFree(key.str); - return; - - default: - return; - } -} - -/** - * Add a node to the free_list of the database. - * Marks the node as deleted. - * If the key isn't duplicated, the key is duplicated and released. - * @param db Target database - * @param root Root of the tree from the node - * @param node Target node - * @private - * @see #struct db_free - * @see Database#free_list - * @see Database#free_count - * @see Database#free_max - * @see #db_remove(DBInterface,DBKey) - * @see #db_free_remove(Database,DBNode) - */ -static void db_free_add(Database db, DBNode node, DBNode *root) -{ - DBKey old_key; - -#ifdef DB_ENABLE_STATS - if (stats.db_free_add != (unsigned int)~0) stats.db_free_add++; -#endif /* DB_ENABLE_STATS */ - if (db->free_lock == (unsigned int)~0) { - ShowFatalError("db_free_add: free_lock overflow\n" - "Database allocated at %s:%d\n", - db->alloc_file, db->alloc_line); - exit(EXIT_FAILURE); - } - if (!(db->options&DB_OPT_DUP_KEY)) { // Make shure we have a key until the node is freed - old_key = node->key; - node->key = db_dup_key(db, node->key); - db->release(old_key, node->data, DB_RELEASE_KEY); - } - if (db->free_count == db->free_max) { // No more space, expand free_list - db->free_max = (db->free_max<<2) +3; // = db->free_max*4 +3 - if (db->free_max <= db->free_count) { - if (db->free_count == (unsigned int)~0) { - ShowFatalError("db_free_add: free_count overflow\n" - "Database allocated at %s:%d\n", - db->alloc_file, db->alloc_line); - exit(EXIT_FAILURE); - } - db->free_max = (unsigned int)~0; - } - RECREATE(db->free_list, struct db_free, db->free_max); - } - node->deleted = 1; - db->free_list[db->free_count].node = node; - db->free_list[db->free_count].root = root; - db->free_count++; - db->item_count--; -} - -/** - * Remove a node from the free_list of the database. - * Marks the node as not deleted. - * NOTE: Frees the duplicated key of the node. - * @param db Target database - * @param node Node being removed from free_list - * @private - * @see #struct db_free - * @see Database#free_list - * @see Database#free_count - * @see #db_put(DBInterface,DBKey,void *) - * @see #db_free_add(Database,DBNode *,DBNode) - */ -static void db_free_remove(Database db, DBNode node) -{ - unsigned int i; - -#ifdef DB_ENABLE_STATS - if (stats.db_free_remove != (unsigned int)~0) stats.db_free_remove++; -#endif /* DB_ENABLE_STATS */ - for (i = 0; i < db->free_count; i++) { - if (db->free_list[i].node == node) { - if (i < db->free_count -1) // copy the last item to where the removed one was - memcpy(&db->free_list[i], &db->free_list[db->free_count -1], sizeof(struct db_free)); - db_dup_key_free(db, node->key); - break; - } - } - node->deleted = 0; - if (i == db->free_count) { - ShowWarning("db_free_remove: node was not found - database allocated at %s:%d\n", db->alloc_file, db->alloc_line); - } else { - db->free_count--; - } - db->item_count++; -} - -/** - * Increment the free_lock of the database. - * @param db Target database - * @private - * @see Database#free_lock - * @see #db_unlock(Database) - */ -static void db_free_lock(Database db) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_free_lock != (unsigned int)~0) stats.db_free_lock++; -#endif /* DB_ENABLE_STATS */ - if (db->free_lock == (unsigned int)~0) { - ShowFatalError("db_free_lock: free_lock overflow\n" - "Database allocated at %s:%d\n", - db->alloc_file, db->alloc_line); - exit(EXIT_FAILURE); - } - db->free_lock++; -} - -/** - * Decrement the free_lock of the database. - * If it was the last lock, frees the nodes of the database. - * Keeps the tree balanced. - * NOTE: Frees the duplicated keys of the nodes - * @param db Target database - * @private - * @see Database#free_lock - * @see #db_free_dbn(DBNode) - * @see #db_lock(Database) - */ -static void db_free_unlock(Database db) -{ - unsigned int i; - -#ifdef DB_ENABLE_STATS - if (stats.db_free_unlock != (unsigned int)~0) stats.db_free_unlock++; -#endif /* DB_ENABLE_STATS */ - if (db->free_lock == 0) { - ShowWarning("db_free_unlock: free_lock was already 0\n" - "Database allocated at %s:%d\n", - db->alloc_file, db->alloc_line); - } else { - db->free_lock--; - } - if (db->free_lock) - return; // Not last lock - - for (i = 0; i < db->free_count ; i++) { - db_rebalance_erase(db->free_list[i].node, db->free_list[i].root); - db_dup_key_free(db, db->free_list[i].node->key); -#ifdef DB_ENABLE_STATS - if (stats.db_node_free != (unsigned int)~0) stats.db_node_free++; -#endif /* DB_ENABLE_STATS */ - ers_free(db->nodes, db->free_list[i].node); - } - db->free_count = 0; -} - -/*****************************************************************************\ - * (3) Section of protected functions used internally. * - * NOTE: the protected functions used in the database interface are in the * - * next section. * - * db_int_cmp - Default comparator for DB_INT databases. * - * db_uint_cmp - Default comparator for DB_UINT databases. * - * db_string_cmp - Default comparator for DB_STRING databases. * - * db_istring_cmp - Default comparator for DB_ISTRING databases. * - * db_int_hash - Default hasher for DB_INT databases. * - * db_uint_hash - Default hasher for DB_UINT databases. * - * db_string_hash - Default hasher for DB_STRING databases. * - * db_istring_hash - Default hasher for DB_ISTRING databases. * - * db_release_nothing - Releaser that releases nothing. * - * db_release_key - Releaser that only releases the key. * - * db_release_data - Releaser that only releases the data. * - * db_release_both - Releaser that releases key and data. * -\*****************************************************************************/ - -/** - * Default comparator for DB_INT databases. - * Compares key1 to key2. - * Return 0 if equal, negative if lower and positive if higher. - * maxlen is ignored. - * @param key1 Key to be compared - * @param key2 Key being compared to - * @param maxlen Maximum length of the key to hash - * @return 0 if equal, negative if lower and positive if higher - * @see common\db.h#DBKey - * @see common\db.h\DBType#DB_INT - * @see common\db.h#DBComparator - * @see #db_default_cmp(DBType) - */ -static int db_int_cmp(DBKey key1, DBKey key2, unsigned short maxlen) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_int_cmp != (unsigned int)~0) stats.db_int_cmp++; -#endif /* DB_ENABLE_STATS */ - if (key1.i < key2.i) return -1; - if (key1.i > key2.i) return 1; - return 0; -} - -/** - * Default comparator for DB_UINT databases. - * Compares key1 to key2. - * Return 0 if equal, negative if lower and positive if higher. - * maxlen is ignored. - * @param key1 Key to be compared - * @param key2 Key being compared to - * @param maxlen Maximum length of the key to hash - * @return 0 if equal, negative if lower and positive if higher - * @see common\db.h#DBKey - * @see common\db.h\DBType#DB_UINT - * @see common\db.h#DBComparator - * @see #db_default_cmp(DBType) - */ -static int db_uint_cmp(DBKey key1, DBKey key2, unsigned short maxlen) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_uint_cmp != (unsigned int)~0) stats.db_uint_cmp++; -#endif /* DB_ENABLE_STATS */ - if (key1.ui < key2.ui) return -1; - if (key1.ui > key2.ui) return 1; - return 0; -} - -/** - * Default comparator for DB_STRING databases. - * Compares key1 to key2. - * Return 0 if equal, negative if lower and positive if higher. - * @param key1 Key to be compared - * @param key2 Key being compared to - * @param maxlen Maximum length of the key to hash - * @return 0 if equal, negative if lower and positive if higher - * @see common\db.h#DBKey - * @see common\db.h\DBType#DB_STRING - * @see common\db.h#DBComparator - * @see #db_default_cmp(DBType) - */ -static int db_string_cmp(DBKey key1, DBKey key2, unsigned short maxlen) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_string_cmp != (unsigned int)~0) stats.db_string_cmp++; -#endif /* DB_ENABLE_STATS */ - if (maxlen == 0) maxlen = (unsigned short)~0; - return strncmp((const char *)key1.str, (const char *)key2.str, maxlen); -} - -/** - * Default comparator for DB_ISTRING databases. - * Compares key1 to key2 case insensitively. - * Return 0 if equal, negative if lower and positive if higher. - * @param key1 Key to be compared - * @param key2 Key being compared to - * @param maxlen Maximum length of the key to hash - * @return 0 if equal, negative if lower and positive if higher - * @see common\db.h#DBKey - * @see common\db.h\DBType#DB_ISTRING - * @see common\db.h#DBComparator - * @see #db_default_cmp(DBType) - */ -static int db_istring_cmp(DBKey key1, DBKey key2, unsigned short maxlen) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_istring_cmp != (unsigned int)~0) stats.db_istring_cmp++; -#endif /* DB_ENABLE_STATS */ - if (maxlen == 0) maxlen = (unsigned short)~0; - return strncasecmp((const char *)key1.str, (const char *)key2.str, maxlen); -} - -/** - * Default hasher for DB_INT databases. - * Returns the value of the key as an unsigned int. - * maxlen is ignored. - * @param key Key to be hashed - * @param maxlen Maximum length of the key to hash - * @return hash of the key - * @see common\db.h#DBKey - * @see common\db.h\DBType#DB_INT - * @see common\db.h#DBHasher - * @see #db_default_hash(DBType) - */ -static unsigned int db_int_hash(DBKey key, unsigned short maxlen) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_int_hash != (unsigned int)~0) stats.db_int_hash++; -#endif /* DB_ENABLE_STATS */ - return (unsigned int)key.i; -} - -/** - * Default hasher for DB_UINT databases. - * Just returns the value of the key. - * maxlen is ignored. - * @param key Key to be hashed - * @param maxlen Maximum length of the key to hash - * @return hash of the key - * @see common\db.h#DBKey - * @see common\db.h\DBType#DB_UINT - * @see #db_default_hash(DBType) - */ -static unsigned int db_uint_hash(DBKey key, unsigned short maxlen) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_uint_hash != (unsigned int)~0) stats.db_uint_hash++; -#endif /* DB_ENABLE_STATS */ - return key.ui; -} - -/** - * Default hasher for DB_STRING databases. - * If maxlen if 0, the maximum number of maxlen is used instead. - * @param key Key to be hashed - * @param maxlen Maximum length of the key to hash - * @return hash of the key - * @see common\db.h#DBKey - * @see common\db.h\DBType#DB_STRING - * @see #db_default_hash(DBType) - */ -static unsigned int db_string_hash(DBKey key, unsigned short maxlen) -{ - unsigned char *k = key.str; - unsigned int hash = 0; - unsigned short i; - -#ifdef DB_ENABLE_STATS - if (stats.db_string_hash != (unsigned int)~0) stats.db_string_hash++; -#endif /* DB_ENABLE_STATS */ - if (maxlen == 0) - maxlen = (unsigned short)~0; // Maximum - - for (i = 0; *k; i++) { - hash = (hash*33 + *k++)^(hash>>24); - if (i == maxlen) - break; - } - - return hash; -} - -/** - * Default hasher for DB_ISTRING databases. - * If maxlen if 0, the maximum number of maxlen is used instead. - * @param key Key to be hashed - * @param maxlen Maximum length of the key to hash - * @return hash of the key - * @see common\db.h#DBKey - * @see common\db.h\DBType#DB_ISTRING - * @see #db_default_hash(DBType) - */ -static unsigned int db_istring_hash(DBKey key, unsigned short maxlen) -{ - unsigned char *k = key.str; - unsigned int hash = 0; - unsigned short i; - -#ifdef DB_ENABLE_STATS - if (stats.db_istring_hash != (unsigned int)~0) stats.db_istring_hash++; -#endif /* DB_ENABLE_STATS */ - if (maxlen == 0) - maxlen = (unsigned short)~0; // Maximum - - for (i = 0; *k; i++) { - hash = (hash*33 + LOWER(*k))^(hash>>24); - k++; - if (i == maxlen) - break; - } - - return hash; -} - -/** - * Releaser that releases nothing. - * @param key Key of the database entry - * @param data Data of the database entry - * @param which What is being requested to be released - * @protected - * @see common\db.h#DBKey - * @see common\db.h#DBRelease - * @see common\db.h#DBReleaser - * @see #db_default_releaser(DBType,DBOptions) - */ -static void db_release_nothing(DBKey key, void *data, DBRelease which) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_release_nothing != (unsigned int)~0) stats.db_release_nothing++; -#endif /* DB_ENABLE_STATS */ -} - -/** - * Releaser that only releases the key. - * @param key Key of the database entry - * @param data Data of the database entry - * @param which What is being requested to be released - * @protected - * @see common\db.h#DBKey - * @see common\db.h#DBRelease - * @see common\db.h#DBReleaser - * @see #db_default_release(DBType,DBOptions) - */ -static void db_release_key(DBKey key, void *data, DBRelease which) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_release_key != (unsigned int)~0) stats.db_release_key++; -#endif /* DB_ENABLE_STATS */ - if (which&DB_RELEASE_KEY) aFree(key.str); // needs to be a pointer -} - -/** - * Releaser that only releases the data. - * @param key Key of the database entry - * @param data Data of the database entry - * @param which What is being requested to be released - * @protected - * @see common\db.h#DBKey - * @see common\db.h#DBRelease - * @see common\db.h#DBReleaser - * @see #db_default_release(DBType,DBOptions) - */ -static void db_release_data(DBKey key, void *data, DBRelease which) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_release_data != (unsigned int)~0) stats.db_release_data++; -#endif /* DB_ENABLE_STATS */ - if (which&DB_RELEASE_DATA) aFree(data); -} - -/** - * Releaser that releases both key and data. - * @param key Key of the database entry - * @param data Data of the database entry - * @param which What is being requested to be released - * @protected - * @see common\db.h#DBKey - * @see common\db.h#DBRelease - * @see common\db.h#DBReleaser - * @see #db_default_release(DBType,DBOptions) - */ -static void db_release_both(DBKey key, void *data, DBRelease which) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_release_both != (unsigned int)~0) stats.db_release_both++; -#endif /* DB_ENABLE_STATS */ - if (which&DB_RELEASE_KEY) aFree(key.str); // needs to be a pointer - if (which&DB_RELEASE_DATA) aFree(data); -} - -/*****************************************************************************\ - * (4) Section with protected functions used in the interface of the * - * database. * - * db_obj_get - Get the data identified by the key. * - * db_obj_vgetall - Get the data of the matched entries. * - * db_obj_getall - Get the data of the matched entries. * - * db_obj_vensure - Get the data identified by the key, creating if it * - * doesn't exist yet. * - * db_obj_ensure - Get the data identified by the key, creating if it * - * doesn't exist yet. * - * db_obj_put - Put data identified by the key in the database. * - * db_obj_remove - Remove an entry from the database. * - * db_obj_vforeach - Apply a function to every entry in the database. * - * db_obj_foreach - Apply a function to every entry in the database. * - * db_obj_vclear - Remove all entries from the database. * - * db_obj_clear - Remove all entries from the database. * - * db_obj_vdestroy - Destroy the database, freeing all the used memory. * - * db_obj_destroy - Destroy the database, freeing all the used memory. * - * db_obj_size - Return the size of the database. * - * db_obj_type - Return the type of the database. * - * db_obj_options - Return the options of the database. * -\*****************************************************************************/ - -/** - * Get the data of the entry identifid by the key. - * @param self Interface of the database - * @param key Key that identifies the entry - * @return Data of the entry or NULL if not found - * @protected - * @see common\db.h#DBKey - * @see common\db.h#DBInterface - * @see common\db.h\DBInterface#get(DBInterface,DBKey) - */ -static void *db_obj_get(DBInterface self, DBKey key) -{ - Database db = (Database)self; - DBNode node; - int c; - void *data = NULL; - -#ifdef DB_ENABLE_STATS - if (stats.db_get != (unsigned int)~0) stats.db_get++; -#endif /* DB_ENABLE_STATS */ - if (db == NULL) return NULL; // nullpo candidate - if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) { - ShowError("db_get: Attempted to retrieve non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); - return NULL; // nullpo candidate - } - - db_free_lock(db); - node = db->ht[db->hash(key, db->maxlen)%HASH_SIZE]; - while (node) { - c = db->cmp(key, node->key, db->maxlen); - if (c == 0) { - data = node->data; - break; - } - if (c < 0) - node = node->left; - else - node = node->right; - } - db_free_unlock(db); - return data; -} - -/** - * Get the data of the entries matched by match. - * It puts a maximum of max entries into buf. - * If buf is NULL, it only counts the matches. - * Returns the number of entries that matched. - * NOTE: if the value returned is greater than max, only the - * first max entries found are put into the buffer. - * @param self Interface of the database - * @param buf Buffer to put the data of the matched entries - * @param max Maximum number of data entries to be put into buf - * @param match Function that matches the database entries - * @param ... Extra arguments for match - * @return The number of entries that matched - * @protected - * @see common\db.h#DBInterface - * @see common\db.h#DBMatcher(DBKey key, void *data, va_list args) - * @see common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list) - */ -static unsigned int db_obj_vgetall(DBInterface self, void **buf, unsigned int max, DBMatcher match, va_list args) -{ - Database db = (Database)self; - unsigned int i; - DBNode node; - DBNode parent; - unsigned int ret = 0; - -#ifdef DB_ENABLE_STATS - if (stats.db_vgetall != (unsigned int)~0) stats.db_vgetall++; -#endif /* DB_ENABLE_STATS */ - if (db == NULL) return 0; // nullpo candidate - if (match == NULL) return 0; // nullpo candidate - - db_free_lock(db); - for (i = 0; i < HASH_SIZE; i++) { - // Match in the order: current node, left tree, right tree - node = db->ht[i]; - while (node) { - parent = node->parent; - if (!(node->deleted) && match(node->key, node->data, args) == 0) { - if (buf && ret < max) - buf[ret] = node->data; - ret++; - } - if (node->left) { - node = node->left; - continue; - } - if (node->right) { - node = node->right; - continue; - } - while (node) { - parent = node->parent; - if (parent && parent->right && parent->left == node) { - node = parent->right; - break; - } - node = parent; - } - } - } - db_free_unlock(db); - return ret; -} - -/** - * Just calls {@link common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)}. - * Get the data of the entries matched by match. - * It puts a maximum of max entries into buf. - * If buf is NULL, it only counts the matches. - * Returns the number of entries that matched. - * NOTE: if the value returned is greater than max, only the - * first max entries found are put into the buffer. - * @param self Interface of the database - * @param buf Buffer to put the data of the matched entries - * @param max Maximum number of data entries to be put into buf - * @param match Function that matches the database entries - * @param ... Extra arguments for match - * @return The number of entries that matched - * @protected - * @see common\db.h#DBMatcher(DBKey key, void *data, va_list args) - * @see common\db.h#DBInterface - * @see common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list) - * @see common\db.h\DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...) - */ -static unsigned int db_obj_getall(DBInterface self, void **buf, unsigned int max, DBMatcher match, ...) -{ - va_list args; - unsigned int ret; - -#ifdef DB_ENABLE_STATS - if (stats.db_getall != (unsigned int)~0) stats.db_getall++; -#endif /* DB_ENABLE_STATS */ - if (self == NULL) return 0; // nullpo candidate - - va_start(args, match); - ret = self->vgetall(self, buf, max, match, args); - va_end(args); - return ret; -} - -/** - * Get the data of the entry identified by the key. - * If the entry does not exist, an entry is added with the data returned by - * create. - * @param self Interface of the database - * @param key Key that identifies the entry - * @param create Function used to create the data if the entry doesn't exist - * @param args Extra arguments for create - * @return Data of the entry - * @protected - * @see common\db.h#DBKey - * @see common\db.h#DBCreateData - * @see common\db.h#DBInterface - * @see common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list) - */ -static void *db_obj_vensure(DBInterface self, DBKey key, DBCreateData create, va_list args) -{ - Database db = (Database)self; - DBNode node; - DBNode parent = NULL; - unsigned int hash; - int c = 0; - void *data = NULL; - -#ifdef DB_ENABLE_STATS - if (stats.db_vensure != (unsigned int)~0) stats.db_vensure++; -#endif /* DB_ENABLE_STATS */ - if (db == NULL) return NULL; // nullpo candidate - if (create == NULL) { - ShowError("db_ensure: Create function is NULL for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); - return NULL; // nullpo candidate - } - if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) { - ShowError("db_ensure: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); - return NULL; // nullpo candidate - } - - db_free_lock(db); - hash = db->hash(key, db->maxlen)%HASH_SIZE; - node = db->ht[hash]; - while (node) { - c = db->cmp(key, node->key, db->maxlen); - if (c == 0) { - break; - } - parent = node; - if (c < 0) - node = node->left; - else - node = node->right; - } - // Create node if necessary - if (node == NULL) { - if (db->item_count == (unsigned int)~0) { - ShowError("db_vensure: item_count overflow, aborting item insertion.\n" - "Database allocated at %s:%d", - db->alloc_file, db->alloc_line); - return NULL; - } -#ifdef DB_ENABLE_STATS - if (stats.db_node_alloc != (unsigned int)~0) stats.db_node_alloc++; -#endif /* DB_ENABLE_STATS */ - node = ers_alloc(db->nodes, struct dbn); - node->left = NULL; - node->right = NULL; - node->deleted = 0; - db->item_count++; - if (c == 0) { // hash entry is empty - node->color = BLACK; - node->parent = NULL; - db->ht[hash] = node; - } else { - node->color = RED; - if (c < 0) { // put at the left - parent->left = node; - node->parent = parent; - } else { // put at the right - parent->right = node; - node->parent = parent; - } - if (parent->color == RED) // two consecutive RED nodes, must rebalance - db_rebalance(node, &db->ht[hash]); - } - // put key and data in the node - if (db->options&DB_OPT_DUP_KEY) { - node->key = db_dup_key(db, key); - if (db->options&DB_OPT_RELEASE_KEY) - db->release(key, data, DB_RELEASE_KEY); - } else { - node->key = key; - } - node->data = create(key, args); - } - data = node->data; - db_free_unlock(db); - return data; -} - -/** - * Just calls {@link common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)}. - * Get the data of the entry identified by the key. - * If the entry does not exist, an entry is added with the data returned by - * create. - * @param self Interface of the database - * @param key Key that identifies the entry - * @param create Function used to create the data if the entry doesn't exist - * @param ... Extra arguments for create - * @return Data of the entry - * @protected - * @see common\db.h#DBKey - * @see common\db.h#DBCreateData - * @see common\db.h#DBInterface - * @see common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list) - * @see common\db.h\DBInterface#ensure(DBInterface,DBKey,DBCreateData,...) - */ -static void *db_obj_ensure(DBInterface self, DBKey key, DBCreateData create, ...) -{ - va_list args; - void *ret; - -#ifdef DB_ENABLE_STATS - if (stats.db_ensure != (unsigned int)~0) stats.db_ensure++; -#endif /* DB_ENABLE_STATS */ - if (self == NULL) return 0; // nullpo candidate - - va_start(args, create); - ret = self->vensure(self, key, create, args); - va_end(args); - return ret; -} - -/** - * Put the data identified by the key in the database. - * Returns the previous data if the entry exists or NULL. - * NOTE: Uses the new key, the old one is released. - * @param self Interface of the database - * @param key Key that identifies the data - * @param data Data to be put in the database - * @return The previous data if the entry exists or NULL - * @protected - * @see common\db.h#DBKey - * @see common\db.h#DBInterface - * @see #db_malloc_dbn(void) - * @see common\db.h\DBInterface#put(DBInterface,DBKey,void *) - */ -static void *db_obj_put(DBInterface self, DBKey key, void *data) -{ - Database db = (Database)self; - DBNode node; - DBNode parent = NULL; - int c = 0; - unsigned int hash; - void *old_data = NULL; - -#ifdef DB_ENABLE_STATS - if (stats.db_put != (unsigned int)~0) stats.db_put++; -#endif /* DB_ENABLE_STATS */ - if (db == NULL) return NULL; // nullpo candidate - if (db->global_lock) { - ShowError("db_put: Database is being destroyed, aborting entry insertion.\n" - "Database allocated at %s:%d\n", - db->alloc_file, db->alloc_line); - return NULL; // nullpo candidate - } - if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) { - ShowError("db_put: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); - return NULL; // nullpo candidate - } - if (!(data || db->options&DB_OPT_ALLOW_NULL_DATA)) { - ShowError("db_put: Attempted to use non-allowed NULL data for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); - return NULL; // nullpo candidate - } - - if (db->item_count == (unsigned int)~0) { - ShowError("db_put: item_count overflow, aborting item insertion.\n" - "Database allocated at %s:%d", - db->alloc_file, db->alloc_line); - return NULL; - } - // search for an equal node - db_free_lock(db); - hash = db->hash(key, db->maxlen)%HASH_SIZE; - for (node = db->ht[hash]; node; ) { - c = db->cmp(key, node->key, db->maxlen); - if (c == 0) { // equal entry, replace - if (node->deleted) { - db_free_remove(db, node); - } else { - db->release(node->key, node->data, DB_RELEASE_BOTH); - } - old_data = node->data; - break; - } - parent = node; - if (c < 0) { - node = node->left; - } else { - node = node->right; - } - } - // allocate a new node if necessary - if (node == NULL) { -#ifdef DB_ENABLE_STATS - if (stats.db_node_alloc != (unsigned int)~0) stats.db_node_alloc++; -#endif /* DB_ENABLE_STATS */ - node = ers_alloc(db->nodes, struct dbn); - node->left = NULL; - node->right = NULL; - node->deleted = 0; - db->item_count++; - if (c == 0) { // hash entry is empty - node->color = BLACK; - node->parent = NULL; - db->ht[hash] = node; - } else { - node->color = RED; - if (c < 0) { // put at the left - parent->left = node; - node->parent = parent; - } else { // put at the right - parent->right = node; - node->parent = parent; - } - if (parent->color == RED) // two consecutive RED nodes, must rebalance - db_rebalance(node, &db->ht[hash]); - } - } - // put key and data in the node - if (db->options&DB_OPT_DUP_KEY) { - node->key = db_dup_key(db, key); - if (db->options&DB_OPT_RELEASE_KEY) - db->release(key, data, DB_RELEASE_KEY); - } else { - node->key = key; - } - node->data = data; - db_free_unlock(db); - return old_data; -} - -/** - * Remove an entry from the database. - * Returns the data of the entry. - * NOTE: The key (of the database) is released in {@link #db_free_add(Database,DBNode,DBNode *)}. - * @param self Interface of the database - * @param key Key that identifies the entry - * @return The data of the entry or NULL if not found - * @protected - * @see common\db.h#DBKey - * @see common\db.h#DBInterface - * @see #db_free_add(Database,DBNode,DBNode *) - * @see common\db.h\DBInterface#remove(DBInterface,DBKey) - */ -static void *db_obj_remove(DBInterface self, DBKey key) -{ - Database db = (Database)self; - void *data = NULL; - DBNode node; - unsigned int hash; - int c = 0; - -#ifdef DB_ENABLE_STATS - if (stats.db_remove != (unsigned int)~0) stats.db_remove++; -#endif /* DB_ENABLE_STATS */ - if (db == NULL) return NULL; // nullpo candidate - if (db->global_lock) { - ShowError("db_remove: Database is being destroyed. Aborting entry deletion.\n" - "Database allocated at %s:%d\n", - db->alloc_file, db->alloc_line); - return NULL; // nullpo candidate - } - if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) { - ShowError("db_remove: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); - return NULL; // nullpo candidate - } - - db_free_lock(db); - hash = db->hash(key, db->maxlen)%HASH_SIZE; - for(node = db->ht[hash]; node; ){ - c = db->cmp(key, node->key, db->maxlen); - if (c == 0) { - if (!(node->deleted)) { - data = node->data; - db->release(node->key, node->data, DB_RELEASE_DATA); - db_free_add(db, node, &db->ht[hash]); - } - break; - } - if (c < 0) - node = node->left; - else - node = node->right; - } - db_free_unlock(db); - return data; -} - -/** - * Apply func to every entry in the database. - * Returns the sum of values returned by func. - * @param self Interface of the database - * @param func Function to be applyed - * @param args Extra arguments for func - * @return Sum of the values returned by func - * @protected - * @see common\db.h#DBInterface - * @see common\db.h#DBApply(DBKey,void *,va_list) - * @see common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list) - */ -static int db_obj_vforeach(DBInterface self, DBApply func, va_list args) -{ - Database db = (Database)self; - unsigned int i; - int sum = 0; - DBNode node; - DBNode parent; - -#ifdef DB_ENABLE_STATS - if (stats.db_vforeach != (unsigned int)~0) stats.db_vforeach++; -#endif /* DB_ENABLE_STATS */ - if (db == NULL) return 0; // nullpo candidate - if (func == NULL) { - ShowError("db_foreach: Passed function is NULL for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); - return 0; // nullpo candidate - } - - db_free_lock(db); - for (i = 0; i < HASH_SIZE; i++) { - // Apply func in the order: current node, left node, right node - node = db->ht[i]; - while (node) { - parent = node->parent; - if (!(node->deleted)) - sum += func(node->key, node->data, args); - if (node->left) { - node = node->left; - continue; - } - if (node->right) { - node = node->right; - continue; - } - while (node) { - parent = node->parent; - if (parent && parent->right && parent->left == node) { - node = parent->right; - break; - } - node = parent; - } - } - } - db_free_unlock(db); - return sum; -} - -/** - * Just calls {@link common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list)}. - * Apply func to every entry in the database. - * Returns the sum of values returned by func. - * @param self Interface of the database - * @param func Function to be applyed - * @param ... Extra arguments for func - * @return Sum of the values returned by func - * @protected - * @see common\db.h#DBInterface - * @see common\db.h#DBApply(DBKey,void *,va_list) - * @see common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list) - * @see common\db.h\DBInterface#foreach(DBInterface,DBApply,...) - */ -static int db_obj_foreach(DBInterface self, DBApply func, ...) -{ - va_list args; - int ret; - -#ifdef DB_ENABLE_STATS - if (stats.db_foreach != (unsigned int)~0) stats.db_foreach++; -#endif /* DB_ENABLE_STATS */ - if (self == NULL) return 0; // nullpo candidate - - va_start(args, func); - ret = self->vforeach(self, func, args); - va_end(args); - return ret; -} - -/** - * Removes all entries from the database. - * Before deleting an entry, func is applyed to it. - * Releases the key and the data. - * Returns the sum of values returned by func, if it exists. - * @param self Interface of the database - * @param func Function to be applyed to every entry before deleting - * @param args Extra arguments for func - * @return Sum of values returned by func - * @protected - * @see common\db.h#DBApply(DBKey,void *,va_list) - * @see common\db.h#DBInterface - * @see common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list) - */ -static int db_obj_vclear(DBInterface self, DBApply func, va_list args) -{ - Database db = (Database)self; - int sum = 0; - unsigned int i; - DBNode node; - DBNode parent; - -#ifdef DB_ENABLE_STATS - if (stats.db_vclear != (unsigned int)~0) stats.db_vclear++; -#endif /* DB_ENABLE_STATS */ - if (db == NULL) return 0; // nullpo candidate - - db_free_lock(db); - for (i = 0; i < HASH_SIZE; i++) { - // Apply the func and delete in the order: left tree, right tree, current node - node = db->ht[i]; - db->ht[i] = NULL; - while (node) { - parent = node->parent; - if (node->left) { - node = node->left; - continue; - } - if (node->right) { - node = node->right; - continue; - } - if (node->deleted) { - db_dup_key_free(db, node->key); - } else { - if (func) - sum += func(node->key, node->data, args); - db->release(node->key, node->data, DB_RELEASE_BOTH); - node->deleted = 1; - } -#ifdef DB_ENABLE_STATS - if (stats.db_node_free != (unsigned int)~0) stats.db_node_free++; -#endif /* DB_ENABLE_STATS */ - ers_free(db->nodes, node); - if (parent) { - if (parent->left == node) - parent->left = NULL; - else - parent->right = NULL; - } - node = parent; - } - db->ht[i] = NULL; - } - db->free_count = 0; - db->item_count = 0; - db_free_unlock(db); - return sum; -} - -/** - * Just calls {@link common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list)}. - * Removes all entries from the database. - * Before deleting an entry, func is applyed to it. - * Releases the key and the data. - * Returns the sum of values returned by func, if it exists. - * NOTE: This locks the database globally. Any attempt to insert or remove - * a database entry will give an error and be aborted (except for clearing). - * @param self Interface of the database - * @param func Function to be applyed to every entry before deleting - * @param ... Extra arguments for func - * @return Sum of values returned by func - * @protected - * @see common\db.h#DBApply(DBKey,void *,va_list) - * @see common\db.h#DBInterface - * @see common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list) - * @see common\db.h\DBInterface#clear(DBInterface,DBApply,...) - */ -static int db_obj_clear(DBInterface self, DBApply func, ...) -{ - va_list args; - int ret; - -#ifdef DB_ENABLE_STATS - if (stats.db_clear != (unsigned int)~0) stats.db_clear++; -#endif /* DB_ENABLE_STATS */ - if (self == NULL) return 0; // nullpo candidate - - va_start(args, func); - ret = self->vclear(self, func, args); - va_end(args); - return ret; -} - -/** - * Finalize the database, feeing all the memory it uses. - * Before deleting an entry, func is applyed to it. - * Returns the sum of values returned by func, if it exists. - * NOTE: This locks the database globally. Any attempt to insert or remove - * a database entry will give an error and be aborted (except for clearing). - * @param self Interface of the database - * @param func Function to be applyed to every entry before deleting - * @param args Extra arguments for func - * @return Sum of values returned by func - * @protected - * @see common\db.h#DBApply(DBKey,void *,va_list) - * @see common\db.h#DBInterface - * @see common\db.h\DBInterface#vdestroy(DBInterface,DBApply,va_list) - */ -static int db_obj_vdestroy(DBInterface self, DBApply func, va_list args) -{ - Database db = (Database)self; - int sum; - -#ifdef DB_ENABLE_STATS - if (stats.db_vdestroy != (unsigned int)~0) stats.db_vdestroy++; -#endif /* DB_ENABLE_STATS */ - if (db == NULL) return 0; // nullpo candidate - if (db->global_lock) { - ShowError("db_vdestroy: Database is already locked for destruction. Aborting second database destruction.\n" - "Database allocated at %s:%d\n", - db->alloc_file, db->alloc_line); - return 0; - } - if (db->free_lock) - ShowWarning("db_vdestroy: Database is still in use, %u lock(s) left. Continuing database destruction.\n" - "Database allocated at %s:%d\n", - db->alloc_file, db->alloc_line, db->free_lock); - -#ifdef DB_ENABLE_STATS - switch (db->type) { - case DB_INT: - stats.db_int_destroy++; - break; - case DB_UINT: - stats.db_uint_destroy++; - break; - case DB_STRING: - stats.db_string_destroy++; - break; - case DB_ISTRING: - stats.db_istring_destroy++; - break; - } -#endif /* DB_ENABLE_STATS */ - db_free_lock(db); - db->global_lock = 1; - sum = self->vclear(self, func, args); - aFree(db->free_list); - db->free_list = NULL; - db->free_max = 0; - ers_destroy(db->nodes); - db_free_unlock(db); - aFree(db); - return sum; -} - -/** - * Just calls {@link common\db.h\DBInterface#db_vdestroy(DBInterface,DBApply,va_list)}. - * Finalize the database, feeing all the memory it uses. - * Before deleting an entry, func is applyed to it. - * Releases the key and the data. - * Returns the sum of values returned by func, if it exists. - * NOTE: This locks the database globally. Any attempt to insert or remove - * a database entry will give an error and be aborted. - * @param self Interface of the database - * @param func Function to be applyed to every entry before deleting - * @param ... Extra arguments for func - * @return Sum of values returned by func - * @protected - * @see common\db.h#DBApply(DBKey,void *,va_list) - * @see common\db.h#DBInterface - * @see common\db.h\DBInterface#vdestroy(DBInterface,DBApply,va_list) - * @see common\db.h\DBInterface#destroy(DBInterface,DBApply,...) - */ -static int db_obj_destroy(DBInterface self, DBApply func, ...) -{ - va_list args; - int ret; - -#ifdef DB_ENABLE_STATS - if (stats.db_destroy != (unsigned int)~0) stats.db_destroy++; -#endif /* DB_ENABLE_STATS */ - if (self == NULL) return 0; // nullpo candidate - - va_start(args, func); - ret = self->vdestroy(self, func, args); - va_end(args); - return ret; -} - -/** - * Return the size of the database (number of items in the database). - * @param self Interface of the database - * @return Size of the database - * @protected - * @see common\db.h#DBInterface - * @see Database#item_count - * @see common\db.h\DBInterface#size(DBInterface) - */ -static unsigned int db_obj_size(DBInterface self) -{ - Database db = (Database)self; - unsigned int item_count; - -#ifdef DB_ENABLE_STATS - if (stats.db_size != (unsigned int)~0) stats.db_size++; -#endif /* DB_ENABLE_STATS */ - if (db == NULL) return 0; // nullpo candidate - - db_free_lock(db); - item_count = db->item_count; - db_free_unlock(db); - - return item_count; -} - -/** - * Return the type of database. - * @param self Interface of the database - * @return Type of the database - * @protected - * @see common\db.h#DBType - * @see common\db.h#DBInterface - * @see Database#type - * @see common\db.h\DBInterface#type(DBInterface) - */ -static DBType db_obj_type(DBInterface self) -{ - Database db = (Database)self; - DBType type; - -#ifdef DB_ENABLE_STATS - if (stats.db_type != (unsigned int)~0) stats.db_type++; -#endif /* DB_ENABLE_STATS */ - if (db == NULL) return -1; // nullpo candidate - TODO what should this return? - - db_free_lock(db); - type = db->type; - db_free_unlock(db); - - return type; -} - -/** - * Return the options of the database. - * @param self Interface of the database - * @return Options of the database - * @protected - * @see common\db.h#DBOptions - * @see common\db.h#DBInterface - * @see Database#options - * @see common\db.h\DBInterface#options(DBInterface) - */ -static DBOptions db_obj_options(DBInterface self) -{ - Database db = (Database)self; - DBOptions options; - -#ifdef DB_ENABLE_STATS - if (stats.db_options != (unsigned int)~0) stats.db_options++; -#endif /* DB_ENABLE_STATS */ - if (db == NULL) return DB_OPT_BASE; // nullpo candidate - TODO what should this return? - - db_free_lock(db); - options = db->options; - db_free_unlock(db); - - return options; -} - -/*****************************************************************************\ - * (5) Section with public functions. * - * db_fix_options - Apply database type restrictions to the options. * - * db_default_cmp - Get the default comparator for a type of database. * - * db_default_hash - Get the default hasher for a type of database. * - * db_default_release - Get the default releaser for a type of database * - * with the specified options. * - * db_custom_release - Get a releaser that behaves a certains way. * - * db_alloc - Allocate a new database. * - * db_i2key - Manual cast from 'int' to 'DBKey'. * - * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. * - * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. * - * db_init - Initialize the database system. * - * db_final - Finalize the database system. * -\*****************************************************************************/ - -/** - * Returns the fixed options according to the database type. - * Sets required options and unsets unsupported options. - * For numeric databases DB_OPT_DUP_KEY and DB_OPT_RELEASE_KEY are unset. - * @param type Type of the database - * @param options Original options of the database - * @return Fixed options of the database - * @private - * @see common\db.h#DBType - * @see common\db.h#DBOptions - * @see #db_default_release(DBType,DBOptions) - * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short) - * @see common\db.h#db_fix_options(DBType,DBOptions) - */ -DBOptions db_fix_options(DBType type, DBOptions options) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_fix_options != (unsigned int)~0) stats.db_fix_options++; -#endif /* DB_ENABLE_STATS */ - switch (type) { - case DB_INT: - case DB_UINT: // Numeric database, do nothing with the keys - return options&~(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY); - - default: - ShowError("db_fix_options: Unknown database type %u with options %x\n", type, options); - case DB_STRING: - case DB_ISTRING: // String databases, no fix required - return options; - } -} - -/** - * Returns the default comparator for the specified type of database. - * @param type Type of database - * @return Comparator for the type of database or NULL if unknown database - * @public - * @see common\db.h#DBType - * @see #db_int_cmp(DBKey,DBKey,unsigned short) - * @see #db_uint_cmp(DBKey,DBKey,unsigned short) - * @see #db_string_cmp(DBKey,DBKey,unsigned short) - * @see #db_istring_cmp(DBKey,DBKey,unsigned short) - * @see common\db.h#db_default_cmp(DBType) - */ -DBComparator db_default_cmp(DBType type) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_default_cmp != (unsigned int)~0) stats.db_default_cmp++; -#endif /* DB_ENABLE_STATS */ - switch (type) { - case DB_INT: return db_int_cmp; - case DB_UINT: return db_uint_cmp; - case DB_STRING: return db_string_cmp; - case DB_ISTRING: return db_istring_cmp; - default: - ShowError("db_default_cmp: Unknown database type %u\n", type); - return NULL; - } -} - -/** - * Returns the default hasher for the specified type of database. - * @param type Type of database - * @return Hasher of the type of database or NULL if unknown database - * @public - * @see common\db.h#DBType - * @see #db_int_hash(DBKey,unsigned short) - * @see #db_uint_hash(DBKey,unsigned short) - * @see #db_string_hash(DBKey,unsigned short) - * @see #db_istring_hash(DBKey,unsigned short) - * @see common\db.h#db_default_hash(DBType) - */ -DBHasher db_default_hash(DBType type) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_default_hash != (unsigned int)~0) stats.db_default_hash++; -#endif /* DB_ENABLE_STATS */ - switch (type) { - case DB_INT: return db_int_hash; - case DB_UINT: return db_uint_hash; - case DB_STRING: return db_string_hash; - case DB_ISTRING: return db_istring_hash; - default: - ShowError("db_default_hash: Unknown database type %u\n", type); - return NULL; - } -} - -/** - * Returns the default releaser for the specified type of database with the - * specified options. - * NOTE: the options are fixed with {@link #db_fix_options(DBType,DBOptions)} - * before choosing the releaser. - * @param type Type of database - * @param options Options of the database - * @return Default releaser for the type of database with the specified options - * @public - * @see common\db.h#DBType - * @see common\db.h#DBOptions - * @see common\db.h#DBReleaser - * @see #db_release_nothing(DBKey,void *,DBRelease) - * @see #db_release_key(DBKey,void *,DBRelease) - * @see #db_release_data(DBKey,void *,DBRelease) - * @see #db_release_both(DBKey,void *,DBRelease) - * @see #db_custom_release(DBRelease) - * @see common\db.h#db_default_release(DBType,DBOptions) - */ -DBReleaser db_default_release(DBType type, DBOptions options) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_default_release != (unsigned int)~0) stats.db_default_release++; -#endif /* DB_ENABLE_STATS */ - options = db_fix_options(type, options); - if (options&DB_OPT_RELEASE_DATA) { // Release data, what about the key? - if (options&(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY)) - return db_release_both; // Release both key and data - return db_release_data; // Only release data - } - if (options&(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY)) - return db_release_key; // Only release key - return db_release_nothing; // Release nothing -} - -/** - * Returns the releaser that releases the specified release options. - * @param which Options that specified what the releaser releases - * @return Releaser for the specified release options - * @public - * @see common\db.h#DBRelease - * @see common\db.h#DBReleaser - * @see #db_release_nothing(DBKey,void *,DBRelease) - * @see #db_release_key(DBKey,void *,DBRelease) - * @see #db_release_data(DBKey,void *,DBRelease) - * @see #db_release_both(DBKey,void *,DBRelease) - * @see #db_default_release(DBType,DBOptions) - * @see common\db.h#db_custom_release(DBRelease) - */ -DBReleaser db_custom_release(DBRelease which) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_custom_release != (unsigned int)~0) stats.db_custom_release++; -#endif /* DB_ENABLE_STATS */ - switch (which) { - case DB_RELEASE_NOTHING: return db_release_nothing; - case DB_RELEASE_KEY: return db_release_key; - case DB_RELEASE_DATA: return db_release_data; - case DB_RELEASE_BOTH: return db_release_both; - default: - ShowError("db_custom_release: Unknown release options %u\n", which); - return NULL; - } -} - -/** - * Allocate a new database of the specified type. - * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)} - * before creating the database. - * @param file File where the database is being allocated - * @param line Line of the file where the database is being allocated - * @param type Type of database - * @param options Options of the database - * @param maxlen Maximum length of the string to be used as key in string - * databases - * @return The interface of the database - * @public - * @see common\db.h#DBType - * @see common\db.h#DBInterface - * @see #Database - * @see #db_fix_options(DBType,DBOptions) - * @see common\db.h#db_alloc(const char *,int,DBType,unsigned short) - */ -DBInterface db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen) -{ - Database db; - unsigned int i; - -#ifdef DB_ENABLE_STATS - if (stats.db_alloc != (unsigned int)~0) stats.db_alloc++; - switch (type) { - case DB_INT: - stats.db_int_alloc++; - break; - case DB_UINT: - stats.db_uint_alloc++; - break; - case DB_STRING: - stats.db_string_alloc++; - break; - case DB_ISTRING: - stats.db_istring_alloc++; - break; - } -#endif /* DB_ENABLE_STATS */ - CREATE(db, struct db, 1); - - options = db_fix_options(type, options); - /* Interface of the database */ - db->dbi.get = db_obj_get; - db->dbi.getall = db_obj_getall; - db->dbi.vgetall = db_obj_vgetall; - db->dbi.ensure = db_obj_ensure; - db->dbi.vensure = db_obj_vensure; - db->dbi.put = db_obj_put; - db->dbi.remove = db_obj_remove; - db->dbi.foreach = db_obj_foreach; - db->dbi.vforeach = db_obj_vforeach; - db->dbi.clear = db_obj_clear; - db->dbi.vclear = db_obj_vclear; - db->dbi.destroy = db_obj_destroy; - db->dbi.vdestroy = db_obj_vdestroy; - db->dbi.size = db_obj_size; - db->dbi.type = db_obj_type; - db->dbi.options = db_obj_options; - /* File and line of allocation */ - db->alloc_file = file; - db->alloc_line = line; - /* Lock system */ - db->free_list = NULL; - db->free_count = 0; - db->free_max = 0; - db->free_lock = 0; - /* Other */ - db->nodes = ers_new((uint32)sizeof(struct dbn)); - db->cmp = db_default_cmp(type); - db->hash = db_default_hash(type); - db->release = db_default_release(type, options); - for (i = 0; i < HASH_SIZE; i++) - db->ht[i] = NULL; - db->type = type; - db->options = options; - db->item_count = 0; - db->maxlen = maxlen; - db->global_lock = 0; - - return &db->dbi; -} - -#ifdef DB_MANUAL_CAST_TO_UNION -/** - * Manual cast from 'int' to the union DBKey. - * Created for compilers that don't support casting to unions. - * @param key Key to be casted - * @return The key as a DBKey union - * @public - * @see common\db.h#DB_MANUAL_CAST_TO_UNION - * @see #db_ui2key(unsigned int) - * @see #db_str2key(unsigned char *) - * @see common\db.h#db_i2key(int) - */ -DBKey db_i2key(int key) -{ - DBKey ret; - -#ifdef DB_ENABLE_STATS - if (stats.db_i2key != (unsigned int)~0) stats.db_i2key++; -#endif /* DB_ENABLE_STATS */ - ret.i = key; - return ret; -} - -/** - * Manual cast from 'unsigned int' to the union DBKey. - * Created for compilers that don't support casting to unions. - * @param key Key to be casted - * @return The key as a DBKey union - * @public - * @see common\db.h#DB_MANUAL_CAST_TO_UNION - * @see #db_i2key(int) - * @see #db_str2key(unsigned char *) - * @see common\db.h#db_ui2key(unsigned int) - */ -DBKey db_ui2key(unsigned int key) -{ - DBKey ret; - -#ifdef DB_ENABLE_STATS - if (stats.db_ui2key != (unsigned int)~0) stats.db_ui2key++; -#endif /* DB_ENABLE_STATS */ - ret.ui = key; - return ret; -} - -/** - * Manual cast from 'unsigned char *' to the union DBKey. - * Created for compilers that don't support casting to unions. - * @param key Key to be casted - * @return The key as a DBKey union - * @public - * @see common\db.h#DB_MANUAL_CAST_TO_UNION - * @see #db_i2key(int) - * @see #db_ui2key(unsigned int) - * @see common\db.h#db_str2key(unsigned char *) - */ -DBKey db_str2key(unsigned char *key) -{ - DBKey ret; - -#ifdef DB_ENABLE_STATS - if (stats.db_str2key != (unsigned int)~0) stats.db_str2key++; -#endif /* DB_ENABLE_STATS */ - ret.str = key; - return ret; -} -#endif /* DB_MANUAL_CAST_TO_UNION */ - -/** - * Initialize the database system. - * @public - * @see #db_final(void) - * @see common\db.h#db_init(void) - */ -void db_init(void) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_init != (unsigned int)~0) stats.db_init++; -#endif /* DB_ENABLE_STATS */ -} - -/** - * Finalize the database system. - * Frees the memory used by the block reusage system. - * @public - * @see common\db.h#DB_FINAL_NODE_CHECK - * @see #db_init(void) - * @see common\db.h#db_final(void) - */ -void db_final(void) -{ -#ifdef DB_ENABLE_STATS - if (stats.db_final != (unsigned int)~0) - stats.db_final++; - ShowInfo(CL_WHITE"Database nodes"CL_RESET":\n" - "allocated %u, freed %u\n", - stats.db_node_alloc, stats.db_node_free); - ShowInfo(CL_WHITE"Database types"CL_RESET":\n" - "DB_INT : allocated %10u, destroyed %10u\n" - "DB_UINT : allocated %10u, destroyed %10u\n" - "DB_STRING : allocated %10u, destroyed %10u\n" - "DB_ISTRING : allocated %10u, destroyed %10u\n", - stats.db_int_alloc, stats.db_int_destroy, - stats.db_uint_alloc, stats.db_uint_destroy, - stats.db_string_alloc, stats.db_string_destroy, - stats.db_istring_alloc, stats.db_istring_destroy); - ShowInfo(CL_WHITE"Database function counters"CL_RESET":\n" - "db_rotate_left %10u, db_rotate_right %10u,\n" - "db_rebalance %10u, db_rebalance_erase %10u,\n" - "db_is_key_null %10u,\n" - "db_dup_key %10u, db_dup_key_free %10u,\n" - "db_free_add %10u, db_free_remove %10u,\n" - "db_free_lock %10u, db_free_unlock %10u,\n" - "db_int_cmp %10u, db_uint_cmp %10u,\n" - "db_string_cmp %10u, db_istring_cmp %10u,\n" - "db_int_hash %10u, db_uint_hash %10u,\n" - "db_string_hash %10u, db_istring_hash %10u,\n" - "db_release_nothing %10u, db_release_key %10u,\n" - "db_release_data %10u, db_release_both %10u,\n" - "db_get %10u,\n" - "db_getall %10u, db_vgetall %10u,\n" - "db_ensure %10u, db_vensure %10u,\n" - "db_put %10u, db_remove %10u,\n" - "db_foreach %10u, db_vforeach %10u,\n" - "db_clear %10u, db_vclear %10u,\n" - "db_destroy %10u, db_vdestroy %10u,\n" - "db_size %10u, db_type %10u,\n" - "db_options %10u, db_fix_options %10u,\n" - "db_default_cmp %10u, db_default_hash %10u,\n" - "db_default_release %10u, db_custom_release %10u,\n" - "db_alloc %10u, db_i2key %10u,\n" - "db_ui2key %10u, db_str2key %10u,\n" - "db_init %10u, db_final %10u\n", - stats.db_rotate_left, stats.db_rotate_right, - stats.db_rebalance, stats.db_rebalance_erase, - stats.db_is_key_null, - stats.db_dup_key, stats.db_dup_key_free, - stats.db_free_add, stats.db_free_remove, - stats.db_free_lock, stats.db_free_unlock, - stats.db_int_cmp, stats.db_uint_cmp, - stats.db_string_cmp, stats.db_istring_cmp, - stats.db_int_hash, stats.db_uint_hash, - stats.db_string_hash, stats.db_istring_hash, - stats.db_release_nothing, stats.db_release_key, - stats.db_release_data, stats.db_release_both, - stats.db_get, - stats.db_getall, stats.db_vgetall, - stats.db_ensure, stats.db_vensure, - stats.db_put, stats.db_remove, - stats.db_foreach, stats.db_vforeach, - stats.db_clear, stats.db_vclear, - stats.db_destroy, stats.db_vdestroy, - stats.db_size, stats.db_type, - stats.db_options, stats.db_fix_options, - stats.db_default_cmp, stats.db_default_hash, - stats.db_default_release, stats.db_custom_release, - stats.db_alloc, stats.db_i2key, - stats.db_ui2key, stats.db_str2key, - stats.db_init, stats.db_final); -#endif /* DB_ENABLE_STATS */ -} - -// Link DB System - jAthena -void linkdb_insert( struct linkdb_node** head, void *key, void* data) { - struct linkdb_node *node; - if( head == NULL ) return ; - node = aMalloc( sizeof(struct linkdb_node) ); - if( *head == NULL ) { - // first node - *head = node; - node->prev = NULL; - node->next = NULL; - } else { - // link nodes - node->next = *head; - node->prev = (*head)->prev; - (*head)->prev = node; - (*head) = node; - } - node->key = key; - node->data = data; -} - -void* linkdb_search( struct linkdb_node** head, void *key) { - int n = 0; - struct linkdb_node *node; - if( head == NULL ) return NULL; - node = *head; - while( node ) { - if( node->key == key ) { - if( node->prev && n > 5 ) { - // 処理効率改善の為にheadに移動させる - if(node->prev) node->prev->next = node->next; - if(node->next) node->next->prev = node->prev; - node->next = *head; - node->prev = (*head)->prev; - (*head)->prev = node; - (*head) = node; - } - return node->data; - } - node = node->next; - n++; - } - return NULL; -} - -void* linkdb_erase( struct linkdb_node** head, void *key) { - struct linkdb_node *node; - if( head == NULL ) return NULL; - node = *head; - while( node ) { - if( node->key == key ) { - void *data = node->data; - if( node->prev == NULL ) - *head = node->next; - else - node->prev->next = node->next; - if( node->next ) - node->next->prev = node->prev; - aFree( node ); - return data; - } - node = node->next; - } - return NULL; -} - -void linkdb_replace( struct linkdb_node** head, void *key, void *data ) { - int n = 0; - struct linkdb_node *node; - if( head == NULL ) return ; - node = *head; - while( node ) { - if( node->key == key ) { - if( node->prev && n > 5 ) { - // 処理効率改善の為にheadに移動させる - if(node->prev) node->prev->next = node->next; - if(node->next) node->next->prev = node->prev; - node->next = *head; - node->prev = (*head)->prev; - (*head)->prev = node; - (*head) = node; - } - node->data = data; - return ; - } - node = node->next; - n++; - } - // 見つからないので挿入 - linkdb_insert( head, key, data ); -} - -void linkdb_final( struct linkdb_node** head ) { - struct linkdb_node *node, *node2; - if( head == NULL ) return ; - node = *head; - while( node ) { - node2 = node->next; - aFree( node ); - node = node2; - } - *head = NULL; -} - +/*****************************************************************************\ + * Copyright (c) Athena Dev Teams - Licensed under GNU GPL * + * For more information, see LICENCE in the main folder * + * * + * This file is separated in five sections: * + * (1) Private typedefs, enums, structures, defines and gblobal variables * + * (2) Private functions * + * (3) Protected functions used internally * + * (4) Protected functions used in the interface of the database * + * (5) Public functions * + * * + * The databases are structured as a hashtable of RED-BLACK trees. * + * * + * Properties of the RED-BLACK trees being used: * + * 1. The value of any node is greater than the value of its left child and * + * less than the value of its right child. * + * 2. Every node is colored either RED or BLACK. * + * 3. Every red node that is not a leaf has only black children. * + * 4. Every path from the root to a leaf contains the same number of black * + * nodes. * + * 5. The root node is black. * + * An n node in a RED-BLACK tree has the property that its * + * height is O(lg(n)). * + * Another important property is that after adding a node to a RED-BLACK * + * tree, the tree can be readjusted in O(lg(n)) time. * + * Similarly, after deleting a node from a RED-BLACK tree, the tree can be * + * readjusted in O(lg(n)) time. * + * {@link http://www.cs.mcgill.ca/~cs251/OldCourses/1997/topic18/} * + * * + * How to add new database types: * + * 1. Add the identifier of the new database type to the enum DBType * + * 2. If not already there, add the data type of the key to the union DBKey * + * 3. If the key can be considered NULL, update the function db_is_key_null * + * 4. If the key can be duplicated, update the functions db_dup_key and * + * db_dup_key_free * + * 5. Create a comparator and update the function db_default_cmp * + * 6. Create a hasher and update the function db_default_hash * + * 7. If the new database type requires or does not support some options, * + * update the function db_fix_options * + * * + * TODO: * + * - create test cases to test the database system thoroughly * + * - make data an enumeration * + * - finish this header describing the database system * + * - create custom database allocator * + * - make the system thread friendly * + * - change the structure of the database to T-Trees * + * - create a db that organizes itself by splaying * + * * + * HISTORY: * + * 2.1 (Athena build #???#) - Portability fix * + * - Fixed the portability of casting to union and added the functions * + * {@link DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)} and * + * {@link DBInterface#clear(DBInterface,DBApply,...)}. * + * 2.0 (Athena build 4859) - Transition version * + * - Almost everything recoded with a strategy similar to objects, * + * database structure is maintained. * + * 1.0 (up to Athena build 4706) * + * - Previous database system. * + * * + * @version 2.1 (Athena build #???#) - Portability fix * + * @author (Athena build 4859) Flavio @ Amazon Project * + * @author (up to Athena build 4706) Athena Dev Teams * + * @encoding US-ASCII * + * @see common#db.h * +\*****************************************************************************/ +#include +#include +#include + +#include "db.h" +#include "../common/mmo.h" +#include "../common/utils.h" +#include "../common/malloc.h" +#include "../common/showmsg.h" +#include "../common/ers.h" + +/*****************************************************************************\ + * (1) Private typedefs, enums, structures, defines and global variables of * + * the database system. * + * DB_ENABLE_STATS - Define to enable database statistics. * + * HASH_SIZE - Define with the size of the hashtable. * + * DBNColor - Enumeration of colors of the nodes. * + * DBNode - Structure of a node in RED-BLACK trees. * + * struct db_free - Structure that holds a deleted node to be freed. * + * Database - Struture of the database. * + * stats - Statistics about the database system. * +\*****************************************************************************/ + +/** + * If defined statistics about database nodes, database creating/destruction + * and function usage are keept and displayed when finalizing the database + * system. + * WARNING: This adds overhead to every database operation (not shure how much). + * @private + * @see #DBStats + * @see #stats + * @see #db_final(void) + */ +//#define DB_ENABLE_STATS + +/** + * Size of the hashtable in the database. + * @private + * @see Database#ht + */ +#define HASH_SIZE (256+27) + +/** + * A node in a RED-BLACK tree of the database. + * @param parent Parent node + * @param left Left child node + * @param right Right child node + * @param key Key of this database entry + * @param data Data of this database entry + * @param deleted If the node is deleted + * @param color Color of the node + * @private + * @see Database#ht + */ +typedef struct dbn { + // Tree structure + struct dbn *parent; + struct dbn *left; + struct dbn *right; + // Node data + DBKey key; + void *data; + // Other + enum {RED, BLACK} color; + unsigned deleted : 1; +} *DBNode; + +/** + * Structure that holds a deleted node. + * @param node Deleted node + * @param root Address to the root of the tree + * @private + * @see Database#free_list + */ +struct db_free { + DBNode node; + DBNode *root; +}; + +/** + * Complete database structure. + * @param dbi Interface of the database + * @param alloc_file File where the database was allocated + * @param alloc_line Line in the file where the database was allocated + * @param free_list Array of deleted nodes to be freed + * @param free_count Number of deleted nodes in free_list + * @param free_max Current maximum capacity of free_list + * @param free_lock Lock for freeing the nodes + * @param nodes Manager of reusable tree nodes + * @param cmp Comparator of the database + * @param hash Hasher of the database + * @param release Releaser of the database + * @param ht Hashtable of RED-BLACK trees + * @param type Type of the database + * @param options Options of the database + * @param item_count Number of items in the database + * @param maxlen Maximum length of strings in DB_STRING and DB_ISTRING databases + * @param global_lock Global lock of the database + * @private + * @see common\db.h#DBInterface + * @see #HASH_SIZE + * @see #DBNode + * @see #struct db_free + * @see common\db.h#DBComparator(void *,void *) + * @see common\db.h#DBHasher(void *) + * @see common\db.h#DBReleaser(void *,void *,DBRelease) + * @see common\db.h#DBOptions + * @see common\db.h#DBType + * @see #db_alloc(const char *,int,DBOptions,DBType,...) + */ +typedef struct db { + // Database interface + struct dbt dbi; + // File and line of allocation + const char *alloc_file; + int alloc_line; + // Lock system + struct db_free *free_list; + unsigned int free_count; + unsigned int free_max; + unsigned int free_lock; + // Other + ERInterface nodes; + DBComparator cmp; + DBHasher hash; + DBReleaser release; + DBNode ht[HASH_SIZE]; + DBType type; + DBOptions options; + unsigned int item_count; + unsigned short maxlen; + unsigned global_lock : 1; +} *Database; + +#ifdef DB_ENABLE_STATS +/** + * Structure with what is counted when the database estatistics are enabled. + * @private + * @see #DB_ENABLE_STATS + * @see #stats + */ +static struct { + // Node alloc/free + unsigned int db_node_alloc; + unsigned int db_node_free; + // Database creating/destruction counters + unsigned int db_int_alloc; + unsigned int db_uint_alloc; + unsigned int db_string_alloc; + unsigned int db_istring_alloc; + unsigned int db_int_destroy; + unsigned int db_uint_destroy; + unsigned int db_string_destroy; + unsigned int db_istring_destroy; + // Function usage counters + unsigned int db_rotate_left; + unsigned int db_rotate_right; + unsigned int db_rebalance; + unsigned int db_rebalance_erase; + unsigned int db_is_key_null; + unsigned int db_dup_key; + unsigned int db_dup_key_free; + unsigned int db_free_add; + unsigned int db_free_remove; + unsigned int db_free_lock; + unsigned int db_free_unlock; + unsigned int db_int_cmp; + unsigned int db_uint_cmp; + unsigned int db_string_cmp; + unsigned int db_istring_cmp; + unsigned int db_int_hash; + unsigned int db_uint_hash; + unsigned int db_string_hash; + unsigned int db_istring_hash; + unsigned int db_release_nothing; + unsigned int db_release_key; + unsigned int db_release_data; + unsigned int db_release_both; + unsigned int db_get; + unsigned int db_getall; + unsigned int db_vgetall; + unsigned int db_ensure; + unsigned int db_vensure; + unsigned int db_put; + unsigned int db_remove; + unsigned int db_foreach; + unsigned int db_vforeach; + unsigned int db_clear; + unsigned int db_vclear; + unsigned int db_destroy; + unsigned int db_vdestroy; + unsigned int db_size; + unsigned int db_type; + unsigned int db_options; + unsigned int db_fix_options; + unsigned int db_default_cmp; + unsigned int db_default_hash; + unsigned int db_default_release; + unsigned int db_custom_release; + unsigned int db_alloc; + unsigned int db_i2key; + unsigned int db_ui2key; + unsigned int db_str2key; + unsigned int db_init; + unsigned int db_final; +} stats = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif /* DB_ENABLE_STATS */ + +/*****************************************************************************\ + * (2) Section of private functions used by the database system. * + * db_rotate_left - Rotate a tree node to the left. * + * db_rotate_right - Rotate a tree node to the right. * + * db_rebalance - Rebalance the tree. * + * db_rebalance_erase - Rebalance the tree after a BLACK node was erased. * + * db_is_key_null - Returns not 0 if the key is considered NULL. * + * db_dup_key - Duplicate a key for internal use. * + * db_dup_key_free - Free the duplicated key. * + * db_free_add - Add a node to the free_list of a database. * + * db_free_remove - Remove a node from the free_list of a database. * + * db_free_lock - Increment the free_lock of a database. * + * db_free_unlock - Decrement the free_lock of a database. * + * If it was the last lock, frees the nodes in free_list. * + * NOTE: Keeps the database trees balanced. * +\*****************************************************************************/ + +/** + * Rotate a node to the left. + * @param node Node to be rotated + * @param root Pointer to the root of the tree + * @private + * @see #db_rebalance(DBNode,DBNode *) + * @see #db_rebalance_erase(DBNode,DBNode *) + */ +static void db_rotate_left(DBNode node, DBNode *root) +{ + DBNode y = node->right; + +#ifdef DB_ENABLE_STATS + if (stats.db_rotate_left != (unsigned int)~0) stats.db_rotate_left++; +#endif /* DB_ENABLE_STATS */ + // put the left of y at the right of node + node->right = y->left; + if (y->left) + y->left->parent = node; + y->parent = node->parent; + // link y and node's parent + if (node == *root) { + *root = y; // node was root + } else if (node == node->parent->left) { + node->parent->left = y; // node was at the left + } else { + node->parent->right = y; // node was at the right + } + // put node at the left of y + y->left = node; + node->parent = y; +} + +/** + * Rotate a node to the right + * @param node Node to be rotated + * @param root Pointer to the root of the tree + * @private + * @see #db_rebalance(DBNode,DBNode *) + * @see #db_rebalance_erase(DBNode,DBNode *) + */ +static void db_rotate_right(DBNode node, DBNode *root) +{ + DBNode y = node->left; + +#ifdef DB_ENABLE_STATS + if (stats.db_rotate_right != (unsigned int)~0) stats.db_rotate_right++; +#endif /* DB_ENABLE_STATS */ + // put the right of y at the left of node + node->left = y->right; + if (y->right != 0) + y->right->parent = node; + y->parent = node->parent; + // link y and node's parent + if (node == *root) { + *root = y; // node was root + } else if (node == node->parent->right) { + node->parent->right = y; // node was at the right + } else { + node->parent->left = y; // node was at the left + } + // put node at the right of y + y->right = node; + node->parent = y; +} + +/** + * Rebalance the RED-BLACK tree. + * Called when the node and it's parent are both RED. + * @param node Node to be rebalanced + * @param root Pointer to the root of the tree + * @private + * @see #db_rotate_left(DBNode,DBNode *) + * @see #db_rotate_right(DBNode,DBNode *) + * @see #db_put(DBInterface,DBKey,void *) + */ +static void db_rebalance(DBNode node, DBNode *root) +{ + DBNode y; + +#ifdef DB_ENABLE_STATS + if (stats.db_rebalance != (unsigned int)~0) stats.db_rebalance++; +#endif /* DB_ENABLE_STATS */ + // Restore the RED-BLACK properties + node->color = RED; + while (node != *root && node->parent->color == RED) { + if (node->parent == node->parent->parent->left) { + // If node's parent is a left, y is node's right 'uncle' + y = node->parent->parent->right; + if (y && y->color == RED) { // case 1 + // change the colors and move up the tree + node->parent->color = BLACK; + y->color = BLACK; + node->parent->parent->color = RED; + node = node->parent->parent; + } else { + if (node == node->parent->right) { // case 2 + // move up and rotate + node = node->parent; + db_rotate_left(node, root); + } + // case 3 + node->parent->color = BLACK; + node->parent->parent->color = RED; + db_rotate_right(node->parent->parent, root); + } + } else { + // If node's parent is a right, y is node's left 'uncle' + y = node->parent->parent->left; + if (y && y->color == RED) { // case 1 + // change the colors and move up the tree + node->parent->color = BLACK; + y->color = BLACK; + node->parent->parent->color = RED; + node = node->parent->parent; + } else { + if (node == node->parent->left) { // case 2 + // move up and rotate + node = node->parent; + db_rotate_right(node, root); + } + // case 3 + node->parent->color = BLACK; + node->parent->parent->color = RED; + db_rotate_left(node->parent->parent, root); + } + } + } + (*root)->color = BLACK; // the root can and should always be black +} + +/** + * Erase a node from the RED-BLACK tree, keeping the tree balanced. + * @param node Node to be erased from the tree + * @param root Root of the tree + * @private + * @see #db_rotate_left(DBNode,DBNode *) + * @see #db_rotate_right(DBNode,DBNode *) + * @see #db_free_unlock(Database) + */ +static void db_rebalance_erase(DBNode node, DBNode *root) +{ + DBNode y = node; + DBNode x = NULL; + DBNode x_parent = NULL; + DBNode w; + +#ifdef DB_ENABLE_STATS + if (stats.db_rebalance_erase != (unsigned int)~0) stats.db_rebalance_erase++; +#endif /* DB_ENABLE_STATS */ + // Select where to change the tree + if (y->left == NULL) { // no left + x = y->right; + } else if (y->right == NULL) { // no right + x = y->left; + } else { // both exist, go to the leftmost node of the right sub-tree + y = y->right; + while (y->left != NULL) + y = y->left; + x = y->right; + } + + // Remove the node from the tree + if (y != node) { // both childs existed + // put the left of 'node' in the left of 'y' + node->left->parent = y; + y->left = node->left; + + // 'y' is not the direct child of 'node' + if (y != node->right) { + // put 'x' in the old position of 'y' + x_parent = y->parent; + if (x) x->parent = y->parent; + y->parent->left = x; + // put the right of 'node' in 'y' + y->right = node->right; + node->right->parent = y; + // 'y' is a direct child of 'node' + } else { + x_parent = y; + } + + // link 'y' and the parent of 'node' + if (*root == node) { + *root = y; // 'node' was the root + } else if (node->parent->left == node) { + node->parent->left = y; // 'node' was at the left + } else { + node->parent->right = y; // 'node' was at the right + } + y->parent = node->parent; + // switch colors + { + int tmp = y->color; + y->color = node->color; + node->color = tmp; + } + y = node; + } else { // one child did not exist + // put x in node's position + x_parent = y->parent; + if (x) x->parent = y->parent; + // link x and node's parent + if (*root == node) { + *root = x; // node was the root + } else if (node->parent->left == node) { + node->parent->left = x; // node was at the left + } else { + node->parent->right = x; // node was at the right + } + } + + // Restore the RED-BLACK properties + if (y->color != RED) { + while (x != *root && (x == NULL || x->color == BLACK)) { + if (x == x_parent->left) { + w = x_parent->right; + if (w->color == RED) { + w->color = BLACK; + x_parent->color = RED; + db_rotate_left(x_parent, root); + w = x_parent->right; + } + if ((w->left == NULL || w->left->color == BLACK) && + (w->right == NULL || w->right->color == BLACK)) { + w->color = RED; + x = x_parent; + x_parent = x_parent->parent; + } else { + if (w->right == NULL || w->right->color == BLACK) { + if (w->left) w->left->color = BLACK; + w->color = RED; + db_rotate_right(w, root); + w = x_parent->right; + } + w->color = x_parent->color; + x_parent->color = BLACK; + if (w->right) w->right->color = BLACK; + db_rotate_left(x_parent, root); + break; + } + } else { + w = x_parent->left; + if (w->color == RED) { + w->color = BLACK; + x_parent->color = RED; + db_rotate_right(x_parent, root); + w = x_parent->left; + } + if ((w->right == NULL || w->right->color == BLACK) && + (w->left == NULL || w->left->color == BLACK)) { + w->color = RED; + x = x_parent; + x_parent = x_parent->parent; + } else { + if (w->left == NULL || w->left->color == BLACK) { + if (w->right) w->right->color = BLACK; + w->color = RED; + db_rotate_left(w, root); + w = x_parent->left; + } + w->color = x_parent->color; + x_parent->color = BLACK; + if (w->left) w->left->color = BLACK; + db_rotate_right(x_parent, root); + break; + } + } + } + if (x) x->color = BLACK; + } +} + +/** + * Returns not 0 if the key is considerd to be NULL. + * @param type Type of database + * @param key Key being tested + * @return not 0 if considered NULL, 0 otherwise + * @private + * @see common\db.h#DBType + * @see common\db.h#DBKey + * @see #db_get(DBInterface,DBKey) + * @see #db_put(DBInterface,DBKey,void *) + * @see #db_remove(DBInterface,DBKey) + */ +static int db_is_key_null(DBType type, DBKey key) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_is_key_null != (unsigned int)~0) stats.db_is_key_null++; +#endif /* DB_ENABLE_STATS */ + switch (type) { + case DB_STRING: + case DB_ISTRING: + return (key.str == NULL); + + default: // Not a pointer + return 0; + } +} + +/** + * Duplicate the key used in the database. + * @param db Database the key is being used in + * @param key Key to be duplicated + * @param Duplicated key + * @private + * @see #db_free_add(Database,DBNode,DBNode *) + * @see #db_free_remove(Database,DBNode) + * @see #db_put(DBInterface,DBKey,void *) + * @see #db_dup_key_free(Database,DBKey) + */ +static DBKey db_dup_key(Database db, DBKey key) +{ + unsigned char *str; + +#ifdef DB_ENABLE_STATS + if (stats.db_dup_key != (unsigned int)~0) stats.db_dup_key++; +#endif /* DB_ENABLE_STATS */ + switch (db->type) { + case DB_STRING: + case DB_ISTRING: + if (db->maxlen) { + CREATE(str, unsigned char, db->maxlen +1); + memcpy(str, key.str, db->maxlen); + str[db->maxlen] = '\0'; + key.str = str; + } else { + key.str = (unsigned char *)aStrdup((const char *)key.str); + } + return key; + + default: + return key; + } +} + +/** + * Free a key duplicated by db_dup_key. + * @param db Database the key is being used in + * @param key Key to be freed + * @private + * @see #db_dup_key(Database,DBKey) + */ +static void db_dup_key_free(Database db, DBKey key) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_dup_key_free != (unsigned int)~0) stats.db_dup_key_free++; +#endif /* DB_ENABLE_STATS */ + switch (db->type) { + case DB_STRING: + case DB_ISTRING: + aFree(key.str); + return; + + default: + return; + } +} + +/** + * Add a node to the free_list of the database. + * Marks the node as deleted. + * If the key isn't duplicated, the key is duplicated and released. + * @param db Target database + * @param root Root of the tree from the node + * @param node Target node + * @private + * @see #struct db_free + * @see Database#free_list + * @see Database#free_count + * @see Database#free_max + * @see #db_remove(DBInterface,DBKey) + * @see #db_free_remove(Database,DBNode) + */ +static void db_free_add(Database db, DBNode node, DBNode *root) +{ + DBKey old_key; + +#ifdef DB_ENABLE_STATS + if (stats.db_free_add != (unsigned int)~0) stats.db_free_add++; +#endif /* DB_ENABLE_STATS */ + if (db->free_lock == (unsigned int)~0) { + ShowFatalError("db_free_add: free_lock overflow\n" + "Database allocated at %s:%d\n", + db->alloc_file, db->alloc_line); + exit(EXIT_FAILURE); + } + if (!(db->options&DB_OPT_DUP_KEY)) { // Make shure we have a key until the node is freed + old_key = node->key; + node->key = db_dup_key(db, node->key); + db->release(old_key, node->data, DB_RELEASE_KEY); + } + if (db->free_count == db->free_max) { // No more space, expand free_list + db->free_max = (db->free_max<<2) +3; // = db->free_max*4 +3 + if (db->free_max <= db->free_count) { + if (db->free_count == (unsigned int)~0) { + ShowFatalError("db_free_add: free_count overflow\n" + "Database allocated at %s:%d\n", + db->alloc_file, db->alloc_line); + exit(EXIT_FAILURE); + } + db->free_max = (unsigned int)~0; + } + RECREATE(db->free_list, struct db_free, db->free_max); + } + node->deleted = 1; + db->free_list[db->free_count].node = node; + db->free_list[db->free_count].root = root; + db->free_count++; + db->item_count--; +} + +/** + * Remove a node from the free_list of the database. + * Marks the node as not deleted. + * NOTE: Frees the duplicated key of the node. + * @param db Target database + * @param node Node being removed from free_list + * @private + * @see #struct db_free + * @see Database#free_list + * @see Database#free_count + * @see #db_put(DBInterface,DBKey,void *) + * @see #db_free_add(Database,DBNode *,DBNode) + */ +static void db_free_remove(Database db, DBNode node) +{ + unsigned int i; + +#ifdef DB_ENABLE_STATS + if (stats.db_free_remove != (unsigned int)~0) stats.db_free_remove++; +#endif /* DB_ENABLE_STATS */ + for (i = 0; i < db->free_count; i++) { + if (db->free_list[i].node == node) { + if (i < db->free_count -1) // copy the last item to where the removed one was + memcpy(&db->free_list[i], &db->free_list[db->free_count -1], sizeof(struct db_free)); + db_dup_key_free(db, node->key); + break; + } + } + node->deleted = 0; + if (i == db->free_count) { + ShowWarning("db_free_remove: node was not found - database allocated at %s:%d\n", db->alloc_file, db->alloc_line); + } else { + db->free_count--; + } + db->item_count++; +} + +/** + * Increment the free_lock of the database. + * @param db Target database + * @private + * @see Database#free_lock + * @see #db_unlock(Database) + */ +static void db_free_lock(Database db) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_free_lock != (unsigned int)~0) stats.db_free_lock++; +#endif /* DB_ENABLE_STATS */ + if (db->free_lock == (unsigned int)~0) { + ShowFatalError("db_free_lock: free_lock overflow\n" + "Database allocated at %s:%d\n", + db->alloc_file, db->alloc_line); + exit(EXIT_FAILURE); + } + db->free_lock++; +} + +/** + * Decrement the free_lock of the database. + * If it was the last lock, frees the nodes of the database. + * Keeps the tree balanced. + * NOTE: Frees the duplicated keys of the nodes + * @param db Target database + * @private + * @see Database#free_lock + * @see #db_free_dbn(DBNode) + * @see #db_lock(Database) + */ +static void db_free_unlock(Database db) +{ + unsigned int i; + +#ifdef DB_ENABLE_STATS + if (stats.db_free_unlock != (unsigned int)~0) stats.db_free_unlock++; +#endif /* DB_ENABLE_STATS */ + if (db->free_lock == 0) { + ShowWarning("db_free_unlock: free_lock was already 0\n" + "Database allocated at %s:%d\n", + db->alloc_file, db->alloc_line); + } else { + db->free_lock--; + } + if (db->free_lock) + return; // Not last lock + + for (i = 0; i < db->free_count ; i++) { + db_rebalance_erase(db->free_list[i].node, db->free_list[i].root); + db_dup_key_free(db, db->free_list[i].node->key); +#ifdef DB_ENABLE_STATS + if (stats.db_node_free != (unsigned int)~0) stats.db_node_free++; +#endif /* DB_ENABLE_STATS */ + ers_free(db->nodes, db->free_list[i].node); + } + db->free_count = 0; +} + +/*****************************************************************************\ + * (3) Section of protected functions used internally. * + * NOTE: the protected functions used in the database interface are in the * + * next section. * + * db_int_cmp - Default comparator for DB_INT databases. * + * db_uint_cmp - Default comparator for DB_UINT databases. * + * db_string_cmp - Default comparator for DB_STRING databases. * + * db_istring_cmp - Default comparator for DB_ISTRING databases. * + * db_int_hash - Default hasher for DB_INT databases. * + * db_uint_hash - Default hasher for DB_UINT databases. * + * db_string_hash - Default hasher for DB_STRING databases. * + * db_istring_hash - Default hasher for DB_ISTRING databases. * + * db_release_nothing - Releaser that releases nothing. * + * db_release_key - Releaser that only releases the key. * + * db_release_data - Releaser that only releases the data. * + * db_release_both - Releaser that releases key and data. * +\*****************************************************************************/ + +/** + * Default comparator for DB_INT databases. + * Compares key1 to key2. + * Return 0 if equal, negative if lower and positive if higher. + * maxlen is ignored. + * @param key1 Key to be compared + * @param key2 Key being compared to + * @param maxlen Maximum length of the key to hash + * @return 0 if equal, negative if lower and positive if higher + * @see common\db.h#DBKey + * @see common\db.h\DBType#DB_INT + * @see common\db.h#DBComparator + * @see #db_default_cmp(DBType) + */ +static int db_int_cmp(DBKey key1, DBKey key2, unsigned short maxlen) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_int_cmp != (unsigned int)~0) stats.db_int_cmp++; +#endif /* DB_ENABLE_STATS */ + if (key1.i < key2.i) return -1; + if (key1.i > key2.i) return 1; + return 0; +} + +/** + * Default comparator for DB_UINT databases. + * Compares key1 to key2. + * Return 0 if equal, negative if lower and positive if higher. + * maxlen is ignored. + * @param key1 Key to be compared + * @param key2 Key being compared to + * @param maxlen Maximum length of the key to hash + * @return 0 if equal, negative if lower and positive if higher + * @see common\db.h#DBKey + * @see common\db.h\DBType#DB_UINT + * @see common\db.h#DBComparator + * @see #db_default_cmp(DBType) + */ +static int db_uint_cmp(DBKey key1, DBKey key2, unsigned short maxlen) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_uint_cmp != (unsigned int)~0) stats.db_uint_cmp++; +#endif /* DB_ENABLE_STATS */ + if (key1.ui < key2.ui) return -1; + if (key1.ui > key2.ui) return 1; + return 0; +} + +/** + * Default comparator for DB_STRING databases. + * Compares key1 to key2. + * Return 0 if equal, negative if lower and positive if higher. + * @param key1 Key to be compared + * @param key2 Key being compared to + * @param maxlen Maximum length of the key to hash + * @return 0 if equal, negative if lower and positive if higher + * @see common\db.h#DBKey + * @see common\db.h\DBType#DB_STRING + * @see common\db.h#DBComparator + * @see #db_default_cmp(DBType) + */ +static int db_string_cmp(DBKey key1, DBKey key2, unsigned short maxlen) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_string_cmp != (unsigned int)~0) stats.db_string_cmp++; +#endif /* DB_ENABLE_STATS */ + if (maxlen == 0) maxlen = (unsigned short)~0; + return strncmp((const char *)key1.str, (const char *)key2.str, maxlen); +} + +/** + * Default comparator for DB_ISTRING databases. + * Compares key1 to key2 case insensitively. + * Return 0 if equal, negative if lower and positive if higher. + * @param key1 Key to be compared + * @param key2 Key being compared to + * @param maxlen Maximum length of the key to hash + * @return 0 if equal, negative if lower and positive if higher + * @see common\db.h#DBKey + * @see common\db.h\DBType#DB_ISTRING + * @see common\db.h#DBComparator + * @see #db_default_cmp(DBType) + */ +static int db_istring_cmp(DBKey key1, DBKey key2, unsigned short maxlen) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_istring_cmp != (unsigned int)~0) stats.db_istring_cmp++; +#endif /* DB_ENABLE_STATS */ + if (maxlen == 0) maxlen = (unsigned short)~0; + return strncasecmp((const char *)key1.str, (const char *)key2.str, maxlen); +} + +/** + * Default hasher for DB_INT databases. + * Returns the value of the key as an unsigned int. + * maxlen is ignored. + * @param key Key to be hashed + * @param maxlen Maximum length of the key to hash + * @return hash of the key + * @see common\db.h#DBKey + * @see common\db.h\DBType#DB_INT + * @see common\db.h#DBHasher + * @see #db_default_hash(DBType) + */ +static unsigned int db_int_hash(DBKey key, unsigned short maxlen) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_int_hash != (unsigned int)~0) stats.db_int_hash++; +#endif /* DB_ENABLE_STATS */ + return (unsigned int)key.i; +} + +/** + * Default hasher for DB_UINT databases. + * Just returns the value of the key. + * maxlen is ignored. + * @param key Key to be hashed + * @param maxlen Maximum length of the key to hash + * @return hash of the key + * @see common\db.h#DBKey + * @see common\db.h\DBType#DB_UINT + * @see #db_default_hash(DBType) + */ +static unsigned int db_uint_hash(DBKey key, unsigned short maxlen) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_uint_hash != (unsigned int)~0) stats.db_uint_hash++; +#endif /* DB_ENABLE_STATS */ + return key.ui; +} + +/** + * Default hasher for DB_STRING databases. + * If maxlen if 0, the maximum number of maxlen is used instead. + * @param key Key to be hashed + * @param maxlen Maximum length of the key to hash + * @return hash of the key + * @see common\db.h#DBKey + * @see common\db.h\DBType#DB_STRING + * @see #db_default_hash(DBType) + */ +static unsigned int db_string_hash(DBKey key, unsigned short maxlen) +{ + unsigned char *k = key.str; + unsigned int hash = 0; + unsigned short i; + +#ifdef DB_ENABLE_STATS + if (stats.db_string_hash != (unsigned int)~0) stats.db_string_hash++; +#endif /* DB_ENABLE_STATS */ + if (maxlen == 0) + maxlen = (unsigned short)~0; // Maximum + + for (i = 0; *k; i++) { + hash = (hash*33 + *k++)^(hash>>24); + if (i == maxlen) + break; + } + + return hash; +} + +/** + * Default hasher for DB_ISTRING databases. + * If maxlen if 0, the maximum number of maxlen is used instead. + * @param key Key to be hashed + * @param maxlen Maximum length of the key to hash + * @return hash of the key + * @see common\db.h#DBKey + * @see common\db.h\DBType#DB_ISTRING + * @see #db_default_hash(DBType) + */ +static unsigned int db_istring_hash(DBKey key, unsigned short maxlen) +{ + unsigned char *k = key.str; + unsigned int hash = 0; + unsigned short i; + +#ifdef DB_ENABLE_STATS + if (stats.db_istring_hash != (unsigned int)~0) stats.db_istring_hash++; +#endif /* DB_ENABLE_STATS */ + if (maxlen == 0) + maxlen = (unsigned short)~0; // Maximum + + for (i = 0; *k; i++) { + hash = (hash*33 + LOWER(*k))^(hash>>24); + k++; + if (i == maxlen) + break; + } + + return hash; +} + +/** + * Releaser that releases nothing. + * @param key Key of the database entry + * @param data Data of the database entry + * @param which What is being requested to be released + * @protected + * @see common\db.h#DBKey + * @see common\db.h#DBRelease + * @see common\db.h#DBReleaser + * @see #db_default_releaser(DBType,DBOptions) + */ +static void db_release_nothing(DBKey key, void *data, DBRelease which) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_release_nothing != (unsigned int)~0) stats.db_release_nothing++; +#endif /* DB_ENABLE_STATS */ +} + +/** + * Releaser that only releases the key. + * @param key Key of the database entry + * @param data Data of the database entry + * @param which What is being requested to be released + * @protected + * @see common\db.h#DBKey + * @see common\db.h#DBRelease + * @see common\db.h#DBReleaser + * @see #db_default_release(DBType,DBOptions) + */ +static void db_release_key(DBKey key, void *data, DBRelease which) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_release_key != (unsigned int)~0) stats.db_release_key++; +#endif /* DB_ENABLE_STATS */ + if (which&DB_RELEASE_KEY) aFree(key.str); // needs to be a pointer +} + +/** + * Releaser that only releases the data. + * @param key Key of the database entry + * @param data Data of the database entry + * @param which What is being requested to be released + * @protected + * @see common\db.h#DBKey + * @see common\db.h#DBRelease + * @see common\db.h#DBReleaser + * @see #db_default_release(DBType,DBOptions) + */ +static void db_release_data(DBKey key, void *data, DBRelease which) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_release_data != (unsigned int)~0) stats.db_release_data++; +#endif /* DB_ENABLE_STATS */ + if (which&DB_RELEASE_DATA) aFree(data); +} + +/** + * Releaser that releases both key and data. + * @param key Key of the database entry + * @param data Data of the database entry + * @param which What is being requested to be released + * @protected + * @see common\db.h#DBKey + * @see common\db.h#DBRelease + * @see common\db.h#DBReleaser + * @see #db_default_release(DBType,DBOptions) + */ +static void db_release_both(DBKey key, void *data, DBRelease which) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_release_both != (unsigned int)~0) stats.db_release_both++; +#endif /* DB_ENABLE_STATS */ + if (which&DB_RELEASE_KEY) aFree(key.str); // needs to be a pointer + if (which&DB_RELEASE_DATA) aFree(data); +} + +/*****************************************************************************\ + * (4) Section with protected functions used in the interface of the * + * database. * + * db_obj_get - Get the data identified by the key. * + * db_obj_vgetall - Get the data of the matched entries. * + * db_obj_getall - Get the data of the matched entries. * + * db_obj_vensure - Get the data identified by the key, creating if it * + * doesn't exist yet. * + * db_obj_ensure - Get the data identified by the key, creating if it * + * doesn't exist yet. * + * db_obj_put - Put data identified by the key in the database. * + * db_obj_remove - Remove an entry from the database. * + * db_obj_vforeach - Apply a function to every entry in the database. * + * db_obj_foreach - Apply a function to every entry in the database. * + * db_obj_vclear - Remove all entries from the database. * + * db_obj_clear - Remove all entries from the database. * + * db_obj_vdestroy - Destroy the database, freeing all the used memory. * + * db_obj_destroy - Destroy the database, freeing all the used memory. * + * db_obj_size - Return the size of the database. * + * db_obj_type - Return the type of the database. * + * db_obj_options - Return the options of the database. * +\*****************************************************************************/ + +/** + * Get the data of the entry identifid by the key. + * @param self Interface of the database + * @param key Key that identifies the entry + * @return Data of the entry or NULL if not found + * @protected + * @see common\db.h#DBKey + * @see common\db.h#DBInterface + * @see common\db.h\DBInterface#get(DBInterface,DBKey) + */ +static void *db_obj_get(DBInterface self, DBKey key) +{ + Database db = (Database)self; + DBNode node; + int c; + void *data = NULL; + +#ifdef DB_ENABLE_STATS + if (stats.db_get != (unsigned int)~0) stats.db_get++; +#endif /* DB_ENABLE_STATS */ + if (db == NULL) return NULL; // nullpo candidate + if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) { + ShowError("db_get: Attempted to retrieve non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); + return NULL; // nullpo candidate + } + + db_free_lock(db); + node = db->ht[db->hash(key, db->maxlen)%HASH_SIZE]; + while (node) { + c = db->cmp(key, node->key, db->maxlen); + if (c == 0) { + data = node->data; + break; + } + if (c < 0) + node = node->left; + else + node = node->right; + } + db_free_unlock(db); + return data; +} + +/** + * Get the data of the entries matched by match. + * It puts a maximum of max entries into buf. + * If buf is NULL, it only counts the matches. + * Returns the number of entries that matched. + * NOTE: if the value returned is greater than max, only the + * first max entries found are put into the buffer. + * @param self Interface of the database + * @param buf Buffer to put the data of the matched entries + * @param max Maximum number of data entries to be put into buf + * @param match Function that matches the database entries + * @param ... Extra arguments for match + * @return The number of entries that matched + * @protected + * @see common\db.h#DBInterface + * @see common\db.h#DBMatcher(DBKey key, void *data, va_list args) + * @see common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list) + */ +static unsigned int db_obj_vgetall(DBInterface self, void **buf, unsigned int max, DBMatcher match, va_list args) +{ + Database db = (Database)self; + unsigned int i; + DBNode node; + DBNode parent; + unsigned int ret = 0; + +#ifdef DB_ENABLE_STATS + if (stats.db_vgetall != (unsigned int)~0) stats.db_vgetall++; +#endif /* DB_ENABLE_STATS */ + if (db == NULL) return 0; // nullpo candidate + if (match == NULL) return 0; // nullpo candidate + + db_free_lock(db); + for (i = 0; i < HASH_SIZE; i++) { + // Match in the order: current node, left tree, right tree + node = db->ht[i]; + while (node) { + parent = node->parent; + if (!(node->deleted) && match(node->key, node->data, args) == 0) { + if (buf && ret < max) + buf[ret] = node->data; + ret++; + } + if (node->left) { + node = node->left; + continue; + } + if (node->right) { + node = node->right; + continue; + } + while (node) { + parent = node->parent; + if (parent && parent->right && parent->left == node) { + node = parent->right; + break; + } + node = parent; + } + } + } + db_free_unlock(db); + return ret; +} + +/** + * Just calls {@link common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)}. + * Get the data of the entries matched by match. + * It puts a maximum of max entries into buf. + * If buf is NULL, it only counts the matches. + * Returns the number of entries that matched. + * NOTE: if the value returned is greater than max, only the + * first max entries found are put into the buffer. + * @param self Interface of the database + * @param buf Buffer to put the data of the matched entries + * @param max Maximum number of data entries to be put into buf + * @param match Function that matches the database entries + * @param ... Extra arguments for match + * @return The number of entries that matched + * @protected + * @see common\db.h#DBMatcher(DBKey key, void *data, va_list args) + * @see common\db.h#DBInterface + * @see common\db.h\DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list) + * @see common\db.h\DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...) + */ +static unsigned int db_obj_getall(DBInterface self, void **buf, unsigned int max, DBMatcher match, ...) +{ + va_list args; + unsigned int ret; + +#ifdef DB_ENABLE_STATS + if (stats.db_getall != (unsigned int)~0) stats.db_getall++; +#endif /* DB_ENABLE_STATS */ + if (self == NULL) return 0; // nullpo candidate + + va_start(args, match); + ret = self->vgetall(self, buf, max, match, args); + va_end(args); + return ret; +} + +/** + * Get the data of the entry identified by the key. + * If the entry does not exist, an entry is added with the data returned by + * create. + * @param self Interface of the database + * @param key Key that identifies the entry + * @param create Function used to create the data if the entry doesn't exist + * @param args Extra arguments for create + * @return Data of the entry + * @protected + * @see common\db.h#DBKey + * @see common\db.h#DBCreateData + * @see common\db.h#DBInterface + * @see common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list) + */ +static void *db_obj_vensure(DBInterface self, DBKey key, DBCreateData create, va_list args) +{ + Database db = (Database)self; + DBNode node; + DBNode parent = NULL; + unsigned int hash; + int c = 0; + void *data = NULL; + +#ifdef DB_ENABLE_STATS + if (stats.db_vensure != (unsigned int)~0) stats.db_vensure++; +#endif /* DB_ENABLE_STATS */ + if (db == NULL) return NULL; // nullpo candidate + if (create == NULL) { + ShowError("db_ensure: Create function is NULL for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); + return NULL; // nullpo candidate + } + if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) { + ShowError("db_ensure: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); + return NULL; // nullpo candidate + } + + db_free_lock(db); + hash = db->hash(key, db->maxlen)%HASH_SIZE; + node = db->ht[hash]; + while (node) { + c = db->cmp(key, node->key, db->maxlen); + if (c == 0) { + break; + } + parent = node; + if (c < 0) + node = node->left; + else + node = node->right; + } + // Create node if necessary + if (node == NULL) { + if (db->item_count == (unsigned int)~0) { + ShowError("db_vensure: item_count overflow, aborting item insertion.\n" + "Database allocated at %s:%d", + db->alloc_file, db->alloc_line); + return NULL; + } +#ifdef DB_ENABLE_STATS + if (stats.db_node_alloc != (unsigned int)~0) stats.db_node_alloc++; +#endif /* DB_ENABLE_STATS */ + node = ers_alloc(db->nodes, struct dbn); + node->left = NULL; + node->right = NULL; + node->deleted = 0; + db->item_count++; + if (c == 0) { // hash entry is empty + node->color = BLACK; + node->parent = NULL; + db->ht[hash] = node; + } else { + node->color = RED; + if (c < 0) { // put at the left + parent->left = node; + node->parent = parent; + } else { // put at the right + parent->right = node; + node->parent = parent; + } + if (parent->color == RED) // two consecutive RED nodes, must rebalance + db_rebalance(node, &db->ht[hash]); + } + // put key and data in the node + if (db->options&DB_OPT_DUP_KEY) { + node->key = db_dup_key(db, key); + if (db->options&DB_OPT_RELEASE_KEY) + db->release(key, data, DB_RELEASE_KEY); + } else { + node->key = key; + } + node->data = create(key, args); + } + data = node->data; + db_free_unlock(db); + return data; +} + +/** + * Just calls {@link common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)}. + * Get the data of the entry identified by the key. + * If the entry does not exist, an entry is added with the data returned by + * create. + * @param self Interface of the database + * @param key Key that identifies the entry + * @param create Function used to create the data if the entry doesn't exist + * @param ... Extra arguments for create + * @return Data of the entry + * @protected + * @see common\db.h#DBKey + * @see common\db.h#DBCreateData + * @see common\db.h#DBInterface + * @see common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list) + * @see common\db.h\DBInterface#ensure(DBInterface,DBKey,DBCreateData,...) + */ +static void *db_obj_ensure(DBInterface self, DBKey key, DBCreateData create, ...) +{ + va_list args; + void *ret; + +#ifdef DB_ENABLE_STATS + if (stats.db_ensure != (unsigned int)~0) stats.db_ensure++; +#endif /* DB_ENABLE_STATS */ + if (self == NULL) return 0; // nullpo candidate + + va_start(args, create); + ret = self->vensure(self, key, create, args); + va_end(args); + return ret; +} + +/** + * Put the data identified by the key in the database. + * Returns the previous data if the entry exists or NULL. + * NOTE: Uses the new key, the old one is released. + * @param self Interface of the database + * @param key Key that identifies the data + * @param data Data to be put in the database + * @return The previous data if the entry exists or NULL + * @protected + * @see common\db.h#DBKey + * @see common\db.h#DBInterface + * @see #db_malloc_dbn(void) + * @see common\db.h\DBInterface#put(DBInterface,DBKey,void *) + */ +static void *db_obj_put(DBInterface self, DBKey key, void *data) +{ + Database db = (Database)self; + DBNode node; + DBNode parent = NULL; + int c = 0; + unsigned int hash; + void *old_data = NULL; + +#ifdef DB_ENABLE_STATS + if (stats.db_put != (unsigned int)~0) stats.db_put++; +#endif /* DB_ENABLE_STATS */ + if (db == NULL) return NULL; // nullpo candidate + if (db->global_lock) { + ShowError("db_put: Database is being destroyed, aborting entry insertion.\n" + "Database allocated at %s:%d\n", + db->alloc_file, db->alloc_line); + return NULL; // nullpo candidate + } + if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) { + ShowError("db_put: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); + return NULL; // nullpo candidate + } + if (!(data || db->options&DB_OPT_ALLOW_NULL_DATA)) { + ShowError("db_put: Attempted to use non-allowed NULL data for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); + return NULL; // nullpo candidate + } + + if (db->item_count == (unsigned int)~0) { + ShowError("db_put: item_count overflow, aborting item insertion.\n" + "Database allocated at %s:%d", + db->alloc_file, db->alloc_line); + return NULL; + } + // search for an equal node + db_free_lock(db); + hash = db->hash(key, db->maxlen)%HASH_SIZE; + for (node = db->ht[hash]; node; ) { + c = db->cmp(key, node->key, db->maxlen); + if (c == 0) { // equal entry, replace + if (node->deleted) { + db_free_remove(db, node); + } else { + db->release(node->key, node->data, DB_RELEASE_BOTH); + } + old_data = node->data; + break; + } + parent = node; + if (c < 0) { + node = node->left; + } else { + node = node->right; + } + } + // allocate a new node if necessary + if (node == NULL) { +#ifdef DB_ENABLE_STATS + if (stats.db_node_alloc != (unsigned int)~0) stats.db_node_alloc++; +#endif /* DB_ENABLE_STATS */ + node = ers_alloc(db->nodes, struct dbn); + node->left = NULL; + node->right = NULL; + node->deleted = 0; + db->item_count++; + if (c == 0) { // hash entry is empty + node->color = BLACK; + node->parent = NULL; + db->ht[hash] = node; + } else { + node->color = RED; + if (c < 0) { // put at the left + parent->left = node; + node->parent = parent; + } else { // put at the right + parent->right = node; + node->parent = parent; + } + if (parent->color == RED) // two consecutive RED nodes, must rebalance + db_rebalance(node, &db->ht[hash]); + } + } + // put key and data in the node + if (db->options&DB_OPT_DUP_KEY) { + node->key = db_dup_key(db, key); + if (db->options&DB_OPT_RELEASE_KEY) + db->release(key, data, DB_RELEASE_KEY); + } else { + node->key = key; + } + node->data = data; + db_free_unlock(db); + return old_data; +} + +/** + * Remove an entry from the database. + * Returns the data of the entry. + * NOTE: The key (of the database) is released in {@link #db_free_add(Database,DBNode,DBNode *)}. + * @param self Interface of the database + * @param key Key that identifies the entry + * @return The data of the entry or NULL if not found + * @protected + * @see common\db.h#DBKey + * @see common\db.h#DBInterface + * @see #db_free_add(Database,DBNode,DBNode *) + * @see common\db.h\DBInterface#remove(DBInterface,DBKey) + */ +static void *db_obj_remove(DBInterface self, DBKey key) +{ + Database db = (Database)self; + void *data = NULL; + DBNode node; + unsigned int hash; + int c = 0; + +#ifdef DB_ENABLE_STATS + if (stats.db_remove != (unsigned int)~0) stats.db_remove++; +#endif /* DB_ENABLE_STATS */ + if (db == NULL) return NULL; // nullpo candidate + if (db->global_lock) { + ShowError("db_remove: Database is being destroyed. Aborting entry deletion.\n" + "Database allocated at %s:%d\n", + db->alloc_file, db->alloc_line); + return NULL; // nullpo candidate + } + if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) { + ShowError("db_remove: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); + return NULL; // nullpo candidate + } + + db_free_lock(db); + hash = db->hash(key, db->maxlen)%HASH_SIZE; + for(node = db->ht[hash]; node; ){ + c = db->cmp(key, node->key, db->maxlen); + if (c == 0) { + if (!(node->deleted)) { + data = node->data; + db->release(node->key, node->data, DB_RELEASE_DATA); + db_free_add(db, node, &db->ht[hash]); + } + break; + } + if (c < 0) + node = node->left; + else + node = node->right; + } + db_free_unlock(db); + return data; +} + +/** + * Apply func to every entry in the database. + * Returns the sum of values returned by func. + * @param self Interface of the database + * @param func Function to be applyed + * @param args Extra arguments for func + * @return Sum of the values returned by func + * @protected + * @see common\db.h#DBInterface + * @see common\db.h#DBApply(DBKey,void *,va_list) + * @see common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list) + */ +static int db_obj_vforeach(DBInterface self, DBApply func, va_list args) +{ + Database db = (Database)self; + unsigned int i; + int sum = 0; + DBNode node; + DBNode parent; + +#ifdef DB_ENABLE_STATS + if (stats.db_vforeach != (unsigned int)~0) stats.db_vforeach++; +#endif /* DB_ENABLE_STATS */ + if (db == NULL) return 0; // nullpo candidate + if (func == NULL) { + ShowError("db_foreach: Passed function is NULL for db allocated at %s:%d\n",db->alloc_file, db->alloc_line); + return 0; // nullpo candidate + } + + db_free_lock(db); + for (i = 0; i < HASH_SIZE; i++) { + // Apply func in the order: current node, left node, right node + node = db->ht[i]; + while (node) { + parent = node->parent; + if (!(node->deleted)) + sum += func(node->key, node->data, args); + if (node->left) { + node = node->left; + continue; + } + if (node->right) { + node = node->right; + continue; + } + while (node) { + parent = node->parent; + if (parent && parent->right && parent->left == node) { + node = parent->right; + break; + } + node = parent; + } + } + } + db_free_unlock(db); + return sum; +} + +/** + * Just calls {@link common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list)}. + * Apply func to every entry in the database. + * Returns the sum of values returned by func. + * @param self Interface of the database + * @param func Function to be applyed + * @param ... Extra arguments for func + * @return Sum of the values returned by func + * @protected + * @see common\db.h#DBInterface + * @see common\db.h#DBApply(DBKey,void *,va_list) + * @see common\db.h\DBInterface#vforeach(DBInterface,DBApply,va_list) + * @see common\db.h\DBInterface#foreach(DBInterface,DBApply,...) + */ +static int db_obj_foreach(DBInterface self, DBApply func, ...) +{ + va_list args; + int ret; + +#ifdef DB_ENABLE_STATS + if (stats.db_foreach != (unsigned int)~0) stats.db_foreach++; +#endif /* DB_ENABLE_STATS */ + if (self == NULL) return 0; // nullpo candidate + + va_start(args, func); + ret = self->vforeach(self, func, args); + va_end(args); + return ret; +} + +/** + * Removes all entries from the database. + * Before deleting an entry, func is applyed to it. + * Releases the key and the data. + * Returns the sum of values returned by func, if it exists. + * @param self Interface of the database + * @param func Function to be applyed to every entry before deleting + * @param args Extra arguments for func + * @return Sum of values returned by func + * @protected + * @see common\db.h#DBApply(DBKey,void *,va_list) + * @see common\db.h#DBInterface + * @see common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list) + */ +static int db_obj_vclear(DBInterface self, DBApply func, va_list args) +{ + Database db = (Database)self; + int sum = 0; + unsigned int i; + DBNode node; + DBNode parent; + +#ifdef DB_ENABLE_STATS + if (stats.db_vclear != (unsigned int)~0) stats.db_vclear++; +#endif /* DB_ENABLE_STATS */ + if (db == NULL) return 0; // nullpo candidate + + db_free_lock(db); + for (i = 0; i < HASH_SIZE; i++) { + // Apply the func and delete in the order: left tree, right tree, current node + node = db->ht[i]; + db->ht[i] = NULL; + while (node) { + parent = node->parent; + if (node->left) { + node = node->left; + continue; + } + if (node->right) { + node = node->right; + continue; + } + if (node->deleted) { + db_dup_key_free(db, node->key); + } else { + if (func) + sum += func(node->key, node->data, args); + db->release(node->key, node->data, DB_RELEASE_BOTH); + node->deleted = 1; + } +#ifdef DB_ENABLE_STATS + if (stats.db_node_free != (unsigned int)~0) stats.db_node_free++; +#endif /* DB_ENABLE_STATS */ + ers_free(db->nodes, node); + if (parent) { + if (parent->left == node) + parent->left = NULL; + else + parent->right = NULL; + } + node = parent; + } + db->ht[i] = NULL; + } + db->free_count = 0; + db->item_count = 0; + db_free_unlock(db); + return sum; +} + +/** + * Just calls {@link common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list)}. + * Removes all entries from the database. + * Before deleting an entry, func is applyed to it. + * Releases the key and the data. + * Returns the sum of values returned by func, if it exists. + * NOTE: This locks the database globally. Any attempt to insert or remove + * a database entry will give an error and be aborted (except for clearing). + * @param self Interface of the database + * @param func Function to be applyed to every entry before deleting + * @param ... Extra arguments for func + * @return Sum of values returned by func + * @protected + * @see common\db.h#DBApply(DBKey,void *,va_list) + * @see common\db.h#DBInterface + * @see common\db.h\DBInterface#vclear(DBInterface,DBApply,va_list) + * @see common\db.h\DBInterface#clear(DBInterface,DBApply,...) + */ +static int db_obj_clear(DBInterface self, DBApply func, ...) +{ + va_list args; + int ret; + +#ifdef DB_ENABLE_STATS + if (stats.db_clear != (unsigned int)~0) stats.db_clear++; +#endif /* DB_ENABLE_STATS */ + if (self == NULL) return 0; // nullpo candidate + + va_start(args, func); + ret = self->vclear(self, func, args); + va_end(args); + return ret; +} + +/** + * Finalize the database, feeing all the memory it uses. + * Before deleting an entry, func is applyed to it. + * Returns the sum of values returned by func, if it exists. + * NOTE: This locks the database globally. Any attempt to insert or remove + * a database entry will give an error and be aborted (except for clearing). + * @param self Interface of the database + * @param func Function to be applyed to every entry before deleting + * @param args Extra arguments for func + * @return Sum of values returned by func + * @protected + * @see common\db.h#DBApply(DBKey,void *,va_list) + * @see common\db.h#DBInterface + * @see common\db.h\DBInterface#vdestroy(DBInterface,DBApply,va_list) + */ +static int db_obj_vdestroy(DBInterface self, DBApply func, va_list args) +{ + Database db = (Database)self; + int sum; + +#ifdef DB_ENABLE_STATS + if (stats.db_vdestroy != (unsigned int)~0) stats.db_vdestroy++; +#endif /* DB_ENABLE_STATS */ + if (db == NULL) return 0; // nullpo candidate + if (db->global_lock) { + ShowError("db_vdestroy: Database is already locked for destruction. Aborting second database destruction.\n" + "Database allocated at %s:%d\n", + db->alloc_file, db->alloc_line); + return 0; + } + if (db->free_lock) + ShowWarning("db_vdestroy: Database is still in use, %u lock(s) left. Continuing database destruction.\n" + "Database allocated at %s:%d\n", + db->alloc_file, db->alloc_line, db->free_lock); + +#ifdef DB_ENABLE_STATS + switch (db->type) { + case DB_INT: + stats.db_int_destroy++; + break; + case DB_UINT: + stats.db_uint_destroy++; + break; + case DB_STRING: + stats.db_string_destroy++; + break; + case DB_ISTRING: + stats.db_istring_destroy++; + break; + } +#endif /* DB_ENABLE_STATS */ + db_free_lock(db); + db->global_lock = 1; + sum = self->vclear(self, func, args); + aFree(db->free_list); + db->free_list = NULL; + db->free_max = 0; + ers_destroy(db->nodes); + db_free_unlock(db); + aFree(db); + return sum; +} + +/** + * Just calls {@link common\db.h\DBInterface#db_vdestroy(DBInterface,DBApply,va_list)}. + * Finalize the database, feeing all the memory it uses. + * Before deleting an entry, func is applyed to it. + * Releases the key and the data. + * Returns the sum of values returned by func, if it exists. + * NOTE: This locks the database globally. Any attempt to insert or remove + * a database entry will give an error and be aborted. + * @param self Interface of the database + * @param func Function to be applyed to every entry before deleting + * @param ... Extra arguments for func + * @return Sum of values returned by func + * @protected + * @see common\db.h#DBApply(DBKey,void *,va_list) + * @see common\db.h#DBInterface + * @see common\db.h\DBInterface#vdestroy(DBInterface,DBApply,va_list) + * @see common\db.h\DBInterface#destroy(DBInterface,DBApply,...) + */ +static int db_obj_destroy(DBInterface self, DBApply func, ...) +{ + va_list args; + int ret; + +#ifdef DB_ENABLE_STATS + if (stats.db_destroy != (unsigned int)~0) stats.db_destroy++; +#endif /* DB_ENABLE_STATS */ + if (self == NULL) return 0; // nullpo candidate + + va_start(args, func); + ret = self->vdestroy(self, func, args); + va_end(args); + return ret; +} + +/** + * Return the size of the database (number of items in the database). + * @param self Interface of the database + * @return Size of the database + * @protected + * @see common\db.h#DBInterface + * @see Database#item_count + * @see common\db.h\DBInterface#size(DBInterface) + */ +static unsigned int db_obj_size(DBInterface self) +{ + Database db = (Database)self; + unsigned int item_count; + +#ifdef DB_ENABLE_STATS + if (stats.db_size != (unsigned int)~0) stats.db_size++; +#endif /* DB_ENABLE_STATS */ + if (db == NULL) return 0; // nullpo candidate + + db_free_lock(db); + item_count = db->item_count; + db_free_unlock(db); + + return item_count; +} + +/** + * Return the type of database. + * @param self Interface of the database + * @return Type of the database + * @protected + * @see common\db.h#DBType + * @see common\db.h#DBInterface + * @see Database#type + * @see common\db.h\DBInterface#type(DBInterface) + */ +static DBType db_obj_type(DBInterface self) +{ + Database db = (Database)self; + DBType type; + +#ifdef DB_ENABLE_STATS + if (stats.db_type != (unsigned int)~0) stats.db_type++; +#endif /* DB_ENABLE_STATS */ + if (db == NULL) return -1; // nullpo candidate - TODO what should this return? + + db_free_lock(db); + type = db->type; + db_free_unlock(db); + + return type; +} + +/** + * Return the options of the database. + * @param self Interface of the database + * @return Options of the database + * @protected + * @see common\db.h#DBOptions + * @see common\db.h#DBInterface + * @see Database#options + * @see common\db.h\DBInterface#options(DBInterface) + */ +static DBOptions db_obj_options(DBInterface self) +{ + Database db = (Database)self; + DBOptions options; + +#ifdef DB_ENABLE_STATS + if (stats.db_options != (unsigned int)~0) stats.db_options++; +#endif /* DB_ENABLE_STATS */ + if (db == NULL) return DB_OPT_BASE; // nullpo candidate - TODO what should this return? + + db_free_lock(db); + options = db->options; + db_free_unlock(db); + + return options; +} + +/*****************************************************************************\ + * (5) Section with public functions. * + * db_fix_options - Apply database type restrictions to the options. * + * db_default_cmp - Get the default comparator for a type of database. * + * db_default_hash - Get the default hasher for a type of database. * + * db_default_release - Get the default releaser for a type of database * + * with the specified options. * + * db_custom_release - Get a releaser that behaves a certains way. * + * db_alloc - Allocate a new database. * + * db_i2key - Manual cast from 'int' to 'DBKey'. * + * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. * + * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. * + * db_init - Initialize the database system. * + * db_final - Finalize the database system. * +\*****************************************************************************/ + +/** + * Returns the fixed options according to the database type. + * Sets required options and unsets unsupported options. + * For numeric databases DB_OPT_DUP_KEY and DB_OPT_RELEASE_KEY are unset. + * @param type Type of the database + * @param options Original options of the database + * @return Fixed options of the database + * @private + * @see common\db.h#DBType + * @see common\db.h#DBOptions + * @see #db_default_release(DBType,DBOptions) + * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short) + * @see common\db.h#db_fix_options(DBType,DBOptions) + */ +DBOptions db_fix_options(DBType type, DBOptions options) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_fix_options != (unsigned int)~0) stats.db_fix_options++; +#endif /* DB_ENABLE_STATS */ + switch (type) { + case DB_INT: + case DB_UINT: // Numeric database, do nothing with the keys + return options&~(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY); + + default: + ShowError("db_fix_options: Unknown database type %u with options %x\n", type, options); + case DB_STRING: + case DB_ISTRING: // String databases, no fix required + return options; + } +} + +/** + * Returns the default comparator for the specified type of database. + * @param type Type of database + * @return Comparator for the type of database or NULL if unknown database + * @public + * @see common\db.h#DBType + * @see #db_int_cmp(DBKey,DBKey,unsigned short) + * @see #db_uint_cmp(DBKey,DBKey,unsigned short) + * @see #db_string_cmp(DBKey,DBKey,unsigned short) + * @see #db_istring_cmp(DBKey,DBKey,unsigned short) + * @see common\db.h#db_default_cmp(DBType) + */ +DBComparator db_default_cmp(DBType type) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_default_cmp != (unsigned int)~0) stats.db_default_cmp++; +#endif /* DB_ENABLE_STATS */ + switch (type) { + case DB_INT: return db_int_cmp; + case DB_UINT: return db_uint_cmp; + case DB_STRING: return db_string_cmp; + case DB_ISTRING: return db_istring_cmp; + default: + ShowError("db_default_cmp: Unknown database type %u\n", type); + return NULL; + } +} + +/** + * Returns the default hasher for the specified type of database. + * @param type Type of database + * @return Hasher of the type of database or NULL if unknown database + * @public + * @see common\db.h#DBType + * @see #db_int_hash(DBKey,unsigned short) + * @see #db_uint_hash(DBKey,unsigned short) + * @see #db_string_hash(DBKey,unsigned short) + * @see #db_istring_hash(DBKey,unsigned short) + * @see common\db.h#db_default_hash(DBType) + */ +DBHasher db_default_hash(DBType type) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_default_hash != (unsigned int)~0) stats.db_default_hash++; +#endif /* DB_ENABLE_STATS */ + switch (type) { + case DB_INT: return db_int_hash; + case DB_UINT: return db_uint_hash; + case DB_STRING: return db_string_hash; + case DB_ISTRING: return db_istring_hash; + default: + ShowError("db_default_hash: Unknown database type %u\n", type); + return NULL; + } +} + +/** + * Returns the default releaser for the specified type of database with the + * specified options. + * NOTE: the options are fixed with {@link #db_fix_options(DBType,DBOptions)} + * before choosing the releaser. + * @param type Type of database + * @param options Options of the database + * @return Default releaser for the type of database with the specified options + * @public + * @see common\db.h#DBType + * @see common\db.h#DBOptions + * @see common\db.h#DBReleaser + * @see #db_release_nothing(DBKey,void *,DBRelease) + * @see #db_release_key(DBKey,void *,DBRelease) + * @see #db_release_data(DBKey,void *,DBRelease) + * @see #db_release_both(DBKey,void *,DBRelease) + * @see #db_custom_release(DBRelease) + * @see common\db.h#db_default_release(DBType,DBOptions) + */ +DBReleaser db_default_release(DBType type, DBOptions options) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_default_release != (unsigned int)~0) stats.db_default_release++; +#endif /* DB_ENABLE_STATS */ + options = db_fix_options(type, options); + if (options&DB_OPT_RELEASE_DATA) { // Release data, what about the key? + if (options&(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY)) + return db_release_both; // Release both key and data + return db_release_data; // Only release data + } + if (options&(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY)) + return db_release_key; // Only release key + return db_release_nothing; // Release nothing +} + +/** + * Returns the releaser that releases the specified release options. + * @param which Options that specified what the releaser releases + * @return Releaser for the specified release options + * @public + * @see common\db.h#DBRelease + * @see common\db.h#DBReleaser + * @see #db_release_nothing(DBKey,void *,DBRelease) + * @see #db_release_key(DBKey,void *,DBRelease) + * @see #db_release_data(DBKey,void *,DBRelease) + * @see #db_release_both(DBKey,void *,DBRelease) + * @see #db_default_release(DBType,DBOptions) + * @see common\db.h#db_custom_release(DBRelease) + */ +DBReleaser db_custom_release(DBRelease which) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_custom_release != (unsigned int)~0) stats.db_custom_release++; +#endif /* DB_ENABLE_STATS */ + switch (which) { + case DB_RELEASE_NOTHING: return db_release_nothing; + case DB_RELEASE_KEY: return db_release_key; + case DB_RELEASE_DATA: return db_release_data; + case DB_RELEASE_BOTH: return db_release_both; + default: + ShowError("db_custom_release: Unknown release options %u\n", which); + return NULL; + } +} + +/** + * Allocate a new database of the specified type. + * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)} + * before creating the database. + * @param file File where the database is being allocated + * @param line Line of the file where the database is being allocated + * @param type Type of database + * @param options Options of the database + * @param maxlen Maximum length of the string to be used as key in string + * databases + * @return The interface of the database + * @public + * @see common\db.h#DBType + * @see common\db.h#DBInterface + * @see #Database + * @see #db_fix_options(DBType,DBOptions) + * @see common\db.h#db_alloc(const char *,int,DBType,unsigned short) + */ +DBInterface db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen) +{ + Database db; + unsigned int i; + +#ifdef DB_ENABLE_STATS + if (stats.db_alloc != (unsigned int)~0) stats.db_alloc++; + switch (type) { + case DB_INT: + stats.db_int_alloc++; + break; + case DB_UINT: + stats.db_uint_alloc++; + break; + case DB_STRING: + stats.db_string_alloc++; + break; + case DB_ISTRING: + stats.db_istring_alloc++; + break; + } +#endif /* DB_ENABLE_STATS */ + CREATE(db, struct db, 1); + + options = db_fix_options(type, options); + /* Interface of the database */ + db->dbi.get = db_obj_get; + db->dbi.getall = db_obj_getall; + db->dbi.vgetall = db_obj_vgetall; + db->dbi.ensure = db_obj_ensure; + db->dbi.vensure = db_obj_vensure; + db->dbi.put = db_obj_put; + db->dbi.remove = db_obj_remove; + db->dbi.foreach = db_obj_foreach; + db->dbi.vforeach = db_obj_vforeach; + db->dbi.clear = db_obj_clear; + db->dbi.vclear = db_obj_vclear; + db->dbi.destroy = db_obj_destroy; + db->dbi.vdestroy = db_obj_vdestroy; + db->dbi.size = db_obj_size; + db->dbi.type = db_obj_type; + db->dbi.options = db_obj_options; + /* File and line of allocation */ + db->alloc_file = file; + db->alloc_line = line; + /* Lock system */ + db->free_list = NULL; + db->free_count = 0; + db->free_max = 0; + db->free_lock = 0; + /* Other */ + db->nodes = ers_new((uint32)sizeof(struct dbn)); + db->cmp = db_default_cmp(type); + db->hash = db_default_hash(type); + db->release = db_default_release(type, options); + for (i = 0; i < HASH_SIZE; i++) + db->ht[i] = NULL; + db->type = type; + db->options = options; + db->item_count = 0; + db->maxlen = maxlen; + db->global_lock = 0; + + return &db->dbi; +} + +#ifdef DB_MANUAL_CAST_TO_UNION +/** + * Manual cast from 'int' to the union DBKey. + * Created for compilers that don't support casting to unions. + * @param key Key to be casted + * @return The key as a DBKey union + * @public + * @see common\db.h#DB_MANUAL_CAST_TO_UNION + * @see #db_ui2key(unsigned int) + * @see #db_str2key(unsigned char *) + * @see common\db.h#db_i2key(int) + */ +DBKey db_i2key(int key) +{ + DBKey ret; + +#ifdef DB_ENABLE_STATS + if (stats.db_i2key != (unsigned int)~0) stats.db_i2key++; +#endif /* DB_ENABLE_STATS */ + ret.i = key; + return ret; +} + +/** + * Manual cast from 'unsigned int' to the union DBKey. + * Created for compilers that don't support casting to unions. + * @param key Key to be casted + * @return The key as a DBKey union + * @public + * @see common\db.h#DB_MANUAL_CAST_TO_UNION + * @see #db_i2key(int) + * @see #db_str2key(unsigned char *) + * @see common\db.h#db_ui2key(unsigned int) + */ +DBKey db_ui2key(unsigned int key) +{ + DBKey ret; + +#ifdef DB_ENABLE_STATS + if (stats.db_ui2key != (unsigned int)~0) stats.db_ui2key++; +#endif /* DB_ENABLE_STATS */ + ret.ui = key; + return ret; +} + +/** + * Manual cast from 'unsigned char *' to the union DBKey. + * Created for compilers that don't support casting to unions. + * @param key Key to be casted + * @return The key as a DBKey union + * @public + * @see common\db.h#DB_MANUAL_CAST_TO_UNION + * @see #db_i2key(int) + * @see #db_ui2key(unsigned int) + * @see common\db.h#db_str2key(unsigned char *) + */ +DBKey db_str2key(unsigned char *key) +{ + DBKey ret; + +#ifdef DB_ENABLE_STATS + if (stats.db_str2key != (unsigned int)~0) stats.db_str2key++; +#endif /* DB_ENABLE_STATS */ + ret.str = key; + return ret; +} +#endif /* DB_MANUAL_CAST_TO_UNION */ + +/** + * Initialize the database system. + * @public + * @see #db_final(void) + * @see common\db.h#db_init(void) + */ +void db_init(void) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_init != (unsigned int)~0) stats.db_init++; +#endif /* DB_ENABLE_STATS */ +} + +/** + * Finalize the database system. + * Frees the memory used by the block reusage system. + * @public + * @see common\db.h#DB_FINAL_NODE_CHECK + * @see #db_init(void) + * @see common\db.h#db_final(void) + */ +void db_final(void) +{ +#ifdef DB_ENABLE_STATS + if (stats.db_final != (unsigned int)~0) + stats.db_final++; + ShowInfo(CL_WHITE"Database nodes"CL_RESET":\n" + "allocated %u, freed %u\n", + stats.db_node_alloc, stats.db_node_free); + ShowInfo(CL_WHITE"Database types"CL_RESET":\n" + "DB_INT : allocated %10u, destroyed %10u\n" + "DB_UINT : allocated %10u, destroyed %10u\n" + "DB_STRING : allocated %10u, destroyed %10u\n" + "DB_ISTRING : allocated %10u, destroyed %10u\n", + stats.db_int_alloc, stats.db_int_destroy, + stats.db_uint_alloc, stats.db_uint_destroy, + stats.db_string_alloc, stats.db_string_destroy, + stats.db_istring_alloc, stats.db_istring_destroy); + ShowInfo(CL_WHITE"Database function counters"CL_RESET":\n" + "db_rotate_left %10u, db_rotate_right %10u,\n" + "db_rebalance %10u, db_rebalance_erase %10u,\n" + "db_is_key_null %10u,\n" + "db_dup_key %10u, db_dup_key_free %10u,\n" + "db_free_add %10u, db_free_remove %10u,\n" + "db_free_lock %10u, db_free_unlock %10u,\n" + "db_int_cmp %10u, db_uint_cmp %10u,\n" + "db_string_cmp %10u, db_istring_cmp %10u,\n" + "db_int_hash %10u, db_uint_hash %10u,\n" + "db_string_hash %10u, db_istring_hash %10u,\n" + "db_release_nothing %10u, db_release_key %10u,\n" + "db_release_data %10u, db_release_both %10u,\n" + "db_get %10u,\n" + "db_getall %10u, db_vgetall %10u,\n" + "db_ensure %10u, db_vensure %10u,\n" + "db_put %10u, db_remove %10u,\n" + "db_foreach %10u, db_vforeach %10u,\n" + "db_clear %10u, db_vclear %10u,\n" + "db_destroy %10u, db_vdestroy %10u,\n" + "db_size %10u, db_type %10u,\n" + "db_options %10u, db_fix_options %10u,\n" + "db_default_cmp %10u, db_default_hash %10u,\n" + "db_default_release %10u, db_custom_release %10u,\n" + "db_alloc %10u, db_i2key %10u,\n" + "db_ui2key %10u, db_str2key %10u,\n" + "db_init %10u, db_final %10u\n", + stats.db_rotate_left, stats.db_rotate_right, + stats.db_rebalance, stats.db_rebalance_erase, + stats.db_is_key_null, + stats.db_dup_key, stats.db_dup_key_free, + stats.db_free_add, stats.db_free_remove, + stats.db_free_lock, stats.db_free_unlock, + stats.db_int_cmp, stats.db_uint_cmp, + stats.db_string_cmp, stats.db_istring_cmp, + stats.db_int_hash, stats.db_uint_hash, + stats.db_string_hash, stats.db_istring_hash, + stats.db_release_nothing, stats.db_release_key, + stats.db_release_data, stats.db_release_both, + stats.db_get, + stats.db_getall, stats.db_vgetall, + stats.db_ensure, stats.db_vensure, + stats.db_put, stats.db_remove, + stats.db_foreach, stats.db_vforeach, + stats.db_clear, stats.db_vclear, + stats.db_destroy, stats.db_vdestroy, + stats.db_size, stats.db_type, + stats.db_options, stats.db_fix_options, + stats.db_default_cmp, stats.db_default_hash, + stats.db_default_release, stats.db_custom_release, + stats.db_alloc, stats.db_i2key, + stats.db_ui2key, stats.db_str2key, + stats.db_init, stats.db_final); +#endif /* DB_ENABLE_STATS */ +} + +// Link DB System - jAthena +void linkdb_insert( struct linkdb_node** head, void *key, void* data) { + struct linkdb_node *node; + if( head == NULL ) return ; + node = aMalloc( sizeof(struct linkdb_node) ); + if( *head == NULL ) { + // first node + *head = node; + node->prev = NULL; + node->next = NULL; + } else { + // link nodes + node->next = *head; + node->prev = (*head)->prev; + (*head)->prev = node; + (*head) = node; + } + node->key = key; + node->data = data; +} + +void* linkdb_search( struct linkdb_node** head, void *key) { + int n = 0; + struct linkdb_node *node; + if( head == NULL ) return NULL; + node = *head; + while( node ) { + if( node->key == key ) { + if( node->prev && n > 5 ) { + // 処理効率改善の為にheadに移動させる + if(node->prev) node->prev->next = node->next; + if(node->next) node->next->prev = node->prev; + node->next = *head; + node->prev = (*head)->prev; + (*head)->prev = node; + (*head) = node; + } + return node->data; + } + node = node->next; + n++; + } + return NULL; +} + +void* linkdb_erase( struct linkdb_node** head, void *key) { + struct linkdb_node *node; + if( head == NULL ) return NULL; + node = *head; + while( node ) { + if( node->key == key ) { + void *data = node->data; + if( node->prev == NULL ) + *head = node->next; + else + node->prev->next = node->next; + if( node->next ) + node->next->prev = node->prev; + aFree( node ); + return data; + } + node = node->next; + } + return NULL; +} + +void linkdb_replace( struct linkdb_node** head, void *key, void *data ) { + int n = 0; + struct linkdb_node *node; + if( head == NULL ) return ; + node = *head; + while( node ) { + if( node->key == key ) { + if( node->prev && n > 5 ) { + // 処理効率改善の為にheadに移動させる + if(node->prev) node->prev->next = node->next; + if(node->next) node->next->prev = node->prev; + node->next = *head; + node->prev = (*head)->prev; + (*head)->prev = node; + (*head) = node; + } + node->data = data; + return ; + } + node = node->next; + n++; + } + // 見つからないので挿入 + linkdb_insert( head, key, data ); +} + +void linkdb_final( struct linkdb_node** head ) { + struct linkdb_node *node, *node2; + if( head == NULL ) return ; + node = *head; + while( node ) { + node2 = node->next; + aFree( node ); + node = node2; + } + *head = NULL; +} + diff --git a/src/common/db.h b/src/common/db.h index 0b6fc08c5..b17ae27f9 100644 --- a/src/common/db.h +++ b/src/common/db.h @@ -1,748 +1,748 @@ -/*****************************************************************************\ - * Copyright (c) Athena Dev Teams - Licensed under GNU GPL * - * For more information, see LICENCE in the main folder * - * * - * This file is separated in two sections: * - * (1) public typedefs, enums, unions, structures and defines * - * (2) public functions * - * * - * Notes on the release system: * - * Whenever an entry is removed from the database both the key and the * - * data are requested to be released. * - * At least one entry is removed when replacing an entry, removing an * - * entry, clearing the database or destroying the database. * - * What is actually released is defined by the release function, the * - * functions of the database only ask for the key and/or data to be * - * released. * - * * - * TODO: * - * - create an enum for the data (with int, unsigned int and void *) * - * - create a custom database allocator * - * - see what functions need or should be added to the database interface * - * * - * HISTORY: * - * 2.1 (Athena build #???#) - Portability fix * - * - Fixed the portability of casting to union and added the functions * - * {@link DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)} and * - * {@link DBInterface#clear(DBInterface,DBApply,...)}. * - * 2.0 (Athena build 4859) - Transition version * - * - Almost everything recoded with a strategy similar to objects, * - * database structure is maintained. * - * 1.0 (up to Athena build 4706) * - * - Previous database system. * - * * - * @version 2.1 (Athena build #???#) - Portability fix * - * @author (Athena build 4859) Flavio @ Amazon Project * - * @author (up to Athena build 4706) Athena Dev Teams * - * @encoding US-ASCII * - * @see common#db.c * -\*****************************************************************************/ -#ifndef _DB_H_ -#define _DB_H_ - -#include - -/*****************************************************************************\ - * (1) Section with public typedefs, enums, unions, structures and defines. * - * DB_MANUAL_CAST_TO_UNION - Define when the compiler doesn't allow casting * - * to unions. * - * DBRelease - Enumeration of release options. * - * DBType - Enumeration of database types. * - * DBOptions - Bitfield enumeration of database options. * - * DBKey - Union of used key types. * - * DBApply - Format of functions applyed to the databases. * - * DBMatcher - Format of matchers used in DBInterface->getall. * - * DBComparator - Format of the comparators used by the databases. * - * DBHasher - Format of the hashers used by the databases. * - * DBReleaser - Format of the releasers used by the databases. * - * DBInterface - Structure of the interface of the database. * -\*****************************************************************************/ - -/** - * Define this to enable the functions that cast to unions. - * Required when the compiler doesn't support casting to unions. - * NOTE: It is recommened that the conditional tests to determine if this - * should be defined be located in a makefile or a header file specific for - * of compatibility and portability issues. - * @public - * @see #db_i2key(int) - * @see #db_ui2key(unsigned int) - * @see #db_str2key(unsigned char *) - */ -//#define DB_MANUAL_CAST_TO_UNION - -/** - * Bitfield with what should be released by the releaser function (if the - * function supports it). - * @public - * @see #DBReleaser - * @see #db_custom_release(DBRelease) - */ -typedef enum { - DB_RELEASE_NOTHING = 0, - DB_RELEASE_KEY = 1, - DB_RELEASE_DATA = 2, - DB_RELEASE_BOTH = 3 -} DBRelease; - -/** - * Supported types of database. - * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the - * types of databases. - * @param DB_INT Uses int's for keys - * @param DB_UINT Uses unsigned int's for keys - * @param DB_STRING Uses strings for keys. - * @param DB_ISTRING Uses case insensitive strings for keys. - * @public - * @see #DBOptions - * @see #DBKey - * @see #db_fix_options(DBType,DBOptions) - * @see #db_default_cmp(DBType) - * @see #db_default_hash(DBType) - * @see #db_default_release(DBType,DBOptions) - * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short) - */ -typedef enum { - DB_INT, - DB_UINT, - DB_STRING, - DB_ISTRING -} DBType; - -/** - * Bitfield of options that define the behaviour of the database. - * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the - * types of databases. - * @param DB_OPT_BASE Base options: does not duplicate keys, releases nothing - * and does not allow NULL keys or NULL data. - * @param DB_OPT_DUP_KEY Duplicates the keys internally. If DB_OPT_RELEASE_KEY - * is defined, the real key is freed as soon as the entry is added. - * @param DB_OPT_RELEASE_KEY Releases the key. - * @param DB_OPT_RELEASE_DATA Releases the data whenever an entry is removed - * from the database. - * WARNING: for funtions that return the data (like DBInterface->remove), - * a dangling pointer will be returned. - * @param DB_OPT_RELEASE_BOTH Releases both key and data. - * @param DB_OPT_ALLOW_NULL_KEY Allow NULL keys in the database. - * @param DB_OPT_ALLOW_NULL_DATA Allow NULL data in the database. - * @public - * @see #db_fix_options(DBType,DBOptions) - * @see #db_default_release(DBType,DBOptions) - * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short) - */ -typedef enum { - DB_OPT_BASE = 0, - DB_OPT_DUP_KEY = 1, - DB_OPT_RELEASE_KEY = 2, - DB_OPT_RELEASE_DATA = 4, - DB_OPT_RELEASE_BOTH = 6, - DB_OPT_ALLOW_NULL_KEY = 8, - DB_OPT_ALLOW_NULL_DATA = 16, -} DBOptions; - -/** - * Union of key types used by the database. - * @param i Type of key for DB_INT databases - * @param ui Type of key for DB_UINT databases - * @param str Type of key for DB_STRING and DB_ISTRING databases - * @public - * @see #DBType - * @see #DBApply(DBKey,void *,va_list) - * @see #DBMatcher(DBKey,void *,va_list) - * @see #DBComparator(DBKey,DBKey,unsigned short) - * @see #DBHasher(DBKey,unsigned short) - * @see #DBReleaser(DBKey,void *,DBRelease) - * @see DBInterface#get(DBInterface,DBKey) - * @see DBInterface#put(DBInterface,DBKey,void *) - * @see DBInterface#remove(DBInterface,DBKey) - */ -typedef union { - int i; - unsigned int ui; - unsigned char *str; -} DBKey; - -/** - * Format of funtions that create the data for the key when the entry doesn't - * exist in the database yet. - * @param key Key of the database entry - * @param args Extra arguments of the funtion - * @return Data identified by the key to be put in the database - * @public - * @see #DBKey - * @see DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list) - * @see DBInterface#ensure(DBInterface,DBKey,DBCreateData,...) - */ -typedef void *(*DBCreateData)(DBKey key, va_list args); - -/** - * Format of functions to be applyed to an unspecified quantity of entries of - * a database. - * Any function that applyes this function to the database will return the sum - * of values returned by this function. - * @param key Key of the database entry - * @param data Data of the database entry - * @param args Extra arguments of the funtion - * @return Value to be added up by the funtion that is applying this - * @public - * @see #DBKey - * @see DBInterface#vforeach(DBInterface,DBApply,va_list) - * @see DBInterface#foreach(DBInterface,DBApply,...) - * @see DBInterface#vdestroy(DBInterface,DBApply,va_list) - * @see DBInterface#destroy(DBInterface,DBApply,...) - */ -typedef int (*DBApply)(DBKey key, void *data, va_list args); - -/** - * Format of functions that match database entries. - * The purpose of the match depends on the function that is calling the matcher. - * Returns 0 if it is a match, another number otherwise. - * @param key Key of the database entry - * @param data Data of the database entry - * @param args Extra arguments of the function - * @return 0 if a match, another number otherwise - * @public - * @see #DBKey - * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatcher,...) - */ -typedef int (*DBMatcher)(DBKey key, void *data, va_list args); - -/** - * Format of the comparators used internally by the database system. - * Compares key1 to key2. - * maxlen is the maximum number of character used in DB_STRING and - * DB_ISTRING databases. If 0, the maximum number of maxlen is used (64K). - * Returns 0 is equal, negative if lower and positive is higher. - * @param key1 Key being compared - * @param key2 Key we are comparing to - * @param maxlen Maximum number of characters used in DB_STRING and DB_ISTRING - * databases. - * @return 0 if equal, negative if lower and positive if higher - * @public - * @see #DBKey - * @see #db_default_cmp(DBType) - */ -typedef int (*DBComparator)(DBKey key1, DBKey key2, unsigned short maxlen); - -/** - * Format of the hashers used internally by the database system. - * Creates the hash of the key. - * maxlen is the maximum number of character used in DB_STRING and - * DB_ISTRING databases. If 0, the maximum number of maxlen is used (64K). - * @param key Key being hashed - * @param maxlen Maximum number of characters used in DB_STRING and DB_ISTRING - * databases. - * @return Hash of the key - * @public - * @see #DBKey - * @see #db_default_hash(DBType) - */ -typedef unsigned int (*DBHasher)(DBKey key, unsigned short maxlen); - -/** - * Format of the releaser used by the database system. - * Releases nothing, the key, the data or both. - * All standard releasers use aFree to release. - * @param key Key of the database entry - * @param data Data of the database entry - * @param which What is being requested to be released - * @public - * @see #DBRelease - * @see #DBKey - * @see #db_default_releaser(DBType,DBOptions) - * @see #db_custom_release(DBRelease) - */ -typedef void (*DBReleaser)(DBKey key, void *data, DBRelease which); - -/** - * Public interface of a database. Only contains funtions. - * All the functions take the interface as the first argument. - * @public - * @see DBInterface#get(DBInterface,DBKey) - * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...) - * @see DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list) - * @see DBInterface#put(DBInterface,DBKey,void *) - * @see DBInterface#remove(DBInterface,DBKey) - * @see DBInterface#foreach(DBInterface,DBApply,...) - * @see DBInterface#vforeach(DBInterface,DBApply,va_list) - * @see DBInterface#destroy(DBInterface,DBApply,...) - * @see DBInterface#destroy(DBInterface,DBApply,va_list) - * @see DBInterface#size(DBInterface) - * @see DBInterface#type(DBInterface) - * @see DBInterface#options(DBInterface) - * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short) - */ -typedef struct dbt { - - /** - * Get the data of the entry identifid by the key. - * @param dbi Interface of the database - * @param key Key that identifies the entry - * @return Data of the entry or NULL if not found - * @protected - * @see #DBKey - * @see #DBInterface - * @see common\db.c#db_get(DBInterface,DBKey) - */ - void *(*get)(struct dbt *dbi, DBKey key); - - /** - * Just calls {@link DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)}. - * Get the data of the entries matched by match. - * It puts a maximum of max entries into buf. - * If buf is NULL, it only counts the matches. - * Returns the number of entries that matched. - * NOTE: if the value returned is greater than max, only the - * first max entries found are put into the buffer. - * @param dbi Interface of the database - * @param buf Buffer to put the data of the matched entries - * @param max Maximum number of data entries to be put into buf - * @param match Function that matches the database entries - * @param ... Extra arguments for match - * @return The number of entries that matched - * @protected - * @see #DBMatcher(DBKey key, void *data, va_list args) - * @see #DBInterface - * @see DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list) - * @see common\db.c#db_getall(DBInterface,void **,unsigned int,DBMatch,...) - */ - unsigned int (*getall)(struct dbt *dbi, void **buf, unsigned int max, DBMatcher match, ...); - - /** - * Get the data of the entries matched by match. - * It puts a maximum of max entries into buf. - * If buf is NULL, it only counts the matches. - * Returns the number of entries that matched. - * NOTE: if the value returned is greater than max, only the - * first max entries found are put into the buffer. - * @param dbi Interface of the database - * @param buf Buffer to put the data of the matched entries - * @param max Maximum number of data entries to be put into buf - * @param match Function that matches the database entries - * @param ... Extra arguments for match - * @return The number of entries that matched - * @protected - * @see #DBMatcher(DBKey key, void *data, va_list args) - * @see #DBInterface - * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...) - * @see common\db.c#db_vgetall(DBInterface,void **,unsigned int,DBMatch,va_list) - */ - unsigned int (*vgetall)(struct dbt *dbi, void **buf, unsigned int max, DBMatcher match, va_list args); - - /** - * Just calls {@link common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)}. - * Get the data of the entry identified by the key. - * If the entry does not exist, an entry is added with the data returned by - * create. - * @param dbi Interface of the database - * @param key Key that identifies the entry - * @param create Function used to create the data if the entry doesn't exist - * @param ... Extra arguments for create - * @return Data of the entry - * @protected - * @see #DBKey - * @see #DBCreateData - * @see #DBInterface - * @see DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list) - * @see common\db.c#db_ensure(DBInterface,DBKey,DBCreateData,...) - */ - void *(*ensure)(struct dbt *dbi, DBKey key, DBCreateData create, ...); - - /** - * Get the data of the entry identified by the key. - * If the entry does not exist, an entry is added with the data returned by - * create. - * @param dbi Interface of the database - * @param key Key that identifies the entry - * @param create Function used to create the data if the entry doesn't exist - * @param args Extra arguments for create - * @return Data of the entry - * @protected - * @see #DBKey - * @see #DBCreateData - * @see #DBInterface - * @see DBInterface#ensure(DBInterface,DBKey,DBCreateData,...) - * @see common\db.c#db_vensure(DBInterface,DBKey,DBCreateData,va_list) - */ - void *(*vensure)(struct dbt *dbi, DBKey key, DBCreateData create, va_list args); - - /** - * Put the data identified by the key in the database. - * Returns the previous data if the entry exists or NULL. - * NOTE: Uses the new key, the old one is released. - * @param dbi Interface of the database - * @param key Key that identifies the data - * @param data Data to be put in the database - * @return The previous data if the entry exists or NULL - * @protected - * @see #DBKey - * @see #DBInterface - * @see common\db.c#db_put(DBInterface,DBKey,void *) - */ - void *(*put)(struct dbt *dbi, DBKey key, void *data); - - /** - * Remove an entry from the database. - * Returns the data of the entry. - * NOTE: The key (of the database) is released. - * @param dbi Interface of the database - * @param key Key that identifies the entry - * @return The data of the entry or NULL if not found - * @protected - * @see #DBKey - * @see #DBInterface - * @see common\db.c#db_remove(DBInterface,DBKey) - */ - void *(*remove)(struct dbt *dbi, DBKey key); - - /** - * Just calls {@link DBInterface#vforeach(DBInterface,DBApply,va_list)}. - * Apply func to every entry in the database. - * Returns the sum of values returned by func. - * @param dbi Interface of the database - * @param func Function to be applyed - * @param ... Extra arguments for func - * @return Sum of the values returned by func - * @protected - * @see #DBInterface - * @see #DBApply(DBKey,void *,va_list) - * @see DBInterface#vforeach(DBInterface,DBApply,va_list) - * @see common\db.c#db_foreach(DBInterface,DBApply,...) - */ - int (*foreach)(struct dbt *dbi, DBApply func, ...); - - /** - * Apply func to every entry in the database. - * Returns the sum of values returned by func. - * @param dbi Interface of the database - * @param func Function to be applyed - * @param args Extra arguments for func - * @return Sum of the values returned by func - * @protected - * @see #DBApply(DBKey,void *,va_list) - * @see #DBInterface - * @see DBInterface#foreach(DBInterface,DBApply,...) - * @see common\db.c#db_vforeach(DBInterface,DBApply,va_list) - */ - int (*vforeach)(struct dbt *dbi, DBApply func, va_list args); - - /** - * Just calls {@link DBInterface#vclear(DBInterface,DBApply,va_list)}. - * Removes all entries from the database. - * Before deleting an entry, func is applyed to it. - * Releases the key and the data. - * Returns the sum of values returned by func, if it exists. - * @param dbi Interface of the database - * @param func Function to be applyed to every entry before deleting - * @param ... Extra arguments for func - * @return Sum of values returned by func - * @protected - * @see #DBApply(DBKey,void *,va_list) - * @see #DBInterface - * @see DBInterface#vclear(DBInterface,DBApply,va_list) - * @see common\db.c#db_clear(DBInterface,DBApply,...) - */ - int (*clear)(struct dbt *dbi, DBApply func, ...); - - /** - * Removes all entries from the database. - * Before deleting an entry, func is applyed to it. - * Releases the key and the data. - * Returns the sum of values returned by func, if it exists. - * @param dbi Interface of the database - * @param func Function to be applyed to every entry before deleting - * @param args Extra arguments for func - * @return Sum of values returned by func - * @protected - * @see #DBApply(DBKey,void *,va_list) - * @see #DBInterface - * @see DBInterface#clear(DBInterface,DBApply,...) - * @see common\db.c#vclear(DBInterface,DBApply,va_list) - */ - int (*vclear)(struct dbt *dbi, DBApply func, va_list args); - - /** - * Just calls {@link DBInterface#vdestroy(DBInterface,DBApply,va_list)}. - * Finalize the database, feeing all the memory it uses. - * Before deleting an entry, func is applyed to it. - * Releases the key and the data. - * Returns the sum of values returned by func, if it exists. - * NOTE: This locks the database globally. Any attempt to insert or remove - * a database entry will give an error and be aborted (except for clearing). - * @param dbi Interface of the database - * @param func Function to be applyed to every entry before deleting - * @param ... Extra arguments for func - * @return Sum of values returned by func - * @protected - * @see #DBApply(DBKey,void *,va_list) - * @see #DBInterface - * @see DBInterface#vdestroy(DBInterface,DBApply,va_list) - * @see common\db.c#db_destroy(DBInterface,DBApply,...) - */ - int (*destroy)(struct dbt *dbi, DBApply func, ...); - - /** - * Finalize the database, feeing all the memory it uses. - * Before deleting an entry, func is applyed to it. - * Returns the sum of values returned by func, if it exists. - * NOTE: This locks the database globally. Any attempt to insert or remove - * a database entry will give an error and be aborted (except for clearing). - * @param dbi Interface of the database - * @param func Function to be applyed to every entry before deleting - * @param args Extra arguments for func - * @return Sum of values returned by func - * @protected - * @see #DBInterface - * @see #DBApply(DBKey,void *,va_list) - * @see DBInterface#destroy(DBInterface,DBApply,...) - * @see common\db.c#db_vdestroy(DBInterface,DBApply,va_list) - */ - int (*vdestroy)(struct dbt *dbi, DBApply func, va_list args); - - /** - * Return the size of the database (number of items in the database). - * @param dbi Interface of the database - * @return Size of the database - * @protected - * @see #DBInterface - * @see common\db.c#db_size(DBInterface) - */ - unsigned int (*size)(struct dbt *dbi); - - /** - * Return the type of the database. - * @param dbi Interface of the database - * @return Type of the database - * @protected - * @see #DBType - * @see #DBInterface - * @see common\db.c#db_type(DBInterface) - */ - DBType (*type)(struct dbt *dbi); - - /** - * Return the options of the database. - * @param dbi Interface of the database - * @return Options of the database - * @protected - * @see #DBOptions - * @see #DBInterface - * @see common\db.c#db_options(DBInterface) - */ - DBOptions (*options)(struct dbt *dbi); - -} *DBInterface; - -//For easy access to the common functions. -#ifdef DB_MANUAL_CAST_TO_UNION -# define i2key db_i2key -# define ui2key db_ui2key -# define str2key db_str2key -#else /* not DB_MANUAL_CAST_TO_UNION */ -# define i2key(k) ((DBKey)(int)(k)) -# define ui2key(k) ((DBKey)(unsigned int)(k)) -# define str2key(k) ((DBKey)(unsigned char *)(k)) -#endif /* DB_MANUAL_CAST_TO_UNION / not DB_MANUAL_CAST_TO_UNION */ - -#define db_get(db,k) (db)->get((db),(k)) -#define idb_get(db,k) (db)->get((db),i2key(k)) -#define uidb_get(db,k) (db)->get((db),ui2key(k)) -#define strdb_get(db,k) (db)->get((db),str2key(k)) - -#define db_put(db,k,d) (db)->put((db),(k),(d)) -#define idb_put(db,k,d) (db)->put((db),i2key(k),(d)) -#define uidb_put(db,k,d) (db)->put((db),ui2key(k),(d)) -#define strdb_put(db,k,d) (db)->put((db),str2key(k),(d)) - -#define db_remove(db,k) (db)->remove((db),(k)) -#define idb_remove(db,k) (db)->remove((db),i2key(k)) -#define uidb_remove(db,k) (db)->remove((db),ui2key(k)) -#define strdb_remove(db,k) (db)->remove((db),str2key(k)) - -//These are discarding the possible vargs you could send to the function, so those -//that require vargs must not use these defines. -#define db_ensure(db,k,f) (db)->ensure((db),(k),f) -#define idb_ensure(db,k,f) (db)->ensure((db),i2key(k),f) -#define uidb_ensure(db,k,f) (db)->ensure((db),ui2key(k),f) -#define strdb_ensure(db,k,f) (db)->ensure((db),str2key(k),f) - -/*****************************************************************************\ - * (2) Section with public functions. * - * db_fix_options - Fix the options for a type of database. * - * db_default_cmp - Get the default comparator for a type of database. * - * db_default_hash - Get the default hasher for a type of database. * - * db_default_release - Get the default releaser for a type of database * - * with the fixed options. * - * db_custom_release - Get the releaser that behaves as specified. * - * db_alloc - Allocate a new database. * - * db_i2key - Manual cast from 'int' to 'DBKey'. * - * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. * - * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. * - * db_init - Initialise the database system. * - * db_final - Finalise the database system. * -\*****************************************************************************/ - -/** - * Returns the fixed options according to the database type. - * Sets required options and unsets unsupported options. - * For numeric databases DB_OPT_DUP_KEY and DB_OPT_RELEASE_KEY are unset. - * @param type Type of the database - * @param options Original options of the database - * @return Fixed options of the database - * @private - * @see #DBType - * @see #DBOptions - * @see #db_default_release(DBType,DBOptions) - * @see common\db.c#db_fix_options(DBType,DBOptions) - */ -DBOptions db_fix_options(DBType type, DBOptions options); - -/** - * Returns the default comparator for the type of database. - * @param type Type of database - * @return Comparator for the type of database or NULL if unknown database - * @public - * @see #DBType - * @see #DBComparator - * @see common\db.c#db_default_cmp(DBType) - */ -DBComparator db_default_cmp(DBType type); - -/** - * Returns the default hasher for the specified type of database. - * @param type Type of database - * @return Hasher of the type of database or NULL if unknown database - * @public - * @see #DBType - * @see #DBHasher - * @see common\db.c#db_default_hash(DBType) - */ -DBHasher db_default_hash(DBType type); - -/** - * Returns the default releaser for the specified type of database with the - * specified options. - * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)} - * before choosing the releaser - * @param type Type of database - * @param options Options of the database - * @return Default releaser for the type of database with the fixed options - * @public - * @see #DBType - * @see #DBOptions - * @see #DBReleaser - * @see #db_fix_options(DBType,DBOptions) - * @see #db_custom_release(DBRelease) - * @see common\db.c#db_default_release(DBType,DBOptions) - */ -DBReleaser db_default_release(DBType type, DBOptions options); - -/** - * Returns the releaser that behaves as which specifies. - * @param which Defines what the releaser releases - * @return Releaser for the specified release options - * @public - * @see #DBRelease - * @see #DBReleaser - * @see #db_default_release(DBType,DBOptions) - * @see common\db.c#db_custom_release(DBRelease) - */ -DBReleaser db_custom_release(DBRelease which); - -/** - * Allocate a new database of the specified type. - * It uses the default comparator, hasher and releaser of the specified - * database type and fixed options. - * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)} - * before creating the database. - * @param file File where the database is being allocated - * @param line Line of the file where the database is being allocated - * @param type Type of database - * @param options Options of the database - * @param maxlen Maximum length of the string to be used as key in string - * databases - * @return The interface of the database - * @public - * @see #DBType - * @see #DBInterface - * @see #db_default_cmp(DBType) - * @see #db_default_hash(DBType) - * @see #db_default_release(DBType,DBOptions) - * @see #db_fix_options(DBType,DBOptions) - * @see common\db.c#db_alloc(const char *,int,DBType,DBOptions,unsigned short) - */ -DBInterface db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen); - -#ifdef DB_MANUAL_CAST_TO_UNION -/** - * Manual cast from 'int' to the union DBKey. - * Created for compilers that don't support casting to unions. - * @param key Key to be casted - * @return The key as a DBKey union - * @public - * @see #DB_MANUAL_CAST_TO_UNION - * @see #db_ui2key(unsigned int) - * @see #db_str2key(unsigned char *) - * @see common\db.c#db_i2key(int) - */ -DBKey db_i2key(int key); - -/** - * Manual cast from 'unsigned int' to the union DBKey. - * Created for compilers that don't support casting to unions. - * @param key Key to be casted - * @return The key as a DBKey union - * @public - * @see #DB_MANUAL_CAST_TO_UNION - * @see #db_i2key(int) - * @see #db_str2key(unsigned char *) - * @see common\db.c#db_ui2key(unsigned int) - */ -DBKey db_ui2key(unsigned int key); - -/** - * Manual cast from 'unsigned char *' to the union DBKey. - * Created for compilers that don't support casting to unions. - * @param key Key to be casted - * @return The key as a DBKey union - * @public - * @see #DB_MANUAL_CAST_TO_UNION - * @see #db_i2key(int) - * @see #db_ui2key(unsigned int) - * @see common\db.c#db_str2key(unsigned char *) - */ -DBKey db_str2key(unsigned char *key); -#endif /* DB_MANUAL_CAST_TO_UNION */ - -/** - * Initialize the database system. - * @public - * @see #db_final(void) - * @see common\db.c#db_init(void) - */ -void db_init(void); - -/** - * Finalize the database system. - * Frees the memory used by the block reusage system. - * @public - * @see #db_init(void) - * @see common\db.c#db_final(void) - */ -void db_final(void); - -// Link DB System - From jAthena -struct linkdb_node { - struct linkdb_node *next; - struct linkdb_node *prev; - void *key; - void *data; -}; - -void linkdb_insert ( struct linkdb_node** head, void *key, void* data); // 重複を考慮しない -void linkdb_replace( struct linkdb_node** head, void *key, void* data); // 重複を考慮する -void* linkdb_search ( struct linkdb_node** head, void *key); -void* linkdb_erase ( struct linkdb_node** head, void *key); -void linkdb_final ( struct linkdb_node** head ); - -#endif +/*****************************************************************************\ + * Copyright (c) Athena Dev Teams - Licensed under GNU GPL * + * For more information, see LICENCE in the main folder * + * * + * This file is separated in two sections: * + * (1) public typedefs, enums, unions, structures and defines * + * (2) public functions * + * * + * Notes on the release system: * + * Whenever an entry is removed from the database both the key and the * + * data are requested to be released. * + * At least one entry is removed when replacing an entry, removing an * + * entry, clearing the database or destroying the database. * + * What is actually released is defined by the release function, the * + * functions of the database only ask for the key and/or data to be * + * released. * + * * + * TODO: * + * - create an enum for the data (with int, unsigned int and void *) * + * - create a custom database allocator * + * - see what functions need or should be added to the database interface * + * * + * HISTORY: * + * 2.1 (Athena build #???#) - Portability fix * + * - Fixed the portability of casting to union and added the functions * + * {@link DBInterface#ensure(DBInterface,DBKey,DBCreateData,...)} and * + * {@link DBInterface#clear(DBInterface,DBApply,...)}. * + * 2.0 (Athena build 4859) - Transition version * + * - Almost everything recoded with a strategy similar to objects, * + * database structure is maintained. * + * 1.0 (up to Athena build 4706) * + * - Previous database system. * + * * + * @version 2.1 (Athena build #???#) - Portability fix * + * @author (Athena build 4859) Flavio @ Amazon Project * + * @author (up to Athena build 4706) Athena Dev Teams * + * @encoding US-ASCII * + * @see common#db.c * +\*****************************************************************************/ +#ifndef _DB_H_ +#define _DB_H_ + +#include + +/*****************************************************************************\ + * (1) Section with public typedefs, enums, unions, structures and defines. * + * DB_MANUAL_CAST_TO_UNION - Define when the compiler doesn't allow casting * + * to unions. * + * DBRelease - Enumeration of release options. * + * DBType - Enumeration of database types. * + * DBOptions - Bitfield enumeration of database options. * + * DBKey - Union of used key types. * + * DBApply - Format of functions applyed to the databases. * + * DBMatcher - Format of matchers used in DBInterface->getall. * + * DBComparator - Format of the comparators used by the databases. * + * DBHasher - Format of the hashers used by the databases. * + * DBReleaser - Format of the releasers used by the databases. * + * DBInterface - Structure of the interface of the database. * +\*****************************************************************************/ + +/** + * Define this to enable the functions that cast to unions. + * Required when the compiler doesn't support casting to unions. + * NOTE: It is recommened that the conditional tests to determine if this + * should be defined be located in a makefile or a header file specific for + * of compatibility and portability issues. + * @public + * @see #db_i2key(int) + * @see #db_ui2key(unsigned int) + * @see #db_str2key(unsigned char *) + */ +//#define DB_MANUAL_CAST_TO_UNION + +/** + * Bitfield with what should be released by the releaser function (if the + * function supports it). + * @public + * @see #DBReleaser + * @see #db_custom_release(DBRelease) + */ +typedef enum { + DB_RELEASE_NOTHING = 0, + DB_RELEASE_KEY = 1, + DB_RELEASE_DATA = 2, + DB_RELEASE_BOTH = 3 +} DBRelease; + +/** + * Supported types of database. + * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the + * types of databases. + * @param DB_INT Uses int's for keys + * @param DB_UINT Uses unsigned int's for keys + * @param DB_STRING Uses strings for keys. + * @param DB_ISTRING Uses case insensitive strings for keys. + * @public + * @see #DBOptions + * @see #DBKey + * @see #db_fix_options(DBType,DBOptions) + * @see #db_default_cmp(DBType) + * @see #db_default_hash(DBType) + * @see #db_default_release(DBType,DBOptions) + * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short) + */ +typedef enum { + DB_INT, + DB_UINT, + DB_STRING, + DB_ISTRING +} DBType; + +/** + * Bitfield of options that define the behaviour of the database. + * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the + * types of databases. + * @param DB_OPT_BASE Base options: does not duplicate keys, releases nothing + * and does not allow NULL keys or NULL data. + * @param DB_OPT_DUP_KEY Duplicates the keys internally. If DB_OPT_RELEASE_KEY + * is defined, the real key is freed as soon as the entry is added. + * @param DB_OPT_RELEASE_KEY Releases the key. + * @param DB_OPT_RELEASE_DATA Releases the data whenever an entry is removed + * from the database. + * WARNING: for funtions that return the data (like DBInterface->remove), + * a dangling pointer will be returned. + * @param DB_OPT_RELEASE_BOTH Releases both key and data. + * @param DB_OPT_ALLOW_NULL_KEY Allow NULL keys in the database. + * @param DB_OPT_ALLOW_NULL_DATA Allow NULL data in the database. + * @public + * @see #db_fix_options(DBType,DBOptions) + * @see #db_default_release(DBType,DBOptions) + * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short) + */ +typedef enum { + DB_OPT_BASE = 0, + DB_OPT_DUP_KEY = 1, + DB_OPT_RELEASE_KEY = 2, + DB_OPT_RELEASE_DATA = 4, + DB_OPT_RELEASE_BOTH = 6, + DB_OPT_ALLOW_NULL_KEY = 8, + DB_OPT_ALLOW_NULL_DATA = 16, +} DBOptions; + +/** + * Union of key types used by the database. + * @param i Type of key for DB_INT databases + * @param ui Type of key for DB_UINT databases + * @param str Type of key for DB_STRING and DB_ISTRING databases + * @public + * @see #DBType + * @see #DBApply(DBKey,void *,va_list) + * @see #DBMatcher(DBKey,void *,va_list) + * @see #DBComparator(DBKey,DBKey,unsigned short) + * @see #DBHasher(DBKey,unsigned short) + * @see #DBReleaser(DBKey,void *,DBRelease) + * @see DBInterface#get(DBInterface,DBKey) + * @see DBInterface#put(DBInterface,DBKey,void *) + * @see DBInterface#remove(DBInterface,DBKey) + */ +typedef union { + int i; + unsigned int ui; + unsigned char *str; +} DBKey; + +/** + * Format of funtions that create the data for the key when the entry doesn't + * exist in the database yet. + * @param key Key of the database entry + * @param args Extra arguments of the funtion + * @return Data identified by the key to be put in the database + * @public + * @see #DBKey + * @see DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list) + * @see DBInterface#ensure(DBInterface,DBKey,DBCreateData,...) + */ +typedef void *(*DBCreateData)(DBKey key, va_list args); + +/** + * Format of functions to be applyed to an unspecified quantity of entries of + * a database. + * Any function that applyes this function to the database will return the sum + * of values returned by this function. + * @param key Key of the database entry + * @param data Data of the database entry + * @param args Extra arguments of the funtion + * @return Value to be added up by the funtion that is applying this + * @public + * @see #DBKey + * @see DBInterface#vforeach(DBInterface,DBApply,va_list) + * @see DBInterface#foreach(DBInterface,DBApply,...) + * @see DBInterface#vdestroy(DBInterface,DBApply,va_list) + * @see DBInterface#destroy(DBInterface,DBApply,...) + */ +typedef int (*DBApply)(DBKey key, void *data, va_list args); + +/** + * Format of functions that match database entries. + * The purpose of the match depends on the function that is calling the matcher. + * Returns 0 if it is a match, another number otherwise. + * @param key Key of the database entry + * @param data Data of the database entry + * @param args Extra arguments of the function + * @return 0 if a match, another number otherwise + * @public + * @see #DBKey + * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatcher,...) + */ +typedef int (*DBMatcher)(DBKey key, void *data, va_list args); + +/** + * Format of the comparators used internally by the database system. + * Compares key1 to key2. + * maxlen is the maximum number of character used in DB_STRING and + * DB_ISTRING databases. If 0, the maximum number of maxlen is used (64K). + * Returns 0 is equal, negative if lower and positive is higher. + * @param key1 Key being compared + * @param key2 Key we are comparing to + * @param maxlen Maximum number of characters used in DB_STRING and DB_ISTRING + * databases. + * @return 0 if equal, negative if lower and positive if higher + * @public + * @see #DBKey + * @see #db_default_cmp(DBType) + */ +typedef int (*DBComparator)(DBKey key1, DBKey key2, unsigned short maxlen); + +/** + * Format of the hashers used internally by the database system. + * Creates the hash of the key. + * maxlen is the maximum number of character used in DB_STRING and + * DB_ISTRING databases. If 0, the maximum number of maxlen is used (64K). + * @param key Key being hashed + * @param maxlen Maximum number of characters used in DB_STRING and DB_ISTRING + * databases. + * @return Hash of the key + * @public + * @see #DBKey + * @see #db_default_hash(DBType) + */ +typedef unsigned int (*DBHasher)(DBKey key, unsigned short maxlen); + +/** + * Format of the releaser used by the database system. + * Releases nothing, the key, the data or both. + * All standard releasers use aFree to release. + * @param key Key of the database entry + * @param data Data of the database entry + * @param which What is being requested to be released + * @public + * @see #DBRelease + * @see #DBKey + * @see #db_default_releaser(DBType,DBOptions) + * @see #db_custom_release(DBRelease) + */ +typedef void (*DBReleaser)(DBKey key, void *data, DBRelease which); + +/** + * Public interface of a database. Only contains funtions. + * All the functions take the interface as the first argument. + * @public + * @see DBInterface#get(DBInterface,DBKey) + * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...) + * @see DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list) + * @see DBInterface#put(DBInterface,DBKey,void *) + * @see DBInterface#remove(DBInterface,DBKey) + * @see DBInterface#foreach(DBInterface,DBApply,...) + * @see DBInterface#vforeach(DBInterface,DBApply,va_list) + * @see DBInterface#destroy(DBInterface,DBApply,...) + * @see DBInterface#destroy(DBInterface,DBApply,va_list) + * @see DBInterface#size(DBInterface) + * @see DBInterface#type(DBInterface) + * @see DBInterface#options(DBInterface) + * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short) + */ +typedef struct dbt { + + /** + * Get the data of the entry identifid by the key. + * @param dbi Interface of the database + * @param key Key that identifies the entry + * @return Data of the entry or NULL if not found + * @protected + * @see #DBKey + * @see #DBInterface + * @see common\db.c#db_get(DBInterface,DBKey) + */ + void *(*get)(struct dbt *dbi, DBKey key); + + /** + * Just calls {@link DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list)}. + * Get the data of the entries matched by match. + * It puts a maximum of max entries into buf. + * If buf is NULL, it only counts the matches. + * Returns the number of entries that matched. + * NOTE: if the value returned is greater than max, only the + * first max entries found are put into the buffer. + * @param dbi Interface of the database + * @param buf Buffer to put the data of the matched entries + * @param max Maximum number of data entries to be put into buf + * @param match Function that matches the database entries + * @param ... Extra arguments for match + * @return The number of entries that matched + * @protected + * @see #DBMatcher(DBKey key, void *data, va_list args) + * @see #DBInterface + * @see DBInterface#vgetall(DBInterface,void **,unsigned int,DBMatch,va_list) + * @see common\db.c#db_getall(DBInterface,void **,unsigned int,DBMatch,...) + */ + unsigned int (*getall)(struct dbt *dbi, void **buf, unsigned int max, DBMatcher match, ...); + + /** + * Get the data of the entries matched by match. + * It puts a maximum of max entries into buf. + * If buf is NULL, it only counts the matches. + * Returns the number of entries that matched. + * NOTE: if the value returned is greater than max, only the + * first max entries found are put into the buffer. + * @param dbi Interface of the database + * @param buf Buffer to put the data of the matched entries + * @param max Maximum number of data entries to be put into buf + * @param match Function that matches the database entries + * @param ... Extra arguments for match + * @return The number of entries that matched + * @protected + * @see #DBMatcher(DBKey key, void *data, va_list args) + * @see #DBInterface + * @see DBInterface#getall(DBInterface,void **,unsigned int,DBMatch,...) + * @see common\db.c#db_vgetall(DBInterface,void **,unsigned int,DBMatch,va_list) + */ + unsigned int (*vgetall)(struct dbt *dbi, void **buf, unsigned int max, DBMatcher match, va_list args); + + /** + * Just calls {@link common\db.h\DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list)}. + * Get the data of the entry identified by the key. + * If the entry does not exist, an entry is added with the data returned by + * create. + * @param dbi Interface of the database + * @param key Key that identifies the entry + * @param create Function used to create the data if the entry doesn't exist + * @param ... Extra arguments for create + * @return Data of the entry + * @protected + * @see #DBKey + * @see #DBCreateData + * @see #DBInterface + * @see DBInterface#vensure(DBInterface,DBKey,DBCreateData,va_list) + * @see common\db.c#db_ensure(DBInterface,DBKey,DBCreateData,...) + */ + void *(*ensure)(struct dbt *dbi, DBKey key, DBCreateData create, ...); + + /** + * Get the data of the entry identified by the key. + * If the entry does not exist, an entry is added with the data returned by + * create. + * @param dbi Interface of the database + * @param key Key that identifies the entry + * @param create Function used to create the data if the entry doesn't exist + * @param args Extra arguments for create + * @return Data of the entry + * @protected + * @see #DBKey + * @see #DBCreateData + * @see #DBInterface + * @see DBInterface#ensure(DBInterface,DBKey,DBCreateData,...) + * @see common\db.c#db_vensure(DBInterface,DBKey,DBCreateData,va_list) + */ + void *(*vensure)(struct dbt *dbi, DBKey key, DBCreateData create, va_list args); + + /** + * Put the data identified by the key in the database. + * Returns the previous data if the entry exists or NULL. + * NOTE: Uses the new key, the old one is released. + * @param dbi Interface of the database + * @param key Key that identifies the data + * @param data Data to be put in the database + * @return The previous data if the entry exists or NULL + * @protected + * @see #DBKey + * @see #DBInterface + * @see common\db.c#db_put(DBInterface,DBKey,void *) + */ + void *(*put)(struct dbt *dbi, DBKey key, void *data); + + /** + * Remove an entry from the database. + * Returns the data of the entry. + * NOTE: The key (of the database) is released. + * @param dbi Interface of the database + * @param key Key that identifies the entry + * @return The data of the entry or NULL if not found + * @protected + * @see #DBKey + * @see #DBInterface + * @see common\db.c#db_remove(DBInterface,DBKey) + */ + void *(*remove)(struct dbt *dbi, DBKey key); + + /** + * Just calls {@link DBInterface#vforeach(DBInterface,DBApply,va_list)}. + * Apply func to every entry in the database. + * Returns the sum of values returned by func. + * @param dbi Interface of the database + * @param func Function to be applyed + * @param ... Extra arguments for func + * @return Sum of the values returned by func + * @protected + * @see #DBInterface + * @see #DBApply(DBKey,void *,va_list) + * @see DBInterface#vforeach(DBInterface,DBApply,va_list) + * @see common\db.c#db_foreach(DBInterface,DBApply,...) + */ + int (*foreach)(struct dbt *dbi, DBApply func, ...); + + /** + * Apply func to every entry in the database. + * Returns the sum of values returned by func. + * @param dbi Interface of the database + * @param func Function to be applyed + * @param args Extra arguments for func + * @return Sum of the values returned by func + * @protected + * @see #DBApply(DBKey,void *,va_list) + * @see #DBInterface + * @see DBInterface#foreach(DBInterface,DBApply,...) + * @see common\db.c#db_vforeach(DBInterface,DBApply,va_list) + */ + int (*vforeach)(struct dbt *dbi, DBApply func, va_list args); + + /** + * Just calls {@link DBInterface#vclear(DBInterface,DBApply,va_list)}. + * Removes all entries from the database. + * Before deleting an entry, func is applyed to it. + * Releases the key and the data. + * Returns the sum of values returned by func, if it exists. + * @param dbi Interface of the database + * @param func Function to be applyed to every entry before deleting + * @param ... Extra arguments for func + * @return Sum of values returned by func + * @protected + * @see #DBApply(DBKey,void *,va_list) + * @see #DBInterface + * @see DBInterface#vclear(DBInterface,DBApply,va_list) + * @see common\db.c#db_clear(DBInterface,DBApply,...) + */ + int (*clear)(struct dbt *dbi, DBApply func, ...); + + /** + * Removes all entries from the database. + * Before deleting an entry, func is applyed to it. + * Releases the key and the data. + * Returns the sum of values returned by func, if it exists. + * @param dbi Interface of the database + * @param func Function to be applyed to every entry before deleting + * @param args Extra arguments for func + * @return Sum of values returned by func + * @protected + * @see #DBApply(DBKey,void *,va_list) + * @see #DBInterface + * @see DBInterface#clear(DBInterface,DBApply,...) + * @see common\db.c#vclear(DBInterface,DBApply,va_list) + */ + int (*vclear)(struct dbt *dbi, DBApply func, va_list args); + + /** + * Just calls {@link DBInterface#vdestroy(DBInterface,DBApply,va_list)}. + * Finalize the database, feeing all the memory it uses. + * Before deleting an entry, func is applyed to it. + * Releases the key and the data. + * Returns the sum of values returned by func, if it exists. + * NOTE: This locks the database globally. Any attempt to insert or remove + * a database entry will give an error and be aborted (except for clearing). + * @param dbi Interface of the database + * @param func Function to be applyed to every entry before deleting + * @param ... Extra arguments for func + * @return Sum of values returned by func + * @protected + * @see #DBApply(DBKey,void *,va_list) + * @see #DBInterface + * @see DBInterface#vdestroy(DBInterface,DBApply,va_list) + * @see common\db.c#db_destroy(DBInterface,DBApply,...) + */ + int (*destroy)(struct dbt *dbi, DBApply func, ...); + + /** + * Finalize the database, feeing all the memory it uses. + * Before deleting an entry, func is applyed to it. + * Returns the sum of values returned by func, if it exists. + * NOTE: This locks the database globally. Any attempt to insert or remove + * a database entry will give an error and be aborted (except for clearing). + * @param dbi Interface of the database + * @param func Function to be applyed to every entry before deleting + * @param args Extra arguments for func + * @return Sum of values returned by func + * @protected + * @see #DBInterface + * @see #DBApply(DBKey,void *,va_list) + * @see DBInterface#destroy(DBInterface,DBApply,...) + * @see common\db.c#db_vdestroy(DBInterface,DBApply,va_list) + */ + int (*vdestroy)(struct dbt *dbi, DBApply func, va_list args); + + /** + * Return the size of the database (number of items in the database). + * @param dbi Interface of the database + * @return Size of the database + * @protected + * @see #DBInterface + * @see common\db.c#db_size(DBInterface) + */ + unsigned int (*size)(struct dbt *dbi); + + /** + * Return the type of the database. + * @param dbi Interface of the database + * @return Type of the database + * @protected + * @see #DBType + * @see #DBInterface + * @see common\db.c#db_type(DBInterface) + */ + DBType (*type)(struct dbt *dbi); + + /** + * Return the options of the database. + * @param dbi Interface of the database + * @return Options of the database + * @protected + * @see #DBOptions + * @see #DBInterface + * @see common\db.c#db_options(DBInterface) + */ + DBOptions (*options)(struct dbt *dbi); + +} *DBInterface; + +//For easy access to the common functions. +#ifdef DB_MANUAL_CAST_TO_UNION +# define i2key db_i2key +# define ui2key db_ui2key +# define str2key db_str2key +#else /* not DB_MANUAL_CAST_TO_UNION */ +# define i2key(k) ((DBKey)(int)(k)) +# define ui2key(k) ((DBKey)(unsigned int)(k)) +# define str2key(k) ((DBKey)(unsigned char *)(k)) +#endif /* DB_MANUAL_CAST_TO_UNION / not DB_MANUAL_CAST_TO_UNION */ + +#define db_get(db,k) (db)->get((db),(k)) +#define idb_get(db,k) (db)->get((db),i2key(k)) +#define uidb_get(db,k) (db)->get((db),ui2key(k)) +#define strdb_get(db,k) (db)->get((db),str2key(k)) + +#define db_put(db,k,d) (db)->put((db),(k),(d)) +#define idb_put(db,k,d) (db)->put((db),i2key(k),(d)) +#define uidb_put(db,k,d) (db)->put((db),ui2key(k),(d)) +#define strdb_put(db,k,d) (db)->put((db),str2key(k),(d)) + +#define db_remove(db,k) (db)->remove((db),(k)) +#define idb_remove(db,k) (db)->remove((db),i2key(k)) +#define uidb_remove(db,k) (db)->remove((db),ui2key(k)) +#define strdb_remove(db,k) (db)->remove((db),str2key(k)) + +//These are discarding the possible vargs you could send to the function, so those +//that require vargs must not use these defines. +#define db_ensure(db,k,f) (db)->ensure((db),(k),f) +#define idb_ensure(db,k,f) (db)->ensure((db),i2key(k),f) +#define uidb_ensure(db,k,f) (db)->ensure((db),ui2key(k),f) +#define strdb_ensure(db,k,f) (db)->ensure((db),str2key(k),f) + +/*****************************************************************************\ + * (2) Section with public functions. * + * db_fix_options - Fix the options for a type of database. * + * db_default_cmp - Get the default comparator for a type of database. * + * db_default_hash - Get the default hasher for a type of database. * + * db_default_release - Get the default releaser for a type of database * + * with the fixed options. * + * db_custom_release - Get the releaser that behaves as specified. * + * db_alloc - Allocate a new database. * + * db_i2key - Manual cast from 'int' to 'DBKey'. * + * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. * + * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. * + * db_init - Initialise the database system. * + * db_final - Finalise the database system. * +\*****************************************************************************/ + +/** + * Returns the fixed options according to the database type. + * Sets required options and unsets unsupported options. + * For numeric databases DB_OPT_DUP_KEY and DB_OPT_RELEASE_KEY are unset. + * @param type Type of the database + * @param options Original options of the database + * @return Fixed options of the database + * @private + * @see #DBType + * @see #DBOptions + * @see #db_default_release(DBType,DBOptions) + * @see common\db.c#db_fix_options(DBType,DBOptions) + */ +DBOptions db_fix_options(DBType type, DBOptions options); + +/** + * Returns the default comparator for the type of database. + * @param type Type of database + * @return Comparator for the type of database or NULL if unknown database + * @public + * @see #DBType + * @see #DBComparator + * @see common\db.c#db_default_cmp(DBType) + */ +DBComparator db_default_cmp(DBType type); + +/** + * Returns the default hasher for the specified type of database. + * @param type Type of database + * @return Hasher of the type of database or NULL if unknown database + * @public + * @see #DBType + * @see #DBHasher + * @see common\db.c#db_default_hash(DBType) + */ +DBHasher db_default_hash(DBType type); + +/** + * Returns the default releaser for the specified type of database with the + * specified options. + * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)} + * before choosing the releaser + * @param type Type of database + * @param options Options of the database + * @return Default releaser for the type of database with the fixed options + * @public + * @see #DBType + * @see #DBOptions + * @see #DBReleaser + * @see #db_fix_options(DBType,DBOptions) + * @see #db_custom_release(DBRelease) + * @see common\db.c#db_default_release(DBType,DBOptions) + */ +DBReleaser db_default_release(DBType type, DBOptions options); + +/** + * Returns the releaser that behaves as which specifies. + * @param which Defines what the releaser releases + * @return Releaser for the specified release options + * @public + * @see #DBRelease + * @see #DBReleaser + * @see #db_default_release(DBType,DBOptions) + * @see common\db.c#db_custom_release(DBRelease) + */ +DBReleaser db_custom_release(DBRelease which); + +/** + * Allocate a new database of the specified type. + * It uses the default comparator, hasher and releaser of the specified + * database type and fixed options. + * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)} + * before creating the database. + * @param file File where the database is being allocated + * @param line Line of the file where the database is being allocated + * @param type Type of database + * @param options Options of the database + * @param maxlen Maximum length of the string to be used as key in string + * databases + * @return The interface of the database + * @public + * @see #DBType + * @see #DBInterface + * @see #db_default_cmp(DBType) + * @see #db_default_hash(DBType) + * @see #db_default_release(DBType,DBOptions) + * @see #db_fix_options(DBType,DBOptions) + * @see common\db.c#db_alloc(const char *,int,DBType,DBOptions,unsigned short) + */ +DBInterface db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen); + +#ifdef DB_MANUAL_CAST_TO_UNION +/** + * Manual cast from 'int' to the union DBKey. + * Created for compilers that don't support casting to unions. + * @param key Key to be casted + * @return The key as a DBKey union + * @public + * @see #DB_MANUAL_CAST_TO_UNION + * @see #db_ui2key(unsigned int) + * @see #db_str2key(unsigned char *) + * @see common\db.c#db_i2key(int) + */ +DBKey db_i2key(int key); + +/** + * Manual cast from 'unsigned int' to the union DBKey. + * Created for compilers that don't support casting to unions. + * @param key Key to be casted + * @return The key as a DBKey union + * @public + * @see #DB_MANUAL_CAST_TO_UNION + * @see #db_i2key(int) + * @see #db_str2key(unsigned char *) + * @see common\db.c#db_ui2key(unsigned int) + */ +DBKey db_ui2key(unsigned int key); + +/** + * Manual cast from 'unsigned char *' to the union DBKey. + * Created for compilers that don't support casting to unions. + * @param key Key to be casted + * @return The key as a DBKey union + * @public + * @see #DB_MANUAL_CAST_TO_UNION + * @see #db_i2key(int) + * @see #db_ui2key(unsigned int) + * @see common\db.c#db_str2key(unsigned char *) + */ +DBKey db_str2key(unsigned char *key); +#endif /* DB_MANUAL_CAST_TO_UNION */ + +/** + * Initialize the database system. + * @public + * @see #db_final(void) + * @see common\db.c#db_init(void) + */ +void db_init(void); + +/** + * Finalize the database system. + * Frees the memory used by the block reusage system. + * @public + * @see #db_init(void) + * @see common\db.c#db_final(void) + */ +void db_final(void); + +// Link DB System - From jAthena +struct linkdb_node { + struct linkdb_node *next; + struct linkdb_node *prev; + void *key; + void *data; +}; + +void linkdb_insert ( struct linkdb_node** head, void *key, void* data); // 重複を考慮しない +void linkdb_replace( struct linkdb_node** head, void *key, void* data); // 重複を考慮する +void* linkdb_search ( struct linkdb_node** head, void *key); +void* linkdb_erase ( struct linkdb_node** head, void *key); +void linkdb_final ( struct linkdb_node** head ); + +#endif diff --git a/src/common/ers.h b/src/common/ers.h index a512f6365..9b6b4b62d 100644 --- a/src/common/ers.h +++ b/src/common/ers.h @@ -1,193 +1,193 @@ -/*****************************************************************************\ - * Copyright (c) Athena Dev Teams - Licensed under GNU GPL * - * For more information, see LICENCE in the main folder * - * * - *

Entry Reusage System

* - * * - * There are several root entry managers, each with a different entry size. * - * Each manager will keep track of how many instances have been 'created'. * - * They will only automatically destroy themselves after the last instance * - * is destroyed. * - * * - * Entries can be allocated from the managers. * - * If it has reusable entries (freed entry), it uses one. * - * So no assumption should be made about the data of the entry. * - * Entries should be freed in the manager they where allocated from. * - * Failure to do so can lead to unexpected behaviours. * - * * - *

Advantages:

* - * - The same manager is used for entries of the same size. * - * So entries freed in one instance of the manager can be used by other * - * instances of the manager. * - * - Much less memory allocation/deallocation - program will be faster. * - * - Avoids memory fragmentaion - program will run better for longer. * - * * - *

Disavantages:

* - * - Unused entries are almost inevitable - memory being wasted. * - * - A manager will only auto-destroy when all of its instances are * - * destroyed so memory will usually only be recovered near the end. * - * - Always wastes space for entries smaller than a pointer. * - * * - * WARNING: The system is not thread-safe at the moment. * - * * - * HISTORY: * - * 0.1 - Initial version * - * * - * @version 0.1 - Initial version * - * @author Flavio @ Amazon Project * - * @encoding US-ASCII * - * @see common#ers.c * -\*****************************************************************************/ -#ifndef _ERS_H_ -#define _ERS_H_ - -#include "../common/cbasetypes.h" - -/*****************************************************************************\ - * (1) All public parts of the Entry Reusage System. * - * DISABLE_ERS - Define to disable this system. * - * ERS_ALIGNED - Alignment of the entries in the blocks. * - * ERInterface - Interface of the entry manager. * - * ers_new - Allocate an instance of an entry manager. * - * ers_report - Print a report about the current state. * - * ers_force_destroy_all - Force the destruction of all the managers. * -\*****************************************************************************/ - -/** - * Define this to disable the Entry Reusage System. - * All code except the typedef of ERInterface will be disabled. - * To allow a smooth transition, - * @public - */ -//#define DISABLE_ERS - -/** - * Entries are aligned to ERS_ALIGNED bytes in the blocks of entries. - * By default it aligns to one byte, using the "natural order" of the entries. - * This should NEVER be set to zero or less. - * If greater than one, some memory can be wasted. This should never be needed - * but is here just in case some aligment issues arise. - * @public - * @see #ers_new(uint32) - */ -#ifndef ERS_ALIGNED -# define ERS_ALIGNED 1 -#endif /* not ERS_ALIGN_ENTRY */ - -/** - * Public interface of the entry manager. - * @param alloc Allocate an entry from this manager - * @param free Free an entry allocated from this manager - * @param entry_size Return the size of the entries of this manager - * @param destroy Destroy this instance of the manager - * @public - * @see #ers_new(uint32) - */ -typedef struct eri { - - /** - * Allocate an entry from this entry manager. - * If there are reusable entries available, it reuses one instead. - * @param self Interface of the entry manager - * @return An entry - * @protected - * @see #ERInterface - * @see ERInterface#free(ERInterface,void *) - */ - void *(*alloc)(struct eri *self); - - /** - * Free an entry allocated from this manager. - * WARNING: Does not check if the entry was allocated by this manager. - * Freeing such an entry can lead to unexpected behaviour. - * @param self Interface of the entry manager - * @param entry Entry to be freed - * @protected - * @see #ERInterface - * @see ERInterface#alloc(ERInterface) - */ - void (*free)(struct eri *self, void *entry); - - /** - * Return the size of the entries allocated from this manager. - * @param self Interface of the entry manager - * @return Size of the entries of this manager in bytes - * @protected - * @see #ERInterface - */ - uint32 (*entry_size)(struct eri *self); - - /** - * Destroy this instance of the manager. - * The manager is actually only destroyed when all the instances are destroyed. - * When destroying the manager a warning is shown if the manager has - * missing/extra entries. - * @param self Interface of the entry manager - * @protected - * @see #ERInterface - * @see #ers_new(uint32) - */ - void (*destroy)(struct eri *self); - -} *ERInterface; - -#ifdef DISABLE_ERS -// Use memory manager to allocate/free and disable other interface functions -# define ers_alloc(obj,type) (type *)aMalloc(sizeof(type)) -# define ers_free(obj,entry) aFree(entry) -# define ers_entry_size(obj) (uint32)0 -# define ers_destroy(obj) -// Disable the public functions -# define ers_new(size) NULL -# define ers_report() -# define ers_force_destroy_all() -#else /* not DISABLE_ERS */ -// These defines should be used to allow the code to keep working whenever -// the system is disabled -# define ers_alloc(obj,type) (type *)(obj)->alloc(obj) -# define ers_free(obj,entry) (obj)->free((obj),(entry)) -# define ers_entry_size(obj) (obj)->entry_size(obj) -# define ers_destroy(obj) (obj)->destroy(obj) - -/** - * Get a new instance of the manager that handles the specified entry size. - * Size has to greater than 0. - * If the specified size is smaller than a pointer, the size of a pointer is - * used instead. - * It's also aligned to ERS_ALIGNED bytes, so the smallest multiple of - * ERS_ALIGNED that is greater or equal to size is what's actually used. - * @param The requested size of the entry in bytes - * @return Interface of the object - * @public - * @see #ERS_ALIGNED - * @see #ERInterface - * @see ERInterface#destroy(ERInterface) - * @see common\ers.c#ers_new(uint32) - */ -ERInterface ers_new(uint32 size); - -/** - * Print a report about the current state of the Entry Reusage System. - * Shows information about the global system and each entry manager. - * The number of entries are checked and a warning is shown if extra reusable - * entries are found. - * The extra entries are included in the count of reusable entries. - * @public - * @see common\ers.c#ers_report(void) - */ -void ers_report(void); - -/** - * Forcibly destroy all the entry managers, checking for nothing. - * The system is left as if no instances or entries had ever been allocated. - * All previous entries and instances of the managers become invalid. - * The use of this is NOT recommended. - * It should only be used in extreme situations to make shure all the memory - * allocated by this system is released. - * @public - * @see common\ers.c#ers_force_destroy_all(void) - */ -void ers_force_destroy_all(void); -#endif /* DISABLE_ERS / not DISABLE_ERS */ - -#endif /* _ERS_H_ */ +/*****************************************************************************\ + * Copyright (c) Athena Dev Teams - Licensed under GNU GPL * + * For more information, see LICENCE in the main folder * + * * + *

Entry Reusage System

* + * * + * There are several root entry managers, each with a different entry size. * + * Each manager will keep track of how many instances have been 'created'. * + * They will only automatically destroy themselves after the last instance * + * is destroyed. * + * * + * Entries can be allocated from the managers. * + * If it has reusable entries (freed entry), it uses one. * + * So no assumption should be made about the data of the entry. * + * Entries should be freed in the manager they where allocated from. * + * Failure to do so can lead to unexpected behaviours. * + * * + *

Advantages:

* + * - The same manager is used for entries of the same size. * + * So entries freed in one instance of the manager can be used by other * + * instances of the manager. * + * - Much less memory allocation/deallocation - program will be faster. * + * - Avoids memory fragmentaion - program will run better for longer. * + * * + *

Disavantages:

* + * - Unused entries are almost inevitable - memory being wasted. * + * - A manager will only auto-destroy when all of its instances are * + * destroyed so memory will usually only be recovered near the end. * + * - Always wastes space for entries smaller than a pointer. * + * * + * WARNING: The system is not thread-safe at the moment. * + * * + * HISTORY: * + * 0.1 - Initial version * + * * + * @version 0.1 - Initial version * + * @author Flavio @ Amazon Project * + * @encoding US-ASCII * + * @see common#ers.c * +\*****************************************************************************/ +#ifndef _ERS_H_ +#define _ERS_H_ + +#include "../common/cbasetypes.h" + +/*****************************************************************************\ + * (1) All public parts of the Entry Reusage System. * + * DISABLE_ERS - Define to disable this system. * + * ERS_ALIGNED - Alignment of the entries in the blocks. * + * ERInterface - Interface of the entry manager. * + * ers_new - Allocate an instance of an entry manager. * + * ers_report - Print a report about the current state. * + * ers_force_destroy_all - Force the destruction of all the managers. * +\*****************************************************************************/ + +/** + * Define this to disable the Entry Reusage System. + * All code except the typedef of ERInterface will be disabled. + * To allow a smooth transition, + * @public + */ +//#define DISABLE_ERS + +/** + * Entries are aligned to ERS_ALIGNED bytes in the blocks of entries. + * By default it aligns to one byte, using the "natural order" of the entries. + * This should NEVER be set to zero or less. + * If greater than one, some memory can be wasted. This should never be needed + * but is here just in case some aligment issues arise. + * @public + * @see #ers_new(uint32) + */ +#ifndef ERS_ALIGNED +# define ERS_ALIGNED 1 +#endif /* not ERS_ALIGN_ENTRY */ + +/** + * Public interface of the entry manager. + * @param alloc Allocate an entry from this manager + * @param free Free an entry allocated from this manager + * @param entry_size Return the size of the entries of this manager + * @param destroy Destroy this instance of the manager + * @public + * @see #ers_new(uint32) + */ +typedef struct eri { + + /** + * Allocate an entry from this entry manager. + * If there are reusable entries available, it reuses one instead. + * @param self Interface of the entry manager + * @return An entry + * @protected + * @see #ERInterface + * @see ERInterface#free(ERInterface,void *) + */ + void *(*alloc)(struct eri *self); + + /** + * Free an entry allocated from this manager. + * WARNING: Does not check if the entry was allocated by this manager. + * Freeing such an entry can lead to unexpected behaviour. + * @param self Interface of the entry manager + * @param entry Entry to be freed + * @protected + * @see #ERInterface + * @see ERInterface#alloc(ERInterface) + */ + void (*free)(struct eri *self, void *entry); + + /** + * Return the size of the entries allocated from this manager. + * @param self Interface of the entry manager + * @return Size of the entries of this manager in bytes + * @protected + * @see #ERInterface + */ + uint32 (*entry_size)(struct eri *self); + + /** + * Destroy this instance of the manager. + * The manager is actually only destroyed when all the instances are destroyed. + * When destroying the manager a warning is shown if the manager has + * missing/extra entries. + * @param self Interface of the entry manager + * @protected + * @see #ERInterface + * @see #ers_new(uint32) + */ + void (*destroy)(struct eri *self); + +} *ERInterface; + +#ifdef DISABLE_ERS +// Use memory manager to allocate/free and disable other interface functions +# define ers_alloc(obj,type) (type *)aMalloc(sizeof(type)) +# define ers_free(obj,entry) aFree(entry) +# define ers_entry_size(obj) (uint32)0 +# define ers_destroy(obj) +// Disable the public functions +# define ers_new(size) NULL +# define ers_report() +# define ers_force_destroy_all() +#else /* not DISABLE_ERS */ +// These defines should be used to allow the code to keep working whenever +// the system is disabled +# define ers_alloc(obj,type) (type *)(obj)->alloc(obj) +# define ers_free(obj,entry) (obj)->free((obj),(entry)) +# define ers_entry_size(obj) (obj)->entry_size(obj) +# define ers_destroy(obj) (obj)->destroy(obj) + +/** + * Get a new instance of the manager that handles the specified entry size. + * Size has to greater than 0. + * If the specified size is smaller than a pointer, the size of a pointer is + * used instead. + * It's also aligned to ERS_ALIGNED bytes, so the smallest multiple of + * ERS_ALIGNED that is greater or equal to size is what's actually used. + * @param The requested size of the entry in bytes + * @return Interface of the object + * @public + * @see #ERS_ALIGNED + * @see #ERInterface + * @see ERInterface#destroy(ERInterface) + * @see common\ers.c#ers_new(uint32) + */ +ERInterface ers_new(uint32 size); + +/** + * Print a report about the current state of the Entry Reusage System. + * Shows information about the global system and each entry manager. + * The number of entries are checked and a warning is shown if extra reusable + * entries are found. + * The extra entries are included in the count of reusable entries. + * @public + * @see common\ers.c#ers_report(void) + */ +void ers_report(void); + +/** + * Forcibly destroy all the entry managers, checking for nothing. + * The system is left as if no instances or entries had ever been allocated. + * All previous entries and instances of the managers become invalid. + * The use of this is NOT recommended. + * It should only be used in extreme situations to make shure all the memory + * allocated by this system is released. + * @public + * @see common\ers.c#ers_force_destroy_all(void) + */ +void ers_force_destroy_all(void); +#endif /* DISABLE_ERS / not DISABLE_ERS */ + +#endif /* _ERS_H_ */ diff --git a/src/common/graph.c b/src/common/graph.c index 3602f511f..e56783816 100644 --- a/src/common/graph.c +++ b/src/common/graph.c @@ -1,318 +1,318 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -// graph creation is enabled -// #define ENABLE_GRAPH - -#ifdef ENABLE_GRAPH - -#include -#include -#include -#ifndef _WIN32 - #include -#endif -#ifdef MINGW - #include -#endif - -#include "../common/core.h" -#include "../common/timer.h" -#include "../common/grfio.h" -#include "../common/malloc.h" -#include "graph.h" - -struct graph { - int width; - int height; - int pallet_count; - int png_len; - int png_dirty; - unsigned char* raw_data; - unsigned char* png_data; - int * graph_value; - int graph_max; -}; - -void graph_write_dword(unsigned char* p,unsigned int v) { - p[0] = (unsigned char)((v >> 24) & 0xFF); - p[1] = (unsigned char)((v >> 16) & 0xFF); - p[2] = (unsigned char)((v >> 8) & 0xFF); - p[3] = (unsigned char)(v & 0xFF); -} - -struct graph* graph_create(unsigned int x,unsigned int y) { - struct graph *g = (struct graph*)aCalloc(sizeof(struct graph),1); - if(g == NULL) return NULL; - // 256 * 3 : パレットデータ - // x * y * 2 : イメージのバッファ - // 256 : チャンクデータなどの予備 - g->png_data = (unsigned char *) aMalloc(4 * 256 + (x + 1) * y * 2); - g->raw_data = (unsigned char *) aCalloc( (x + 1) * y , 1); - memcpy( - g->png_data, - "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52" - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x03\x00\x00\x00\xFF\xFF\xFF" - "\xFF\x00\x00\x00\x03\x50\x4C\x54\x45\xFF\xFF\xFF\xA7\xC4\x1B\xC8",0x30 - ); - graph_write_dword(g->png_data + 0x10,x); - graph_write_dword(g->png_data + 0x14,y); - graph_write_dword(g->png_data + 0x1D,grfio_crc32(g->png_data+0x0C,0x11)); - g->pallet_count = 1; - g->width = x; - g->height = y; - g->png_dirty = 1; - g->graph_value = (int *) aCalloc(x,sizeof(int)); - g->graph_max = 1; - return g; -} - -void graph_pallet(struct graph* g, int index,unsigned long c) { - if(g == NULL || c >= 256) return; - - if(g->pallet_count <= index) { - malloc_set(g->png_data + 0x29 + 3 * g->pallet_count,0,(index - g->pallet_count) * 3); - g->pallet_count = index + 1; - } - g->png_data[0x29 + index * 3 ] = (unsigned char)((c >> 16) & 0xFF); // R - g->png_data[0x29 + index * 3 + 1] = (unsigned char)((c >> 8) & 0xFF); // G - g->png_data[0x29 + index * 3 + 2] = (unsigned char)( c & 0xFF); // B - graph_write_dword(g->png_data + 0x21,g->pallet_count * 3); - graph_write_dword( - g->png_data + 0x29 + g->pallet_count * 3, - grfio_crc32(g->png_data + 0x25,g->pallet_count * 3 + 4) - ); - g->png_dirty = 1; -} - -void graph_setpixel(struct graph* g,int x,int y,int color) { - if(g == NULL || color >= 256) { return; } - if(x < 0) x = 0; - if(y < 0) y = 0; - if(x >= g->width) { x = g->width - 1; } - if(y >= g->height) { y = g->height - 1; } - if(color >= g->pallet_count) { graph_pallet(g,color,graph_rgb(0,0,0)); } - - g->raw_data[y * (g->width + 1) + x + 1] = (unsigned char)color; - g->png_dirty = 1; -} - -int graph_getpixel(struct graph* g,int x,int y) { - if(x < 0) x = 0; - if(y < 0) y = 0; - if(x >= g->width) { x = g->width - 1; } - if(y >= g->height) { y = g->height - 1; } - return g->raw_data[y * (g->width + 1) + x + 1]; -} - -const unsigned char* graph_output(struct graph* g,int *len) { - unsigned long inflate_len; - unsigned char *p; - - if(g == NULL) return NULL; - if(g->png_dirty == 0) { - *len = g->png_len; - return g->png_data; - } - - p = g->png_data + 0x2D + 3 * g->pallet_count; - inflate_len = 2 * (g->width + 1) * g->height; - memcpy(p + 4,"IDAT",4); - encode_zip(p + 8,&inflate_len,g->raw_data,(g->width + 1) * g->height); - graph_write_dword(p,inflate_len); - graph_write_dword(p + 8 + inflate_len,grfio_crc32(p + 4, inflate_len + 4)); - - p += 0x0C + inflate_len; - memcpy(p,"\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82",0x0C); - p += 0x0C; - g->png_len = p - g->png_data; - g->png_dirty = 0; - *len = g->png_len; - return g->png_data; -} - -void graph_free(struct graph* g) { - if(g != NULL) { - aFree(g->png_data); - aFree(g->raw_data); - aFree(g->graph_value); - aFree(g); - } -} - -// とりあえず不効率版。後ほど書き直し予定 -void graph_square(struct graph* g,int x,int y,int xe,int ye,int color) { - int i,j; - if(g == NULL) return; - if(x < 0) { x = 0; } - if(y < 0) { y = 0; } - if(xe > g->width) { xe = g->width; } - if(ye > g->height) { ye = g->height; } - for(i = y;i < ye ; i++) { - for(j = x; j < xe ; j++) { - graph_setpixel(g,j,i,color); - } - } -} - -// とりあえず不効率版。後ほど書き直し予定 -void graph_scroll(struct graph* g,int n,int color) { - int x,y; - if(g == NULL) return; - for(y = 0; y < g->height; y++) { - for(x = 0; x < g->width - n; x++) { - graph_setpixel(g,x,y,graph_getpixel(g,x + n,y)); - } - for( ; x < g->width; x++) { - graph_setpixel(g,x,y,color); - } - } -} - -void graph_data(struct graph* g,int value) { - int i, j, start; - if(g == NULL) return; - memmove(&g->graph_value[0],&g->graph_value[1],sizeof(int) * (g->width - 1)); - g->graph_value[g->width - 1] = value; - if(value > g->graph_max) { - // 最大値を超えたので再描画 - g->graph_max = value; - graph_square(g,0,0,g->width,g->height,0); - start = 0; - } else { - // スクロールしてポイント打つ - graph_scroll(g,1,0); - start = g->width - 1; - } - for(i = start; i < g->width; i++) { - int h0 = (i == 0 ? 0 : g->graph_value[i - 1]) * g->height / g->graph_max; - int h1 = (g->graph_value[i] ) * g->height / g->graph_max; - int h2 = (h0 < h1 ? 1 : -1); - for(j = h0; j != h1; j += h2) { - graph_setpixel(g,i,g->height - 1 - j,1); - } - graph_setpixel(g,i,g->height - 1 - h1,1); - } -} - -// 上の関数群を利用して、自動的にグラフを作成するタイマー群 - -#define GRP_WIDTH 300 // グラフの幅 -#define GRP_HEIGHT 200 // グラフの高さ -#define GRP_COLOR graph_rgb(0,0,255) // グラフの色 -#define GRP_INTERVEL 60*1000 // グラフの更新間隔 - -#define GRP_PATH "httpd/" - -struct graph_sensor { - struct graph* graph; - char* str; - char hash[32]; - int scanid; - int drawid; - int interval; - unsigned int (*func)(void); -}; - -static struct graph_sensor *sensor; -static int sensor_max; - -static int graph_scan_timer(int tid,unsigned int tick,int id,int data) -{ - if(id >= 0 && id < sensor_max) - graph_data(sensor[id].graph,sensor[id].func()); - return 0; -} - -// modified by Celest -- i'm trying to separate it from httpd if possible ^^; -static int graph_draw_timer(int tid,unsigned int tick,int id,int data) -{ - char png_file[24]; - FILE *fp; - - // create/update the png file - do { - const char *png_data; - int len; - sprintf (png_file, GRP_PATH"%s.png", sensor[id].hash); - fp = fopen(png_file, "w"); - // if another png of the same hash exists - // (i.e 2nd login server with the same sensors) - // this will fail = not good >.< - if (fp == NULL) - break; - png_data = graph_output(sensor[id].graph, &len); - fwrite(png_data,1,len,fp); - fclose(fp); - } while (0); - - // create/update text snippet - do { - char buf[8192], *p; - p = buf; - sprintf (png_file, GRP_PATH"%s.graph", sensor[id].hash); - fp = fopen(png_file, "w"); - if (fp == NULL) - break; - p += sprintf(p,"

%s

\n\n", - sensor[id].str); - p += sprintf(p,"

\n", - sensor[id].hash, GRP_WIDTH,GRP_HEIGHT); - p += sprintf(p,"

Max: %d, Interval: %d sec

\n\n", - sensor[id].graph->graph_max, sensor[id].interval / 1000); - fprintf(fp, buf); - fclose(fp); - } while (0); - - return 0; -} - -void graph_add_sensor(const unsigned char* string, int interval, unsigned int (*callback_func)(void)) -{ - int draw_interval = interval * 2; - struct graph *g = graph_create(GRP_WIDTH,GRP_HEIGHT); - graph_pallet(g,1,GRP_COLOR); - - sensor = (struct graph_sensor *) aRealloc(sensor, sizeof(struct graph_sensor) * (sensor_max + 1)); - sensor[sensor_max].graph = g; - sensor[sensor_max].str = aStrdup(string); - // create crc32 hash of the sensor's name - sprintf (sensor[sensor_max].hash, "%lu%c", grfio_crc32(string,strlen(string)), 'a' + SERVER_TYPE); - sensor[sensor_max].func = callback_func; - sensor[sensor_max].scanid = add_timer_interval(gettick() + 500, graph_scan_timer, sensor_max, 0, interval); - sensor[sensor_max].drawid = add_timer_interval(gettick() + 1000, graph_draw_timer, sensor_max, 0, draw_interval < 60000 ? 60000 : draw_interval); - sensor[sensor_max].interval = interval; - sensor_max++; - -} - -void graph_final (void) -{ - int i; - for(i = 0; i < sensor_max; i++) { - char png_file[24]; - // remove the png and snippet file - sprintf (png_file, GRP_PATH"%s.png", sensor[i].hash); - unlink (png_file); - sprintf (png_file, GRP_PATH"%s.graph", sensor[i].hash); - unlink (png_file); - graph_free(sensor[i].graph); - aFree(sensor[i].str); - //delete_timer(sensor[i].scanid,graph_scan_timer); - //delete_timer(sensor[i].drawid,graph_draw_timer); - } - aFree(sensor); - sensor_max = 0; -} - -void graph_init (void) -{ - graph_add_sensor ("Memory Usage", 1000, malloc_usage); - add_timer_func_list(graph_scan_timer, "graph_scan_timer"); - add_timer_func_list(graph_draw_timer, "graph_draw_timer"); -} - -#else -void graph_init (void) {} -void graph_final (void) {} -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +// graph creation is enabled +// #define ENABLE_GRAPH + +#ifdef ENABLE_GRAPH + +#include +#include +#include +#ifndef _WIN32 + #include +#endif +#ifdef MINGW + #include +#endif + +#include "../common/core.h" +#include "../common/timer.h" +#include "../common/grfio.h" +#include "../common/malloc.h" +#include "graph.h" + +struct graph { + int width; + int height; + int pallet_count; + int png_len; + int png_dirty; + unsigned char* raw_data; + unsigned char* png_data; + int * graph_value; + int graph_max; +}; + +void graph_write_dword(unsigned char* p,unsigned int v) { + p[0] = (unsigned char)((v >> 24) & 0xFF); + p[1] = (unsigned char)((v >> 16) & 0xFF); + p[2] = (unsigned char)((v >> 8) & 0xFF); + p[3] = (unsigned char)(v & 0xFF); +} + +struct graph* graph_create(unsigned int x,unsigned int y) { + struct graph *g = (struct graph*)aCalloc(sizeof(struct graph),1); + if(g == NULL) return NULL; + // 256 * 3 : パレットデータ + // x * y * 2 : イメージのバッファ + // 256 : チャンクデータなどの予備 + g->png_data = (unsigned char *) aMalloc(4 * 256 + (x + 1) * y * 2); + g->raw_data = (unsigned char *) aCalloc( (x + 1) * y , 1); + memcpy( + g->png_data, + "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x03\x00\x00\x00\xFF\xFF\xFF" + "\xFF\x00\x00\x00\x03\x50\x4C\x54\x45\xFF\xFF\xFF\xA7\xC4\x1B\xC8",0x30 + ); + graph_write_dword(g->png_data + 0x10,x); + graph_write_dword(g->png_data + 0x14,y); + graph_write_dword(g->png_data + 0x1D,grfio_crc32(g->png_data+0x0C,0x11)); + g->pallet_count = 1; + g->width = x; + g->height = y; + g->png_dirty = 1; + g->graph_value = (int *) aCalloc(x,sizeof(int)); + g->graph_max = 1; + return g; +} + +void graph_pallet(struct graph* g, int index,unsigned long c) { + if(g == NULL || c >= 256) return; + + if(g->pallet_count <= index) { + malloc_set(g->png_data + 0x29 + 3 * g->pallet_count,0,(index - g->pallet_count) * 3); + g->pallet_count = index + 1; + } + g->png_data[0x29 + index * 3 ] = (unsigned char)((c >> 16) & 0xFF); // R + g->png_data[0x29 + index * 3 + 1] = (unsigned char)((c >> 8) & 0xFF); // G + g->png_data[0x29 + index * 3 + 2] = (unsigned char)( c & 0xFF); // B + graph_write_dword(g->png_data + 0x21,g->pallet_count * 3); + graph_write_dword( + g->png_data + 0x29 + g->pallet_count * 3, + grfio_crc32(g->png_data + 0x25,g->pallet_count * 3 + 4) + ); + g->png_dirty = 1; +} + +void graph_setpixel(struct graph* g,int x,int y,int color) { + if(g == NULL || color >= 256) { return; } + if(x < 0) x = 0; + if(y < 0) y = 0; + if(x >= g->width) { x = g->width - 1; } + if(y >= g->height) { y = g->height - 1; } + if(color >= g->pallet_count) { graph_pallet(g,color,graph_rgb(0,0,0)); } + + g->raw_data[y * (g->width + 1) + x + 1] = (unsigned char)color; + g->png_dirty = 1; +} + +int graph_getpixel(struct graph* g,int x,int y) { + if(x < 0) x = 0; + if(y < 0) y = 0; + if(x >= g->width) { x = g->width - 1; } + if(y >= g->height) { y = g->height - 1; } + return g->raw_data[y * (g->width + 1) + x + 1]; +} + +const unsigned char* graph_output(struct graph* g,int *len) { + unsigned long inflate_len; + unsigned char *p; + + if(g == NULL) return NULL; + if(g->png_dirty == 0) { + *len = g->png_len; + return g->png_data; + } + + p = g->png_data + 0x2D + 3 * g->pallet_count; + inflate_len = 2 * (g->width + 1) * g->height; + memcpy(p + 4,"IDAT",4); + encode_zip(p + 8,&inflate_len,g->raw_data,(g->width + 1) * g->height); + graph_write_dword(p,inflate_len); + graph_write_dword(p + 8 + inflate_len,grfio_crc32(p + 4, inflate_len + 4)); + + p += 0x0C + inflate_len; + memcpy(p,"\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82",0x0C); + p += 0x0C; + g->png_len = p - g->png_data; + g->png_dirty = 0; + *len = g->png_len; + return g->png_data; +} + +void graph_free(struct graph* g) { + if(g != NULL) { + aFree(g->png_data); + aFree(g->raw_data); + aFree(g->graph_value); + aFree(g); + } +} + +// とりあえず不効率版。後ほど書き直し予定 +void graph_square(struct graph* g,int x,int y,int xe,int ye,int color) { + int i,j; + if(g == NULL) return; + if(x < 0) { x = 0; } + if(y < 0) { y = 0; } + if(xe > g->width) { xe = g->width; } + if(ye > g->height) { ye = g->height; } + for(i = y;i < ye ; i++) { + for(j = x; j < xe ; j++) { + graph_setpixel(g,j,i,color); + } + } +} + +// とりあえず不効率版。後ほど書き直し予定 +void graph_scroll(struct graph* g,int n,int color) { + int x,y; + if(g == NULL) return; + for(y = 0; y < g->height; y++) { + for(x = 0; x < g->width - n; x++) { + graph_setpixel(g,x,y,graph_getpixel(g,x + n,y)); + } + for( ; x < g->width; x++) { + graph_setpixel(g,x,y,color); + } + } +} + +void graph_data(struct graph* g,int value) { + int i, j, start; + if(g == NULL) return; + memmove(&g->graph_value[0],&g->graph_value[1],sizeof(int) * (g->width - 1)); + g->graph_value[g->width - 1] = value; + if(value > g->graph_max) { + // 最大値を超えたので再描画 + g->graph_max = value; + graph_square(g,0,0,g->width,g->height,0); + start = 0; + } else { + // スクロールしてポイント打つ + graph_scroll(g,1,0); + start = g->width - 1; + } + for(i = start; i < g->width; i++) { + int h0 = (i == 0 ? 0 : g->graph_value[i - 1]) * g->height / g->graph_max; + int h1 = (g->graph_value[i] ) * g->height / g->graph_max; + int h2 = (h0 < h1 ? 1 : -1); + for(j = h0; j != h1; j += h2) { + graph_setpixel(g,i,g->height - 1 - j,1); + } + graph_setpixel(g,i,g->height - 1 - h1,1); + } +} + +// 上の関数群を利用して、自動的にグラフを作成するタイマー群 + +#define GRP_WIDTH 300 // グラフの幅 +#define GRP_HEIGHT 200 // グラフの高さ +#define GRP_COLOR graph_rgb(0,0,255) // グラフの色 +#define GRP_INTERVEL 60*1000 // グラフの更新間隔 + +#define GRP_PATH "httpd/" + +struct graph_sensor { + struct graph* graph; + char* str; + char hash[32]; + int scanid; + int drawid; + int interval; + unsigned int (*func)(void); +}; + +static struct graph_sensor *sensor; +static int sensor_max; + +static int graph_scan_timer(int tid,unsigned int tick,int id,int data) +{ + if(id >= 0 && id < sensor_max) + graph_data(sensor[id].graph,sensor[id].func()); + return 0; +} + +// modified by Celest -- i'm trying to separate it from httpd if possible ^^; +static int graph_draw_timer(int tid,unsigned int tick,int id,int data) +{ + char png_file[24]; + FILE *fp; + + // create/update the png file + do { + const char *png_data; + int len; + sprintf (png_file, GRP_PATH"%s.png", sensor[id].hash); + fp = fopen(png_file, "w"); + // if another png of the same hash exists + // (i.e 2nd login server with the same sensors) + // this will fail = not good >.< + if (fp == NULL) + break; + png_data = graph_output(sensor[id].graph, &len); + fwrite(png_data,1,len,fp); + fclose(fp); + } while (0); + + // create/update text snippet + do { + char buf[8192], *p; + p = buf; + sprintf (png_file, GRP_PATH"%s.graph", sensor[id].hash); + fp = fopen(png_file, "w"); + if (fp == NULL) + break; + p += sprintf(p,"

%s

\n\n", + sensor[id].str); + p += sprintf(p,"

\n", + sensor[id].hash, GRP_WIDTH,GRP_HEIGHT); + p += sprintf(p,"

Max: %d, Interval: %d sec

\n\n", + sensor[id].graph->graph_max, sensor[id].interval / 1000); + fprintf(fp, buf); + fclose(fp); + } while (0); + + return 0; +} + +void graph_add_sensor(const unsigned char* string, int interval, unsigned int (*callback_func)(void)) +{ + int draw_interval = interval * 2; + struct graph *g = graph_create(GRP_WIDTH,GRP_HEIGHT); + graph_pallet(g,1,GRP_COLOR); + + sensor = (struct graph_sensor *) aRealloc(sensor, sizeof(struct graph_sensor) * (sensor_max + 1)); + sensor[sensor_max].graph = g; + sensor[sensor_max].str = aStrdup(string); + // create crc32 hash of the sensor's name + sprintf (sensor[sensor_max].hash, "%lu%c", grfio_crc32(string,strlen(string)), 'a' + SERVER_TYPE); + sensor[sensor_max].func = callback_func; + sensor[sensor_max].scanid = add_timer_interval(gettick() + 500, graph_scan_timer, sensor_max, 0, interval); + sensor[sensor_max].drawid = add_timer_interval(gettick() + 1000, graph_draw_timer, sensor_max, 0, draw_interval < 60000 ? 60000 : draw_interval); + sensor[sensor_max].interval = interval; + sensor_max++; + +} + +void graph_final (void) +{ + int i; + for(i = 0; i < sensor_max; i++) { + char png_file[24]; + // remove the png and snippet file + sprintf (png_file, GRP_PATH"%s.png", sensor[i].hash); + unlink (png_file); + sprintf (png_file, GRP_PATH"%s.graph", sensor[i].hash); + unlink (png_file); + graph_free(sensor[i].graph); + aFree(sensor[i].str); + //delete_timer(sensor[i].scanid,graph_scan_timer); + //delete_timer(sensor[i].drawid,graph_draw_timer); + } + aFree(sensor); + sensor_max = 0; +} + +void graph_init (void) +{ + graph_add_sensor ("Memory Usage", 1000, malloc_usage); + add_timer_func_list(graph_scan_timer, "graph_scan_timer"); + add_timer_func_list(graph_draw_timer, "graph_draw_timer"); +} + +#else +void graph_init (void) {} +void graph_final (void) {} +#endif diff --git a/src/common/graph.h b/src/common/graph.h index 6c80dd41c..9c8b73580 100644 --- a/src/common/graph.h +++ b/src/common/graph.h @@ -1,27 +1,27 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _GRAPH_H_ -#define _GRAPH_H_ - -void graph_init (void); -void graph_final (void); - -struct graph* graph_create(unsigned int x,unsigned int y); -void graph_pallet(struct graph* g, int index,unsigned long c); -const unsigned char* graph_output(struct graph* g,int *len); -void graph_setpixel(struct graph* g,int x,int y,int color); -void graph_scroll(struct graph* g,int n,int color); -void graph_square(struct graph* g,int x,int y,int xe,int ye,int color); - -// athenaの状態を調査するセンサーを追加する。 -// string : センサーの名称(Login Users など) -// inetrval : センサーの値を所得する間隔(msec) -// callback_func : センサーの値を返す関数( unsigned int login_users(void); など) - -void graph_add_sensor(const char* string, int interval, unsigned int (*callback_func)(void)); - -#define graph_rgb(r,g,b) (((r) << 16) | ((g) << 8) | (b)) - -#endif - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _GRAPH_H_ +#define _GRAPH_H_ + +void graph_init (void); +void graph_final (void); + +struct graph* graph_create(unsigned int x,unsigned int y); +void graph_pallet(struct graph* g, int index,unsigned long c); +const unsigned char* graph_output(struct graph* g,int *len); +void graph_setpixel(struct graph* g,int x,int y,int color); +void graph_scroll(struct graph* g,int n,int color); +void graph_square(struct graph* g,int x,int y,int xe,int ye,int color); + +// athenaの状態を調査するセンサーを追加する。 +// string : センサーの名称(Login Users など) +// inetrval : センサーの値を所得する間隔(msec) +// callback_func : センサーの値を返す関数( unsigned int login_users(void); など) + +void graph_add_sensor(const char* string, int interval, unsigned int (*callback_func)(void)); + +#define graph_rgb(r,g,b) (((r) << 16) | ((g) << 8) | (b)) + +#endif + diff --git a/src/common/grfio.c b/src/common/grfio.c index 5597177c5..a821ee269 100644 --- a/src/common/grfio.c +++ b/src/common/grfio.c @@ -1,1031 +1,1031 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -/********************************************************************* - * - * Ragnarok Online Emulator : grfio.c -- grf file I/O Module - *-------------------------------------------------------------------- - * special need library : zlib - ********************************************************************* - * $Id: grfio.c,v 1.2 2004/09/29 17:31:49 kalaspuff Exp $ - * - * 2002/12/18... the original edition - * 2003/01/23 ... Code correction - * 2003/02/01 ... An addition and decryption processing are improved for LocalFile and two or more GRF(s) check processing. - * 2003/02/02 ... Even if there is no grf it does not stop -- as -- correction - * 2003/02/02... grf reading specification can be added later -- as -- correction (grfio_add function addition) - * 2003/02 / 03... at the time of grfio_resourcecheck processing the entry addition processing method -- correction - * 2003/02/05... change of the processing in grfio_init - * 2003/02/23... a local file check -- GRFIO_LOCAL -- switch (Defoe -- Function Off) - * 2003/10/21 ... The data of alpha client was read. - * 2003/11/10 ... Ready new grf format. - * 2003/11/11 ... version check fix & bug fix - * 2006/04/16 ... fixed crash grfio_find_file when file is not found. - */ - -#include -#include -#include -#include -#include - -#include "grfio.h" -#include "../common/mmo.h" -#include "../common/showmsg.h" -#include "../common/malloc.h" -#include "../zlib/unzip.h" - -#define CHUNK 16384 - -#ifdef __WIN32 - #include "../zlib/zlib.h" - #include "../zlib/iowin32.h" -#else - #ifndef __FREEBSD__ - #include - #endif -#endif - -typedef unsigned char BYTE; -typedef unsigned short WORD; -typedef unsigned long DWORD; - -//static char data_file[1024] = ""; // "data.grf"; -//static char sdata_file[1024] = ""; // "sdata.grf"; -//static char adata_file[1024] = ""; // "adata.grf"; -static char data_dir[1024] = ""; // "../"; - -//---------------------------- -// file entry table struct -//---------------------------- -typedef struct { - int srclen; // compressed size - int srclen_aligned; // - int declen; // original size - int srcpos; - short next; - int cycle; - char type; - char fn[128-4*5]; // file name - char *fnd; - signed char gentry; // read grf file select -} FILELIST; -//gentry ... 0 : It acquires from a local file. -// It acquires from the resource file of 1>=:gentry_table[gentry-1]. -// 1<=: Check a local file. -// If it is, after re-setting to 0, it acquires from a local file. -// If there is nothing, mark reversal will be carried out, and it will re-set, and will acquire from a resource file as well as 1>=. - -//Since char defines *FILELIST.gentry, the maximum which can be added by grfio_add becomes by 127 pieces. - -#define GENTRY_LIMIT 512 -#define FILELIST_LIMIT 1048576 // temporary maximum, and a theory top maximum are 2G. - -static FILELIST *filelist = NULL; -static int filelist_entrys = 0; -static int filelist_maxentry = 0; - -static char **gentry_table = NULL; -static int gentry_entrys = 0; -static int gentry_maxentry = 0; - -//---------------------------- -// file list hash table -//---------------------------- -static int filelist_hash[256]; - -//---------------------------- -// grf decode data table -//---------------------------- -static unsigned char BitMaskTable[8] = { - 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 -}; - -static char BitSwapTable1[64] = { - 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, - 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, - 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, - 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 -}; -static char BitSwapTable2[64] = { - 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, - 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, - 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, - 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25 -}; -static char BitSwapTable3[32] = { - 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, - 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 -}; - -static unsigned char NibbleData[4][64]={ - { - 0xef, 0x03, 0x41, 0xfd, 0xd8, 0x74, 0x1e, 0x47, 0x26, 0xef, 0xfb, 0x22, 0xb3, 0xd8, 0x84, 0x1e, - 0x39, 0xac, 0xa7, 0x60, 0x62, 0xc1, 0xcd, 0xba, 0x5c, 0x96, 0x90, 0x59, 0x05, 0x3b, 0x7a, 0x85, - 0x40, 0xfd, 0x1e, 0xc8, 0xe7, 0x8a, 0x8b, 0x21, 0xda, 0x43, 0x64, 0x9f, 0x2d, 0x14, 0xb1, 0x72, - 0xf5, 0x5b, 0xc8, 0xb6, 0x9c, 0x37, 0x76, 0xec, 0x39, 0xa0, 0xa3, 0x05, 0x52, 0x6e, 0x0f, 0xd9, - }, { - 0xa7, 0xdd, 0x0d, 0x78, 0x9e, 0x0b, 0xe3, 0x95, 0x60, 0x36, 0x36, 0x4f, 0xf9, 0x60, 0x5a, 0xa3, - 0x11, 0x24, 0xd2, 0x87, 0xc8, 0x52, 0x75, 0xec, 0xbb, 0xc1, 0x4c, 0xba, 0x24, 0xfe, 0x8f, 0x19, - 0xda, 0x13, 0x66, 0xaf, 0x49, 0xd0, 0x90, 0x06, 0x8c, 0x6a, 0xfb, 0x91, 0x37, 0x8d, 0x0d, 0x78, - 0xbf, 0x49, 0x11, 0xf4, 0x23, 0xe5, 0xce, 0x3b, 0x55, 0xbc, 0xa2, 0x57, 0xe8, 0x22, 0x74, 0xce, - }, { - 0x2c, 0xea, 0xc1, 0xbf, 0x4a, 0x24, 0x1f, 0xc2, 0x79, 0x47, 0xa2, 0x7c, 0xb6, 0xd9, 0x68, 0x15, - 0x80, 0x56, 0x5d, 0x01, 0x33, 0xfd, 0xf4, 0xae, 0xde, 0x30, 0x07, 0x9b, 0xe5, 0x83, 0x9b, 0x68, - 0x49, 0xb4, 0x2e, 0x83, 0x1f, 0xc2, 0xb5, 0x7c, 0xa2, 0x19, 0xd8, 0xe5, 0x7c, 0x2f, 0x83, 0xda, - 0xf7, 0x6b, 0x90, 0xfe, 0xc4, 0x01, 0x5a, 0x97, 0x61, 0xa6, 0x3d, 0x40, 0x0b, 0x58, 0xe6, 0x3d, - }, { - 0x4d, 0xd1, 0xb2, 0x0f, 0x28, 0xbd, 0xe4, 0x78, 0xf6, 0x4a, 0x0f, 0x93, 0x8b, 0x17, 0xd1, 0xa4, - 0x3a, 0xec, 0xc9, 0x35, 0x93, 0x56, 0x7e, 0xcb, 0x55, 0x20, 0xa0, 0xfe, 0x6c, 0x89, 0x17, 0x62, - 0x17, 0x62, 0x4b, 0xb1, 0xb4, 0xde, 0xd1, 0x87, 0xc9, 0x14, 0x3c, 0x4a, 0x7e, 0xa8, 0xe2, 0x7d, - 0xa0, 0x9f, 0xf6, 0x5c, 0x6a, 0x09, 0x8d, 0xf0, 0x0f, 0xe3, 0x53, 0x25, 0x95, 0x36, 0x28, 0xcb, - } -}; -/*----------------- - * long data get - */ -static unsigned int getlong(unsigned char *p) -{ -// return *p+p[1]*256+(p[2]+p[3]*256)*65536; - return p[0] - | p[1] << 0x08 - | p[2] << 0x10 - | p[3] << 0x18; // Shinomori -} - -/*========================================== - * Grf data decode : Subs - *------------------------------------------ - */ -static void NibbleSwap(BYTE *Src, int len) -{ - for(;0>4) | (*Src<<4); - } -} - -static void BitConvert(BYTE *Src,char *BitSwapTable) -{ - int lop,prm; - BYTE tmp[8]; -// *(DWORD*)tmp=*(DWORD*)(tmp+4)=0; - malloc_tsetdword(tmp,0,8); - for(lop=0;lop!=64;lop++) { - prm = BitSwapTable[lop]-1; - if (Src[(prm >> 3) & 7] & BitMaskTable[prm & 7]) { - tmp[(lop >> 3) & 7] |= BitMaskTable[lop & 7]; - } - } -// *(DWORD*)Src = *(DWORD*)tmp; -// *(DWORD*)(Src+4) = *(DWORD*)(tmp+4); - memcpy(Src,tmp,8); -} - -static void BitConvert4(BYTE *Src) -{ - int lop,prm; - BYTE tmp[8]; - tmp[0] = ((Src[7]<<5) | (Src[4]>>3)) & 0x3f; // ..0 vutsr - tmp[1] = ((Src[4]<<1) | (Src[5]>>7)) & 0x3f; // ..srqpo n - tmp[2] = ((Src[4]<<5) | (Src[5]>>3)) & 0x3f; // ..o nmlkj - tmp[3] = ((Src[5]<<1) | (Src[6]>>7)) & 0x3f; // ..kjihg f - tmp[4] = ((Src[5]<<5) | (Src[6]>>3)) & 0x3f; // ..g fedcb - tmp[5] = ((Src[6]<<1) | (Src[7]>>7)) & 0x3f; // ..cba98 7 - tmp[6] = ((Src[6]<<5) | (Src[7]>>3)) & 0x3f; // ..8 76543 - tmp[7] = ((Src[7]<<1) | (Src[4]>>7)) & 0x3f; // ..43210 v - - for(lop=0;lop!=4;lop++) { - tmp[lop] = (NibbleData[lop][tmp[lop*2]] & 0xf0) - | (NibbleData[lop][tmp[lop*2+1]] & 0x0f); - } - - *(DWORD*)(tmp+4)=0; - for(lop=0;lop!=32;lop++) { - prm = BitSwapTable3[lop]-1; - if (tmp[prm >> 3] & BitMaskTable[prm & 7]) { - tmp[(lop >> 3) + 4] |= BitMaskTable[lop & 7]; - } - } -// *(DWORD*)Src ^= *(DWORD*)(tmp+4); - Src[0] ^= tmp[4]; - Src[1] ^= tmp[5]; - Src[2] ^= tmp[6]; - Src[3] ^= tmp[7]; -} - -static void decode_des_etc(BYTE *buf,int len,int type,int cycle) -{ - int lop,cnt=0; - if(cycle<3) cycle=3; - else if(cycle<5) cycle++; - else if(cycle<7) cycle+=9; - else cycle+=15; - - for(lop=0;lop*8 64K on 16-bit machine: */ - if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; - - stream.next_out = (Bytef*) dest; - stream.avail_out = (uInt)*destLen; - if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; - - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - - err = inflateInit(&stream); - if (err != Z_OK) return err; - - err = inflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - inflateEnd(&stream); - return err == Z_OK ? Z_BUF_ERROR : err; - } - *destLen = stream.total_out; - - err = inflateEnd(&stream); - return err; -} - -int encode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen) { - z_stream stream; - int err; - malloc_tsetdword(&stream, 0, sizeof(stream)); - stream.next_in = (Bytef*)source; - stream.avail_in = (uInt)sourceLen; - /* Check for source > 64K on 16-bit machine: */ - if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; - - stream.next_out = (Bytef*) dest; - stream.avail_out = (uInt)*destLen; - if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; - - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - - err = deflateInit(&stream,Z_DEFAULT_COMPRESSION); - if (err != Z_OK) return err; - - err = deflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - inflateEnd(&stream); - return err == Z_OK ? Z_BUF_ERROR : err; - } - *destLen = stream.total_out; - - err = deflateEnd(&stream); - return err; -} - -/* =================================== -* Unzips a file. 1: success, 0: error -* Adapted from miniunz.c [Celest] -* Version 1.01b, May 30th, 2004 -* Copyright (C) 1998-2004 Gilles Vollant -* ------------------------------------- -*/ -int deflate_file (const char *source, const char *filename) -{ -#ifdef _WIN32 - zlib_filefunc_def ffunc; -#endif - unzFile uf = NULL; - int err = UNZ_OK; - uInt size_buf = 8192; - FILE *fout = NULL; - void *buf; - -#ifdef _WIN32 - fill_win32_filefunc(&ffunc); - uf = unzOpen2(source, &ffunc); -#else - uf = unzOpen(source); -#endif - - if (uf == NULL) { - //printf("Cannot open %s\n", source); - return 0; - } - //printf("%s opened\n", source); - - if (unzLocateFile(uf, filename, 0) != UNZ_OK) { - //printf("file %s not found in the zipfile\n", filename); - return 0; - } - - err = unzOpenCurrentFilePassword(uf, NULL); - //if (err != UNZ_OK) - // printf("error %d with zipfile in unzOpenCurrentFilePassword\n", err); - - fout = fopen(filename,"wb"); - if (fout == NULL) { - //printf("error opening %s\n", filename); - return 0; - } - - buf = (void *)aMalloc(size_buf); - do { - err = unzReadCurrentFile(uf, buf, size_buf); - if (err < 0) { - //printf("error %d with zipfile in unzReadCurrentFile\n", err); - break; - } - if (err > 0 && - fwrite(buf, err, 1, fout)!=1) - { - //printf("error in writing extracted file\n"); - err = UNZ_ERRNO; - break; - } - } while (err > 0); - - if (fout) fclose(fout); - - if (err == UNZ_OK) { - err = unzCloseCurrentFile (uf); - //if (err != UNZ_OK) - // printf("error %d with zipfile in unzCloseCurrentFile\n", err); - aFree(buf); - return (err == UNZ_OK); - } - - unzCloseCurrentFile(uf); /* don't lose the error */ - - return 0; -} - -unsigned long grfio_crc32 (const unsigned char *buf, unsigned int len) -{ - return crc32(crc32(0L, Z_NULL, 0), buf, len); -} - -/*********************************************************** - *** File List Subroutines *** - ***********************************************************/ - -/*========================================== - * File List : Hash make - *------------------------------------------ - */ -static int filehash(unsigned char *fname) -{ - unsigned int hash=0; - while(*fname) { - hash = ((hash<<1)+(hash>>7)*9+tolower(*fname)); - fname++; - } - return hash & 255; -} - -/*========================================== - * File List : Hash initalize - *------------------------------------------ - */ -static void hashinit(void) -{ - int lop; - for (lop = 0; lop < 256; lop++) - filelist_hash[lop] = -1; -} - -/*========================================== - * File List : File find - *------------------------------------------ - */ -static FILELIST *filelist_find(char *fname) -{ - int hash; - - if (!filelist) - return NULL; - - for (hash = filelist_hash[filehash((unsigned char *) fname)]; hash >= 0; hash = filelist[hash].next) { - if(strcmpi(filelist[hash].fn, fname) == 0) - break; - } - - return (hash >= 0) ? &filelist[hash] : NULL; -} - -char *grfio_find_file(char *fname){ - FILELIST *filelist = filelist_find(fname); - if (!filelist) return NULL; - return (!filelist->fnd?filelist->fn:filelist->fnd); -} - -/*========================================== - * File List : Filelist add - *------------------------------------------ - */ -#define FILELIST_ADDS 1024 // number increment of file lists ` - -static FILELIST* filelist_add(FILELIST *entry) -{ - int hash; - - if (filelist_entrys >= FILELIST_LIMIT) { - ShowFatalError("GRF filelist limit reached (filelist_add)!\n"); - exit(1); - } - - if (filelist_entrys >= filelist_maxentry) { - filelist = (FILELIST *)aRealloc(filelist, (filelist_maxentry + FILELIST_ADDS) * sizeof(FILELIST)); - malloc_tsetdword(filelist + filelist_maxentry, '\0', FILELIST_ADDS * sizeof(FILELIST)); - filelist_maxentry += FILELIST_ADDS; - } - - memcpy (&filelist[filelist_entrys], entry, sizeof(FILELIST)); - - hash = filehash((unsigned char *) entry->fn); - filelist[filelist_entrys].next = filelist_hash[hash]; - filelist_hash[hash] = filelist_entrys; - - filelist_entrys++; - - return &filelist[filelist_entrys - 1]; -} - -static FILELIST* filelist_modify(FILELIST *entry) -{ - FILELIST *fentry; - if ((fentry = filelist_find(entry->fn)) != NULL) { - int tmp = fentry->next; - memcpy(fentry, entry, sizeof(FILELIST)); - fentry->next = tmp; - } else { - fentry = filelist_add(entry); - } - return fentry; -} - -/*========================================== - * File List : filelist size adjust - *------------------------------------------ - */ -static void filelist_adjust(void) -{ - if (filelist != NULL) { - if (filelist_maxentry > filelist_entrys) { - filelist = (FILELIST *)aRealloc( - filelist, filelist_entrys * sizeof(FILELIST)); - filelist_maxentry = filelist_entrys; - } - } -} - -/*********************************************************** - *** Grfio Sobroutines *** - ***********************************************************/ - -/*========================================== - * Grfio : Resource file size get - *------------------------------------------ - */ -int grfio_size(char *fname) -{ - FILELIST *entry; - - entry = filelist_find(fname); - - if (entry == NULL || entry->gentry < 0) { // LocalFileCheck - char lfname[256], *p; - FILELIST lentry; - struct stat st; - - sprintf(lfname, "%s%s", data_dir, fname); - - for (p = &lfname[0]; *p != 0; p++) - if (*p=='\\') *p = '/'; // * At the time of Unix - - if (stat(lfname, &st) == 0) { - strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1); - lentry.fnd = NULL; - lentry.declen = st.st_size; - lentry.gentry = 0; // 0:LocalFile - entry = filelist_modify(&lentry); - } else if (entry == NULL) { - ShowError("%s not found (grfio_size)\n", fname); - //exit(1); - return -1; - } - } - return entry->declen; -} - -/*========================================== - * Grfio : Resource file read & size get - *------------------------------------------ - */ -void* grfio_reads(char *fname, int *size) -{ - FILE *in; - FILELIST *entry; - unsigned char *buf2 = NULL; - - entry = filelist_find(fname); - - if (entry == NULL || entry->gentry <= 0) { // LocalFileCheck - char lfname[256], *p; - FILELIST lentry; - - sprintf(lfname, "%s%s", data_dir, fname); - - for (p = &lfname[0]; *p != 0; p++) - if (*p == '\\') *p = '/'; // * At the time of Unix - - in = fopen(lfname, "rb"); - if (in != NULL) { - if (entry != NULL && entry->gentry == 0) { - lentry.declen = entry->declen; - } else { - fseek(in,0,2); // SEEK_END - lentry.declen = ftell(in); - } - fseek(in,0,0); // SEEK_SET - buf2 = (unsigned char *)aMallocA(lentry.declen + 1024); - fread(buf2, 1, lentry.declen, in); - fclose(in); - strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1); - lentry.fnd = NULL; - lentry.gentry = 0; // 0:LocalFile - entry = filelist_modify(&lentry); - } else { - if (entry != NULL && entry->gentry < 0) { - entry->gentry = -entry->gentry; // local file checked - } else { - ShowError("%s not found (grfio_reads - local file %s)\n", fname, lfname); - return NULL; - } - } - } - if (entry != NULL && entry->gentry > 0) { // Archive[GRF] File Read - char *gfname = gentry_table[entry->gentry - 1]; - in = fopen(gfname, "rb"); - if(in != NULL) { - unsigned char *buf = (unsigned char *)aMallocA(entry->srclen_aligned + 1024); - fseek(in, entry->srcpos, 0); - fread(buf, 1, entry->srclen_aligned, in); - fclose(in); - buf2 = (unsigned char *)aMallocA(entry->declen + 1024); - if (entry->type == 1 || entry->type == 3 || entry->type == 5) { - uLongf len; - if (entry->cycle >= 0) - decode_des_etc(buf, entry->srclen_aligned, entry->cycle == 0, entry->cycle); - len = entry->declen; - decode_zip(buf2, &len, buf, entry->srclen); - if (len != entry->declen) { - ShowError("decode_zip size miss match err: %d != %d\n", (int)len, entry->declen); - aFree(buf); - aFree(buf2); - return NULL; - } - } else { - memcpy(buf2, buf, entry->declen); - } - aFree(buf); - } else { - ShowError("%s not found (grfio_reads - grf file %s)\n", fname, gfname); - return NULL; - } - } - if (size != NULL && entry != NULL) - *size = entry->declen; - - return buf2; -} - -/*========================================== - * Resource filename decode - *------------------------------------------ - */ -static char * decode_filename(unsigned char *buf,int len) -{ - int lop; - for(lop=0;lop> 8; - - if (grf_version == 0x01) { //****** Grf version 01xx ****** - list_size = grf_size - ftell(fp); - grf_filelist = (unsigned char *) aMallocA(list_size); - /*if (grf_filelist == NULL){ - fclose(fp); - ShowError("out of memory : grf_filelist\n"); - return 3; // 3:memory alloc error - }*/ - fread(grf_filelist,1,list_size,fp); - fclose(fp); - - entrys = getlong(grf_header+0x26) - getlong(grf_header+0x22) - 7; - - // Get an entry - for (entry = 0,ofs = 0; entry < entrys; entry++) { - int ofs2, srclen, srccount, type; - char *period_ptr; - FILELIST aentry; - - ofs2 = ofs+getlong(grf_filelist+ofs)+4; - type = grf_filelist[ofs2+12]; - if (type != 0) { // Directory Index ... skip - fname = decode_filename(grf_filelist+ofs+6, grf_filelist[ofs]-6); - if (strlen(fname) > sizeof(aentry.fn) - 1) { - ShowFatalError("GRF file name %s is too long\n", fname); - aFree(grf_filelist); - exit(1); - } - srclen = 0; - if ((period_ptr = strrchr(fname, '.')) != NULL) { - for(lop = 0; lop < 4; lop++) { - if (strcmpi(period_ptr, ".gnd\0.gat\0.act\0.str"+lop*5) == 0) - break; - } - srclen = getlong(grf_filelist+ofs2) - getlong(grf_filelist+ofs2+8) - 715; - if(lop == 4) { - for(lop = 10, srccount = 1; srclen >= lop; lop = lop * 10, srccount++); - } else { - srccount = 0; - } - } else { - srccount = 0; - } - - aentry.srclen = srclen; - aentry.srclen_aligned = getlong(grf_filelist+ofs2+4)-37579; - aentry.declen = getlong(grf_filelist+ofs2+8); - aentry.srcpos = getlong(grf_filelist+ofs2+13)+0x2e; - aentry.cycle = srccount; - aentry.type = type; - strncpy(aentry.fn, fname,sizeof(aentry.fn)-1); - aentry.fnd = NULL; -#ifdef GRFIO_LOCAL - aentry.gentry = -(gentry+1); // As Flag for making it a negative number carrying out the first time LocalFileCheck -#else - aentry.gentry = gentry+1; // With no first time LocalFileCheck -#endif - filelist_modify(&aentry); - } - ofs = ofs2 + 17; - } - aFree(grf_filelist); - - } else if (grf_version == 0x02) { //****** Grf version 02xx ****** - unsigned char eheader[8]; - unsigned char *rBuf; - uLongf rSize, eSize; - - fread(eheader,1,8,fp); - rSize = getlong(eheader); // Read Size - eSize = getlong(eheader+4); // Extend Size - - if ((long)rSize > grf_size-ftell(fp)) { // Warning fix [Lance] - fclose(fp); - ShowError("Illegal data format : grf compress entry size\n"); - return 4; - } - - rBuf = (unsigned char *)aMallocA(rSize); // Get a Read Size - /*if (rBuf==NULL) { - fclose(fp); - ShowError("out of memory : grf compress entry table buffer\n"); - return 3; - }*/ - grf_filelist = (unsigned char *)aMallocA(eSize); // Get a Extend Size - /*if (grf_filelist==NULL) { - aFree(rBuf); - fclose(fp); - ShowError("out of memory : grf extract entry table buffer\n"); - return 3; - }*/ - fread(rBuf,1,rSize,fp); - fclose(fp); - decode_zip(grf_filelist, &eSize, rBuf, rSize); // Decode function - list_size = eSize; - aFree(rBuf); - - entrys = getlong(grf_header+0x26) - 7; - - // Get an entry - for(entry = 0, ofs = 0; entry < entrys; entry++){ - int ofs2, srclen, srccount, type; - FILELIST aentry; - - fname = (char*)(grf_filelist+ofs); - if (strlen(fname) > sizeof(aentry.fn)-1) { - ShowFatalError("GRF file name %s is too long\n", fname); - aFree(grf_filelist); - exit(1); - } - //ofs2 = ofs+strlen((char*)(grf_filelist+ofs))+1; - ofs2 = ofs + strlen(fname)+1; - type = grf_filelist[ofs2+12]; - if (type == 1 || type == 3 || type == 5) { - srclen = getlong(grf_filelist+ofs2); - if (grf_filelist[ofs2+12] == 3) { - for (lop = 10, srccount = 1; srclen >= lop; lop = lop * 10, srccount++); - } else if (grf_filelist[ofs2+12] == 5) { - srccount = 0; - } else { // if (grf_filelist[ofs2+12]==1) { - srccount = -1; - } - - aentry.srclen = srclen; - aentry.srclen_aligned = getlong(grf_filelist+ofs2+4); - aentry.declen = getlong(grf_filelist+ofs2+8); - aentry.srcpos = getlong(grf_filelist+ofs2+13)+0x2e; - aentry.cycle = srccount; - aentry.type = type; - strncpy(aentry.fn,fname,sizeof(aentry.fn)-1); - aentry.fnd = NULL; -#ifdef GRFIO_LOCAL - aentry.gentry = -(gentry+1); // As Flag for making it a negative number carrying out the first time LocalFileCheck -#else - aentry.gentry = gentry+1; // With no first time LocalFileCheck -#endif - filelist_modify(&aentry); - } - ofs = ofs2 + 17; - } - aFree(grf_filelist); - - } else { //****** Grf Other version ****** - fclose(fp); - ShowError("GRF version %04x not supported\n",getlong(grf_header+0x2a)); - return 4; - } - - filelist_adjust(); // Unnecessary area release of filelist - - return 0; // 0:no error -} - -/*========================================== - * Grfio : Resource file check - *------------------------------------------ - */ -static void grfio_resourcecheck(void) -{ - char w1[256], w2[256], src[256], dst[256], restable[256], line[256]; - char *ptr, *buf; - FILELIST *entry; - int size, i = 0; - FILE *fp; - - // read resnametable from data directory and return if successful - sprintf(restable, "%sdata\\resnametable.txt", data_dir); - for (ptr = &restable[0]; *ptr != 0; ptr++) - if (*ptr == '\\') *ptr = '/'; - - fp = fopen(restable,"rb"); - if (fp) { - while (fgets(line, sizeof(line) - 1, fp)) { - if (sscanf(line, "%[^#]#%[^#]#", w1, w2) == 2 && - // we only need the map names and text files - (strstr(w2, ".gat") || strstr(w2, ".txt"))) - { - sprintf(src, "data\\%s", w1); - sprintf(dst, "data\\%s", w2); - entry = filelist_find(dst); - // create new entries reusing the original's info - if (entry != NULL) { - FILELIST fentry; - memcpy(&fentry, entry, sizeof(FILELIST)); - strncpy(fentry.fn, src, sizeof(fentry.fn) - 1); - fentry.fnd = grfio_alloc_ptr(dst); - filelist_modify(&fentry); - i++; - } - } - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, "resnametable.txt"); - return; // we're done here! - } - - // read resnametable from loaded GRF's, only if it cannot be - // loaded from the data directory - buf = (char *)grfio_reads("data\\resnametable.txt", &size); - if (buf) { - buf[size] = 0; - ptr = buf; - - while (ptr - buf < size) { - if (sscanf(ptr, "%[^#]#%[^#]#", w1, w2) == 2 && - (strstr(w2, ".gat") || strstr(w2, ".txt"))) - { - sprintf(src, "data\\%s", w1); - sprintf(dst, "data\\%s", w2); - entry = filelist_find(dst); - if (entry != NULL) { - FILELIST fentry; - memcpy(&fentry, entry, sizeof(FILELIST)); - strncpy(fentry.fn, src, sizeof(fentry.fn) - 1); - fentry.fnd = grfio_alloc_ptr(dst); - filelist_modify(&fentry); - i++; - } - } - ptr = strchr(ptr,'\n'); // Next line - if (!ptr) break; - ptr++; - } - aFree(buf); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, "data\\resnametable.txt"); - return; - } - - //ShowWarning("GRF: No resnametable found! Panic?\n"); -} - -/*========================================== - * Grfio : Resource add - *------------------------------------------ - */ -#define GENTRY_ADDS 4 // The number increment of gentry_table entries - -static int grfio_add(char *fname) -{ - grfio_alloc_ptr(fname); - - return grfio_entryread(fname, gentry_entrys - 1); -} - -char *grfio_alloc_ptr(char *fname) -{ - int len; - char *buf; - - if (gentry_entrys >= GENTRY_LIMIT) { - ShowFatalError("gentrys limit : grfio_add\n"); - exit(1); - } - - if (gentry_entrys >= gentry_maxentry) { - gentry_maxentry += GENTRY_ADDS; - gentry_table = (char**)aRealloc(gentry_table, gentry_maxentry * sizeof(char*)); - malloc_tsetdword(gentry_table + (gentry_maxentry - GENTRY_ADDS), 0, sizeof(char*) * GENTRY_ADDS); - } - len = strlen( fname ); - buf = (char*)aMallocA(len + 1); - strcpy(buf, fname); - gentry_table[gentry_entrys++] = buf; - - return buf; -} - -/*========================================== - * Grfio : Finalize - *------------------------------------------ - */ -void grfio_final(void) -{ - if (filelist != NULL) - aFree(filelist); - - filelist_entrys = filelist_maxentry = 0; - - if (gentry_table != NULL) { - int lop; - for (lop = 0; lop < gentry_entrys; lop++) { - if (gentry_table[lop] != NULL) - aFree(gentry_table[lop]); - } - aFree(gentry_table); - } - gentry_table = NULL; - gentry_entrys = gentry_maxentry = 0; -} - -/*========================================== - * Grfio : Initialize - *------------------------------------------ - */ -void grfio_init(char *fname) -{ - FILE *data_conf; - char line[1024], w1[1024], w2[1024]; - int result = 0; - - hashinit(); // hash table initialization - - data_conf = fopen(fname, "r"); - // It will read, if there is grf-files.txt. - if (data_conf) { - while(fgets(line, sizeof(line) - 1, data_conf)) { - if (line[0] == '/' && line[1] == '/') - continue; - if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) - continue; - // Entry table reading - if(strcmp(w1, "grf") == 0 || - strcmp(w1, "data") == 0 || // Primary data file - strcmp(w1, "sdata") == 0 || // Sakray data file - strcmp(w1, "adata") == 0) // Alpha version data file - // increment if successfully loaded - result += (grfio_add(w2) == 0); - else if(strcmp(w1,"data_dir") == 0) // Data directory - strcpy(data_dir, w2); - } - - fclose(data_conf); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", fname); - } // end of reading grf-files.txt - - if (result == 0) { - ShowInfo("No grf's loaded.. using default data directory\n"); - //exit(1); // It ends, if a resource cannot read one. - } - - // Unnecessary area release of filelist - filelist_adjust(); - // Resource check - grfio_resourcecheck(); - - return; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +/********************************************************************* + * + * Ragnarok Online Emulator : grfio.c -- grf file I/O Module + *-------------------------------------------------------------------- + * special need library : zlib + ********************************************************************* + * $Id: grfio.c,v 1.2 2004/09/29 17:31:49 kalaspuff Exp $ + * + * 2002/12/18... the original edition + * 2003/01/23 ... Code correction + * 2003/02/01 ... An addition and decryption processing are improved for LocalFile and two or more GRF(s) check processing. + * 2003/02/02 ... Even if there is no grf it does not stop -- as -- correction + * 2003/02/02... grf reading specification can be added later -- as -- correction (grfio_add function addition) + * 2003/02 / 03... at the time of grfio_resourcecheck processing the entry addition processing method -- correction + * 2003/02/05... change of the processing in grfio_init + * 2003/02/23... a local file check -- GRFIO_LOCAL -- switch (Defoe -- Function Off) + * 2003/10/21 ... The data of alpha client was read. + * 2003/11/10 ... Ready new grf format. + * 2003/11/11 ... version check fix & bug fix + * 2006/04/16 ... fixed crash grfio_find_file when file is not found. + */ + +#include +#include +#include +#include +#include + +#include "grfio.h" +#include "../common/mmo.h" +#include "../common/showmsg.h" +#include "../common/malloc.h" +#include "../zlib/unzip.h" + +#define CHUNK 16384 + +#ifdef __WIN32 + #include "../zlib/zlib.h" + #include "../zlib/iowin32.h" +#else + #ifndef __FREEBSD__ + #include + #endif +#endif + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; + +//static char data_file[1024] = ""; // "data.grf"; +//static char sdata_file[1024] = ""; // "sdata.grf"; +//static char adata_file[1024] = ""; // "adata.grf"; +static char data_dir[1024] = ""; // "../"; + +//---------------------------- +// file entry table struct +//---------------------------- +typedef struct { + int srclen; // compressed size + int srclen_aligned; // + int declen; // original size + int srcpos; + short next; + int cycle; + char type; + char fn[128-4*5]; // file name + char *fnd; + signed char gentry; // read grf file select +} FILELIST; +//gentry ... 0 : It acquires from a local file. +// It acquires from the resource file of 1>=:gentry_table[gentry-1]. +// 1<=: Check a local file. +// If it is, after re-setting to 0, it acquires from a local file. +// If there is nothing, mark reversal will be carried out, and it will re-set, and will acquire from a resource file as well as 1>=. + +//Since char defines *FILELIST.gentry, the maximum which can be added by grfio_add becomes by 127 pieces. + +#define GENTRY_LIMIT 512 +#define FILELIST_LIMIT 1048576 // temporary maximum, and a theory top maximum are 2G. + +static FILELIST *filelist = NULL; +static int filelist_entrys = 0; +static int filelist_maxentry = 0; + +static char **gentry_table = NULL; +static int gentry_entrys = 0; +static int gentry_maxentry = 0; + +//---------------------------- +// file list hash table +//---------------------------- +static int filelist_hash[256]; + +//---------------------------- +// grf decode data table +//---------------------------- +static unsigned char BitMaskTable[8] = { + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 +}; + +static char BitSwapTable1[64] = { + 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 +}; +static char BitSwapTable2[64] = { + 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25 +}; +static char BitSwapTable3[32] = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +static unsigned char NibbleData[4][64]={ + { + 0xef, 0x03, 0x41, 0xfd, 0xd8, 0x74, 0x1e, 0x47, 0x26, 0xef, 0xfb, 0x22, 0xb3, 0xd8, 0x84, 0x1e, + 0x39, 0xac, 0xa7, 0x60, 0x62, 0xc1, 0xcd, 0xba, 0x5c, 0x96, 0x90, 0x59, 0x05, 0x3b, 0x7a, 0x85, + 0x40, 0xfd, 0x1e, 0xc8, 0xe7, 0x8a, 0x8b, 0x21, 0xda, 0x43, 0x64, 0x9f, 0x2d, 0x14, 0xb1, 0x72, + 0xf5, 0x5b, 0xc8, 0xb6, 0x9c, 0x37, 0x76, 0xec, 0x39, 0xa0, 0xa3, 0x05, 0x52, 0x6e, 0x0f, 0xd9, + }, { + 0xa7, 0xdd, 0x0d, 0x78, 0x9e, 0x0b, 0xe3, 0x95, 0x60, 0x36, 0x36, 0x4f, 0xf9, 0x60, 0x5a, 0xa3, + 0x11, 0x24, 0xd2, 0x87, 0xc8, 0x52, 0x75, 0xec, 0xbb, 0xc1, 0x4c, 0xba, 0x24, 0xfe, 0x8f, 0x19, + 0xda, 0x13, 0x66, 0xaf, 0x49, 0xd0, 0x90, 0x06, 0x8c, 0x6a, 0xfb, 0x91, 0x37, 0x8d, 0x0d, 0x78, + 0xbf, 0x49, 0x11, 0xf4, 0x23, 0xe5, 0xce, 0x3b, 0x55, 0xbc, 0xa2, 0x57, 0xe8, 0x22, 0x74, 0xce, + }, { + 0x2c, 0xea, 0xc1, 0xbf, 0x4a, 0x24, 0x1f, 0xc2, 0x79, 0x47, 0xa2, 0x7c, 0xb6, 0xd9, 0x68, 0x15, + 0x80, 0x56, 0x5d, 0x01, 0x33, 0xfd, 0xf4, 0xae, 0xde, 0x30, 0x07, 0x9b, 0xe5, 0x83, 0x9b, 0x68, + 0x49, 0xb4, 0x2e, 0x83, 0x1f, 0xc2, 0xb5, 0x7c, 0xa2, 0x19, 0xd8, 0xe5, 0x7c, 0x2f, 0x83, 0xda, + 0xf7, 0x6b, 0x90, 0xfe, 0xc4, 0x01, 0x5a, 0x97, 0x61, 0xa6, 0x3d, 0x40, 0x0b, 0x58, 0xe6, 0x3d, + }, { + 0x4d, 0xd1, 0xb2, 0x0f, 0x28, 0xbd, 0xe4, 0x78, 0xf6, 0x4a, 0x0f, 0x93, 0x8b, 0x17, 0xd1, 0xa4, + 0x3a, 0xec, 0xc9, 0x35, 0x93, 0x56, 0x7e, 0xcb, 0x55, 0x20, 0xa0, 0xfe, 0x6c, 0x89, 0x17, 0x62, + 0x17, 0x62, 0x4b, 0xb1, 0xb4, 0xde, 0xd1, 0x87, 0xc9, 0x14, 0x3c, 0x4a, 0x7e, 0xa8, 0xe2, 0x7d, + 0xa0, 0x9f, 0xf6, 0x5c, 0x6a, 0x09, 0x8d, 0xf0, 0x0f, 0xe3, 0x53, 0x25, 0x95, 0x36, 0x28, 0xcb, + } +}; +/*----------------- + * long data get + */ +static unsigned int getlong(unsigned char *p) +{ +// return *p+p[1]*256+(p[2]+p[3]*256)*65536; + return p[0] + | p[1] << 0x08 + | p[2] << 0x10 + | p[3] << 0x18; // Shinomori +} + +/*========================================== + * Grf data decode : Subs + *------------------------------------------ + */ +static void NibbleSwap(BYTE *Src, int len) +{ + for(;0>4) | (*Src<<4); + } +} + +static void BitConvert(BYTE *Src,char *BitSwapTable) +{ + int lop,prm; + BYTE tmp[8]; +// *(DWORD*)tmp=*(DWORD*)(tmp+4)=0; + malloc_tsetdword(tmp,0,8); + for(lop=0;lop!=64;lop++) { + prm = BitSwapTable[lop]-1; + if (Src[(prm >> 3) & 7] & BitMaskTable[prm & 7]) { + tmp[(lop >> 3) & 7] |= BitMaskTable[lop & 7]; + } + } +// *(DWORD*)Src = *(DWORD*)tmp; +// *(DWORD*)(Src+4) = *(DWORD*)(tmp+4); + memcpy(Src,tmp,8); +} + +static void BitConvert4(BYTE *Src) +{ + int lop,prm; + BYTE tmp[8]; + tmp[0] = ((Src[7]<<5) | (Src[4]>>3)) & 0x3f; // ..0 vutsr + tmp[1] = ((Src[4]<<1) | (Src[5]>>7)) & 0x3f; // ..srqpo n + tmp[2] = ((Src[4]<<5) | (Src[5]>>3)) & 0x3f; // ..o nmlkj + tmp[3] = ((Src[5]<<1) | (Src[6]>>7)) & 0x3f; // ..kjihg f + tmp[4] = ((Src[5]<<5) | (Src[6]>>3)) & 0x3f; // ..g fedcb + tmp[5] = ((Src[6]<<1) | (Src[7]>>7)) & 0x3f; // ..cba98 7 + tmp[6] = ((Src[6]<<5) | (Src[7]>>3)) & 0x3f; // ..8 76543 + tmp[7] = ((Src[7]<<1) | (Src[4]>>7)) & 0x3f; // ..43210 v + + for(lop=0;lop!=4;lop++) { + tmp[lop] = (NibbleData[lop][tmp[lop*2]] & 0xf0) + | (NibbleData[lop][tmp[lop*2+1]] & 0x0f); + } + + *(DWORD*)(tmp+4)=0; + for(lop=0;lop!=32;lop++) { + prm = BitSwapTable3[lop]-1; + if (tmp[prm >> 3] & BitMaskTable[prm & 7]) { + tmp[(lop >> 3) + 4] |= BitMaskTable[lop & 7]; + } + } +// *(DWORD*)Src ^= *(DWORD*)(tmp+4); + Src[0] ^= tmp[4]; + Src[1] ^= tmp[5]; + Src[2] ^= tmp[6]; + Src[3] ^= tmp[7]; +} + +static void decode_des_etc(BYTE *buf,int len,int type,int cycle) +{ + int lop,cnt=0; + if(cycle<3) cycle=3; + else if(cycle<5) cycle++; + else if(cycle<7) cycle+=9; + else cycle+=15; + + for(lop=0;lop*8 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = (Bytef*) dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} + +int encode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen) { + z_stream stream; + int err; + malloc_tsetdword(&stream, 0, sizeof(stream)); + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = (Bytef*) dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = deflateInit(&stream,Z_DEFAULT_COMPRESSION); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =================================== +* Unzips a file. 1: success, 0: error +* Adapted from miniunz.c [Celest] +* Version 1.01b, May 30th, 2004 +* Copyright (C) 1998-2004 Gilles Vollant +* ------------------------------------- +*/ +int deflate_file (const char *source, const char *filename) +{ +#ifdef _WIN32 + zlib_filefunc_def ffunc; +#endif + unzFile uf = NULL; + int err = UNZ_OK; + uInt size_buf = 8192; + FILE *fout = NULL; + void *buf; + +#ifdef _WIN32 + fill_win32_filefunc(&ffunc); + uf = unzOpen2(source, &ffunc); +#else + uf = unzOpen(source); +#endif + + if (uf == NULL) { + //printf("Cannot open %s\n", source); + return 0; + } + //printf("%s opened\n", source); + + if (unzLocateFile(uf, filename, 0) != UNZ_OK) { + //printf("file %s not found in the zipfile\n", filename); + return 0; + } + + err = unzOpenCurrentFilePassword(uf, NULL); + //if (err != UNZ_OK) + // printf("error %d with zipfile in unzOpenCurrentFilePassword\n", err); + + fout = fopen(filename,"wb"); + if (fout == NULL) { + //printf("error opening %s\n", filename); + return 0; + } + + buf = (void *)aMalloc(size_buf); + do { + err = unzReadCurrentFile(uf, buf, size_buf); + if (err < 0) { + //printf("error %d with zipfile in unzReadCurrentFile\n", err); + break; + } + if (err > 0 && + fwrite(buf, err, 1, fout)!=1) + { + //printf("error in writing extracted file\n"); + err = UNZ_ERRNO; + break; + } + } while (err > 0); + + if (fout) fclose(fout); + + if (err == UNZ_OK) { + err = unzCloseCurrentFile (uf); + //if (err != UNZ_OK) + // printf("error %d with zipfile in unzCloseCurrentFile\n", err); + aFree(buf); + return (err == UNZ_OK); + } + + unzCloseCurrentFile(uf); /* don't lose the error */ + + return 0; +} + +unsigned long grfio_crc32 (const unsigned char *buf, unsigned int len) +{ + return crc32(crc32(0L, Z_NULL, 0), buf, len); +} + +/*********************************************************** + *** File List Subroutines *** + ***********************************************************/ + +/*========================================== + * File List : Hash make + *------------------------------------------ + */ +static int filehash(unsigned char *fname) +{ + unsigned int hash=0; + while(*fname) { + hash = ((hash<<1)+(hash>>7)*9+tolower(*fname)); + fname++; + } + return hash & 255; +} + +/*========================================== + * File List : Hash initalize + *------------------------------------------ + */ +static void hashinit(void) +{ + int lop; + for (lop = 0; lop < 256; lop++) + filelist_hash[lop] = -1; +} + +/*========================================== + * File List : File find + *------------------------------------------ + */ +static FILELIST *filelist_find(char *fname) +{ + int hash; + + if (!filelist) + return NULL; + + for (hash = filelist_hash[filehash((unsigned char *) fname)]; hash >= 0; hash = filelist[hash].next) { + if(strcmpi(filelist[hash].fn, fname) == 0) + break; + } + + return (hash >= 0) ? &filelist[hash] : NULL; +} + +char *grfio_find_file(char *fname){ + FILELIST *filelist = filelist_find(fname); + if (!filelist) return NULL; + return (!filelist->fnd?filelist->fn:filelist->fnd); +} + +/*========================================== + * File List : Filelist add + *------------------------------------------ + */ +#define FILELIST_ADDS 1024 // number increment of file lists ` + +static FILELIST* filelist_add(FILELIST *entry) +{ + int hash; + + if (filelist_entrys >= FILELIST_LIMIT) { + ShowFatalError("GRF filelist limit reached (filelist_add)!\n"); + exit(1); + } + + if (filelist_entrys >= filelist_maxentry) { + filelist = (FILELIST *)aRealloc(filelist, (filelist_maxentry + FILELIST_ADDS) * sizeof(FILELIST)); + malloc_tsetdword(filelist + filelist_maxentry, '\0', FILELIST_ADDS * sizeof(FILELIST)); + filelist_maxentry += FILELIST_ADDS; + } + + memcpy (&filelist[filelist_entrys], entry, sizeof(FILELIST)); + + hash = filehash((unsigned char *) entry->fn); + filelist[filelist_entrys].next = filelist_hash[hash]; + filelist_hash[hash] = filelist_entrys; + + filelist_entrys++; + + return &filelist[filelist_entrys - 1]; +} + +static FILELIST* filelist_modify(FILELIST *entry) +{ + FILELIST *fentry; + if ((fentry = filelist_find(entry->fn)) != NULL) { + int tmp = fentry->next; + memcpy(fentry, entry, sizeof(FILELIST)); + fentry->next = tmp; + } else { + fentry = filelist_add(entry); + } + return fentry; +} + +/*========================================== + * File List : filelist size adjust + *------------------------------------------ + */ +static void filelist_adjust(void) +{ + if (filelist != NULL) { + if (filelist_maxentry > filelist_entrys) { + filelist = (FILELIST *)aRealloc( + filelist, filelist_entrys * sizeof(FILELIST)); + filelist_maxentry = filelist_entrys; + } + } +} + +/*********************************************************** + *** Grfio Sobroutines *** + ***********************************************************/ + +/*========================================== + * Grfio : Resource file size get + *------------------------------------------ + */ +int grfio_size(char *fname) +{ + FILELIST *entry; + + entry = filelist_find(fname); + + if (entry == NULL || entry->gentry < 0) { // LocalFileCheck + char lfname[256], *p; + FILELIST lentry; + struct stat st; + + sprintf(lfname, "%s%s", data_dir, fname); + + for (p = &lfname[0]; *p != 0; p++) + if (*p=='\\') *p = '/'; // * At the time of Unix + + if (stat(lfname, &st) == 0) { + strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1); + lentry.fnd = NULL; + lentry.declen = st.st_size; + lentry.gentry = 0; // 0:LocalFile + entry = filelist_modify(&lentry); + } else if (entry == NULL) { + ShowError("%s not found (grfio_size)\n", fname); + //exit(1); + return -1; + } + } + return entry->declen; +} + +/*========================================== + * Grfio : Resource file read & size get + *------------------------------------------ + */ +void* grfio_reads(char *fname, int *size) +{ + FILE *in; + FILELIST *entry; + unsigned char *buf2 = NULL; + + entry = filelist_find(fname); + + if (entry == NULL || entry->gentry <= 0) { // LocalFileCheck + char lfname[256], *p; + FILELIST lentry; + + sprintf(lfname, "%s%s", data_dir, fname); + + for (p = &lfname[0]; *p != 0; p++) + if (*p == '\\') *p = '/'; // * At the time of Unix + + in = fopen(lfname, "rb"); + if (in != NULL) { + if (entry != NULL && entry->gentry == 0) { + lentry.declen = entry->declen; + } else { + fseek(in,0,2); // SEEK_END + lentry.declen = ftell(in); + } + fseek(in,0,0); // SEEK_SET + buf2 = (unsigned char *)aMallocA(lentry.declen + 1024); + fread(buf2, 1, lentry.declen, in); + fclose(in); + strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1); + lentry.fnd = NULL; + lentry.gentry = 0; // 0:LocalFile + entry = filelist_modify(&lentry); + } else { + if (entry != NULL && entry->gentry < 0) { + entry->gentry = -entry->gentry; // local file checked + } else { + ShowError("%s not found (grfio_reads - local file %s)\n", fname, lfname); + return NULL; + } + } + } + if (entry != NULL && entry->gentry > 0) { // Archive[GRF] File Read + char *gfname = gentry_table[entry->gentry - 1]; + in = fopen(gfname, "rb"); + if(in != NULL) { + unsigned char *buf = (unsigned char *)aMallocA(entry->srclen_aligned + 1024); + fseek(in, entry->srcpos, 0); + fread(buf, 1, entry->srclen_aligned, in); + fclose(in); + buf2 = (unsigned char *)aMallocA(entry->declen + 1024); + if (entry->type == 1 || entry->type == 3 || entry->type == 5) { + uLongf len; + if (entry->cycle >= 0) + decode_des_etc(buf, entry->srclen_aligned, entry->cycle == 0, entry->cycle); + len = entry->declen; + decode_zip(buf2, &len, buf, entry->srclen); + if (len != entry->declen) { + ShowError("decode_zip size miss match err: %d != %d\n", (int)len, entry->declen); + aFree(buf); + aFree(buf2); + return NULL; + } + } else { + memcpy(buf2, buf, entry->declen); + } + aFree(buf); + } else { + ShowError("%s not found (grfio_reads - grf file %s)\n", fname, gfname); + return NULL; + } + } + if (size != NULL && entry != NULL) + *size = entry->declen; + + return buf2; +} + +/*========================================== + * Resource filename decode + *------------------------------------------ + */ +static char * decode_filename(unsigned char *buf,int len) +{ + int lop; + for(lop=0;lop> 8; + + if (grf_version == 0x01) { //****** Grf version 01xx ****** + list_size = grf_size - ftell(fp); + grf_filelist = (unsigned char *) aMallocA(list_size); + /*if (grf_filelist == NULL){ + fclose(fp); + ShowError("out of memory : grf_filelist\n"); + return 3; // 3:memory alloc error + }*/ + fread(grf_filelist,1,list_size,fp); + fclose(fp); + + entrys = getlong(grf_header+0x26) - getlong(grf_header+0x22) - 7; + + // Get an entry + for (entry = 0,ofs = 0; entry < entrys; entry++) { + int ofs2, srclen, srccount, type; + char *period_ptr; + FILELIST aentry; + + ofs2 = ofs+getlong(grf_filelist+ofs)+4; + type = grf_filelist[ofs2+12]; + if (type != 0) { // Directory Index ... skip + fname = decode_filename(grf_filelist+ofs+6, grf_filelist[ofs]-6); + if (strlen(fname) > sizeof(aentry.fn) - 1) { + ShowFatalError("GRF file name %s is too long\n", fname); + aFree(grf_filelist); + exit(1); + } + srclen = 0; + if ((period_ptr = strrchr(fname, '.')) != NULL) { + for(lop = 0; lop < 4; lop++) { + if (strcmpi(period_ptr, ".gnd\0.gat\0.act\0.str"+lop*5) == 0) + break; + } + srclen = getlong(grf_filelist+ofs2) - getlong(grf_filelist+ofs2+8) - 715; + if(lop == 4) { + for(lop = 10, srccount = 1; srclen >= lop; lop = lop * 10, srccount++); + } else { + srccount = 0; + } + } else { + srccount = 0; + } + + aentry.srclen = srclen; + aentry.srclen_aligned = getlong(grf_filelist+ofs2+4)-37579; + aentry.declen = getlong(grf_filelist+ofs2+8); + aentry.srcpos = getlong(grf_filelist+ofs2+13)+0x2e; + aentry.cycle = srccount; + aentry.type = type; + strncpy(aentry.fn, fname,sizeof(aentry.fn)-1); + aentry.fnd = NULL; +#ifdef GRFIO_LOCAL + aentry.gentry = -(gentry+1); // As Flag for making it a negative number carrying out the first time LocalFileCheck +#else + aentry.gentry = gentry+1; // With no first time LocalFileCheck +#endif + filelist_modify(&aentry); + } + ofs = ofs2 + 17; + } + aFree(grf_filelist); + + } else if (grf_version == 0x02) { //****** Grf version 02xx ****** + unsigned char eheader[8]; + unsigned char *rBuf; + uLongf rSize, eSize; + + fread(eheader,1,8,fp); + rSize = getlong(eheader); // Read Size + eSize = getlong(eheader+4); // Extend Size + + if ((long)rSize > grf_size-ftell(fp)) { // Warning fix [Lance] + fclose(fp); + ShowError("Illegal data format : grf compress entry size\n"); + return 4; + } + + rBuf = (unsigned char *)aMallocA(rSize); // Get a Read Size + /*if (rBuf==NULL) { + fclose(fp); + ShowError("out of memory : grf compress entry table buffer\n"); + return 3; + }*/ + grf_filelist = (unsigned char *)aMallocA(eSize); // Get a Extend Size + /*if (grf_filelist==NULL) { + aFree(rBuf); + fclose(fp); + ShowError("out of memory : grf extract entry table buffer\n"); + return 3; + }*/ + fread(rBuf,1,rSize,fp); + fclose(fp); + decode_zip(grf_filelist, &eSize, rBuf, rSize); // Decode function + list_size = eSize; + aFree(rBuf); + + entrys = getlong(grf_header+0x26) - 7; + + // Get an entry + for(entry = 0, ofs = 0; entry < entrys; entry++){ + int ofs2, srclen, srccount, type; + FILELIST aentry; + + fname = (char*)(grf_filelist+ofs); + if (strlen(fname) > sizeof(aentry.fn)-1) { + ShowFatalError("GRF file name %s is too long\n", fname); + aFree(grf_filelist); + exit(1); + } + //ofs2 = ofs+strlen((char*)(grf_filelist+ofs))+1; + ofs2 = ofs + strlen(fname)+1; + type = grf_filelist[ofs2+12]; + if (type == 1 || type == 3 || type == 5) { + srclen = getlong(grf_filelist+ofs2); + if (grf_filelist[ofs2+12] == 3) { + for (lop = 10, srccount = 1; srclen >= lop; lop = lop * 10, srccount++); + } else if (grf_filelist[ofs2+12] == 5) { + srccount = 0; + } else { // if (grf_filelist[ofs2+12]==1) { + srccount = -1; + } + + aentry.srclen = srclen; + aentry.srclen_aligned = getlong(grf_filelist+ofs2+4); + aentry.declen = getlong(grf_filelist+ofs2+8); + aentry.srcpos = getlong(grf_filelist+ofs2+13)+0x2e; + aentry.cycle = srccount; + aentry.type = type; + strncpy(aentry.fn,fname,sizeof(aentry.fn)-1); + aentry.fnd = NULL; +#ifdef GRFIO_LOCAL + aentry.gentry = -(gentry+1); // As Flag for making it a negative number carrying out the first time LocalFileCheck +#else + aentry.gentry = gentry+1; // With no first time LocalFileCheck +#endif + filelist_modify(&aentry); + } + ofs = ofs2 + 17; + } + aFree(grf_filelist); + + } else { //****** Grf Other version ****** + fclose(fp); + ShowError("GRF version %04x not supported\n",getlong(grf_header+0x2a)); + return 4; + } + + filelist_adjust(); // Unnecessary area release of filelist + + return 0; // 0:no error +} + +/*========================================== + * Grfio : Resource file check + *------------------------------------------ + */ +static void grfio_resourcecheck(void) +{ + char w1[256], w2[256], src[256], dst[256], restable[256], line[256]; + char *ptr, *buf; + FILELIST *entry; + int size, i = 0; + FILE *fp; + + // read resnametable from data directory and return if successful + sprintf(restable, "%sdata\\resnametable.txt", data_dir); + for (ptr = &restable[0]; *ptr != 0; ptr++) + if (*ptr == '\\') *ptr = '/'; + + fp = fopen(restable,"rb"); + if (fp) { + while (fgets(line, sizeof(line) - 1, fp)) { + if (sscanf(line, "%[^#]#%[^#]#", w1, w2) == 2 && + // we only need the map names and text files + (strstr(w2, ".gat") || strstr(w2, ".txt"))) + { + sprintf(src, "data\\%s", w1); + sprintf(dst, "data\\%s", w2); + entry = filelist_find(dst); + // create new entries reusing the original's info + if (entry != NULL) { + FILELIST fentry; + memcpy(&fentry, entry, sizeof(FILELIST)); + strncpy(fentry.fn, src, sizeof(fentry.fn) - 1); + fentry.fnd = grfio_alloc_ptr(dst); + filelist_modify(&fentry); + i++; + } + } + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, "resnametable.txt"); + return; // we're done here! + } + + // read resnametable from loaded GRF's, only if it cannot be + // loaded from the data directory + buf = (char *)grfio_reads("data\\resnametable.txt", &size); + if (buf) { + buf[size] = 0; + ptr = buf; + + while (ptr - buf < size) { + if (sscanf(ptr, "%[^#]#%[^#]#", w1, w2) == 2 && + (strstr(w2, ".gat") || strstr(w2, ".txt"))) + { + sprintf(src, "data\\%s", w1); + sprintf(dst, "data\\%s", w2); + entry = filelist_find(dst); + if (entry != NULL) { + FILELIST fentry; + memcpy(&fentry, entry, sizeof(FILELIST)); + strncpy(fentry.fn, src, sizeof(fentry.fn) - 1); + fentry.fnd = grfio_alloc_ptr(dst); + filelist_modify(&fentry); + i++; + } + } + ptr = strchr(ptr,'\n'); // Next line + if (!ptr) break; + ptr++; + } + aFree(buf); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, "data\\resnametable.txt"); + return; + } + + //ShowWarning("GRF: No resnametable found! Panic?\n"); +} + +/*========================================== + * Grfio : Resource add + *------------------------------------------ + */ +#define GENTRY_ADDS 4 // The number increment of gentry_table entries + +static int grfio_add(char *fname) +{ + grfio_alloc_ptr(fname); + + return grfio_entryread(fname, gentry_entrys - 1); +} + +char *grfio_alloc_ptr(char *fname) +{ + int len; + char *buf; + + if (gentry_entrys >= GENTRY_LIMIT) { + ShowFatalError("gentrys limit : grfio_add\n"); + exit(1); + } + + if (gentry_entrys >= gentry_maxentry) { + gentry_maxentry += GENTRY_ADDS; + gentry_table = (char**)aRealloc(gentry_table, gentry_maxentry * sizeof(char*)); + malloc_tsetdword(gentry_table + (gentry_maxentry - GENTRY_ADDS), 0, sizeof(char*) * GENTRY_ADDS); + } + len = strlen( fname ); + buf = (char*)aMallocA(len + 1); + strcpy(buf, fname); + gentry_table[gentry_entrys++] = buf; + + return buf; +} + +/*========================================== + * Grfio : Finalize + *------------------------------------------ + */ +void grfio_final(void) +{ + if (filelist != NULL) + aFree(filelist); + + filelist_entrys = filelist_maxentry = 0; + + if (gentry_table != NULL) { + int lop; + for (lop = 0; lop < gentry_entrys; lop++) { + if (gentry_table[lop] != NULL) + aFree(gentry_table[lop]); + } + aFree(gentry_table); + } + gentry_table = NULL; + gentry_entrys = gentry_maxentry = 0; +} + +/*========================================== + * Grfio : Initialize + *------------------------------------------ + */ +void grfio_init(char *fname) +{ + FILE *data_conf; + char line[1024], w1[1024], w2[1024]; + int result = 0; + + hashinit(); // hash table initialization + + data_conf = fopen(fname, "r"); + // It will read, if there is grf-files.txt. + if (data_conf) { + while(fgets(line, sizeof(line) - 1, data_conf)) { + if (line[0] == '/' && line[1] == '/') + continue; + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + // Entry table reading + if(strcmp(w1, "grf") == 0 || + strcmp(w1, "data") == 0 || // Primary data file + strcmp(w1, "sdata") == 0 || // Sakray data file + strcmp(w1, "adata") == 0) // Alpha version data file + // increment if successfully loaded + result += (grfio_add(w2) == 0); + else if(strcmp(w1,"data_dir") == 0) // Data directory + strcpy(data_dir, w2); + } + + fclose(data_conf); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", fname); + } // end of reading grf-files.txt + + if (result == 0) { + ShowInfo("No grf's loaded.. using default data directory\n"); + //exit(1); // It ends, if a resource cannot read one. + } + + // Unnecessary area release of filelist + filelist_adjust(); + // Resource check + grfio_resourcecheck(); + + return; +} diff --git a/src/common/grfio.h b/src/common/grfio.h index 4ccdd00c7..572258b04 100644 --- a/src/common/grfio.h +++ b/src/common/grfio.h @@ -1,22 +1,22 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _GRFIO_H_ -#define _GRFIO_H_ - -void grfio_init(char*); // GRFIO Initialize -void grfio_final(void); // GRFIO Finalize -void* grfio_reads(char*,int*); // GRFIO data file read & size get -char *grfio_find_file(char *fname); -char *grfio_alloc_ptr(char *fname); - -#define grfio_read(fn) grfio_reads(fn, NULL) - -int grfio_size(char*); // GRFIO data file size get -unsigned long grfio_crc32(const unsigned char *buf, unsigned int len); - -int decode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen); -int encode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen); -int deflate_file (const char *source, const char *filename); - -#endif // _GRFIO_H_ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _GRFIO_H_ +#define _GRFIO_H_ + +void grfio_init(char*); // GRFIO Initialize +void grfio_final(void); // GRFIO Finalize +void* grfio_reads(char*,int*); // GRFIO data file read & size get +char *grfio_find_file(char *fname); +char *grfio_alloc_ptr(char *fname); + +#define grfio_read(fn) grfio_reads(fn, NULL) + +int grfio_size(char*); // GRFIO data file size get +unsigned long grfio_crc32(const unsigned char *buf, unsigned int len); + +int decode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen); +int encode_zip(unsigned char *dest, unsigned long* destLen, const unsigned char* source, unsigned long sourceLen); +int deflate_file (const char *source, const char *filename); + +#endif // _GRFIO_H_ diff --git a/src/common/lock.c b/src/common/lock.c index c7bf623e5..18091820d 100644 --- a/src/common/lock.c +++ b/src/common/lock.c @@ -1,71 +1,71 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#ifndef WIN32 -#include -#else -#include -#define F_OK 0x0 -#define R_OK 0x4 -#endif -#include "lock.h" -#include "showmsg.h" - -#ifndef _WIN32 - #define exists(filename) (!access(filename, F_OK)) -#else -// could be speed up maybe? -int exists(char *file) { - FILE *fp; - if ((fp = fopen(file,"r")) && fclose(fp) == 0) return 1; - return 0; -} -#endif - -// 書き込みファイルの保護処理 -// (書き込みが終わるまで、旧ファイルを保管しておく) - -// 新しいファイルの書き込み開始 -FILE* lock_fopen (const char* filename, int *info) { - char newfile[512]; - FILE *fp; - int no = 0; - - // 安全なファイル名を得る(手抜き) - do { - sprintf(newfile, "%s_%04d.tmp", filename, ++no); - } while((fp = fopen(newfile,"r")) && (fclose(fp), no < 9999)); - *info = no; - return fopen(newfile,"w"); -} - -// 旧ファイルを削除&新ファイルをリネーム -int lock_fclose (FILE *fp, const char* filename, int *info) { - int ret = 1; - char newfile[512]; - char oldfile[512]; - if (fp != NULL) { - ret = fclose(fp); - sprintf(newfile, "%s_%04d.tmp", filename, *info); - sprintf(oldfile, "%s.bak", filename); // old backup file - - if (exists(oldfile)) remove(oldfile); // remove backup file if it already exists - rename (filename, oldfile); // backup our older data instead of deleting it - - // このタイミングで落ちると最悪。 - if ((ret = rename(newfile,filename)) != 0) { // rename our temporary file to its correct name -#if defined(__NETBSD__) || defined(_WIN32) || defined(sun) || defined (_sun) || defined (__sun__) - ShowError("%s - '"CL_WHITE"%s"CL_RESET"'\n", strerror(errno), newfile); -#else - char ebuf[255]; - ShowError("%s - '"CL_WHITE"%s"CL_RESET"'\n", strerror_r(errno, ebuf, sizeof(ebuf)), newfile); -#endif - } - } - - return ret; -} - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#ifndef WIN32 +#include +#else +#include +#define F_OK 0x0 +#define R_OK 0x4 +#endif +#include "lock.h" +#include "showmsg.h" + +#ifndef _WIN32 + #define exists(filename) (!access(filename, F_OK)) +#else +// could be speed up maybe? +int exists(char *file) { + FILE *fp; + if ((fp = fopen(file,"r")) && fclose(fp) == 0) return 1; + return 0; +} +#endif + +// 書き込みファイルの保護処理 +// (書き込みが終わるまで、旧ファイルを保管しておく) + +// 新しいファイルの書き込み開始 +FILE* lock_fopen (const char* filename, int *info) { + char newfile[512]; + FILE *fp; + int no = 0; + + // 安全なファイル名を得る(手抜き) + do { + sprintf(newfile, "%s_%04d.tmp", filename, ++no); + } while((fp = fopen(newfile,"r")) && (fclose(fp), no < 9999)); + *info = no; + return fopen(newfile,"w"); +} + +// 旧ファイルを削除&新ファイルをリネーム +int lock_fclose (FILE *fp, const char* filename, int *info) { + int ret = 1; + char newfile[512]; + char oldfile[512]; + if (fp != NULL) { + ret = fclose(fp); + sprintf(newfile, "%s_%04d.tmp", filename, *info); + sprintf(oldfile, "%s.bak", filename); // old backup file + + if (exists(oldfile)) remove(oldfile); // remove backup file if it already exists + rename (filename, oldfile); // backup our older data instead of deleting it + + // このタイミングで落ちると最悪。 + if ((ret = rename(newfile,filename)) != 0) { // rename our temporary file to its correct name +#if defined(__NETBSD__) || defined(_WIN32) || defined(sun) || defined (_sun) || defined (__sun__) + ShowError("%s - '"CL_WHITE"%s"CL_RESET"'\n", strerror(errno), newfile); +#else + char ebuf[255]; + ShowError("%s - '"CL_WHITE"%s"CL_RESET"'\n", strerror_r(errno, ebuf, sizeof(ebuf)), newfile); +#endif + } + } + + return ret; +} + diff --git a/src/common/lock.h b/src/common/lock.h index 5c846eb73..e2247fc68 100644 --- a/src/common/lock.h +++ b/src/common/lock.h @@ -1,11 +1,11 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _LOCK_H_ -#define _LOCK_H_ - -FILE* lock_fopen(const char* filename,int *info); -int lock_fclose(FILE *fp,const char* filename,int *info); - -#endif - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _LOCK_H_ +#define _LOCK_H_ + +FILE* lock_fopen(const char* filename,int *info); +int lock_fclose(FILE *fp,const char* filename,int *info); + +#endif + diff --git a/src/common/malloc.c b/src/common/malloc.c index 1a4729fa2..d4dda5b4c 100644 --- a/src/common/malloc.c +++ b/src/common/malloc.c @@ -1,733 +1,733 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include "../common/malloc.h" -#include "../common/core.h" -#include "../common/showmsg.h" - -#ifdef MINICORE - #undef LOG_MEMMGR -#endif - -void* aMalloc_(size_t size, const char *file, int line, const char *func) -{ - void *ret = MALLOC(size, file, line, func); - // ShowMessage("%s:%d: in func %s: aMalloc %d\n",file,line,func,size); - if (ret == NULL){ - ShowFatalError("%s:%d: in func %s: aMalloc error out of memory!\n",file,line,func); - exit(1); - } - - return ret; -} -void* aMallocA_(size_t size, const char *file, int line, const char *func) -{ - void *ret = MALLOCA(size, file, line, func); - // ShowMessage("%s:%d: in func %s: aMallocA %d\n",file,line,func,size); - if (ret == NULL){ - ShowFatalError("%s:%d: in func %s: aMallocA error out of memory!\n",file,line,func); - exit(1); - } - - return ret; -} -void* aCalloc_(size_t num, size_t size, const char *file, int line, const char *func) -{ - void *ret = CALLOC(num, size, file, line, func); - // ShowMessage("%s:%d: in func %s: aCalloc %d %d\n",file,line,func,num,size); - if (ret == NULL){ - ShowFatalError("%s:%d: in func %s: aCalloc error out of memory!\n", file, line, func); - exit(1); - } - return ret; -} -void* aCallocA_(size_t num, size_t size, const char *file, int line, const char *func) -{ - void *ret = CALLOCA(num, size, file, line, func); - // ShowMessage("%s:%d: in func %s: aCallocA %d %d\n",file,line,func,num,size); - if (ret == NULL){ - ShowFatalError("%s:%d: in func %s: aCallocA error out of memory!\n",file,line,func); - exit(1); - } - return ret; -} -void* aRealloc_(void *p, size_t size, const char *file, int line, const char *func) -{ - void *ret = REALLOC(p, size, file, line, func); - // ShowMessage("%s:%d: in func %s: aRealloc %p %d\n",file,line,func,p,size); - if (ret == NULL){ - ShowFatalError("%s:%d: in func %s: aRealloc error out of memory!\n",file,line,func); - exit(1); - } - return ret; -} -char* aStrdup_(const char *p, const char *file, int line, const char *func) -{ - char *ret = STRDUP(p, file, line, func); - // ShowMessage("%s:%d: in func %s: aStrdup %p\n",file,line,func,p); - if (ret == NULL){ - ShowFatalError("%s:%d: in func %s: aStrdup error out of memory!\n", file, line, func); - exit(1); - } - return ret; -} -void aFree_(void *p, const char *file, int line, const char *func) -{ - // ShowMessage("%s:%d: in func %s: aFree %p\n",file,line,func,p); - if (p) - FREE(p, file, line, func); - - p = NULL; -} - -#ifdef GCOLLECT - -void* _bcallocA(size_t size, size_t cnt) -{ - void *ret = MALLOCA(size * cnt); - if (ret) //malloc_set(ret, 0, size * cnt); - malloc_set(ret, 0, size*cnt); - return ret; -} -void* _bcalloc(size_t size, size_t cnt) -{ - void *ret = MALLOC(size * cnt); - if (ret) //malloc_set(ret, 0, size * cnt); - malloc_set(ret, 0, size*cnt); - return ret; -} -char* _bstrdup(const char *chr) -{ - int len = strlen(chr); - char *ret = (char*)MALLOC(len + 1); - if (ret) memcpy(ret, chr, len + 1); - return ret; -} - -#endif - -#ifdef USE_MEMMGR - -/* USE_MEMMGR */ - -/* - * メモリマネージャ - * malloc , free の処理を効率的に出来るようにしたもの。 - * 複雑な処理を行っているので、若干重くなるかもしれません。 - * - * データ構造など(説明下手ですいません^^; ) - * ・メモリを複数の「ブロック」に分けて、さらにブロックを複数の「ユニット」 - * に分けています。ユニットのサイズは、1ブロックの容量を複数個に均等配分 - * したものです。たとえば、1ユニット32KBの場合、ブロック1つは32Byteのユ - * ニットが、1024個集まって出来ていたり、64Byteのユニットが 512個集まって - * 出来ていたりします。(padding,unit_head を除く) - * - * ・ブロック同士はリンクリスト(block_prev,block_next) でつながり、同じサイ - * ズを持つブロック同士もリンクリスト(samesize_prev,samesize_nect) でつな - * がっています。それにより、不要となったメモリの再利用が効率的に行えます。 - */ - -/* ブロックに入るデータ量 */ -#define BLOCK_DATA_SIZE 80*1024 - -/* 一度に確保するブロックの数。 */ -#define BLOCK_ALLOC 32 - -/* ブロックのアライメント */ -#define BLOCK_ALIGNMENT 64 - -/* ブロック */ -struct block { - int block_no; /* ブロック番号 */ - struct block* block_prev; /* 前に確保した領域 */ - struct block* block_next; /* 次に確保した領域 */ - int samesize_no; /* 同じサイズの番号 */ - struct block* samesize_prev; /* 同じサイズの前の領域 */ - struct block* samesize_next; /* 同じサイズの次の領域 */ - size_t unit_size; /* ユニットのバイト数 0=未使用 */ - size_t unit_hash; /* ユニットのハッシュ */ - int unit_count; /* ユニットの数 */ - int unit_used; /* 使用済みユニット */ - char data[BLOCK_DATA_SIZE]; -}; - -struct unit_head { - struct block* block; - size_t size; - const char* file; - int line; - unsigned int checksum; -}; - -struct chunk { - char *block; - struct chunk *next; -}; - -static struct block* block_first = NULL; -static struct block* block_last = NULL; -static struct block* block_unused = NULL; - -/* ユニットへのハッシュ。80KB/64Byte = 1280個 */ -static struct block* unit_first[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 最初 */ -static struct block* unit_unfill[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 埋まってない */ -static struct block* unit_last[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 最後 */ - -/* メモリを使い回せない領域用のデータ */ -struct unit_head_large { - struct unit_head_large* prev; - struct unit_head_large* next; - struct unit_head unit_head; -}; -static struct unit_head_large *unit_head_large_first = NULL; - -static struct chunk *chunk_first = NULL; - -static struct block* block_malloc(void); -static void block_free(struct block* p); -static void memmgr_info(void); -static unsigned int memmgr_usage_bytes = 0; - -void* _mmalloc(size_t size, const char *file, int line, const char *func ) { - int i; - struct block *block; - size_t size_hash; - - if (((long) size) < 0) { - printf("_mmalloc: %d\n", size); - return 0; - } - - size_hash = (size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT; - if(size == 0) { - return NULL; - } - memmgr_usage_bytes += size; - - /* ブロック長を超える領域の確保には、malloc() を用いる */ - /* その際、unit_head.block に NULL を代入して区別する */ - if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) { - struct unit_head_large* p = (struct unit_head_large*)MALLOC(sizeof(struct unit_head_large)+size,file,line,func); - if(p != NULL) { - p->unit_head.block = NULL; - p->unit_head.size = size; - p->unit_head.file = file; - p->unit_head.line = line; - p->prev = NULL; - if (unit_head_large_first == NULL) - p->next = NULL; - else { - unit_head_large_first->prev = p; - p->next = unit_head_large_first; - } - unit_head_large_first = p; - *(int*)((char*)p + sizeof(struct unit_head_large) - sizeof(int) + size) = 0xdeadbeaf; - return (char *)p + sizeof(struct unit_head_large) - sizeof(int); - } else { - ShowFatalError("Memory manager::memmgr_alloc failed (allocating %d+%d bytes at %s:%d).\n", sizeof(struct unit_head_large), size, file, line); - exit(1); - } - } - - /* 同一サイズのブロックが確保されていない時、新たに確保する */ - if(unit_unfill[size_hash] == NULL) { - block = block_malloc(); - if(unit_first[size_hash] == NULL) { - /* 初回確保 */ - unit_first[size_hash] = block; - unit_last[size_hash] = block; - block->samesize_no = 0; - block->samesize_prev = NULL; - block->samesize_next = NULL; - } else { - /* 連結作業 */ - unit_last[size_hash]->samesize_next = block; - block->samesize_no = unit_last[size_hash]->samesize_no + 1; - block->samesize_prev = unit_last[size_hash]; - block->samesize_next = NULL; - unit_last[size_hash] = block; - } - unit_unfill[size_hash] = block; - block->unit_size = size_hash * BLOCK_ALIGNMENT + sizeof(struct unit_head); - block->unit_count = (int)(BLOCK_DATA_SIZE / block->unit_size); - block->unit_used = 0; - block->unit_hash = size_hash; - /* 未使用Flagを立てる */ - for(i=0;iunit_count;i++) { - ((struct unit_head*)(&block->data[block->unit_size * i]))->block = NULL; - } - } - /* ユニット使用個数加算 */ - block = unit_unfill[size_hash]; - block->unit_used++; - - /* ユニット内を全て使い果たした */ - if(block->unit_count == block->unit_used) { - do { - unit_unfill[size_hash] = unit_unfill[size_hash]->samesize_next; - } while( - unit_unfill[size_hash] != NULL && - unit_unfill[size_hash]->unit_count == unit_unfill[size_hash]->unit_used - ); - } - - /* ブロックの中の空きユニット捜索 */ - for(i=0;iunit_count;i++) { - struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]); - if(head->block == NULL) { - head->block = block; - head->size = size; - head->line = line; - head->file = file; - *(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + size) = 0xdeadbeaf; - return (char *)head + sizeof(struct unit_head) - sizeof(int); - } - } - // ここに来てはいけない。 - ShowFatalError("Memory manager::memmgr_malloc() serious error (allocating %d+%d bytes at %s:%d)\n", sizeof(struct unit_head_large), size, file, line); - memmgr_info(); - exit(1); - return NULL; -}; - -void* _mcalloc(size_t num, size_t size, const char *file, int line, const char *func ) { - void *p = _mmalloc(num * size,file,line,func); - //malloc_set(p,0,num * size); - malloc_set(p,0,num*size); - return p; -} - -void* _mrealloc(void *memblock, size_t size, const char *file, int line, const char *func ) { - size_t old_size; - if(memblock == NULL) { - return _mmalloc(size,file,line,func); - } - - old_size = ((struct unit_head *)((char *)memblock - sizeof(struct unit_head) + sizeof(int)))->size; - if(old_size > size) { - // サイズ縮小 -> そのまま返す(手抜き) - return memblock; - } else { - // サイズ拡大 - void *p = _mmalloc(size,file,line,func); - if(p != NULL) { - memcpy(p,memblock,old_size); - } - _mfree(memblock,file,line,func); - return p; - } -} - -char* _mstrdup(const char *p, const char *file, int line, const char *func ) { - if(p == NULL) { - return NULL; - } else { - size_t len = strlen(p); - char *string = (char *)_mmalloc(len + 1,file,line,func); - memcpy(string,p,len+1); - return string; - } -} - -void _mfree(void *ptr, const char *file, int line, const char *func ) { - struct unit_head *head; - size_t size_hash; - - if (ptr == NULL) - return; - - head = (struct unit_head *)((char *)ptr - sizeof(struct unit_head) + sizeof(int)); - size_hash = (head->size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT; - - if(head->block == NULL) { - if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) { - /* malloc() で直に確保された領域 */ - struct unit_head_large *head_large = (struct unit_head_large *)((char *)ptr - sizeof(struct unit_head_large) + sizeof(int)); - if( - *(int*)((char*)head_large + sizeof(struct unit_head_large) - sizeof(int) + head->size) - != 0xdeadbeaf) - { - ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line); - } - if(head_large->prev) { - head_large->prev->next = head_large->next; - } else { - unit_head_large_first = head_large->next; - } - if(head_large->next) { - head_large->next->prev = head_large->prev; - } - head->block = NULL; - memmgr_usage_bytes -= head->size; - FREE(head_large,file,line,func); - } else { - ShowError("Memory manager: args of aFree is freed pointer %s:%d@%s\n", file, line, func); - } - ptr = NULL; - return; - } else { - /* ユニット解放 */ - struct block *block = head->block; - if((unsigned long)block % sizeof(struct block) != 0) { - ShowError("Memory manager: args of aFree is not valid pointer %s line %d\n", file, line); - } else if(*(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + head->size) != 0xdeadbeaf) { - ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line); - } else { - head->block = NULL; - memmgr_usage_bytes -= head->size; - if(--block->unit_used == 0) { - /* ブロックの解放 */ - if(unit_unfill[block->unit_hash] == block) { - /* 空きユニットに指定されている */ - do { - unit_unfill[block->unit_hash] = unit_unfill[block->unit_hash]->samesize_next; - } while( - unit_unfill[block->unit_hash] != NULL && - unit_unfill[block->unit_hash]->unit_count == unit_unfill[block->unit_hash]->unit_used - ); - } - if(block->samesize_prev == NULL && block->samesize_next == NULL) { - /* 独立ブロックの解放 */ - unit_first[block->unit_hash] = NULL; - unit_last[block->unit_hash] = NULL; - unit_unfill[block->unit_hash] = NULL; - } else if(block->samesize_prev == NULL) { - /* 先頭ブロックの解放 */ - unit_first[block->unit_hash] = block->samesize_next; - (block->samesize_next)->samesize_prev = NULL; - } else if(block->samesize_next == NULL) { - /* 末端ブロックの解放 */ - unit_last[block->unit_hash] = block->samesize_prev; - (block->samesize_prev)->samesize_next = NULL; - } else { - /* 中間ブロックの解放 */ - (block->samesize_next)->samesize_prev = block->samesize_prev; - (block->samesize_prev)->samesize_next = block->samesize_next; - } - block_free(block); - } else { - /* 空きユニットの再設定 */ - if( - unit_unfill[block->unit_hash] == NULL || - unit_unfill[block->unit_hash]->samesize_no > block->samesize_no - ) { - unit_unfill[block->unit_hash] = block; - } - } - ptr = NULL; - } - } -} - -/* 現在の状況を表示する */ -static void memmgr_info(void) { - int i; - struct block *p; - ShowInfo("** Memory Manager Information **\n"); - if(block_first == NULL) { - ShowMessage("Uninitialized.\n"); - return; - } - ShowMessage( - "Blocks: %04u , BlockSize: %06u Byte , Used: %08uKB\n", - block_last->block_no+1,sizeof(struct block), - (block_last->block_no+1) * sizeof(struct block) / 1024 - ); - p = block_first; - for(i=0;i<=block_last->block_no;i++) { - ShowMessage(" Block #%04u : ",p->block_no); - if(p->unit_size == 0) { - ShowMessage("unused.\n"); - } else { - ShowMessage( - "size: %05u byte. used: %04u/%04u prev:", - p->unit_size - sizeof(struct unit_head),p->unit_used,p->unit_count - ); - if(p->samesize_prev == NULL) { - ShowMessage("NULL"); - } else { - ShowMessage("%04u",(p->samesize_prev)->block_no); - } - ShowMessage(" next:"); - if(p->samesize_next == NULL) { - ShowMessage("NULL"); - } else { - ShowMessage("%04u",(p->samesize_next)->block_no); - } - ShowMessage("\n"); - } - p = p->block_next; - } -} - -/* ブロックを確保する */ -static struct block* block_malloc(void) { - if(block_unused != NULL) { - /* ブロック用の領域は確保済み */ - struct block* ret = block_unused; - do { - block_unused = block_unused->block_next; - } while(block_unused != NULL && block_unused->unit_size != 0); - return ret; - } else { - /* ブロック用の領域を新たに確保する */ - int i; - int block_no; - struct block* p; - struct chunk* chunk; - char *pb = (char *)CALLOC(sizeof(struct block),BLOCK_ALLOC+1,file,line,func); - if(pb == NULL) { - ShowFatalError("Memory manager::block_alloc failed.\n"); - exit(1); - } - - // store original block address in chunk - chunk = (struct chunk *)MALLOC(sizeof(struct chunk),file,line,func); - if (chunk == NULL) { - ShowFatalError("Memory manager::block_alloc failed.\n"); - exit(1); - } - chunk->block = pb; - chunk->next = (chunk_first) ? chunk_first : NULL; - chunk_first = chunk; - - // ブロックのポインタの先頭をsizeof(block) アライメントに揃える - // このアドレスをfree() することはないので、直接ポインタを変更している。 - pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block)); - p = (struct block*)pb; - if(block_first == NULL) { - /* 初回確保 */ - block_no = 0; - block_first = p; - } else { - block_no = block_last->block_no + 1; - block_last->block_next = p; - p->block_prev = block_last; - } - block_last = &p[BLOCK_ALLOC - 1]; - /* ブロックを連結させる */ - for(i=0;iunit_size = 1; - return p; - } -} - -static void block_free(struct block* p) { - /* free() せずに、未使用フラグを付けるだけ */ - p->unit_size = 0; - /* 未使用ポインターを更新する */ - if(block_unused == NULL) { - block_unused = p; - } else if(block_unused->block_no > p->block_no) { - block_unused = p; - } -} - -unsigned int memmgr_usage (void) -{ - return memmgr_usage_bytes / 1024; -} - -#ifdef LOG_MEMMGR -static char memmer_logfile[128]; -static FILE *log_fp; - -static void memmgr_log (char *buf) -{ - if (!log_fp) { - log_fp = fopen(memmer_logfile,"w"); - if (!log_fp) log_fp = stdout; - fprintf(log_fp, "Memory manager: Memory leaks found (Revision %s).\n", get_svn_revision()); - } - fprintf(log_fp, buf); - return; -} -#endif - -static void memmgr_final (void) -{ - struct block *block = block_first; - struct chunk *chunk = chunk_first, *chunk2; - struct unit_head_large *large = unit_head_large_first, *large2; - int i; - -#ifdef LOG_MEMMGR - int count = 0; - char buf[128]; -#endif - - while (block) { - if (block->unit_size) { - for (i = 0; i < block->unit_count; i++) { - struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]); - if(head->block != NULL) - { - #ifdef LOG_MEMMGR - sprintf (buf, - "%04d : %s line %d size %d\n", ++count, - head->file, head->line, head->size); - memmgr_log (buf); - #endif - // get block pointer and free it [celest] - _mfree ((char *)head + sizeof(struct unit_head) - sizeof(int), ALC_MARK); - } - } - } - //if (block->block_no >= block2->block_no + BLOCK_ALLOC - 1) { - // reached a new block array - //block = block->block_next; - - /* Okay wise guys... this is how block2 was allocated: [Skotlex] - struct block* p; - char *pb = (char *) CALLOC (sizeof(struct block),BLOCK_ALLOC + 1); - pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block)); - p = (struct block*)pb; - - The reason we get an invalid pointer is that we allocated pb, not p. - So how do you get pb when you only have p? - The answer is, you can't, because the original pointer was lost when - memory-aligning the block. So we either forget this FREE or use a - self-reference... - Since we are already quitting, it might be ok to just not free the block - as it is. - */ - // didn't realise that before o.o -- block chunks are now freed below [celest] - // FREE(block2); - //block2 = block; - //continue; - //} - block = block->block_next; - } - - // free the allocated block chunks - chunk = chunk_first; - while (chunk) { - chunk2 = chunk->next; - FREE(chunk->block,file,line,func); - FREE(chunk,file,line,func); - chunk = chunk2; - } - - while(large) { - large2 = large->next; - #ifdef LOG_MEMMGR - sprintf (buf, - "%04d : %s line %d size %d\n", ++count, - large->unit_head.file, large->unit_head.line, large->unit_head.size); - memmgr_log (buf); - #endif - FREE(large,file,line,func); - large = large2; - } -#ifdef LOG_MEMMGR - if(count == 0) { - ShowInfo("Memory manager: No memory leaks found.\n"); - } else { - ShowWarning("Memory manager: Memory leaks found and fixed.\n"); - fclose(log_fp); - } -#endif - return; -} - -static void memmgr_init (void) -{ - #ifdef LOG_MEMMGR - sprintf(memmer_logfile, "log/%s.leaks", SERVER_NAME); - ShowStatus("Memory manager initialised: "CL_WHITE"%s"CL_RESET"\n", memmer_logfile); - #endif - return; -} -#endif - -#if defined(MEMSET_TURBO) && defined(_WIN32) - void malloc_set(void *dest, int value, int count){ - _asm - { - mov eax, value - mov ecx, count - mov ebx, ecx - mov edi, dest - shr ecx, 2 - test ecx, ecx - jz ByteOp - shl ecx, 2 - sub ebx, ecx - shr ecx, 2 - rep stosd - test ebx, ebx - jz Done - ByteOp: - mov ecx, ebx - rep stosb - Done: - } - } - // Sets 32-bit aligned memory. - void malloc_tsetdword(void *dest, int value, int count){ - _asm - { - mov edi, dest - mov ecx, count - shr ecx, 2 - mov eax, value - rep stosd - } - } - - // Sets 16-bit aligned memory. - void malloc_tsetword(void *dest, short value, int count){ - _asm - { - mov edi, dest - mov ecx, count - shr ecx, 1 - mov ax, value - rep stosw - } - } -#endif - -/*====================================== - * Initialise - *-------------------------------------- - */ - -unsigned int malloc_usage (void) -{ -#ifdef USE_MEMMGR - return memmgr_usage (); -#else - return 0; -#endif -} - -void malloc_final (void) -{ -#ifdef USE_MEMMGR - memmgr_final (); -#endif - return; -} - -void malloc_init (void) -{ -#ifdef USE_MEMMGR - memmgr_init (); -#endif - return; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include "../common/malloc.h" +#include "../common/core.h" +#include "../common/showmsg.h" + +#ifdef MINICORE + #undef LOG_MEMMGR +#endif + +void* aMalloc_(size_t size, const char *file, int line, const char *func) +{ + void *ret = MALLOC(size, file, line, func); + // ShowMessage("%s:%d: in func %s: aMalloc %d\n",file,line,func,size); + if (ret == NULL){ + ShowFatalError("%s:%d: in func %s: aMalloc error out of memory!\n",file,line,func); + exit(1); + } + + return ret; +} +void* aMallocA_(size_t size, const char *file, int line, const char *func) +{ + void *ret = MALLOCA(size, file, line, func); + // ShowMessage("%s:%d: in func %s: aMallocA %d\n",file,line,func,size); + if (ret == NULL){ + ShowFatalError("%s:%d: in func %s: aMallocA error out of memory!\n",file,line,func); + exit(1); + } + + return ret; +} +void* aCalloc_(size_t num, size_t size, const char *file, int line, const char *func) +{ + void *ret = CALLOC(num, size, file, line, func); + // ShowMessage("%s:%d: in func %s: aCalloc %d %d\n",file,line,func,num,size); + if (ret == NULL){ + ShowFatalError("%s:%d: in func %s: aCalloc error out of memory!\n", file, line, func); + exit(1); + } + return ret; +} +void* aCallocA_(size_t num, size_t size, const char *file, int line, const char *func) +{ + void *ret = CALLOCA(num, size, file, line, func); + // ShowMessage("%s:%d: in func %s: aCallocA %d %d\n",file,line,func,num,size); + if (ret == NULL){ + ShowFatalError("%s:%d: in func %s: aCallocA error out of memory!\n",file,line,func); + exit(1); + } + return ret; +} +void* aRealloc_(void *p, size_t size, const char *file, int line, const char *func) +{ + void *ret = REALLOC(p, size, file, line, func); + // ShowMessage("%s:%d: in func %s: aRealloc %p %d\n",file,line,func,p,size); + if (ret == NULL){ + ShowFatalError("%s:%d: in func %s: aRealloc error out of memory!\n",file,line,func); + exit(1); + } + return ret; +} +char* aStrdup_(const char *p, const char *file, int line, const char *func) +{ + char *ret = STRDUP(p, file, line, func); + // ShowMessage("%s:%d: in func %s: aStrdup %p\n",file,line,func,p); + if (ret == NULL){ + ShowFatalError("%s:%d: in func %s: aStrdup error out of memory!\n", file, line, func); + exit(1); + } + return ret; +} +void aFree_(void *p, const char *file, int line, const char *func) +{ + // ShowMessage("%s:%d: in func %s: aFree %p\n",file,line,func,p); + if (p) + FREE(p, file, line, func); + + p = NULL; +} + +#ifdef GCOLLECT + +void* _bcallocA(size_t size, size_t cnt) +{ + void *ret = MALLOCA(size * cnt); + if (ret) //malloc_set(ret, 0, size * cnt); + malloc_set(ret, 0, size*cnt); + return ret; +} +void* _bcalloc(size_t size, size_t cnt) +{ + void *ret = MALLOC(size * cnt); + if (ret) //malloc_set(ret, 0, size * cnt); + malloc_set(ret, 0, size*cnt); + return ret; +} +char* _bstrdup(const char *chr) +{ + int len = strlen(chr); + char *ret = (char*)MALLOC(len + 1); + if (ret) memcpy(ret, chr, len + 1); + return ret; +} + +#endif + +#ifdef USE_MEMMGR + +/* USE_MEMMGR */ + +/* + * メモリマネージャ + * malloc , free の処理を効率的に出来るようにしたもの。 + * 複雑な処理を行っているので、若干重くなるかもしれません。 + * + * データ構造など(説明下手ですいません^^; ) + * ・メモリを複数の「ブロック」に分けて、さらにブロックを複数の「ユニット」 + * に分けています。ユニットのサイズは、1ブロックの容量を複数個に均等配分 + * したものです。たとえば、1ユニット32KBの場合、ブロック1つは32Byteのユ + * ニットが、1024個集まって出来ていたり、64Byteのユニットが 512個集まって + * 出来ていたりします。(padding,unit_head を除く) + * + * ・ブロック同士はリンクリスト(block_prev,block_next) でつながり、同じサイ + * ズを持つブロック同士もリンクリスト(samesize_prev,samesize_nect) でつな + * がっています。それにより、不要となったメモリの再利用が効率的に行えます。 + */ + +/* ブロックに入るデータ量 */ +#define BLOCK_DATA_SIZE 80*1024 + +/* 一度に確保するブロックの数。 */ +#define BLOCK_ALLOC 32 + +/* ブロックのアライメント */ +#define BLOCK_ALIGNMENT 64 + +/* ブロック */ +struct block { + int block_no; /* ブロック番号 */ + struct block* block_prev; /* 前に確保した領域 */ + struct block* block_next; /* 次に確保した領域 */ + int samesize_no; /* 同じサイズの番号 */ + struct block* samesize_prev; /* 同じサイズの前の領域 */ + struct block* samesize_next; /* 同じサイズの次の領域 */ + size_t unit_size; /* ユニットのバイト数 0=未使用 */ + size_t unit_hash; /* ユニットのハッシュ */ + int unit_count; /* ユニットの数 */ + int unit_used; /* 使用済みユニット */ + char data[BLOCK_DATA_SIZE]; +}; + +struct unit_head { + struct block* block; + size_t size; + const char* file; + int line; + unsigned int checksum; +}; + +struct chunk { + char *block; + struct chunk *next; +}; + +static struct block* block_first = NULL; +static struct block* block_last = NULL; +static struct block* block_unused = NULL; + +/* ユニットへのハッシュ。80KB/64Byte = 1280個 */ +static struct block* unit_first[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 最初 */ +static struct block* unit_unfill[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 埋まってない */ +static struct block* unit_last[BLOCK_DATA_SIZE/BLOCK_ALIGNMENT]; /* 最後 */ + +/* メモリを使い回せない領域用のデータ */ +struct unit_head_large { + struct unit_head_large* prev; + struct unit_head_large* next; + struct unit_head unit_head; +}; +static struct unit_head_large *unit_head_large_first = NULL; + +static struct chunk *chunk_first = NULL; + +static struct block* block_malloc(void); +static void block_free(struct block* p); +static void memmgr_info(void); +static unsigned int memmgr_usage_bytes = 0; + +void* _mmalloc(size_t size, const char *file, int line, const char *func ) { + int i; + struct block *block; + size_t size_hash; + + if (((long) size) < 0) { + printf("_mmalloc: %d\n", size); + return 0; + } + + size_hash = (size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT; + if(size == 0) { + return NULL; + } + memmgr_usage_bytes += size; + + /* ブロック長を超える領域の確保には、malloc() を用いる */ + /* その際、unit_head.block に NULL を代入して区別する */ + if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) { + struct unit_head_large* p = (struct unit_head_large*)MALLOC(sizeof(struct unit_head_large)+size,file,line,func); + if(p != NULL) { + p->unit_head.block = NULL; + p->unit_head.size = size; + p->unit_head.file = file; + p->unit_head.line = line; + p->prev = NULL; + if (unit_head_large_first == NULL) + p->next = NULL; + else { + unit_head_large_first->prev = p; + p->next = unit_head_large_first; + } + unit_head_large_first = p; + *(int*)((char*)p + sizeof(struct unit_head_large) - sizeof(int) + size) = 0xdeadbeaf; + return (char *)p + sizeof(struct unit_head_large) - sizeof(int); + } else { + ShowFatalError("Memory manager::memmgr_alloc failed (allocating %d+%d bytes at %s:%d).\n", sizeof(struct unit_head_large), size, file, line); + exit(1); + } + } + + /* 同一サイズのブロックが確保されていない時、新たに確保する */ + if(unit_unfill[size_hash] == NULL) { + block = block_malloc(); + if(unit_first[size_hash] == NULL) { + /* 初回確保 */ + unit_first[size_hash] = block; + unit_last[size_hash] = block; + block->samesize_no = 0; + block->samesize_prev = NULL; + block->samesize_next = NULL; + } else { + /* 連結作業 */ + unit_last[size_hash]->samesize_next = block; + block->samesize_no = unit_last[size_hash]->samesize_no + 1; + block->samesize_prev = unit_last[size_hash]; + block->samesize_next = NULL; + unit_last[size_hash] = block; + } + unit_unfill[size_hash] = block; + block->unit_size = size_hash * BLOCK_ALIGNMENT + sizeof(struct unit_head); + block->unit_count = (int)(BLOCK_DATA_SIZE / block->unit_size); + block->unit_used = 0; + block->unit_hash = size_hash; + /* 未使用Flagを立てる */ + for(i=0;iunit_count;i++) { + ((struct unit_head*)(&block->data[block->unit_size * i]))->block = NULL; + } + } + /* ユニット使用個数加算 */ + block = unit_unfill[size_hash]; + block->unit_used++; + + /* ユニット内を全て使い果たした */ + if(block->unit_count == block->unit_used) { + do { + unit_unfill[size_hash] = unit_unfill[size_hash]->samesize_next; + } while( + unit_unfill[size_hash] != NULL && + unit_unfill[size_hash]->unit_count == unit_unfill[size_hash]->unit_used + ); + } + + /* ブロックの中の空きユニット捜索 */ + for(i=0;iunit_count;i++) { + struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]); + if(head->block == NULL) { + head->block = block; + head->size = size; + head->line = line; + head->file = file; + *(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + size) = 0xdeadbeaf; + return (char *)head + sizeof(struct unit_head) - sizeof(int); + } + } + // ここに来てはいけない。 + ShowFatalError("Memory manager::memmgr_malloc() serious error (allocating %d+%d bytes at %s:%d)\n", sizeof(struct unit_head_large), size, file, line); + memmgr_info(); + exit(1); + return NULL; +}; + +void* _mcalloc(size_t num, size_t size, const char *file, int line, const char *func ) { + void *p = _mmalloc(num * size,file,line,func); + //malloc_set(p,0,num * size); + malloc_set(p,0,num*size); + return p; +} + +void* _mrealloc(void *memblock, size_t size, const char *file, int line, const char *func ) { + size_t old_size; + if(memblock == NULL) { + return _mmalloc(size,file,line,func); + } + + old_size = ((struct unit_head *)((char *)memblock - sizeof(struct unit_head) + sizeof(int)))->size; + if(old_size > size) { + // サイズ縮小 -> そのまま返す(手抜き) + return memblock; + } else { + // サイズ拡大 + void *p = _mmalloc(size,file,line,func); + if(p != NULL) { + memcpy(p,memblock,old_size); + } + _mfree(memblock,file,line,func); + return p; + } +} + +char* _mstrdup(const char *p, const char *file, int line, const char *func ) { + if(p == NULL) { + return NULL; + } else { + size_t len = strlen(p); + char *string = (char *)_mmalloc(len + 1,file,line,func); + memcpy(string,p,len+1); + return string; + } +} + +void _mfree(void *ptr, const char *file, int line, const char *func ) { + struct unit_head *head; + size_t size_hash; + + if (ptr == NULL) + return; + + head = (struct unit_head *)((char *)ptr - sizeof(struct unit_head) + sizeof(int)); + size_hash = (head->size+BLOCK_ALIGNMENT-1) / BLOCK_ALIGNMENT; + + if(head->block == NULL) { + if(size_hash * BLOCK_ALIGNMENT > BLOCK_DATA_SIZE - sizeof(struct unit_head)) { + /* malloc() で直に確保された領域 */ + struct unit_head_large *head_large = (struct unit_head_large *)((char *)ptr - sizeof(struct unit_head_large) + sizeof(int)); + if( + *(int*)((char*)head_large + sizeof(struct unit_head_large) - sizeof(int) + head->size) + != 0xdeadbeaf) + { + ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line); + } + if(head_large->prev) { + head_large->prev->next = head_large->next; + } else { + unit_head_large_first = head_large->next; + } + if(head_large->next) { + head_large->next->prev = head_large->prev; + } + head->block = NULL; + memmgr_usage_bytes -= head->size; + FREE(head_large,file,line,func); + } else { + ShowError("Memory manager: args of aFree is freed pointer %s:%d@%s\n", file, line, func); + } + ptr = NULL; + return; + } else { + /* ユニット解放 */ + struct block *block = head->block; + if((unsigned long)block % sizeof(struct block) != 0) { + ShowError("Memory manager: args of aFree is not valid pointer %s line %d\n", file, line); + } else if(*(int*)((char*)head + sizeof(struct unit_head) - sizeof(int) + head->size) != 0xdeadbeaf) { + ShowError("Memory manager: args of aFree is overflowed pointer %s line %d\n", file, line); + } else { + head->block = NULL; + memmgr_usage_bytes -= head->size; + if(--block->unit_used == 0) { + /* ブロックの解放 */ + if(unit_unfill[block->unit_hash] == block) { + /* 空きユニットに指定されている */ + do { + unit_unfill[block->unit_hash] = unit_unfill[block->unit_hash]->samesize_next; + } while( + unit_unfill[block->unit_hash] != NULL && + unit_unfill[block->unit_hash]->unit_count == unit_unfill[block->unit_hash]->unit_used + ); + } + if(block->samesize_prev == NULL && block->samesize_next == NULL) { + /* 独立ブロックの解放 */ + unit_first[block->unit_hash] = NULL; + unit_last[block->unit_hash] = NULL; + unit_unfill[block->unit_hash] = NULL; + } else if(block->samesize_prev == NULL) { + /* 先頭ブロックの解放 */ + unit_first[block->unit_hash] = block->samesize_next; + (block->samesize_next)->samesize_prev = NULL; + } else if(block->samesize_next == NULL) { + /* 末端ブロックの解放 */ + unit_last[block->unit_hash] = block->samesize_prev; + (block->samesize_prev)->samesize_next = NULL; + } else { + /* 中間ブロックの解放 */ + (block->samesize_next)->samesize_prev = block->samesize_prev; + (block->samesize_prev)->samesize_next = block->samesize_next; + } + block_free(block); + } else { + /* 空きユニットの再設定 */ + if( + unit_unfill[block->unit_hash] == NULL || + unit_unfill[block->unit_hash]->samesize_no > block->samesize_no + ) { + unit_unfill[block->unit_hash] = block; + } + } + ptr = NULL; + } + } +} + +/* 現在の状況を表示する */ +static void memmgr_info(void) { + int i; + struct block *p; + ShowInfo("** Memory Manager Information **\n"); + if(block_first == NULL) { + ShowMessage("Uninitialized.\n"); + return; + } + ShowMessage( + "Blocks: %04u , BlockSize: %06u Byte , Used: %08uKB\n", + block_last->block_no+1,sizeof(struct block), + (block_last->block_no+1) * sizeof(struct block) / 1024 + ); + p = block_first; + for(i=0;i<=block_last->block_no;i++) { + ShowMessage(" Block #%04u : ",p->block_no); + if(p->unit_size == 0) { + ShowMessage("unused.\n"); + } else { + ShowMessage( + "size: %05u byte. used: %04u/%04u prev:", + p->unit_size - sizeof(struct unit_head),p->unit_used,p->unit_count + ); + if(p->samesize_prev == NULL) { + ShowMessage("NULL"); + } else { + ShowMessage("%04u",(p->samesize_prev)->block_no); + } + ShowMessage(" next:"); + if(p->samesize_next == NULL) { + ShowMessage("NULL"); + } else { + ShowMessage("%04u",(p->samesize_next)->block_no); + } + ShowMessage("\n"); + } + p = p->block_next; + } +} + +/* ブロックを確保する */ +static struct block* block_malloc(void) { + if(block_unused != NULL) { + /* ブロック用の領域は確保済み */ + struct block* ret = block_unused; + do { + block_unused = block_unused->block_next; + } while(block_unused != NULL && block_unused->unit_size != 0); + return ret; + } else { + /* ブロック用の領域を新たに確保する */ + int i; + int block_no; + struct block* p; + struct chunk* chunk; + char *pb = (char *)CALLOC(sizeof(struct block),BLOCK_ALLOC+1,file,line,func); + if(pb == NULL) { + ShowFatalError("Memory manager::block_alloc failed.\n"); + exit(1); + } + + // store original block address in chunk + chunk = (struct chunk *)MALLOC(sizeof(struct chunk),file,line,func); + if (chunk == NULL) { + ShowFatalError("Memory manager::block_alloc failed.\n"); + exit(1); + } + chunk->block = pb; + chunk->next = (chunk_first) ? chunk_first : NULL; + chunk_first = chunk; + + // ブロックのポインタの先頭をsizeof(block) アライメントに揃える + // このアドレスをfree() することはないので、直接ポインタを変更している。 + pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block)); + p = (struct block*)pb; + if(block_first == NULL) { + /* 初回確保 */ + block_no = 0; + block_first = p; + } else { + block_no = block_last->block_no + 1; + block_last->block_next = p; + p->block_prev = block_last; + } + block_last = &p[BLOCK_ALLOC - 1]; + /* ブロックを連結させる */ + for(i=0;iunit_size = 1; + return p; + } +} + +static void block_free(struct block* p) { + /* free() せずに、未使用フラグを付けるだけ */ + p->unit_size = 0; + /* 未使用ポインターを更新する */ + if(block_unused == NULL) { + block_unused = p; + } else if(block_unused->block_no > p->block_no) { + block_unused = p; + } +} + +unsigned int memmgr_usage (void) +{ + return memmgr_usage_bytes / 1024; +} + +#ifdef LOG_MEMMGR +static char memmer_logfile[128]; +static FILE *log_fp; + +static void memmgr_log (char *buf) +{ + if (!log_fp) { + log_fp = fopen(memmer_logfile,"w"); + if (!log_fp) log_fp = stdout; + fprintf(log_fp, "Memory manager: Memory leaks found (Revision %s).\n", get_svn_revision()); + } + fprintf(log_fp, buf); + return; +} +#endif + +static void memmgr_final (void) +{ + struct block *block = block_first; + struct chunk *chunk = chunk_first, *chunk2; + struct unit_head_large *large = unit_head_large_first, *large2; + int i; + +#ifdef LOG_MEMMGR + int count = 0; + char buf[128]; +#endif + + while (block) { + if (block->unit_size) { + for (i = 0; i < block->unit_count; i++) { + struct unit_head *head = (struct unit_head*)(&block->data[block->unit_size * i]); + if(head->block != NULL) + { + #ifdef LOG_MEMMGR + sprintf (buf, + "%04d : %s line %d size %d\n", ++count, + head->file, head->line, head->size); + memmgr_log (buf); + #endif + // get block pointer and free it [celest] + _mfree ((char *)head + sizeof(struct unit_head) - sizeof(int), ALC_MARK); + } + } + } + //if (block->block_no >= block2->block_no + BLOCK_ALLOC - 1) { + // reached a new block array + //block = block->block_next; + + /* Okay wise guys... this is how block2 was allocated: [Skotlex] + struct block* p; + char *pb = (char *) CALLOC (sizeof(struct block),BLOCK_ALLOC + 1); + pb += sizeof(struct block) - ((unsigned long)pb % sizeof(struct block)); + p = (struct block*)pb; + + The reason we get an invalid pointer is that we allocated pb, not p. + So how do you get pb when you only have p? + The answer is, you can't, because the original pointer was lost when + memory-aligning the block. So we either forget this FREE or use a + self-reference... + Since we are already quitting, it might be ok to just not free the block + as it is. + */ + // didn't realise that before o.o -- block chunks are now freed below [celest] + // FREE(block2); + //block2 = block; + //continue; + //} + block = block->block_next; + } + + // free the allocated block chunks + chunk = chunk_first; + while (chunk) { + chunk2 = chunk->next; + FREE(chunk->block,file,line,func); + FREE(chunk,file,line,func); + chunk = chunk2; + } + + while(large) { + large2 = large->next; + #ifdef LOG_MEMMGR + sprintf (buf, + "%04d : %s line %d size %d\n", ++count, + large->unit_head.file, large->unit_head.line, large->unit_head.size); + memmgr_log (buf); + #endif + FREE(large,file,line,func); + large = large2; + } +#ifdef LOG_MEMMGR + if(count == 0) { + ShowInfo("Memory manager: No memory leaks found.\n"); + } else { + ShowWarning("Memory manager: Memory leaks found and fixed.\n"); + fclose(log_fp); + } +#endif + return; +} + +static void memmgr_init (void) +{ + #ifdef LOG_MEMMGR + sprintf(memmer_logfile, "log/%s.leaks", SERVER_NAME); + ShowStatus("Memory manager initialised: "CL_WHITE"%s"CL_RESET"\n", memmer_logfile); + #endif + return; +} +#endif + +#if defined(MEMSET_TURBO) && defined(_WIN32) + void malloc_set(void *dest, int value, int count){ + _asm + { + mov eax, value + mov ecx, count + mov ebx, ecx + mov edi, dest + shr ecx, 2 + test ecx, ecx + jz ByteOp + shl ecx, 2 + sub ebx, ecx + shr ecx, 2 + rep stosd + test ebx, ebx + jz Done + ByteOp: + mov ecx, ebx + rep stosb + Done: + } + } + // Sets 32-bit aligned memory. + void malloc_tsetdword(void *dest, int value, int count){ + _asm + { + mov edi, dest + mov ecx, count + shr ecx, 2 + mov eax, value + rep stosd + } + } + + // Sets 16-bit aligned memory. + void malloc_tsetword(void *dest, short value, int count){ + _asm + { + mov edi, dest + mov ecx, count + shr ecx, 1 + mov ax, value + rep stosw + } + } +#endif + +/*====================================== + * Initialise + *-------------------------------------- + */ + +unsigned int malloc_usage (void) +{ +#ifdef USE_MEMMGR + return memmgr_usage (); +#else + return 0; +#endif +} + +void malloc_final (void) +{ +#ifdef USE_MEMMGR + memmgr_final (); +#endif + return; +} + +void malloc_init (void) +{ +#ifdef USE_MEMMGR + memmgr_init (); +#endif + return; +} diff --git a/src/common/malloc.h b/src/common/malloc.h index f65593eb1..503eba75b 100644 --- a/src/common/malloc.h +++ b/src/common/malloc.h @@ -1,178 +1,178 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _MALLOC_H_ -#define _MALLOC_H_ -// Q: What are the 'a'-variant allocation functions? -// A: They allocate memory from the stack, which is automatically -// freed when the invoking function returns. -// But it's not portable (http://c-faq.com/malloc/alloca.html) -// and I have doubts our implementation works. -// -> They should NOT be used, period. - -#define MEMSET_TURBO - -#ifndef __NETBSD__ -#if __STDC_VERSION__ < 199901L -# if __GNUC__ >= 2 -# define __func__ __FUNCTION__ -# else -# define __func__ "" -# endif -#endif -#endif -#define ALC_MARK __FILE__, __LINE__, __func__ - -////////////////////////////////////////////////////////////////////// -// Whether to use Athena's built-in Memory Manager (enabled by default) -// To disable just comment the following line -#if !defined(DMALLOC) && !defined(BCHECK) - #define USE_MEMMGR -#endif -// Whether to enable Memory Manager's logging -#define LOG_MEMMGR - -#ifdef USE_MEMMGR - -# define aMalloc(n) _mmalloc(n,ALC_MARK) -# define aMallocA(n) _mmalloc(n,ALC_MARK) -# define aCalloc(m,n) _mcalloc(m,n,ALC_MARK) -# define aCallocA(m,n) _mcalloc(m,n,ALC_MARK) -# define aRealloc(p,n) _mrealloc(p,n,ALC_MARK) -# define aStrdup(p) _mstrdup(p,ALC_MARK) -# define aFree(p) _mfree(p,ALC_MARK) - - void* _mmalloc (size_t size, const char *file, int line, const char *func); - void* _mcalloc (size_t num, size_t size, const char *file, int line, const char *func); - void* _mrealloc (void *p, size_t size, const char *file, int line, const char *func); - char* _mstrdup (const char *p, const char *file, int line, const char *func); - void _mfree (void *p, const char *file, int line, const char *func); - -#else - -# define aMalloc(n) aMalloc_((n),ALC_MARK) -# define aMallocA(n) aMallocA_((n),ALC_MARK) -# define aCalloc(m,n) aCalloc_((m),(n),ALC_MARK) -# define aCallocA(m,n) aCallocA_(m,n,ALC_MARK) -# define aRealloc(p,n) aRealloc_(p,n,ALC_MARK) -# define aStrdup(p) aStrdup_(p,ALC_MARK) -# define aFree(p) aFree_(p,ALC_MARK) - - void* aMalloc_ (size_t size, const char *file, int line, const char *func); - void* aMallocA_ (size_t size, const char *file, int line, const char *func); - void* aCalloc_ (size_t num, size_t size, const char *file, int line, const char *func); - void* aCallocA_ (size_t num, size_t size, const char *file, int line, const char *func); - void* aRealloc_ (void *p, size_t size, const char *file, int line, const char *func); - char* aStrdup_ (const char *p, const char *file, int line, const char *func); - void aFree_ (void *p, const char *file, int line, const char *func); - -#endif - -////////////// Memory Managers ////////////////// - -#ifdef MEMWATCH - -# include "memwatch.h" -# define MALLOC(n,file,line,func) mwMalloc((n),(file),(line)) -# define MALLOCA(n,file,line,func) mwMalloc((n),(file),(line)) -# define CALLOC(m,n,file,line,func) mwCalloc((m),(n),(file),(line)) -# define CALLOCA(m,n,file,line,func) mwCalloc((m),(n),(file),(line)) -# define REALLOC(p,n,file,line,func) mwRealloc((p),(n),(file),(line)) -# define STRDUP(p,file,line,func) mwStrdup((p),(file),(line)) -# define FREE(p,file,line,func) mwFree((p),(file),(line)) - -#elif defined(DMALLOC) - -# include "dmalloc.h" -# define MALLOC(n,file,line,func) dmalloc_malloc((file),(line),(n),DMALLOC_FUNC_MALLOC,0,0) -# define MALLOCA(n,file,line,func) dmalloc_malloc((file),(line),(n),DMALLOC_FUNC_MALLOC,0,0) -# define CALLOC(m,n,file,line,func) dmalloc_malloc((file),(line),(m)*(n),DMALLOC_FUNC_CALLOC,0,0) -# define CALLOCA(m,n,file,line,func) dmalloc_malloc((file),(line),(m)*(n),DMALLOC_FUNC_CALLOC,0,0) -# define REALLOC(p,n,file,line,func) dmalloc_realloc((file),(line),(p),(n),DMALLOC_FUNC_REALLOC,0) -# define STRDUP(p,file,line,func) strdup(p) -# define FREE(p,file,line,func) free(p) - -#elif defined(GCOLLECT) - -# include "gc.h" -# define MALLOC(n,file,line,func) GC_MALLOC(n) -# define MALLOCA(n,file,line,func) GC_MALLOC_ATOMIC(n) -# define CALLOC(m,n,file,line,func) _bcalloc((m),(n)) -# define CALLOCA(m,n,file,line,func) _bcallocA((m),(n)) -# define REALLOC(p,n,file,line,func) GC_REALLOC((p),(n)) -# define STRDUP(p,file,line,func) _bstrdup(p) -# define FREE(p,file,line,func) GC_FREE(p) - - void * _bcalloc(size_t, size_t); - void * _bcallocA(size_t, size_t); - char * _bstrdup(const char *); - -/* FIXME Why is this the same as #else? [FlavioJS] -#elif defined(BCHECK) - -# define MALLOC(n,file,line,func) malloc(n) -# define MALLOCA(n,file,line,func) malloc(n) -# define CALLOC(m,n,file,line,func) calloc((m),(n)) -# define CALLOCA(m,n,file,line,func) calloc((m),(n)) -# define REALLOC(p,n,file,line,func) realloc((p),(n)) -# define STRDUP(p,file,line,func) strdup(p) -# define FREE(p,file,line,func) free(p) -*/ -#else - -# define MALLOC(n,file,line,func) malloc(n) -# define MALLOCA(n,file,line,func) malloc(n) -# define CALLOC(m,n,file,line,func) calloc((m),(n)) -# define CALLOCA(m,n,file,line,func) calloc((m),(n)) -# define REALLOC(p,n,file,line,func) realloc((p),(n)) -# define STRDUP(p,file,line,func) strdup(p) -# define FREE(p,file,line,func) free(p) - -#endif - -/////////////// Buffer Creation ///////////////// -// Full credit for this goes to Shinomori [Ajarn] - -#ifdef __GNUC__ // GCC has variable length arrays - - #define CREATE_BUFFER(name, type, size) type name[size] - #define DELETE_BUFFER(name) - -#else // others don't, so we emulate them - - #define CREATE_BUFFER(name, type, size) type *name = (type *) aCalloc (size, sizeof(type)) - #define DELETE_BUFFER(name) aFree(name) - -#endif - -////////////// Others ////////////////////////// -// should be merged with any of above later -#define CREATE(result, type, number) (result) = (type *) aCalloc ((number), sizeof(type)); - -#define CREATE_A(result, type, number) (result) = (type *) aCallocA ((number), sizeof(type)); - -#define RECREATE(result, type, number) (result) = (type *) aRealloc ((result), sizeof(type) * (number)); - -//////////////////////////////////////////////// - -unsigned int malloc_usage (void); -#ifndef INLINE - #ifdef _WIN32 - #define INLINE - #else - #define INLINE inline - #endif -#endif -#if defined(MEMSET_TURBO) && defined(_WIN32) - INLINE void malloc_set(void *, int, int); - INLINE void malloc_tsetdword(void *, int, int); - INLINE void malloc_tsetword(void *, short, int); -#else - #define malloc_set(x,y,z) memset(x,y,z) - #define malloc_tsetdword(x,y,z) memset(x,y,z) - #define malloc_tsetword(x,y,z) memset(x,y,z) -#endif -void malloc_init (void); -void malloc_final (void); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _MALLOC_H_ +#define _MALLOC_H_ +// Q: What are the 'a'-variant allocation functions? +// A: They allocate memory from the stack, which is automatically +// freed when the invoking function returns. +// But it's not portable (http://c-faq.com/malloc/alloca.html) +// and I have doubts our implementation works. +// -> They should NOT be used, period. + +#define MEMSET_TURBO + +#ifndef __NETBSD__ +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +#endif +#endif +#define ALC_MARK __FILE__, __LINE__, __func__ + +////////////////////////////////////////////////////////////////////// +// Whether to use Athena's built-in Memory Manager (enabled by default) +// To disable just comment the following line +#if !defined(DMALLOC) && !defined(BCHECK) + #define USE_MEMMGR +#endif +// Whether to enable Memory Manager's logging +#define LOG_MEMMGR + +#ifdef USE_MEMMGR + +# define aMalloc(n) _mmalloc(n,ALC_MARK) +# define aMallocA(n) _mmalloc(n,ALC_MARK) +# define aCalloc(m,n) _mcalloc(m,n,ALC_MARK) +# define aCallocA(m,n) _mcalloc(m,n,ALC_MARK) +# define aRealloc(p,n) _mrealloc(p,n,ALC_MARK) +# define aStrdup(p) _mstrdup(p,ALC_MARK) +# define aFree(p) _mfree(p,ALC_MARK) + + void* _mmalloc (size_t size, const char *file, int line, const char *func); + void* _mcalloc (size_t num, size_t size, const char *file, int line, const char *func); + void* _mrealloc (void *p, size_t size, const char *file, int line, const char *func); + char* _mstrdup (const char *p, const char *file, int line, const char *func); + void _mfree (void *p, const char *file, int line, const char *func); + +#else + +# define aMalloc(n) aMalloc_((n),ALC_MARK) +# define aMallocA(n) aMallocA_((n),ALC_MARK) +# define aCalloc(m,n) aCalloc_((m),(n),ALC_MARK) +# define aCallocA(m,n) aCallocA_(m,n,ALC_MARK) +# define aRealloc(p,n) aRealloc_(p,n,ALC_MARK) +# define aStrdup(p) aStrdup_(p,ALC_MARK) +# define aFree(p) aFree_(p,ALC_MARK) + + void* aMalloc_ (size_t size, const char *file, int line, const char *func); + void* aMallocA_ (size_t size, const char *file, int line, const char *func); + void* aCalloc_ (size_t num, size_t size, const char *file, int line, const char *func); + void* aCallocA_ (size_t num, size_t size, const char *file, int line, const char *func); + void* aRealloc_ (void *p, size_t size, const char *file, int line, const char *func); + char* aStrdup_ (const char *p, const char *file, int line, const char *func); + void aFree_ (void *p, const char *file, int line, const char *func); + +#endif + +////////////// Memory Managers ////////////////// + +#ifdef MEMWATCH + +# include "memwatch.h" +# define MALLOC(n,file,line,func) mwMalloc((n),(file),(line)) +# define MALLOCA(n,file,line,func) mwMalloc((n),(file),(line)) +# define CALLOC(m,n,file,line,func) mwCalloc((m),(n),(file),(line)) +# define CALLOCA(m,n,file,line,func) mwCalloc((m),(n),(file),(line)) +# define REALLOC(p,n,file,line,func) mwRealloc((p),(n),(file),(line)) +# define STRDUP(p,file,line,func) mwStrdup((p),(file),(line)) +# define FREE(p,file,line,func) mwFree((p),(file),(line)) + +#elif defined(DMALLOC) + +# include "dmalloc.h" +# define MALLOC(n,file,line,func) dmalloc_malloc((file),(line),(n),DMALLOC_FUNC_MALLOC,0,0) +# define MALLOCA(n,file,line,func) dmalloc_malloc((file),(line),(n),DMALLOC_FUNC_MALLOC,0,0) +# define CALLOC(m,n,file,line,func) dmalloc_malloc((file),(line),(m)*(n),DMALLOC_FUNC_CALLOC,0,0) +# define CALLOCA(m,n,file,line,func) dmalloc_malloc((file),(line),(m)*(n),DMALLOC_FUNC_CALLOC,0,0) +# define REALLOC(p,n,file,line,func) dmalloc_realloc((file),(line),(p),(n),DMALLOC_FUNC_REALLOC,0) +# define STRDUP(p,file,line,func) strdup(p) +# define FREE(p,file,line,func) free(p) + +#elif defined(GCOLLECT) + +# include "gc.h" +# define MALLOC(n,file,line,func) GC_MALLOC(n) +# define MALLOCA(n,file,line,func) GC_MALLOC_ATOMIC(n) +# define CALLOC(m,n,file,line,func) _bcalloc((m),(n)) +# define CALLOCA(m,n,file,line,func) _bcallocA((m),(n)) +# define REALLOC(p,n,file,line,func) GC_REALLOC((p),(n)) +# define STRDUP(p,file,line,func) _bstrdup(p) +# define FREE(p,file,line,func) GC_FREE(p) + + void * _bcalloc(size_t, size_t); + void * _bcallocA(size_t, size_t); + char * _bstrdup(const char *); + +/* FIXME Why is this the same as #else? [FlavioJS] +#elif defined(BCHECK) + +# define MALLOC(n,file,line,func) malloc(n) +# define MALLOCA(n,file,line,func) malloc(n) +# define CALLOC(m,n,file,line,func) calloc((m),(n)) +# define CALLOCA(m,n,file,line,func) calloc((m),(n)) +# define REALLOC(p,n,file,line,func) realloc((p),(n)) +# define STRDUP(p,file,line,func) strdup(p) +# define FREE(p,file,line,func) free(p) +*/ +#else + +# define MALLOC(n,file,line,func) malloc(n) +# define MALLOCA(n,file,line,func) malloc(n) +# define CALLOC(m,n,file,line,func) calloc((m),(n)) +# define CALLOCA(m,n,file,line,func) calloc((m),(n)) +# define REALLOC(p,n,file,line,func) realloc((p),(n)) +# define STRDUP(p,file,line,func) strdup(p) +# define FREE(p,file,line,func) free(p) + +#endif + +/////////////// Buffer Creation ///////////////// +// Full credit for this goes to Shinomori [Ajarn] + +#ifdef __GNUC__ // GCC has variable length arrays + + #define CREATE_BUFFER(name, type, size) type name[size] + #define DELETE_BUFFER(name) + +#else // others don't, so we emulate them + + #define CREATE_BUFFER(name, type, size) type *name = (type *) aCalloc (size, sizeof(type)) + #define DELETE_BUFFER(name) aFree(name) + +#endif + +////////////// Others ////////////////////////// +// should be merged with any of above later +#define CREATE(result, type, number) (result) = (type *) aCalloc ((number), sizeof(type)); + +#define CREATE_A(result, type, number) (result) = (type *) aCallocA ((number), sizeof(type)); + +#define RECREATE(result, type, number) (result) = (type *) aRealloc ((result), sizeof(type) * (number)); + +//////////////////////////////////////////////// + +unsigned int malloc_usage (void); +#ifndef INLINE + #ifdef _WIN32 + #define INLINE + #else + #define INLINE inline + #endif +#endif +#if defined(MEMSET_TURBO) && defined(_WIN32) + INLINE void malloc_set(void *, int, int); + INLINE void malloc_tsetdword(void *, int, int); + INLINE void malloc_tsetword(void *, short, int); +#else + #define malloc_set(x,y,z) memset(x,y,z) + #define malloc_tsetdword(x,y,z) memset(x,y,z) + #define malloc_tsetword(x,y,z) memset(x,y,z) +#endif +void malloc_init (void); +void malloc_final (void); + +#endif diff --git a/src/common/nullpo.c b/src/common/nullpo.c index 8508f1333..931846bd6 100644 --- a/src/common/nullpo.c +++ b/src/common/nullpo.c @@ -1,94 +1,94 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include "nullpo.h" -#include "../common/showmsg.h" -// #include "logs.h" // 布石してみる - -static void nullpo_info_core(const char *file, int line, const char *func, - const char *fmt, va_list ap); - -/*====================================== - * Nullチェック 及び 情報出力 - *-------------------------------------- - */ -int nullpo_chk_f(const char *file, int line, const char *func, const void *target, - const char *fmt, ...) -{ - va_list ap; - - if (target != NULL) - return 0; - - va_start(ap, fmt); - nullpo_info_core(file, line, func, fmt, ap); - va_end(ap); - return 1; -} - -int nullpo_chk(const char *file, int line, const char *func, const void *target) -{ - if (target != NULL) - return 0; - - nullpo_info_core(file, line, func, NULL, NULL); - return 1; -} - - -/*====================================== - * nullpo情報出力(外部呼出し向けラッパ) - *-------------------------------------- - */ -void nullpo_info_f(const char *file, int line, const char *func, - const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - nullpo_info_core(file, line, func, fmt, ap); - va_end(ap); -} - -void nullpo_info(const char *file, int line, const char *func) -{ - nullpo_info_core(file, line, func, NULL, NULL); -} - - -/*====================================== - * nullpo情報出力(Main) - *-------------------------------------- - */ -static void nullpo_info_core(const char *file, int line, const char *func, - const char *fmt, va_list ap) -{ - if (file == NULL) - file = "??"; - - func = - func == NULL ? "unknown": - func[0] == '\0' ? "unknown": - func; - - ShowMessage("--- nullpo info --------------------------------------------\n"); - ShowMessage("%s:%d: in func `%s'\n", file, line, func); - if (fmt != NULL) - { - if (fmt[0] != '\0') - { - vprintf(fmt, ap); - - // 最後に改行したか確認 - if (fmt[strlen(fmt)-1] != '\n') - ShowMessage("\n"); - } - } - ShowMessage("--- end nullpo info ----------------------------------------\n"); - - // ここらでnullpoログをファイルに書き出せたら - // まとめて提出できるなと思っていたり。 -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include "nullpo.h" +#include "../common/showmsg.h" +// #include "logs.h" // 布石してみる + +static void nullpo_info_core(const char *file, int line, const char *func, + const char *fmt, va_list ap); + +/*====================================== + * Nullチェック 及び 情報出力 + *-------------------------------------- + */ +int nullpo_chk_f(const char *file, int line, const char *func, const void *target, + const char *fmt, ...) +{ + va_list ap; + + if (target != NULL) + return 0; + + va_start(ap, fmt); + nullpo_info_core(file, line, func, fmt, ap); + va_end(ap); + return 1; +} + +int nullpo_chk(const char *file, int line, const char *func, const void *target) +{ + if (target != NULL) + return 0; + + nullpo_info_core(file, line, func, NULL, NULL); + return 1; +} + + +/*====================================== + * nullpo情報出力(外部呼出し向けラッパ) + *-------------------------------------- + */ +void nullpo_info_f(const char *file, int line, const char *func, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + nullpo_info_core(file, line, func, fmt, ap); + va_end(ap); +} + +void nullpo_info(const char *file, int line, const char *func) +{ + nullpo_info_core(file, line, func, NULL, NULL); +} + + +/*====================================== + * nullpo情報出力(Main) + *-------------------------------------- + */ +static void nullpo_info_core(const char *file, int line, const char *func, + const char *fmt, va_list ap) +{ + if (file == NULL) + file = "??"; + + func = + func == NULL ? "unknown": + func[0] == '\0' ? "unknown": + func; + + ShowMessage("--- nullpo info --------------------------------------------\n"); + ShowMessage("%s:%d: in func `%s'\n", file, line, func); + if (fmt != NULL) + { + if (fmt[0] != '\0') + { + vprintf(fmt, ap); + + // 最後に改行したか確認 + if (fmt[strlen(fmt)-1] != '\n') + ShowMessage("\n"); + } + } + ShowMessage("--- end nullpo info ----------------------------------------\n"); + + // ここらでnullpoログをファイルに書き出せたら + // まとめて提出できるなと思っていたり。 +} diff --git a/src/common/nullpo.h b/src/common/nullpo.h index 66d984224..d5ccb8f95 100644 --- a/src/common/nullpo.h +++ b/src/common/nullpo.h @@ -1,237 +1,237 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _NULLPO_H_ -#define _NULLPO_H_ - - -#define NULLPO_CHECK 1 - // 全体のスイッチを宣言しているヘッダがあれば - // そこに移動していただけると - -#ifndef __NETBSD__ -#if __STDC_VERSION__ < 199901L -# if __GNUC__ >= 2 -# define __func__ __FUNCTION__ -# else -# define __func__ "" -# endif -#endif -#endif - -#ifdef _WIN32 -#define __attribute__(x) /* nothing */ -#endif - - -#define NLP_MARK __FILE__, __LINE__, __func__ - -/*---------------------------------------------------------------------------- - * Macros - *---------------------------------------------------------------------------- - */ -/*====================================== - * Nullチェック 及び 情報出力後 return - *・展開するとifとかreturn等が出るので - * 一行単体で使ってください。 - *・nullpo_ret(x = func()); - * のような使用法も想定しています。 - *-------------------------------------- - * nullpo_ret(t) - * 戻り値 0固定 - * [引数] - * t チェック対象 - *-------------------------------------- - * nullpo_retv(t) - * 戻り値 なし - * [引数] - * t チェック対象 - *-------------------------------------- - * nullpo_retr(ret, t) - * 戻り値 指定 - * [引数] - * ret return(ret); - * t チェック対象 - *-------------------------------------- - * nullpo_ret_f(t, fmt, ...) - * 詳細情報出力用 - * 戻り値 0 - * [引数] - * t チェック対象 - * fmt ... vprintfに渡される - * 備考や関係変数の書き出しなどに - *-------------------------------------- - * nullpo_retv_f(t, fmt, ...) - * 詳細情報出力用 - * 戻り値 なし - * [引数] - * t チェック対象 - * fmt ... vprintfに渡される - * 備考や関係変数の書き出しなどに - *-------------------------------------- - * nullpo_retr_f(ret, t, fmt, ...) - * 詳細情報出力用 - * 戻り値 指定 - * [引数] - * ret return(ret); - * t チェック対象 - * fmt ... vprintfに渡される - * 備考や関係変数の書き出しなどに - *-------------------------------------- - */ - -#if NULLPO_CHECK - -#define nullpo_ret(t) \ - if (nullpo_chk(NLP_MARK, (void *)(t))) {return(0);} - -#define nullpo_retv(t) \ - if (nullpo_chk(NLP_MARK, (void *)(t))) {return;} - -#define nullpo_retr(ret, t) \ - if (nullpo_chk(NLP_MARK, (void *)(t))) {return(ret);} - -#define nullpo_retb(t) \ - if (nullpo_chk(NLP_MARK, (void *)(t))) {break;} - -// 可変引数マクロに関する条件コンパイル -#if __STDC_VERSION__ >= 199901L -/* C99に対応 */ -#define nullpo_ret_f(t, fmt, ...) \ - if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(0);} - -#define nullpo_retv_f(t, fmt, ...) \ - if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return;} - -#define nullpo_retr_f(ret, t, fmt, ...) \ - if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(ret);} - -#define nullpo_retb_f(t, fmt, ...) \ - if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {break;} - -#elif __GNUC__ >= 2 -/* GCC用 */ -#define nullpo_ret_f(t, fmt, args...) \ - if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(0);} - -#define nullpo_retv_f(t, fmt, args...) \ - if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return;} - -#define nullpo_retr_f(ret, t, fmt, args...) \ - if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(ret);} - -#define nullpo_retb_f(t, fmt, args...) \ - if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {break;} - -#else - -/* その他の場合・・・ orz */ - -#endif - -#else /* NULLPO_CHECK */ -/* No Nullpo check */ - -// if((t)){;} -// 良い方法が思いつかなかったので・・・苦肉の策です。 -// 一応ワーニングは出ないはず - -#define nullpo_ret(t) if((t)){;} -#define nullpo_retv(t) if((t)){;} -#define nullpo_retr(ret, t) if((t)){;} -#define nullpo_retb(t) if((t)){;} - -// 可変引数マクロに関する条件コンパイル -#if __STDC_VERSION__ >= 199901L -/* C99に対応 */ -#define nullpo_ret_f(t, fmt, ...) if((t)){;} -#define nullpo_retv_f(t, fmt, ...) if((t)){;} -#define nullpo_retr_f(ret, t, fmt, ...) if((t)){;} -#define nullpo_retb_f(t, fmt, ...) if((t)){;} - -#elif __GNUC__ >= 2 -/* GCC用 */ -#define nullpo_ret_f(t, fmt, args...) if((t)){;} -#define nullpo_retv_f(t, fmt, args...) if((t)){;} -#define nullpo_retr_f(ret, t, fmt, args...) if((t)){;} -#define nullpo_retb_f(t, fmt, args...) if((t)){;} - -#else -/* その他の場合・・・ orz */ -#endif - -#endif /* NULLPO_CHECK */ - -/*---------------------------------------------------------------------------- - * Functions - *---------------------------------------------------------------------------- - */ -/*====================================== - * nullpo_chk - * Nullチェック 及び 情報出力 - * [引数] - * file __FILE__ - * line __LINE__ - * func __func__ (関数名) - * これらには NLP_MARK を使うとよい - * target チェック対象 - * [返り値] - * 0 OK - * 1 NULL - *-------------------------------------- - */ -int nullpo_chk(const char *file, int line, const char *func, const void *target); - - -/*====================================== - * nullpo_chk_f - * Nullチェック 及び 詳細な情報出力 - * [引数] - * file __FILE__ - * line __LINE__ - * func __func__ (関数名) - * これらには NLP_MARK を使うとよい - * target チェック対象 - * fmt ... vprintfに渡される - * 備考や関係変数の書き出しなどに - * [返り値] - * 0 OK - * 1 NULL - *-------------------------------------- - */ -int nullpo_chk_f(const char *file, int line, const char *func, const void *target, - const char *fmt, ...) - __attribute__((format(printf,5,6))); - - -/*====================================== - * nullpo_info - * nullpo情報出力 - * [引数] - * file __FILE__ - * line __LINE__ - * func __func__ (関数名) - * これらには NLP_MARK を使うとよい - *-------------------------------------- - */ -void nullpo_info(const char *file, int line, const char *func); - - -/*====================================== - * nullpo_info_f - * nullpo詳細情報出力 - * [引数] - * file __FILE__ - * line __LINE__ - * func __func__ (関数名) - * これらには NLP_MARK を使うとよい - * fmt ... vprintfに渡される - * 備考や関係変数の書き出しなどに - *-------------------------------------- - */ -void nullpo_info_f(const char *file, int line, const char *func, - const char *fmt, ...) - __attribute__((format(printf,4,5))); - - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _NULLPO_H_ +#define _NULLPO_H_ + + +#define NULLPO_CHECK 1 + // 全体のスイッチを宣言しているヘッダがあれば + // そこに移動していただけると + +#ifndef __NETBSD__ +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +#endif +#endif + +#ifdef _WIN32 +#define __attribute__(x) /* nothing */ +#endif + + +#define NLP_MARK __FILE__, __LINE__, __func__ + +/*---------------------------------------------------------------------------- + * Macros + *---------------------------------------------------------------------------- + */ +/*====================================== + * Nullチェック 及び 情報出力後 return + *・展開するとifとかreturn等が出るので + * 一行単体で使ってください。 + *・nullpo_ret(x = func()); + * のような使用法も想定しています。 + *-------------------------------------- + * nullpo_ret(t) + * 戻り値 0固定 + * [引数] + * t チェック対象 + *-------------------------------------- + * nullpo_retv(t) + * 戻り値 なし + * [引数] + * t チェック対象 + *-------------------------------------- + * nullpo_retr(ret, t) + * 戻り値 指定 + * [引数] + * ret return(ret); + * t チェック対象 + *-------------------------------------- + * nullpo_ret_f(t, fmt, ...) + * 詳細情報出力用 + * 戻り値 0 + * [引数] + * t チェック対象 + * fmt ... vprintfに渡される + * 備考や関係変数の書き出しなどに + *-------------------------------------- + * nullpo_retv_f(t, fmt, ...) + * 詳細情報出力用 + * 戻り値 なし + * [引数] + * t チェック対象 + * fmt ... vprintfに渡される + * 備考や関係変数の書き出しなどに + *-------------------------------------- + * nullpo_retr_f(ret, t, fmt, ...) + * 詳細情報出力用 + * 戻り値 指定 + * [引数] + * ret return(ret); + * t チェック対象 + * fmt ... vprintfに渡される + * 備考や関係変数の書き出しなどに + *-------------------------------------- + */ + +#if NULLPO_CHECK + +#define nullpo_ret(t) \ + if (nullpo_chk(NLP_MARK, (void *)(t))) {return(0);} + +#define nullpo_retv(t) \ + if (nullpo_chk(NLP_MARK, (void *)(t))) {return;} + +#define nullpo_retr(ret, t) \ + if (nullpo_chk(NLP_MARK, (void *)(t))) {return(ret);} + +#define nullpo_retb(t) \ + if (nullpo_chk(NLP_MARK, (void *)(t))) {break;} + +// 可変引数マクロに関する条件コンパイル +#if __STDC_VERSION__ >= 199901L +/* C99に対応 */ +#define nullpo_ret_f(t, fmt, ...) \ + if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(0);} + +#define nullpo_retv_f(t, fmt, ...) \ + if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return;} + +#define nullpo_retr_f(ret, t, fmt, ...) \ + if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {return(ret);} + +#define nullpo_retb_f(t, fmt, ...) \ + if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), __VA_ARGS__)) {break;} + +#elif __GNUC__ >= 2 +/* GCC用 */ +#define nullpo_ret_f(t, fmt, args...) \ + if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(0);} + +#define nullpo_retv_f(t, fmt, args...) \ + if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return;} + +#define nullpo_retr_f(ret, t, fmt, args...) \ + if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {return(ret);} + +#define nullpo_retb_f(t, fmt, args...) \ + if (nullpo_chk_f(NLP_MARK, (void *)(t), (fmt), ## args)) {break;} + +#else + +/* その他の場合・・・ orz */ + +#endif + +#else /* NULLPO_CHECK */ +/* No Nullpo check */ + +// if((t)){;} +// 良い方法が思いつかなかったので・・・苦肉の策です。 +// 一応ワーニングは出ないはず + +#define nullpo_ret(t) if((t)){;} +#define nullpo_retv(t) if((t)){;} +#define nullpo_retr(ret, t) if((t)){;} +#define nullpo_retb(t) if((t)){;} + +// 可変引数マクロに関する条件コンパイル +#if __STDC_VERSION__ >= 199901L +/* C99に対応 */ +#define nullpo_ret_f(t, fmt, ...) if((t)){;} +#define nullpo_retv_f(t, fmt, ...) if((t)){;} +#define nullpo_retr_f(ret, t, fmt, ...) if((t)){;} +#define nullpo_retb_f(t, fmt, ...) if((t)){;} + +#elif __GNUC__ >= 2 +/* GCC用 */ +#define nullpo_ret_f(t, fmt, args...) if((t)){;} +#define nullpo_retv_f(t, fmt, args...) if((t)){;} +#define nullpo_retr_f(ret, t, fmt, args...) if((t)){;} +#define nullpo_retb_f(t, fmt, args...) if((t)){;} + +#else +/* その他の場合・・・ orz */ +#endif + +#endif /* NULLPO_CHECK */ + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------- + */ +/*====================================== + * nullpo_chk + * Nullチェック 及び 情報出力 + * [引数] + * file __FILE__ + * line __LINE__ + * func __func__ (関数名) + * これらには NLP_MARK を使うとよい + * target チェック対象 + * [返り値] + * 0 OK + * 1 NULL + *-------------------------------------- + */ +int nullpo_chk(const char *file, int line, const char *func, const void *target); + + +/*====================================== + * nullpo_chk_f + * Nullチェック 及び 詳細な情報出力 + * [引数] + * file __FILE__ + * line __LINE__ + * func __func__ (関数名) + * これらには NLP_MARK を使うとよい + * target チェック対象 + * fmt ... vprintfに渡される + * 備考や関係変数の書き出しなどに + * [返り値] + * 0 OK + * 1 NULL + *-------------------------------------- + */ +int nullpo_chk_f(const char *file, int line, const char *func, const void *target, + const char *fmt, ...) + __attribute__((format(printf,5,6))); + + +/*====================================== + * nullpo_info + * nullpo情報出力 + * [引数] + * file __FILE__ + * line __LINE__ + * func __func__ (関数名) + * これらには NLP_MARK を使うとよい + *-------------------------------------- + */ +void nullpo_info(const char *file, int line, const char *func); + + +/*====================================== + * nullpo_info_f + * nullpo詳細情報出力 + * [引数] + * file __FILE__ + * line __LINE__ + * func __func__ (関数名) + * これらには NLP_MARK を使うとよい + * fmt ... vprintfに渡される + * 備考や関係変数の書き出しなどに + *-------------------------------------- + */ +void nullpo_info_f(const char *file, int line, const char *func, + const char *fmt, ...) + __attribute__((format(printf,4,5))); + + +#endif diff --git a/src/common/plugin.h b/src/common/plugin.h index 2ccefb6bd..402636b1d 100644 --- a/src/common/plugin.h +++ b/src/common/plugin.h @@ -1,40 +1,40 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _PLUGIN_H_ -#define _PLUGIN_H_ - -////// Plugin functions /////////////// - -#define PLUGIN_VERSION "1.02" - -typedef struct _Plugin_Info { - char *name; - char type; - char *version; - char *req_version; - char *description; -} Plugin_Info; - -typedef struct _Plugin_Event_Table { - char *func_name; - char *event_name; -} Plugin_Event_Table; - -////// Plugin Export functions ///////////// - -#define PLUGIN_ALL 0 -#define PLUGIN_LOGIN 1 -#define PLUGIN_CHAR 2 -#define PLUGIN_MAP 8 -#define PLUGIN_CORE 16 - -#define IMPORT_SYMBOL(s,n) (s) = plugin_call_table[n] - -////// Global Plugin variables ///////////// - -#define PLUGIN_INFO struct _Plugin_Info plugin_info -#define PLUGIN_EVENTS_TABLE struct _Plugin_Event_Table plugin_event_table[] -void **plugin_call_table; - -#endif // _PLUGIN_H_ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _PLUGIN_H_ +#define _PLUGIN_H_ + +////// Plugin functions /////////////// + +#define PLUGIN_VERSION "1.02" + +typedef struct _Plugin_Info { + char *name; + char type; + char *version; + char *req_version; + char *description; +} Plugin_Info; + +typedef struct _Plugin_Event_Table { + char *func_name; + char *event_name; +} Plugin_Event_Table; + +////// Plugin Export functions ///////////// + +#define PLUGIN_ALL 0 +#define PLUGIN_LOGIN 1 +#define PLUGIN_CHAR 2 +#define PLUGIN_MAP 8 +#define PLUGIN_CORE 16 + +#define IMPORT_SYMBOL(s,n) (s) = plugin_call_table[n] + +////// Global Plugin variables ///////////// + +#define PLUGIN_INFO struct _Plugin_Info plugin_info +#define PLUGIN_EVENTS_TABLE struct _Plugin_Event_Table plugin_event_table[] +void **plugin_call_table; + +#endif // _PLUGIN_H_ diff --git a/src/common/plugins.c b/src/common/plugins.c index a057190b9..5951885a1 100644 --- a/src/common/plugins.c +++ b/src/common/plugins.c @@ -1,367 +1,367 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#ifndef _WIN32 -#include -#endif - -#include "plugin.h" -#include "plugins.h" -#include "../common/mmo.h" -#include "../common/core.h" -#include "../common/timer.h" -#include "../common/utils.h" -#include "../common/socket.h" -#include "../common/malloc.h" -#include "../common/version.h" -#include "../common/showmsg.h" - -////////////////////////////////////////////// - -typedef struct _Plugin_Event { - void (*func)(void); - struct _Plugin_Event *next; -} Plugin_Event; - -typedef struct _Plugin_Event_List { - char *name; - struct _Plugin_Event_List *next; - struct _Plugin_Event *events; -} Plugin_Event_List; - -static int auto_search = 1; -static int load_priority = 0; -Plugin_Event_List *event_head = NULL; -Plugin *plugin_head = NULL; - -Plugin_Info default_info = { "Unknown", PLUGIN_ALL, "0", PLUGIN_VERSION, "Unknown" }; - -static size_t call_table_size = 0; -static size_t max_call_table = 0; - -////// Plugin Events Functions ////////////////// - -int register_plugin_func (char *name) -{ - Plugin_Event_List *evl; - if (name) { - evl = (Plugin_Event_List *) aMalloc(sizeof(Plugin_Event_List)); - evl->name = (char *) aMalloc (strlen(name) + 1); - - evl->next = event_head; - strcpy(evl->name, name); - evl->events = NULL; - event_head = evl; - } - return 0; -} - -Plugin_Event_List *search_plugin_func (char *name) -{ - Plugin_Event_List *evl = event_head; - while (evl) { - if (strcmpi(evl->name, name) == 0) - return evl; - evl = evl->next; - } - return NULL; -} - -int register_plugin_event (void (*func)(void), char* name) -{ - Plugin_Event_List *evl = search_plugin_func(name); - if (!evl) { - // register event if it doesn't exist already - register_plugin_func(name); - // relocate the new event list - evl = search_plugin_func(name); - } - if (evl) { - Plugin_Event *ev; - - ev = (Plugin_Event *) aMalloc(sizeof(Plugin_Event)); - ev->func = func; - ev->next = NULL; - - if (evl->events == NULL) - evl->events = ev; - else { - Plugin_Event *ev2 = evl->events; - while (ev2) { - if (ev2->next == NULL) { - ev2->next = ev; - break; - } - ev2 = ev2->next; - } - } - } - return 0; -} - -int plugin_event_trigger (char *name) -{ - int c = 0; - Plugin_Event_List *evl = search_plugin_func(name); - if (evl) { - Plugin_Event *ev = evl->events; - while (ev) { - ev->func(); - ev = ev->next; - c++; - } - } - return c; -} - -////// Plugins Call Table Functions ///////// - -int export_symbol (void *var, int offset) -{ - //printf ("0x%x\n", var); - - // add to the end of the list - if (offset < 0) - offset = call_table_size; - - // realloc if not large enough - if ((size_t)offset >= max_call_table) { - max_call_table = 1 + offset; - plugin_call_table = (void**)aRealloc(plugin_call_table, max_call_table*sizeof(void*)); - - // clear the new alloced block - malloc_tsetdword(plugin_call_table + call_table_size, 0, (max_call_table-call_table_size)*sizeof(void*)); - } - - // the new table size is delimited by the new element at the end - if ((size_t)offset >= call_table_size) - call_table_size = offset+1; - - // put the pointer at the selected place - plugin_call_table[offset] = var; - return 0; -} - -////// Plugins Core ///////////////////////// - -Plugin *plugin_open (const char *filename) -{ - Plugin *plugin; - Plugin_Info *info; - Plugin_Event_Table *events; - void **procs; - int init_flag = 1; - - //printf ("loading %s\n", filename); - - // Check if the plugin has been loaded before - plugin = plugin_head; - while (plugin) { - // returns handle to the already loaded plugin - if (plugin->state && strcmpi(plugin->filename, filename) == 0) { - //printf ("not loaded (duplicate) : %s\n", filename); - return plugin; - } - plugin = plugin->next; - } - - plugin = (Plugin *)aCalloc(1, sizeof(Plugin)); - plugin->state = -1; // not loaded - - plugin->dll = DLL_OPEN(filename); - if (!plugin->dll) { - //printf ("not loaded (invalid file) : %s\n", filename); - plugin_unload(plugin); - return NULL; - } - - // Retrieve plugin information - plugin->state = 0; // initialising - DLL_SYM (info, plugin->dll, "plugin_info"); - // For high priority plugins (those that are explicitly loaded from the conf file) - // we'll ignore them even (could be a 3rd party dll file) - if ((!info && load_priority == 0) || - (info && ((atof(info->req_version) < atof(PLUGIN_VERSION)) || // plugin is based on older code - (info->type != PLUGIN_ALL && info->type != PLUGIN_CORE && info->type != SERVER_TYPE) || // plugin is not for this server - (info->type == PLUGIN_CORE && SERVER_TYPE != PLUGIN_LOGIN && SERVER_TYPE != PLUGIN_CHAR && SERVER_TYPE != PLUGIN_MAP)))) - { - //printf ("not loaded (incompatible) : %s\n", filename); - plugin_unload(plugin); - return NULL; - } - plugin->info = (info) ? info : &default_info; - - plugin->filename = (char *) aMalloc (strlen(filename) + 1); - strcpy(plugin->filename, filename); - - // Initialise plugin call table (For exporting procedures) - DLL_SYM (procs, plugin->dll, "plugin_call_table"); - if (procs) *procs = plugin_call_table; - - // Register plugin events - DLL_SYM (events, plugin->dll, "plugin_event_table"); - if (events) { - int i = 0; - while (events[i].func_name) { - if (strcmpi(events[i].event_name, "Plugin_Test") == 0) { - int (*test_func)(void); - DLL_SYM (test_func, plugin->dll, events[i].func_name); - if (test_func && test_func() == 0) { - // plugin has failed test, disabling - //printf ("disabled (failed test) : %s\n", filename); - init_flag = 0; - } - } else { - void (*func)(void); - DLL_SYM (func, plugin->dll, events[i].func_name); - if (func) register_plugin_event (func, events[i].event_name); - } - i++; - } - } - - plugin->next = plugin_head; - plugin_head = plugin; - - plugin->state = init_flag; // fully loaded - ShowStatus ("Done loading plugin '"CL_WHITE"%s"CL_RESET"'\n", (info) ? plugin->info->name : filename); - - return plugin; -} - -void plugin_load (const char *filename) -{ - plugin_open(filename); -} - -void plugin_unload (Plugin *plugin) -{ - if (plugin == NULL) - return; - if (plugin->filename) aFree(plugin->filename); - if (plugin->dll) DLL_CLOSE(plugin->dll); - aFree(plugin); -} - -#ifdef _WIN32 -char *DLL_ERROR(void) -{ - static char dllbuf[80]; - DWORD dw = GetLastError(); - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, 0, dllbuf, 80, NULL); - return dllbuf; -} -#endif - -////// Initialize/Finalize //////////////////// - -int plugins_config_read(const char *cfgName) -{ - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - fp = fopen(cfgName, "r"); - if (fp == NULL) { - ShowError("File not found: %s\n", cfgName); - return 1; - } - while (fgets(line, 1020, fp)) { - if(line[0] == '/' && line[1] == '/') - continue; - if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2) - continue; - - if (strcmpi(w1, "auto_search") == 0) { - if(strcmpi(w2, "yes")==0) - auto_search = 1; - else if(strcmpi(w2, "no")==0) - auto_search = 0; - else auto_search = atoi(w2); - } else if (strcmpi(w1, "plugin") == 0) { - char filename[128]; - sprintf (filename, "plugins/%s%s", w2, DLL_EXT); - plugin_load(filename); - } else if (strcmpi(w1, "import") == 0) - plugins_config_read(w2); - } - fclose(fp); - return 0; -} - -void plugins_init (void) -{ - char *PLUGIN_CONF_FILENAME = "conf/plugin_athena.conf"; - register_plugin_func("Plugin_Init"); - register_plugin_func("Plugin_Final"); - register_plugin_func("Athena_Init"); - register_plugin_func("Athena_Final"); - - // networking - export_symbol (func_parse_table, 18); - export_symbol (RFIFOSKIP, 17); - export_symbol (WFIFOSET, 16); - export_symbol (delete_session, 15); - export_symbol (session, 14); - export_symbol (&fd_max, 13); - export_symbol (addr_, 12); - // timers - export_symbol (get_uptime, 11); - export_symbol (delete_timer, 10); - export_symbol (add_timer_func_list, 9); - export_symbol (add_timer_interval, 8); - export_symbol (add_timer, 7); - export_symbol ((void *)get_svn_revision, 6); - export_symbol (gettick, 5); - // core - export_symbol (&runflag, 4); - export_symbol (arg_v, 3); - export_symbol (&arg_c, 2); - export_symbol (SERVER_NAME, 1); - export_symbol (&SERVER_TYPE, 0); - - load_priority = 1; - plugins_config_read (PLUGIN_CONF_FILENAME); - load_priority = 0; - - if (auto_search) - findfile("plugins", DLL_EXT, plugin_load); - - plugin_event_trigger("Plugin_Init"); - - return; -} - -void plugins_final (void) -{ - Plugin *plugin = plugin_head, *plugin2; - Plugin_Event_List *evl = event_head, *evl2; - Plugin_Event *ev, *ev2; - - plugin_event_trigger("Plugin_Final"); - - while (plugin) { - plugin2 = plugin->next; - plugin_unload(plugin); - plugin = plugin2; - } - - while (evl) { - ev = evl->events; - while (ev) { - ev2 = ev->next; - aFree(ev); - ev = ev2; - } - evl2 = evl->next; - aFree(evl->name); - aFree(evl); - evl = evl2; - } - - aFree(plugin_call_table); - - return; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "plugin.h" +#include "plugins.h" +#include "../common/mmo.h" +#include "../common/core.h" +#include "../common/timer.h" +#include "../common/utils.h" +#include "../common/socket.h" +#include "../common/malloc.h" +#include "../common/version.h" +#include "../common/showmsg.h" + +////////////////////////////////////////////// + +typedef struct _Plugin_Event { + void (*func)(void); + struct _Plugin_Event *next; +} Plugin_Event; + +typedef struct _Plugin_Event_List { + char *name; + struct _Plugin_Event_List *next; + struct _Plugin_Event *events; +} Plugin_Event_List; + +static int auto_search = 1; +static int load_priority = 0; +Plugin_Event_List *event_head = NULL; +Plugin *plugin_head = NULL; + +Plugin_Info default_info = { "Unknown", PLUGIN_ALL, "0", PLUGIN_VERSION, "Unknown" }; + +static size_t call_table_size = 0; +static size_t max_call_table = 0; + +////// Plugin Events Functions ////////////////// + +int register_plugin_func (char *name) +{ + Plugin_Event_List *evl; + if (name) { + evl = (Plugin_Event_List *) aMalloc(sizeof(Plugin_Event_List)); + evl->name = (char *) aMalloc (strlen(name) + 1); + + evl->next = event_head; + strcpy(evl->name, name); + evl->events = NULL; + event_head = evl; + } + return 0; +} + +Plugin_Event_List *search_plugin_func (char *name) +{ + Plugin_Event_List *evl = event_head; + while (evl) { + if (strcmpi(evl->name, name) == 0) + return evl; + evl = evl->next; + } + return NULL; +} + +int register_plugin_event (void (*func)(void), char* name) +{ + Plugin_Event_List *evl = search_plugin_func(name); + if (!evl) { + // register event if it doesn't exist already + register_plugin_func(name); + // relocate the new event list + evl = search_plugin_func(name); + } + if (evl) { + Plugin_Event *ev; + + ev = (Plugin_Event *) aMalloc(sizeof(Plugin_Event)); + ev->func = func; + ev->next = NULL; + + if (evl->events == NULL) + evl->events = ev; + else { + Plugin_Event *ev2 = evl->events; + while (ev2) { + if (ev2->next == NULL) { + ev2->next = ev; + break; + } + ev2 = ev2->next; + } + } + } + return 0; +} + +int plugin_event_trigger (char *name) +{ + int c = 0; + Plugin_Event_List *evl = search_plugin_func(name); + if (evl) { + Plugin_Event *ev = evl->events; + while (ev) { + ev->func(); + ev = ev->next; + c++; + } + } + return c; +} + +////// Plugins Call Table Functions ///////// + +int export_symbol (void *var, int offset) +{ + //printf ("0x%x\n", var); + + // add to the end of the list + if (offset < 0) + offset = call_table_size; + + // realloc if not large enough + if ((size_t)offset >= max_call_table) { + max_call_table = 1 + offset; + plugin_call_table = (void**)aRealloc(plugin_call_table, max_call_table*sizeof(void*)); + + // clear the new alloced block + malloc_tsetdword(plugin_call_table + call_table_size, 0, (max_call_table-call_table_size)*sizeof(void*)); + } + + // the new table size is delimited by the new element at the end + if ((size_t)offset >= call_table_size) + call_table_size = offset+1; + + // put the pointer at the selected place + plugin_call_table[offset] = var; + return 0; +} + +////// Plugins Core ///////////////////////// + +Plugin *plugin_open (const char *filename) +{ + Plugin *plugin; + Plugin_Info *info; + Plugin_Event_Table *events; + void **procs; + int init_flag = 1; + + //printf ("loading %s\n", filename); + + // Check if the plugin has been loaded before + plugin = plugin_head; + while (plugin) { + // returns handle to the already loaded plugin + if (plugin->state && strcmpi(plugin->filename, filename) == 0) { + //printf ("not loaded (duplicate) : %s\n", filename); + return plugin; + } + plugin = plugin->next; + } + + plugin = (Plugin *)aCalloc(1, sizeof(Plugin)); + plugin->state = -1; // not loaded + + plugin->dll = DLL_OPEN(filename); + if (!plugin->dll) { + //printf ("not loaded (invalid file) : %s\n", filename); + plugin_unload(plugin); + return NULL; + } + + // Retrieve plugin information + plugin->state = 0; // initialising + DLL_SYM (info, plugin->dll, "plugin_info"); + // For high priority plugins (those that are explicitly loaded from the conf file) + // we'll ignore them even (could be a 3rd party dll file) + if ((!info && load_priority == 0) || + (info && ((atof(info->req_version) < atof(PLUGIN_VERSION)) || // plugin is based on older code + (info->type != PLUGIN_ALL && info->type != PLUGIN_CORE && info->type != SERVER_TYPE) || // plugin is not for this server + (info->type == PLUGIN_CORE && SERVER_TYPE != PLUGIN_LOGIN && SERVER_TYPE != PLUGIN_CHAR && SERVER_TYPE != PLUGIN_MAP)))) + { + //printf ("not loaded (incompatible) : %s\n", filename); + plugin_unload(plugin); + return NULL; + } + plugin->info = (info) ? info : &default_info; + + plugin->filename = (char *) aMalloc (strlen(filename) + 1); + strcpy(plugin->filename, filename); + + // Initialise plugin call table (For exporting procedures) + DLL_SYM (procs, plugin->dll, "plugin_call_table"); + if (procs) *procs = plugin_call_table; + + // Register plugin events + DLL_SYM (events, plugin->dll, "plugin_event_table"); + if (events) { + int i = 0; + while (events[i].func_name) { + if (strcmpi(events[i].event_name, "Plugin_Test") == 0) { + int (*test_func)(void); + DLL_SYM (test_func, plugin->dll, events[i].func_name); + if (test_func && test_func() == 0) { + // plugin has failed test, disabling + //printf ("disabled (failed test) : %s\n", filename); + init_flag = 0; + } + } else { + void (*func)(void); + DLL_SYM (func, plugin->dll, events[i].func_name); + if (func) register_plugin_event (func, events[i].event_name); + } + i++; + } + } + + plugin->next = plugin_head; + plugin_head = plugin; + + plugin->state = init_flag; // fully loaded + ShowStatus ("Done loading plugin '"CL_WHITE"%s"CL_RESET"'\n", (info) ? plugin->info->name : filename); + + return plugin; +} + +void plugin_load (const char *filename) +{ + plugin_open(filename); +} + +void plugin_unload (Plugin *plugin) +{ + if (plugin == NULL) + return; + if (plugin->filename) aFree(plugin->filename); + if (plugin->dll) DLL_CLOSE(plugin->dll); + aFree(plugin); +} + +#ifdef _WIN32 +char *DLL_ERROR(void) +{ + static char dllbuf[80]; + DWORD dw = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, 0, dllbuf, 80, NULL); + return dllbuf; +} +#endif + +////// Initialize/Finalize //////////////////// + +int plugins_config_read(const char *cfgName) +{ + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp = fopen(cfgName, "r"); + if (fp == NULL) { + ShowError("File not found: %s\n", cfgName); + return 1; + } + while (fgets(line, 1020, fp)) { + if(line[0] == '/' && line[1] == '/') + continue; + if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + if (strcmpi(w1, "auto_search") == 0) { + if(strcmpi(w2, "yes")==0) + auto_search = 1; + else if(strcmpi(w2, "no")==0) + auto_search = 0; + else auto_search = atoi(w2); + } else if (strcmpi(w1, "plugin") == 0) { + char filename[128]; + sprintf (filename, "plugins/%s%s", w2, DLL_EXT); + plugin_load(filename); + } else if (strcmpi(w1, "import") == 0) + plugins_config_read(w2); + } + fclose(fp); + return 0; +} + +void plugins_init (void) +{ + char *PLUGIN_CONF_FILENAME = "conf/plugin_athena.conf"; + register_plugin_func("Plugin_Init"); + register_plugin_func("Plugin_Final"); + register_plugin_func("Athena_Init"); + register_plugin_func("Athena_Final"); + + // networking + export_symbol (func_parse_table, 18); + export_symbol (RFIFOSKIP, 17); + export_symbol (WFIFOSET, 16); + export_symbol (delete_session, 15); + export_symbol (session, 14); + export_symbol (&fd_max, 13); + export_symbol (addr_, 12); + // timers + export_symbol (get_uptime, 11); + export_symbol (delete_timer, 10); + export_symbol (add_timer_func_list, 9); + export_symbol (add_timer_interval, 8); + export_symbol (add_timer, 7); + export_symbol ((void *)get_svn_revision, 6); + export_symbol (gettick, 5); + // core + export_symbol (&runflag, 4); + export_symbol (arg_v, 3); + export_symbol (&arg_c, 2); + export_symbol (SERVER_NAME, 1); + export_symbol (&SERVER_TYPE, 0); + + load_priority = 1; + plugins_config_read (PLUGIN_CONF_FILENAME); + load_priority = 0; + + if (auto_search) + findfile("plugins", DLL_EXT, plugin_load); + + plugin_event_trigger("Plugin_Init"); + + return; +} + +void plugins_final (void) +{ + Plugin *plugin = plugin_head, *plugin2; + Plugin_Event_List *evl = event_head, *evl2; + Plugin_Event *ev, *ev2; + + plugin_event_trigger("Plugin_Final"); + + while (plugin) { + plugin2 = plugin->next; + plugin_unload(plugin); + plugin = plugin2; + } + + while (evl) { + ev = evl->events; + while (ev) { + ev2 = ev->next; + aFree(ev); + ev = ev2; + } + evl2 = evl->next; + aFree(evl->name); + aFree(evl); + evl = evl2; + } + + aFree(plugin_call_table); + + return; +} diff --git a/src/common/plugins.h b/src/common/plugins.h index d642b5965..34fd18a64 100644 --- a/src/common/plugins.h +++ b/src/common/plugins.h @@ -1,61 +1,61 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _PLUGINS_H_ -#define _PLUGINS_H_ - -////// Dynamic Link Library functions /////////////// - -#ifdef _WIN32 - - #include - #define DLL_OPEN(x) LoadLibrary(x) - #define DLL_SYM(x,y,z) (FARPROC)(x) = GetProcAddress(y,z) - #define DLL_CLOSE(x) FreeLibrary(x) - #define DLL_EXT ".dll" - #define DLL HINSTANCE - char *DLL_ERROR(void); - -#else - - #include - #define DLL_OPEN(x) dlopen(x,RTLD_NOW) - #define DLL_SYM(x,y,z) (x) = (void *)dlsym(y,z) - #define DLL_CLOSE(x) dlclose(x) - #define DLL_ERROR dlerror - - #ifdef CYGWIN - #define DLL_EXT ".dll" - #else - #define DLL_EXT ".so" - #endif - #define DLL void * - -#endif - -////// Plugin Definitions /////////////////// - -typedef struct _Plugin { - DLL dll; - char state; - char *filename; - struct _Plugin_Info *info; - struct _Plugin *next; -} Plugin; - -///////////////////////////////////////////// - -int register_plugin_func (char *); -int register_plugin_event (void (*)(void), char *); -int plugin_event_trigger (char *); - -int export_symbol (void *, int); -#define EXPORT_SYMBOL(s) export_symbol((s), -1); - -Plugin *plugin_open (const char *); -void plugin_load (const char *); -void plugin_unload (Plugin *); -void plugins_init (void); -void plugins_final (void); - -#endif // _PLUGINS_H_ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _PLUGINS_H_ +#define _PLUGINS_H_ + +////// Dynamic Link Library functions /////////////// + +#ifdef _WIN32 + + #include + #define DLL_OPEN(x) LoadLibrary(x) + #define DLL_SYM(x,y,z) (FARPROC)(x) = GetProcAddress(y,z) + #define DLL_CLOSE(x) FreeLibrary(x) + #define DLL_EXT ".dll" + #define DLL HINSTANCE + char *DLL_ERROR(void); + +#else + + #include + #define DLL_OPEN(x) dlopen(x,RTLD_NOW) + #define DLL_SYM(x,y,z) (x) = (void *)dlsym(y,z) + #define DLL_CLOSE(x) dlclose(x) + #define DLL_ERROR dlerror + + #ifdef CYGWIN + #define DLL_EXT ".dll" + #else + #define DLL_EXT ".so" + #endif + #define DLL void * + +#endif + +////// Plugin Definitions /////////////////// + +typedef struct _Plugin { + DLL dll; + char state; + char *filename; + struct _Plugin_Info *info; + struct _Plugin *next; +} Plugin; + +///////////////////////////////////////////// + +int register_plugin_func (char *); +int register_plugin_event (void (*)(void), char *); +int plugin_event_trigger (char *); + +int export_symbol (void *, int); +#define EXPORT_SYMBOL(s) export_symbol((s), -1); + +Plugin *plugin_open (const char *); +void plugin_load (const char *); +void plugin_unload (Plugin *); +void plugins_init (void); +void plugins_final (void); + +#endif // _PLUGINS_H_ diff --git a/src/common/showmsg.c b/src/common/showmsg.c index b0f1d1d8f..fd376f100 100644 --- a/src/common/showmsg.c +++ b/src/common/showmsg.c @@ -1,826 +1,826 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include -#include // atexit -#include "../common/cbasetypes.h" -#include "showmsg.h" - -#ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #include - - #ifdef DEBUGLOGMAP - #define DEBUGLOGPATH "log\\map-server.log" - #else - #ifdef DEBUGLOGCHAR - #define DEBUGLOGPATH "log\\char-server.log" - #else - #ifdef DEBUGLOGLOGIN - #define DEBUGLOGPATH "log\\login-server.log" - #endif - #endif - #endif -#else - #include - #include - - #ifdef DEBUGLOGMAP - #define DEBUGLOGPATH "log/map-server.log" - #else - #ifdef DEBUGLOGCHAR - #define DEBUGLOGPATH "log/char-server.log" - #else - #ifdef DEBUGLOGLOGIN - #define DEBUGLOGPATH "log/login-server.log" - #endif - #endif - #endif -#endif - -/////////////////////////////////////////////////////////////////////////////// -/// behavioral parameter. -/// when true, prints ansi sequences also when redirecting outputs to file -/// otherwise remove them -int stdout_with_ansisequence = 1; - -int msg_silent; //Specifies how silent the console is. - -/////////////////////////////////////////////////////////////////////////////// -/// small reallocating temporary printer buffer -static char *tempbuf = NULL; -static size_t sz = 0; -#define tempbuf_size() (sz) -static void tempbuf_free(void){ free(tempbuf); } -static void tempbuf_alloc(void){ sz = 256; tempbuf = (char *)malloc(sz); atexit(tempbuf_free); } -static void tempbuf_realloc(void){ sz <<= 1; tempbuf = (char *)realloc(tempbuf,sz); } - -/////////////////////////////////////////////////////////////////////////////// -#ifdef _WIN32 -// XXX adapted from eApp (comments are left untouched) [flaviojs] - -/////////////////////////////////////////////////////////////////////////////// -// ansi compatible printf with control sequence parser for windows -// fast hack, handle with care, not everything implemented -// -// \033[#;...;#m - Set Graphics Rendition (SGR) -// -// printf("\x1b[1;31;40m"); // Bright red on black -// printf("\x1b[3;33;45m"); // Blinking yellow on magenta (blink not implemented) -// printf("\x1b[1;30;47m"); // Bright black (grey) on dim white -// -// Style Foreground Background -// 1st Digit 2nd Digit 3rd Digit RGB -// 0 - Reset 30 - Black 40 - Black 000 -// 1 - FG Bright 31 - Red 41 - Red 100 -// 2 - Unknown 32 - Green 42 - Green 010 -// 3 - Blink 33 - Yellow 43 - Yellow 110 -// 4 - Underline 34 - Blue 44 - Blue 001 -// 5 - BG Bright 35 - Magenta 45 - Magenta 101 -// 6 - Unknown 36 - Cyan 46 - Cyan 011 -// 7 - Reverse 37 - White 47 - White 111 -// 8 - Concealed (invisible) -// -// \033[#A - Cursor Up (CUU) -// Moves the cursor up by the specified number of lines without changing columns. -// If the cursor is already on the top line, this sequence is ignored. \e[A is equivalent to \e[1A. -// -// \033[#B - Cursor Down (CUD) -// Moves the cursor down by the specified number of lines without changing columns. -// If the cursor is already on the bottom line, this sequence is ignored. \e[B is equivalent to \e[1B. -// -// \033[#C - Cursor Forward (CUF) -// Moves the cursor forward by the specified number of columns without changing lines. -// If the cursor is already in the rightmost column, this sequence is ignored. \e[C is equivalent to \e[1C. -// -// \033[#D - Cursor Backward (CUB) -// Moves the cursor back by the specified number of columns without changing lines. -// If the cursor is already in the leftmost column, this sequence is ignored. \e[D is equivalent to \e[1D. -// -// \033[#E - Cursor Next Line (CNL) -// Moves the cursor down the indicated # of rows, to column 1. \e[E is equivalent to \e[1E. -// -// \033[#F - Cursor Preceding Line (CPL) -// Moves the cursor up the indicated # of rows, to column 1. \e[F is equivalent to \e[1F. -// -// \033[#G - Cursor Horizontal Absolute (CHA) -// Moves the cursor to indicated column in current row. \e[G is equivalent to \e[1G. -// -// \033[#;#H - Cursor Position (CUP) -// Moves the cursor to the specified position. The first # specifies the line number, -// the second # specifies the column. If you do not specify a position, the cursor moves to the home position: -// the upper-left corner of the screen (line 1, column 1). -// -// \033[#;#f - Horizontal & Vertical Position -// (same as \033[#;#H) -// -// \033[s - Save Cursor Position (SCP) -// The current cursor position is saved. -// -// \033[u - Restore cursor position (RCP) -// Restores the cursor position saved with the (SCP) sequence \033[s. -// (addition, restore to 0,0 if nothinh was saved before) -// - -// \033[#J - Erase Display (ED) -// Clears the screen and moves to the home position -// \033[0J - Clears the screen from cursor to end of display. The cursor position is unchanged. (default) -// \033[1J - Clears the screen from start to cursor. The cursor position is unchanged. -// \033[2J - Clears the screen and moves the cursor to the home position (line 1, column 1). -// -// \033[#K - Erase Line (EL) -// Clears the current line from the cursor position -// \033[0K - Clears all characters from the cursor position to the end of the line (including the character at the cursor position). The cursor position is unchanged. (default) -// \033[1K - Clears all characters from start of line to the cursor position. (including the character at the cursor position). The cursor position is unchanged. -// \033[2K - Clears all characters of the whole line. The cursor position is unchanged. - - -/* -not implemented - -\033[#L -IL: Insert Lines: The cursor line and all lines below it move down # lines, leaving blank space. The cursor position is unchanged. The bottommost # lines are lost. \e[L is equivalent to \e[1L. -\033[#M -DL: Delete Line: The block of # lines at and below the cursor are deleted; all lines below them move up # lines to fill in the gap, leaving # blank lines at the bottom of the screen. The cursor position is unchanged. \e[M is equivalent to \e[1M. -\033[#\@ -ICH: Insert CHaracter: The cursor character and all characters to the right of it move right # columns, leaving behind blank space. The cursor position is unchanged. The rightmost # characters on the line are lost. \e[\@ is equivalent to \e[1\@. -\033[#P -DCH: Delete CHaracter: The block of # characters at and to the right of the cursor are deleted; all characters to the right of it move left # columns, leaving behind blank space. The cursor position is unchanged. \e[P is equivalent to \e[1P. - -Escape sequences for Select Character Set -*/ - -#define is_console(handle) (FILE_TYPE_CHAR==GetFileType(handle)) - -/////////////////////////////////////////////////////////////////////////////// -int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr) -{ - ///////////////////////////////////////////////////////////////// - /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs] - static COORD saveposition = {0,0}; - */ - - ///////////////////////////////////////////////////////////////// - unsigned long written; - char *p, *q; - - if(!fmt || !*fmt) - return 0; - - if(tempbuf == NULL) - tempbuf_alloc(); - for(; vsnprintf(tempbuf, tempbuf_size(), fmt, argptr)<0; tempbuf_realloc()); - // vsnprintf returns -1 in case of insufficient buffer size - // tempbuf_realloc doubles the size of the buffer in this case - - if( !is_console(handle) && stdout_with_ansisequence ) - { - WriteFile(handle,tempbuf, strlen(tempbuf), &written, 0); - return 0; - } - - // start with processing - p = tempbuf; - while ((q = strchr(p, 0x1b)) != NULL) - { // find the escape character - if( 0==WriteConsole(handle, p, q-p, &written, 0) ) // write up to the escape - WriteFile(handle, p, q-p, &written, 0); - - if( q[1]!='[' ) - { // write the escape char (whatever purpose it has) - if(0==WriteConsole(handle, q, 1, &written, 0) ) - WriteFile(handle,q, 1, &written, 0); - p=q+1; //and start searching again - } - else - { // from here, we will skip the '\033[' - // we break at the first unprocessible position - // assuming regular text is starting there - uchar numbers[16], numpoint=0; - CONSOLE_SCREEN_BUFFER_INFO info; - - // initialize - GetConsoleScreenBufferInfo(handle, &info); - memset(numbers,0,sizeof(numbers)); - - // skip escape and bracket - q=q+2; - while(1) - { - if( isdigit((int)((unsigned char)*q)) ) - { // add number to number array, only accept 2digits, shift out the rest - // so // \033[123456789m will become \033[89m - numbers[numpoint] = (numbers[numpoint]<<4) | (*q-'0'); - ++q; - // and next character - continue; - } - else if( *q == ';' ) - { // delimiter - if(numpoint7) num=7; // set white for 37, 38 and 39 - info.wAttributes &= ~(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); - if( (num & 0x01)>0 ) // lowest bit set = red - info.wAttributes |= FOREGROUND_RED; - if( (num & 0x02)>0 ) // second bit set = green - info.wAttributes |= FOREGROUND_GREEN; - if( (num & 0x04)>0 ) // third bit set = blue - info.wAttributes |= FOREGROUND_BLUE; - } - else if( 0x40 == (0xF0 & numbers[i]) ) - { // background - uint num = numbers[i]&0x0F; - if(num==9) info.wAttributes |= BACKGROUND_INTENSITY; - if(num>7) num=7; // set white for 47, 48 and 49 - info.wAttributes &= ~(BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE); - if( (num & 0x01)>0 ) // lowest bit set = red - info.wAttributes |= BACKGROUND_RED; - if( (num & 0x02)>0 ) // second bit set = green - info.wAttributes |= BACKGROUND_GREEN; - if( (num & 0x04)>0 ) // third bit set = blue - info.wAttributes |= BACKGROUND_BLUE; - } - } - // set the attributes - SetConsoleTextAttribute(handle, info.wAttributes); - } - else if( *q=='J' ) - { // \033[#J - Erase Display (ED) - // \033[0J - Clears the screen from cursor to end of display. The cursor position is unchanged. - // \033[1J - Clears the screen from start to cursor. The cursor position is unchanged. - // \033[2J - Clears the screen and moves the cursor to the home position (line 1, column 1). - uint num = (numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F); - int cnt; - COORD origin = {0,0}; - if(num==1) - { // chars from start up to and including cursor - cnt = info.dwSize.X * info.dwCursorPosition.Y + info.dwCursorPosition.X + 1; - } - else if(num==2) - { // Number of chars on screen. - cnt = info.dwSize.X * info.dwSize.Y; - SetConsoleCursorPosition(handle, origin); - } - else// 0 and default - { // number of chars from cursor to end - origin = info.dwCursorPosition; - cnt = info.dwSize.X * (info.dwSize.Y - info.dwCursorPosition.Y) - info.dwCursorPosition.X; - } - FillConsoleOutputAttribute(handle,info.wAttributes,cnt,origin,NULL); - FillConsoleOutputCharacter(handle,' ', cnt,origin,NULL); - } - else if( *q=='K' ) - { // \033[K : clear line from actual position to end of the line - // \033[0K - Clears all characters from the cursor position to the end of the line. - // \033[1K - Clears all characters from start of line to the cursor position. - // \033[2K - Clears all characters of the whole line. - - uint num = (numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F); - COORD origin = {0,info.dwCursorPosition.Y}; - SHORT cnt; - if(num==1) - { - cnt = info.dwCursorPosition.X + 1; - } - else if(num==2) - { - cnt = info.dwSize.X; - } - else// 0 and default - { - origin = info.dwCursorPosition; - cnt = info.dwSize.X - info.dwCursorPosition.X; // how many spaces until line is full - } - FillConsoleOutputAttribute(handle, info.wAttributes, cnt, origin, NULL); - FillConsoleOutputCharacter(handle, ' ', cnt, origin, NULL); - } - else if( *q == 'H' || *q == 'f' ) - { // \033[#;#H - Cursor Position (CUP) - // \033[#;#f - Horizontal & Vertical Position - // The first # specifies the line number, the second # specifies the column. - // The default for both is 1 - info.dwCursorPosition.X = (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F-1):0; - info.dwCursorPosition.Y = (numpoint && numbers[numpoint-1])?(numbers[numpoint-1]>>4)*10+(numbers[numpoint-1]&0x0F-1):0; - - if( info.dwCursorPosition.X >= info.dwSize.X ) info.dwCursorPosition.Y = info.dwSize.X-1; - if( info.dwCursorPosition.Y >= info.dwSize.Y ) info.dwCursorPosition.Y = info.dwSize.Y-1; - SetConsoleCursorPosition(handle, info.dwCursorPosition); - } - else if( *q=='s' ) - { // \033[s - Save Cursor Position (SCP) - /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs] - CONSOLE_SCREEN_BUFFER_INFO info; - GetConsoleScreenBufferInfo(handle, &info); - saveposition = info.dwCursorPosition; - */ - } - else if( *q=='u' ) - { // \033[u - Restore cursor position (RCP) - /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs] - SetConsoleCursorPosition(handle, saveposition); - */ - } - else if( *q == 'A' ) - { // \033[#A - Cursor Up (CUU) - // Moves the cursor UP # number of lines - info.dwCursorPosition.Y -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; - - if( info.dwCursorPosition.Y < 0 ) - info.dwCursorPosition.Y = 0; - SetConsoleCursorPosition(handle, info.dwCursorPosition); - } - else if( *q == 'B' ) - { // \033[#B - Cursor Down (CUD) - // Moves the cursor DOWN # number of lines - info.dwCursorPosition.Y += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; - - if( info.dwCursorPosition.Y >= info.dwSize.Y ) - info.dwCursorPosition.Y = info.dwSize.Y-1; - SetConsoleCursorPosition(handle, info.dwCursorPosition); - } - else if( *q == 'C' ) - { // \033[#C - Cursor Forward (CUF) - // Moves the cursor RIGHT # number of columns - info.dwCursorPosition.X += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; - - if( info.dwCursorPosition.X >= info.dwSize.X ) - info.dwCursorPosition.X = info.dwSize.X-1; - SetConsoleCursorPosition(handle, info.dwCursorPosition); - } - else if( *q == 'D' ) - { // \033[#D - Cursor Backward (CUB) - // Moves the cursor LEFT # number of columns - info.dwCursorPosition.X -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; - - if( info.dwCursorPosition.X < 0 ) - info.dwCursorPosition.X = 0; - SetConsoleCursorPosition(handle, info.dwCursorPosition); - } - else if( *q == 'E' ) - { // \033[#E - Cursor Next Line (CNL) - // Moves the cursor down the indicated # of rows, to column 1 - info.dwCursorPosition.Y += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; - info.dwCursorPosition.X = 0; - - if( info.dwCursorPosition.Y >= info.dwSize.Y ) - info.dwCursorPosition.Y = info.dwSize.Y-1; - SetConsoleCursorPosition(handle, info.dwCursorPosition); - } - else if( *q == 'F' ) - { // \033[#F - Cursor Preceding Line (CPL) - // Moves the cursor up the indicated # of rows, to column 1. - info.dwCursorPosition.Y -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; - info.dwCursorPosition.X = 0; - - if( info.dwCursorPosition.Y < 0 ) - info.dwCursorPosition.Y = 0; - SetConsoleCursorPosition(handle, info.dwCursorPosition); - } - else if( *q == 'G' ) - { // \033[#G - Cursor Horizontal Absolute (CHA) - // Moves the cursor to indicated column in current row. - info.dwCursorPosition.X = (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F-1):0; - - if( info.dwCursorPosition.X >= info.dwSize.X ) - info.dwCursorPosition.X = info.dwSize.X-1; - SetConsoleCursorPosition(handle, info.dwCursorPosition); - } - else if( *q == 'L' || *q == 'M' || *q == '@' || *q == 'P') - { // not implemented, just skip - } - else - { // no number nor valid sequencer - // something is fishy, we break and give the current char free - --q; - } - // skip the sequencer and search again - p = q+1; - break; - }// end while - } - } - if (*p) // write the rest of the buffer - if( 0==WriteConsole(handle, p, strlen(p), &written, 0) ) - WriteFile(handle,p, strlen(p), &written, 0); - return 0; -} - -int FPRINTF(HANDLE handle, const char *fmt, ...) -{ - int ret; - va_list argptr; - va_start(argptr, fmt); - ret = VFPRINTF(handle,fmt,argptr); - va_end(argptr); - return ret; -} - -#define FFLUSH(handle) - -#define STDOUT GetStdHandle(STD_OUTPUT_HANDLE) -#define STDERR GetStdHandle(STD_ERROR_HANDLE) - -#else // not _WIN32 - - -//#define VPRINTF vprintf -//#define PRINTF printf - -#define is_console(file) (0!=isatty(fileno(file))) - -//vprintf_without_ansiformats -int VFPRINTF(FILE *file, const char *fmt, va_list argptr) -{ - char *p, *q; - - if(!fmt || !*fmt) - return 0; - - if( is_console(file) || stdout_with_ansisequence ) - { - vfprintf(file, fmt, argptr); - return 0; - } - - if(tempbuf == NULL) - tempbuf_alloc(); - for(; vsnprintf(tempbuf, tempbuf_size(), fmt, argptr)<0; tempbuf_realloc()); - // vsnprintf returns -1 in case of insufficient buffer size - // tempbuf.realloc doubles the size of the buffer in this case - - // start with processing - p = tempbuf; - while ((q = strchr(p, 0x1b)) != NULL) - { // find the escape character - fprintf(file, "%.*s", (int)(q-p), p); // write up to the escape - if( q[1]!='[' ) - { // write the escape char (whatever purpose it has) - fprintf(file, "%.*s", 1, q); - p=q+1; //and start searching again - } - else - { // from here, we will skip the '\033[' - // we break at the first unprocessible position - // assuming regular text is starting there - - // skip escape and bracket - q=q+2; - while(1) - { - if( isdigit((int)((unsigned char)*q)) ) - { - ++q; - // and next character - continue; - } - else if( *q == ';' ) - { // delimiter - ++q; - // and next number - continue; - } - else if( *q == 'm' ) - { // \033[#;...;#m - Set Graphics Rendition (SGR) - // set the attributes - } - else if( *q=='J' ) - { // \033[#J - Erase Display (ED) - } - else if( *q=='K' ) - { // \033[K : clear line from actual position to end of the line - } - else if( *q == 'H' || *q == 'f' ) - { // \033[#;#H - Cursor Position (CUP) - // \033[#;#f - Horizontal & Vertical Position - } - else if( *q=='s' ) - { // \033[s - Save Cursor Position (SCP) - } - else if( *q=='u' ) - { // \033[u - Restore cursor position (RCP) - } - else if( *q == 'A' ) - { // \033[#A - Cursor Up (CUU) - // Moves the cursor UP # number of lines - } - else if( *q == 'B' ) - { // \033[#B - Cursor Down (CUD) - // Moves the cursor DOWN # number of lines - } - else if( *q == 'C' ) - { // \033[#C - Cursor Forward (CUF) - // Moves the cursor RIGHT # number of columns - } - else if( *q == 'D' ) - { // \033[#D - Cursor Backward (CUB) - // Moves the cursor LEFT # number of columns - } - else if( *q == 'E' ) - { // \033[#E - Cursor Next Line (CNL) - // Moves the cursor down the indicated # of rows, to column 1 - } - else if( *q == 'F' ) - { // \033[#F - Cursor Preceding Line (CPL) - // Moves the cursor up the indicated # of rows, to column 1. - } - else if( *q == 'G' ) - { // \033[#G - Cursor Horizontal Absolute (CHA) - // Moves the cursor to indicated column in current row. - } - else if( *q == 'L' || *q == 'M' || *q == '@' || *q == 'P') - { // not implemented, just skip - } - else - { // no number nor valid sequencer - // something is fishy, we break and give the current char free - --q; - } - // skip the sequencer and search again - p = q+1; - break; - }// end while - } - } - if (*p) // write the rest of the buffer - fprintf(file, "%s", p); - return 0; -} -int FPRINTF(FILE *file, const char *fmt, ...) -{ - int ret; - va_list argptr; - va_start(argptr, fmt); - ret = VFPRINTF(file,fmt,argptr); - va_end(argptr); - return ret; -} - -#define FFLUSH fflush - -#define STDOUT stdout -#define STDERR stderr - -#endif// not _WIN32 - - - - - - - - - - -char timestamp_format[20] = ""; //For displaying Timestamps - -// by MC Cameri -int _vShowMessage(enum msg_type flag, const char *string, va_list ap) -{ - // _ShowMessage MUST be used instead of printf as of 10/24/2004. - // Return: 0 = Successful, 1 = Failed. -// int ret = 0; - char prefix[100]; -#if defined(DEBUGLOGMAP) || defined(DEBUGLOGCHAR) || defined(DEBUGLOGLOGIN) - FILE *fp; -#endif - - if (!string || *string == '\0') { - ShowError("Empty string passed to _vShowMessage().\n"); - return 1; - } - if ((flag == MSG_DEBUG && !SHOW_DEBUG_MSG) || - (flag == MSG_INFORMATION && msg_silent&1) || - (flag == MSG_STATUS && msg_silent&2) || - (flag == MSG_NOTICE && msg_silent&4) || - (flag == MSG_WARNING && msg_silent&8) || - (flag == MSG_ERROR && msg_silent&16) || - (flag == MSG_SQL && msg_silent&16)) - return 0; //Do not print it. - - if (timestamp_format[0]) - { //Display time format. [Skotlex] - time_t t = time(NULL); - strftime(prefix, 80, timestamp_format, localtime(&t)); - } else prefix[0]='\0'; - - switch (flag) { - case MSG_NONE: // direct printf replacement - break; - case MSG_STATUS: //Bright Green (To inform about good things) - strcat(prefix,CL_GREEN"[Status]"CL_RESET":"); - break; - case MSG_SQL: //Bright Violet (For dumping out anything related with SQL) <- Actually, this is mostly used for SQL errors with the database, as successes can as well just be anything else... [Skotlex] - strcat(prefix,CL_MAGENTA"[SQL]"CL_RESET":"); - break; - case MSG_INFORMATION: //Bright White (Variable information) - strcat(prefix,CL_WHITE"[Info]"CL_RESET":"); - break; - case MSG_NOTICE: //Bright White (Less than a warning) - strcat(prefix,CL_WHITE"[Notice]"CL_RESET":"); - break; - case MSG_WARNING: //Bright Yellow - strcat(prefix,CL_YELLOW"[Warning]"CL_RESET":"); - break; - case MSG_DEBUG: //Bright Cyan, important stuff! - strcat(prefix,CL_CYAN"[Debug]"CL_RESET":"); - break; - case MSG_ERROR: //Bright Red (Regular errors) - strcat(prefix,CL_RED"[Error]"CL_RESET":"); - break; - case MSG_FATALERROR: //Bright Red (Fatal errors, abort(); if possible) - strcat(prefix,CL_RED"[Fatal Error]"CL_RESET":"); - break; - default: - ShowError("In function _vShowMessage() -> Invalid flag passed.\n"); - return 1; - } - - if (flag == MSG_ERROR || flag == MSG_FATALERROR || flag == MSG_SQL) - { //Send Errors to StdErr [Skotlex] - FPRINTF(STDERR, "%s ", prefix); - VFPRINTF(STDERR, string, ap); - FFLUSH(STDERR); - } else { - if (flag != MSG_NONE) - FPRINTF(STDOUT, "%s ", prefix); - VFPRINTF(STDOUT, string, ap); - FFLUSH(STDOUT); - } - -#if defined(DEBUGLOGMAP) || defined(DEBUGLOGCHAR) || defined(DEBUGLOGLOGIN) - if(strlen(DEBUGLOGPATH) > 0) { - fp=fopen(DEBUGLOGPATH,"a"); - if (fp == NULL) { - FPRINTF(STDERR, CL_RED"[ERROR]"CL_RESET": Could not open '"CL_WHITE"%s"CL_RESET"', access denied.\n", DEBUGLOGPATH); - FFLUSH(STDERR); - } else { - fprintf(fp,"%s ", prefix); - vfprintf(fp,string,ap); - fclose(fp); - } - } else { - FPRINTF(STDERR, CL_RED"[ERROR]"CL_RESET": DEBUGLOGPATH not defined!\n"); - FFLUSH(STDERR); - } -#endif - - va_end(ap); - return 0; -} - -void ClearScreen(void) -{ -#ifndef _WIN32 - ShowMessage(CL_CLS); // to prevent empty string passed messages -#endif -} -int _ShowMessage(enum msg_type flag, const char *string, ...) -{ - int ret; - va_list ap; - va_start(ap, string); - ret = _vShowMessage(flag, string, ap); - va_end(ap); - return ret; -} - -// direct printf replacement -int ShowMessage(const char *string, ...) { - int ret; - va_list ap; - va_start(ap, string); - ret = _vShowMessage(MSG_NONE, string, ap); - va_end(ap); - return ret; -} -int ShowStatus(const char *string, ...) { - int ret; - va_list ap; - va_start(ap, string); - ret = _vShowMessage(MSG_STATUS, string, ap); - va_end(ap); - return ret; -} -int ShowSQL(const char *string, ...) { - int ret; - va_list ap; - va_start(ap, string); - ret = _vShowMessage(MSG_SQL, string, ap); - va_end(ap); - return ret; -} -int ShowInfo(const char *string, ...) { - int ret; - va_list ap; - va_start(ap, string); - ret = _vShowMessage(MSG_INFORMATION, string, ap); - va_end(ap); - return ret; -} -int ShowNotice(const char *string, ...) { - int ret; - va_list ap; - va_start(ap, string); - ret = _vShowMessage(MSG_NOTICE, string, ap); - va_end(ap); - return ret; -} -int ShowWarning(const char *string, ...) { - int ret; - va_list ap; - va_start(ap, string); - ret = _vShowMessage(MSG_WARNING, string, ap); - va_end(ap); - return ret; -} -int ShowDebug(const char *string, ...) { - int ret; - va_list ap; - va_start(ap, string); - ret = _vShowMessage(MSG_DEBUG, string, ap); - va_end(ap); - return ret; -} -int ShowError(const char *string, ...) { - int ret; - va_list ap; - va_start(ap, string); - ret = _vShowMessage(MSG_ERROR, string, ap); - va_end(ap); - return ret; -} -int ShowFatalError(const char *string, ...) { - int ret; - va_list ap; - va_start(ap, string); - ret = _vShowMessage(MSG_FATALERROR, string, ap); - va_end(ap); - return ret; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include +#include // atexit +#include "../common/cbasetypes.h" +#include "showmsg.h" + +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include + + #ifdef DEBUGLOGMAP + #define DEBUGLOGPATH "log\\map-server.log" + #else + #ifdef DEBUGLOGCHAR + #define DEBUGLOGPATH "log\\char-server.log" + #else + #ifdef DEBUGLOGLOGIN + #define DEBUGLOGPATH "log\\login-server.log" + #endif + #endif + #endif +#else + #include + #include + + #ifdef DEBUGLOGMAP + #define DEBUGLOGPATH "log/map-server.log" + #else + #ifdef DEBUGLOGCHAR + #define DEBUGLOGPATH "log/char-server.log" + #else + #ifdef DEBUGLOGLOGIN + #define DEBUGLOGPATH "log/login-server.log" + #endif + #endif + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/// behavioral parameter. +/// when true, prints ansi sequences also when redirecting outputs to file +/// otherwise remove them +int stdout_with_ansisequence = 1; + +int msg_silent; //Specifies how silent the console is. + +/////////////////////////////////////////////////////////////////////////////// +/// small reallocating temporary printer buffer +static char *tempbuf = NULL; +static size_t sz = 0; +#define tempbuf_size() (sz) +static void tempbuf_free(void){ free(tempbuf); } +static void tempbuf_alloc(void){ sz = 256; tempbuf = (char *)malloc(sz); atexit(tempbuf_free); } +static void tempbuf_realloc(void){ sz <<= 1; tempbuf = (char *)realloc(tempbuf,sz); } + +/////////////////////////////////////////////////////////////////////////////// +#ifdef _WIN32 +// XXX adapted from eApp (comments are left untouched) [flaviojs] + +/////////////////////////////////////////////////////////////////////////////// +// ansi compatible printf with control sequence parser for windows +// fast hack, handle with care, not everything implemented +// +// \033[#;...;#m - Set Graphics Rendition (SGR) +// +// printf("\x1b[1;31;40m"); // Bright red on black +// printf("\x1b[3;33;45m"); // Blinking yellow on magenta (blink not implemented) +// printf("\x1b[1;30;47m"); // Bright black (grey) on dim white +// +// Style Foreground Background +// 1st Digit 2nd Digit 3rd Digit RGB +// 0 - Reset 30 - Black 40 - Black 000 +// 1 - FG Bright 31 - Red 41 - Red 100 +// 2 - Unknown 32 - Green 42 - Green 010 +// 3 - Blink 33 - Yellow 43 - Yellow 110 +// 4 - Underline 34 - Blue 44 - Blue 001 +// 5 - BG Bright 35 - Magenta 45 - Magenta 101 +// 6 - Unknown 36 - Cyan 46 - Cyan 011 +// 7 - Reverse 37 - White 47 - White 111 +// 8 - Concealed (invisible) +// +// \033[#A - Cursor Up (CUU) +// Moves the cursor up by the specified number of lines without changing columns. +// If the cursor is already on the top line, this sequence is ignored. \e[A is equivalent to \e[1A. +// +// \033[#B - Cursor Down (CUD) +// Moves the cursor down by the specified number of lines without changing columns. +// If the cursor is already on the bottom line, this sequence is ignored. \e[B is equivalent to \e[1B. +// +// \033[#C - Cursor Forward (CUF) +// Moves the cursor forward by the specified number of columns without changing lines. +// If the cursor is already in the rightmost column, this sequence is ignored. \e[C is equivalent to \e[1C. +// +// \033[#D - Cursor Backward (CUB) +// Moves the cursor back by the specified number of columns without changing lines. +// If the cursor is already in the leftmost column, this sequence is ignored. \e[D is equivalent to \e[1D. +// +// \033[#E - Cursor Next Line (CNL) +// Moves the cursor down the indicated # of rows, to column 1. \e[E is equivalent to \e[1E. +// +// \033[#F - Cursor Preceding Line (CPL) +// Moves the cursor up the indicated # of rows, to column 1. \e[F is equivalent to \e[1F. +// +// \033[#G - Cursor Horizontal Absolute (CHA) +// Moves the cursor to indicated column in current row. \e[G is equivalent to \e[1G. +// +// \033[#;#H - Cursor Position (CUP) +// Moves the cursor to the specified position. The first # specifies the line number, +// the second # specifies the column. If you do not specify a position, the cursor moves to the home position: +// the upper-left corner of the screen (line 1, column 1). +// +// \033[#;#f - Horizontal & Vertical Position +// (same as \033[#;#H) +// +// \033[s - Save Cursor Position (SCP) +// The current cursor position is saved. +// +// \033[u - Restore cursor position (RCP) +// Restores the cursor position saved with the (SCP) sequence \033[s. +// (addition, restore to 0,0 if nothinh was saved before) +// + +// \033[#J - Erase Display (ED) +// Clears the screen and moves to the home position +// \033[0J - Clears the screen from cursor to end of display. The cursor position is unchanged. (default) +// \033[1J - Clears the screen from start to cursor. The cursor position is unchanged. +// \033[2J - Clears the screen and moves the cursor to the home position (line 1, column 1). +// +// \033[#K - Erase Line (EL) +// Clears the current line from the cursor position +// \033[0K - Clears all characters from the cursor position to the end of the line (including the character at the cursor position). The cursor position is unchanged. (default) +// \033[1K - Clears all characters from start of line to the cursor position. (including the character at the cursor position). The cursor position is unchanged. +// \033[2K - Clears all characters of the whole line. The cursor position is unchanged. + + +/* +not implemented + +\033[#L +IL: Insert Lines: The cursor line and all lines below it move down # lines, leaving blank space. The cursor position is unchanged. The bottommost # lines are lost. \e[L is equivalent to \e[1L. +\033[#M +DL: Delete Line: The block of # lines at and below the cursor are deleted; all lines below them move up # lines to fill in the gap, leaving # blank lines at the bottom of the screen. The cursor position is unchanged. \e[M is equivalent to \e[1M. +\033[#\@ +ICH: Insert CHaracter: The cursor character and all characters to the right of it move right # columns, leaving behind blank space. The cursor position is unchanged. The rightmost # characters on the line are lost. \e[\@ is equivalent to \e[1\@. +\033[#P +DCH: Delete CHaracter: The block of # characters at and to the right of the cursor are deleted; all characters to the right of it move left # columns, leaving behind blank space. The cursor position is unchanged. \e[P is equivalent to \e[1P. + +Escape sequences for Select Character Set +*/ + +#define is_console(handle) (FILE_TYPE_CHAR==GetFileType(handle)) + +/////////////////////////////////////////////////////////////////////////////// +int VFPRINTF(HANDLE handle, const char *fmt, va_list argptr) +{ + ///////////////////////////////////////////////////////////////// + /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs] + static COORD saveposition = {0,0}; + */ + + ///////////////////////////////////////////////////////////////// + unsigned long written; + char *p, *q; + + if(!fmt || !*fmt) + return 0; + + if(tempbuf == NULL) + tempbuf_alloc(); + for(; vsnprintf(tempbuf, tempbuf_size(), fmt, argptr)<0; tempbuf_realloc()); + // vsnprintf returns -1 in case of insufficient buffer size + // tempbuf_realloc doubles the size of the buffer in this case + + if( !is_console(handle) && stdout_with_ansisequence ) + { + WriteFile(handle,tempbuf, strlen(tempbuf), &written, 0); + return 0; + } + + // start with processing + p = tempbuf; + while ((q = strchr(p, 0x1b)) != NULL) + { // find the escape character + if( 0==WriteConsole(handle, p, q-p, &written, 0) ) // write up to the escape + WriteFile(handle, p, q-p, &written, 0); + + if( q[1]!='[' ) + { // write the escape char (whatever purpose it has) + if(0==WriteConsole(handle, q, 1, &written, 0) ) + WriteFile(handle,q, 1, &written, 0); + p=q+1; //and start searching again + } + else + { // from here, we will skip the '\033[' + // we break at the first unprocessible position + // assuming regular text is starting there + uchar numbers[16], numpoint=0; + CONSOLE_SCREEN_BUFFER_INFO info; + + // initialize + GetConsoleScreenBufferInfo(handle, &info); + memset(numbers,0,sizeof(numbers)); + + // skip escape and bracket + q=q+2; + while(1) + { + if( isdigit((int)((unsigned char)*q)) ) + { // add number to number array, only accept 2digits, shift out the rest + // so // \033[123456789m will become \033[89m + numbers[numpoint] = (numbers[numpoint]<<4) | (*q-'0'); + ++q; + // and next character + continue; + } + else if( *q == ';' ) + { // delimiter + if(numpoint7) num=7; // set white for 37, 38 and 39 + info.wAttributes &= ~(FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); + if( (num & 0x01)>0 ) // lowest bit set = red + info.wAttributes |= FOREGROUND_RED; + if( (num & 0x02)>0 ) // second bit set = green + info.wAttributes |= FOREGROUND_GREEN; + if( (num & 0x04)>0 ) // third bit set = blue + info.wAttributes |= FOREGROUND_BLUE; + } + else if( 0x40 == (0xF0 & numbers[i]) ) + { // background + uint num = numbers[i]&0x0F; + if(num==9) info.wAttributes |= BACKGROUND_INTENSITY; + if(num>7) num=7; // set white for 47, 48 and 49 + info.wAttributes &= ~(BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE); + if( (num & 0x01)>0 ) // lowest bit set = red + info.wAttributes |= BACKGROUND_RED; + if( (num & 0x02)>0 ) // second bit set = green + info.wAttributes |= BACKGROUND_GREEN; + if( (num & 0x04)>0 ) // third bit set = blue + info.wAttributes |= BACKGROUND_BLUE; + } + } + // set the attributes + SetConsoleTextAttribute(handle, info.wAttributes); + } + else if( *q=='J' ) + { // \033[#J - Erase Display (ED) + // \033[0J - Clears the screen from cursor to end of display. The cursor position is unchanged. + // \033[1J - Clears the screen from start to cursor. The cursor position is unchanged. + // \033[2J - Clears the screen and moves the cursor to the home position (line 1, column 1). + uint num = (numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F); + int cnt; + COORD origin = {0,0}; + if(num==1) + { // chars from start up to and including cursor + cnt = info.dwSize.X * info.dwCursorPosition.Y + info.dwCursorPosition.X + 1; + } + else if(num==2) + { // Number of chars on screen. + cnt = info.dwSize.X * info.dwSize.Y; + SetConsoleCursorPosition(handle, origin); + } + else// 0 and default + { // number of chars from cursor to end + origin = info.dwCursorPosition; + cnt = info.dwSize.X * (info.dwSize.Y - info.dwCursorPosition.Y) - info.dwCursorPosition.X; + } + FillConsoleOutputAttribute(handle,info.wAttributes,cnt,origin,NULL); + FillConsoleOutputCharacter(handle,' ', cnt,origin,NULL); + } + else if( *q=='K' ) + { // \033[K : clear line from actual position to end of the line + // \033[0K - Clears all characters from the cursor position to the end of the line. + // \033[1K - Clears all characters from start of line to the cursor position. + // \033[2K - Clears all characters of the whole line. + + uint num = (numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F); + COORD origin = {0,info.dwCursorPosition.Y}; + SHORT cnt; + if(num==1) + { + cnt = info.dwCursorPosition.X + 1; + } + else if(num==2) + { + cnt = info.dwSize.X; + } + else// 0 and default + { + origin = info.dwCursorPosition; + cnt = info.dwSize.X - info.dwCursorPosition.X; // how many spaces until line is full + } + FillConsoleOutputAttribute(handle, info.wAttributes, cnt, origin, NULL); + FillConsoleOutputCharacter(handle, ' ', cnt, origin, NULL); + } + else if( *q == 'H' || *q == 'f' ) + { // \033[#;#H - Cursor Position (CUP) + // \033[#;#f - Horizontal & Vertical Position + // The first # specifies the line number, the second # specifies the column. + // The default for both is 1 + info.dwCursorPosition.X = (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F-1):0; + info.dwCursorPosition.Y = (numpoint && numbers[numpoint-1])?(numbers[numpoint-1]>>4)*10+(numbers[numpoint-1]&0x0F-1):0; + + if( info.dwCursorPosition.X >= info.dwSize.X ) info.dwCursorPosition.Y = info.dwSize.X-1; + if( info.dwCursorPosition.Y >= info.dwSize.Y ) info.dwCursorPosition.Y = info.dwSize.Y-1; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q=='s' ) + { // \033[s - Save Cursor Position (SCP) + /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs] + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo(handle, &info); + saveposition = info.dwCursorPosition; + */ + } + else if( *q=='u' ) + { // \033[u - Restore cursor position (RCP) + /* XXX Two streams are being used. Disabled to avoid inconsistency [flaviojs] + SetConsoleCursorPosition(handle, saveposition); + */ + } + else if( *q == 'A' ) + { // \033[#A - Cursor Up (CUU) + // Moves the cursor UP # number of lines + info.dwCursorPosition.Y -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + + if( info.dwCursorPosition.Y < 0 ) + info.dwCursorPosition.Y = 0; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'B' ) + { // \033[#B - Cursor Down (CUD) + // Moves the cursor DOWN # number of lines + info.dwCursorPosition.Y += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + + if( info.dwCursorPosition.Y >= info.dwSize.Y ) + info.dwCursorPosition.Y = info.dwSize.Y-1; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'C' ) + { // \033[#C - Cursor Forward (CUF) + // Moves the cursor RIGHT # number of columns + info.dwCursorPosition.X += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + + if( info.dwCursorPosition.X >= info.dwSize.X ) + info.dwCursorPosition.X = info.dwSize.X-1; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'D' ) + { // \033[#D - Cursor Backward (CUB) + // Moves the cursor LEFT # number of columns + info.dwCursorPosition.X -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + + if( info.dwCursorPosition.X < 0 ) + info.dwCursorPosition.X = 0; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'E' ) + { // \033[#E - Cursor Next Line (CNL) + // Moves the cursor down the indicated # of rows, to column 1 + info.dwCursorPosition.Y += (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + info.dwCursorPosition.X = 0; + + if( info.dwCursorPosition.Y >= info.dwSize.Y ) + info.dwCursorPosition.Y = info.dwSize.Y-1; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'F' ) + { // \033[#F - Cursor Preceding Line (CPL) + // Moves the cursor up the indicated # of rows, to column 1. + info.dwCursorPosition.Y -= (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F):1; + info.dwCursorPosition.X = 0; + + if( info.dwCursorPosition.Y < 0 ) + info.dwCursorPosition.Y = 0; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'G' ) + { // \033[#G - Cursor Horizontal Absolute (CHA) + // Moves the cursor to indicated column in current row. + info.dwCursorPosition.X = (numbers[numpoint])?(numbers[numpoint]>>4)*10+(numbers[numpoint]&0x0F-1):0; + + if( info.dwCursorPosition.X >= info.dwSize.X ) + info.dwCursorPosition.X = info.dwSize.X-1; + SetConsoleCursorPosition(handle, info.dwCursorPosition); + } + else if( *q == 'L' || *q == 'M' || *q == '@' || *q == 'P') + { // not implemented, just skip + } + else + { // no number nor valid sequencer + // something is fishy, we break and give the current char free + --q; + } + // skip the sequencer and search again + p = q+1; + break; + }// end while + } + } + if (*p) // write the rest of the buffer + if( 0==WriteConsole(handle, p, strlen(p), &written, 0) ) + WriteFile(handle,p, strlen(p), &written, 0); + return 0; +} + +int FPRINTF(HANDLE handle, const char *fmt, ...) +{ + int ret; + va_list argptr; + va_start(argptr, fmt); + ret = VFPRINTF(handle,fmt,argptr); + va_end(argptr); + return ret; +} + +#define FFLUSH(handle) + +#define STDOUT GetStdHandle(STD_OUTPUT_HANDLE) +#define STDERR GetStdHandle(STD_ERROR_HANDLE) + +#else // not _WIN32 + + +//#define VPRINTF vprintf +//#define PRINTF printf + +#define is_console(file) (0!=isatty(fileno(file))) + +//vprintf_without_ansiformats +int VFPRINTF(FILE *file, const char *fmt, va_list argptr) +{ + char *p, *q; + + if(!fmt || !*fmt) + return 0; + + if( is_console(file) || stdout_with_ansisequence ) + { + vfprintf(file, fmt, argptr); + return 0; + } + + if(tempbuf == NULL) + tempbuf_alloc(); + for(; vsnprintf(tempbuf, tempbuf_size(), fmt, argptr)<0; tempbuf_realloc()); + // vsnprintf returns -1 in case of insufficient buffer size + // tempbuf.realloc doubles the size of the buffer in this case + + // start with processing + p = tempbuf; + while ((q = strchr(p, 0x1b)) != NULL) + { // find the escape character + fprintf(file, "%.*s", (int)(q-p), p); // write up to the escape + if( q[1]!='[' ) + { // write the escape char (whatever purpose it has) + fprintf(file, "%.*s", 1, q); + p=q+1; //and start searching again + } + else + { // from here, we will skip the '\033[' + // we break at the first unprocessible position + // assuming regular text is starting there + + // skip escape and bracket + q=q+2; + while(1) + { + if( isdigit((int)((unsigned char)*q)) ) + { + ++q; + // and next character + continue; + } + else if( *q == ';' ) + { // delimiter + ++q; + // and next number + continue; + } + else if( *q == 'm' ) + { // \033[#;...;#m - Set Graphics Rendition (SGR) + // set the attributes + } + else if( *q=='J' ) + { // \033[#J - Erase Display (ED) + } + else if( *q=='K' ) + { // \033[K : clear line from actual position to end of the line + } + else if( *q == 'H' || *q == 'f' ) + { // \033[#;#H - Cursor Position (CUP) + // \033[#;#f - Horizontal & Vertical Position + } + else if( *q=='s' ) + { // \033[s - Save Cursor Position (SCP) + } + else if( *q=='u' ) + { // \033[u - Restore cursor position (RCP) + } + else if( *q == 'A' ) + { // \033[#A - Cursor Up (CUU) + // Moves the cursor UP # number of lines + } + else if( *q == 'B' ) + { // \033[#B - Cursor Down (CUD) + // Moves the cursor DOWN # number of lines + } + else if( *q == 'C' ) + { // \033[#C - Cursor Forward (CUF) + // Moves the cursor RIGHT # number of columns + } + else if( *q == 'D' ) + { // \033[#D - Cursor Backward (CUB) + // Moves the cursor LEFT # number of columns + } + else if( *q == 'E' ) + { // \033[#E - Cursor Next Line (CNL) + // Moves the cursor down the indicated # of rows, to column 1 + } + else if( *q == 'F' ) + { // \033[#F - Cursor Preceding Line (CPL) + // Moves the cursor up the indicated # of rows, to column 1. + } + else if( *q == 'G' ) + { // \033[#G - Cursor Horizontal Absolute (CHA) + // Moves the cursor to indicated column in current row. + } + else if( *q == 'L' || *q == 'M' || *q == '@' || *q == 'P') + { // not implemented, just skip + } + else + { // no number nor valid sequencer + // something is fishy, we break and give the current char free + --q; + } + // skip the sequencer and search again + p = q+1; + break; + }// end while + } + } + if (*p) // write the rest of the buffer + fprintf(file, "%s", p); + return 0; +} +int FPRINTF(FILE *file, const char *fmt, ...) +{ + int ret; + va_list argptr; + va_start(argptr, fmt); + ret = VFPRINTF(file,fmt,argptr); + va_end(argptr); + return ret; +} + +#define FFLUSH fflush + +#define STDOUT stdout +#define STDERR stderr + +#endif// not _WIN32 + + + + + + + + + + +char timestamp_format[20] = ""; //For displaying Timestamps + +// by MC Cameri +int _vShowMessage(enum msg_type flag, const char *string, va_list ap) +{ + // _ShowMessage MUST be used instead of printf as of 10/24/2004. + // Return: 0 = Successful, 1 = Failed. +// int ret = 0; + char prefix[100]; +#if defined(DEBUGLOGMAP) || defined(DEBUGLOGCHAR) || defined(DEBUGLOGLOGIN) + FILE *fp; +#endif + + if (!string || *string == '\0') { + ShowError("Empty string passed to _vShowMessage().\n"); + return 1; + } + if ((flag == MSG_DEBUG && !SHOW_DEBUG_MSG) || + (flag == MSG_INFORMATION && msg_silent&1) || + (flag == MSG_STATUS && msg_silent&2) || + (flag == MSG_NOTICE && msg_silent&4) || + (flag == MSG_WARNING && msg_silent&8) || + (flag == MSG_ERROR && msg_silent&16) || + (flag == MSG_SQL && msg_silent&16)) + return 0; //Do not print it. + + if (timestamp_format[0]) + { //Display time format. [Skotlex] + time_t t = time(NULL); + strftime(prefix, 80, timestamp_format, localtime(&t)); + } else prefix[0]='\0'; + + switch (flag) { + case MSG_NONE: // direct printf replacement + break; + case MSG_STATUS: //Bright Green (To inform about good things) + strcat(prefix,CL_GREEN"[Status]"CL_RESET":"); + break; + case MSG_SQL: //Bright Violet (For dumping out anything related with SQL) <- Actually, this is mostly used for SQL errors with the database, as successes can as well just be anything else... [Skotlex] + strcat(prefix,CL_MAGENTA"[SQL]"CL_RESET":"); + break; + case MSG_INFORMATION: //Bright White (Variable information) + strcat(prefix,CL_WHITE"[Info]"CL_RESET":"); + break; + case MSG_NOTICE: //Bright White (Less than a warning) + strcat(prefix,CL_WHITE"[Notice]"CL_RESET":"); + break; + case MSG_WARNING: //Bright Yellow + strcat(prefix,CL_YELLOW"[Warning]"CL_RESET":"); + break; + case MSG_DEBUG: //Bright Cyan, important stuff! + strcat(prefix,CL_CYAN"[Debug]"CL_RESET":"); + break; + case MSG_ERROR: //Bright Red (Regular errors) + strcat(prefix,CL_RED"[Error]"CL_RESET":"); + break; + case MSG_FATALERROR: //Bright Red (Fatal errors, abort(); if possible) + strcat(prefix,CL_RED"[Fatal Error]"CL_RESET":"); + break; + default: + ShowError("In function _vShowMessage() -> Invalid flag passed.\n"); + return 1; + } + + if (flag == MSG_ERROR || flag == MSG_FATALERROR || flag == MSG_SQL) + { //Send Errors to StdErr [Skotlex] + FPRINTF(STDERR, "%s ", prefix); + VFPRINTF(STDERR, string, ap); + FFLUSH(STDERR); + } else { + if (flag != MSG_NONE) + FPRINTF(STDOUT, "%s ", prefix); + VFPRINTF(STDOUT, string, ap); + FFLUSH(STDOUT); + } + +#if defined(DEBUGLOGMAP) || defined(DEBUGLOGCHAR) || defined(DEBUGLOGLOGIN) + if(strlen(DEBUGLOGPATH) > 0) { + fp=fopen(DEBUGLOGPATH,"a"); + if (fp == NULL) { + FPRINTF(STDERR, CL_RED"[ERROR]"CL_RESET": Could not open '"CL_WHITE"%s"CL_RESET"', access denied.\n", DEBUGLOGPATH); + FFLUSH(STDERR); + } else { + fprintf(fp,"%s ", prefix); + vfprintf(fp,string,ap); + fclose(fp); + } + } else { + FPRINTF(STDERR, CL_RED"[ERROR]"CL_RESET": DEBUGLOGPATH not defined!\n"); + FFLUSH(STDERR); + } +#endif + + va_end(ap); + return 0; +} + +void ClearScreen(void) +{ +#ifndef _WIN32 + ShowMessage(CL_CLS); // to prevent empty string passed messages +#endif +} +int _ShowMessage(enum msg_type flag, const char *string, ...) +{ + int ret; + va_list ap; + va_start(ap, string); + ret = _vShowMessage(flag, string, ap); + va_end(ap); + return ret; +} + +// direct printf replacement +int ShowMessage(const char *string, ...) { + int ret; + va_list ap; + va_start(ap, string); + ret = _vShowMessage(MSG_NONE, string, ap); + va_end(ap); + return ret; +} +int ShowStatus(const char *string, ...) { + int ret; + va_list ap; + va_start(ap, string); + ret = _vShowMessage(MSG_STATUS, string, ap); + va_end(ap); + return ret; +} +int ShowSQL(const char *string, ...) { + int ret; + va_list ap; + va_start(ap, string); + ret = _vShowMessage(MSG_SQL, string, ap); + va_end(ap); + return ret; +} +int ShowInfo(const char *string, ...) { + int ret; + va_list ap; + va_start(ap, string); + ret = _vShowMessage(MSG_INFORMATION, string, ap); + va_end(ap); + return ret; +} +int ShowNotice(const char *string, ...) { + int ret; + va_list ap; + va_start(ap, string); + ret = _vShowMessage(MSG_NOTICE, string, ap); + va_end(ap); + return ret; +} +int ShowWarning(const char *string, ...) { + int ret; + va_list ap; + va_start(ap, string); + ret = _vShowMessage(MSG_WARNING, string, ap); + va_end(ap); + return ret; +} +int ShowDebug(const char *string, ...) { + int ret; + va_list ap; + va_start(ap, string); + ret = _vShowMessage(MSG_DEBUG, string, ap); + va_end(ap); + return ret; +} +int ShowError(const char *string, ...) { + int ret; + va_list ap; + va_start(ap, string); + ret = _vShowMessage(MSG_ERROR, string, ap); + va_end(ap); + return ret; +} +int ShowFatalError(const char *string, ...) { + int ret; + va_list ap; + va_start(ap, string); + ret = _vShowMessage(MSG_FATALERROR, string, ap); + va_end(ap); + return ret; +} diff --git a/src/common/showmsg.h b/src/common/showmsg.h index f947b34e3..cf9c16d23 100644 --- a/src/common/showmsg.h +++ b/src/common/showmsg.h @@ -1,96 +1,96 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _SHOWMSG_H_ -#define _SHOWMSG_H_ - -#define SHOW_DEBUG_MSG 1 -// for help with the console colors look here: -// http://www.edoceo.com/liberum/?doc=printf-with-color -// some code explanation (used here): -// \033[2J : clear screen and go up/left (0, 0 position) -// \033[K : clear line from actual position to end of the line -// \033[0m : reset color parameter -// \033[1m : use bold for font - -#define CL_RESET "\033[0m" -#define CL_CLS "\033[2J" -#define CL_CLL "\033[K" - -// font settings -#define CL_BOLD "\033[1m" -#define CL_NORM CL_RESET -#define CL_NORMAL CL_RESET -#define CL_NONE CL_RESET -// foreground color and bold font (bright color on windows) -#define CL_WHITE "\033[1;37m" -#define CL_GRAY "\033[1;30m" -#define CL_RED "\033[1;31m" -#define CL_GREEN "\033[1;32m" -#define CL_YELLOW "\033[1;33m" -#define CL_BLUE "\033[1;34m" -#define CL_MAGENTA "\033[1;35m" -#define CL_CYAN "\033[1;36m" - -// background color -#define CL_BG_BLACK "\033[40m" -#define CL_BG_RED "\033[41m" -#define CL_BG_GREEN "\033[42m" -#define CL_BG_YELLOW "\033[43m" -#define CL_BG_BLUE "\033[44m" -#define CL_BG_MAGENTA "\033[45m" -#define CL_BG_CYAN "\033[46m" -#define CL_BG_WHITE "\033[47m" -// foreground color and normal font (normal color on windows) -#define CL_LT_BLACK "\033[0;30m" -#define CL_LT_RED "\033[0;31m" -#define CL_LT_GREEN "\033[0;32m" -#define CL_LT_YELLOW "\033[0;33m" -#define CL_LT_BLUE "\033[0;34m" -#define CL_LT_MAGENTA "\033[0;35m" -#define CL_LT_CYAN "\033[0;36m" -#define CL_LT_WHITE "\033[0;37m" -// foreground color and bold font (bright color on windows) -#define CL_BT_BLACK "\033[1;30m" -#define CL_BT_RED "\033[1;31m" -#define CL_BT_GREEN "\033[1;32m" -#define CL_BT_YELLOW "\033[1;33m" -#define CL_BT_BLUE "\033[1;34m" -#define CL_BT_MAGENTA "\033[1;35m" -#define CL_BT_CYAN "\033[1;36m" -#define CL_BT_WHITE "\033[1;37m" - -#define CL_WTBL "\033[37;44m" // white on blue -#define CL_XXBL "\033[0;44m" // default on blue -#define CL_PASS "\033[0;32;42m" // green on green - -#define CL_SPACE " " // space aquivalent of the print messages - -extern int stdout_with_ansisequence; //If the color ansi sequences are to be used. [flaviojs] -extern int msg_silent; //Specifies how silent the console is. [Skotlex] -extern char timestamp_format[20]; //For displaying Timestamps [Skotlex] - -enum msg_type { - MSG_NONE, - MSG_STATUS, - MSG_SQL, - MSG_INFORMATION, - MSG_NOTICE, - MSG_WARNING, - MSG_DEBUG, - MSG_ERROR, - MSG_FATALERROR -}; - -extern void ClearScreen(void); -extern int ShowMessage(const char *, ...); -extern int ShowStatus(const char *, ...); -extern int ShowSQL(const char *, ...); -extern int ShowInfo(const char *, ...); -extern int ShowNotice(const char *, ...); -extern int ShowWarning(const char *, ...); -extern int ShowDebug(const char *, ...); -extern int ShowError(const char *, ...); -extern int ShowFatalError(const char *, ...); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _SHOWMSG_H_ +#define _SHOWMSG_H_ + +#define SHOW_DEBUG_MSG 1 +// for help with the console colors look here: +// http://www.edoceo.com/liberum/?doc=printf-with-color +// some code explanation (used here): +// \033[2J : clear screen and go up/left (0, 0 position) +// \033[K : clear line from actual position to end of the line +// \033[0m : reset color parameter +// \033[1m : use bold for font + +#define CL_RESET "\033[0m" +#define CL_CLS "\033[2J" +#define CL_CLL "\033[K" + +// font settings +#define CL_BOLD "\033[1m" +#define CL_NORM CL_RESET +#define CL_NORMAL CL_RESET +#define CL_NONE CL_RESET +// foreground color and bold font (bright color on windows) +#define CL_WHITE "\033[1;37m" +#define CL_GRAY "\033[1;30m" +#define CL_RED "\033[1;31m" +#define CL_GREEN "\033[1;32m" +#define CL_YELLOW "\033[1;33m" +#define CL_BLUE "\033[1;34m" +#define CL_MAGENTA "\033[1;35m" +#define CL_CYAN "\033[1;36m" + +// background color +#define CL_BG_BLACK "\033[40m" +#define CL_BG_RED "\033[41m" +#define CL_BG_GREEN "\033[42m" +#define CL_BG_YELLOW "\033[43m" +#define CL_BG_BLUE "\033[44m" +#define CL_BG_MAGENTA "\033[45m" +#define CL_BG_CYAN "\033[46m" +#define CL_BG_WHITE "\033[47m" +// foreground color and normal font (normal color on windows) +#define CL_LT_BLACK "\033[0;30m" +#define CL_LT_RED "\033[0;31m" +#define CL_LT_GREEN "\033[0;32m" +#define CL_LT_YELLOW "\033[0;33m" +#define CL_LT_BLUE "\033[0;34m" +#define CL_LT_MAGENTA "\033[0;35m" +#define CL_LT_CYAN "\033[0;36m" +#define CL_LT_WHITE "\033[0;37m" +// foreground color and bold font (bright color on windows) +#define CL_BT_BLACK "\033[1;30m" +#define CL_BT_RED "\033[1;31m" +#define CL_BT_GREEN "\033[1;32m" +#define CL_BT_YELLOW "\033[1;33m" +#define CL_BT_BLUE "\033[1;34m" +#define CL_BT_MAGENTA "\033[1;35m" +#define CL_BT_CYAN "\033[1;36m" +#define CL_BT_WHITE "\033[1;37m" + +#define CL_WTBL "\033[37;44m" // white on blue +#define CL_XXBL "\033[0;44m" // default on blue +#define CL_PASS "\033[0;32;42m" // green on green + +#define CL_SPACE " " // space aquivalent of the print messages + +extern int stdout_with_ansisequence; //If the color ansi sequences are to be used. [flaviojs] +extern int msg_silent; //Specifies how silent the console is. [Skotlex] +extern char timestamp_format[20]; //For displaying Timestamps [Skotlex] + +enum msg_type { + MSG_NONE, + MSG_STATUS, + MSG_SQL, + MSG_INFORMATION, + MSG_NOTICE, + MSG_WARNING, + MSG_DEBUG, + MSG_ERROR, + MSG_FATALERROR +}; + +extern void ClearScreen(void); +extern int ShowMessage(const char *, ...); +extern int ShowStatus(const char *, ...); +extern int ShowSQL(const char *, ...); +extern int ShowInfo(const char *, ...); +extern int ShowNotice(const char *, ...); +extern int ShowWarning(const char *, ...); +extern int ShowDebug(const char *, ...); +extern int ShowError(const char *, ...); +extern int ShowFatalError(const char *, ...); + +#endif diff --git a/src/common/strlib.h b/src/common/strlib.h index 225228c74..d3b9a1dec 100644 --- a/src/common/strlib.h +++ b/src/common/strlib.h @@ -1,24 +1,24 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _J_STR_LIB_H_ -#define _J_STR_LIB_H_ -#define J_MAX_MALLOC_SIZE 65535 -// String function library. -// code by Jioh L. Jung (ziozzang@4wish.net) -// This code is under license "BSD" -char* jstrescape (char* pt); -char* jstrescapecpy (char* pt,char* spt); -int jmemescapecpy (char* pt,char* spt, int size); - -#ifdef __WIN32 -#define HAVE_STRTOK_R -#define strtok_r(s,delim,save_ptr) _strtok_r((s),(delim),(save_ptr)) -char *_strtok_r(char *s1, const char *s2, char **lasts); -#endif - -// custom functions -int remove_control_chars(unsigned char *); -char *trim(char *str, const char *delim); -const char *stristr(const char *haystack, const char *needle); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _J_STR_LIB_H_ +#define _J_STR_LIB_H_ +#define J_MAX_MALLOC_SIZE 65535 +// String function library. +// code by Jioh L. Jung (ziozzang@4wish.net) +// This code is under license "BSD" +char* jstrescape (char* pt); +char* jstrescapecpy (char* pt,char* spt); +int jmemescapecpy (char* pt,char* spt, int size); + +#ifdef __WIN32 +#define HAVE_STRTOK_R +#define strtok_r(s,delim,save_ptr) _strtok_r((s),(delim),(save_ptr)) +char *_strtok_r(char *s1, const char *s2, char **lasts); +#endif + +// custom functions +int remove_control_chars(unsigned char *); +char *trim(char *str, const char *delim); +const char *stristr(const char *haystack, const char *needle); +#endif diff --git a/src/common/timer.c b/src/common/timer.c index a848266ac..77d88d17d 100644 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -1,436 +1,436 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include - -#ifdef __WIN32 -#define __USE_W32_SOCKETS -// Well, this won't last another 30++ years (where conversion will truncate). -//#define _USE_32BIT_TIME_T // use 32 bit time variables on 64bit windows -#include -#else -#include -#include -#endif - - -#include -#include -#include -#include - -#include "timer.h" -#include "malloc.h" -#include "showmsg.h" - -// タイマー間隔の最小値。モンスターの大量召還時、多数のクライアント接続時に -// サーバーが反応しなくなる場合は、TIMER_MIN_INTERVAL を増やしてください。 - -// If the server shows no reaction when processing thousands of monsters -// or connected by many clients, please increase TIMER_MIN_INTERVAL. - -#define TIMER_MIN_INTERVAL 50 - -static struct TimerData* timer_data = NULL; -static int timer_data_max = 0; -static int timer_data_num = 0; - -static int* free_timer_list = NULL; -static int free_timer_list_max = 0; -static int free_timer_list_pos = 0; - -static int timer_heap_num = 0; -static int timer_heap_max = 0; -static int* timer_heap = NULL; - -static int fix_heap_flag =0; //Flag for fixing the stack only once per tick loop. May not be the best way, but it's all I can think of currently :X [Skotlex] - -// for debug -struct timer_func_list { - int (*func)(int,unsigned int,int,int); - struct timer_func_list* next; - char* name; -}; -static struct timer_func_list* tfl_root; - -time_t start_time; - -#ifdef __WIN32 -/* Modified struct timezone to void - we pass NULL anyway */ -void gettimeofday (struct timeval *t, void *dummy) -{ - DWORD millisec = GetTickCount(); - - t->tv_sec = (int) (millisec / 1000); - t->tv_usec = (millisec % 1000) * 1000; -} -#endif - -// -int add_timer_func_list(int (*func)(int,unsigned int,int,int), char* name) -{ - struct timer_func_list* tfl; - - if (name) { - tfl = (struct timer_func_list*) aCalloc (sizeof(struct timer_func_list), 1); - tfl->name = (char *) aMalloc (strlen(name) + 1); - - tfl->next = tfl_root; - tfl->func = func; - strcpy(tfl->name, name); - tfl_root = tfl; - } - return 0; -} - -char* search_timer_func_list(int (*func)(int,unsigned int,int,int)) -{ - struct timer_func_list* tfl = tfl_root; - while (tfl) { - if (func == tfl->func) - return tfl->name; - tfl = tfl->next; - } - - return "unknown timer function"; -} - -/*---------------------------- - * Get tick time - *----------------------------*/ -static unsigned int gettick_cache; -static int gettick_count; - -unsigned int gettick_nocache(void) -{ - struct timeval tval; - - gettimeofday(&tval, NULL); - gettick_count = 256; - - return gettick_cache = tval.tv_sec * 1000 + tval.tv_usec / 1000; -} - -unsigned int gettick(void) -{ - gettick_count--; - if (gettick_count < 0) - return gettick_nocache(); - - return gettick_cache; -} - -/*====================================== - * CORE : Timer Heap - *-------------------------------------- - */ -static void push_timer_heap(int index) -{ - int i, j; - int min, max, pivot; // for sorting - - // check number of element - if (timer_heap_num >= timer_heap_max) { - if (timer_heap_max == 0) { - timer_heap_max = 256; - timer_heap = (int *) aCalloc( sizeof(int) , 256); - } else { - timer_heap_max += 256; - timer_heap = (int *) aRealloc( timer_heap, sizeof(int) * timer_heap_max); - malloc_tsetdword(timer_heap + (timer_heap_max - 256), 0, sizeof(int) * 256); - } - } - - // do a sorting from higher to lower - j = timer_data[index].tick; // speed up - // with less than 4 values, it's speeder to use simple loop - if (timer_heap_num < 4) { - for(i = timer_heap_num; i > 0; i--) -// if (j < timer_data[timer_heap[i - 1]].tick) //Plain comparisons break on bound looping timers. [Skotlex] - if (DIFF_TICK(j, timer_data[timer_heap[i - 1]].tick) < 0) - break; - else - timer_heap[i] = timer_heap[i - 1]; - timer_heap[i] = index; - // searching by dichotomie - } else { - // if lower actual item is higher than new -// if (j < timer_data[timer_heap[timer_heap_num - 1]].tick) //Plain comparisons break on bound looping timers. [Skotlex] - if (DIFF_TICK(j, timer_data[timer_heap[timer_heap_num - 1]].tick) < 0) - timer_heap[timer_heap_num] = index; - else { - // searching position - min = 0; - max = timer_heap_num - 1; - while (min < max) { - pivot = (min + max) / 2; -// if (j < timer_data[timer_heap[pivot]].tick) //Plain comparisons break on bound looping timers. [Skotlex] - if (DIFF_TICK(j, timer_data[timer_heap[pivot]].tick) < 0) - min = pivot + 1; - else - max = pivot; - } - // move elements - do loop if there are a little number of elements to move - if (timer_heap_num - min < 5) { - for(i = timer_heap_num; i > min; i--) - timer_heap[i] = timer_heap[i - 1]; - // move elements - else use memmove (speeder for a lot of elements) - } else - memmove(&timer_heap[min + 1], &timer_heap[min], sizeof(int) * (timer_heap_num - min)); - // save new element - timer_heap[min] = index; - } - } - - timer_heap_num++; -} - -/*========================== - * Timer Management - *-------------------------- - */ - -int acquire_timer (void) -{ - int i; - - if (free_timer_list_pos) { - do { - i = free_timer_list[--free_timer_list_pos]; - } while(i >= timer_data_num && free_timer_list_pos > 0); - } else - i = timer_data_num; - - if (i >= timer_data_num) - for (i = timer_data_num; i < timer_data_max && timer_data[i].type; i++); - if (i >= timer_data_num && i >= timer_data_max) { - if (timer_data_max == 0) { - timer_data_max = 256; - timer_data = (struct TimerData*) aCalloc( sizeof(struct TimerData) , timer_data_max); - } else { - timer_data_max += 256; - timer_data = (struct TimerData *) aRealloc( timer_data, sizeof(struct TimerData) * timer_data_max); - malloc_tsetdword(timer_data + (timer_data_max - 256), 0, sizeof(struct TimerData) * 256); - } - } - - return i; -} - -int add_timer(unsigned int tick,int (*func)(int,unsigned int,int,int), int id, int data) -{ - int tid = acquire_timer(); - - timer_data[tid].tick = tick; - timer_data[tid].func = func; - timer_data[tid].id = id; - timer_data[tid].data = data; - timer_data[tid].type = TIMER_ONCE_AUTODEL; - timer_data[tid].interval = 1000; - push_timer_heap(tid); - - if (tid >= timer_data_num) - timer_data_num = tid + 1; - - return tid; -} - -int add_timer_interval(unsigned int tick, int (*func)(int,unsigned int,int,int), int id, int data, int interval) -{ - int tid; - - if (interval < 1) { - ShowError("add_timer_interval : function %08x(%s) has invalid interval %d!\n", - (int)func, search_timer_func_list(func), interval); - return -1; - } - - tid = acquire_timer(); - timer_data[tid].tick = tick; - timer_data[tid].func = func; - timer_data[tid].id = id; - timer_data[tid].data = data; - timer_data[tid].type = TIMER_INTERVAL; - timer_data[tid].interval = interval; - push_timer_heap(tid); - - if (tid >= timer_data_num) - timer_data_num = tid + 1; - - return tid; -} - -int delete_timer(int id, int (*func)(int,unsigned int,int,int)) -{ - if (id <= 0 || id >= timer_data_num) { - ShowError("delete_timer error : no such timer %d (%08x(%s))\n", id, (int)func, search_timer_func_list(func)); - return -1; - } - if (timer_data[id].func != func) { - ShowError("delete_timer error : function mismatch %08x(%s) != %08x(%s)\n", - (int)timer_data[id].func, search_timer_func_list(timer_data[id].func), - (int)func, search_timer_func_list(func)); - return -2; - } - // そのうち消えるにまかせる - timer_data[id].func = NULL; - timer_data[id].type = TIMER_ONCE_AUTODEL; - - return 0; -} - -int addtick_timer(int tid, unsigned int tick) -{ - return timer_data[tid].tick += tick; -} - -//Sets the tick at which the timer triggers directly (meant as a replacement of delete_timer + add_timer) [Skotlex] -//FIXME: DON'T use this function yet, it is not correctly reorganizing the timer stack causing unexpected problems later on! -int settick_timer(int tid, unsigned int tick) -{ - int i,j; - if (timer_data[tid].tick == tick) - return tick; - - //FIXME: This search is not all that effective... there doesn't seems to be a better way to locate an element in the heap. - for(i = timer_heap_num-1; i >= 0 && timer_heap[i] != tid; i--); - - if (i < 0) - return -1; //Sort of impossible, isn't it? - if (DIFF_TICK(timer_data[tid].tick, tick) > 0) - { //Timer is accelerated, shift timer near the end of the heap. - if (i == timer_heap_num-1) //Nothing to shift. - j = timer_heap_num-1; - else { - for (j = i+1; j < timer_heap_num && DIFF_TICK(timer_data[j].tick, tick) > 0; j++); - j--; - memmove(&timer_heap[i], &timer_heap[i+1], (j-i)*sizeof(int)); - } - } else { //Timer is delayed, shift timer near the beginning of the heap. - if (i == 0) //Nothing to shift. - j = 0; - else { - for (j = i-1; j >= 0 && DIFF_TICK(timer_data[j].tick, tick) < 0; j--); - j++; - memmove(&timer_heap[j+1], &timer_heap[j], (i-j)*sizeof(int)); - } - } - timer_heap[j] = tid; - timer_data[tid].tick = tick; - return tick; -} - -struct TimerData* get_timer(int tid) -{ - return &timer_data[tid]; -} - -//Correcting the heap when the tick overflows is an idea taken from jA to -//prevent timer problems. Thanks to [End of Exam] for providing the required data. [Skotlex] -//This funtion will rearrange the heap and assign new tick values. -static void fix_timer_heap(unsigned int tick) -{ - if (timer_heap_num >= 0 && tick < 0x00010000 && timer_data[timer_heap[0]].tick > 0xf0000000) - { //The last timer is way too far into the future, and the current tick is too close to 0, overflow was very likely - //(not perfect, but will work as long as the timer is not expected to happen 50 or so days into the future) - int i; - int *tmp_heap; - for (i=0; i < timer_heap_num && timer_data[timer_heap[i]].tick > 0xf0000000; i++) - { //All functions with high tick value should had been executed already... - timer_data[timer_heap[i]].tick = 0; - } - //Move elements to readjust the heap. - tmp_heap = aCalloc(sizeof(int), i); - memmove(&tmp_heap[0], &timer_heap[0], i*sizeof(int)); - memmove(&timer_heap[0], &timer_heap[i], (timer_heap_num-i)*sizeof(int)); - memmove(&timer_heap[timer_heap_num-i], &tmp_heap[0], i*sizeof(int)); - aFree(tmp_heap); - } -} - -int do_timer(unsigned int tick) -{ - int i, nextmin = 1000; - - if (tick < 0x010000 && fix_heap_flag) - { - fix_timer_heap(tick); - fix_heap_flag = 0; - } - - while(timer_heap_num) { - i = timer_heap[timer_heap_num - 1]; // next shorter element - if ((nextmin = DIFF_TICK(timer_data[i].tick, tick)) > 0) - break; - if (timer_heap_num > 0) // suppress the actual element from the table - timer_heap_num--; - timer_data[i].type |= TIMER_REMOVE_HEAP; - if (timer_data[i].func) { - if (nextmin < -1000) { - // 1秒以上の大幅な遅延が発生しているので、 - // timer処理タイミングを現在値とする事で - // 呼び出し時タイミング(引数のtick)相対で処理してる - // timer関数の次回処理タイミングを遅らせる - timer_data[i].func(i, tick, timer_data[i].id, timer_data[i].data); - } else { - timer_data[i].func(i, timer_data[i].tick, timer_data[i].id, timer_data[i].data); - } - } - if (timer_data[i].type & TIMER_REMOVE_HEAP) { - switch(timer_data[i].type & ~TIMER_REMOVE_HEAP) { - case TIMER_ONCE_AUTODEL: - timer_data[i].type = 0; - if (free_timer_list_pos >= free_timer_list_max) { - free_timer_list_max += 256; - free_timer_list = (int *) aRealloc(free_timer_list, sizeof(int) * free_timer_list_max); - malloc_tsetdword(free_timer_list + (free_timer_list_max - 256), 0, 256 * sizeof(int)); - } - free_timer_list[free_timer_list_pos++] = i; - break; - case TIMER_INTERVAL: - if (DIFF_TICK(timer_data[i].tick , tick) < -1000) { - timer_data[i].tick = tick + timer_data[i].interval; - } else { - timer_data[i].tick += timer_data[i].interval; - } - timer_data[i].type &= ~TIMER_REMOVE_HEAP; - push_timer_heap(i); - break; - } - } - } - - if (nextmin < TIMER_MIN_INTERVAL) - nextmin = TIMER_MIN_INTERVAL; - - if ((unsigned int)(tick + nextmin) < tick) //Tick will loop, rearrange the heap on the next iteration. - fix_heap_flag = 1; - return nextmin; -} - -unsigned long get_uptime (void) -{ - return (unsigned long) difftime (time(NULL), start_time); -} - -void timer_init(void) -{ - time(&start_time); -} - -void timer_final(void) -{ - struct timer_func_list* tfl = tfl_root, *tfl2; - - while (tfl) { - tfl2 = tfl->next; // copy next pointer - aFree(tfl->name); // free structures - aFree(tfl); - tfl = tfl2; // use copied pointer for next cycle - } - - if (timer_data) aFree(timer_data); - if (timer_heap) aFree(timer_heap); - if (free_timer_list) aFree(free_timer_list); -} - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include + +#ifdef __WIN32 +#define __USE_W32_SOCKETS +// Well, this won't last another 30++ years (where conversion will truncate). +//#define _USE_32BIT_TIME_T // use 32 bit time variables on 64bit windows +#include +#else +#include +#include +#endif + + +#include +#include +#include +#include + +#include "timer.h" +#include "malloc.h" +#include "showmsg.h" + +// タイマー間隔の最小値。モンスターの大量召還時、多数のクライアント接続時に +// サーバーが反応しなくなる場合は、TIMER_MIN_INTERVAL を増やしてください。 + +// If the server shows no reaction when processing thousands of monsters +// or connected by many clients, please increase TIMER_MIN_INTERVAL. + +#define TIMER_MIN_INTERVAL 50 + +static struct TimerData* timer_data = NULL; +static int timer_data_max = 0; +static int timer_data_num = 0; + +static int* free_timer_list = NULL; +static int free_timer_list_max = 0; +static int free_timer_list_pos = 0; + +static int timer_heap_num = 0; +static int timer_heap_max = 0; +static int* timer_heap = NULL; + +static int fix_heap_flag =0; //Flag for fixing the stack only once per tick loop. May not be the best way, but it's all I can think of currently :X [Skotlex] + +// for debug +struct timer_func_list { + int (*func)(int,unsigned int,int,int); + struct timer_func_list* next; + char* name; +}; +static struct timer_func_list* tfl_root; + +time_t start_time; + +#ifdef __WIN32 +/* Modified struct timezone to void - we pass NULL anyway */ +void gettimeofday (struct timeval *t, void *dummy) +{ + DWORD millisec = GetTickCount(); + + t->tv_sec = (int) (millisec / 1000); + t->tv_usec = (millisec % 1000) * 1000; +} +#endif + +// +int add_timer_func_list(int (*func)(int,unsigned int,int,int), char* name) +{ + struct timer_func_list* tfl; + + if (name) { + tfl = (struct timer_func_list*) aCalloc (sizeof(struct timer_func_list), 1); + tfl->name = (char *) aMalloc (strlen(name) + 1); + + tfl->next = tfl_root; + tfl->func = func; + strcpy(tfl->name, name); + tfl_root = tfl; + } + return 0; +} + +char* search_timer_func_list(int (*func)(int,unsigned int,int,int)) +{ + struct timer_func_list* tfl = tfl_root; + while (tfl) { + if (func == tfl->func) + return tfl->name; + tfl = tfl->next; + } + + return "unknown timer function"; +} + +/*---------------------------- + * Get tick time + *----------------------------*/ +static unsigned int gettick_cache; +static int gettick_count; + +unsigned int gettick_nocache(void) +{ + struct timeval tval; + + gettimeofday(&tval, NULL); + gettick_count = 256; + + return gettick_cache = tval.tv_sec * 1000 + tval.tv_usec / 1000; +} + +unsigned int gettick(void) +{ + gettick_count--; + if (gettick_count < 0) + return gettick_nocache(); + + return gettick_cache; +} + +/*====================================== + * CORE : Timer Heap + *-------------------------------------- + */ +static void push_timer_heap(int index) +{ + int i, j; + int min, max, pivot; // for sorting + + // check number of element + if (timer_heap_num >= timer_heap_max) { + if (timer_heap_max == 0) { + timer_heap_max = 256; + timer_heap = (int *) aCalloc( sizeof(int) , 256); + } else { + timer_heap_max += 256; + timer_heap = (int *) aRealloc( timer_heap, sizeof(int) * timer_heap_max); + malloc_tsetdword(timer_heap + (timer_heap_max - 256), 0, sizeof(int) * 256); + } + } + + // do a sorting from higher to lower + j = timer_data[index].tick; // speed up + // with less than 4 values, it's speeder to use simple loop + if (timer_heap_num < 4) { + for(i = timer_heap_num; i > 0; i--) +// if (j < timer_data[timer_heap[i - 1]].tick) //Plain comparisons break on bound looping timers. [Skotlex] + if (DIFF_TICK(j, timer_data[timer_heap[i - 1]].tick) < 0) + break; + else + timer_heap[i] = timer_heap[i - 1]; + timer_heap[i] = index; + // searching by dichotomie + } else { + // if lower actual item is higher than new +// if (j < timer_data[timer_heap[timer_heap_num - 1]].tick) //Plain comparisons break on bound looping timers. [Skotlex] + if (DIFF_TICK(j, timer_data[timer_heap[timer_heap_num - 1]].tick) < 0) + timer_heap[timer_heap_num] = index; + else { + // searching position + min = 0; + max = timer_heap_num - 1; + while (min < max) { + pivot = (min + max) / 2; +// if (j < timer_data[timer_heap[pivot]].tick) //Plain comparisons break on bound looping timers. [Skotlex] + if (DIFF_TICK(j, timer_data[timer_heap[pivot]].tick) < 0) + min = pivot + 1; + else + max = pivot; + } + // move elements - do loop if there are a little number of elements to move + if (timer_heap_num - min < 5) { + for(i = timer_heap_num; i > min; i--) + timer_heap[i] = timer_heap[i - 1]; + // move elements - else use memmove (speeder for a lot of elements) + } else + memmove(&timer_heap[min + 1], &timer_heap[min], sizeof(int) * (timer_heap_num - min)); + // save new element + timer_heap[min] = index; + } + } + + timer_heap_num++; +} + +/*========================== + * Timer Management + *-------------------------- + */ + +int acquire_timer (void) +{ + int i; + + if (free_timer_list_pos) { + do { + i = free_timer_list[--free_timer_list_pos]; + } while(i >= timer_data_num && free_timer_list_pos > 0); + } else + i = timer_data_num; + + if (i >= timer_data_num) + for (i = timer_data_num; i < timer_data_max && timer_data[i].type; i++); + if (i >= timer_data_num && i >= timer_data_max) { + if (timer_data_max == 0) { + timer_data_max = 256; + timer_data = (struct TimerData*) aCalloc( sizeof(struct TimerData) , timer_data_max); + } else { + timer_data_max += 256; + timer_data = (struct TimerData *) aRealloc( timer_data, sizeof(struct TimerData) * timer_data_max); + malloc_tsetdword(timer_data + (timer_data_max - 256), 0, sizeof(struct TimerData) * 256); + } + } + + return i; +} + +int add_timer(unsigned int tick,int (*func)(int,unsigned int,int,int), int id, int data) +{ + int tid = acquire_timer(); + + timer_data[tid].tick = tick; + timer_data[tid].func = func; + timer_data[tid].id = id; + timer_data[tid].data = data; + timer_data[tid].type = TIMER_ONCE_AUTODEL; + timer_data[tid].interval = 1000; + push_timer_heap(tid); + + if (tid >= timer_data_num) + timer_data_num = tid + 1; + + return tid; +} + +int add_timer_interval(unsigned int tick, int (*func)(int,unsigned int,int,int), int id, int data, int interval) +{ + int tid; + + if (interval < 1) { + ShowError("add_timer_interval : function %08x(%s) has invalid interval %d!\n", + (int)func, search_timer_func_list(func), interval); + return -1; + } + + tid = acquire_timer(); + timer_data[tid].tick = tick; + timer_data[tid].func = func; + timer_data[tid].id = id; + timer_data[tid].data = data; + timer_data[tid].type = TIMER_INTERVAL; + timer_data[tid].interval = interval; + push_timer_heap(tid); + + if (tid >= timer_data_num) + timer_data_num = tid + 1; + + return tid; +} + +int delete_timer(int id, int (*func)(int,unsigned int,int,int)) +{ + if (id <= 0 || id >= timer_data_num) { + ShowError("delete_timer error : no such timer %d (%08x(%s))\n", id, (int)func, search_timer_func_list(func)); + return -1; + } + if (timer_data[id].func != func) { + ShowError("delete_timer error : function mismatch %08x(%s) != %08x(%s)\n", + (int)timer_data[id].func, search_timer_func_list(timer_data[id].func), + (int)func, search_timer_func_list(func)); + return -2; + } + // そのうち消えるにまかせる + timer_data[id].func = NULL; + timer_data[id].type = TIMER_ONCE_AUTODEL; + + return 0; +} + +int addtick_timer(int tid, unsigned int tick) +{ + return timer_data[tid].tick += tick; +} + +//Sets the tick at which the timer triggers directly (meant as a replacement of delete_timer + add_timer) [Skotlex] +//FIXME: DON'T use this function yet, it is not correctly reorganizing the timer stack causing unexpected problems later on! +int settick_timer(int tid, unsigned int tick) +{ + int i,j; + if (timer_data[tid].tick == tick) + return tick; + + //FIXME: This search is not all that effective... there doesn't seems to be a better way to locate an element in the heap. + for(i = timer_heap_num-1; i >= 0 && timer_heap[i] != tid; i--); + + if (i < 0) + return -1; //Sort of impossible, isn't it? + if (DIFF_TICK(timer_data[tid].tick, tick) > 0) + { //Timer is accelerated, shift timer near the end of the heap. + if (i == timer_heap_num-1) //Nothing to shift. + j = timer_heap_num-1; + else { + for (j = i+1; j < timer_heap_num && DIFF_TICK(timer_data[j].tick, tick) > 0; j++); + j--; + memmove(&timer_heap[i], &timer_heap[i+1], (j-i)*sizeof(int)); + } + } else { //Timer is delayed, shift timer near the beginning of the heap. + if (i == 0) //Nothing to shift. + j = 0; + else { + for (j = i-1; j >= 0 && DIFF_TICK(timer_data[j].tick, tick) < 0; j--); + j++; + memmove(&timer_heap[j+1], &timer_heap[j], (i-j)*sizeof(int)); + } + } + timer_heap[j] = tid; + timer_data[tid].tick = tick; + return tick; +} + +struct TimerData* get_timer(int tid) +{ + return &timer_data[tid]; +} + +//Correcting the heap when the tick overflows is an idea taken from jA to +//prevent timer problems. Thanks to [End of Exam] for providing the required data. [Skotlex] +//This funtion will rearrange the heap and assign new tick values. +static void fix_timer_heap(unsigned int tick) +{ + if (timer_heap_num >= 0 && tick < 0x00010000 && timer_data[timer_heap[0]].tick > 0xf0000000) + { //The last timer is way too far into the future, and the current tick is too close to 0, overflow was very likely + //(not perfect, but will work as long as the timer is not expected to happen 50 or so days into the future) + int i; + int *tmp_heap; + for (i=0; i < timer_heap_num && timer_data[timer_heap[i]].tick > 0xf0000000; i++) + { //All functions with high tick value should had been executed already... + timer_data[timer_heap[i]].tick = 0; + } + //Move elements to readjust the heap. + tmp_heap = aCalloc(sizeof(int), i); + memmove(&tmp_heap[0], &timer_heap[0], i*sizeof(int)); + memmove(&timer_heap[0], &timer_heap[i], (timer_heap_num-i)*sizeof(int)); + memmove(&timer_heap[timer_heap_num-i], &tmp_heap[0], i*sizeof(int)); + aFree(tmp_heap); + } +} + +int do_timer(unsigned int tick) +{ + int i, nextmin = 1000; + + if (tick < 0x010000 && fix_heap_flag) + { + fix_timer_heap(tick); + fix_heap_flag = 0; + } + + while(timer_heap_num) { + i = timer_heap[timer_heap_num - 1]; // next shorter element + if ((nextmin = DIFF_TICK(timer_data[i].tick, tick)) > 0) + break; + if (timer_heap_num > 0) // suppress the actual element from the table + timer_heap_num--; + timer_data[i].type |= TIMER_REMOVE_HEAP; + if (timer_data[i].func) { + if (nextmin < -1000) { + // 1秒以上の大幅な遅延が発生しているので、 + // timer処理タイミングを現在値とする事で + // 呼び出し時タイミング(引数のtick)相対で処理してる + // timer関数の次回処理タイミングを遅らせる + timer_data[i].func(i, tick, timer_data[i].id, timer_data[i].data); + } else { + timer_data[i].func(i, timer_data[i].tick, timer_data[i].id, timer_data[i].data); + } + } + if (timer_data[i].type & TIMER_REMOVE_HEAP) { + switch(timer_data[i].type & ~TIMER_REMOVE_HEAP) { + case TIMER_ONCE_AUTODEL: + timer_data[i].type = 0; + if (free_timer_list_pos >= free_timer_list_max) { + free_timer_list_max += 256; + free_timer_list = (int *) aRealloc(free_timer_list, sizeof(int) * free_timer_list_max); + malloc_tsetdword(free_timer_list + (free_timer_list_max - 256), 0, 256 * sizeof(int)); + } + free_timer_list[free_timer_list_pos++] = i; + break; + case TIMER_INTERVAL: + if (DIFF_TICK(timer_data[i].tick , tick) < -1000) { + timer_data[i].tick = tick + timer_data[i].interval; + } else { + timer_data[i].tick += timer_data[i].interval; + } + timer_data[i].type &= ~TIMER_REMOVE_HEAP; + push_timer_heap(i); + break; + } + } + } + + if (nextmin < TIMER_MIN_INTERVAL) + nextmin = TIMER_MIN_INTERVAL; + + if ((unsigned int)(tick + nextmin) < tick) //Tick will loop, rearrange the heap on the next iteration. + fix_heap_flag = 1; + return nextmin; +} + +unsigned long get_uptime (void) +{ + return (unsigned long) difftime (time(NULL), start_time); +} + +void timer_init(void) +{ + time(&start_time); +} + +void timer_final(void) +{ + struct timer_func_list* tfl = tfl_root, *tfl2; + + while (tfl) { + tfl2 = tfl->next; // copy next pointer + aFree(tfl->name); // free structures + aFree(tfl); + tfl = tfl2; // use copied pointer for next cycle + } + + if (timer_data) aFree(timer_data); + if (timer_heap) aFree(timer_heap); + if (free_timer_list) aFree(free_timer_list); +} + diff --git a/src/common/timer.h b/src/common/timer.h index aafefd1e2..9acc7c640 100644 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -1,60 +1,60 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _TIMER_H_ -#define _TIMER_H_ - -#ifdef __WIN32 -/* We need winsock lib to have timeval struct - windows is weirdo */ -#define __USE_W32_SOCKETS -#include -#endif - -#define BASE_TICK 5 - -#define TIMER_ONCE_AUTODEL 1 -#define TIMER_INTERVAL 2 -#define TIMER_REMOVE_HEAP 16 - -#define DIFF_TICK(a,b) ((int)((a)-(b))) - -// Struct declaration - -struct TimerData { - unsigned int tick; - int (*func)(int,unsigned int,int,int); - int id; - int data; - int type; - int interval; - int heap_pos; -}; - -// Function prototype declaration - -#ifdef __WIN32 -void gettimeofday(struct timeval *t, void *dummy); -#endif - -unsigned int gettick_nocache(void); -unsigned int gettick(void); - -int add_timer(unsigned int,int (*)(int,unsigned int,int,int),int,int); -int add_timer_interval(unsigned int,int (*)(int,unsigned int,int,int),int,int,int); -int delete_timer(int,int (*)(int,unsigned int,int,int)); - -int addtick_timer(int tid,unsigned int tick); -int settick_timer(int tid,unsigned int tick); -struct TimerData *get_timer(int tid); - -int do_timer(unsigned int tick); - -int add_timer_func_list(int (*)(int,unsigned int,int,int),char*); -char* search_timer_func_list(int (*)(int,unsigned int,int,int)); - -unsigned long get_uptime(void); - -void timer_init(void); -void timer_final(void); - -#endif // _TIMER_H_ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#ifdef __WIN32 +/* We need winsock lib to have timeval struct - windows is weirdo */ +#define __USE_W32_SOCKETS +#include +#endif + +#define BASE_TICK 5 + +#define TIMER_ONCE_AUTODEL 1 +#define TIMER_INTERVAL 2 +#define TIMER_REMOVE_HEAP 16 + +#define DIFF_TICK(a,b) ((int)((a)-(b))) + +// Struct declaration + +struct TimerData { + unsigned int tick; + int (*func)(int,unsigned int,int,int); + int id; + int data; + int type; + int interval; + int heap_pos; +}; + +// Function prototype declaration + +#ifdef __WIN32 +void gettimeofday(struct timeval *t, void *dummy); +#endif + +unsigned int gettick_nocache(void); +unsigned int gettick(void); + +int add_timer(unsigned int,int (*)(int,unsigned int,int,int),int,int); +int add_timer_interval(unsigned int,int (*)(int,unsigned int,int,int),int,int,int); +int delete_timer(int,int (*)(int,unsigned int,int,int)); + +int addtick_timer(int tid,unsigned int tick); +int settick_timer(int tid,unsigned int tick); +struct TimerData *get_timer(int tid); + +int do_timer(unsigned int tick); + +int add_timer_func_list(int (*)(int,unsigned int,int,int),char*); +char* search_timer_func_list(int (*)(int,unsigned int,int,int)); + +unsigned long get_uptime(void); + +void timer_init(void); +void timer_final(void); + +#endif // _TIMER_H_ diff --git a/src/common/utils.c b/src/common/utils.c index bbf9f8398..3172e89d9 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -1,384 +1,384 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include - -#ifdef WIN32 - #include - #define PATHSEP '\\' -#else - #include - #include - #include - #define PATHSEP '/' -#endif - -#include "utils.h" -#include "../common/mmo.h" -#include "../common/malloc.h" -#include "../common/showmsg.h" - -void dump(unsigned char *buffer, int num) -{ - int icnt,jcnt; - - printf(" Hex ASCII\n"); - printf(" ----------------------------------------------- ----------------"); - - for (icnt=0;icnt 31 && buffer[jcnt] < 127) - printf("%c",buffer[jcnt]); - else - printf("."); - } else - printf(" "); - } - } - printf("\n"); -} - -//NOTE: There is no need to use this function as the standard sqrt is plenty fast as it is. [Skotlex] -int newt_sqrt(int input) -{ - int new_value, value = input/2, count = 0; - if (!value) //Division by zero fix, pointed out by Shinomori. [Skotlex] - return input; - do - { - new_value = (value + input/value)>>1; - if (abs(value - new_value) <= 1) - return new_value; - value = new_value; - } - while (count++ < 25); - return new_value; -} - -#if defined(_WIN32) && !defined(MINGW) -char *rindex(char *str, char c) -{ - char *sptr; - - sptr = str; - while(*sptr) - ++sptr; - if (c == '\0') - return(sptr); - while(str != sptr) - if (*sptr-- == c) - return(++sptr); - return(NULL); -} - -int strcasecmp(const char *arg1, const char *arg2) -{ - int chk, i; - - if (arg1 == NULL || arg2 == NULL) { - ShowError("strcasecmp: received a NULL pointer, %p or %p.\n", arg1, arg2); - return (0); - } - - for (i = 0; arg1[i] || arg2[i]; i++) - if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0) - return (chk); /* not equal */ - - return (0); -} - -int strncasecmp(const char *arg1, const char *arg2, size_t n) -{ - int chk, i; - - if (arg1 == NULL || arg2 == NULL) { - ShowError("strncasecmp(): received a NULL pointer, %p or %p.\n", arg1, arg2); - return (0); - } - - for (i = 0; (arg1[i] || arg2[i]) && (n > 0); i++, n--) - if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0) - return (chk); /* not equal */ - - return (0); -} - -void str_upper(char *name) -{ - - int len = (int)strlen(name); - while (len--) { - if (*name >= 'a' && *name <= 'z') - *name -= ('a' - 'A'); - name++; - } -} - -void str_lower(char *name) -{ - int len = (int)strlen(name); - - while (len--) { - if (*name >= 'A' && *name <= 'Z') - *name += ('a' - 'A'); - name++; - } -} - -#endif - -// Allocate a StringBuf [MouseJstr] -struct StringBuf * StringBuf_Malloc() -{ - struct StringBuf * ret = (struct StringBuf *) aMallocA(sizeof(struct StringBuf)); - StringBuf_Init(ret); - return ret; -} - -// Initialize a previously allocated StringBuf [MouseJstr] -void StringBuf_Init(struct StringBuf * sbuf) { - sbuf->max_ = 1024; - sbuf->ptr_ = sbuf->buf_ = (char *) aMallocA(sbuf->max_ + 1); -} - -// printf into a StringBuf, moving the pointer [MouseJstr] -int StringBuf_Printf(struct StringBuf *sbuf,const char *fmt,...) -{ - va_list ap; - int n, size, off; - - while (1) { - /* Try to print in the allocated space. */ - va_start(ap, fmt); - size = sbuf->max_ - (sbuf->ptr_ - sbuf->buf_); - n = vsnprintf (sbuf->ptr_, size, fmt, ap); - va_end(ap); - /* If that worked, return the length. */ - if (n > -1 && n < size) { - sbuf->ptr_ += n; - return (int)(sbuf->ptr_ - sbuf->buf_); - } - /* Else try again with more space. */ - sbuf->max_ *= 2; // twice the old size - off = (int)(sbuf->ptr_ - sbuf->buf_); - sbuf->buf_ = (char *) aRealloc(sbuf->buf_, sbuf->max_ + 1); - sbuf->ptr_ = sbuf->buf_ + off; - } -} - -// Append buf2 onto the end of buf1 [MouseJstr] -int StringBuf_Append(struct StringBuf *buf1,const struct StringBuf *buf2) -{ - int buf1_avail = buf1->max_ - (buf1->ptr_ - buf1->buf_); - int size2 = (int)(buf2->ptr_ - buf2->buf_); - - if (size2 >= buf1_avail) { - int off = (int)(buf1->ptr_ - buf1->buf_); - buf1->max_ += size2; - buf1->buf_ = (char *) aRealloc(buf1->buf_, buf1->max_ + 1); - buf1->ptr_ = buf1->buf_ + off; - } - - memcpy(buf1->ptr_, buf2->buf_, size2); - buf1->ptr_ += size2; - return (int)(buf1->ptr_ - buf1->buf_); -} - -// Destroy a StringBuf [MouseJstr] -void StringBuf_Destroy(struct StringBuf *sbuf) -{ - aFree(sbuf->buf_); - sbuf->ptr_ = sbuf->buf_ = 0; -} - -// Free a StringBuf returned by StringBuf_Malloc [MouseJstr] -void StringBuf_Free(struct StringBuf *sbuf) -{ - StringBuf_Destroy(sbuf); - aFree(sbuf); -} - -// Return the built string from the StringBuf [MouseJstr] -char * StringBuf_Value(struct StringBuf *sbuf) -{ - *sbuf->ptr_ = '\0'; - return sbuf->buf_; -} - -#ifdef WIN32 - -char* checkpath(char *path, const char *srcpath) -{ // just make sure the char*path is not const - char *p=path; - if(NULL!=path && NULL!=srcpath) - while(*srcpath) { - if (*srcpath=='/') { - *p++ = '\\'; - srcpath++; - } - else - *p++ = *srcpath++; - } - *p = *srcpath; //EOS - return path; -} - -void findfile(const char *p, const char *pat, void (func)(const char*)) -{ - WIN32_FIND_DATA FindFileData; - HANDLE hFind; - char tmppath[MAX_PATH+1]; - - const char *path = (p ==NULL)? "." : p; - const char *pattern = (pat==NULL)? "" : pat; - - checkpath(tmppath,path); - if( PATHSEP != tmppath[strlen(tmppath)-1]) - strcat(tmppath, "\\*"); - else - strcat(tmppath, "*"); - - hFind = FindFirstFile(tmppath, &FindFileData); - if (hFind != INVALID_HANDLE_VALUE) - { - do - { - if (strcmp(FindFileData.cFileName, ".") == 0) - continue; - if (strcmp(FindFileData.cFileName, "..") == 0) - continue; - - sprintf(tmppath,"%s%c%s",path,PATHSEP,FindFileData.cFileName); - - if (FindFileData.cFileName && strstr(FindFileData.cFileName, pattern)) { - func( tmppath ); - } - - - if( FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) - { - findfile(tmppath, pat, func); - } - }while (FindNextFile(hFind, &FindFileData) != 0); - FindClose(hFind); - } - return; -} -#else - -#define MAX_DIR_PATH 2048 - -char* checkpath(char *path, const char*srcpath) -{ // just make sure the char*path is not const - char *p=path; - if(NULL!=path && NULL!=srcpath) - while(*srcpath) { - if (*srcpath=='\\') { - *p++ = '/'; - srcpath++; - } - else - *p++ = *srcpath++; - } - *p = *srcpath; //EOS - return path; -} - -void findfile(const char *p, const char *pat, void (func)(const char*)) -{ - DIR* dir; // pointer to the scanned directory. - struct dirent* entry; // pointer to one directory entry. - struct stat dir_stat; // used by stat(). - char tmppath[MAX_DIR_PATH+1]; - char path[MAX_DIR_PATH+1]= "."; - const char *pattern = (pat==NULL)? "" : pat; - if(p!=NULL) strcpy(path,p); - - // open the directory for reading - dir = opendir( checkpath(path, path) ); - if (!dir) { - ShowError("Cannot read directory '%s'\n", path); - return; - } - - // scan the directory, traversing each sub-directory - // matching the pattern for each file name. - while ((entry = readdir(dir))) { - // skip the "." and ".." entries. - if (strcmp(entry->d_name, ".") == 0) - continue; - if (strcmp(entry->d_name, "..") == 0) - continue; - - sprintf(tmppath,"%s%c%s",path, PATHSEP, entry->d_name); - - // check if the pattern matchs. - if (entry->d_name && strstr(entry->d_name, pattern)) { - func( tmppath ); - } - // check if it is a directory. - if (stat(tmppath, &dir_stat) == -1) { - ShowError("stat error %s\n': ", tmppath); - continue; - } - // is this a directory? - if (S_ISDIR(dir_stat.st_mode)) { - // decent recursivly - findfile(tmppath, pat, func); - } - }//end while -} -#endif - -unsigned char GetByte(unsigned long val, size_t num) -{ - switch(num) - { - case 0: - return (unsigned char)((val & 0x000000FF) ); - case 1: - return (unsigned char)((val & 0x0000FF00)>>0x08); - case 2: - return (unsigned char)((val & 0x00FF0000)>>0x10); - case 3: - return (unsigned char)((val & 0xFF000000)>>0x18); - default: - return 0; //better throw something here - } -} -unsigned short GetWord(unsigned long val, size_t num) -{ - switch(num) - { - case 0: - return (unsigned short)((val & 0x0000FFFF) ); - case 1: - return (unsigned short)((val & 0xFFFF0000)>>0x10); - default: - return 0; //better throw something here - } -} -unsigned short MakeWord(unsigned char byte0, unsigned char byte1) -{ - return byte0 | (byte1<<0x08); -} -unsigned long MakeDWord(unsigned short word0, unsigned short word1) -{ - return ((unsigned long)word0) - | ((unsigned long)word1<<0x10); -} - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include + +#ifdef WIN32 + #include + #define PATHSEP '\\' +#else + #include + #include + #include + #define PATHSEP '/' +#endif + +#include "utils.h" +#include "../common/mmo.h" +#include "../common/malloc.h" +#include "../common/showmsg.h" + +void dump(unsigned char *buffer, int num) +{ + int icnt,jcnt; + + printf(" Hex ASCII\n"); + printf(" ----------------------------------------------- ----------------"); + + for (icnt=0;icnt 31 && buffer[jcnt] < 127) + printf("%c",buffer[jcnt]); + else + printf("."); + } else + printf(" "); + } + } + printf("\n"); +} + +//NOTE: There is no need to use this function as the standard sqrt is plenty fast as it is. [Skotlex] +int newt_sqrt(int input) +{ + int new_value, value = input/2, count = 0; + if (!value) //Division by zero fix, pointed out by Shinomori. [Skotlex] + return input; + do + { + new_value = (value + input/value)>>1; + if (abs(value - new_value) <= 1) + return new_value; + value = new_value; + } + while (count++ < 25); + return new_value; +} + +#if defined(_WIN32) && !defined(MINGW) +char *rindex(char *str, char c) +{ + char *sptr; + + sptr = str; + while(*sptr) + ++sptr; + if (c == '\0') + return(sptr); + while(str != sptr) + if (*sptr-- == c) + return(++sptr); + return(NULL); +} + +int strcasecmp(const char *arg1, const char *arg2) +{ + int chk, i; + + if (arg1 == NULL || arg2 == NULL) { + ShowError("strcasecmp: received a NULL pointer, %p or %p.\n", arg1, arg2); + return (0); + } + + for (i = 0; arg1[i] || arg2[i]; i++) + if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0) + return (chk); /* not equal */ + + return (0); +} + +int strncasecmp(const char *arg1, const char *arg2, size_t n) +{ + int chk, i; + + if (arg1 == NULL || arg2 == NULL) { + ShowError("strncasecmp(): received a NULL pointer, %p or %p.\n", arg1, arg2); + return (0); + } + + for (i = 0; (arg1[i] || arg2[i]) && (n > 0); i++, n--) + if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0) + return (chk); /* not equal */ + + return (0); +} + +void str_upper(char *name) +{ + + int len = (int)strlen(name); + while (len--) { + if (*name >= 'a' && *name <= 'z') + *name -= ('a' - 'A'); + name++; + } +} + +void str_lower(char *name) +{ + int len = (int)strlen(name); + + while (len--) { + if (*name >= 'A' && *name <= 'Z') + *name += ('a' - 'A'); + name++; + } +} + +#endif + +// Allocate a StringBuf [MouseJstr] +struct StringBuf * StringBuf_Malloc() +{ + struct StringBuf * ret = (struct StringBuf *) aMallocA(sizeof(struct StringBuf)); + StringBuf_Init(ret); + return ret; +} + +// Initialize a previously allocated StringBuf [MouseJstr] +void StringBuf_Init(struct StringBuf * sbuf) { + sbuf->max_ = 1024; + sbuf->ptr_ = sbuf->buf_ = (char *) aMallocA(sbuf->max_ + 1); +} + +// printf into a StringBuf, moving the pointer [MouseJstr] +int StringBuf_Printf(struct StringBuf *sbuf,const char *fmt,...) +{ + va_list ap; + int n, size, off; + + while (1) { + /* Try to print in the allocated space. */ + va_start(ap, fmt); + size = sbuf->max_ - (sbuf->ptr_ - sbuf->buf_); + n = vsnprintf (sbuf->ptr_, size, fmt, ap); + va_end(ap); + /* If that worked, return the length. */ + if (n > -1 && n < size) { + sbuf->ptr_ += n; + return (int)(sbuf->ptr_ - sbuf->buf_); + } + /* Else try again with more space. */ + sbuf->max_ *= 2; // twice the old size + off = (int)(sbuf->ptr_ - sbuf->buf_); + sbuf->buf_ = (char *) aRealloc(sbuf->buf_, sbuf->max_ + 1); + sbuf->ptr_ = sbuf->buf_ + off; + } +} + +// Append buf2 onto the end of buf1 [MouseJstr] +int StringBuf_Append(struct StringBuf *buf1,const struct StringBuf *buf2) +{ + int buf1_avail = buf1->max_ - (buf1->ptr_ - buf1->buf_); + int size2 = (int)(buf2->ptr_ - buf2->buf_); + + if (size2 >= buf1_avail) { + int off = (int)(buf1->ptr_ - buf1->buf_); + buf1->max_ += size2; + buf1->buf_ = (char *) aRealloc(buf1->buf_, buf1->max_ + 1); + buf1->ptr_ = buf1->buf_ + off; + } + + memcpy(buf1->ptr_, buf2->buf_, size2); + buf1->ptr_ += size2; + return (int)(buf1->ptr_ - buf1->buf_); +} + +// Destroy a StringBuf [MouseJstr] +void StringBuf_Destroy(struct StringBuf *sbuf) +{ + aFree(sbuf->buf_); + sbuf->ptr_ = sbuf->buf_ = 0; +} + +// Free a StringBuf returned by StringBuf_Malloc [MouseJstr] +void StringBuf_Free(struct StringBuf *sbuf) +{ + StringBuf_Destroy(sbuf); + aFree(sbuf); +} + +// Return the built string from the StringBuf [MouseJstr] +char * StringBuf_Value(struct StringBuf *sbuf) +{ + *sbuf->ptr_ = '\0'; + return sbuf->buf_; +} + +#ifdef WIN32 + +char* checkpath(char *path, const char *srcpath) +{ // just make sure the char*path is not const + char *p=path; + if(NULL!=path && NULL!=srcpath) + while(*srcpath) { + if (*srcpath=='/') { + *p++ = '\\'; + srcpath++; + } + else + *p++ = *srcpath++; + } + *p = *srcpath; //EOS + return path; +} + +void findfile(const char *p, const char *pat, void (func)(const char*)) +{ + WIN32_FIND_DATA FindFileData; + HANDLE hFind; + char tmppath[MAX_PATH+1]; + + const char *path = (p ==NULL)? "." : p; + const char *pattern = (pat==NULL)? "" : pat; + + checkpath(tmppath,path); + if( PATHSEP != tmppath[strlen(tmppath)-1]) + strcat(tmppath, "\\*"); + else + strcat(tmppath, "*"); + + hFind = FindFirstFile(tmppath, &FindFileData); + if (hFind != INVALID_HANDLE_VALUE) + { + do + { + if (strcmp(FindFileData.cFileName, ".") == 0) + continue; + if (strcmp(FindFileData.cFileName, "..") == 0) + continue; + + sprintf(tmppath,"%s%c%s",path,PATHSEP,FindFileData.cFileName); + + if (FindFileData.cFileName && strstr(FindFileData.cFileName, pattern)) { + func( tmppath ); + } + + + if( FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + { + findfile(tmppath, pat, func); + } + }while (FindNextFile(hFind, &FindFileData) != 0); + FindClose(hFind); + } + return; +} +#else + +#define MAX_DIR_PATH 2048 + +char* checkpath(char *path, const char*srcpath) +{ // just make sure the char*path is not const + char *p=path; + if(NULL!=path && NULL!=srcpath) + while(*srcpath) { + if (*srcpath=='\\') { + *p++ = '/'; + srcpath++; + } + else + *p++ = *srcpath++; + } + *p = *srcpath; //EOS + return path; +} + +void findfile(const char *p, const char *pat, void (func)(const char*)) +{ + DIR* dir; // pointer to the scanned directory. + struct dirent* entry; // pointer to one directory entry. + struct stat dir_stat; // used by stat(). + char tmppath[MAX_DIR_PATH+1]; + char path[MAX_DIR_PATH+1]= "."; + const char *pattern = (pat==NULL)? "" : pat; + if(p!=NULL) strcpy(path,p); + + // open the directory for reading + dir = opendir( checkpath(path, path) ); + if (!dir) { + ShowError("Cannot read directory '%s'\n", path); + return; + } + + // scan the directory, traversing each sub-directory + // matching the pattern for each file name. + while ((entry = readdir(dir))) { + // skip the "." and ".." entries. + if (strcmp(entry->d_name, ".") == 0) + continue; + if (strcmp(entry->d_name, "..") == 0) + continue; + + sprintf(tmppath,"%s%c%s",path, PATHSEP, entry->d_name); + + // check if the pattern matchs. + if (entry->d_name && strstr(entry->d_name, pattern)) { + func( tmppath ); + } + // check if it is a directory. + if (stat(tmppath, &dir_stat) == -1) { + ShowError("stat error %s\n': ", tmppath); + continue; + } + // is this a directory? + if (S_ISDIR(dir_stat.st_mode)) { + // decent recursivly + findfile(tmppath, pat, func); + } + }//end while +} +#endif + +unsigned char GetByte(unsigned long val, size_t num) +{ + switch(num) + { + case 0: + return (unsigned char)((val & 0x000000FF) ); + case 1: + return (unsigned char)((val & 0x0000FF00)>>0x08); + case 2: + return (unsigned char)((val & 0x00FF0000)>>0x10); + case 3: + return (unsigned char)((val & 0xFF000000)>>0x18); + default: + return 0; //better throw something here + } +} +unsigned short GetWord(unsigned long val, size_t num) +{ + switch(num) + { + case 0: + return (unsigned short)((val & 0x0000FFFF) ); + case 1: + return (unsigned short)((val & 0xFFFF0000)>>0x10); + default: + return 0; //better throw something here + } +} +unsigned short MakeWord(unsigned char byte0, unsigned char byte1) +{ + return byte0 | (byte1<<0x08); +} +unsigned long MakeDWord(unsigned short word0, unsigned short word1) +{ + return ((unsigned long)word0) + | ((unsigned long)word1<<0x10); +} + diff --git a/src/common/utils.h b/src/common/utils.h index 7c3c1f40c..1f7eeba5c 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -1,52 +1,52 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef COMMON_UTILS_H -#define COMMON_UTILS_H - - -#ifndef NULL -#define NULL (void *)0 -#endif - -#define LOWER(c) (((c)>='A' && (c) <= 'Z') ? ((c)+('a'-'A')) : (c)) -#define UPPER(c) (((c)>='a' && (c) <= 'z') ? ((c)+('A'-'a')) : (c) ) - -/* strcasecmp -> stricmp -> str_cmp */ -#if defined(_WIN32) && !defined(MINGW) - int strcasecmp(const char *arg1, const char *arg2); - int strncasecmp(const char *arg1, const char *arg2, size_t n); - void str_upper(char *name); - void str_lower(char *name); - char *rindex(char *str, char c); -#endif - -void dump(unsigned char *buffer, int num); -int newt_sqrt(int value); //Newton aproximation for getting a fast sqrt. - -struct StringBuf { - char *buf_; - char *ptr_; - unsigned int max_; -}; - -struct StringBuf * StringBuf_Malloc(void); -void StringBuf_Init(struct StringBuf *); -int StringBuf_Printf(struct StringBuf *,const char *,...); -int StringBuf_Append(struct StringBuf *,const struct StringBuf *); -char * StringBuf_Value(struct StringBuf *); -void StringBuf_Destroy(struct StringBuf *); -void StringBuf_Free(struct StringBuf *); - -void findfile(const char *p, const char *pat, void (func)(const char*)); - -////////////////////////////////////////////////////////////////////////// -// byte word dword access [Shinomori] -////////////////////////////////////////////////////////////////////////// - -extern unsigned char GetByte(unsigned long val, size_t num); -extern unsigned short GetWord(unsigned long val, size_t num); -extern unsigned short MakeWord(unsigned char byte0, unsigned char byte1); -extern unsigned long MakeDWord(unsigned short word0, unsigned short word1); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef COMMON_UTILS_H +#define COMMON_UTILS_H + + +#ifndef NULL +#define NULL (void *)0 +#endif + +#define LOWER(c) (((c)>='A' && (c) <= 'Z') ? ((c)+('a'-'A')) : (c)) +#define UPPER(c) (((c)>='a' && (c) <= 'z') ? ((c)+('A'-'a')) : (c) ) + +/* strcasecmp -> stricmp -> str_cmp */ +#if defined(_WIN32) && !defined(MINGW) + int strcasecmp(const char *arg1, const char *arg2); + int strncasecmp(const char *arg1, const char *arg2, size_t n); + void str_upper(char *name); + void str_lower(char *name); + char *rindex(char *str, char c); +#endif + +void dump(unsigned char *buffer, int num); +int newt_sqrt(int value); //Newton aproximation for getting a fast sqrt. + +struct StringBuf { + char *buf_; + char *ptr_; + unsigned int max_; +}; + +struct StringBuf * StringBuf_Malloc(void); +void StringBuf_Init(struct StringBuf *); +int StringBuf_Printf(struct StringBuf *,const char *,...); +int StringBuf_Append(struct StringBuf *,const struct StringBuf *); +char * StringBuf_Value(struct StringBuf *); +void StringBuf_Destroy(struct StringBuf *); +void StringBuf_Free(struct StringBuf *); + +void findfile(const char *p, const char *pat, void (func)(const char*)); + +////////////////////////////////////////////////////////////////////////// +// byte word dword access [Shinomori] +////////////////////////////////////////////////////////////////////////// + +extern unsigned char GetByte(unsigned long val, size_t num); +extern unsigned short GetWord(unsigned long val, size_t num); +extern unsigned short MakeWord(unsigned char byte0, unsigned char byte1); +extern unsigned long MakeDWord(unsigned short word0, unsigned short word1); + +#endif diff --git a/src/common/version.h b/src/common/version.h index 1c7961ee1..2560c1019 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -1,30 +1,30 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _VERSION_H_ -#define _VERSION_H_ - -#define ATHENA_MAJOR_VERSION 1 // Major Version -#define ATHENA_MINOR_VERSION 0 // Minor Version -#define ATHENA_REVISION 0 // Revision - -#define ATHENA_RELEASE_FLAG 1 // 1=Develop,0=Stable -#define ATHENA_OFFICIAL_FLAG 1 // 1=Mod,0=Official - -#define ATHENA_SERVER_NONE 0 // not defined -#define ATHENA_SERVER_LOGIN 1 // login server -#define ATHENA_SERVER_CHAR 2 // char server -#define ATHENA_SERVER_INTER 4 // inter server -#define ATHENA_SERVER_MAP 8 // map server - -// ATHENA_MOD_VERSIONはパッチ番号です。 -// これは無理に変えなくても気が向いたら変える程度の扱いで。 -// (毎回アップロードの度に変更するのも面倒と思われるし、そもそも -//  この項目を参照する人がいるかどうかで疑問だから。) -// その程度の扱いなので、サーバーに問い合わせる側も、あくまで目安程度の扱いで -// あんまり信用しないこと。 -// 鯖snapshotの時や、大きな変更があった場合は設定してほしいです。 -// C言語の仕様上、最初に0を付けると8進数になるので間違えないで下さい。 -#define ATHENA_MOD_VERSION 1249 // mod version (patch No.) - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#define ATHENA_MAJOR_VERSION 1 // Major Version +#define ATHENA_MINOR_VERSION 0 // Minor Version +#define ATHENA_REVISION 0 // Revision + +#define ATHENA_RELEASE_FLAG 1 // 1=Develop,0=Stable +#define ATHENA_OFFICIAL_FLAG 1 // 1=Mod,0=Official + +#define ATHENA_SERVER_NONE 0 // not defined +#define ATHENA_SERVER_LOGIN 1 // login server +#define ATHENA_SERVER_CHAR 2 // char server +#define ATHENA_SERVER_INTER 4 // inter server +#define ATHENA_SERVER_MAP 8 // map server + +// ATHENA_MOD_VERSIONはパッチ番号です。 +// これは無理に変えなくても気が向いたら変える程度の扱いで。 +// (毎回アップロードの度に変更するのも面倒と思われるし、そもそも +//  この項目を参照する人がいるかどうかで疑問だから。) +// その程度の扱いなので、サーバーに問い合わせる側も、あくまで目安程度の扱いで +// あんまり信用しないこと。 +// 鯖snapshotの時や、大きな変更があった場合は設定してほしいです。 +// C言語の仕様上、最初に0を付けると8進数になるので間違えないで下さい。 +#define ATHENA_MOD_VERSION 1249 // mod version (patch No.) + +#endif diff --git a/src/ladmin/ladmin.c b/src/ladmin/ladmin.c index 64214f352..848c6f3de 100644 --- a/src/ladmin/ladmin.c +++ b/src/ladmin/ladmin.c @@ -1,4436 +1,4436 @@ -// (c) eAthena Dev Team - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -/////////////////////////////////////////////////////////////////////////// -// EAthena login-server remote administration tool -// Ladamin in C by [Yor] -// if you modify this software, modify ladmin in tool too. -/////////////////////////////////////////////////////////////////////////// - -#include -#include -#ifdef WIN32 -#define WIN32_LEAN_AND_MEAN -#include -void Gettimeofday(struct timeval *timenow) -{ - time_t t; - t = clock(); - timenow->tv_usec = t; - timenow->tv_sec = t / CLK_TCK; - return; -} -#define gettimeofday(timenow, dummy) Gettimeofday(timenow) -#else -#include -#include -#include // gettimeofday -#include -#include // close -#include // inet_addr -#include // gethostbyname -#endif -#include -#include -#include -#include -#include // str* -#include // valist -#include // tolower - -#include "../common/core.h" -#include "../common/strlib.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "ladmin.h" -#include "../common/version.h" -#include "../common/mmo.h" - -#ifdef PASSWORDENC -#include "md5calc.h" -#endif - -//-------------------------------INSTRUCTIONS------------------------------ -// Set the variables below: -// IP of the login server. -// Port where the login-server listens incoming packets. -// Password of administration (same of config_athena.conf). -// Displayed language of the sofware (if not correct, english is used). -// IMPORTANT: -// Be sure that you authorize remote administration in login-server -// (see login_athena.conf, 'admin_state' parameter) -//------------------------------------------------------------------------- -char loginserverip[16] = "127.0.0.1"; // IP of login-server -int loginserverport = 6900; // Port of login-server -char loginserveradminpassword[24] = "admin"; // Administration password -#ifdef PASSWORDENC -int passenc = 2; // Encoding type of the password -#else -int passenc = 0; // Encoding type of the password -#endif -char defaultlanguage = 'E'; // Default language (F: Fran軋is/E: English) - // (if it's not 'F', default is English) -char ladmin_log_filename[1024] = "log/ladmin.log"; -char date_format[32] = "%Y-%m-%d %H:%M:%S"; -//------------------------------------------------------------------------- -// LIST of COMMANDs that you can type at the prompt: -// To use these commands you can only type only the first letters. -// You must type a minimum of letters (you can not type 'a', -// because ladmin doesn't know if it's for 'aide' or for 'add') -// q <= quit, li <= list, pass <= passwd, etc. -// -// Note: every time you must give a account_name, you can use "" or '' (spaces can be included) -// -// aide/help/? -// Display the description of the commands -// aide/help/? [command] -// Display the description of the specified command -// -// add -// Create an account with the default email (a@a.com). -// Concerning the sex, only the first letter is used (F or M). -// The e-mail is set to a@a.com (default e-mail). It's like to have no e-mail. -// When the password is omitted, the input is done without displaying of the pressed keys. -// add testname Male testpass -// -// ban/banish yyyy/mm/dd hh:mm:ss -// Changes the final date of a banishment of an account. -// Like banset, but is at end. -// -// banadd -// Adds or substracts time from the final date of a banishment of an account. -// Modifier is done as follows: -// Adjustment value (-1, 1, +1, etc...) -// Modified element: -// a or y: year -// m: month -// j or d: day -// h: hour -// mn: minute -// s: second -// banadd testname +1m-2mn1s-6y -// this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time. -// NOTE: If you modify the final date of a non-banished account, -// you fix the final date to (actual time +- adjustments) -// -// banset yyyy/mm/dd [hh:mm:ss] -// Changes the final date of a banishment of an account. -// Default time [hh:mm:ss]: 23:59:59. -// banset 0 -// Set a non-banished account (0 = unbanished). -// -// block -// Set state 5 (You have been blocked by the GM Team) to an account. -// Like state 5. -// -// check -// Check the validity of a password for an account -// NOTE: Server will never sends back a password. -// It's the only method you have to know if a password is correct. -// The other method is to have a ('physical') access to the accounts file. -// -// create -// Like the 'add' command, but with e-mail moreover. -// create testname Male my@mail.com testpass -// -// del -// Remove an account. -// This order requires confirmation. After confirmation, the account is deleted. -// -// email -// Modify the e-mail of an account. -// -// getcount -// Give the number of players online on all char-servers. -// -// gm [GM_level] -// Modify the GM level of an account. -// Default value remove GM level (GM level = 0). -// gm testname 80 -// -// id -// Give the id of an account. -// -// info -// Display complete information of an account. -// -// kami -// Sends a broadcast message on all map-server (in yellow). -// kamib -// Sends a broadcast message on all map-server (in blue). -// -// language -// Change the language of displaying. -// -// list/ls [start_id [end_id]] -// Display a list of accounts. -// 'start_id', 'end_id': indicate end and start identifiers. -// Research by name is not possible with this command. -// list 10 9999999 -// -// listBan/lsBan [start_id [end_id]] -// Like list/ls, but only for accounts with state or banished -// -// listGM/lsGM [start_id [end_id]] -// Like list/ls, but only for GM accounts -// -// listOK/lsOK [start_id [end_id]] -// Like list/ls, but only for accounts without state and not banished -// -// memo -// Modify the memo of an account. -// 'memo': it can have until 253 characters (with spaces or not). -// -// name -// Give the name of an account. -// -// passwd -// Change the password of an account. -// When new password is omitted, the input is done without displaying of the pressed keys. -// -// quit/end/exit -// End of the program of administration -// -// reloadGM -// Reload GM configuration file -// -// search -// Seek accounts. -// Displays the accounts whose names correspond. -// search -r/-e/--expr/--regex -// Seek accounts by regular expression. -// Displays the accounts whose names correspond. -// -// sex -// Modify the sex of an account. -// sex testname Male -// -// state -// Change the state of an account. -// 'new_state': state is the state of the packet 0x006a + 1. The possibilities are: -// 0 = Account ok 6 = Your Game's EXE file is not the latest version -// 1 = Unregistered ID 7 = You are Prohibited to log in until %s -// 2 = Incorrect Password 8 = Server is jammed due to over populated -// 3 = This ID is expired 9 = No MSG -// 4 = Rejected from Server 100 = This ID has been totally erased -// 5 = You have been blocked by the GM Team -// all other values are 'No MSG', then use state 9 please. -// 'error_message_#7': message of the code error 6 = Your are Prohibited to log in until %s (packet 0x006a) -// -// timeadd -// Adds or substracts time from the validity limit of an account. -// Modifier is done as follows: -// Adjustment value (-1, 1, +1, etc...) -// Modified element: -// a or y: year -// m: month -// j or d: day -// h: hour -// mn: minute -// s: second -// timeadd testname +1m-2mn1s-6y -// this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time. -// NOTE: You can not modify a unlimited validity limit. -// If you want modify it, you want probably create a limited validity limit. -// So, at first, you must set the validity limit to a date/time. -// -// timeset yyyy/mm/dd [hh:mm:ss] -// Changes the validity limit of an account. -// Default time [hh:mm:ss]: 23:59:59. -// timeset 0 -// Gives an unlimited validity limit (0 = unlimited). -// -// unban/unbanish -// Unban an account. -// Like banset 0. -// -// unblock -// Set state 0 (Account ok) to an account. -// Like state 0. -// -// version -// Display the version of the login-server. -// -// who -// Displays complete information of an account. -// -//------------------------------------------------------------------------- -int login_fd; -int login_ip; -int bytes_to_read = 0; // flag to know if we waiting bytes from login-server -char command[1024]; -char parameters[1024]; -int list_first, list_last, list_type, list_count; // parameter to display a list of accounts -int already_exit_function = 0; // sometimes, the exit function is called twice... so, don't log twice the message - -//------------------------------ -// Writing function of logs file -//------------------------------ -int ladmin_log(char *fmt, ...) { - FILE *logfp; - va_list ap; - struct timeval tv; - char tmpstr[2048]; - - va_start(ap, fmt); - - logfp = fopen(ladmin_log_filename, "a"); - if (logfp) { - if (fmt[0] == '\0') // jump a line if no message - fprintf(logfp, RETCODE); - else { - gettimeofday(&tv, NULL); - strftime(tmpstr, 24, date_format, localtime((const time_t*)&(tv.tv_sec))); - sprintf(tmpstr + strlen(tmpstr), ".%03d: %s", (int)tv.tv_usec / 1000, fmt); - vfprintf(logfp, tmpstr, ap); - } - fclose(logfp); - } - - va_end(ap); - return 0; -} - -//--------------------------------------------- -// Function to return ordonal text of a number. -//--------------------------------------------- -char* makeordinal(int number) { - if (defaultlanguage == 'F') { - if (number == 0) - return ""; - else if (number == 1) - return "er"; - else - return "鑪e"; - } else { - if ((number % 10) < 4 && (number % 10) != 0 && (number < 10 || number > 20)) { - if ((number % 10) == 1) - return "st"; - else if ((number % 10) == 2) - return "nd"; - else - return "rd"; - } else { - return "th"; - } - } - return ""; -} - -//----------------------------------------------------------------------------------------- -// Function to test of the validity of an account name (return 0 if incorrect, and 1 if ok) -//----------------------------------------------------------------------------------------- -int verify_accountname(char* account_name) { - int i; - - for(i = 0; account_name[i]; i++) { - if (account_name[i] < 32) { - if (defaultlanguage == 'F') { - printf("Caract鑽e interdit trouv dans le nom du compte (%d%s caract鑽e).\n", i+1, makeordinal(i+1)); - ladmin_log("Caract鑽e interdit trouv dans le nom du compte (%d%s caract鑽e)." RETCODE, i+1, makeordinal(i+1)); - } else { - printf("Illegal character found in the account name (%d%s character).\n", i+1, makeordinal(i+1)); - ladmin_log("Illegal character found in the account name (%d%s character)." RETCODE, i+1, makeordinal(i+1)); - } - return 0; - } - } - - if (strlen(account_name) < 4) { - if (defaultlanguage == 'F') { - printf("Nom du compte trop court. Entrez un nom de compte de 4-23 caract鑽es.\n"); - ladmin_log("Nom du compte trop court. Entrez un nom de compte de 4-23 caract鑽es." RETCODE); - } else { - printf("Account name is too short. Please input an account name of 4-23 bytes.\n"); - ladmin_log("Account name is too short. Please input an account name of 4-23 bytes." RETCODE); - } - return 0; - } - - if (strlen(account_name) > 23) { - if (defaultlanguage == 'F') { - printf("Nom du compte trop long. Entrez un nom de compte de 4-23 caract鑽es.\n"); - ladmin_log("Nom du compte trop long. Entrez un nom de compte de 4-23 caract鑽es." RETCODE); - } else { - printf("Account name is too long. Please input an account name of 4-23 bytes.\n"); - ladmin_log("Account name is too long. Please input an account name of 4-23 bytes." RETCODE); - } - return 0; - } - - return 1; -} - -//--------------------------------------------------- -// E-mail check: return 0 (not correct) or 1 (valid). -//--------------------------------------------------- -int e_mail_check(char *email) { - char ch; - char* last_arobas; - - // athena limits - if (strlen(email) < 3 || strlen(email) > 39) - return 0; - - // part of RFC limits (official reference of e-mail description) - if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') - return 0; - - if (email[strlen(email)-1] == '.') - return 0; - - last_arobas = strrchr(email, '@'); - - if (strstr(last_arobas, "@.") != NULL || - strstr(last_arobas, "..") != NULL) - return 0; - - for(ch = 1; ch < 32; ch++) { - if (strchr(last_arobas, ch) != NULL) { - return 0; - break; - } - } - - if (strchr(last_arobas, ' ') != NULL || - strchr(last_arobas, ';') != NULL) - return 0; - - // all correct - return 1; -} - -//---------------------------------- -// Sub-function: Input of a password -//---------------------------------- -int typepasswd(char * password) { - char password1[1023], password2[1023]; - int letter; - int i; - - if (defaultlanguage == 'F') { - ladmin_log("Aucun mot de passe n'a 騁 donn. Demande d'un mot de passe." RETCODE); - } else { - ladmin_log("No password was given. Request to obtain a password." RETCODE); - } - - memset(password1, '\0', sizeof(password1)); - memset(password2, '\0', sizeof(password2)); - if (defaultlanguage == 'F') - printf("\033[1;36m Entrez le mot de passe > \033[0;32;42m"); - else - printf("\033[1;36m Type the password > \033[0;32;42m"); - i = 0; - while ((letter = getchar()) != '\n') - password1[i++] = letter; - if (defaultlanguage == 'F') - printf("\033[0m\033[1;36m R-entrez le mot de passe > \033[0;32;42m"); - else - printf("\033[0m\033[1;36m Verify the password > \033[0;32;42m"); - i = 0; - while ((letter = getchar()) != '\n') - password2[i++] = letter; - - printf("\033[0m"); - fflush(stdout); - fflush(stdin); - - if (strcmp(password1, password2) != 0) { - if (defaultlanguage == 'F') { - printf("Erreur de v駻ification du mot de passe: Saisissez le m麥e mot de passe svp.\n"); - ladmin_log("Erreur de v駻ification du mot de passe: Saisissez le m麥e mot de passe svp." RETCODE); - ladmin_log(" Premier mot de passe: %s, second mot de passe: %s." RETCODE, password1, password2); - } else { - printf("Password verification failed. Please input same password.\n"); - ladmin_log("Password verification failed. Please input same password." RETCODE); - ladmin_log(" First password: %s, second password: %s." RETCODE, password1, password2); - } - return 0; - } - if (defaultlanguage == 'F') { - ladmin_log("Mot de passe saisi: %s." RETCODE, password1); - } else { - ladmin_log("Typed password: %s." RETCODE, password1); - } - strcpy(password, password1); - return 1; -} - -//------------------------------------------------------------------------------------ -// Sub-function: Test of the validity of password (return 0 if incorrect, and 1 if ok) -//------------------------------------------------------------------------------------ -int verify_password(char * password) { - int i; - - for(i = 0; password[i]; i++) { - if (password[i] < 32) { - if (defaultlanguage == 'F') { - printf("Caract鑽e interdit trouv dans le mot de passe (%d%s caract鑽e).\n", i+1, makeordinal(i+1)); - ladmin_log("Caract鑽e interdit trouv dans le nom du compte (%d%s caract鑽e)." RETCODE, i+1, makeordinal(i+1)); - } else { - printf("Illegal character found in the password (%d%s character).\n", i+1, makeordinal(i+1)); - ladmin_log("Illegal character found in the password (%d%s character)." RETCODE, i+1, makeordinal(i+1)); - } - return 0; - } - } - - if (strlen(password) < 4) { - if (defaultlanguage == 'F') { - printf("Nom du compte trop court. Entrez un nom de compte de 4-23 caract鑽es.\n"); - ladmin_log("Nom du compte trop court. Entrez un nom de compte de 4-23 caract鑽es." RETCODE); - } else { - printf("Account name is too short. Please input an account name of 4-23 bytes.\n"); - ladmin_log("Account name is too short. Please input an account name of 4-23 bytes." RETCODE); - } - return 0; - } - - if (strlen(password) > 23) { - if (defaultlanguage == 'F') { - printf("Mot de passe trop long. Entrez un mot de passe de 4-23 caract鑽es.\n"); - ladmin_log("Mot de passe trop long. Entrez un mot de passe de 4-23 caract鑽es." RETCODE); - } else { - printf("Password is too long. Please input a password of 4-23 bytes.\n"); - ladmin_log("Password is too long. Please input a password of 4-23 bytes." RETCODE); - } - return 0; - } - - return 1; -} - -//------------------------------------------------------------------ -// Sub-function: Check the name of a command (return complete name) -//----------------------------------------------------------------- -int check_command(char * command) { -// help - if (strncmp(command, "aide", 2) == 0 && strncmp(command, "aide", strlen(command)) == 0) // not 1 letter command: 'aide' or 'add'? - strcpy(command, "aide"); - else if (strncmp(command, "help", 1) == 0 && strncmp(command, "help", strlen(command)) == 0) - strcpy(command, "help"); -// general commands - else if (strncmp(command, "add", 2) == 0 && strncmp(command, "add", strlen(command)) == 0) // not 1 letter command: 'aide' or 'add'? - strcpy(command, "add"); - else if ((strncmp(command, "ban", 3) == 0 && strncmp(command, "ban", strlen(command)) == 0) || - (strncmp(command, "banish", 4) == 0 && strncmp(command, "banish", strlen(command)) == 0)) - strcpy(command, "ban"); - else if ((strncmp(command, "banadd", 4) == 0 && strncmp(command, "banadd", strlen(command)) == 0) || // not 1 letter command: 'ba' or 'bs'? 'banadd' or 'banset' ? - strcmp(command, "ba") == 0) - strcpy(command, "banadd"); - else if ((strncmp(command, "banset", 4) == 0 && strncmp(command, "banset", strlen(command)) == 0) || // not 1 letter command: 'ba' or 'bs'? 'banadd' or 'banset' ? - strcmp(command, "bs") == 0) - strcpy(command, "banset"); - else if (strncmp(command, "block", 2) == 0 && strncmp(command, "block", strlen(command)) == 0) - strcpy(command, "block"); - else if (strncmp(command, "check", 2) == 0 && strncmp(command, "check", strlen(command)) == 0) // not 1 letter command: 'check' or 'create'? - strcpy(command, "check"); - else if (strncmp(command, "create", 2) == 0 && strncmp(command, "create", strlen(command)) == 0) // not 1 letter command: 'check' or 'create'? - strcpy(command, "create"); - else if (strncmp(command, "delete", 1) == 0 && strncmp(command, "delete", strlen(command)) == 0) - strcpy(command, "delete"); - else if ((strncmp(command, "email", 2) == 0 && strncmp(command, "email", strlen(command)) == 0) || // not 1 letter command: 'email', 'end' or 'exit'? - (strncmp(command, "e-mail", 2) == 0 && strncmp(command, "e-mail", strlen(command)) == 0)) - strcpy(command, "email"); - else if (strncmp(command, "getcount", 2) == 0 && strncmp(command, "getcount", strlen(command)) == 0) // not 1 letter command: 'getcount' or 'gm'? - strcpy(command, "getcount"); -// else if (strncmp(command, "gm", 2) == 0 && strncmp(command, "gm", strlen(command)) == 0) // not 1 letter command: 'getcount' or 'gm'? -// strcpy(command, "gm"); -// else if (strncmp(command, "id", 2) == 0 && strncmp(command, "id", strlen(command)) == 0) // not 1 letter command: 'id' or 'info'? -// strcpy(command, "id"); - else if (strncmp(command, "info", 2) == 0 && strncmp(command, "info", strlen(command)) == 0) // not 1 letter command: 'id' or 'info'? - strcpy(command, "info"); -// else if (strncmp(command, "kami", 4) == 0 && strncmp(command, "kami", strlen(command)) == 0) // only all letters command: 'kami' or 'kamib'? -// strcpy(command, "kami"); -// else if (strncmp(command, "kamib", 5) == 0 && strncmp(command, "kamib", strlen(command)) == 0) // only all letters command: 'kami' or 'kamib'? -// strcpy(command, "kamib"); - else if ((strncmp(command, "language", 2) == 0 && strncmp(command, "language", strlen(command)) == 0)) // not 1 letter command: 'language' or 'list'? - strcpy(command, "language"); - else if ((strncmp(command, "list", 2) == 0 && strncmp(command, "list", strlen(command)) == 0) || // 'list' is default list command // not 1 letter command: 'language' or 'list'? - strcmp(command, "ls") == 0) - strcpy(command, "list"); - else if ((strncmp(command, "listban", 5) == 0 && strncmp(command, "listban", strlen(command)) == 0) || - (strncmp(command, "lsban", 3) == 0 && strncmp(command, "lsban", strlen(command)) == 0) || - strcmp(command, "lb") == 0) - strcpy(command, "listban"); - else if ((strncmp(command, "listgm", 5) == 0 && strncmp(command, "listgm", strlen(command)) == 0) || - (strncmp(command, "lsgm", 3) == 0 && strncmp(command, "lsgm", strlen(command)) == 0) || - strcmp(command, "lg") == 0) - strcpy(command, "listgm"); - else if ((strncmp(command, "listok", 5) == 0 && strncmp(command, "listok", strlen(command)) == 0) || - (strncmp(command, "lsok", 3) == 0 && strncmp(command, "lsok", strlen(command)) == 0) || - strcmp(command, "lo") == 0) - strcpy(command, "listok"); - else if (strncmp(command, "memo", 1) == 0 && strncmp(command, "memo", strlen(command)) == 0) - strcpy(command, "memo"); - else if (strncmp(command, "name", 1) == 0 && strncmp(command, "name", strlen(command)) == 0) - strcpy(command, "name"); - else if ((strncmp(command, "password", 1) == 0 && strncmp(command, "password", strlen(command)) == 0) || - strcmp(command, "passwd") == 0) - strcpy(command, "password"); - else if (strncmp(command, "reloadgm", 1) == 0 && strncmp(command, "reloadgm", strlen(command)) == 0) - strcpy(command, "reloadgm"); - else if (strncmp(command, "search", 3) == 0 && strncmp(command, "search", strlen(command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? - strcpy(command, "search"); // not 2 letters command: 'search' or 'sex'? -// else if (strncmp(command, "sex", 3) == 0 && strncmp(command, "sex", strlen(command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? -// strcpy(command, "sex"); // not 2 letters command: 'search' or 'sex'? - else if (strncmp(command, "state", 2) == 0 && strncmp(command, "state", strlen(command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? - strcpy(command, "state"); - else if ((strncmp(command, "timeadd", 5) == 0 && strncmp(command, "timeadd", strlen(command)) == 0) || // not 1 letter command: 'ta' or 'ts'? 'timeadd' or 'timeset'? - strcmp(command, "ta") == 0) - strcpy(command, "timeadd"); - else if ((strncmp(command, "timeset", 5) == 0 && strncmp(command, "timeset", strlen(command)) == 0) || // not 1 letter command: 'ta' or 'ts'? 'timeadd' or 'timeset'? - strcmp(command, "ts") == 0) - strcpy(command, "timeset"); - else if ((strncmp(command, "unban", 5) == 0 && strncmp(command, "unban", strlen(command)) == 0) || - (strncmp(command, "unbanish", 4) == 0 && strncmp(command, "unbanish", strlen(command)) == 0)) - strcpy(command, "unban"); - else if (strncmp(command, "unblock", 4) == 0 && strncmp(command, "unblock", strlen(command)) == 0) - strcpy(command, "unblock"); - else if (strncmp(command, "version", 1) == 0 && strncmp(command, "version", strlen(command)) == 0) - strcpy(command, "version"); - else if (strncmp(command, "who", 1) == 0 && strncmp(command, "who", strlen(command)) == 0) - strcpy(command, "who"); -// quit - else if (strncmp(command, "quit", 1) == 0 && strncmp(command, "quit", strlen(command)) == 0) - strcpy(command, "quit"); - else if (strncmp(command, "exit", 2) == 0 && strncmp(command, "exit", strlen(command)) == 0) // not 1 letter command: 'email', 'end' or 'exit'? - strcpy(command, "exit"); - else if (strncmp(command, "end", 2) == 0 && strncmp(command, "end", strlen(command)) == 0) // not 1 letter command: 'email', 'end' or 'exit'? - strcpy(command, "end"); - - return 0; -} - -//----------------------------------------- -// Sub-function: Display commands of ladmin -//----------------------------------------- -void display_help(char* param, int language) { - char command[1023]; - int i; - - memset(command, '\0', sizeof(command)); - - if (sscanf(param, "%s ", command) < 1 || strlen(command) == 0) - strcpy(command, ""); // any value that is not a command - - if (command[0] == '?') { - if (defaultlanguage == 'F') - strcpy(command, "aide"); - else - strcpy(command, "help"); - } - - // lowercase for command - for (i = 0; command[i]; i++) - command[i] = tolower(command[i]); - - // Analyse of the command - check_command(command); // give complete name to the command - - if (defaultlanguage == 'F') { - ladmin_log("Affichage des commandes ou d'une commande." RETCODE); - } else { - ladmin_log("Displaying of the commands or a command." RETCODE); - } - - if (language == 1) { - if (strcmp(command, "aide") == 0) { - printf("aide/help/?\n"); - printf(" Affiche la description des commandes\n"); - printf("aide/help/? [commande]\n"); - printf(" Affiche la description de la commande specifi馥\n"); - } else if (strcmp(command, "help") == 0 ) { - printf("aide/help/?\n"); - printf(" Display the description of the commands\n"); - printf("aide/help/? [command]\n"); - printf(" Display the description of the specified command\n"); -// general commands - } else if (strcmp(command, "add") == 0) { - printf("add \n"); - printf(" Cr馥 un compte avec l'email par d馭aut (a@a.com).\n"); - printf(" Concernant le sexe, seule la premi鑽e lettre compte (F ou M).\n"); - printf(" L'e-mail est a@a.com (e-mail par d馭aut). C'est comme n'avoir aucun e-mail.\n"); - printf(" Lorsque motdepasse est omis, la saisie se fait sans que la frappe se voit.\n"); - printf(" add testname Male testpass\n"); - } else if (strcmp(command, "ban") == 0) { - printf("ban/banish aaaa/mm/jj hh:mm:ss \n"); - printf(" Change la date de fin de bannissement d'un compte.\n"); - printf(" Comme banset, mais est la fin.\n"); - } else if (strcmp(command, "banadd") == 0) { - printf("banadd \n"); - printf(" Ajoute ou soustrait du temps la date de banissement d'un compte.\n"); - printf(" Les modificateurs sont construits comme suit:\n"); - printf(" Valeur d'ajustement (-1, 1, +1, etc...)\n"); - printf(" El駑ent modifi:\n"); - printf(" a ou y: ann馥\n"); - printf(" m: mois\n"); - printf(" j ou d: jour\n"); - printf(" h: heure\n"); - printf(" mn: minute\n"); - printf(" s: seconde\n"); - printf(" banadd testname +1m-2mn1s-6a\n"); - printf(" Cette exemple ajoute 1 mois et une seconde, et soustrait 2 minutes\n"); - printf(" et 6 ans dans le m麥e temps.\n"); - printf("NOTE: Si vous modifez la date de banissement d'un compte non bani,\n"); - printf(" vous indiquez comme date (le moment actuel +- les ajustements)\n"); - } else if (strcmp(command, "banset") == 0) { - printf("banset aaaa/mm/jj [hh:mm:ss]\n"); - printf(" Change la date de fin de bannissement d'un compte.\n"); - printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); - printf("banset 0\n"); - printf(" D饕anni un compte (0 = de-banni).\n"); - } else if (strcmp(command, "block") == 0) { - printf("block \n"); - printf(" Place le status d'un compte 5 (You have been blocked by the GM Team).\n"); - printf(" La commande est l'駲uivalent de state 5.\n"); - } else if (strcmp(command, "check") == 0) { - printf("check \n"); - printf(" V駻ifie la validit d'un mot de passe pour un compte\n"); - printf(" NOTE: Le serveur n'enverra jamais un mot de passe.\n"); - printf(" C'est la seule m騁hode que vous poss馘ez pour savoir\n"); - printf(" si un mot de passe est le bon. L'autre m騁hode est\n"); - printf(" d'avoir un acc鑚 ('physique') au fichier des comptes.\n"); - } else if (strcmp(command, "create") == 0) { - printf("create \n"); - printf(" Comme la commande add, mais avec l'e-mail en plus.\n"); - printf(" create testname Male mon@mail.com testpass\n"); - } else if (strcmp(command, "delete") == 0) { - printf("del \n"); - printf(" Supprime un compte.\n"); - printf(" La commande demande confirmation. Apr鑚 confirmation, le compte est d騁ruit.\n"); - } else if (strcmp(command, "email") == 0) { - printf("email \n"); - printf(" Modifie l'e-mail d'un compte.\n"); - } else if (strcmp(command, "getcount") == 0) { - printf("getcount\n"); - printf(" Donne le nombre de joueurs en ligne par serveur de char.\n"); - } else if (strcmp(command, "gm") == 0) { - printf("gm [Niveau_GM]\n"); - printf(" Modifie le niveau de GM d'un compte.\n"); - printf(" Valeur par d馭aut: 0 (suppression du niveau de GM).\n"); - printf(" gm nomtest 80\n"); - } else if (strcmp(command, "id") == 0) { - printf("id \n"); - printf(" Donne l'id d'un compte.\n"); - } else if (strcmp(command, "info") == 0) { - printf("info \n"); - printf(" Affiche les informations sur un compte.\n"); - } else if (strcmp(command, "kami") == 0) { - printf("kami \n"); - printf(" Envoi un message g駭駻al sur tous les serveurs de map (en jaune).\n"); - } else if (strcmp(command, "kamib") == 0) { - printf("kamib \n"); - printf(" Envoi un message g駭駻al sur tous les serveurs de map (en bleu).\n"); - } else if (strcmp(command, "language") == 0) { - printf("language \n"); - printf(" Change la langue d'affichage.\n"); - printf(" Langues possibles: 'Fran軋is' ou 'English'.\n"); - } else if (strcmp(command, "list") == 0) { - printf("list/ls [Premier_id [Dernier_id]]\n"); - printf(" Affiche une liste de comptes.\n"); - printf(" 'Premier_id', 'Dernier_id': indique les identifiants de d駱art et de fin.\n"); - printf(" La recherche par nom n'est pas possible avec cette commande.\n"); - printf(" list 10 9999999\n"); - } else if (strcmp(command, "listban") == 0) { - printf("listBan/lsBan [Premier_id [Dernier_id]]\n"); - printf(" Comme list/ls, mais seulement pour les comptes avec statut ou bannis.\n"); - } else if (strcmp(command, "listgm") == 0) { - printf("listGM/lsGM [Premier_id [Dernier_id]]\n"); - printf(" Comme list/ls, mais seulement pour les comptes GM.\n"); - } else if (strcmp(command, "listok") == 0) { - printf("listOK/lsOK [Premier_id [Dernier_id]]\n"); - printf(" Comme list/ls, mais seulement pour les comptes sans statut et non bannis.\n"); - } else if (strcmp(command, "memo") == 0) { - printf("memo \n"); - printf(" Modifie le m駑o d'un compte.\n"); - printf(" 'memo': Il peut avoir jusqu' 253 caract鑽es (avec des espaces ou non).\n"); - } else if (strcmp(command, "name") == 0) { - printf("name \n"); - printf(" Donne le nom d'un compte.\n"); - } else if (strcmp(command, "password") == 0) { - printf("passwd \n"); - printf(" Change le mot de passe d'un compte.\n"); - printf(" Lorsque nouveaumotdepasse est omis,\n"); - printf(" la saisie se fait sans que la frappe ne se voit.\n"); - } else if (strcmp(command, "reloadgm") == 0) { - printf("reloadGM\n"); - printf(" Reload GM configuration file\n"); - } else if (strcmp(command, "search") == 0) { - printf("search \n"); - printf(" Cherche des comptes.\n"); - printf(" Affiche les comptes dont les noms correspondent.\n"); -// printf("search -r/-e/--expr/--regex \n"); -// printf(" Cherche des comptes par expression reguli鑽e.\n"); -// printf(" Affiche les comptes dont les noms correspondent.\n"); - } else if (strcmp(command, "sex") == 0) { - printf("sex \n"); - printf(" Modifie le sexe d'un compte.\n"); - printf(" sex testname Male\n"); - } else if (strcmp(command, "state") == 0) { - printf("state \n"); - printf(" Change le statut d'un compte.\n"); - printf(" 'nouveaustatut': Le statut est le m麥e que celui du packet 0x006a + 1.\n"); - printf(" les possibilit駸 sont:\n"); - printf(" 0 = Compte ok\n"); - printf(" 1 = Unregistered ID\n"); - printf(" 2 = Incorrect Password\n"); - printf(" 3 = This ID is expired\n"); - printf(" 4 = Rejected from Server\n"); - printf(" 5 = You have been blocked by the GM Team\n"); - printf(" 6 = Your Game's EXE file is not the latest version\n"); - printf(" 7 = You are Prohibited to log in until...\n"); - printf(" 8 = Server is jammed due to over populated\n"); - printf(" 9 = No MSG\n"); - printf(" 100 = This ID has been totally erased\n"); - printf(" all other values are 'No MSG', then use state 9 please.\n"); - printf(" 'message_erreur_7': message du code erreur 6 =\n"); - printf(" = Your are Prohibited to log in until... (packet 0x006a)\n"); - } else if (strcmp(command, "timeadd") == 0) { - printf("timeadd \n"); - printf(" Ajoute/soustrait du temps la limite de validit d'un compte.\n"); - printf(" Le modificateur est compos comme suit:\n"); - printf(" Valeur modificatrice (-1, 1, +1, etc...)\n"); - printf(" El駑ent modifi:\n"); - printf(" a ou y: ann馥\n"); - printf(" m: mois\n"); - printf(" j ou d: jour\n"); - printf(" h: heure\n"); - printf(" mn: minute\n"); - printf(" s: seconde\n"); - printf(" timeadd testname +1m-2mn1s-6a\n"); - printf(" Cette exemple ajoute 1 mois et une seconde, et soustrait 2 minutes\n"); - printf(" et 6 ans dans le m麥e temps.\n"); - printf("NOTE: Vous ne pouvez pas modifier une limite de validit illimit馥. Si vous\n"); - printf(" d駸irez le faire, c'est que vous voulez probablement cr馥r un limite de\n"); - printf(" validit limit馥. Donc, en premier, fix une limite de valitid.\n"); - } else if (strcmp(command, "timeadd") == 0) { - printf("timeset aaaa/mm/jj [hh:mm:ss]\n"); - printf(" Change la limite de validit d'un compte.\n"); - printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); - printf("timeset 0\n"); - printf(" Donne une limite de validit illimit馥 (0 = illimit馥).\n"); - } else if (strcmp(command, "unban") == 0) { - printf("unban/unbanish \n"); - printf(" Ote le banissement d'un compte.\n"); - printf(" La commande est l'駲uivalent de banset 0.\n"); - } else if (strcmp(command, "unblock") == 0) { - printf("unblock \n"); - printf(" Place le status d'un compte 0 (Compte ok).\n"); - printf(" La commande est l'駲uivalent de state 0.\n"); - } else if (strcmp(command, "version") == 0) { - printf("version\n"); - printf(" Affiche la version du login-serveur.\n"); - } else if (strcmp(command, "who") == 0) { - printf("who \n"); - printf(" Affiche les informations sur un compte.\n"); -// quit - } else if (strcmp(command, "quit") == 0 || - strcmp(command, "exit") == 0 || - strcmp(command, "end") == 0) { - printf("quit/end/exit\n"); - printf(" Fin du programme d'administration.\n"); -// unknown command - } else { - if (strlen(command) > 0) - printf("Commande inconnue [%s] pour l'aide. Affichage de toutes les commandes.\n", command); - printf(" aide/help/? -- Affiche cet aide\n"); - printf(" aide/help/? [commande] -- Affiche l'aide de la commande\n"); - printf(" add -- Cr馥 un compte (sans email)\n"); - printf(" ban/banish aaaa/mm/jj hh:mm:ss -- Fixe la date finale de banismnt\n"); - printf(" banadd/ba -- Ajout/soustrait du temps la\n"); - printf(" exemple: ba moncompte +1m-2mn1s-2y date finale de banissement\n"); - printf(" banset/bs aaaa/mm/jj [hh:mm:ss] -- Change la date fin de banisemnt\n"); - printf(" banset/bs 0 -- D-banis un compte.\n"); - printf(" block -- Mets le status d'un compte 5 (blocked by the GM Team)\n"); - printf(" check -- V駻ifie un mot de passe d'un compte\n"); - printf(" create -- Cr馥 un compte (avec email)\n"); - printf(" del -- Supprime un compte\n"); - printf(" email -- Modifie l'e-mail d'un compte\n"); - printf(" getcount -- Donne le nb de joueurs en ligne\n"); - printf(" gm [Niveau_GM] -- Modifie le niveau de GM d'un compte\n"); - printf(" id -- Donne l'id d'un compte\n"); - printf(" info -- Affiche les infos sur un compte\n"); - printf(" kami -- Envoi un message g駭駻al (en jaune)\n"); - printf(" kamib -- Envoi un message g駭駻al (en bleu)\n"); - printf(" language -- Change la langue d'affichage.\n"); - printf(" list/ls [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); - printf(" listBan/lsBan [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); - printf(" avec un statut ou bannis\n"); - printf(" listGM/lsGM [Premier_id [Dernier_id] ] -- Affiche une liste de comptes GM\n"); - printf(" listOK/lsOK [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); - printf(" sans status et non bannis\n"); - printf(" memo -- Modifie le memo d'un compte\n"); - printf(" name -- Donne le nom d'un compte\n"); - printf(" passwd -- Change le mot de passe d'un compte\n"); - printf(" quit/end/exit -- Fin du programme d'administation\n"); - printf(" reloadGM -- Recharger le fichier de config des GM\n"); - printf(" search -- Cherche des comptes\n"); -// printf(" search -e/-r/--expr/--regex -- Cherche des comptes par REGEX\n"); - printf(" sex -- Modifie le sexe d'un compte\n"); - printf(" state -- Change le statut d'1 compte\n"); - printf(" timeadd/ta -- Ajout/soustrait du temps la\n"); - printf(" exemple: ta moncompte +1m-2mn1s-2y limite de validit饅n"); - printf(" timeset/ts aaaa/mm/jj [hh:mm:ss] -- Change la limite de validit饅n"); - printf(" timeset/ts 0 -- limite de validit = illimit馥\n"); - printf(" unban/unbanish -- Ote le banissement d'un compte\n"); - printf(" unblock -- Mets le status d'un compte 0 (Compte ok)\n"); - printf(" version -- Donne la version du login-serveur\n"); - printf(" who -- Affiche les infos sur un compte\n"); - printf(" Note: Pour les noms de compte avec des espaces, tapez \"\" (ou ').\n"); - } - } else { - if (strcmp(command, "aide") == 0) { - printf("aide/help/?\n"); - printf(" Display the description of the commands\n"); - printf("aide/help/? [command]\n"); - printf(" Display the description of the specified command\n"); - } else if (strcmp(command, "help") == 0 ) { - printf("aide/help/?\n"); - printf(" Display the description of the commands\n"); - printf("aide/help/? [command]\n"); - printf(" Display the description of the specified command\n"); -// general commands - } else if (strcmp(command, "add") == 0) { - printf("add \n"); - printf(" Create an account with the default email (a@a.com).\n"); - printf(" Concerning the sex, only the first letter is used (F or M).\n"); - printf(" The e-mail is set to a@a.com (default e-mail). It's like to have no e-mail.\n"); - printf(" When the password is omitted,\n"); - printf(" the input is done without displaying of the pressed keys.\n"); - printf(" add testname Male testpass\n"); - } else if (strcmp(command, "ban") == 0) { - printf("ban/banish yyyy/mm/dd hh:mm:ss \n"); - printf(" Changes the final date of a banishment of an account.\n"); - printf(" Like banset, but is at end.\n"); - } else if (strcmp(command, "banadd") == 0) { - printf("banadd \n"); - printf(" Adds or substracts time from the final date of a banishment of an account.\n"); - printf(" Modifier is done as follows:\n"); - printf(" Adjustment value (-1, 1, +1, etc...)\n"); - printf(" Modified element:\n"); - printf(" a or y: year\n"); - printf(" m: month\n"); - printf(" j or d: day\n"); - printf(" h: hour\n"); - printf(" mn: minute\n"); - printf(" s: second\n"); - printf(" banadd testname +1m-2mn1s-6y\n"); - printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf(" and 6 years at the same time.\n"); - printf("NOTE: If you modify the final date of a non-banished account,\n"); - printf(" you fix the final date to (actual time +- adjustments)\n"); - } else if (strcmp(command, "banset") == 0) { - printf("banset yyyy/mm/dd [hh:mm:ss]\n"); - printf(" Changes the final date of a banishment of an account.\n"); - printf(" Default time [hh:mm:ss]: 23:59:59.\n"); - printf("banset 0\n"); - printf(" Set a non-banished account (0 = unbanished).\n"); - } else if (strcmp(command, "block") == 0) { - printf("block \n"); - printf(" Set state 5 (You have been blocked by the GM Team) to an account.\n"); - printf(" This command works like state 5.\n"); - } else if (strcmp(command, "check") == 0) { - printf("check \n"); - printf(" Check the validity of a password for an account.\n"); - printf(" NOTE: Server will never sends back a password.\n"); - printf(" It's the only method you have to know if a password is correct.\n"); - printf(" The other method is to have a ('physical') access to the accounts file.\n"); - } else if (strcmp(command, "create") == 0) { - printf("create \n"); - printf(" Like the 'add' command, but with e-mail moreover.\n"); - printf(" create testname Male my@mail.com testpass\n"); - } else if (strcmp(command, "delete") == 0) { - printf("del \n"); - printf(" Remove an account.\n"); - printf(" This order requires confirmation. After confirmation, the account is deleted.\n"); - } else if (strcmp(command, "email") == 0) { - printf("email \n"); - printf(" Modify the e-mail of an account.\n"); - } else if (strcmp(command, "getcount") == 0) { - printf("getcount\n"); - printf(" Give the number of players online on all char-servers.\n"); - } else if (strcmp(command, "gm") == 0) { - printf("gm [GM_level]\n"); - printf(" Modify the GM level of an account.\n"); - printf(" Default value remove GM level (GM level = 0).\n"); - printf(" gm testname 80\n"); - } else if (strcmp(command, "id") == 0) { - printf("id \n"); - printf(" Give the id of an account.\n"); - } else if (strcmp(command, "info") == 0) { - printf("info \n"); - printf(" Display complete information of an account.\n"); - } else if (strcmp(command, "kami") == 0) { - printf("kami \n"); - printf(" Sends a broadcast message on all map-server (in yellow).\n"); - } else if (strcmp(command, "kamib") == 0) { - printf("kamib \n"); - printf(" Sends a broadcast message on all map-server (in blue).\n"); - } else if (strcmp(command, "language") == 0) { - printf("language \n"); - printf(" Change the language of displaying.\n"); - printf(" Possible languages: Fran軋is or English.\n"); - } else if (strcmp(command, "list") == 0) { - printf("list/ls [start_id [end_id]]\n"); - printf(" Display a list of accounts.\n"); - printf(" 'start_id', 'end_id': indicate end and start identifiers.\n"); - printf(" Research by name is not possible with this command.\n"); - printf(" list 10 9999999\n"); - } else if (strcmp(command, "listban") == 0) { - printf("listBan/lsBan [start_id [end_id]]\n"); - printf(" Like list/ls, but only for accounts with state or banished.\n"); - } else if (strcmp(command, "listgm") == 0) { - printf("listGM/lsGM [start_id [end_id]]\n"); - printf(" Like list/ls, but only for GM accounts.\n"); - } else if (strcmp(command, "listok") == 0) { - printf("listOK/lsOK [start_id [end_id]]\n"); - printf(" Like list/ls, but only for accounts without state and not banished.\n"); - } else if (strcmp(command, "memo") == 0) { - printf("memo \n"); - printf(" Modify the memo of an account.\n"); - printf(" 'memo': it can have until 253 characters (with spaces or not).\n"); - } else if (strcmp(command, "name") == 0) { - printf("name \n"); - printf(" Give the name of an account.\n"); - } else if (strcmp(command, "password") == 0) { - printf("passwd \n"); - printf(" Change the password of an account.\n"); - printf(" When new password is omitted,\n"); - printf(" the input is done without displaying of the pressed keys.\n"); - } else if (strcmp(command, "reloadgm") == 0) { - printf("reloadGM\n"); - printf(" Reload GM configuration file\n"); - } else if (strcmp(command, "search") == 0) { - printf("search \n"); - printf(" Seek accounts.\n"); - printf(" Displays the accounts whose names correspond.\n"); -// printf("search -r/-e/--expr/--regex \n"); -// printf(" Seek accounts by regular expression.\n"); -// printf(" Displays the accounts whose names correspond.\n"); - } else if (strcmp(command, "sex") == 0) { - printf("sex \n"); - printf(" Modify the sex of an account.\n"); - printf(" sex testname Male\n"); - } else if (strcmp(command, "state") == 0) { - printf("state \n"); - printf(" Change the state of an account.\n"); - printf(" 'new_state': state is the state of the packet 0x006a + 1.\n"); - printf(" The possibilities are:\n"); - printf(" 0 = Account ok\n"); - printf(" 1 = Unregistered ID\n"); - printf(" 2 = Incorrect Password\n"); - printf(" 3 = This ID is expired\n"); - printf(" 4 = Rejected from Server\n"); - printf(" 5 = You have been blocked by the GM Team\n"); - printf(" 6 = Your Game's EXE file is not the latest version\n"); - printf(" 7 = You are Prohibited to log in until...\n"); - printf(" 8 = Server is jammed due to over populated\n"); - printf(" 9 = No MSG\n"); - printf(" 100 = This ID has been totally erased\n"); - printf(" all other values are 'No MSG', then use state 9 please.\n"); - printf(" 'error_message_#7': message of the code error 6\n"); - printf(" = Your are Prohibited to log in until... (packet 0x006a)\n"); - } else if (strcmp(command, "timeadd") == 0) { - printf("timeadd \n"); - printf(" Adds or substracts time from the validity limit of an account.\n"); - printf(" Modifier is done as follows:\n"); - printf(" Adjustment value (-1, 1, +1, etc...)\n"); - printf(" Modified element:\n"); - printf(" a or y: year\n"); - printf(" m: month\n"); - printf(" j or d: day\n"); - printf(" h: hour\n"); - printf(" mn: minute\n"); - printf(" s: second\n"); - printf(" timeadd testname +1m-2mn1s-6y\n"); - printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf(" and 6 years at the same time.\n"); - printf("NOTE: You can not modify a unlimited validity limit.\n"); - printf(" If you want modify it, you want probably create a limited validity limit.\n"); - printf(" So, at first, you must set the validity limit to a date/time.\n"); - } else if (strcmp(command, "timeadd") == 0) { - printf("timeset yyyy/mm/dd [hh:mm:ss]\n"); - printf(" Changes the validity limit of an account.\n"); - printf(" Default time [hh:mm:ss]: 23:59:59.\n"); - printf("timeset 0\n"); - printf(" Gives an unlimited validity limit (0 = unlimited).\n"); - } else if (strcmp(command, "unban") == 0) { - printf("unban/unbanish \n"); - printf(" Remove the banishment of an account.\n"); - printf(" This command works like banset 0.\n"); - } else if (strcmp(command, "unblock") == 0) { - printf("unblock \n"); - printf(" Set state 0 (Account ok) to an account.\n"); - printf(" This command works like state 0.\n"); - } else if (strcmp(command, "version") == 0) { - printf("version\n"); - printf(" Display the version of the login-server.\n"); - } else if (strcmp(command, "who") == 0) { - printf("who \n"); - printf(" Displays complete information of an account.\n"); -// quit - } else if (strcmp(command, "quit") == 0 || - strcmp(command, "exit") == 0 || - strcmp(command, "end") == 0) { - printf("quit/end/exit\n"); - printf(" End of the program of administration.\n"); -// unknown command - } else { - if (strlen(command) > 0) - printf("Unknown command [%s] for help. Displaying of all commands.\n", command); - printf(" aide/help/? -- Display this help\n"); - printf(" aide/help/? [command] -- Display the help of the command\n"); - printf(" add -- Create an account with default email\n"); - printf(" ban/banish yyyy/mm/dd hh:mm:ss -- Change final date of a ban\n"); - printf(" banadd/ba -- Add or substract time from the final\n"); - printf(" example: ba apple +1m-2mn1s-2y date of a banishment of an account\n"); - printf(" banset/bs yyyy/mm/dd [hh:mm:ss] -- Change final date of a ban\n"); - printf(" banset/bs 0 -- Un-banish an account\n"); - printf(" block -- Set state 5 (blocked by the GM Team) to an account\n"); - printf(" check -- Check the validity of a password\n"); - printf(" create -- Create an account with email\n"); - printf(" del -- Remove an account\n"); - printf(" email -- Modify an email of an account\n"); - printf(" getcount -- Give the number of players online\n"); - printf(" gm [GM_level] -- Modify the GM level of an account\n"); - printf(" id -- Give the id of an account\n"); - printf(" info -- Display all information of an account\n"); - printf(" kami -- Sends a broadcast message (in yellow)\n"); - printf(" kamib -- Sends a broadcast message (in blue)\n"); - printf(" language -- Change the language of displaying.\n"); - printf(" list/ls [First_id [Last_id]] -- Display a list of accounts\n"); - printf(" listBan/lsBan [First_id [Last_id] ] -- Display a list of accounts\n"); - printf(" with state or banished\n"); - printf(" listGM/lsGM [First_id [Last_id]] -- Display a list of GM accounts\n"); - printf(" listOK/lsOK [First_id [Last_id] ] -- Display a list of accounts\n"); - printf(" without state and not banished\n"); - printf(" memo -- Modify the memo of an account\n"); - printf(" name -- Give the name of an account\n"); - printf(" passwd -- Change the password of an account\n"); - printf(" quit/end/exit -- End of the program of administation\n"); - printf(" reloadGM -- Reload GM configuration file\n"); - printf(" search -- Seek accounts\n"); -// printf(" search -e/-r/--expr/--regex -- Seek accounts by regular-expression\n"); - printf(" sex -- Modify the sex of an account\n"); - printf(" state -- Change the state\n"); - printf(" timeadd/ta -- Add or substract time from the\n"); - printf(" example: ta apple +1m-2mn1s-2y validity limit of an account\n"); - printf(" timeset/ts yyyy/mm/dd [hh:mm:ss] -- Change the validify limit\n"); - printf(" timeset/ts 0 -- Give a unlimited validity limit\n"); - printf(" unban/unbanish -- Remove the banishment of an account\n"); - printf(" unblock -- Set state 0 (Account ok) to an account\n"); - printf(" version -- Gives the version of the login-server\n"); - printf(" who -- Display all information of an account\n"); - printf(" who -- Display all information of an account\n"); - printf(" Note: To use spaces in an account name, type \"\" (or ').\n"); - } - } -} - -//----------------------------- -// Sub-function: add an account -//----------------------------- -int addaccount(char* param, int emailflag) { - char name[1023], sex[1023], email[1023], password[1023]; -// int i; - WFIFOHEAD(login_fd,91); - - memset(name, '\0', sizeof(name)); - memset(sex, '\0', sizeof(sex)); - memset(email, '\0', sizeof(email)); - memset(password, '\0', sizeof(password)); - - if (emailflag == 0) { // add command - if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, sex, password) < 2 && // password can be void - sscanf(param, "'%[^']' %s %[^\r\n]", name, sex, password) < 2 && // password can be void - sscanf(param, "%s %s %[^\r\n]", name, sex, password) < 2) { // password can be void - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte, un sexe et un mot de passe svp.\n"); - printf(" add nomtest Male motdepassetest\n"); - ladmin_log("Nombre incorrect de param鑼res pour cr馥r un compte (commande 'add')." RETCODE); - } else { - printf("Please input an account name, a sex and a password.\n"); - printf(" add testname Male testpass\n"); - ladmin_log("Incomplete parameters to create an account ('add' command)." RETCODE); - } - return 136; - } - strcpy(email, "a@a.com"); // default email - } else { // 1: create command - if (sscanf(param, "\"%[^\"]\" %s %s %[^\r\n]", name, sex, email, password) < 3 && // password can be void - sscanf(param, "'%[^']' %s %s %[^\r\n]", name, sex, email, password) < 3 && // password can be void - sscanf(param, "%s %s %s %[^\r\n]", name, sex, email, password) < 3) { // password can be void - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte, un sexe et un mot de passe svp.\n"); - printf(" create nomtest Male mo@mail.com motdepassetest\n"); - ladmin_log("Nombre incorrect de param鑼res pour cr馥r un compte (commande 'create')." RETCODE); - } else { - printf("Please input an account name, a sex and a password.\n"); - printf(" create testname Male my@mail.com testpass\n"); - ladmin_log("Incomplete parameters to create an account ('create' command)." RETCODE); - } - return 136; - } - } - if (verify_accountname(name) == 0) { - return 102; - } - -/* for(i = 0; name[i]; i++) { - if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_", name[i]) == NULL) { - if (defaultlanguage == 'F') { - printf("Caract鑽e interdit (%c) trouv dans le nom du compte (%d%s caract鑽e).\n", name[i], i+1, makeordinal(i+1)); - ladmin_log("Caract鑽e interdit (%c) trouv dans le nom du compte (%d%s caract鑽e)." RETCODE, name[i], i+1, makeordinal(i+1)); - } else { - printf("Illegal character (%c) found in the account name (%d%s character).\n", name[i], i+1, makeordinal(i+1)); - ladmin_log("Illegal character (%c) found in the account name (%d%s character)." RETCODE, name[i], i+1, makeordinal(i+1)); - } - return 101; - } - }*/ - - sex[0] = toupper(sex[0]); - if (strchr("MF", sex[0]) == NULL) { - if (defaultlanguage == 'F') { - printf("Sexe incorrect [%s]. Entrez M ou F svp.\n", sex); - ladmin_log("Sexe incorrect [%s]. Entrez M ou F svp." RETCODE, sex); - } else { - printf("Illegal gender [%s]. Please input M or F.\n", sex); - ladmin_log("Illegal gender [%s]. Please input M or F." RETCODE, sex); - } - return 103; - } - - if (strlen(email) < 3) { - if (defaultlanguage == 'F') { - printf("Email trop courte [%s]. Entrez une e-mail valide svp.\n", email); - ladmin_log("Email trop courte [%s]. Entrez une e-mail valide svp." RETCODE, email); - } else { - printf("Email is too short [%s]. Please input a valid e-mail.\n", email); - ladmin_log("Email is too short [%s]. Please input a valid e-mail." RETCODE, email); - } - return 109; - } - if (strlen(email) > 39) { - if (defaultlanguage == 'F') { - printf("Email trop longue [%s]. Entrez une e-mail de 39 caract鑽es maximum svp.\n", email); - ladmin_log("Email trop longue [%s]. Entrez une e-mail de 39 caract鑽es maximum svp." RETCODE, email); - } else { - printf("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", email); - ladmin_log("Email is too long [%s]. Please input an e-mail with 39 bytes at the most." RETCODE, email); - } - return 109; - } - if (e_mail_check(email) == 0) { - if (defaultlanguage == 'F') { - printf("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", email); - ladmin_log("Email incorrecte [%s]. Entrez une e-mail valide svp." RETCODE, email); - } else { - printf("Invalid email [%s]. Please input a valid e-mail.\n", email); - ladmin_log("Invalid email [%s]. Please input a valid e-mail." RETCODE, email); - } - return 109; - } - - if (strlen(password) == 0) { - if (typepasswd(password) == 0) - return 108; - } - if (verify_password(password) == 0) - return 104; - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour cr馥r un compte." RETCODE); - } else { - ladmin_log("Request to login-server to create an account." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7930; - memcpy(WFIFOP(login_fd,2), name, 24); - memcpy(WFIFOP(login_fd,26), password, 24); - WFIFOB(login_fd,50) = sex[0]; - memcpy(WFIFOP(login_fd,51), email, 40); - WFIFOSET(login_fd,91); - bytes_to_read = 1; - - return 0; -} - -//--------------------------------------------------------------------------------- -// Sub-function: Add/substract time to the final date of a banishment of an account -//--------------------------------------------------------------------------------- -int banaddaccount(char* param) { - char name[1023], modif[1023]; - int year, month, day, hour, minute, second; - char * p_modif; - int value, i; - WFIFOHEAD(login_fd,38); - - memset(name, '\0', sizeof(name)); - memset(modif, '\0', sizeof(modif)); - year = month = day = hour = minute = second = 0; - - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, modif) < 2 && - sscanf(param, "'%[^']' %[^\r\n]", name, modif) < 2 && - sscanf(param, "%s %[^\r\n]", name, modif) < 2) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte et un modificateur svp.\n"); - printf(" banadd nomtest +1m-2mn1s-6y\n"); - printf(" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); - printf(" et 6 ans dans le m麥e temps.\n"); - ladmin_log("Nombre incorrect de param鑼res pour modifier la fin de ban d'un compte (commande 'banadd')." RETCODE); - } else { - printf("Please input an account name and a modifier.\n"); - printf(" : banadd testname +1m-2mn1s-6y\n"); - printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf(" and 6 years at the same time.\n"); - ladmin_log("Incomplete parameters to modify the ban date/time of an account ('banadd' command)." RETCODE); - } - return 136; - } - if (verify_accountname(name) == 0) { - return 102; - } - - // lowercase for modif - for (i = 0; modif[i]; i++) - modif[i] = tolower(modif[i]); - p_modif = modif; - while (strlen(p_modif) > 0) { - value = atoi(p_modif); - if (value == 0) { - p_modif++; - } else { - if (p_modif[0] == '-' || p_modif[0] == '+') - p_modif++; - while (strlen(p_modif) > 0 && p_modif[0] >= '0' && p_modif[0] <= '9') { - p_modif++; - } - if (p_modif[0] == 's') { - second = value; - p_modif++; - } else if (p_modif[0] == 'm' && p_modif[1] == 'n') { - minute = value; - p_modif += 2; - } else if (p_modif[0] == 'h') { - hour = value; - p_modif++; - } else if (p_modif[0] == 'd' || p_modif[0] == 'j') { - day = value; - p_modif += 2; - } else if (p_modif[0] == 'm') { - month = value; - p_modif++; - } else if (p_modif[0] == 'y' || p_modif[0] == 'a') { - year = value; - p_modif++; - } else { - p_modif++; - } - } - } - - if (defaultlanguage == 'F') { - printf(" ann馥: %d\n", year); - printf(" mois: %d\n", month); - printf(" jour: %d\n", day); - printf(" heure: %d\n", hour); - printf(" minute: %d\n", minute); - printf(" seconde: %d\n", second); - } else { - printf(" year: %d\n", year); - printf(" month: %d\n", month); - printf(" day: %d\n", day); - printf(" hour: %d\n", hour); - printf(" minute: %d\n", minute); - printf(" second: %d\n", second); - } - - if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0) { - if (defaultlanguage == 'F') { - printf("Vous devez entrer un ajustement avec cette commande, svp:\n"); - printf(" Valeur d'ajustement (-1, 1, +1, etc...)\n"); - printf(" Element modifi:\n"); - printf(" a ou y: ann馥\n"); - printf(" m: mois\n"); - printf(" j ou d: jour\n"); - printf(" h: heure\n"); - printf(" mn: minute\n"); - printf(" s: seconde\n"); - printf(" banadd nomtest +1m-2mn1s-6y\n"); - printf(" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); - printf(" et 6 ans dans le m麥e temps.\n"); - ladmin_log("Aucun ajustement n'est pas un ajustement (commande 'banadd')." RETCODE); - } else { - printf("Please give an adjustment with this command:\n"); - printf(" Adjustment value (-1, 1, +1, etc...)\n"); - printf(" Modified element:\n"); - printf(" a or y: year\n"); - printf(" m: month\n"); - printf(" j or d: day\n"); - printf(" h: hour\n"); - printf(" mn: minute\n"); - printf(" s: second\n"); - printf(" banadd testname +1m-2mn1s-6y\n"); - printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf(" and 6 years at the same time.\n"); - ladmin_log("No adjustment isn't an adjustment ('banadd' command)." RETCODE); - } - return 137; - } - if (year > 127 || year < -127) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement d'ann馥s correct (de -127 127), svp.\n"); - ladmin_log("Ajustement de l'ann馥 hors norme (commande 'banadd')." RETCODE); - } else { - printf("Please give a correct adjustment for the years (from -127 to 127).\n"); - ladmin_log("Abnormal adjustement for the year ('banadd' command)." RETCODE); - } - return 137; - } - if (month > 255 || month < -255) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement de mois correct (de -255 255), svp.\n"); - ladmin_log("Ajustement du mois hors norme (commande 'banadd')." RETCODE); - } else { - printf("Please give a correct adjustment for the months (from -255 to 255).\n"); - ladmin_log("Abnormal adjustement for the month ('banadd' command)." RETCODE); - } - return 137; - } - if (day > 32767 || day < -32767) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement de jours correct (de -32767 32767), svp.\n"); - ladmin_log("Ajustement des jours hors norme (commande 'banadd')." RETCODE); - } else { - printf("Please give a correct adjustment for the days (from -32767 to 32767).\n"); - ladmin_log("Abnormal adjustement for the days ('banadd' command)." RETCODE); - } - return 137; - } - if (hour > 32767 || hour < -32767) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement d'heures correct (de -32767 32767), svp.\n"); - ladmin_log("Ajustement des heures hors norme (commande 'banadd')." RETCODE); - } else { - printf("Please give a correct adjustment for the hours (from -32767 to 32767).\n"); - ladmin_log("Abnormal adjustement for the hours ('banadd' command)." RETCODE); - } - return 137; - } - if (minute > 32767 || minute < -32767) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement de minutes correct (de -32767 32767), svp.\n"); - ladmin_log("Ajustement des minutes hors norme (commande 'banadd')." RETCODE); - } else { - printf("Please give a correct adjustment for the minutes (from -32767 to 32767).\n"); - ladmin_log("Abnormal adjustement for the minutes ('banadd' command)." RETCODE); - } - return 137; - } - if (second > 32767 || second < -32767) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement de secondes correct (de -32767 32767), svp.\n"); - ladmin_log("Ajustement des secondes hors norme (commande 'banadd')." RETCODE); - } else { - printf("Please give a correct adjustment for the seconds (from -32767 to 32767).\n"); - ladmin_log("Abnormal adjustement for the seconds ('banadd' command)." RETCODE); - } - return 137; - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour modifier la date d'un bannissement." RETCODE); - } else { - ladmin_log("Request to login-server to modify a ban date/time." RETCODE); - } - - WFIFOW(login_fd,0) = 0x794c; - memcpy(WFIFOP(login_fd,2), name, 24); - WFIFOW(login_fd,26) = (short)year; - WFIFOW(login_fd,28) = (short)month; - WFIFOW(login_fd,30) = (short)day; - WFIFOW(login_fd,32) = (short)hour; - WFIFOW(login_fd,34) = (short)minute; - WFIFOW(login_fd,36) = (short)second; - WFIFOSET(login_fd,38); - bytes_to_read = 1; - - return 0; -} - -//----------------------------------------------------------------------- -// Sub-function of sub-function banaccount, unbanaccount or bansetaccount -// Set the final date of a banishment of an account -//----------------------------------------------------------------------- -int bansetaccountsub(char* name, char* date, char* time) { - int year, month, day, hour, minute, second; - time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) - struct tm *tmtime; - WFIFOHEAD(login_fd,30); - - year = month = day = hour = minute = second = 0; - ban_until_time = 0; - tmtime = localtime(&ban_until_time); // initialize - - if (verify_accountname(name) == 0) { - return 102; - } - - if (atoi(date) != 0 && - ((sscanf(date, "%d/%d/%d", &year, &month, &day) < 3 && - sscanf(date, "%d-%d-%d", &year, &month, &day) < 3 && - sscanf(date, "%d.%d.%d", &year, &month, &day) < 3) || - sscanf(time, "%d:%d:%d", &hour, &minute, &second) < 3)) { - if (defaultlanguage == 'F') { - printf("Entrez une date et une heure svp (format: aaaa/mm/jj hh:mm:ss).\n"); - printf("Vous pouvez aussi mettre 0 la place si vous utilisez la commande 'banset'.\n"); - ladmin_log("Format incorrect pour la date/heure (commande'banset' ou 'ban')." RETCODE); - } else { - printf("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n"); - printf("You can imput 0 instead of if you use 'banset' command.\n"); - ladmin_log("Invalid format for the date/time ('banset' or 'ban' command)." RETCODE); - } - return 102; - } - - if (atoi(date) == 0) { - ban_until_time = 0; - } else { - if (year < 70) { - year = year + 100; - } - if (year >= 1900) { - year = year - 1900; - } - if (month < 1 || month > 12) { - if (defaultlanguage == 'F') { - printf("Entrez un mois correct svp (entre 1 et 12).\n"); - ladmin_log("Mois incorrect pour la date (command 'banset' ou 'ban')." RETCODE); - } else { - printf("Please give a correct value for the month (from 1 to 12).\n"); - ladmin_log("Invalid month for the date ('banset' or 'ban' command)." RETCODE); - } - return 102; - } - month = month - 1; - if (day < 1 || day > 31) { - if (defaultlanguage == 'F') { - printf("Entrez un jour correct svp (entre 1 et 31).\n"); - ladmin_log("Jour incorrect pour la date (command 'banset' ou 'ban')." RETCODE); - } else { - printf("Please give a correct value for the day (from 1 to 31).\n"); - ladmin_log("Invalid day for the date ('banset' or 'ban' command)." RETCODE); - } - return 102; - } - if (((month == 3 || month == 5 || month == 8 || month == 10) && day > 30) || - (month == 1 && day > 29)) { - if (defaultlanguage == 'F') { - printf("Entrez un jour correct en fonction du mois (%d) svp.\n", month); - ladmin_log("Jour incorrect pour ce mois correspondant (command 'banset' ou 'ban')." RETCODE); - } else { - printf("Please give a correct value for a day of this month (%d).\n", month); - ladmin_log("Invalid day for this month ('banset' or 'ban' command)." RETCODE); - } - return 102; - } - if (hour < 0 || hour > 23) { - if (defaultlanguage == 'F') { - printf("Entrez une heure correcte svp (entre 0 et 23).\n"); - ladmin_log("Heure incorrecte pour l'heure (command 'banset' ou 'ban')." RETCODE); - } else { - printf("Please give a correct value for the hour (from 0 to 23).\n"); - ladmin_log("Invalid hour for the time ('banset' or 'ban' command)." RETCODE); - } - return 102; - } - if (minute < 0 || minute > 59) { - if (defaultlanguage == 'F') { - printf("Entrez des minutes correctes svp (entre 0 et 59).\n"); - ladmin_log("Minute incorrecte pour l'heure (command 'banset' ou 'ban')." RETCODE); - } else { - printf("Please give a correct value for the minutes (from 0 to 59).\n"); - ladmin_log("Invalid minute for the time ('banset' or 'ban' command)." RETCODE); - } - return 102; - } - if (second < 0 || second > 59) { - if (defaultlanguage == 'F') { - printf("Entrez des secondes correctes svp (entre 0 et 59).\n"); - ladmin_log("Seconde incorrecte pour l'heure (command 'banset' ou 'ban')." RETCODE); - } else { - printf("Please give a correct value for the seconds (from 0 to 59).\n"); - ladmin_log("Invalid second for the time ('banset' or 'ban' command)." RETCODE); - } - return 102; - } - tmtime->tm_year = year; - tmtime->tm_mon = month; - tmtime->tm_mday = day; - tmtime->tm_hour = hour; - tmtime->tm_min = minute; - tmtime->tm_sec = second; - tmtime->tm_isdst = -1; // -1: no winter/summer time modification - ban_until_time = mktime(tmtime); - if (ban_until_time == -1) { - if (defaultlanguage == 'F') { - printf("Date incorrecte.\n"); - printf("Entrez une date et une heure svp (format: aaaa/mm/jj hh:mm:ss).\n"); - printf("Vous pouvez aussi mettre 0 la place si vous utilisez la commande 'banset'.\n"); - ladmin_log("Date incorrecte. (command 'banset' ou 'ban')." RETCODE); - } else { - printf("Invalid date.\n"); - printf("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n"); - printf("You can imput 0 instead of if you use 'banset' command.\n"); - ladmin_log("Invalid date. ('banset' or 'ban' command)." RETCODE); - } - return 102; - } - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour fixer un ban." RETCODE); - } else { - ladmin_log("Request to login-server to set a ban." RETCODE); - } - - WFIFOW(login_fd,0) = 0x794a; - memcpy(WFIFOP(login_fd,2), name, 24); - WFIFOL(login_fd,26) = (int)ban_until_time; - WFIFOSET(login_fd,30); - bytes_to_read = 1; - - return 0; -} - -//--------------------------------------------------------------------- -// Sub-function: Set the final date of a banishment of an account (ban) -//--------------------------------------------------------------------- -int banaccount(char* param) { - char name[1023], date[1023], time[1023]; - - memset(name, '\0', sizeof(name)); - memset(date, '\0', sizeof(date)); - memset(time, '\0', sizeof(time)); - - if (sscanf(param, "%s %s \"%[^\"]\"", date, time, name) < 3 && - sscanf(param, "%s %s '%[^']'", date, time, name) < 3 && - sscanf(param, "%s %s %[^\r\n]", date, time, name) < 3) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte, une date et une heure svp.\n"); - printf(": banset aaaa/mm/jj [hh:mm:ss]\n"); - printf(" banset 0 (0 = d-bani)\n"); - printf(" ban/banish aaaa/mm/jj hh:mm:ss \n"); - printf(" unban/unbanish \n"); - printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); - ladmin_log("Nombre incorrect de param鑼res pour fixer un ban (commande 'banset' ou 'ban')." RETCODE); - } else { - printf("Please input an account name, a date and a hour.\n"); - printf(": banset yyyy/mm/dd [hh:mm:ss]\n"); - printf(" banset 0 (0 = un-banished)\n"); - printf(" ban/banish yyyy/mm/dd hh:mm:ss \n"); - printf(" unban/unbanish \n"); - printf(" Default time [hh:mm:ss]: 23:59:59.\n"); - ladmin_log("Incomplete parameters to set a ban ('banset' or 'ban' command)." RETCODE); - } - return 136; - } - - return bansetaccountsub(name, date, time); -} - -//------------------------------------------------------------------------ -// Sub-function: Set the final date of a banishment of an account (banset) -//------------------------------------------------------------------------ -int bansetaccount(char* param) { - char name[1023], date[1023], time[1023]; - - memset(name, '\0', sizeof(name)); - memset(date, '\0', sizeof(date)); - memset(time, '\0', sizeof(time)); - - if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void - sscanf(param, "'%[^']' %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void - sscanf(param, "%s %s %[^\r\n]", name, date, time) < 2) { // if date = 0, time can be void - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte, une date et une heure svp.\n"); - printf(": banset aaaa/mm/jj [hh:mm:ss]\n"); - printf(" banset 0 (0 = d-bani)\n"); - printf(" ban/banish aaaa/mm/jj hh:mm:ss \n"); - printf(" unban/unbanish \n"); - printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); - ladmin_log("Nombre incorrect de param鑼res pour fixer un ban (commande 'banset' ou 'ban')." RETCODE); - } else { - printf("Please input an account name, a date and a hour.\n"); - printf(": banset yyyy/mm/dd [hh:mm:ss]\n"); - printf(" banset 0 (0 = un-banished)\n"); - printf(" ban/banish yyyy/mm/dd hh:mm:ss \n"); - printf(" unban/unbanish \n"); - printf(" Default time [hh:mm:ss]: 23:59:59.\n"); - ladmin_log("Incomplete parameters to set a ban ('banset' or 'ban' command)." RETCODE); - } - return 136; - } - - if (time[0] == '\0') - strcpy(time, "23:59:59"); - - return bansetaccountsub(name, date, time); -} - -//------------------------------------------------- -// Sub-function: unbanishment of an account (unban) -//------------------------------------------------- -int unbanaccount(char* param) { - char name[1023]; - - memset(name, '\0', sizeof(name)); - - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || - strlen(name) == 0) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte svp.\n"); - printf(": banset aaaa/mm/jj [hh:mm:ss]\n"); - printf(" banset 0 (0 = d-bani)\n"); - printf(" ban/banish aaaa/mm/jj hh:mm:ss \n"); - printf(" unban/unbanish \n"); - printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); - ladmin_log("Nombre incorrect de param鑼res pour fixer un ban (commande 'unban')." RETCODE); - } else { - printf("Please input an account name.\n"); - printf(": banset yyyy/mm/dd [hh:mm:ss]\n"); - printf(" banset 0 (0 = un-banished)\n"); - printf(" ban/banish yyyy/mm/dd hh:mm:ss \n"); - printf(" unban/unbanish \n"); - printf(" Default time [hh:mm:ss]: 23:59:59.\n"); - ladmin_log("Incomplete parameters to set a ban ('unban' command)." RETCODE); - } - return 136; - } - - return bansetaccountsub(name, "0", ""); -} - -//--------------------------------------------------------- -// Sub-function: Asking to check the validity of a password -// (Note: never send back a password with login-server!! security of passwords) -//--------------------------------------------------------- -int checkaccount(char* param) { - char name[1023], password[1023]; - WFIFOHEAD(login_fd,50); - - memset(name, '\0', sizeof(name)); - memset(password, '\0', sizeof(password)); - - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, password) < 1 && // password can be void - sscanf(param, "'%[^']' %[^\r\n]", name, password) < 1 && // password can be void - sscanf(param, "%s %[^\r\n]", name, password) < 1) { // password can be void - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte svp.\n"); - printf(" check testname motdepasse\n"); - ladmin_log("Nombre incorrect de param鑼res pour tester le mot d'un passe d'un compte (commande 'check')." RETCODE); - } else { - printf("Please input an account name.\n"); - printf(" check testname password\n"); - ladmin_log("Incomplete parameters to check the password of an account ('check' command)." RETCODE); - } - return 136; - } - - if (verify_accountname(name) == 0) { - return 102; - } - - if (strlen(password) == 0) { - if (typepasswd(password) == 0) - return 134; - } - if (verify_password(password) == 0) - return 131; - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour test un mot de passe." RETCODE); - } else { - ladmin_log("Request to login-server to check a password." RETCODE); - } - - WFIFOW(login_fd,0) = 0x793a; - memcpy(WFIFOP(login_fd,2), name, 24); - memcpy(WFIFOP(login_fd,26), password, 24); - WFIFOSET(login_fd,50); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------------ -// Sub-function: Asking for deletion of an account -//------------------------------------------------ -int delaccount(char* param) { - char name[1023]; - char letter; - char confirm[1023]; - int i; - WFIFOHEAD(login_fd,26); - - memset(name, '\0', sizeof(name)); - - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || - strlen(name) == 0) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte svp.\n"); - printf(" del nomtestasupprimer\n"); - ladmin_log("Aucun nom donn pour supprimer un compte (commande 'delete')." RETCODE); - } else { - printf("Please input an account name.\n"); - printf(" del testnametodelete\n"); - ladmin_log("No name given to delete an account ('delete' command)." RETCODE); - } - return 136; - } - - if (verify_accountname(name) == 0) { - return 102; - } - - memset(confirm, '\0', sizeof(confirm)); - while ((confirm[0] != 'o' || defaultlanguage != 'F') && confirm[0] != 'n' && (confirm[0] != 'y' || defaultlanguage == 'F')) { - if (defaultlanguage == 'F') - printf("\033[1;36m ** Etes-vous vraiment sr de vouloir SUPPRIMER le compte [$userid]? (o/n) > \033[0m"); - else - printf("\033[1;36m ** Are you really sure to DELETE account [$userid]? (y/n) > \033[0m"); - fflush(stdout); - memset(confirm, '\0', sizeof(confirm)); - i = 0; - while ((letter = getchar()) != '\n') - confirm[i++] = letter; - } - - if (confirm[0] == 'n') { - if (defaultlanguage == 'F') { - printf("Suppression annul馥.\n"); - ladmin_log("Suppression annul馥 par l'utilisateur (commande 'delete')." RETCODE); - } else { - printf("Deletion canceled.\n"); - ladmin_log("Deletion canceled by user ('delete' command)." RETCODE); - } - return 121; - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour d騁ruire un compte." RETCODE); - } else { - ladmin_log("Request to login-server to delete an acount." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7932; - memcpy(WFIFOP(login_fd,2), name, 24); - WFIFOSET(login_fd,26); - bytes_to_read = 1; - - return 0; -} - -//---------------------------------------------------------- -// Sub-function: Asking to modification of an account e-mail -//---------------------------------------------------------- -int changeemail(char* param) { - char name[1023], email[1023]; - WFIFOHEAD(login_fd,66); - - memset(name, '\0', sizeof(name)); - memset(email, '\0', sizeof(email)); - - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, email) < 2 && - sscanf(param, "'%[^']' %[^\r\n]", name, email) < 2 && - sscanf(param, "%s %[^\r\n]", name, email) < 2) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte et une email svp.\n"); - printf(" email testname nouveauemail\n"); - ladmin_log("Nombre incorrect de param鑼res pour changer l'email d'un compte (commande 'email')." RETCODE); - } else { - printf("Please input an account name and an email.\n"); - printf(" email testname newemail\n"); - ladmin_log("Incomplete parameters to change the email of an account ('email' command)." RETCODE); - } - return 136; - } - - if (verify_accountname(name) == 0) { - return 102; - } - - if (strlen(email) < 3) { - if (defaultlanguage == 'F') { - printf("Email trop courte [%s]. Entrez une e-mail valide svp.\n", email); - ladmin_log("Email trop courte [%s]. Entrez une e-mail valide svp." RETCODE, email); - } else { - printf("Email is too short [%s]. Please input a valid e-mail.\n", email); - ladmin_log("Email is too short [%s]. Please input a valid e-mail." RETCODE, email); - } - return 109; - } - if (strlen(email) > 39) { - if (defaultlanguage == 'F') { - printf("Email trop longue [%s]. Entrez une e-mail de 39 caract鑽es maximum svp.\n", email); - ladmin_log("Email trop longue [%s]. Entrez une e-mail de 39 caract鑽es maximum svp." RETCODE, email); - } else { - printf("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", email); - ladmin_log("Email is too long [%s]. Please input an e-mail with 39 bytes at the most." RETCODE, email); - } - return 109; - } - if (e_mail_check(email) == 0) { - if (defaultlanguage == 'F') { - printf("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", email); - ladmin_log("Email incorrecte [%s]. Entrez une e-mail valide svp." RETCODE, email); - } else { - printf("Invalid email [%s]. Please input a valid e-mail.\n", email); - ladmin_log("Invalid email [%s]. Please input a valid e-mail." RETCODE, email); - } - return 109; - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer une email." RETCODE); - } else { - ladmin_log("Request to login-server to change an email." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7940; - memcpy(WFIFOP(login_fd,2), name, 24); - memcpy(WFIFOP(login_fd,26), email, 40); - WFIFOSET(login_fd,66); - bytes_to_read = 1; - - return 0; -} - -//----------------------------------------------------- -// Sub-function: Asking of the number of online players -//----------------------------------------------------- -int getlogincount(void) { - WFIFOHEAD(login_fd,2); - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir le nombre de joueurs en jeu." RETCODE); - } else { - ladmin_log("Request to login-server to obtain the # of online players." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7938; - WFIFOSET(login_fd,2); - bytes_to_read = 1; - - return 0; -} - -//---------------------------------------------------------- -// Sub-function: Asking to modify the GM level of an account -//---------------------------------------------------------- -int changegmlevel(char* param) { - char name[1023]; - int GM_level; - WFIFOHEAD(login_fd,27); - - memset(name, '\0', sizeof(name)); - GM_level = 0; - - if (sscanf(param, "\"%[^\"]\" %d", name, &GM_level) < 1 && - sscanf(param, "'%[^']' %d", name, &GM_level) < 1 && - sscanf(param, "%s %d", name, &GM_level) < 1) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte et un niveau de GM svp.\n"); - printf(" gm nomtest 80\n"); - ladmin_log("Nombre incorrect de param鑼res pour changer le Niveau de GM d'un compte (commande 'gm')." RETCODE); - } else { - printf("Please input an account name and a GM level.\n"); - printf(" gm testname 80\n"); - ladmin_log("Incomplete parameters to change the GM level of an account ('gm' command)." RETCODE); - } - return 136; - } - - if (verify_accountname(name) == 0) { - return 102; - } - - if (GM_level < 0 || GM_level > 99) { - if (defaultlanguage == 'F') { - printf("Niveau de GM incorrect [%d]. Entrez une valeur de 0 99 svp.\n", GM_level); - ladmin_log("Niveau de GM incorrect [%d]. La valeur peut 黎re de 0 99." RETCODE, GM_level); - } else { - printf("Illegal GM level [%d]. Please input a value from 0 to 99.\n", GM_level); - ladmin_log("Illegal GM level [%d]. The value can be from 0 to 99." RETCODE, GM_level); - } - return 103; - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer un niveau de GM." RETCODE); - } else { - ladmin_log("Request to login-server to change a GM level." RETCODE); - } - - WFIFOW(login_fd,0) = 0x793e; - memcpy(WFIFOP(login_fd,2), name, 24); - WFIFOB(login_fd,26) = GM_level; - WFIFOSET(login_fd,27); - bytes_to_read = 1; - - return 0; -} - -//--------------------------------------------- -// Sub-function: Asking to obtain an account id -//--------------------------------------------- -int idaccount(char* param) { - char name[1023]; - WFIFOHEAD(login_fd,26); - - memset(name, '\0', sizeof(name)); - - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || - strlen(name) == 0) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte svp.\n"); - printf(" id nomtest\n"); - ladmin_log("Aucun nom donn pour rechecher l'id d'un compte (commande 'id')." RETCODE); - } else { - printf("Please input an account name.\n"); - printf(" id testname\n"); - ladmin_log("No name given to search an account id ('id' command)." RETCODE); - } - return 136; - } - - if (verify_accountname(name) == 0) { - return 102; - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour connatre l'id d'un compte." RETCODE); - } else { - ladmin_log("Request to login-server to know an account id." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7944; - memcpy(WFIFOP(login_fd,2), name, 24); - WFIFOSET(login_fd,26); - bytes_to_read = 1; - - return 0; -} - -//---------------------------------------------------------------------------- -// Sub-function: Asking to displaying information about an account (by its id) -//---------------------------------------------------------------------------- -int infoaccount(int account_id) { - WFIFOHEAD(login_fd,6); - if (account_id < 0) { - if (defaultlanguage == 'F') { - printf("Entrez un id ayant une valeur positive svp.\n"); - ladmin_log("Une valeur n馮ative a 騁 donn pour trouver le compte." RETCODE); - } else { - printf("Please input a positive value for the id.\n"); - ladmin_log("Negative value was given to found the account." RETCODE); - } - return 136; - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir le information d'un compte (par l'id)." RETCODE); - } else { - ladmin_log("Request to login-server to obtain information about an account (by its id)." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7954; - WFIFOL(login_fd,2) = account_id; - WFIFOSET(login_fd,6); - bytes_to_read = 1; - - return 0; -} - -//--------------------------------------- -// Sub-function: Send a broadcast message -//--------------------------------------- -int sendbroadcast(short type, char* message) { - int len = strlen(message); - WFIFOHEAD(login_fd,9+len); - if (len == 0) { - if (defaultlanguage == 'F') { - printf("Entrez un message svp.\n"); - if (type == 0) { - printf(" kami un message\n"); - } else { - printf(" kamib un message\n"); - } - ladmin_log("Le message est vide (commande 'kami(b)')." RETCODE); - } else { - printf("Please input a message.\n"); - if (type == 0) { - printf(" kami a message\n"); - } else { - printf(" kamib a message\n"); - } - ladmin_log("The message is void ('kami(b)' command)." RETCODE); - } - return 136; - } - len++; //+'\0' - WFIFOW(login_fd,0) = 0x794e; - WFIFOW(login_fd,2) = type; - WFIFOL(login_fd,4) = len; - memcpy(WFIFOP(login_fd,8), message, len); - WFIFOSET(login_fd,8+len); - bytes_to_read = 1; - - return 0; -} - -//-------------------------------------------- -// Sub-function: Change language of displaying -//-------------------------------------------- -int changelanguage(char* language) { - if (strlen(language) == 0) { - if (defaultlanguage == 'F') { - printf("Entrez une langue svp.\n"); - printf(" language english\n"); - printf(" language fran軋is\n"); - ladmin_log("La langue est vide (commande 'language')." RETCODE); - } else { - printf("Please input a language.\n"); - printf(" language english\n"); - printf(" language fran軋is\n"); - ladmin_log("The language is void ('language' command)." RETCODE); - } - return 136; - } - - language[0] = toupper(language[0]); - if (language[0] == 'F' || language[0] == 'E') { - defaultlanguage = language[0]; - if (defaultlanguage == 'F') { - printf("Changement de la langue d'affichage en Fran軋is.\n"); - ladmin_log("Changement de la langue d'affichage en Fran軋is." RETCODE); - } else { - printf("Displaying language changed to English.\n"); - ladmin_log("Displaying language changed to English." RETCODE); - } - } else { - if (defaultlanguage == 'F') { - printf("Langue non param騁r馥 (langues possibles: 'Fran軋is' ou 'English').\n"); - ladmin_log("Langue non param騁r馥 (Fran軋is ou English n馗essaire)." RETCODE); - } else { - printf("Undefined language (possible languages: Fran軋is or English).\n"); - ladmin_log("Undefined language (must be Fran軋is or English)." RETCODE); - } - } - - return 0; -} - -//-------------------------------------------------------- -// Sub-function: Asking to Displaying of the accounts list -//-------------------------------------------------------- -int listaccount(char* param, int type) { -//int list_first, list_last, list_type; // parameter to display a list of accounts - int i; - WFIFOHEAD(login_fd,10); - - list_type = type; - - // set default values - list_first = 0; - list_last = 0; - - if (list_type == 1) { // if listgm - // get all accounts = use default - } else if (list_type == 2) { // if search - for (i = 0; param[i]; i++) - param[i] = tolower(param[i]); - // get all accounts = use default - } else if (list_type == 3) { // if listban - // get all accounts = use default - } else if (list_type == 4) { // if listok - // get all accounts = use default - } else { // if list (list_type == 0) - switch(sscanf(param, "%d %d", &list_first, &list_last)) { - case 0: - // get all accounts = use default - break; - case 1: - list_last = 0; - // use tests of the following value - default: - if (list_first < 0) - list_first = 0; - if (list_last < list_first || list_last < 0) - list_last = 0; - break; - } - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir la liste des comptes de %d %d." RETCODE, list_first, list_last); - } else { - ladmin_log("Request to login-server to obtain the list of accounts from %d to %d." RETCODE, list_first, list_last); - } - - WFIFOW(login_fd,0) = 0x7920; - WFIFOL(login_fd,2) = list_first; - WFIFOL(login_fd,6) = list_last; - WFIFOSET(login_fd,10); - bytes_to_read = 1; - - // 0123456789 01 01234567890123456789012301234 012345 0123456789012345678901234567 - if (defaultlanguage == 'F') { - printf(" id_compte GM nom_utilisateur sexe count statut\n"); - } else { - printf("account_id GM user_name sex count state\n"); - } - printf("-------------------------------------------------------------------------------\n"); - list_count = 0; - - return 0; -} - -//-------------------------------------------- -// Sub-function: Asking to modify a memo field -//-------------------------------------------- -int changememo(char* param) { - char name[1023], memo[1023]; - WFIFOHEAD(login_fd,28+255); - - memset(name, '\0', sizeof(name)); - memset(memo, '\0', sizeof(memo)); - - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, memo) < 1 && // memo can be void - sscanf(param, "'%[^']' %[^\r\n]", name, memo) < 1 && // memo can be void - sscanf(param, "%s %[^\r\n]", name, memo) < 1) { // memo can be void - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte et un m駑o svp.\n"); - printf(" memo nomtest nouveau memo\n"); - ladmin_log("Nombre incorrect de param鑼res pour changer le m駑o d'un compte (commande 'email')." RETCODE); - } else { - printf("Please input an account name and a memo.\n"); - printf(" memo testname new memo\n"); - ladmin_log("Incomplete parameters to change the memo of an account ('email' command)." RETCODE); - } - return 136; - } - - if (verify_accountname(name) == 0) { - return 102; - } - - if (strlen(memo) > 254) { - if (defaultlanguage == 'F') { - printf("M駑o trop long (%d caract鑽es).\n", strlen(memo)); - printf("Entrez un m駑o de 254 caract鑽es maximum svp.\n"); - ladmin_log("M駑o trop long (%d caract鑽es). Entrez un m駑o de 254 caract鑽es maximum svp." RETCODE, strlen(memo)); - } else { - printf("Memo is too long (%d characters).\n", strlen(memo)); - printf("Please input a memo of 254 bytes at the maximum.\n"); - ladmin_log("Email is too long (%d characters). Please input a memo of 254 bytes at the maximum." RETCODE, strlen(memo)); - } - return 102; - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer un m駑o." RETCODE); - } else { - ladmin_log("Request to login-server to change a memo." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7942; - memcpy(WFIFOP(login_fd,2), name, 24); - WFIFOW(login_fd,26) = strlen(memo); - if (strlen(memo) > 0) - memcpy(WFIFOP(login_fd,28), memo, strlen(memo)); - WFIFOSET(login_fd,28+strlen(memo)); - bytes_to_read = 1; - - return 0; -} - -//----------------------------------------------- -// Sub-function: Asking to obtain an account name -//----------------------------------------------- -int nameaccount(int id) { - WFIFOHEAD(login_fd,6); - if (id < 0) { - if (defaultlanguage == 'F') { - printf("Entrez un id ayant une valeur positive svp.\n"); - ladmin_log("Id n馮atif donn pour rechecher le nom d'un compte (commande 'name')." RETCODE); - } else { - printf("Please input a positive value for the id.\n"); - ladmin_log("Negativ id given to search an account name ('name' command)." RETCODE); - } - return 136; - } - - if (defaultlanguage == 'F') - ladmin_log("Envoi d'un requ黎e au serveur de logins pour connatre le nom d'un compte." RETCODE); - else - ladmin_log("Request to login-server to know an account name." RETCODE); - - WFIFOW(login_fd,0) = 0x7946; - WFIFOL(login_fd,2) = id; - WFIFOSET(login_fd,6); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------ -// Sub-function: Asking to modify a password -// (Note: never send back a password with login-server!! security of passwords) -//------------------------------------------ -int changepasswd(char* param) { - char name[1023], password[1023]; - WFIFOHEAD(login_fd,50); - - memset(name, '\0', sizeof(name)); - memset(password, '\0', sizeof(password)); - - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, password) < 1 && - sscanf(param, "'%[^']' %[^\r\n]", name, password) < 1 && - sscanf(param, "%s %[^\r\n]", name, password) < 1) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte svp.\n"); - printf(" passwd nomtest nouveaumotdepasse\n"); - ladmin_log("Nombre incorrect de param鑼res pour changer le mot d'un passe d'un compte (commande 'password')." RETCODE); - } else { - printf("Please input an account name.\n"); - printf(" passwd testname newpassword\n"); - ladmin_log("Incomplete parameters to change the password of an account ('password' command)." RETCODE); - } - return 136; - } - - if (verify_accountname(name) == 0) { - return 102; - } - - if (strlen(password) == 0) { - if (typepasswd(password) == 0) - return 134; - } - if (verify_password(password) == 0) - return 131; - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer un mot de passe." RETCODE); - } else { - ladmin_log("Request to login-server to change a password." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7934; - memcpy(WFIFOP(login_fd,2), name, 24); - memcpy(WFIFOP(login_fd,26), password, 24); - WFIFOSET(login_fd,50); - bytes_to_read = 1; - - return 0; -} - -//---------------------------------------------------------------------- -// Sub-function: Request to login-server to reload GM configuration file -// this function have no answer -//---------------------------------------------------------------------- -int reloadGM(void) { - WFIFOHEAD(login_fd,2); - WFIFOW(login_fd,0) = 0x7955; - WFIFOSET(login_fd,2); - bytes_to_read = 0; - - if (defaultlanguage == 'F') { - ladmin_log("Demande de recharger le fichier de configuration des GM envoy馥." RETCODE); - printf("Demande de recharger le fichier de configuration des GM envoy馥.\n"); - printf("V駻ifiez les comptes GM actuels (apr鑚 rechargement):\n"); - } else { - ladmin_log("Request to reload the GM configuration file sended." RETCODE); - printf("Request to reload the GM configuration file sended.\n"); - printf("Check the actual GM accounts (after reloading):\n"); - } - listaccount(parameters, 1); // 1: to list only GM - - return 180; -} - -//----------------------------------------------------- -// Sub-function: Asking to modify the sex of an account -//----------------------------------------------------- -int changesex(char* param) { - char name[1023], sex[1023]; - WFIFOHEAD(login_fd,27); - - memset(name, '\0', sizeof(name)); - memset(sex, '\0', sizeof(sex)); - - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, sex) < 2 && - sscanf(param, "'%[^']' %[^\r\n]", name, sex) < 2 && - sscanf(param, "%s %[^\r\n]", name, sex) < 2) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte et un sexe svp.\n"); - printf(" sex nomtest Male\n"); - ladmin_log("Nombre incorrect de param鑼res pour changer le sexe d'un compte (commande 'sex')." RETCODE); - } else { - printf("Please input an account name and a sex.\n"); - printf(" sex testname Male\n"); - ladmin_log("Incomplete parameters to change the sex of an account ('sex' command)." RETCODE); - } - return 136; - } - - if (verify_accountname(name) == 0) { - return 102; - } - - sex[0] = toupper(sex[0]); - if (strchr("MF", sex[0]) == NULL) { - if (defaultlanguage == 'F') { - printf("Sexe incorrect [%s]. Entrez M ou F svp.\n", sex); - ladmin_log("Sexe incorrect [%s]. Entrez M ou F svp." RETCODE, sex); - } else { - printf("Illegal gender [%s]. Please input M or F.\n", sex); - ladmin_log("Illegal gender [%s]. Please input M or F." RETCODE, sex); - } - return 103; - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer un sexe." RETCODE); - } else { - ladmin_log("Request to login-server to change a sex." RETCODE); - } - - WFIFOW(login_fd,0) = 0x793c; - memcpy(WFIFOP(login_fd,2), name, 24); - WFIFOB(login_fd,26) = sex[0]; - WFIFOSET(login_fd,27); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------------------------------------- -// Sub-function of sub-function changestate, blockaccount or unblockaccount -// Asking to modify the state of an account -//------------------------------------------------------------------------- -int changestatesub(char* name, int state, char* error_message7) { - char error_message[1023]; // need to use, because we can modify error_message7 - WFIFOHEAD(login_fd,50); - - memset(error_message, '\0', sizeof(error_message)); - strncpy(error_message, error_message7, sizeof(error_message)-1); - - if ((state < 0 || state > 9) && state != 100) { // Valid values: 0: ok, or value of the 0x006a packet + 1 - if (defaultlanguage == 'F') { - printf("Entrez une des statuts suivantes svp:\n"); - printf(" 0 = Compte ok 6 = Your Game's EXE file is not the latest version\n"); - } else { - printf("Please input one of these states:\n"); - printf(" 0 = Account ok 6 = Your Game's EXE file is not the latest version\n"); - } - printf(" 1 = Unregistered ID 7 = You are Prohibited to log in until + message\n"); - printf(" 2 = Incorrect Password 8 = Server is jammed due to over populated\n"); - printf(" 3 = This ID is expired 9 = No MSG\n"); - printf(" 4 = Rejected from Server 100 = This ID has been totally erased\n"); - printf(" 5 = You have been blocked by the GM Team\n"); - if (defaultlanguage == 'F') { - printf(" state nomtest 5\n"); - printf(" state nomtest 7 fin de votre ban\n"); - printf(" block \n"); - printf(" unblock \n"); - ladmin_log("Valeur incorrecte pour le statut d'un compte (commande 'state', 'block' ou 'unblock')." RETCODE); - } else { - printf(" state testname 5\n"); - printf(" state testname 7 end of your ban\n"); - printf(" block \n"); - printf(" unblock \n"); - ladmin_log("Invalid value for the state of an account ('state', 'block' or 'unblock' command)." RETCODE); - } - return 151; - } - - if (verify_accountname(name) == 0) { - return 102; - } - - if (state != 7) { - strcpy(error_message, "-"); - } else { - if (strlen(error_message) < 1) { - if (defaultlanguage == 'F') { - printf("Message d'erreur trop court. Entrez un message de 1-19 caract鑽es.\n"); - ladmin_log("Message d'erreur trop court. Entrez un message de 1-19 caract鑽es." RETCODE); - } else { - printf("Error message is too short. Please input a message of 1-19 bytes.\n"); - ladmin_log("Error message is too short. Please input a message of 1-19 bytes." RETCODE); - } - return 102; - } - if (strlen(error_message) > 19) { - if (defaultlanguage == 'F') { - printf("Message d'erreur trop long. Entrez un message de 1-19 caract鑽es.\n"); - ladmin_log("Message d'erreur trop long. Entrez un message de 1-19 caract鑽es." RETCODE); - } else { - printf("Error message is too long. Please input a message of 1-19 bytes.\n"); - ladmin_log("Error message is too long. Please input a message of 1-19 bytes." RETCODE); - } - return 102; - } - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer un statut." RETCODE); - } else { - ladmin_log("Request to login-server to change a state." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7936; - memcpy(WFIFOP(login_fd,2), name, 24); - WFIFOL(login_fd,26) = state; - memcpy(WFIFOP(login_fd,30), error_message, 20); - WFIFOSET(login_fd,50); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------------------- -// Sub-function: Asking to modify the state of an account -//------------------------------------------------------- -int changestate(char* param) { - char name[1023], error_message[1023]; - int state; - - memset(name, '\0', sizeof(name)); - memset(error_message, '\0', sizeof(error_message)); - - if (sscanf(param, "\"%[^\"]\" %d %[^\r\n]", name, &state, error_message) < 2 && - sscanf(param, "'%[^']' %d %[^\r\n]", name, &state, error_message) < 2 && - sscanf(param, "%s %d %[^\r\n]", name, &state, error_message) < 2) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte et un statut svp.\n"); - printf(" state nomtest 5\n"); - printf(" state nomtest 7 fin de votre ban\n"); - printf(" block \n"); - printf(" unblock \n"); - ladmin_log("Nombre incorrect de param鑼res pour changer le statut d'un compte (commande 'state')." RETCODE); - } else { - printf("Please input an account name and a state.\n"); - printf(" state testname 5\n"); - printf(" state testname 7 end of your ban\n"); - printf(" block \n"); - printf(" unblock \n"); - ladmin_log("Incomplete parameters to change the state of an account ('state' command)." RETCODE); - } - return 136; - } - - return changestatesub(name, state, error_message); -} - -//------------------------------------------- -// Sub-function: Asking to unblock an account -//------------------------------------------- -int unblockaccount(char* param) { - char name[1023]; - - memset(name, '\0', sizeof(name)); - - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || - strlen(name) == 0) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte svp.\n"); - printf(" state nomtest 5\n"); - printf(" state nomtest 7 fin de votre ban\n"); - printf(" block \n"); - printf(" unblock \n"); - ladmin_log("Nombre incorrect de param鑼res pour changer le statut d'un compte (commande 'unblock')." RETCODE); - } else { - printf("Please input an account name.\n"); - printf(" state testname 5\n"); - printf(" state testname 7 end of your ban\n"); - printf(" block \n"); - printf(" unblock \n"); - ladmin_log("Incomplete parameters to change the state of an account ('unblock' command)." RETCODE); - } - return 136; - } - - return changestatesub(name, 0, "-"); // state 0, no error message -} - -//------------------------------------------- -// Sub-function: Asking to unblock an account -//------------------------------------------- -int blockaccount(char* param) { - char name[1023]; - - memset(name, '\0', sizeof(name)); - - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || - strlen(name) == 0) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte svp.\n"); - printf(" state nomtest 5\n"); - printf(" state nomtest 7 fin de votre ban\n"); - printf(" block \n"); - printf(" unblock \n"); - ladmin_log("Nombre incorrect de param鑼res pour changer le statut d'un compte (commande 'block')." RETCODE); - } else { - printf("Please input an account name.\n"); - printf(" state testname 5\n"); - printf(" state testname 7 end of your ban\n"); - printf(" block \n"); - printf(" unblock \n"); - ladmin_log("Incomplete parameters to change the state of an account ('block' command)." RETCODE); - } - return 136; - } - - return changestatesub(name, 5, "-"); // state 5, no error message -} - -//--------------------------------------------------------------------- -// Sub-function: Add/substract time to the validity limit of an account -//--------------------------------------------------------------------- -int timeaddaccount(char* param) { - char name[1023], modif[1023]; - int year, month, day, hour, minute, second; - char * p_modif; - int value, i; - WFIFOHEAD(login_fd,38); - - memset(name, '\0', sizeof(name)); - memset(modif, '\0', sizeof(modif)); - year = month = day = hour = minute = second = 0; - - if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, modif) < 2 && - sscanf(param, "'%[^']' %[^\r\n]", name, modif) < 2 && - sscanf(param, "%s %[^\r\n]", name, modif) < 2) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte et un modificateur svp.\n"); - printf(" timeadd nomtest +1m-2mn1s-6y\n"); - printf(" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); - printf(" et 6 ans dans le m麥e temps.\n"); - ladmin_log("Nombre incorrect de param鑼res pour modifier une date limite d'utilisation (commande 'timeadd')." RETCODE); - } else { - printf("Please input an account name and a modifier.\n"); - printf(" : timeadd testname +1m-2mn1s-6y\n"); - printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf(" and 6 years at the same time.\n"); - ladmin_log("Incomplete parameters to modify a limit time ('timeadd' command)." RETCODE); - } - return 136; - } - if (verify_accountname(name) == 0) { - return 102; - } - - // lowercase for modif - for (i = 0; modif[i]; i++) - modif[i] = tolower(modif[i]); - p_modif = modif; - while (strlen(p_modif) > 0) { - value = atoi(p_modif); - if (value == 0) { - p_modif++; - } else { - if (p_modif[0] == '-' || p_modif[0] == '+') - p_modif++; - while (strlen(p_modif) > 0 && p_modif[0] >= '0' && p_modif[0] <= '9') { - p_modif++; - } - if (p_modif[0] == 's') { - second = value; - p_modif++; - } else if (p_modif[0] == 'm' && p_modif[1] == 'n') { - minute = value; - p_modif += 2; - } else if (p_modif[0] == 'h') { - hour = value; - p_modif++; - } else if (p_modif[0] == 'd' || p_modif[0] == 'j') { - day = value; - p_modif += 2; - } else if (p_modif[0] == 'm') { - month = value; - p_modif++; - } else if (p_modif[0] == 'y' || p_modif[0] == 'a') { - year = value; - p_modif++; - } else { - p_modif++; - } - } - } - - if (defaultlanguage == 'F') { - printf(" ann馥: %d\n", year); - printf(" mois: %d\n", month); - printf(" jour: %d\n", day); - printf(" heure: %d\n", hour); - printf(" minute: %d\n", minute); - printf(" seconde: %d\n", second); - } else { - printf(" year: %d\n", year); - printf(" month: %d\n", month); - printf(" day: %d\n", day); - printf(" hour: %d\n", hour); - printf(" minute: %d\n", minute); - printf(" second: %d\n", second); - } - - if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0) { - if (defaultlanguage == 'F') { - printf("Vous devez entrer un ajustement avec cette commande, svp:\n"); - printf(" Valeur d'ajustement (-1, 1, +1, etc...)\n"); - printf(" El駑ent modifi:\n"); - printf(" a ou y: ann馥\n"); - printf(" m: mois\n"); - printf(" j ou d: jour\n"); - printf(" h: heure\n"); - printf(" mn: minute\n"); - printf(" s: seconde\n"); - printf(" timeadd nomtest +1m-2mn1s-6y\n"); - printf(" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); - printf(" et 6 ans dans le m麥e temps.\n"); - ladmin_log("Aucun ajustement n'est pas un ajustement (commande 'timeadd')." RETCODE); - } else { - printf("Please give an adjustment with this command:\n"); - printf(" Adjustment value (-1, 1, +1, etc...)\n"); - printf(" Modified element:\n"); - printf(" a or y: year\n"); - printf(" m: month\n"); - printf(" j or d: day\n"); - printf(" h: hour\n"); - printf(" mn: minute\n"); - printf(" s: second\n"); - printf(" timeadd testname +1m-2mn1s-6y\n"); - printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); - printf(" and 6 years at the same time.\n"); - ladmin_log("No adjustment isn't an adjustment ('timeadd' command)." RETCODE); - } - return 137; - } - if (year > 127 || year < -127) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement d'ann馥s correct (de -127 127), svp.\n"); - ladmin_log("Ajustement de l'ann馥 hors norme ('timeadd' command)." RETCODE); - } else { - printf("Please give a correct adjustment for the years (from -127 to 127).\n"); - ladmin_log("Abnormal adjustement for the year ('timeadd' command)." RETCODE); - } - return 137; - } - if (month > 255 || month < -255) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement de mois correct (de -255 255), svp.\n"); - ladmin_log("Ajustement du mois hors norme ('timeadd' command)." RETCODE); - } else { - printf("Please give a correct adjustment for the months (from -255 to 255).\n"); - ladmin_log("Abnormal adjustement for the month ('timeadd' command)." RETCODE); - } - return 137; - } - if (day > 32767 || day < -32767) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement de jours correct (de -32767 32767), svp.\n"); - ladmin_log("Ajustement des jours hors norme ('timeadd' command)." RETCODE); - } else { - printf("Please give a correct adjustment for the days (from -32767 to 32767).\n"); - ladmin_log("Abnormal adjustement for the days ('timeadd' command)." RETCODE); - } - return 137; - } - if (hour > 32767 || hour < -32767) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement d'heures correct (de -32767 32767), svp.\n"); - ladmin_log("Ajustement des heures hors norme ('timeadd' command)." RETCODE); - } else { - printf("Please give a correct adjustment for the hours (from -32767 to 32767).\n"); - ladmin_log("Abnormal adjustement for the hours ('timeadd' command)." RETCODE); - } - return 137; - } - if (minute > 32767 || minute < -32767) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement de minutes correct (de -32767 32767), svp.\n"); - ladmin_log("Ajustement des minutes hors norme ('timeadd' command)." RETCODE); - } else { - printf("Please give a correct adjustment for the minutes (from -32767 to 32767).\n"); - ladmin_log("Abnormal adjustement for the minutes ('timeadd' command)." RETCODE); - } - return 137; - } - if (second > 32767 || second < -32767) { - if (defaultlanguage == 'F') { - printf("Entrez un ajustement de secondes correct (de -32767 32767), svp.\n"); - ladmin_log("Ajustement des secondes hors norme ('timeadd' command)." RETCODE); - } else { - printf("Please give a correct adjustment for the seconds (from -32767 to 32767).\n"); - ladmin_log("Abnormal adjustement for the seconds ('timeadd' command)." RETCODE); - } - return 137; - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour modifier une date limite d'utilisation." RETCODE); - } else { - ladmin_log("Request to login-server to modify a time limit." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7950; - memcpy(WFIFOP(login_fd,2), name, 24); - WFIFOW(login_fd,26) = (short)year; - WFIFOW(login_fd,28) = (short)month; - WFIFOW(login_fd,30) = (short)day; - WFIFOW(login_fd,32) = (short)hour; - WFIFOW(login_fd,34) = (short)minute; - WFIFOW(login_fd,36) = (short)second; - WFIFOSET(login_fd,38); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------------- -// Sub-function: Set a validity limit of an account -//------------------------------------------------- -int timesetaccount(char* param) { - char name[1023], date[1023], time[1023]; - int year, month, day, hour, minute, second; - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - struct tm *tmtime; - WFIFOHEAD(login_fd,30); - - memset(name, '\0', sizeof(name)); - memset(date, '\0', sizeof(date)); - memset(time, '\0', sizeof(time)); - year = month = day = hour = minute = second = 0; - connect_until_time = 0; - tmtime = localtime(&connect_until_time); // initialize - - if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void - sscanf(param, "'%[^']' %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void - sscanf(param, "%s %s %[^\r\n]", name, date, time) < 2) { // if date = 0, time can be void - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte, une date et une heure svp.\n"); - printf(": timeset aaaa/mm/jj [hh:mm:ss]\n"); - printf(" timeset 0 (0 = illimit)\n"); - printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); - ladmin_log("Nombre incorrect de param鑼res pour fixer une date limite d'utilisation (commande 'timeset')." RETCODE); - } else { - printf("Please input an account name, a date and a hour.\n"); - printf(": timeset yyyy/mm/dd [hh:mm:ss]\n"); - printf(" timeset 0 (0 = unlimited)\n"); - printf(" Default time [hh:mm:ss]: 23:59:59.\n"); - ladmin_log("Incomplete parameters to set a limit time ('timeset' command)." RETCODE); - } - return 136; - } - if (verify_accountname(name) == 0) { - return 102; - } - - if (time[0] == '\0') - strcpy(time, "23:59:59"); - - if (atoi(date) != 0 && - ((sscanf(date, "%d/%d/%d", &year, &month, &day) < 3 && - sscanf(date, "%d-%d-%d", &year, &month, &day) < 3 && - sscanf(date, "%d.%d.%d", &year, &month, &day) < 3 && - sscanf(date, "%d'%d'%d", &year, &month, &day) < 3) || - sscanf(time, "%d:%d:%d", &hour, &minute, &second) < 3)) { - if (defaultlanguage == 'F') { - printf("Entrez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n"); - ladmin_log("Format incorrect pour la date/heure ('timeset' command)." RETCODE); - } else { - printf("Please input 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"); - ladmin_log("Invalid format for the date/time ('timeset' command)." RETCODE); - } - return 102; - } - - if (atoi(date) == 0) { - connect_until_time = 0; - } else { - if (year < 70) { - year = year + 100; - } - if (year >= 1900) { - year = year - 1900; - } - if (month < 1 || month > 12) { - if (defaultlanguage == 'F') { - printf("Entrez un mois correct svp (entre 1 et 12).\n"); - ladmin_log("Mois incorrect pour la date ('timeset' command)." RETCODE); - } else { - printf("Please give a correct value for the month (from 1 to 12).\n"); - ladmin_log("Invalid month for the date ('timeset' command)." RETCODE); - } - return 102; - } - month = month - 1; - if (day < 1 || day > 31) { - if (defaultlanguage == 'F') { - printf("Entrez un jour correct svp (entre 1 et 31).\n"); - ladmin_log("Jour incorrect pour la date ('timeset' command)." RETCODE); - } else { - printf("Please give a correct value for the day (from 1 to 31).\n"); - ladmin_log("Invalid day for the date ('timeset' command)." RETCODE); - } - return 102; - } - if (((month == 3 || month == 5 || month == 8 || month == 10) && day > 30) || - (month == 1 && day > 29)) { - if (defaultlanguage == 'F') { - printf("Entrez un jour correct en fonction du mois (%d) svp.\n", month); - ladmin_log("Jour incorrect pour ce mois correspondant ('timeset' command)." RETCODE); - } else { - printf("Please give a correct value for a day of this month (%d).\n", month); - ladmin_log("Invalid day for this month ('timeset' command)." RETCODE); - } - return 102; - } - if (hour < 0 || hour > 23) { - if (defaultlanguage == 'F') { - printf("Entrez une heure correcte svp (entre 0 et 23).\n"); - ladmin_log("Heure incorrecte pour l'heure ('timeset' command)." RETCODE); - } else { - printf("Please give a correct value for the hour (from 0 to 23).\n"); - ladmin_log("Invalid hour for the time ('timeset' command)." RETCODE); - } - return 102; - } - if (minute < 0 || minute > 59) { - if (defaultlanguage == 'F') { - printf("Entrez des minutes correctes svp (entre 0 et 59).\n"); - ladmin_log("Minute incorrecte pour l'heure ('timeset' command)." RETCODE); - } else { - printf("Please give a correct value for the minutes (from 0 to 59).\n"); - ladmin_log("Invalid minute for the time ('timeset' command)." RETCODE); - } - return 102; - } - if (second < 0 || second > 59) { - if (defaultlanguage == 'F') { - printf("Entrez des secondes correctes svp (entre 0 et 59).\n"); - ladmin_log("Seconde incorrecte pour l'heure ('timeset' command)." RETCODE); - } else { - printf("Please give a correct value for the seconds (from 0 to 59).\n"); - ladmin_log("Invalid second for the time ('timeset' command)." RETCODE); - } - return 102; - } - tmtime->tm_year = year; - tmtime->tm_mon = month; - tmtime->tm_mday = day; - tmtime->tm_hour = hour; - tmtime->tm_min = minute; - tmtime->tm_sec = second; - tmtime->tm_isdst = -1; // -1: no winter/summer time modification - connect_until_time = mktime(tmtime); - if (connect_until_time == -1) { - if (defaultlanguage == 'F') { - printf("Date incorrecte.\n"); - printf("Ajoutez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n"); - ladmin_log("Date incorrecte. ('timeset' command)." RETCODE); - } else { - printf("Invalid date.\n"); - printf("Please add 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"); - ladmin_log("Invalid date. ('timeset' command)." RETCODE); - } - return 102; - } - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour fixer une date limite d'utilisation." RETCODE); - } else { - ladmin_log("Request to login-server to set a time limit." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7948; - memcpy(WFIFOP(login_fd,2), name, 24); - WFIFOL(login_fd,26) = (int)connect_until_time; - WFIFOSET(login_fd,30); - bytes_to_read = 1; - - return 0; -} - -//------------------------------------------------------------------------------ -// Sub-function: Asking to displaying information about an account (by its name) -//------------------------------------------------------------------------------ -int whoaccount(char* param) { - char name[1023]; - WFIFOHEAD(login_fd,26); - - memset(name, '\0', sizeof(name)); - - if (strlen(param) == 0 || - (sscanf(param, "\"%[^\"]\"", name) < 1 && - sscanf(param, "'%[^']'", name) < 1 && - sscanf(param, "%[^\r\n]", name) < 1) || - strlen(name) == 0) { - if (defaultlanguage == 'F') { - printf("Entrez un nom de compte svp.\n"); - printf(" who nomtest\n"); - ladmin_log("Aucun nom n'a 騁 donn pour trouver le compte." RETCODE); - } else { - printf("Please input an account name.\n"); - printf(" who testname\n"); - ladmin_log("No name was given to found the account." RETCODE); - } - return 136; - } - if (verify_accountname(name) == 0) { - return 102; - } - - if (defaultlanguage == 'F') { - ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir le information d'un compte (par le nom)." RETCODE); - } else { - ladmin_log("Request to login-server to obtain information about an account (by its name)." RETCODE); - } - - WFIFOW(login_fd,0) = 0x7952; - memcpy(WFIFOP(login_fd,2), name, 24); - WFIFOSET(login_fd,26); - bytes_to_read = 1; - - return 0; -} - -//-------------------------------------------------------- -// Sub-function: Asking of the version of the login-server -//-------------------------------------------------------- -int checkloginversion(void) { - WFIFOHEAD(login_fd,2); - if (defaultlanguage == 'F') - ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir sa version." RETCODE); - else - ladmin_log("Request to login-server to obtain its version." RETCODE); - - WFIFOW(login_fd,0) = 0x7530; - WFIFOSET(login_fd,2); - bytes_to_read = 1; - - return 0; -} - -//--------------------------------------------- -// Prompt function -// this function wait until user type a command -// and analyse the command. -//--------------------------------------------- -int prompt(void) { - int i, j; - char buf[1024]; - char *p; - - // while we don't wait new packets - while (bytes_to_read == 0) { - // for help with the console colors look here: - // http://www.edoceo.com/liberum/?doc=printf-with-color - // some code explanation (used here): - // \033[2J : clear screen and go up/left (0, 0 position) - // \033[K : clear line from actual position to end of the line - // \033[0m : reset color parameter - // \033[1m : use bold for font - printf("\n"); - if (defaultlanguage == 'F') - printf("\033[32mPour afficher les commandes, tapez 'Entr馥'.\033[0m\n"); - else - printf("\033[32mTo list the commands, type 'enter'.\033[0m\n"); - printf("\033[0;36mLadmin-> \033[0m"); - printf("\033[1m"); - fflush(stdout); - - // get command and parameter - memset(buf, '\0', sizeof(buf)); - fflush(stdin); - fgets(buf, 1023, stdin); - buf[1023] = '\0'; - - printf("\033[0m"); - fflush(stdout); - - // remove final \n - if((p = strrchr(buf, '\n')) != NULL) - p[0] = '\0'; - // remove all control char - for (i = 0; buf[i]; i++) - if (buf[i] < 32) { - // remove cursor control. - if (buf[i] == 27 && buf[i+1] == '[' && - (buf[i+2] == 'H' || // home position (cursor) - buf[i+2] == 'J' || // clear screen - buf[i+2] == 'A' || // up 1 line - buf[i+2] == 'B' || // down 1 line - buf[i+2] == 'C' || // right 1 position - buf[i+2] == 'D' || // left 1 position - buf[i+2] == 'G')) { // center cursor (windows) - for (j = i; buf[j]; j++) - buf[j] = buf[j+3]; - } else if (buf[i] == 27 && buf[i+1] == '[' && buf[i+2] == '2' && buf[i+3] == 'J') { // clear screen - for (j = i; buf[j]; j++) - buf[j] = buf[j+4]; - } else if (buf[i] == 27 && buf[i+1] == '[' && buf[i+3] == '~' && - (buf[i+2] == '1' || // home (windows) - buf[i+2] == '2' || // insert (windows) - buf[i+2] == '3' || // del (windows) - buf[i+2] == '4' || // end (windows) - buf[i+2] == '5' || // pgup (windows) - buf[i+2] == '6')) { // pgdown (windows) - for (j = i; buf[j]; j++) - buf[j] = buf[j+4]; - } else { - // remove other control char. - for (j = i; buf[j]; j++) - buf[j] = buf[j+1]; - } - i--; - } - - // extract command name and parameters - memset(command, '\0', sizeof(command)); - memset(parameters, '\0', sizeof(parameters)); - sscanf(buf, "%1023s %[^\n]", command, parameters); - command[1023] = '\0'; - parameters[1023] = '\0'; - - // lowercase for command line - for (i = 0; command[i]; i++) - command[i] = tolower(command[i]); - - if (command[0] == '?' || strlen(command) == 0) { - if (defaultlanguage == 'F') { - strcpy(buf, "aide"); - strcpy(command, "aide"); - } else { - strcpy(buf, "help"); - strcpy(command, "help"); - } - } - - // Analyse of the command - check_command(command); // give complete name to the command - - if (strlen(parameters) == 0) { - if (defaultlanguage == 'F') { - ladmin_log("Commande: '%s' (sans param鑼re)" RETCODE, command, parameters); - } else { - ladmin_log("Command: '%s' (without parameters)" RETCODE, command, parameters); - } - } else { - if (defaultlanguage == 'F') { - ladmin_log("Commande: '%s', param鑼res: '%s'" RETCODE, command, parameters); - } else { - ladmin_log("Command: '%s', parameters: '%s'" RETCODE, command, parameters); - } - } - - // Analyse of the command -// help - if (strcmp(command, "aide") == 0) { - display_help(parameters, 1); // 1: french - } else if (strcmp(command, "help") == 0 ) { - display_help(parameters, 0); // 0: english -// general commands - } else if (strcmp(command, "add") == 0) { - addaccount(parameters, 0); // 0: no email - } else if (strcmp(command, "ban") == 0) { - banaccount(parameters); - } else if (strcmp(command, "banadd") == 0) { - banaddaccount(parameters); - } else if (strcmp(command, "banset") == 0) { - bansetaccount(parameters); - } else if (strcmp(command, "block") == 0) { - blockaccount(parameters); - } else if (strcmp(command, "check") == 0) { - checkaccount(parameters); - } else if (strcmp(command, "create") == 0) { - addaccount(parameters, 1); // 1: with email - } else if (strcmp(command, "delete") == 0) { - delaccount(parameters); - } else if (strcmp(command, "email") == 0) { - changeemail(parameters); - } else if (strcmp(command, "getcount") == 0) { - getlogincount(); - } else if (strcmp(command, "gm") == 0) { - changegmlevel(parameters); - } else if (strcmp(command, "id") == 0) { - idaccount(parameters); - } else if (strcmp(command, "info") == 0) { - infoaccount(atoi(parameters)); - } else if (strcmp(command, "kami") == 0) { - sendbroadcast(0, parameters); // flag for normal - } else if (strcmp(command, "kamib") == 0) { - sendbroadcast(0x10, parameters); // flag for blue - } else if (strcmp(command, "language") == 0) { - changelanguage(parameters); - } else if (strcmp(command, "list") == 0) { - listaccount(parameters, 0); // 0: to list all - } else if (strcmp(command, "listban") == 0) { - listaccount(parameters, 3); // 3: to list only accounts with state or bannished - } else if (strcmp(command, "listgm") == 0) { - listaccount(parameters, 1); // 1: to list only GM - } else if (strcmp(command, "listok") == 0) { - listaccount(parameters, 4); // 4: to list only accounts without state and not bannished - } else if (strcmp(command, "memo") == 0) { - changememo(parameters); - } else if (strcmp(command, "name") == 0) { - nameaccount(atoi(parameters)); - } else if (strcmp(command, "password") == 0) { - changepasswd(parameters); - } else if (strcmp(command, "reloadgm") == 0) { - reloadGM(); - } else if (strcmp(command, "search") == 0) { // no regex in C version - listaccount(parameters, 2); // 2: to list with pattern - } else if (strcmp(command, "sex") == 0) { - changesex(parameters); - } else if (strcmp(command, "state") == 0) { - changestate(parameters); - } else if (strcmp(command, "timeadd") == 0) { - timeaddaccount(parameters); - } else if (strcmp(command, "timeset") == 0) { - timesetaccount(parameters); - } else if (strcmp(command, "unban") == 0) { - unbanaccount(parameters); - } else if (strcmp(command, "unblock") == 0) { - unblockaccount(parameters); - } else if (strcmp(command, "version") == 0) { - checkloginversion(); - } else if (strcmp(command, "who") == 0) { - whoaccount(parameters); -// quit - } else if (strcmp(command, "quit") == 0 || - strcmp(command, "exit") == 0 || - strcmp(command, "end") == 0) { - if (defaultlanguage == 'F') { - printf("Au revoir.\n"); - } else { - printf("Bye.\n"); - } - exit(0); -// unknown command - } else { - if (defaultlanguage == 'F') { - printf("Commande inconnue [%s].\n", buf); - ladmin_log("Commande inconnue [%s]." RETCODE, buf); - } else { - printf("Unknown command [%s].\n", buf); - ladmin_log("Unknown command [%s]." RETCODE, buf); - } - } - } - - return 0; -} - -//------------------------------------------------------------- -// Function: Parse receiving informations from the login-server -//------------------------------------------------------------- -int parse_fromlogin(int fd) { - struct char_session_data *sd; - int id; - RFIFOHEAD(fd); - if (session[fd]->eof) { - if (defaultlanguage == 'F') { - printf("Impossible de se connecter au serveur de login [%s:%d] !\n", loginserverip, loginserverport); - ladmin_log("Impossible de se connecter au serveur de login [%s:%d] !" RETCODE, loginserverip, loginserverport); - } else { - printf("Impossible to have a connection with the login-server [%s:%d] !\n", loginserverip, loginserverport); - ladmin_log("Impossible to have a connection with the login-server [%s:%d] !" RETCODE, loginserverip, loginserverport); - } - close(fd); - delete_session(fd); - exit (0); - } - -// printf("parse_fromlogin : %d %d %d\n", fd, RFIFOREST(fd), RFIFOW(fd,0)); - sd = (struct char_session_data*)session[fd]->session_data; - - while(RFIFOREST(fd) >= 2) { - switch(RFIFOW(fd,0)) { - case 0x7919: // answer of a connection request - if (RFIFOREST(fd) < 3) - return 0; - if (RFIFOB(fd,2) != 0) { - if (defaultlanguage == 'F') { - printf("Erreur de login:\n"); - printf(" - mot de passe incorrect,\n"); - printf(" - syst鑪e d'administration non activ, ou\n"); - printf(" - IP non autoris馥.\n"); - ladmin_log("Erreur de login: mot de passe incorrect, syst鑪e d'administration non activ, ou IP non autoris馥." RETCODE); - } else { - printf("Error at login:\n"); - printf(" - incorrect password,\n"); - printf(" - administration system not activated, or\n"); - printf(" - unauthorised IP.\n"); - ladmin_log("Error at login: incorrect password, administration system not activated, or unauthorised IP." RETCODE); - } - session[fd]->eof = 1; - //bytes_to_read = 1; // not stop at prompt - } else { - if (defaultlanguage == 'F') { - printf("Connexion 騁ablie.\n"); - ladmin_log("Connexion 騁ablie." RETCODE); - printf("Lecture de la version du serveur de login...\n"); - ladmin_log("Lecture de la version du serveur de login..." RETCODE); - } else { - printf("Established connection.\n"); - ladmin_log("Established connection." RETCODE); - printf("Reading of the version of the login-server...\n"); - ladmin_log("Reading of the version of the login-server..." RETCODE); - } - //bytes_to_read = 1; // unchanged - checkloginversion(); - } - RFIFOSKIP(fd,3); - break; - -#ifdef PASSWORDENC - case 0x01dc: // answer of a coding key request - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { - char md5str[64] = "", md5bin[32]; - WFIFOHEAD(login_fd, 20); - if (passenc == 1) { - strncpy(md5str, (const char*)RFIFOP(fd,4), RFIFOW(fd,2) - 4); - strcat(md5str, loginserveradminpassword); - } else if (passenc == 2) { - strncpy(md5str, loginserveradminpassword, sizeof(loginserveradminpassword)); - strcat(md5str, (const char*)RFIFOP(fd,4)); - } - MD5_String2binary(md5str, md5bin); - WFIFOW(login_fd,0) = 0x7918; // Request for administation login (encrypted password) - WFIFOW(login_fd,2) = passenc; // Encrypted type - memcpy(WFIFOP(login_fd,4), md5bin, 16); - WFIFOSET(login_fd,20); - if (defaultlanguage == 'F') { - printf("R馗eption de la clef MD5.\n"); - ladmin_log("R馗eption de la clef MD5." RETCODE); - printf("Envoi du mot de passe crypt...\n"); - ladmin_log("Envoi du mot de passe crypt..." RETCODE); - } else { - printf("Receiving of the MD5 key.\n"); - ladmin_log("Receiving of the MD5 key." RETCODE); - printf("Sending of the encrypted password...\n"); - ladmin_log("Sending of the encrypted password..." RETCODE); - } - } - bytes_to_read = 1; - RFIFOSKIP(fd,RFIFOW(fd,2)); - break; -#endif - - case 0x7531: // Displaying of the version of the login-server - if (RFIFOREST(fd) < 10) - return 0; - printf(" Login-Server [%s:%d]\n", loginserverip, loginserverport); - if (((int)RFIFOB(login_fd,5)) == 0) { - printf(" eAthena version stable-%d.%d", (int)RFIFOB(login_fd,2), (int)RFIFOB(login_fd,3)); - } else { - printf(" eAthena version dev-%d.%d", (int)RFIFOB(login_fd,2), (int)RFIFOB(login_fd,3)); - } - if (((int)RFIFOB(login_fd,4)) == 0) - printf(" revision %d", (int)RFIFOB(login_fd,4)); - if (((int)RFIFOB(login_fd,6)) == 0) - printf("%d.\n", RFIFOW(login_fd,8)); - else - printf("-mod%d.\n", RFIFOW(login_fd,8)); - bytes_to_read = 0; - RFIFOSKIP(fd,10); - break; - - case 0x7921: // Displaying of the list of accounts - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - if (RFIFOW(fd,2) < 5) { - if (defaultlanguage == 'F') { - ladmin_log(" R馗eption d'une liste des comptes vide." RETCODE); - if (list_count == 0) - printf("Aucun compte trouv.\n"); - else if (list_count == 1) - printf("1 compte trouv.\n"); - else - printf("%d comptes trouv駸.\n", list_count); - } else { - ladmin_log(" Receiving of a void accounts list." RETCODE); - if (list_count == 0) - printf("No account found.\n"); - else if (list_count == 1) - printf("1 account found.\n"); - else - printf("%d accounts found.\n", list_count); - } - bytes_to_read = 0; - } else { - int i; - WFIFOHEAD(login_fd,10); - if (defaultlanguage == 'F') - ladmin_log(" R馗eption d'une liste des comptes." RETCODE); - else - ladmin_log(" Receiving of a accounts list." RETCODE); - for(i = 4; i < RFIFOW(fd,2); i += 38) { - int j; - char userid[24]; - char lower_userid[24]; - memcpy(userid, RFIFOP(fd,i + 5), sizeof(userid)); - userid[sizeof(userid)-1] = '\0'; - memset(lower_userid, '\0', sizeof(lower_userid)); - for (j = 0; userid[j]; j++) - lower_userid[j] = tolower(userid[j]); - list_first = RFIFOL(fd,i) + 1; - // here are checks... - if (list_type == 0 || - (list_type == 1 && RFIFOB(fd,i+4) > 0) || - (list_type == 2 && strstr(lower_userid, parameters) != NULL) || - (list_type == 3 && RFIFOL(fd,i+34) != 0) || - (list_type == 4 && RFIFOL(fd,i+34) == 0)) { - printf("%10d ", (int)RFIFOL(fd,i)); - if (RFIFOB(fd,i+4) == 0) - printf(" "); - else - printf("%2d ", (int)RFIFOB(fd,i+4)); - printf("%-24s", userid); - if (defaultlanguage == 'F') { - if (RFIFOB(fd,i+29) == 0) - printf("%-5s ", "Femme"); - else if (RFIFOB(fd,i+29) == 1) - printf("%-5s ", "Male"); - else - printf("%-5s ", "Servr"); - } else { - if (RFIFOB(fd,i+29) == 0) - printf("%-5s ", "Femal"); - else if (RFIFOB(fd,i+29) == 1) - printf("%-5s ", "Male"); - else - printf("%-5s ", "Servr"); - } - printf("%6d ", (int)RFIFOL(fd,i+30)); - switch(RFIFOL(fd,i+34)) { - case 0: - if (defaultlanguage == 'F') - printf("%-27s\n", "Compte Ok"); - else - printf("%-27s\n", "Account OK"); - break; - case 1: - printf("%-27s\n", "Unregistered ID"); - break; - case 2: - printf("%-27s\n", "Incorrect Password"); - break; - case 3: - printf("%-27s\n", "This ID is expired"); - break; - case 4: - printf("%-27s\n", "Rejected from Server"); - break; - case 5: - printf("%-27s\n", "Blocked by the GM Team"); // You have been blocked by the GM Team - break; - case 6: - printf("%-27s\n", "Your EXE file is too old"); // Your Game's EXE file is not the latest version - break; - case 7: - printf("%-27s\n", "Banishement or"); - printf(" Prohibited to login until...\n"); // You are Prohibited to log in until %s - break; - case 8: - printf("%-27s\n", "Server is over populated"); - break; - case 9: - printf("%-27s\n", "No MSG"); - break; - default: // 100 - printf("%-27s\n", "This ID is totally erased"); // This ID has been totally erased - break; - } - list_count++; - } - } - // asking of the following acounts - if (defaultlanguage == 'F') - ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir la liste des comptes de %d %d (compl駑ent)." RETCODE, list_first, list_last); - else - ladmin_log("Request to login-server to obtain the list of accounts from %d to %d (complement)." RETCODE, list_first, list_last); - WFIFOW(login_fd,0) = 0x7920; - WFIFOL(login_fd,2) = list_first; - WFIFOL(login_fd,6) = list_last; - WFIFOSET(login_fd,10); - bytes_to_read = 1; - } - RFIFOSKIP(fd,RFIFOW(fd,2)); - break; - - case 0x7931: // Answer of login-server about an account creation - if (RFIFOREST(fd) < 30) - return 0; - id=RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Echec la cr饌tion du compte [%s]. Un compte identique existe d駛.\n", RFIFOP(fd,6)); - ladmin_log("Echec la cr饌tion du compte [%s]. Un compte identique existe d駛." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] creation failed. Same account already exists.\n", RFIFOP(fd,6)); - ladmin_log("Account [%s] creation failed. Same account already exists." RETCODE, RFIFOP(fd,6)); - } - } else { - if (defaultlanguage == 'F') { - printf("Compte [%s] cr鳬 avec succ鑚 [id: %d].\n", RFIFOP(fd,6), id); - ladmin_log("Compte [%s] cr鳬 avec succ鑚 [id: %d]." RETCODE, RFIFOP(fd,6), id); - } else { - printf("Account [%s] is successfully created [id: %d].\n", RFIFOP(fd,6), id); - ladmin_log("Account [%s] is successfully created [id: %d]." RETCODE, RFIFOP(fd,6), id); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,30); - break; - - case 0x7933: // Answer of login-server about an account deletion - if (RFIFOREST(fd) < 30) - return 0; - if (RFIFOL(fd,2) == -1) { - if (defaultlanguage == 'F') { - printf("Echec de la suppression du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); - ladmin_log("Echec de la suppression du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] deletion failed. Account doesn't exist.\n", RFIFOP(fd,6)); - ladmin_log("Account [%s] deletion failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); - } - } else { - if (defaultlanguage == 'F') { - printf("Compte [%s][id: %d] SUPPRIME avec succ鑚.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2)); - ladmin_log("Compte [%s][id: %d] SUPPRIME avec succ鑚." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); - } else { - printf("Account [%s][id: %d] is successfully DELETED.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2)); - ladmin_log("Account [%s][id: %d] is successfully DELETED." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,30); - break; - - case 0x7935: // answer of the change of an account password - if (RFIFOREST(fd) < 30) - return 0; - if (RFIFOL(fd,2) == -1) { - if (defaultlanguage == 'F') { - printf("Echec de la modification du mot de passe du compte [%s].\n", RFIFOP(fd,6)); - printf("Le compte [%s] n'existe pas.\n", RFIFOP(fd,6)); - ladmin_log("Echec de la modification du mot de passe du compte. Le compte [%s] n'existe pas." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] password changing failed.\n", RFIFOP(fd,6)); - printf("Account [%s] doesn't exist.\n", RFIFOP(fd,6)); - ladmin_log("Account password changing failed. The compte [%s] doesn't exist." RETCODE, RFIFOP(fd,6)); - } - } else { - if (defaultlanguage == 'F') { - printf("Modification du mot de passe du compte [%s][id: %d] r騏ssie.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2)); - ladmin_log("Modification du mot de passe du compte [%s][id: %d] r騏ssie." RETCODE, RFIFOP(fd,6), (int)RFIFOL(fd,2)); - } else { - printf("Account [%s][id: %d] password successfully changed.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2)); - ladmin_log("Account [%s][id: %d] password successfully changed." RETCODE, RFIFOP(fd,6), (int)RFIFOL(fd,2)); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,30); - break; - - case 0x7937: // answer of the change of an account state - if (RFIFOREST(fd) < 34) - return 0; - if (RFIFOL(fd,2) == -1) { - if (defaultlanguage == 'F') { - printf("Echec du changement du statut du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); - ladmin_log("Echec du changement du statut du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] state changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); - ladmin_log("Account [%s] state changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); - } - } else { - char tmpstr[256]; - if (defaultlanguage == 'F') { - sprintf(tmpstr, "Statut du compte [%s] chang avec succ鑚 en [", RFIFOP(fd,6)); - } else { - sprintf(tmpstr, "Account [%s] state successfully changed in [", RFIFOP(fd,6)); - } - switch(RFIFOL(fd,30)) { - case 0: - if (defaultlanguage == 'F') - strcat(tmpstr, "0: Compte Ok"); - else - strcat(tmpstr, "0: Account OK"); - break; - case 1: - strcat(tmpstr, "1: Unregistered ID"); - break; - case 2: - strcat(tmpstr, "2: Incorrect Password"); - break; - case 3: - strcat(tmpstr, "3: This ID is expired"); - break; - case 4: - strcat(tmpstr, "4: Rejected from Server"); - break; - case 5: - strcat(tmpstr, "5: You have been blocked by the GM Team"); - break; - case 6: - strcat(tmpstr, "6: [Your Game's EXE file is not the latest version"); - break; - case 7: - strcat(tmpstr, "7: You are Prohibited to log in until..."); - break; - case 8: - strcat(tmpstr, "8: Server is jammed due to over populated"); - break; - case 9: - strcat(tmpstr, "9: No MSG"); - break; - default: // 100 - strcat(tmpstr, "100: This ID is totally erased"); - break; - } - strcat(tmpstr, "]"); - printf("%s\n", tmpstr); - ladmin_log("%s%s", tmpstr, RETCODE); - } - bytes_to_read = 0; - RFIFOSKIP(fd,34); - break; - - case 0x7939: // answer of the number of online players - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { - // Get length of the received packet - int i; - char name[20]; - if (defaultlanguage == 'F') { - ladmin_log(" R馗eption du nombre de joueurs en ligne." RETCODE); - } else { - ladmin_log(" Receiving of the number of online players." RETCODE); - } - // Read information of the servers - if (RFIFOW(fd,2) < 5) { - if (defaultlanguage == 'F') { - printf(" Aucun serveur n'est connect au login serveur.\n"); - } else { - printf(" No server is connected to the login-server.\n"); - } - } else { - if (defaultlanguage == 'F') { - printf(" Nombre de joueurs en ligne (serveur: nb):\n"); - } else { - printf(" Number of online players (server: number).\n"); - } - // Displaying of result - for(i = 4; i < RFIFOW(fd,2); i += 32) { - memcpy(name, RFIFOP(fd,i+6), sizeof(name)); - name[sizeof(name) - 1] = '\0'; - printf(" %-20s : %5d\n", name, RFIFOW(fd,i+26)); - } - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,RFIFOW(fd,2)); - break; - - case 0x793b: // answer of the check of a password - if (RFIFOREST(fd) < 30) - return 0; - id = RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Le compte [%s] n'existe pas ou le mot de passe est incorrect.\n", RFIFOP(fd,6)); - ladmin_log("Le compte [%s] n'existe pas ou le mot de passe est incorrect." RETCODE, RFIFOP(fd,6)); - } else { - printf("The account [%s] doesn't exist or the password is incorrect.\n", RFIFOP(fd,6)); - ladmin_log("The account [%s] doesn't exist or the password is incorrect." RETCODE, RFIFOP(fd,6)); - } - } else { - if (defaultlanguage == 'F') { - printf("Le mot de passe donn correspond bien au compte [%s][id: %d].\n", RFIFOP(fd,6), id); - ladmin_log("Le mot de passe donn correspond bien au compte [%s][id: %d]." RETCODE, RFIFOP(fd,6), id); - } else { - printf("The proposed password is correct for the account [%s][id: %d].\n", RFIFOP(fd,6), id); - ladmin_log("The proposed password is correct for the account [%s][id: %d]." RETCODE, RFIFOP(fd,6), id); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,30); - break; - - case 0x793d: // answer of the change of an account sex - if (RFIFOREST(fd) < 30) - return 0; - id = RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Echec de la modification du sexe du compte [%s].\n", RFIFOP(fd,6)); - printf("Le compte [%s] n'existe pas ou le sexe est d駛 celui demand.\n", RFIFOP(fd,6)); - ladmin_log("Echec de la modification du sexe du compte. Le compte [%s] n'existe pas ou le sexe est d駛 celui demand." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] sex changing failed.\n", RFIFOP(fd,6)); - printf("Account [%s] doesn't exist or the sex is already the good sex.\n", RFIFOP(fd,6)); - ladmin_log("Account sex changing failed. The compte [%s] doesn't exist or the sex is already the good sex." RETCODE, RFIFOP(fd,6)); - } - } else { - if (defaultlanguage == 'F') { - printf("Sexe du compte [%s][id: %d] chang avec succ鑚.\n", RFIFOP(fd,6), id); - ladmin_log("Sexe du compte [%s][id: %d] chang avec succ鑚." RETCODE, RFIFOP(fd,6), id); - } else { - printf("Account [%s][id: %d] sex successfully changed.\n", RFIFOP(fd,6), id); - ladmin_log("Account [%s][id: %d] sex successfully changed." RETCODE, RFIFOP(fd,6), id); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,30); - break; - - case 0x793f: // answer of the change of an account GM level - if (RFIFOREST(fd) < 30) - return 0; - id = RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Echec de la modification du niveau de GM du compte [%s].\n", RFIFOP(fd,6)); - printf("Le compte [%s] n'existe pas, le niveau de GM est d駛 celui demand饅n", RFIFOP(fd,6)); - printf("ou il est impossible de modifier le fichier des comptes GM.\n"); - ladmin_log("Echec de la modification du niveau de GM du compte. Le compte [%s] n'existe pas, le niveau de GM est d駛 celui demand ou il est impossible de modifier le fichier des comptes GM." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] GM level changing failed.\n", RFIFOP(fd,6)); - printf("Account [%s] doesn't exist, the GM level is already the good GM level\n", RFIFOP(fd,6)); - printf("or it's impossible to modify the GM accounts file.\n"); - ladmin_log("Account GM level changing failed. The compte [%s] doesn't exist, the GM level is already the good sex or it's impossible to modify the GM accounts file." RETCODE, RFIFOP(fd,6)); - } - } else { - if (defaultlanguage == 'F') { - printf("Niveau de GM du compte [%s][id: %d] chang avec succ鑚.\n", RFIFOP(fd,6), id); - ladmin_log("Niveau de GM du compte [%s][id: %d] chang avec succ鑚." RETCODE, RFIFOP(fd,6), id); - } else { - printf("Account [%s][id: %d] GM level successfully changed.\n", RFIFOP(fd,6), id); - ladmin_log("Account [%s][id: %d] GM level successfully changed." RETCODE, RFIFOP(fd,6), id); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,30); - break; - - case 0x7941: // answer of the change of an account email - if (RFIFOREST(fd) < 30) - return 0; - id = RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Echec de la modification de l'e-mail du compte [%s].\n", RFIFOP(fd,6)); - printf("Le compte [%s] n'existe pas.\n", RFIFOP(fd,6)); - ladmin_log("Echec de la modification de l'e-mail du compte. Le compte [%s] n'existe pas." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] e-mail changing failed.\n", RFIFOP(fd,6)); - printf("Account [%s] doesn't exist.\n", RFIFOP(fd,6)); - ladmin_log("Account e-mail changing failed. The compte [%s] doesn't exist." RETCODE, RFIFOP(fd,6)); - } - } else { - if (defaultlanguage == 'F') { - printf("Modification de l'e-mail du compte [%s][id: %d] r騏ssie.\n", RFIFOP(fd,6), id); - ladmin_log("Modification de l'e-mail du compte [%s][id: %d] r騏ssie." RETCODE, RFIFOP(fd,6), id); - } else { - printf("Account [%s][id: %d] e-mail successfully changed.\n", RFIFOP(fd,6), id); - ladmin_log("Account [%s][id: %d] e-mail successfully changed." RETCODE, RFIFOP(fd,6), id); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,30); - break; - - case 0x7943: // answer of the change of an account memo - if (RFIFOREST(fd) < 30) - return 0; - id = RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Echec du changement du m駑o du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); - ladmin_log("Echec du changement du m駑o du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] memo changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); - ladmin_log("Account [%s] memo changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); - } - } else { - if (defaultlanguage == 'F') { - printf("M駑o du compte [%s][id: %d] chang avec succ鑚.\n", RFIFOP(fd,6), id); - ladmin_log("M駑o du compte [%s][id: %d] chang avec succ鑚." RETCODE, RFIFOP(fd,6), id); - } else { - printf("Account [%s][id: %d] memo successfully changed.\n", RFIFOP(fd,6), id); - ladmin_log("Account [%s][id: %d] memo successfully changed." RETCODE, RFIFOP(fd,6), id); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,30); - break; - - case 0x7945: // answer of an account id search - if (RFIFOREST(fd) < 30) - return 0; - id = RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Impossible de trouver l'id du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); - ladmin_log("Impossible de trouver l'id du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); - } else { - printf("Unable to find the account [%s] id. Account doesn't exist.\n", RFIFOP(fd,6)); - ladmin_log("Unable to find the account [%s] id. Account doesn't exist." RETCODE, RFIFOP(fd,6)); - } - } else { - if (defaultlanguage == 'F') { - printf("Le compte [%s] a pour id: %d.\n", RFIFOP(fd,6), id); - ladmin_log("Le compte [%s] a pour id: %d." RETCODE, RFIFOP(fd,6), id); - } else { - printf("The account [%s] have the id: %d.\n", RFIFOP(fd,6), id); - ladmin_log("The account [%s] have the id: %d." RETCODE, RFIFOP(fd,6), id); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,30); - break; - - case 0x7947: // answer of an account name search - if (RFIFOREST(fd) < 30) - return 0; - id = RFIFOL(fd,2); - if (strcmp((const char*)RFIFOP(fd,6), "") == 0) { - if (defaultlanguage == 'F') { - printf("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas.\n", id); - ladmin_log("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas." RETCODE, id); - } else { - printf("Unable to find the account [%d] name. Account doesn't exist.\n", id); - ladmin_log("Unable to find the account [%d] name. Account doesn't exist." RETCODE, id); - } - } else { - if (defaultlanguage == 'F') { - printf("Le compte [id: %d] a pour nom: %s.\n", id, RFIFOP(fd,6)); - ladmin_log("Le compte [id: %d] a pour nom: %s." RETCODE, id, RFIFOP(fd,6)); - } else { - printf("The account [id: %d] have the name: %s.\n", id, RFIFOP(fd,6)); - ladmin_log("The account [id: %d] have the name: %s." RETCODE, id, RFIFOP(fd,6)); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,30); - break; - - case 0x7949: // answer of an account validity limit set - if (RFIFOREST(fd) < 34) - return 0; - id = RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Echec du changement de la validit du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); - ladmin_log("Echec du changement de la validit du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] validity limit changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); - ladmin_log("Account [%s] validity limit changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); - } - } else { - time_t timestamp = RFIFOL(fd,30); - if (timestamp == 0) { - if (defaultlanguage == 'F') { - printf("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 en [illimit饐.\n", RFIFOP(fd,6), id); - ladmin_log("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 en [illimit饐." RETCODE, RFIFOP(fd,6), id); - } else { - printf("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n", RFIFOP(fd,6), id); - ladmin_log("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited]." RETCODE, RFIFOP(fd,6), id); - } - } else { - char tmpstr[128]; - strftime(tmpstr, 24, date_format, localtime(×tamp)); - if (defaultlanguage == 'F') { - printf("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), id, tmpstr); - ladmin_log("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr); - } else { - printf("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr); - ladmin_log("Validity Limit of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, tmpstr); - } - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,34); - break; - - case 0x794b: // answer of an account ban set - if (RFIFOREST(fd) < 34) - return 0; - id = RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); - ladmin_log("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); - ladmin_log("Account [%s] final date of banishment changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); - } - } else { - time_t timestamp = RFIFOL(fd,30); - if (timestamp == 0) { - if (defaultlanguage == 'F') { - printf("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 en [d-bannie].\n", RFIFOP(fd,6), id); - ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 en [d-bannie]." RETCODE, RFIFOP(fd,6), id); - } else { - printf("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", RFIFOP(fd,6), id); - ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished]." RETCODE, RFIFOP(fd,6), id); - } - } else { - char tmpstr[128]; - strftime(tmpstr, 24, date_format, localtime(×tamp)); - if (defaultlanguage == 'F') { - printf("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), id, tmpstr); - ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr); - } else { - printf("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr); - ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, tmpstr); - } - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,34); - break; - - case 0x794d: // answer of an account ban date/time changing - if (RFIFOREST(fd) < 34) - return 0; - id = RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); - ladmin_log("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); - ladmin_log("Account [%s] final date of banishment changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); - } - } else { - time_t timestamp = RFIFOL(fd,30); - if (timestamp == 0) { - if (defaultlanguage == 'F') { - printf("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 en [d-bannie].\n", RFIFOP(fd,6), id); - ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 en [d-bannie]." RETCODE, RFIFOP(fd,6), id); - } else { - printf("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", RFIFOP(fd,6), id); - ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished]." RETCODE, RFIFOP(fd,6), id); - } - } else { - char tmpstr[128]; - strftime(tmpstr, 24, date_format, localtime(×tamp)); - if (defaultlanguage == 'F') { - printf("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), id, tmpstr); - ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr); - } else { - printf("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr); - ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, tmpstr); - } - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,34); - break; - - case 0x794f: // answer of a broadcast - if (RFIFOREST(fd) < 4) - return 0; - if (RFIFOW(fd,2) == (unsigned short)-1) { - if (defaultlanguage == 'F') { - printf("Echec de l'envoi du message. Aucun server de char en ligne.\n"); - ladmin_log("Echec de l'envoi du message. Aucun server de char en ligne." RETCODE); - } else { - printf("Message sending failed. No online char-server.\n"); - ladmin_log("Message sending failed. No online char-server." RETCODE); - } - } else { - if (defaultlanguage == 'F') { - printf("Message transmis au server de logins avec succ鑚.\n"); - ladmin_log("Message transmis au server de logins avec succ鑚." RETCODE); - } else { - printf("Message successfully sended to login-server.\n"); - ladmin_log("Message successfully sended to login-server." RETCODE); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,4); - break; - - case 0x7951: // answer of an account validity limit changing - if (RFIFOREST(fd) < 34) - return 0; - id = RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Echec du changement de la validit du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); - ladmin_log("Echec du changement de la validit du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); - } else { - printf("Account [%s] validity limit changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); - ladmin_log("Account [%s] validity limit changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); - } - } else { - time_t timestamp = RFIFOL(fd,30); - if (timestamp == 0) { - if (defaultlanguage == 'F') { - printf("Limite de validit du compte [%s][id: %d] inchang馥.\n", RFIFOP(fd,6), id); - printf("Le compte a une validit illimit馥 ou\n"); - printf("la modification est impossible avec les ajustements demand駸.\n"); - ladmin_log("Limite de validit du compte [%s][id: %d] inchang馥. Le compte a une validit illimit馥 ou la modification est impossible avec les ajustements demand駸." RETCODE, RFIFOP(fd,6), id); - } else { - printf("Validity limit of the account [%s][id: %d] unchanged.\n", RFIFOP(fd,6), id); - printf("The account have an unlimited validity limit or\n"); - printf("the changing is impossible with the proposed adjustments.\n"); - ladmin_log("Validity limit of the account [%s][id: %d] unchanged. The account have an unlimited validity limit or the changing is impossible with the proposed adjustments." RETCODE, RFIFOP(fd,6), id); - } - } else { - char tmpstr[128]; - strftime(tmpstr, 24, date_format, localtime(×tamp)); - if (defaultlanguage == 'F') { - printf("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), id, tmpstr); - ladmin_log("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr); - } else { - printf("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr); - ladmin_log("Validity limit of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, tmpstr); - } - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,34); - break; - - case 0x7953: // answer of a request about informations of an account (by account name/id) - if (RFIFOREST(fd) < 150 || RFIFOREST(fd) < (150 + RFIFOW(fd,148))) - return 0; - { - char userid[24], error_message[20], lastlogin[24], last_ip[16], email[40], memo[255]; - time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - memcpy(userid, RFIFOP(fd,7), sizeof(userid)); - userid[sizeof(userid)-1] = '\0'; - memcpy(error_message, RFIFOP(fd,40), sizeof(error_message)); - error_message[sizeof(error_message)-1] = '\0'; - memcpy(lastlogin, RFIFOP(fd,60), sizeof(lastlogin)); - lastlogin[sizeof(lastlogin)-1] = '\0'; - memcpy(last_ip, RFIFOP(fd,84), sizeof(last_ip)); - last_ip[sizeof(last_ip)-1] = '\0'; - memcpy(email, RFIFOP(fd,100), sizeof(email)); - email[sizeof(email)-1] = '\0'; - connect_until_time = (time_t)RFIFOL(fd,140); - ban_until_time = (time_t)RFIFOL(fd,144); - memset(memo, '\0', sizeof(memo)); - strncpy(memo, (const char*)RFIFOP(fd,150), RFIFOW(fd,148)); - id = RFIFOL(fd,2); - if (id == -1) { - if (defaultlanguage == 'F') { - printf("Impossible de trouver le compte [%s]. Le compte n'existe pas.\n", parameters); - ladmin_log("Impossible de trouver le compte [%s]. Le compte n'existe pas." RETCODE, parameters); - } else { - printf("Unabled to find the account [%s]. Account doesn't exist.\n", parameters); - ladmin_log("Unabled to find the account [%s]. Account doesn't exist." RETCODE, parameters); - } - } else if (strlen(userid) == 0) { - if (defaultlanguage == 'F') { - printf("Impossible de trouver le compte [id: %s]. Le compte n'existe pas.\n", parameters); - ladmin_log("Impossible de trouver le compte [id: %s]. Le compte n'existe pas." RETCODE, parameters); - } else { - printf("Unabled to find the account [id: %s]. Account doesn't exist.\n", parameters); - ladmin_log("Unabled to find the account [id: %s]. Account doesn't exist." RETCODE, parameters); - } - } else { - if (defaultlanguage == 'F') { - ladmin_log("R馗eption d'information concernant un compte." RETCODE); - printf("Le compte a les caract駻istiques suivantes:\n"); - } else { - ladmin_log("Receiving information about an account." RETCODE); - printf("The account is set with:\n"); - } - if (RFIFOB(fd,6) == 0) { - printf(" Id: %d (non-GM)\n", id); - } else { - if (defaultlanguage == 'F') { - printf(" Id: %d (GM niveau %d)\n", id, (int)RFIFOB(fd,6)); - } else { - printf(" Id: %d (GM level %d)\n", id, (int)RFIFOB(fd,6)); - } - } - if (defaultlanguage == 'F') { - printf(" Nom: '%s'\n", userid); - if (RFIFOB(fd,31) == 0) - printf(" Sexe: Femme\n"); - else if (RFIFOB(fd,31) == 1) - printf(" Sexe: Male\n"); - else - printf(" Sexe: Serveur\n"); - } else { - printf(" Name: '%s'\n", userid); - if (RFIFOB(fd,31) == 0) - printf(" Sex: Female\n"); - else if (RFIFOB(fd,31) == 1) - printf(" Sex: Male\n"); - else - printf(" Sex: Server\n"); - } - printf(" E-mail: %s\n", email); - switch(RFIFOL(fd,36)) { - case 0: - if (defaultlanguage == 'F') - printf(" Statut: 0 [Compte Ok]\n"); - else - printf(" Statut: 0 [Account OK]\n"); - break; - case 1: - printf(" Statut: 1 [Unregistered ID]\n"); - break; - case 2: - printf(" Statut: 2 [Incorrect Password]\n"); - break; - case 3: - printf(" Statut: 3 [This ID is expired]\n"); - break; - case 4: - printf(" Statut: 4 [Rejected from Server]\n"); - break; - case 5: - printf(" Statut: 5 [You have been blocked by the GM Team]\n"); - break; - case 6: - printf(" Statut: 6 [Your Game's EXE file is not the latest version]\n"); - break; - case 7: - printf(" Statut: 7 [You are Prohibited to log in until %s]\n", error_message); - break; - case 8: - printf(" Statut: 8 [Server is jammed due to over populated]\n"); - break; - case 9: - printf(" Statut: 9 [No MSG]\n"); - break; - default: // 100 - printf(" Statut: %d [This ID is totally erased]\n", (int)RFIFOL(fd,36)); - break; - } - if (defaultlanguage == 'F') { - if (ban_until_time == 0) { - printf(" Banissement: non banni.\n"); - } else { - char tmpstr[128]; - strftime(tmpstr, 24, date_format, localtime(&ban_until_time)); - printf(" Banissement: jusqu'au %s.\n", tmpstr); - } - if (RFIFOL(fd,32) > 1) - printf(" Compteur: %d connexions.\n", (int)RFIFOL(fd,32)); - else - printf(" Compteur: %d connexion.\n", (int)RFIFOL(fd,32)); - printf(" Derni鑽e connexion le: %s (ip: %s)\n", lastlogin, last_ip); - if (connect_until_time == 0) { - printf(" Limite de validit: illimit.\n"); - } else { - char tmpstr[128]; - strftime(tmpstr, 24, date_format, localtime(&connect_until_time)); - printf(" Limite de validit: jusqu'au %s.\n", tmpstr); - } - } else { - if (ban_until_time == 0) { - printf(" Banishment: not banished.\n"); - } else { - char tmpstr[128]; - strftime(tmpstr, 24, date_format, localtime(&ban_until_time)); - printf(" Banishment: until %s.\n", tmpstr); - } - if (RFIFOL(fd,32) > 1) - printf(" Count: %d connections.\n", (int)RFIFOL(fd,32)); - else - printf(" Count: %d connection.\n", (int)RFIFOL(fd,32)); - printf(" Last connection at: %s (ip: %s)\n", lastlogin, last_ip); - if (connect_until_time == 0) { - printf(" Validity limit: unlimited.\n"); - } else { - char tmpstr[128]; - strftime(tmpstr, 24, date_format, localtime(&connect_until_time)); - printf(" Validity limit: until %s.\n", tmpstr); - } - } - printf(" Memo: '%s'\n", memo); - } - } - bytes_to_read = 0; - RFIFOSKIP(fd,150 + RFIFOW(fd,148)); - break; - - default: - printf("Remote administration has been disconnected (unknown packet).\n"); - ladmin_log("'End of connection, unknown packet." RETCODE); - session[fd]->eof = 1; - return 0; - } - } - - // if we don't wait new packets, do the prompt - prompt(); - - return 0; -} - -//------------------------------------ -// Function to connect to login-server -//------------------------------------ -int Connect_login_server(void) { - if (defaultlanguage == 'F') { - printf("Essai de connection au server de logins...\n"); - ladmin_log("Essai de connection au server de logins..." RETCODE); - } else { - printf("Attempt to connect to login-server...\n"); - ladmin_log("Attempt to connect to login-server..." RETCODE); - } - - login_fd = make_connection(login_ip, loginserverport); - if (login_fd == -1) - { //Might not be the most elegant way to handle this, but I've never used ladmin so I dunno what else you could do. [Skotlex] - printf("Error: Failed to connect to Login Server\n"); - exit(1); - } -#ifdef PASSWORDENC - if (passenc == 0) { -#endif - WFIFOHEAD(login_fd,28); - WFIFOW(login_fd,0) = 0x7918; // Request for administation login - WFIFOW(login_fd,2) = 0; // no encrypted - memcpy(WFIFOP(login_fd,4), loginserveradminpassword, 24); - WFIFOSET(login_fd,28); - bytes_to_read = 1; - if (defaultlanguage == 'F') { - printf("Envoi du mot de passe...\n"); - ladmin_log("Envoi du mot de passe..." RETCODE); - } else { - printf("Sending of the password...\n"); - ladmin_log("Sending of the password..." RETCODE); - } -#ifdef PASSWORDENC - } else { - WFIFOHEAD(login_fd,2); - WFIFOW(login_fd,0) = 0x791a; // Sending request about the coding key - WFIFOSET(login_fd,2); - bytes_to_read = 1; - if (defaultlanguage == 'F') { - printf("Demande de la clef MD5...\n"); - ladmin_log("Demande de la clef MD5..." RETCODE); - } else { - printf("Request about the MD5 key...\n"); - ladmin_log("Request about the MD5 key..." RETCODE); - } - } -#endif - - return 0; -} - -//------------------------------------------------- -// Return numerical value of a switch configuration -// on/off, english, fran軋is, deutsch, espaol -//------------------------------------------------- -int config_switch(const char *str) { - if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) - return 1; - if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) - return 0; - - return atoi(str); -} - -//----------------------------------- -// Reading general configuration file -//----------------------------------- -int ladmin_config_read(const char *cfgName) { - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - fp = fopen(cfgName, "r"); - if (fp == NULL) { - if (defaultlanguage == 'F') { - printf("\033[0mFichier de configuration (%s) non trouv.\n", cfgName); - } else { - printf("\033[0mConfiguration file (%s) not found.\n", cfgName); - } - return 1; - } - - if (defaultlanguage == 'F') { - printf("\033[0m---D饕ut de lecture du fichier de configuration Ladmin (%s)\n", cfgName); - } else { - printf("\033[0m---Start reading of Ladmin configuration file (%s)\n", cfgName); - } - while(fgets(line, sizeof(line)-1, fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - - line[sizeof(line)-1] = '\0'; - if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { - remove_control_chars((unsigned char *) w1); - remove_control_chars((unsigned char *) w2); - - if(strcmpi(w1,"login_ip")==0){ - struct hostent *h = gethostbyname (w2); - if (h != NULL) { - if (defaultlanguage == 'F') { - printf("Adresse du serveur de logins: %s -> %d.%d.%d.%d\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); - } else { - printf("Login server IP address: %s -> %d.%d.%d.%d\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); - } - sprintf(loginserverip, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); - } else - memcpy(loginserverip, w2, 16); - } else if (strcmpi(w1, "login_port") == 0) { - loginserverport = atoi(w2); - } else if (strcmpi(w1, "admin_pass") == 0) { - strncpy(loginserveradminpassword, w2, sizeof(loginserveradminpassword)); - loginserveradminpassword[sizeof(loginserveradminpassword)-1] = '\0'; -#ifdef PASSWORDENC - } else if (strcmpi(w1, "passenc") == 0) { - passenc = atoi(w2); - if (passenc < 0 || passenc > 2) - passenc = 0; -#endif - } else if (strcmpi(w1, "defaultlanguage") == 0) { - if (w2[0] == 'F' || w2[0] == 'E') - defaultlanguage = w2[0]; - } else if (strcmpi(w1, "ladmin_log_filename") == 0) { - strncpy(ladmin_log_filename, w2, sizeof(ladmin_log_filename)); - ladmin_log_filename[sizeof(ladmin_log_filename)-1] = '\0'; - } else if (strcmpi(w1, "date_format") == 0) { // note: never have more than 19 char for the date! - switch (atoi(w2)) { - case 0: - strcpy(date_format, "%d-%m-%Y %H:%M:%S"); // 31-12-2004 23:59:59 - break; - case 1: - strcpy(date_format, "%m-%d-%Y %H:%M:%S"); // 12-31-2004 23:59:59 - break; - case 2: - strcpy(date_format, "%Y-%d-%m %H:%M:%S"); // 2004-31-12 23:59:59 - break; - case 3: - strcpy(date_format, "%Y-%m-%d %H:%M:%S"); // 2004-12-31 23:59:59 - break; - } - } else if (strcmpi(w1, "import") == 0) { - ladmin_config_read(w2); - } - } - } - fclose(fp); - - login_ip = inet_addr(loginserverip); - - if (defaultlanguage == 'F') { - printf("---Lecture du fichier de configuration Ladmin termin馥.\n"); - } else { - printf("---End reading of Ladmin configuration file.\n"); - } - - return 0; -} - -//-------------------------------------- -// Function called at exit of the server -//-------------------------------------- -void do_final(void) { - - if (already_exit_function == 0) { - delete_session(login_fd); - - if (defaultlanguage == 'F') { - printf("\033[0m----Fin de Ladmin (fin normale avec fermeture de tous les fichiers).\n"); - ladmin_log("----Fin de Ladmin (fin normale avec fermeture de tous les fichiers)." RETCODE); - } else { - printf("\033[0m----End of Ladmin (normal end with closing of all files).\n"); - ladmin_log("----End of Ladmin (normal end with closing of all files)." RETCODE); - } - - already_exit_function = 1; - } -} - -//------------------------ -// Main function of ladmin -//------------------------ -int do_init(int argc, char **argv) -{ - int next; - socket_init(); - - // read ladmin configuration - ladmin_config_read((argc > 1) ? argv[1] : LADMIN_CONF_NAME); - - ladmin_log(""); - if (defaultlanguage == 'F') { - ladmin_log("Fichier de configuration lu." RETCODE); - } else { - ladmin_log("Configuration file readed." RETCODE); - } - - srand(time(NULL)); - - set_defaultparse(parse_fromlogin); - - if (defaultlanguage == 'F') { - printf("Outil d'administration distance de eAthena.\n"); - printf("(pour eAthena version %d.%d.%d.)\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION); - } else { - printf("EAthena login-server administration tool.\n"); - printf("(for eAthena version %d.%d.%d.)\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION); - } - - if (defaultlanguage == 'F') { - ladmin_log("Ladmin est pr黎." RETCODE); - printf("Ladmin est \033[1;32mpr黎\033[0m.\n\n"); - } else { - ladmin_log("Ladmin is ready." RETCODE); - printf("Ladmin is \033[1;32mready\033[0m.\n\n"); - } - - Connect_login_server(); - - // minimalist core doesn't have sockets parsing, - // so we have to do this ourselves - while (runflag) { - next = do_timer(gettick_nocache()); - do_sendrecv(next); -#ifndef TURBO - do_parsepacket(); -#endif - } - - return 0; -} +// (c) eAthena Dev Team - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +/////////////////////////////////////////////////////////////////////////// +// EAthena login-server remote administration tool +// Ladamin in C by [Yor] +// if you modify this software, modify ladmin in tool too. +/////////////////////////////////////////////////////////////////////////// + +#include +#include +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +void Gettimeofday(struct timeval *timenow) +{ + time_t t; + t = clock(); + timenow->tv_usec = t; + timenow->tv_sec = t / CLK_TCK; + return; +} +#define gettimeofday(timenow, dummy) Gettimeofday(timenow) +#else +#include +#include +#include // gettimeofday +#include +#include // close +#include // inet_addr +#include // gethostbyname +#endif +#include +#include +#include +#include +#include // str* +#include // valist +#include // tolower + +#include "../common/core.h" +#include "../common/strlib.h" +#include "../common/socket.h" +#include "../common/timer.h" +#include "ladmin.h" +#include "../common/version.h" +#include "../common/mmo.h" + +#ifdef PASSWORDENC +#include "md5calc.h" +#endif + +//-------------------------------INSTRUCTIONS------------------------------ +// Set the variables below: +// IP of the login server. +// Port where the login-server listens incoming packets. +// Password of administration (same of config_athena.conf). +// Displayed language of the sofware (if not correct, english is used). +// IMPORTANT: +// Be sure that you authorize remote administration in login-server +// (see login_athena.conf, 'admin_state' parameter) +//------------------------------------------------------------------------- +char loginserverip[16] = "127.0.0.1"; // IP of login-server +int loginserverport = 6900; // Port of login-server +char loginserveradminpassword[24] = "admin"; // Administration password +#ifdef PASSWORDENC +int passenc = 2; // Encoding type of the password +#else +int passenc = 0; // Encoding type of the password +#endif +char defaultlanguage = 'E'; // Default language (F: Fran軋is/E: English) + // (if it's not 'F', default is English) +char ladmin_log_filename[1024] = "log/ladmin.log"; +char date_format[32] = "%Y-%m-%d %H:%M:%S"; +//------------------------------------------------------------------------- +// LIST of COMMANDs that you can type at the prompt: +// To use these commands you can only type only the first letters. +// You must type a minimum of letters (you can not type 'a', +// because ladmin doesn't know if it's for 'aide' or for 'add') +// q <= quit, li <= list, pass <= passwd, etc. +// +// Note: every time you must give a account_name, you can use "" or '' (spaces can be included) +// +// aide/help/? +// Display the description of the commands +// aide/help/? [command] +// Display the description of the specified command +// +// add +// Create an account with the default email (a@a.com). +// Concerning the sex, only the first letter is used (F or M). +// The e-mail is set to a@a.com (default e-mail). It's like to have no e-mail. +// When the password is omitted, the input is done without displaying of the pressed keys. +// add testname Male testpass +// +// ban/banish yyyy/mm/dd hh:mm:ss +// Changes the final date of a banishment of an account. +// Like banset, but is at end. +// +// banadd +// Adds or substracts time from the final date of a banishment of an account. +// Modifier is done as follows: +// Adjustment value (-1, 1, +1, etc...) +// Modified element: +// a or y: year +// m: month +// j or d: day +// h: hour +// mn: minute +// s: second +// banadd testname +1m-2mn1s-6y +// this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time. +// NOTE: If you modify the final date of a non-banished account, +// you fix the final date to (actual time +- adjustments) +// +// banset yyyy/mm/dd [hh:mm:ss] +// Changes the final date of a banishment of an account. +// Default time [hh:mm:ss]: 23:59:59. +// banset 0 +// Set a non-banished account (0 = unbanished). +// +// block +// Set state 5 (You have been blocked by the GM Team) to an account. +// Like state 5. +// +// check +// Check the validity of a password for an account +// NOTE: Server will never sends back a password. +// It's the only method you have to know if a password is correct. +// The other method is to have a ('physical') access to the accounts file. +// +// create +// Like the 'add' command, but with e-mail moreover. +// create testname Male my@mail.com testpass +// +// del +// Remove an account. +// This order requires confirmation. After confirmation, the account is deleted. +// +// email +// Modify the e-mail of an account. +// +// getcount +// Give the number of players online on all char-servers. +// +// gm [GM_level] +// Modify the GM level of an account. +// Default value remove GM level (GM level = 0). +// gm testname 80 +// +// id +// Give the id of an account. +// +// info +// Display complete information of an account. +// +// kami +// Sends a broadcast message on all map-server (in yellow). +// kamib +// Sends a broadcast message on all map-server (in blue). +// +// language +// Change the language of displaying. +// +// list/ls [start_id [end_id]] +// Display a list of accounts. +// 'start_id', 'end_id': indicate end and start identifiers. +// Research by name is not possible with this command. +// list 10 9999999 +// +// listBan/lsBan [start_id [end_id]] +// Like list/ls, but only for accounts with state or banished +// +// listGM/lsGM [start_id [end_id]] +// Like list/ls, but only for GM accounts +// +// listOK/lsOK [start_id [end_id]] +// Like list/ls, but only for accounts without state and not banished +// +// memo +// Modify the memo of an account. +// 'memo': it can have until 253 characters (with spaces or not). +// +// name +// Give the name of an account. +// +// passwd +// Change the password of an account. +// When new password is omitted, the input is done without displaying of the pressed keys. +// +// quit/end/exit +// End of the program of administration +// +// reloadGM +// Reload GM configuration file +// +// search +// Seek accounts. +// Displays the accounts whose names correspond. +// search -r/-e/--expr/--regex +// Seek accounts by regular expression. +// Displays the accounts whose names correspond. +// +// sex +// Modify the sex of an account. +// sex testname Male +// +// state +// Change the state of an account. +// 'new_state': state is the state of the packet 0x006a + 1. The possibilities are: +// 0 = Account ok 6 = Your Game's EXE file is not the latest version +// 1 = Unregistered ID 7 = You are Prohibited to log in until %s +// 2 = Incorrect Password 8 = Server is jammed due to over populated +// 3 = This ID is expired 9 = No MSG +// 4 = Rejected from Server 100 = This ID has been totally erased +// 5 = You have been blocked by the GM Team +// all other values are 'No MSG', then use state 9 please. +// 'error_message_#7': message of the code error 6 = Your are Prohibited to log in until %s (packet 0x006a) +// +// timeadd +// Adds or substracts time from the validity limit of an account. +// Modifier is done as follows: +// Adjustment value (-1, 1, +1, etc...) +// Modified element: +// a or y: year +// m: month +// j or d: day +// h: hour +// mn: minute +// s: second +// timeadd testname +1m-2mn1s-6y +// this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time. +// NOTE: You can not modify a unlimited validity limit. +// If you want modify it, you want probably create a limited validity limit. +// So, at first, you must set the validity limit to a date/time. +// +// timeset yyyy/mm/dd [hh:mm:ss] +// Changes the validity limit of an account. +// Default time [hh:mm:ss]: 23:59:59. +// timeset 0 +// Gives an unlimited validity limit (0 = unlimited). +// +// unban/unbanish +// Unban an account. +// Like banset 0. +// +// unblock +// Set state 0 (Account ok) to an account. +// Like state 0. +// +// version +// Display the version of the login-server. +// +// who +// Displays complete information of an account. +// +//------------------------------------------------------------------------- +int login_fd; +int login_ip; +int bytes_to_read = 0; // flag to know if we waiting bytes from login-server +char command[1024]; +char parameters[1024]; +int list_first, list_last, list_type, list_count; // parameter to display a list of accounts +int already_exit_function = 0; // sometimes, the exit function is called twice... so, don't log twice the message + +//------------------------------ +// Writing function of logs file +//------------------------------ +int ladmin_log(char *fmt, ...) { + FILE *logfp; + va_list ap; + struct timeval tv; + char tmpstr[2048]; + + va_start(ap, fmt); + + logfp = fopen(ladmin_log_filename, "a"); + if (logfp) { + if (fmt[0] == '\0') // jump a line if no message + fprintf(logfp, RETCODE); + else { + gettimeofday(&tv, NULL); + strftime(tmpstr, 24, date_format, localtime((const time_t*)&(tv.tv_sec))); + sprintf(tmpstr + strlen(tmpstr), ".%03d: %s", (int)tv.tv_usec / 1000, fmt); + vfprintf(logfp, tmpstr, ap); + } + fclose(logfp); + } + + va_end(ap); + return 0; +} + +//--------------------------------------------- +// Function to return ordonal text of a number. +//--------------------------------------------- +char* makeordinal(int number) { + if (defaultlanguage == 'F') { + if (number == 0) + return ""; + else if (number == 1) + return "er"; + else + return "鑪e"; + } else { + if ((number % 10) < 4 && (number % 10) != 0 && (number < 10 || number > 20)) { + if ((number % 10) == 1) + return "st"; + else if ((number % 10) == 2) + return "nd"; + else + return "rd"; + } else { + return "th"; + } + } + return ""; +} + +//----------------------------------------------------------------------------------------- +// Function to test of the validity of an account name (return 0 if incorrect, and 1 if ok) +//----------------------------------------------------------------------------------------- +int verify_accountname(char* account_name) { + int i; + + for(i = 0; account_name[i]; i++) { + if (account_name[i] < 32) { + if (defaultlanguage == 'F') { + printf("Caract鑽e interdit trouv dans le nom du compte (%d%s caract鑽e).\n", i+1, makeordinal(i+1)); + ladmin_log("Caract鑽e interdit trouv dans le nom du compte (%d%s caract鑽e)." RETCODE, i+1, makeordinal(i+1)); + } else { + printf("Illegal character found in the account name (%d%s character).\n", i+1, makeordinal(i+1)); + ladmin_log("Illegal character found in the account name (%d%s character)." RETCODE, i+1, makeordinal(i+1)); + } + return 0; + } + } + + if (strlen(account_name) < 4) { + if (defaultlanguage == 'F') { + printf("Nom du compte trop court. Entrez un nom de compte de 4-23 caract鑽es.\n"); + ladmin_log("Nom du compte trop court. Entrez un nom de compte de 4-23 caract鑽es." RETCODE); + } else { + printf("Account name is too short. Please input an account name of 4-23 bytes.\n"); + ladmin_log("Account name is too short. Please input an account name of 4-23 bytes." RETCODE); + } + return 0; + } + + if (strlen(account_name) > 23) { + if (defaultlanguage == 'F') { + printf("Nom du compte trop long. Entrez un nom de compte de 4-23 caract鑽es.\n"); + ladmin_log("Nom du compte trop long. Entrez un nom de compte de 4-23 caract鑽es." RETCODE); + } else { + printf("Account name is too long. Please input an account name of 4-23 bytes.\n"); + ladmin_log("Account name is too long. Please input an account name of 4-23 bytes." RETCODE); + } + return 0; + } + + return 1; +} + +//--------------------------------------------------- +// E-mail check: return 0 (not correct) or 1 (valid). +//--------------------------------------------------- +int e_mail_check(char *email) { + char ch; + char* last_arobas; + + // athena limits + if (strlen(email) < 3 || strlen(email) > 39) + return 0; + + // part of RFC limits (official reference of e-mail description) + if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') + return 0; + + if (email[strlen(email)-1] == '.') + return 0; + + last_arobas = strrchr(email, '@'); + + if (strstr(last_arobas, "@.") != NULL || + strstr(last_arobas, "..") != NULL) + return 0; + + for(ch = 1; ch < 32; ch++) { + if (strchr(last_arobas, ch) != NULL) { + return 0; + break; + } + } + + if (strchr(last_arobas, ' ') != NULL || + strchr(last_arobas, ';') != NULL) + return 0; + + // all correct + return 1; +} + +//---------------------------------- +// Sub-function: Input of a password +//---------------------------------- +int typepasswd(char * password) { + char password1[1023], password2[1023]; + int letter; + int i; + + if (defaultlanguage == 'F') { + ladmin_log("Aucun mot de passe n'a 騁 donn. Demande d'un mot de passe." RETCODE); + } else { + ladmin_log("No password was given. Request to obtain a password." RETCODE); + } + + memset(password1, '\0', sizeof(password1)); + memset(password2, '\0', sizeof(password2)); + if (defaultlanguage == 'F') + printf("\033[1;36m Entrez le mot de passe > \033[0;32;42m"); + else + printf("\033[1;36m Type the password > \033[0;32;42m"); + i = 0; + while ((letter = getchar()) != '\n') + password1[i++] = letter; + if (defaultlanguage == 'F') + printf("\033[0m\033[1;36m R-entrez le mot de passe > \033[0;32;42m"); + else + printf("\033[0m\033[1;36m Verify the password > \033[0;32;42m"); + i = 0; + while ((letter = getchar()) != '\n') + password2[i++] = letter; + + printf("\033[0m"); + fflush(stdout); + fflush(stdin); + + if (strcmp(password1, password2) != 0) { + if (defaultlanguage == 'F') { + printf("Erreur de v駻ification du mot de passe: Saisissez le m麥e mot de passe svp.\n"); + ladmin_log("Erreur de v駻ification du mot de passe: Saisissez le m麥e mot de passe svp." RETCODE); + ladmin_log(" Premier mot de passe: %s, second mot de passe: %s." RETCODE, password1, password2); + } else { + printf("Password verification failed. Please input same password.\n"); + ladmin_log("Password verification failed. Please input same password." RETCODE); + ladmin_log(" First password: %s, second password: %s." RETCODE, password1, password2); + } + return 0; + } + if (defaultlanguage == 'F') { + ladmin_log("Mot de passe saisi: %s." RETCODE, password1); + } else { + ladmin_log("Typed password: %s." RETCODE, password1); + } + strcpy(password, password1); + return 1; +} + +//------------------------------------------------------------------------------------ +// Sub-function: Test of the validity of password (return 0 if incorrect, and 1 if ok) +//------------------------------------------------------------------------------------ +int verify_password(char * password) { + int i; + + for(i = 0; password[i]; i++) { + if (password[i] < 32) { + if (defaultlanguage == 'F') { + printf("Caract鑽e interdit trouv dans le mot de passe (%d%s caract鑽e).\n", i+1, makeordinal(i+1)); + ladmin_log("Caract鑽e interdit trouv dans le nom du compte (%d%s caract鑽e)." RETCODE, i+1, makeordinal(i+1)); + } else { + printf("Illegal character found in the password (%d%s character).\n", i+1, makeordinal(i+1)); + ladmin_log("Illegal character found in the password (%d%s character)." RETCODE, i+1, makeordinal(i+1)); + } + return 0; + } + } + + if (strlen(password) < 4) { + if (defaultlanguage == 'F') { + printf("Nom du compte trop court. Entrez un nom de compte de 4-23 caract鑽es.\n"); + ladmin_log("Nom du compte trop court. Entrez un nom de compte de 4-23 caract鑽es." RETCODE); + } else { + printf("Account name is too short. Please input an account name of 4-23 bytes.\n"); + ladmin_log("Account name is too short. Please input an account name of 4-23 bytes." RETCODE); + } + return 0; + } + + if (strlen(password) > 23) { + if (defaultlanguage == 'F') { + printf("Mot de passe trop long. Entrez un mot de passe de 4-23 caract鑽es.\n"); + ladmin_log("Mot de passe trop long. Entrez un mot de passe de 4-23 caract鑽es." RETCODE); + } else { + printf("Password is too long. Please input a password of 4-23 bytes.\n"); + ladmin_log("Password is too long. Please input a password of 4-23 bytes." RETCODE); + } + return 0; + } + + return 1; +} + +//------------------------------------------------------------------ +// Sub-function: Check the name of a command (return complete name) +//----------------------------------------------------------------- +int check_command(char * command) { +// help + if (strncmp(command, "aide", 2) == 0 && strncmp(command, "aide", strlen(command)) == 0) // not 1 letter command: 'aide' or 'add'? + strcpy(command, "aide"); + else if (strncmp(command, "help", 1) == 0 && strncmp(command, "help", strlen(command)) == 0) + strcpy(command, "help"); +// general commands + else if (strncmp(command, "add", 2) == 0 && strncmp(command, "add", strlen(command)) == 0) // not 1 letter command: 'aide' or 'add'? + strcpy(command, "add"); + else if ((strncmp(command, "ban", 3) == 0 && strncmp(command, "ban", strlen(command)) == 0) || + (strncmp(command, "banish", 4) == 0 && strncmp(command, "banish", strlen(command)) == 0)) + strcpy(command, "ban"); + else if ((strncmp(command, "banadd", 4) == 0 && strncmp(command, "banadd", strlen(command)) == 0) || // not 1 letter command: 'ba' or 'bs'? 'banadd' or 'banset' ? + strcmp(command, "ba") == 0) + strcpy(command, "banadd"); + else if ((strncmp(command, "banset", 4) == 0 && strncmp(command, "banset", strlen(command)) == 0) || // not 1 letter command: 'ba' or 'bs'? 'banadd' or 'banset' ? + strcmp(command, "bs") == 0) + strcpy(command, "banset"); + else if (strncmp(command, "block", 2) == 0 && strncmp(command, "block", strlen(command)) == 0) + strcpy(command, "block"); + else if (strncmp(command, "check", 2) == 0 && strncmp(command, "check", strlen(command)) == 0) // not 1 letter command: 'check' or 'create'? + strcpy(command, "check"); + else if (strncmp(command, "create", 2) == 0 && strncmp(command, "create", strlen(command)) == 0) // not 1 letter command: 'check' or 'create'? + strcpy(command, "create"); + else if (strncmp(command, "delete", 1) == 0 && strncmp(command, "delete", strlen(command)) == 0) + strcpy(command, "delete"); + else if ((strncmp(command, "email", 2) == 0 && strncmp(command, "email", strlen(command)) == 0) || // not 1 letter command: 'email', 'end' or 'exit'? + (strncmp(command, "e-mail", 2) == 0 && strncmp(command, "e-mail", strlen(command)) == 0)) + strcpy(command, "email"); + else if (strncmp(command, "getcount", 2) == 0 && strncmp(command, "getcount", strlen(command)) == 0) // not 1 letter command: 'getcount' or 'gm'? + strcpy(command, "getcount"); +// else if (strncmp(command, "gm", 2) == 0 && strncmp(command, "gm", strlen(command)) == 0) // not 1 letter command: 'getcount' or 'gm'? +// strcpy(command, "gm"); +// else if (strncmp(command, "id", 2) == 0 && strncmp(command, "id", strlen(command)) == 0) // not 1 letter command: 'id' or 'info'? +// strcpy(command, "id"); + else if (strncmp(command, "info", 2) == 0 && strncmp(command, "info", strlen(command)) == 0) // not 1 letter command: 'id' or 'info'? + strcpy(command, "info"); +// else if (strncmp(command, "kami", 4) == 0 && strncmp(command, "kami", strlen(command)) == 0) // only all letters command: 'kami' or 'kamib'? +// strcpy(command, "kami"); +// else if (strncmp(command, "kamib", 5) == 0 && strncmp(command, "kamib", strlen(command)) == 0) // only all letters command: 'kami' or 'kamib'? +// strcpy(command, "kamib"); + else if ((strncmp(command, "language", 2) == 0 && strncmp(command, "language", strlen(command)) == 0)) // not 1 letter command: 'language' or 'list'? + strcpy(command, "language"); + else if ((strncmp(command, "list", 2) == 0 && strncmp(command, "list", strlen(command)) == 0) || // 'list' is default list command // not 1 letter command: 'language' or 'list'? + strcmp(command, "ls") == 0) + strcpy(command, "list"); + else if ((strncmp(command, "listban", 5) == 0 && strncmp(command, "listban", strlen(command)) == 0) || + (strncmp(command, "lsban", 3) == 0 && strncmp(command, "lsban", strlen(command)) == 0) || + strcmp(command, "lb") == 0) + strcpy(command, "listban"); + else if ((strncmp(command, "listgm", 5) == 0 && strncmp(command, "listgm", strlen(command)) == 0) || + (strncmp(command, "lsgm", 3) == 0 && strncmp(command, "lsgm", strlen(command)) == 0) || + strcmp(command, "lg") == 0) + strcpy(command, "listgm"); + else if ((strncmp(command, "listok", 5) == 0 && strncmp(command, "listok", strlen(command)) == 0) || + (strncmp(command, "lsok", 3) == 0 && strncmp(command, "lsok", strlen(command)) == 0) || + strcmp(command, "lo") == 0) + strcpy(command, "listok"); + else if (strncmp(command, "memo", 1) == 0 && strncmp(command, "memo", strlen(command)) == 0) + strcpy(command, "memo"); + else if (strncmp(command, "name", 1) == 0 && strncmp(command, "name", strlen(command)) == 0) + strcpy(command, "name"); + else if ((strncmp(command, "password", 1) == 0 && strncmp(command, "password", strlen(command)) == 0) || + strcmp(command, "passwd") == 0) + strcpy(command, "password"); + else if (strncmp(command, "reloadgm", 1) == 0 && strncmp(command, "reloadgm", strlen(command)) == 0) + strcpy(command, "reloadgm"); + else if (strncmp(command, "search", 3) == 0 && strncmp(command, "search", strlen(command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? + strcpy(command, "search"); // not 2 letters command: 'search' or 'sex'? +// else if (strncmp(command, "sex", 3) == 0 && strncmp(command, "sex", strlen(command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? +// strcpy(command, "sex"); // not 2 letters command: 'search' or 'sex'? + else if (strncmp(command, "state", 2) == 0 && strncmp(command, "state", strlen(command)) == 0) // not 1 letter command: 'search', 'state' or 'sex'? + strcpy(command, "state"); + else if ((strncmp(command, "timeadd", 5) == 0 && strncmp(command, "timeadd", strlen(command)) == 0) || // not 1 letter command: 'ta' or 'ts'? 'timeadd' or 'timeset'? + strcmp(command, "ta") == 0) + strcpy(command, "timeadd"); + else if ((strncmp(command, "timeset", 5) == 0 && strncmp(command, "timeset", strlen(command)) == 0) || // not 1 letter command: 'ta' or 'ts'? 'timeadd' or 'timeset'? + strcmp(command, "ts") == 0) + strcpy(command, "timeset"); + else if ((strncmp(command, "unban", 5) == 0 && strncmp(command, "unban", strlen(command)) == 0) || + (strncmp(command, "unbanish", 4) == 0 && strncmp(command, "unbanish", strlen(command)) == 0)) + strcpy(command, "unban"); + else if (strncmp(command, "unblock", 4) == 0 && strncmp(command, "unblock", strlen(command)) == 0) + strcpy(command, "unblock"); + else if (strncmp(command, "version", 1) == 0 && strncmp(command, "version", strlen(command)) == 0) + strcpy(command, "version"); + else if (strncmp(command, "who", 1) == 0 && strncmp(command, "who", strlen(command)) == 0) + strcpy(command, "who"); +// quit + else if (strncmp(command, "quit", 1) == 0 && strncmp(command, "quit", strlen(command)) == 0) + strcpy(command, "quit"); + else if (strncmp(command, "exit", 2) == 0 && strncmp(command, "exit", strlen(command)) == 0) // not 1 letter command: 'email', 'end' or 'exit'? + strcpy(command, "exit"); + else if (strncmp(command, "end", 2) == 0 && strncmp(command, "end", strlen(command)) == 0) // not 1 letter command: 'email', 'end' or 'exit'? + strcpy(command, "end"); + + return 0; +} + +//----------------------------------------- +// Sub-function: Display commands of ladmin +//----------------------------------------- +void display_help(char* param, int language) { + char command[1023]; + int i; + + memset(command, '\0', sizeof(command)); + + if (sscanf(param, "%s ", command) < 1 || strlen(command) == 0) + strcpy(command, ""); // any value that is not a command + + if (command[0] == '?') { + if (defaultlanguage == 'F') + strcpy(command, "aide"); + else + strcpy(command, "help"); + } + + // lowercase for command + for (i = 0; command[i]; i++) + command[i] = tolower(command[i]); + + // Analyse of the command + check_command(command); // give complete name to the command + + if (defaultlanguage == 'F') { + ladmin_log("Affichage des commandes ou d'une commande." RETCODE); + } else { + ladmin_log("Displaying of the commands or a command." RETCODE); + } + + if (language == 1) { + if (strcmp(command, "aide") == 0) { + printf("aide/help/?\n"); + printf(" Affiche la description des commandes\n"); + printf("aide/help/? [commande]\n"); + printf(" Affiche la description de la commande specifi馥\n"); + } else if (strcmp(command, "help") == 0 ) { + printf("aide/help/?\n"); + printf(" Display the description of the commands\n"); + printf("aide/help/? [command]\n"); + printf(" Display the description of the specified command\n"); +// general commands + } else if (strcmp(command, "add") == 0) { + printf("add \n"); + printf(" Cr馥 un compte avec l'email par d馭aut (a@a.com).\n"); + printf(" Concernant le sexe, seule la premi鑽e lettre compte (F ou M).\n"); + printf(" L'e-mail est a@a.com (e-mail par d馭aut). C'est comme n'avoir aucun e-mail.\n"); + printf(" Lorsque motdepasse est omis, la saisie se fait sans que la frappe se voit.\n"); + printf(" add testname Male testpass\n"); + } else if (strcmp(command, "ban") == 0) { + printf("ban/banish aaaa/mm/jj hh:mm:ss \n"); + printf(" Change la date de fin de bannissement d'un compte.\n"); + printf(" Comme banset, mais est la fin.\n"); + } else if (strcmp(command, "banadd") == 0) { + printf("banadd \n"); + printf(" Ajoute ou soustrait du temps la date de banissement d'un compte.\n"); + printf(" Les modificateurs sont construits comme suit:\n"); + printf(" Valeur d'ajustement (-1, 1, +1, etc...)\n"); + printf(" El駑ent modifi:\n"); + printf(" a ou y: ann馥\n"); + printf(" m: mois\n"); + printf(" j ou d: jour\n"); + printf(" h: heure\n"); + printf(" mn: minute\n"); + printf(" s: seconde\n"); + printf(" banadd testname +1m-2mn1s-6a\n"); + printf(" Cette exemple ajoute 1 mois et une seconde, et soustrait 2 minutes\n"); + printf(" et 6 ans dans le m麥e temps.\n"); + printf("NOTE: Si vous modifez la date de banissement d'un compte non bani,\n"); + printf(" vous indiquez comme date (le moment actuel +- les ajustements)\n"); + } else if (strcmp(command, "banset") == 0) { + printf("banset aaaa/mm/jj [hh:mm:ss]\n"); + printf(" Change la date de fin de bannissement d'un compte.\n"); + printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); + printf("banset 0\n"); + printf(" D饕anni un compte (0 = de-banni).\n"); + } else if (strcmp(command, "block") == 0) { + printf("block \n"); + printf(" Place le status d'un compte 5 (You have been blocked by the GM Team).\n"); + printf(" La commande est l'駲uivalent de state 5.\n"); + } else if (strcmp(command, "check") == 0) { + printf("check \n"); + printf(" V駻ifie la validit d'un mot de passe pour un compte\n"); + printf(" NOTE: Le serveur n'enverra jamais un mot de passe.\n"); + printf(" C'est la seule m騁hode que vous poss馘ez pour savoir\n"); + printf(" si un mot de passe est le bon. L'autre m騁hode est\n"); + printf(" d'avoir un acc鑚 ('physique') au fichier des comptes.\n"); + } else if (strcmp(command, "create") == 0) { + printf("create \n"); + printf(" Comme la commande add, mais avec l'e-mail en plus.\n"); + printf(" create testname Male mon@mail.com testpass\n"); + } else if (strcmp(command, "delete") == 0) { + printf("del \n"); + printf(" Supprime un compte.\n"); + printf(" La commande demande confirmation. Apr鑚 confirmation, le compte est d騁ruit.\n"); + } else if (strcmp(command, "email") == 0) { + printf("email \n"); + printf(" Modifie l'e-mail d'un compte.\n"); + } else if (strcmp(command, "getcount") == 0) { + printf("getcount\n"); + printf(" Donne le nombre de joueurs en ligne par serveur de char.\n"); + } else if (strcmp(command, "gm") == 0) { + printf("gm [Niveau_GM]\n"); + printf(" Modifie le niveau de GM d'un compte.\n"); + printf(" Valeur par d馭aut: 0 (suppression du niveau de GM).\n"); + printf(" gm nomtest 80\n"); + } else if (strcmp(command, "id") == 0) { + printf("id \n"); + printf(" Donne l'id d'un compte.\n"); + } else if (strcmp(command, "info") == 0) { + printf("info \n"); + printf(" Affiche les informations sur un compte.\n"); + } else if (strcmp(command, "kami") == 0) { + printf("kami \n"); + printf(" Envoi un message g駭駻al sur tous les serveurs de map (en jaune).\n"); + } else if (strcmp(command, "kamib") == 0) { + printf("kamib \n"); + printf(" Envoi un message g駭駻al sur tous les serveurs de map (en bleu).\n"); + } else if (strcmp(command, "language") == 0) { + printf("language \n"); + printf(" Change la langue d'affichage.\n"); + printf(" Langues possibles: 'Fran軋is' ou 'English'.\n"); + } else if (strcmp(command, "list") == 0) { + printf("list/ls [Premier_id [Dernier_id]]\n"); + printf(" Affiche une liste de comptes.\n"); + printf(" 'Premier_id', 'Dernier_id': indique les identifiants de d駱art et de fin.\n"); + printf(" La recherche par nom n'est pas possible avec cette commande.\n"); + printf(" list 10 9999999\n"); + } else if (strcmp(command, "listban") == 0) { + printf("listBan/lsBan [Premier_id [Dernier_id]]\n"); + printf(" Comme list/ls, mais seulement pour les comptes avec statut ou bannis.\n"); + } else if (strcmp(command, "listgm") == 0) { + printf("listGM/lsGM [Premier_id [Dernier_id]]\n"); + printf(" Comme list/ls, mais seulement pour les comptes GM.\n"); + } else if (strcmp(command, "listok") == 0) { + printf("listOK/lsOK [Premier_id [Dernier_id]]\n"); + printf(" Comme list/ls, mais seulement pour les comptes sans statut et non bannis.\n"); + } else if (strcmp(command, "memo") == 0) { + printf("memo \n"); + printf(" Modifie le m駑o d'un compte.\n"); + printf(" 'memo': Il peut avoir jusqu' 253 caract鑽es (avec des espaces ou non).\n"); + } else if (strcmp(command, "name") == 0) { + printf("name \n"); + printf(" Donne le nom d'un compte.\n"); + } else if (strcmp(command, "password") == 0) { + printf("passwd \n"); + printf(" Change le mot de passe d'un compte.\n"); + printf(" Lorsque nouveaumotdepasse est omis,\n"); + printf(" la saisie se fait sans que la frappe ne se voit.\n"); + } else if (strcmp(command, "reloadgm") == 0) { + printf("reloadGM\n"); + printf(" Reload GM configuration file\n"); + } else if (strcmp(command, "search") == 0) { + printf("search \n"); + printf(" Cherche des comptes.\n"); + printf(" Affiche les comptes dont les noms correspondent.\n"); +// printf("search -r/-e/--expr/--regex \n"); +// printf(" Cherche des comptes par expression reguli鑽e.\n"); +// printf(" Affiche les comptes dont les noms correspondent.\n"); + } else if (strcmp(command, "sex") == 0) { + printf("sex \n"); + printf(" Modifie le sexe d'un compte.\n"); + printf(" sex testname Male\n"); + } else if (strcmp(command, "state") == 0) { + printf("state \n"); + printf(" Change le statut d'un compte.\n"); + printf(" 'nouveaustatut': Le statut est le m麥e que celui du packet 0x006a + 1.\n"); + printf(" les possibilit駸 sont:\n"); + printf(" 0 = Compte ok\n"); + printf(" 1 = Unregistered ID\n"); + printf(" 2 = Incorrect Password\n"); + printf(" 3 = This ID is expired\n"); + printf(" 4 = Rejected from Server\n"); + printf(" 5 = You have been blocked by the GM Team\n"); + printf(" 6 = Your Game's EXE file is not the latest version\n"); + printf(" 7 = You are Prohibited to log in until...\n"); + printf(" 8 = Server is jammed due to over populated\n"); + printf(" 9 = No MSG\n"); + printf(" 100 = This ID has been totally erased\n"); + printf(" all other values are 'No MSG', then use state 9 please.\n"); + printf(" 'message_erreur_7': message du code erreur 6 =\n"); + printf(" = Your are Prohibited to log in until... (packet 0x006a)\n"); + } else if (strcmp(command, "timeadd") == 0) { + printf("timeadd \n"); + printf(" Ajoute/soustrait du temps la limite de validit d'un compte.\n"); + printf(" Le modificateur est compos comme suit:\n"); + printf(" Valeur modificatrice (-1, 1, +1, etc...)\n"); + printf(" El駑ent modifi:\n"); + printf(" a ou y: ann馥\n"); + printf(" m: mois\n"); + printf(" j ou d: jour\n"); + printf(" h: heure\n"); + printf(" mn: minute\n"); + printf(" s: seconde\n"); + printf(" timeadd testname +1m-2mn1s-6a\n"); + printf(" Cette exemple ajoute 1 mois et une seconde, et soustrait 2 minutes\n"); + printf(" et 6 ans dans le m麥e temps.\n"); + printf("NOTE: Vous ne pouvez pas modifier une limite de validit illimit馥. Si vous\n"); + printf(" d駸irez le faire, c'est que vous voulez probablement cr馥r un limite de\n"); + printf(" validit limit馥. Donc, en premier, fix une limite de valitid.\n"); + } else if (strcmp(command, "timeadd") == 0) { + printf("timeset aaaa/mm/jj [hh:mm:ss]\n"); + printf(" Change la limite de validit d'un compte.\n"); + printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); + printf("timeset 0\n"); + printf(" Donne une limite de validit illimit馥 (0 = illimit馥).\n"); + } else if (strcmp(command, "unban") == 0) { + printf("unban/unbanish \n"); + printf(" Ote le banissement d'un compte.\n"); + printf(" La commande est l'駲uivalent de banset 0.\n"); + } else if (strcmp(command, "unblock") == 0) { + printf("unblock \n"); + printf(" Place le status d'un compte 0 (Compte ok).\n"); + printf(" La commande est l'駲uivalent de state 0.\n"); + } else if (strcmp(command, "version") == 0) { + printf("version\n"); + printf(" Affiche la version du login-serveur.\n"); + } else if (strcmp(command, "who") == 0) { + printf("who \n"); + printf(" Affiche les informations sur un compte.\n"); +// quit + } else if (strcmp(command, "quit") == 0 || + strcmp(command, "exit") == 0 || + strcmp(command, "end") == 0) { + printf("quit/end/exit\n"); + printf(" Fin du programme d'administration.\n"); +// unknown command + } else { + if (strlen(command) > 0) + printf("Commande inconnue [%s] pour l'aide. Affichage de toutes les commandes.\n", command); + printf(" aide/help/? -- Affiche cet aide\n"); + printf(" aide/help/? [commande] -- Affiche l'aide de la commande\n"); + printf(" add -- Cr馥 un compte (sans email)\n"); + printf(" ban/banish aaaa/mm/jj hh:mm:ss -- Fixe la date finale de banismnt\n"); + printf(" banadd/ba -- Ajout/soustrait du temps la\n"); + printf(" exemple: ba moncompte +1m-2mn1s-2y date finale de banissement\n"); + printf(" banset/bs aaaa/mm/jj [hh:mm:ss] -- Change la date fin de banisemnt\n"); + printf(" banset/bs 0 -- D-banis un compte.\n"); + printf(" block -- Mets le status d'un compte 5 (blocked by the GM Team)\n"); + printf(" check -- V駻ifie un mot de passe d'un compte\n"); + printf(" create -- Cr馥 un compte (avec email)\n"); + printf(" del -- Supprime un compte\n"); + printf(" email -- Modifie l'e-mail d'un compte\n"); + printf(" getcount -- Donne le nb de joueurs en ligne\n"); + printf(" gm [Niveau_GM] -- Modifie le niveau de GM d'un compte\n"); + printf(" id -- Donne l'id d'un compte\n"); + printf(" info -- Affiche les infos sur un compte\n"); + printf(" kami -- Envoi un message g駭駻al (en jaune)\n"); + printf(" kamib -- Envoi un message g駭駻al (en bleu)\n"); + printf(" language -- Change la langue d'affichage.\n"); + printf(" list/ls [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); + printf(" listBan/lsBan [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); + printf(" avec un statut ou bannis\n"); + printf(" listGM/lsGM [Premier_id [Dernier_id] ] -- Affiche une liste de comptes GM\n"); + printf(" listOK/lsOK [Premier_id [Dernier_id] ] -- Affiche une liste de comptes\n"); + printf(" sans status et non bannis\n"); + printf(" memo -- Modifie le memo d'un compte\n"); + printf(" name -- Donne le nom d'un compte\n"); + printf(" passwd -- Change le mot de passe d'un compte\n"); + printf(" quit/end/exit -- Fin du programme d'administation\n"); + printf(" reloadGM -- Recharger le fichier de config des GM\n"); + printf(" search -- Cherche des comptes\n"); +// printf(" search -e/-r/--expr/--regex -- Cherche des comptes par REGEX\n"); + printf(" sex -- Modifie le sexe d'un compte\n"); + printf(" state -- Change le statut d'1 compte\n"); + printf(" timeadd/ta -- Ajout/soustrait du temps la\n"); + printf(" exemple: ta moncompte +1m-2mn1s-2y limite de validit饅n"); + printf(" timeset/ts aaaa/mm/jj [hh:mm:ss] -- Change la limite de validit饅n"); + printf(" timeset/ts 0 -- limite de validit = illimit馥\n"); + printf(" unban/unbanish -- Ote le banissement d'un compte\n"); + printf(" unblock -- Mets le status d'un compte 0 (Compte ok)\n"); + printf(" version -- Donne la version du login-serveur\n"); + printf(" who -- Affiche les infos sur un compte\n"); + printf(" Note: Pour les noms de compte avec des espaces, tapez \"\" (ou ').\n"); + } + } else { + if (strcmp(command, "aide") == 0) { + printf("aide/help/?\n"); + printf(" Display the description of the commands\n"); + printf("aide/help/? [command]\n"); + printf(" Display the description of the specified command\n"); + } else if (strcmp(command, "help") == 0 ) { + printf("aide/help/?\n"); + printf(" Display the description of the commands\n"); + printf("aide/help/? [command]\n"); + printf(" Display the description of the specified command\n"); +// general commands + } else if (strcmp(command, "add") == 0) { + printf("add \n"); + printf(" Create an account with the default email (a@a.com).\n"); + printf(" Concerning the sex, only the first letter is used (F or M).\n"); + printf(" The e-mail is set to a@a.com (default e-mail). It's like to have no e-mail.\n"); + printf(" When the password is omitted,\n"); + printf(" the input is done without displaying of the pressed keys.\n"); + printf(" add testname Male testpass\n"); + } else if (strcmp(command, "ban") == 0) { + printf("ban/banish yyyy/mm/dd hh:mm:ss \n"); + printf(" Changes the final date of a banishment of an account.\n"); + printf(" Like banset, but is at end.\n"); + } else if (strcmp(command, "banadd") == 0) { + printf("banadd \n"); + printf(" Adds or substracts time from the final date of a banishment of an account.\n"); + printf(" Modifier is done as follows:\n"); + printf(" Adjustment value (-1, 1, +1, etc...)\n"); + printf(" Modified element:\n"); + printf(" a or y: year\n"); + printf(" m: month\n"); + printf(" j or d: day\n"); + printf(" h: hour\n"); + printf(" mn: minute\n"); + printf(" s: second\n"); + printf(" banadd testname +1m-2mn1s-6y\n"); + printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf(" and 6 years at the same time.\n"); + printf("NOTE: If you modify the final date of a non-banished account,\n"); + printf(" you fix the final date to (actual time +- adjustments)\n"); + } else if (strcmp(command, "banset") == 0) { + printf("banset yyyy/mm/dd [hh:mm:ss]\n"); + printf(" Changes the final date of a banishment of an account.\n"); + printf(" Default time [hh:mm:ss]: 23:59:59.\n"); + printf("banset 0\n"); + printf(" Set a non-banished account (0 = unbanished).\n"); + } else if (strcmp(command, "block") == 0) { + printf("block \n"); + printf(" Set state 5 (You have been blocked by the GM Team) to an account.\n"); + printf(" This command works like state 5.\n"); + } else if (strcmp(command, "check") == 0) { + printf("check \n"); + printf(" Check the validity of a password for an account.\n"); + printf(" NOTE: Server will never sends back a password.\n"); + printf(" It's the only method you have to know if a password is correct.\n"); + printf(" The other method is to have a ('physical') access to the accounts file.\n"); + } else if (strcmp(command, "create") == 0) { + printf("create \n"); + printf(" Like the 'add' command, but with e-mail moreover.\n"); + printf(" create testname Male my@mail.com testpass\n"); + } else if (strcmp(command, "delete") == 0) { + printf("del \n"); + printf(" Remove an account.\n"); + printf(" This order requires confirmation. After confirmation, the account is deleted.\n"); + } else if (strcmp(command, "email") == 0) { + printf("email \n"); + printf(" Modify the e-mail of an account.\n"); + } else if (strcmp(command, "getcount") == 0) { + printf("getcount\n"); + printf(" Give the number of players online on all char-servers.\n"); + } else if (strcmp(command, "gm") == 0) { + printf("gm [GM_level]\n"); + printf(" Modify the GM level of an account.\n"); + printf(" Default value remove GM level (GM level = 0).\n"); + printf(" gm testname 80\n"); + } else if (strcmp(command, "id") == 0) { + printf("id \n"); + printf(" Give the id of an account.\n"); + } else if (strcmp(command, "info") == 0) { + printf("info \n"); + printf(" Display complete information of an account.\n"); + } else if (strcmp(command, "kami") == 0) { + printf("kami \n"); + printf(" Sends a broadcast message on all map-server (in yellow).\n"); + } else if (strcmp(command, "kamib") == 0) { + printf("kamib \n"); + printf(" Sends a broadcast message on all map-server (in blue).\n"); + } else if (strcmp(command, "language") == 0) { + printf("language \n"); + printf(" Change the language of displaying.\n"); + printf(" Possible languages: Fran軋is or English.\n"); + } else if (strcmp(command, "list") == 0) { + printf("list/ls [start_id [end_id]]\n"); + printf(" Display a list of accounts.\n"); + printf(" 'start_id', 'end_id': indicate end and start identifiers.\n"); + printf(" Research by name is not possible with this command.\n"); + printf(" list 10 9999999\n"); + } else if (strcmp(command, "listban") == 0) { + printf("listBan/lsBan [start_id [end_id]]\n"); + printf(" Like list/ls, but only for accounts with state or banished.\n"); + } else if (strcmp(command, "listgm") == 0) { + printf("listGM/lsGM [start_id [end_id]]\n"); + printf(" Like list/ls, but only for GM accounts.\n"); + } else if (strcmp(command, "listok") == 0) { + printf("listOK/lsOK [start_id [end_id]]\n"); + printf(" Like list/ls, but only for accounts without state and not banished.\n"); + } else if (strcmp(command, "memo") == 0) { + printf("memo \n"); + printf(" Modify the memo of an account.\n"); + printf(" 'memo': it can have until 253 characters (with spaces or not).\n"); + } else if (strcmp(command, "name") == 0) { + printf("name \n"); + printf(" Give the name of an account.\n"); + } else if (strcmp(command, "password") == 0) { + printf("passwd \n"); + printf(" Change the password of an account.\n"); + printf(" When new password is omitted,\n"); + printf(" the input is done without displaying of the pressed keys.\n"); + } else if (strcmp(command, "reloadgm") == 0) { + printf("reloadGM\n"); + printf(" Reload GM configuration file\n"); + } else if (strcmp(command, "search") == 0) { + printf("search \n"); + printf(" Seek accounts.\n"); + printf(" Displays the accounts whose names correspond.\n"); +// printf("search -r/-e/--expr/--regex \n"); +// printf(" Seek accounts by regular expression.\n"); +// printf(" Displays the accounts whose names correspond.\n"); + } else if (strcmp(command, "sex") == 0) { + printf("sex \n"); + printf(" Modify the sex of an account.\n"); + printf(" sex testname Male\n"); + } else if (strcmp(command, "state") == 0) { + printf("state \n"); + printf(" Change the state of an account.\n"); + printf(" 'new_state': state is the state of the packet 0x006a + 1.\n"); + printf(" The possibilities are:\n"); + printf(" 0 = Account ok\n"); + printf(" 1 = Unregistered ID\n"); + printf(" 2 = Incorrect Password\n"); + printf(" 3 = This ID is expired\n"); + printf(" 4 = Rejected from Server\n"); + printf(" 5 = You have been blocked by the GM Team\n"); + printf(" 6 = Your Game's EXE file is not the latest version\n"); + printf(" 7 = You are Prohibited to log in until...\n"); + printf(" 8 = Server is jammed due to over populated\n"); + printf(" 9 = No MSG\n"); + printf(" 100 = This ID has been totally erased\n"); + printf(" all other values are 'No MSG', then use state 9 please.\n"); + printf(" 'error_message_#7': message of the code error 6\n"); + printf(" = Your are Prohibited to log in until... (packet 0x006a)\n"); + } else if (strcmp(command, "timeadd") == 0) { + printf("timeadd \n"); + printf(" Adds or substracts time from the validity limit of an account.\n"); + printf(" Modifier is done as follows:\n"); + printf(" Adjustment value (-1, 1, +1, etc...)\n"); + printf(" Modified element:\n"); + printf(" a or y: year\n"); + printf(" m: month\n"); + printf(" j or d: day\n"); + printf(" h: hour\n"); + printf(" mn: minute\n"); + printf(" s: second\n"); + printf(" timeadd testname +1m-2mn1s-6y\n"); + printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf(" and 6 years at the same time.\n"); + printf("NOTE: You can not modify a unlimited validity limit.\n"); + printf(" If you want modify it, you want probably create a limited validity limit.\n"); + printf(" So, at first, you must set the validity limit to a date/time.\n"); + } else if (strcmp(command, "timeadd") == 0) { + printf("timeset yyyy/mm/dd [hh:mm:ss]\n"); + printf(" Changes the validity limit of an account.\n"); + printf(" Default time [hh:mm:ss]: 23:59:59.\n"); + printf("timeset 0\n"); + printf(" Gives an unlimited validity limit (0 = unlimited).\n"); + } else if (strcmp(command, "unban") == 0) { + printf("unban/unbanish \n"); + printf(" Remove the banishment of an account.\n"); + printf(" This command works like banset 0.\n"); + } else if (strcmp(command, "unblock") == 0) { + printf("unblock \n"); + printf(" Set state 0 (Account ok) to an account.\n"); + printf(" This command works like state 0.\n"); + } else if (strcmp(command, "version") == 0) { + printf("version\n"); + printf(" Display the version of the login-server.\n"); + } else if (strcmp(command, "who") == 0) { + printf("who \n"); + printf(" Displays complete information of an account.\n"); +// quit + } else if (strcmp(command, "quit") == 0 || + strcmp(command, "exit") == 0 || + strcmp(command, "end") == 0) { + printf("quit/end/exit\n"); + printf(" End of the program of administration.\n"); +// unknown command + } else { + if (strlen(command) > 0) + printf("Unknown command [%s] for help. Displaying of all commands.\n", command); + printf(" aide/help/? -- Display this help\n"); + printf(" aide/help/? [command] -- Display the help of the command\n"); + printf(" add -- Create an account with default email\n"); + printf(" ban/banish yyyy/mm/dd hh:mm:ss -- Change final date of a ban\n"); + printf(" banadd/ba -- Add or substract time from the final\n"); + printf(" example: ba apple +1m-2mn1s-2y date of a banishment of an account\n"); + printf(" banset/bs yyyy/mm/dd [hh:mm:ss] -- Change final date of a ban\n"); + printf(" banset/bs 0 -- Un-banish an account\n"); + printf(" block -- Set state 5 (blocked by the GM Team) to an account\n"); + printf(" check -- Check the validity of a password\n"); + printf(" create -- Create an account with email\n"); + printf(" del -- Remove an account\n"); + printf(" email -- Modify an email of an account\n"); + printf(" getcount -- Give the number of players online\n"); + printf(" gm [GM_level] -- Modify the GM level of an account\n"); + printf(" id -- Give the id of an account\n"); + printf(" info -- Display all information of an account\n"); + printf(" kami -- Sends a broadcast message (in yellow)\n"); + printf(" kamib -- Sends a broadcast message (in blue)\n"); + printf(" language -- Change the language of displaying.\n"); + printf(" list/ls [First_id [Last_id]] -- Display a list of accounts\n"); + printf(" listBan/lsBan [First_id [Last_id] ] -- Display a list of accounts\n"); + printf(" with state or banished\n"); + printf(" listGM/lsGM [First_id [Last_id]] -- Display a list of GM accounts\n"); + printf(" listOK/lsOK [First_id [Last_id] ] -- Display a list of accounts\n"); + printf(" without state and not banished\n"); + printf(" memo -- Modify the memo of an account\n"); + printf(" name -- Give the name of an account\n"); + printf(" passwd -- Change the password of an account\n"); + printf(" quit/end/exit -- End of the program of administation\n"); + printf(" reloadGM -- Reload GM configuration file\n"); + printf(" search -- Seek accounts\n"); +// printf(" search -e/-r/--expr/--regex -- Seek accounts by regular-expression\n"); + printf(" sex -- Modify the sex of an account\n"); + printf(" state -- Change the state\n"); + printf(" timeadd/ta -- Add or substract time from the\n"); + printf(" example: ta apple +1m-2mn1s-2y validity limit of an account\n"); + printf(" timeset/ts yyyy/mm/dd [hh:mm:ss] -- Change the validify limit\n"); + printf(" timeset/ts 0 -- Give a unlimited validity limit\n"); + printf(" unban/unbanish -- Remove the banishment of an account\n"); + printf(" unblock -- Set state 0 (Account ok) to an account\n"); + printf(" version -- Gives the version of the login-server\n"); + printf(" who -- Display all information of an account\n"); + printf(" who -- Display all information of an account\n"); + printf(" Note: To use spaces in an account name, type \"\" (or ').\n"); + } + } +} + +//----------------------------- +// Sub-function: add an account +//----------------------------- +int addaccount(char* param, int emailflag) { + char name[1023], sex[1023], email[1023], password[1023]; +// int i; + WFIFOHEAD(login_fd,91); + + memset(name, '\0', sizeof(name)); + memset(sex, '\0', sizeof(sex)); + memset(email, '\0', sizeof(email)); + memset(password, '\0', sizeof(password)); + + if (emailflag == 0) { // add command + if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, sex, password) < 2 && // password can be void + sscanf(param, "'%[^']' %s %[^\r\n]", name, sex, password) < 2 && // password can be void + sscanf(param, "%s %s %[^\r\n]", name, sex, password) < 2) { // password can be void + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte, un sexe et un mot de passe svp.\n"); + printf(" add nomtest Male motdepassetest\n"); + ladmin_log("Nombre incorrect de param鑼res pour cr馥r un compte (commande 'add')." RETCODE); + } else { + printf("Please input an account name, a sex and a password.\n"); + printf(" add testname Male testpass\n"); + ladmin_log("Incomplete parameters to create an account ('add' command)." RETCODE); + } + return 136; + } + strcpy(email, "a@a.com"); // default email + } else { // 1: create command + if (sscanf(param, "\"%[^\"]\" %s %s %[^\r\n]", name, sex, email, password) < 3 && // password can be void + sscanf(param, "'%[^']' %s %s %[^\r\n]", name, sex, email, password) < 3 && // password can be void + sscanf(param, "%s %s %s %[^\r\n]", name, sex, email, password) < 3) { // password can be void + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte, un sexe et un mot de passe svp.\n"); + printf(" create nomtest Male mo@mail.com motdepassetest\n"); + ladmin_log("Nombre incorrect de param鑼res pour cr馥r un compte (commande 'create')." RETCODE); + } else { + printf("Please input an account name, a sex and a password.\n"); + printf(" create testname Male my@mail.com testpass\n"); + ladmin_log("Incomplete parameters to create an account ('create' command)." RETCODE); + } + return 136; + } + } + if (verify_accountname(name) == 0) { + return 102; + } + +/* for(i = 0; name[i]; i++) { + if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_", name[i]) == NULL) { + if (defaultlanguage == 'F') { + printf("Caract鑽e interdit (%c) trouv dans le nom du compte (%d%s caract鑽e).\n", name[i], i+1, makeordinal(i+1)); + ladmin_log("Caract鑽e interdit (%c) trouv dans le nom du compte (%d%s caract鑽e)." RETCODE, name[i], i+1, makeordinal(i+1)); + } else { + printf("Illegal character (%c) found in the account name (%d%s character).\n", name[i], i+1, makeordinal(i+1)); + ladmin_log("Illegal character (%c) found in the account name (%d%s character)." RETCODE, name[i], i+1, makeordinal(i+1)); + } + return 101; + } + }*/ + + sex[0] = toupper(sex[0]); + if (strchr("MF", sex[0]) == NULL) { + if (defaultlanguage == 'F') { + printf("Sexe incorrect [%s]. Entrez M ou F svp.\n", sex); + ladmin_log("Sexe incorrect [%s]. Entrez M ou F svp." RETCODE, sex); + } else { + printf("Illegal gender [%s]. Please input M or F.\n", sex); + ladmin_log("Illegal gender [%s]. Please input M or F." RETCODE, sex); + } + return 103; + } + + if (strlen(email) < 3) { + if (defaultlanguage == 'F') { + printf("Email trop courte [%s]. Entrez une e-mail valide svp.\n", email); + ladmin_log("Email trop courte [%s]. Entrez une e-mail valide svp." RETCODE, email); + } else { + printf("Email is too short [%s]. Please input a valid e-mail.\n", email); + ladmin_log("Email is too short [%s]. Please input a valid e-mail." RETCODE, email); + } + return 109; + } + if (strlen(email) > 39) { + if (defaultlanguage == 'F') { + printf("Email trop longue [%s]. Entrez une e-mail de 39 caract鑽es maximum svp.\n", email); + ladmin_log("Email trop longue [%s]. Entrez une e-mail de 39 caract鑽es maximum svp." RETCODE, email); + } else { + printf("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", email); + ladmin_log("Email is too long [%s]. Please input an e-mail with 39 bytes at the most." RETCODE, email); + } + return 109; + } + if (e_mail_check(email) == 0) { + if (defaultlanguage == 'F') { + printf("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", email); + ladmin_log("Email incorrecte [%s]. Entrez une e-mail valide svp." RETCODE, email); + } else { + printf("Invalid email [%s]. Please input a valid e-mail.\n", email); + ladmin_log("Invalid email [%s]. Please input a valid e-mail." RETCODE, email); + } + return 109; + } + + if (strlen(password) == 0) { + if (typepasswd(password) == 0) + return 108; + } + if (verify_password(password) == 0) + return 104; + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour cr馥r un compte." RETCODE); + } else { + ladmin_log("Request to login-server to create an account." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7930; + memcpy(WFIFOP(login_fd,2), name, 24); + memcpy(WFIFOP(login_fd,26), password, 24); + WFIFOB(login_fd,50) = sex[0]; + memcpy(WFIFOP(login_fd,51), email, 40); + WFIFOSET(login_fd,91); + bytes_to_read = 1; + + return 0; +} + +//--------------------------------------------------------------------------------- +// Sub-function: Add/substract time to the final date of a banishment of an account +//--------------------------------------------------------------------------------- +int banaddaccount(char* param) { + char name[1023], modif[1023]; + int year, month, day, hour, minute, second; + char * p_modif; + int value, i; + WFIFOHEAD(login_fd,38); + + memset(name, '\0', sizeof(name)); + memset(modif, '\0', sizeof(modif)); + year = month = day = hour = minute = second = 0; + + if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, modif) < 2 && + sscanf(param, "'%[^']' %[^\r\n]", name, modif) < 2 && + sscanf(param, "%s %[^\r\n]", name, modif) < 2) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte et un modificateur svp.\n"); + printf(" banadd nomtest +1m-2mn1s-6y\n"); + printf(" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); + printf(" et 6 ans dans le m麥e temps.\n"); + ladmin_log("Nombre incorrect de param鑼res pour modifier la fin de ban d'un compte (commande 'banadd')." RETCODE); + } else { + printf("Please input an account name and a modifier.\n"); + printf(" : banadd testname +1m-2mn1s-6y\n"); + printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf(" and 6 years at the same time.\n"); + ladmin_log("Incomplete parameters to modify the ban date/time of an account ('banadd' command)." RETCODE); + } + return 136; + } + if (verify_accountname(name) == 0) { + return 102; + } + + // lowercase for modif + for (i = 0; modif[i]; i++) + modif[i] = tolower(modif[i]); + p_modif = modif; + while (strlen(p_modif) > 0) { + value = atoi(p_modif); + if (value == 0) { + p_modif++; + } else { + if (p_modif[0] == '-' || p_modif[0] == '+') + p_modif++; + while (strlen(p_modif) > 0 && p_modif[0] >= '0' && p_modif[0] <= '9') { + p_modif++; + } + if (p_modif[0] == 's') { + second = value; + p_modif++; + } else if (p_modif[0] == 'm' && p_modif[1] == 'n') { + minute = value; + p_modif += 2; + } else if (p_modif[0] == 'h') { + hour = value; + p_modif++; + } else if (p_modif[0] == 'd' || p_modif[0] == 'j') { + day = value; + p_modif += 2; + } else if (p_modif[0] == 'm') { + month = value; + p_modif++; + } else if (p_modif[0] == 'y' || p_modif[0] == 'a') { + year = value; + p_modif++; + } else { + p_modif++; + } + } + } + + if (defaultlanguage == 'F') { + printf(" ann馥: %d\n", year); + printf(" mois: %d\n", month); + printf(" jour: %d\n", day); + printf(" heure: %d\n", hour); + printf(" minute: %d\n", minute); + printf(" seconde: %d\n", second); + } else { + printf(" year: %d\n", year); + printf(" month: %d\n", month); + printf(" day: %d\n", day); + printf(" hour: %d\n", hour); + printf(" minute: %d\n", minute); + printf(" second: %d\n", second); + } + + if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0) { + if (defaultlanguage == 'F') { + printf("Vous devez entrer un ajustement avec cette commande, svp:\n"); + printf(" Valeur d'ajustement (-1, 1, +1, etc...)\n"); + printf(" Element modifi:\n"); + printf(" a ou y: ann馥\n"); + printf(" m: mois\n"); + printf(" j ou d: jour\n"); + printf(" h: heure\n"); + printf(" mn: minute\n"); + printf(" s: seconde\n"); + printf(" banadd nomtest +1m-2mn1s-6y\n"); + printf(" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); + printf(" et 6 ans dans le m麥e temps.\n"); + ladmin_log("Aucun ajustement n'est pas un ajustement (commande 'banadd')." RETCODE); + } else { + printf("Please give an adjustment with this command:\n"); + printf(" Adjustment value (-1, 1, +1, etc...)\n"); + printf(" Modified element:\n"); + printf(" a or y: year\n"); + printf(" m: month\n"); + printf(" j or d: day\n"); + printf(" h: hour\n"); + printf(" mn: minute\n"); + printf(" s: second\n"); + printf(" banadd testname +1m-2mn1s-6y\n"); + printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf(" and 6 years at the same time.\n"); + ladmin_log("No adjustment isn't an adjustment ('banadd' command)." RETCODE); + } + return 137; + } + if (year > 127 || year < -127) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement d'ann馥s correct (de -127 127), svp.\n"); + ladmin_log("Ajustement de l'ann馥 hors norme (commande 'banadd')." RETCODE); + } else { + printf("Please give a correct adjustment for the years (from -127 to 127).\n"); + ladmin_log("Abnormal adjustement for the year ('banadd' command)." RETCODE); + } + return 137; + } + if (month > 255 || month < -255) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement de mois correct (de -255 255), svp.\n"); + ladmin_log("Ajustement du mois hors norme (commande 'banadd')." RETCODE); + } else { + printf("Please give a correct adjustment for the months (from -255 to 255).\n"); + ladmin_log("Abnormal adjustement for the month ('banadd' command)." RETCODE); + } + return 137; + } + if (day > 32767 || day < -32767) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement de jours correct (de -32767 32767), svp.\n"); + ladmin_log("Ajustement des jours hors norme (commande 'banadd')." RETCODE); + } else { + printf("Please give a correct adjustment for the days (from -32767 to 32767).\n"); + ladmin_log("Abnormal adjustement for the days ('banadd' command)." RETCODE); + } + return 137; + } + if (hour > 32767 || hour < -32767) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement d'heures correct (de -32767 32767), svp.\n"); + ladmin_log("Ajustement des heures hors norme (commande 'banadd')." RETCODE); + } else { + printf("Please give a correct adjustment for the hours (from -32767 to 32767).\n"); + ladmin_log("Abnormal adjustement for the hours ('banadd' command)." RETCODE); + } + return 137; + } + if (minute > 32767 || minute < -32767) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement de minutes correct (de -32767 32767), svp.\n"); + ladmin_log("Ajustement des minutes hors norme (commande 'banadd')." RETCODE); + } else { + printf("Please give a correct adjustment for the minutes (from -32767 to 32767).\n"); + ladmin_log("Abnormal adjustement for the minutes ('banadd' command)." RETCODE); + } + return 137; + } + if (second > 32767 || second < -32767) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement de secondes correct (de -32767 32767), svp.\n"); + ladmin_log("Ajustement des secondes hors norme (commande 'banadd')." RETCODE); + } else { + printf("Please give a correct adjustment for the seconds (from -32767 to 32767).\n"); + ladmin_log("Abnormal adjustement for the seconds ('banadd' command)." RETCODE); + } + return 137; + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour modifier la date d'un bannissement." RETCODE); + } else { + ladmin_log("Request to login-server to modify a ban date/time." RETCODE); + } + + WFIFOW(login_fd,0) = 0x794c; + memcpy(WFIFOP(login_fd,2), name, 24); + WFIFOW(login_fd,26) = (short)year; + WFIFOW(login_fd,28) = (short)month; + WFIFOW(login_fd,30) = (short)day; + WFIFOW(login_fd,32) = (short)hour; + WFIFOW(login_fd,34) = (short)minute; + WFIFOW(login_fd,36) = (short)second; + WFIFOSET(login_fd,38); + bytes_to_read = 1; + + return 0; +} + +//----------------------------------------------------------------------- +// Sub-function of sub-function banaccount, unbanaccount or bansetaccount +// Set the final date of a banishment of an account +//----------------------------------------------------------------------- +int bansetaccountsub(char* name, char* date, char* time) { + int year, month, day, hour, minute, second; + time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) + struct tm *tmtime; + WFIFOHEAD(login_fd,30); + + year = month = day = hour = minute = second = 0; + ban_until_time = 0; + tmtime = localtime(&ban_until_time); // initialize + + if (verify_accountname(name) == 0) { + return 102; + } + + if (atoi(date) != 0 && + ((sscanf(date, "%d/%d/%d", &year, &month, &day) < 3 && + sscanf(date, "%d-%d-%d", &year, &month, &day) < 3 && + sscanf(date, "%d.%d.%d", &year, &month, &day) < 3) || + sscanf(time, "%d:%d:%d", &hour, &minute, &second) < 3)) { + if (defaultlanguage == 'F') { + printf("Entrez une date et une heure svp (format: aaaa/mm/jj hh:mm:ss).\n"); + printf("Vous pouvez aussi mettre 0 la place si vous utilisez la commande 'banset'.\n"); + ladmin_log("Format incorrect pour la date/heure (commande'banset' ou 'ban')." RETCODE); + } else { + printf("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n"); + printf("You can imput 0 instead of if you use 'banset' command.\n"); + ladmin_log("Invalid format for the date/time ('banset' or 'ban' command)." RETCODE); + } + return 102; + } + + if (atoi(date) == 0) { + ban_until_time = 0; + } else { + if (year < 70) { + year = year + 100; + } + if (year >= 1900) { + year = year - 1900; + } + if (month < 1 || month > 12) { + if (defaultlanguage == 'F') { + printf("Entrez un mois correct svp (entre 1 et 12).\n"); + ladmin_log("Mois incorrect pour la date (command 'banset' ou 'ban')." RETCODE); + } else { + printf("Please give a correct value for the month (from 1 to 12).\n"); + ladmin_log("Invalid month for the date ('banset' or 'ban' command)." RETCODE); + } + return 102; + } + month = month - 1; + if (day < 1 || day > 31) { + if (defaultlanguage == 'F') { + printf("Entrez un jour correct svp (entre 1 et 31).\n"); + ladmin_log("Jour incorrect pour la date (command 'banset' ou 'ban')." RETCODE); + } else { + printf("Please give a correct value for the day (from 1 to 31).\n"); + ladmin_log("Invalid day for the date ('banset' or 'ban' command)." RETCODE); + } + return 102; + } + if (((month == 3 || month == 5 || month == 8 || month == 10) && day > 30) || + (month == 1 && day > 29)) { + if (defaultlanguage == 'F') { + printf("Entrez un jour correct en fonction du mois (%d) svp.\n", month); + ladmin_log("Jour incorrect pour ce mois correspondant (command 'banset' ou 'ban')." RETCODE); + } else { + printf("Please give a correct value for a day of this month (%d).\n", month); + ladmin_log("Invalid day for this month ('banset' or 'ban' command)." RETCODE); + } + return 102; + } + if (hour < 0 || hour > 23) { + if (defaultlanguage == 'F') { + printf("Entrez une heure correcte svp (entre 0 et 23).\n"); + ladmin_log("Heure incorrecte pour l'heure (command 'banset' ou 'ban')." RETCODE); + } else { + printf("Please give a correct value for the hour (from 0 to 23).\n"); + ladmin_log("Invalid hour for the time ('banset' or 'ban' command)." RETCODE); + } + return 102; + } + if (minute < 0 || minute > 59) { + if (defaultlanguage == 'F') { + printf("Entrez des minutes correctes svp (entre 0 et 59).\n"); + ladmin_log("Minute incorrecte pour l'heure (command 'banset' ou 'ban')." RETCODE); + } else { + printf("Please give a correct value for the minutes (from 0 to 59).\n"); + ladmin_log("Invalid minute for the time ('banset' or 'ban' command)." RETCODE); + } + return 102; + } + if (second < 0 || second > 59) { + if (defaultlanguage == 'F') { + printf("Entrez des secondes correctes svp (entre 0 et 59).\n"); + ladmin_log("Seconde incorrecte pour l'heure (command 'banset' ou 'ban')." RETCODE); + } else { + printf("Please give a correct value for the seconds (from 0 to 59).\n"); + ladmin_log("Invalid second for the time ('banset' or 'ban' command)." RETCODE); + } + return 102; + } + tmtime->tm_year = year; + tmtime->tm_mon = month; + tmtime->tm_mday = day; + tmtime->tm_hour = hour; + tmtime->tm_min = minute; + tmtime->tm_sec = second; + tmtime->tm_isdst = -1; // -1: no winter/summer time modification + ban_until_time = mktime(tmtime); + if (ban_until_time == -1) { + if (defaultlanguage == 'F') { + printf("Date incorrecte.\n"); + printf("Entrez une date et une heure svp (format: aaaa/mm/jj hh:mm:ss).\n"); + printf("Vous pouvez aussi mettre 0 la place si vous utilisez la commande 'banset'.\n"); + ladmin_log("Date incorrecte. (command 'banset' ou 'ban')." RETCODE); + } else { + printf("Invalid date.\n"); + printf("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n"); + printf("You can imput 0 instead of if you use 'banset' command.\n"); + ladmin_log("Invalid date. ('banset' or 'ban' command)." RETCODE); + } + return 102; + } + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour fixer un ban." RETCODE); + } else { + ladmin_log("Request to login-server to set a ban." RETCODE); + } + + WFIFOW(login_fd,0) = 0x794a; + memcpy(WFIFOP(login_fd,2), name, 24); + WFIFOL(login_fd,26) = (int)ban_until_time; + WFIFOSET(login_fd,30); + bytes_to_read = 1; + + return 0; +} + +//--------------------------------------------------------------------- +// Sub-function: Set the final date of a banishment of an account (ban) +//--------------------------------------------------------------------- +int banaccount(char* param) { + char name[1023], date[1023], time[1023]; + + memset(name, '\0', sizeof(name)); + memset(date, '\0', sizeof(date)); + memset(time, '\0', sizeof(time)); + + if (sscanf(param, "%s %s \"%[^\"]\"", date, time, name) < 3 && + sscanf(param, "%s %s '%[^']'", date, time, name) < 3 && + sscanf(param, "%s %s %[^\r\n]", date, time, name) < 3) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte, une date et une heure svp.\n"); + printf(": banset aaaa/mm/jj [hh:mm:ss]\n"); + printf(" banset 0 (0 = d-bani)\n"); + printf(" ban/banish aaaa/mm/jj hh:mm:ss \n"); + printf(" unban/unbanish \n"); + printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Nombre incorrect de param鑼res pour fixer un ban (commande 'banset' ou 'ban')." RETCODE); + } else { + printf("Please input an account name, a date and a hour.\n"); + printf(": banset yyyy/mm/dd [hh:mm:ss]\n"); + printf(" banset 0 (0 = un-banished)\n"); + printf(" ban/banish yyyy/mm/dd hh:mm:ss \n"); + printf(" unban/unbanish \n"); + printf(" Default time [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Incomplete parameters to set a ban ('banset' or 'ban' command)." RETCODE); + } + return 136; + } + + return bansetaccountsub(name, date, time); +} + +//------------------------------------------------------------------------ +// Sub-function: Set the final date of a banishment of an account (banset) +//------------------------------------------------------------------------ +int bansetaccount(char* param) { + char name[1023], date[1023], time[1023]; + + memset(name, '\0', sizeof(name)); + memset(date, '\0', sizeof(date)); + memset(time, '\0', sizeof(time)); + + if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void + sscanf(param, "'%[^']' %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void + sscanf(param, "%s %s %[^\r\n]", name, date, time) < 2) { // if date = 0, time can be void + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte, une date et une heure svp.\n"); + printf(": banset aaaa/mm/jj [hh:mm:ss]\n"); + printf(" banset 0 (0 = d-bani)\n"); + printf(" ban/banish aaaa/mm/jj hh:mm:ss \n"); + printf(" unban/unbanish \n"); + printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Nombre incorrect de param鑼res pour fixer un ban (commande 'banset' ou 'ban')." RETCODE); + } else { + printf("Please input an account name, a date and a hour.\n"); + printf(": banset yyyy/mm/dd [hh:mm:ss]\n"); + printf(" banset 0 (0 = un-banished)\n"); + printf(" ban/banish yyyy/mm/dd hh:mm:ss \n"); + printf(" unban/unbanish \n"); + printf(" Default time [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Incomplete parameters to set a ban ('banset' or 'ban' command)." RETCODE); + } + return 136; + } + + if (time[0] == '\0') + strcpy(time, "23:59:59"); + + return bansetaccountsub(name, date, time); +} + +//------------------------------------------------- +// Sub-function: unbanishment of an account (unban) +//------------------------------------------------- +int unbanaccount(char* param) { + char name[1023]; + + memset(name, '\0', sizeof(name)); + + if (strlen(param) == 0 || + (sscanf(param, "\"%[^\"]\"", name) < 1 && + sscanf(param, "'%[^']'", name) < 1 && + sscanf(param, "%[^\r\n]", name) < 1) || + strlen(name) == 0) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte svp.\n"); + printf(": banset aaaa/mm/jj [hh:mm:ss]\n"); + printf(" banset 0 (0 = d-bani)\n"); + printf(" ban/banish aaaa/mm/jj hh:mm:ss \n"); + printf(" unban/unbanish \n"); + printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Nombre incorrect de param鑼res pour fixer un ban (commande 'unban')." RETCODE); + } else { + printf("Please input an account name.\n"); + printf(": banset yyyy/mm/dd [hh:mm:ss]\n"); + printf(" banset 0 (0 = un-banished)\n"); + printf(" ban/banish yyyy/mm/dd hh:mm:ss \n"); + printf(" unban/unbanish \n"); + printf(" Default time [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Incomplete parameters to set a ban ('unban' command)." RETCODE); + } + return 136; + } + + return bansetaccountsub(name, "0", ""); +} + +//--------------------------------------------------------- +// Sub-function: Asking to check the validity of a password +// (Note: never send back a password with login-server!! security of passwords) +//--------------------------------------------------------- +int checkaccount(char* param) { + char name[1023], password[1023]; + WFIFOHEAD(login_fd,50); + + memset(name, '\0', sizeof(name)); + memset(password, '\0', sizeof(password)); + + if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, password) < 1 && // password can be void + sscanf(param, "'%[^']' %[^\r\n]", name, password) < 1 && // password can be void + sscanf(param, "%s %[^\r\n]", name, password) < 1) { // password can be void + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte svp.\n"); + printf(" check testname motdepasse\n"); + ladmin_log("Nombre incorrect de param鑼res pour tester le mot d'un passe d'un compte (commande 'check')." RETCODE); + } else { + printf("Please input an account name.\n"); + printf(" check testname password\n"); + ladmin_log("Incomplete parameters to check the password of an account ('check' command)." RETCODE); + } + return 136; + } + + if (verify_accountname(name) == 0) { + return 102; + } + + if (strlen(password) == 0) { + if (typepasswd(password) == 0) + return 134; + } + if (verify_password(password) == 0) + return 131; + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour test un mot de passe." RETCODE); + } else { + ladmin_log("Request to login-server to check a password." RETCODE); + } + + WFIFOW(login_fd,0) = 0x793a; + memcpy(WFIFOP(login_fd,2), name, 24); + memcpy(WFIFOP(login_fd,26), password, 24); + WFIFOSET(login_fd,50); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------------ +// Sub-function: Asking for deletion of an account +//------------------------------------------------ +int delaccount(char* param) { + char name[1023]; + char letter; + char confirm[1023]; + int i; + WFIFOHEAD(login_fd,26); + + memset(name, '\0', sizeof(name)); + + if (strlen(param) == 0 || + (sscanf(param, "\"%[^\"]\"", name) < 1 && + sscanf(param, "'%[^']'", name) < 1 && + sscanf(param, "%[^\r\n]", name) < 1) || + strlen(name) == 0) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte svp.\n"); + printf(" del nomtestasupprimer\n"); + ladmin_log("Aucun nom donn pour supprimer un compte (commande 'delete')." RETCODE); + } else { + printf("Please input an account name.\n"); + printf(" del testnametodelete\n"); + ladmin_log("No name given to delete an account ('delete' command)." RETCODE); + } + return 136; + } + + if (verify_accountname(name) == 0) { + return 102; + } + + memset(confirm, '\0', sizeof(confirm)); + while ((confirm[0] != 'o' || defaultlanguage != 'F') && confirm[0] != 'n' && (confirm[0] != 'y' || defaultlanguage == 'F')) { + if (defaultlanguage == 'F') + printf("\033[1;36m ** Etes-vous vraiment sr de vouloir SUPPRIMER le compte [$userid]? (o/n) > \033[0m"); + else + printf("\033[1;36m ** Are you really sure to DELETE account [$userid]? (y/n) > \033[0m"); + fflush(stdout); + memset(confirm, '\0', sizeof(confirm)); + i = 0; + while ((letter = getchar()) != '\n') + confirm[i++] = letter; + } + + if (confirm[0] == 'n') { + if (defaultlanguage == 'F') { + printf("Suppression annul馥.\n"); + ladmin_log("Suppression annul馥 par l'utilisateur (commande 'delete')." RETCODE); + } else { + printf("Deletion canceled.\n"); + ladmin_log("Deletion canceled by user ('delete' command)." RETCODE); + } + return 121; + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour d騁ruire un compte." RETCODE); + } else { + ladmin_log("Request to login-server to delete an acount." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7932; + memcpy(WFIFOP(login_fd,2), name, 24); + WFIFOSET(login_fd,26); + bytes_to_read = 1; + + return 0; +} + +//---------------------------------------------------------- +// Sub-function: Asking to modification of an account e-mail +//---------------------------------------------------------- +int changeemail(char* param) { + char name[1023], email[1023]; + WFIFOHEAD(login_fd,66); + + memset(name, '\0', sizeof(name)); + memset(email, '\0', sizeof(email)); + + if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, email) < 2 && + sscanf(param, "'%[^']' %[^\r\n]", name, email) < 2 && + sscanf(param, "%s %[^\r\n]", name, email) < 2) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte et une email svp.\n"); + printf(" email testname nouveauemail\n"); + ladmin_log("Nombre incorrect de param鑼res pour changer l'email d'un compte (commande 'email')." RETCODE); + } else { + printf("Please input an account name and an email.\n"); + printf(" email testname newemail\n"); + ladmin_log("Incomplete parameters to change the email of an account ('email' command)." RETCODE); + } + return 136; + } + + if (verify_accountname(name) == 0) { + return 102; + } + + if (strlen(email) < 3) { + if (defaultlanguage == 'F') { + printf("Email trop courte [%s]. Entrez une e-mail valide svp.\n", email); + ladmin_log("Email trop courte [%s]. Entrez une e-mail valide svp." RETCODE, email); + } else { + printf("Email is too short [%s]. Please input a valid e-mail.\n", email); + ladmin_log("Email is too short [%s]. Please input a valid e-mail." RETCODE, email); + } + return 109; + } + if (strlen(email) > 39) { + if (defaultlanguage == 'F') { + printf("Email trop longue [%s]. Entrez une e-mail de 39 caract鑽es maximum svp.\n", email); + ladmin_log("Email trop longue [%s]. Entrez une e-mail de 39 caract鑽es maximum svp." RETCODE, email); + } else { + printf("Email is too long [%s]. Please input an e-mail with 39 bytes at the most.\n", email); + ladmin_log("Email is too long [%s]. Please input an e-mail with 39 bytes at the most." RETCODE, email); + } + return 109; + } + if (e_mail_check(email) == 0) { + if (defaultlanguage == 'F') { + printf("Email incorrecte [%s]. Entrez une e-mail valide svp.\n", email); + ladmin_log("Email incorrecte [%s]. Entrez une e-mail valide svp." RETCODE, email); + } else { + printf("Invalid email [%s]. Please input a valid e-mail.\n", email); + ladmin_log("Invalid email [%s]. Please input a valid e-mail." RETCODE, email); + } + return 109; + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer une email." RETCODE); + } else { + ladmin_log("Request to login-server to change an email." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7940; + memcpy(WFIFOP(login_fd,2), name, 24); + memcpy(WFIFOP(login_fd,26), email, 40); + WFIFOSET(login_fd,66); + bytes_to_read = 1; + + return 0; +} + +//----------------------------------------------------- +// Sub-function: Asking of the number of online players +//----------------------------------------------------- +int getlogincount(void) { + WFIFOHEAD(login_fd,2); + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir le nombre de joueurs en jeu." RETCODE); + } else { + ladmin_log("Request to login-server to obtain the # of online players." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7938; + WFIFOSET(login_fd,2); + bytes_to_read = 1; + + return 0; +} + +//---------------------------------------------------------- +// Sub-function: Asking to modify the GM level of an account +//---------------------------------------------------------- +int changegmlevel(char* param) { + char name[1023]; + int GM_level; + WFIFOHEAD(login_fd,27); + + memset(name, '\0', sizeof(name)); + GM_level = 0; + + if (sscanf(param, "\"%[^\"]\" %d", name, &GM_level) < 1 && + sscanf(param, "'%[^']' %d", name, &GM_level) < 1 && + sscanf(param, "%s %d", name, &GM_level) < 1) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte et un niveau de GM svp.\n"); + printf(" gm nomtest 80\n"); + ladmin_log("Nombre incorrect de param鑼res pour changer le Niveau de GM d'un compte (commande 'gm')." RETCODE); + } else { + printf("Please input an account name and a GM level.\n"); + printf(" gm testname 80\n"); + ladmin_log("Incomplete parameters to change the GM level of an account ('gm' command)." RETCODE); + } + return 136; + } + + if (verify_accountname(name) == 0) { + return 102; + } + + if (GM_level < 0 || GM_level > 99) { + if (defaultlanguage == 'F') { + printf("Niveau de GM incorrect [%d]. Entrez une valeur de 0 99 svp.\n", GM_level); + ladmin_log("Niveau de GM incorrect [%d]. La valeur peut 黎re de 0 99." RETCODE, GM_level); + } else { + printf("Illegal GM level [%d]. Please input a value from 0 to 99.\n", GM_level); + ladmin_log("Illegal GM level [%d]. The value can be from 0 to 99." RETCODE, GM_level); + } + return 103; + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer un niveau de GM." RETCODE); + } else { + ladmin_log("Request to login-server to change a GM level." RETCODE); + } + + WFIFOW(login_fd,0) = 0x793e; + memcpy(WFIFOP(login_fd,2), name, 24); + WFIFOB(login_fd,26) = GM_level; + WFIFOSET(login_fd,27); + bytes_to_read = 1; + + return 0; +} + +//--------------------------------------------- +// Sub-function: Asking to obtain an account id +//--------------------------------------------- +int idaccount(char* param) { + char name[1023]; + WFIFOHEAD(login_fd,26); + + memset(name, '\0', sizeof(name)); + + if (strlen(param) == 0 || + (sscanf(param, "\"%[^\"]\"", name) < 1 && + sscanf(param, "'%[^']'", name) < 1 && + sscanf(param, "%[^\r\n]", name) < 1) || + strlen(name) == 0) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte svp.\n"); + printf(" id nomtest\n"); + ladmin_log("Aucun nom donn pour rechecher l'id d'un compte (commande 'id')." RETCODE); + } else { + printf("Please input an account name.\n"); + printf(" id testname\n"); + ladmin_log("No name given to search an account id ('id' command)." RETCODE); + } + return 136; + } + + if (verify_accountname(name) == 0) { + return 102; + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour connatre l'id d'un compte." RETCODE); + } else { + ladmin_log("Request to login-server to know an account id." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7944; + memcpy(WFIFOP(login_fd,2), name, 24); + WFIFOSET(login_fd,26); + bytes_to_read = 1; + + return 0; +} + +//---------------------------------------------------------------------------- +// Sub-function: Asking to displaying information about an account (by its id) +//---------------------------------------------------------------------------- +int infoaccount(int account_id) { + WFIFOHEAD(login_fd,6); + if (account_id < 0) { + if (defaultlanguage == 'F') { + printf("Entrez un id ayant une valeur positive svp.\n"); + ladmin_log("Une valeur n馮ative a 騁 donn pour trouver le compte." RETCODE); + } else { + printf("Please input a positive value for the id.\n"); + ladmin_log("Negative value was given to found the account." RETCODE); + } + return 136; + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir le information d'un compte (par l'id)." RETCODE); + } else { + ladmin_log("Request to login-server to obtain information about an account (by its id)." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7954; + WFIFOL(login_fd,2) = account_id; + WFIFOSET(login_fd,6); + bytes_to_read = 1; + + return 0; +} + +//--------------------------------------- +// Sub-function: Send a broadcast message +//--------------------------------------- +int sendbroadcast(short type, char* message) { + int len = strlen(message); + WFIFOHEAD(login_fd,9+len); + if (len == 0) { + if (defaultlanguage == 'F') { + printf("Entrez un message svp.\n"); + if (type == 0) { + printf(" kami un message\n"); + } else { + printf(" kamib un message\n"); + } + ladmin_log("Le message est vide (commande 'kami(b)')." RETCODE); + } else { + printf("Please input a message.\n"); + if (type == 0) { + printf(" kami a message\n"); + } else { + printf(" kamib a message\n"); + } + ladmin_log("The message is void ('kami(b)' command)." RETCODE); + } + return 136; + } + len++; //+'\0' + WFIFOW(login_fd,0) = 0x794e; + WFIFOW(login_fd,2) = type; + WFIFOL(login_fd,4) = len; + memcpy(WFIFOP(login_fd,8), message, len); + WFIFOSET(login_fd,8+len); + bytes_to_read = 1; + + return 0; +} + +//-------------------------------------------- +// Sub-function: Change language of displaying +//-------------------------------------------- +int changelanguage(char* language) { + if (strlen(language) == 0) { + if (defaultlanguage == 'F') { + printf("Entrez une langue svp.\n"); + printf(" language english\n"); + printf(" language fran軋is\n"); + ladmin_log("La langue est vide (commande 'language')." RETCODE); + } else { + printf("Please input a language.\n"); + printf(" language english\n"); + printf(" language fran軋is\n"); + ladmin_log("The language is void ('language' command)." RETCODE); + } + return 136; + } + + language[0] = toupper(language[0]); + if (language[0] == 'F' || language[0] == 'E') { + defaultlanguage = language[0]; + if (defaultlanguage == 'F') { + printf("Changement de la langue d'affichage en Fran軋is.\n"); + ladmin_log("Changement de la langue d'affichage en Fran軋is." RETCODE); + } else { + printf("Displaying language changed to English.\n"); + ladmin_log("Displaying language changed to English." RETCODE); + } + } else { + if (defaultlanguage == 'F') { + printf("Langue non param騁r馥 (langues possibles: 'Fran軋is' ou 'English').\n"); + ladmin_log("Langue non param騁r馥 (Fran軋is ou English n馗essaire)." RETCODE); + } else { + printf("Undefined language (possible languages: Fran軋is or English).\n"); + ladmin_log("Undefined language (must be Fran軋is or English)." RETCODE); + } + } + + return 0; +} + +//-------------------------------------------------------- +// Sub-function: Asking to Displaying of the accounts list +//-------------------------------------------------------- +int listaccount(char* param, int type) { +//int list_first, list_last, list_type; // parameter to display a list of accounts + int i; + WFIFOHEAD(login_fd,10); + + list_type = type; + + // set default values + list_first = 0; + list_last = 0; + + if (list_type == 1) { // if listgm + // get all accounts = use default + } else if (list_type == 2) { // if search + for (i = 0; param[i]; i++) + param[i] = tolower(param[i]); + // get all accounts = use default + } else if (list_type == 3) { // if listban + // get all accounts = use default + } else if (list_type == 4) { // if listok + // get all accounts = use default + } else { // if list (list_type == 0) + switch(sscanf(param, "%d %d", &list_first, &list_last)) { + case 0: + // get all accounts = use default + break; + case 1: + list_last = 0; + // use tests of the following value + default: + if (list_first < 0) + list_first = 0; + if (list_last < list_first || list_last < 0) + list_last = 0; + break; + } + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir la liste des comptes de %d %d." RETCODE, list_first, list_last); + } else { + ladmin_log("Request to login-server to obtain the list of accounts from %d to %d." RETCODE, list_first, list_last); + } + + WFIFOW(login_fd,0) = 0x7920; + WFIFOL(login_fd,2) = list_first; + WFIFOL(login_fd,6) = list_last; + WFIFOSET(login_fd,10); + bytes_to_read = 1; + + // 0123456789 01 01234567890123456789012301234 012345 0123456789012345678901234567 + if (defaultlanguage == 'F') { + printf(" id_compte GM nom_utilisateur sexe count statut\n"); + } else { + printf("account_id GM user_name sex count state\n"); + } + printf("-------------------------------------------------------------------------------\n"); + list_count = 0; + + return 0; +} + +//-------------------------------------------- +// Sub-function: Asking to modify a memo field +//-------------------------------------------- +int changememo(char* param) { + char name[1023], memo[1023]; + WFIFOHEAD(login_fd,28+255); + + memset(name, '\0', sizeof(name)); + memset(memo, '\0', sizeof(memo)); + + if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, memo) < 1 && // memo can be void + sscanf(param, "'%[^']' %[^\r\n]", name, memo) < 1 && // memo can be void + sscanf(param, "%s %[^\r\n]", name, memo) < 1) { // memo can be void + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte et un m駑o svp.\n"); + printf(" memo nomtest nouveau memo\n"); + ladmin_log("Nombre incorrect de param鑼res pour changer le m駑o d'un compte (commande 'email')." RETCODE); + } else { + printf("Please input an account name and a memo.\n"); + printf(" memo testname new memo\n"); + ladmin_log("Incomplete parameters to change the memo of an account ('email' command)." RETCODE); + } + return 136; + } + + if (verify_accountname(name) == 0) { + return 102; + } + + if (strlen(memo) > 254) { + if (defaultlanguage == 'F') { + printf("M駑o trop long (%d caract鑽es).\n", strlen(memo)); + printf("Entrez un m駑o de 254 caract鑽es maximum svp.\n"); + ladmin_log("M駑o trop long (%d caract鑽es). Entrez un m駑o de 254 caract鑽es maximum svp." RETCODE, strlen(memo)); + } else { + printf("Memo is too long (%d characters).\n", strlen(memo)); + printf("Please input a memo of 254 bytes at the maximum.\n"); + ladmin_log("Email is too long (%d characters). Please input a memo of 254 bytes at the maximum." RETCODE, strlen(memo)); + } + return 102; + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer un m駑o." RETCODE); + } else { + ladmin_log("Request to login-server to change a memo." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7942; + memcpy(WFIFOP(login_fd,2), name, 24); + WFIFOW(login_fd,26) = strlen(memo); + if (strlen(memo) > 0) + memcpy(WFIFOP(login_fd,28), memo, strlen(memo)); + WFIFOSET(login_fd,28+strlen(memo)); + bytes_to_read = 1; + + return 0; +} + +//----------------------------------------------- +// Sub-function: Asking to obtain an account name +//----------------------------------------------- +int nameaccount(int id) { + WFIFOHEAD(login_fd,6); + if (id < 0) { + if (defaultlanguage == 'F') { + printf("Entrez un id ayant une valeur positive svp.\n"); + ladmin_log("Id n馮atif donn pour rechecher le nom d'un compte (commande 'name')." RETCODE); + } else { + printf("Please input a positive value for the id.\n"); + ladmin_log("Negativ id given to search an account name ('name' command)." RETCODE); + } + return 136; + } + + if (defaultlanguage == 'F') + ladmin_log("Envoi d'un requ黎e au serveur de logins pour connatre le nom d'un compte." RETCODE); + else + ladmin_log("Request to login-server to know an account name." RETCODE); + + WFIFOW(login_fd,0) = 0x7946; + WFIFOL(login_fd,2) = id; + WFIFOSET(login_fd,6); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------ +// Sub-function: Asking to modify a password +// (Note: never send back a password with login-server!! security of passwords) +//------------------------------------------ +int changepasswd(char* param) { + char name[1023], password[1023]; + WFIFOHEAD(login_fd,50); + + memset(name, '\0', sizeof(name)); + memset(password, '\0', sizeof(password)); + + if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, password) < 1 && + sscanf(param, "'%[^']' %[^\r\n]", name, password) < 1 && + sscanf(param, "%s %[^\r\n]", name, password) < 1) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte svp.\n"); + printf(" passwd nomtest nouveaumotdepasse\n"); + ladmin_log("Nombre incorrect de param鑼res pour changer le mot d'un passe d'un compte (commande 'password')." RETCODE); + } else { + printf("Please input an account name.\n"); + printf(" passwd testname newpassword\n"); + ladmin_log("Incomplete parameters to change the password of an account ('password' command)." RETCODE); + } + return 136; + } + + if (verify_accountname(name) == 0) { + return 102; + } + + if (strlen(password) == 0) { + if (typepasswd(password) == 0) + return 134; + } + if (verify_password(password) == 0) + return 131; + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer un mot de passe." RETCODE); + } else { + ladmin_log("Request to login-server to change a password." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7934; + memcpy(WFIFOP(login_fd,2), name, 24); + memcpy(WFIFOP(login_fd,26), password, 24); + WFIFOSET(login_fd,50); + bytes_to_read = 1; + + return 0; +} + +//---------------------------------------------------------------------- +// Sub-function: Request to login-server to reload GM configuration file +// this function have no answer +//---------------------------------------------------------------------- +int reloadGM(void) { + WFIFOHEAD(login_fd,2); + WFIFOW(login_fd,0) = 0x7955; + WFIFOSET(login_fd,2); + bytes_to_read = 0; + + if (defaultlanguage == 'F') { + ladmin_log("Demande de recharger le fichier de configuration des GM envoy馥." RETCODE); + printf("Demande de recharger le fichier de configuration des GM envoy馥.\n"); + printf("V駻ifiez les comptes GM actuels (apr鑚 rechargement):\n"); + } else { + ladmin_log("Request to reload the GM configuration file sended." RETCODE); + printf("Request to reload the GM configuration file sended.\n"); + printf("Check the actual GM accounts (after reloading):\n"); + } + listaccount(parameters, 1); // 1: to list only GM + + return 180; +} + +//----------------------------------------------------- +// Sub-function: Asking to modify the sex of an account +//----------------------------------------------------- +int changesex(char* param) { + char name[1023], sex[1023]; + WFIFOHEAD(login_fd,27); + + memset(name, '\0', sizeof(name)); + memset(sex, '\0', sizeof(sex)); + + if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, sex) < 2 && + sscanf(param, "'%[^']' %[^\r\n]", name, sex) < 2 && + sscanf(param, "%s %[^\r\n]", name, sex) < 2) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte et un sexe svp.\n"); + printf(" sex nomtest Male\n"); + ladmin_log("Nombre incorrect de param鑼res pour changer le sexe d'un compte (commande 'sex')." RETCODE); + } else { + printf("Please input an account name and a sex.\n"); + printf(" sex testname Male\n"); + ladmin_log("Incomplete parameters to change the sex of an account ('sex' command)." RETCODE); + } + return 136; + } + + if (verify_accountname(name) == 0) { + return 102; + } + + sex[0] = toupper(sex[0]); + if (strchr("MF", sex[0]) == NULL) { + if (defaultlanguage == 'F') { + printf("Sexe incorrect [%s]. Entrez M ou F svp.\n", sex); + ladmin_log("Sexe incorrect [%s]. Entrez M ou F svp." RETCODE, sex); + } else { + printf("Illegal gender [%s]. Please input M or F.\n", sex); + ladmin_log("Illegal gender [%s]. Please input M or F." RETCODE, sex); + } + return 103; + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer un sexe." RETCODE); + } else { + ladmin_log("Request to login-server to change a sex." RETCODE); + } + + WFIFOW(login_fd,0) = 0x793c; + memcpy(WFIFOP(login_fd,2), name, 24); + WFIFOB(login_fd,26) = sex[0]; + WFIFOSET(login_fd,27); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------------------------------------- +// Sub-function of sub-function changestate, blockaccount or unblockaccount +// Asking to modify the state of an account +//------------------------------------------------------------------------- +int changestatesub(char* name, int state, char* error_message7) { + char error_message[1023]; // need to use, because we can modify error_message7 + WFIFOHEAD(login_fd,50); + + memset(error_message, '\0', sizeof(error_message)); + strncpy(error_message, error_message7, sizeof(error_message)-1); + + if ((state < 0 || state > 9) && state != 100) { // Valid values: 0: ok, or value of the 0x006a packet + 1 + if (defaultlanguage == 'F') { + printf("Entrez une des statuts suivantes svp:\n"); + printf(" 0 = Compte ok 6 = Your Game's EXE file is not the latest version\n"); + } else { + printf("Please input one of these states:\n"); + printf(" 0 = Account ok 6 = Your Game's EXE file is not the latest version\n"); + } + printf(" 1 = Unregistered ID 7 = You are Prohibited to log in until + message\n"); + printf(" 2 = Incorrect Password 8 = Server is jammed due to over populated\n"); + printf(" 3 = This ID is expired 9 = No MSG\n"); + printf(" 4 = Rejected from Server 100 = This ID has been totally erased\n"); + printf(" 5 = You have been blocked by the GM Team\n"); + if (defaultlanguage == 'F') { + printf(" state nomtest 5\n"); + printf(" state nomtest 7 fin de votre ban\n"); + printf(" block \n"); + printf(" unblock \n"); + ladmin_log("Valeur incorrecte pour le statut d'un compte (commande 'state', 'block' ou 'unblock')." RETCODE); + } else { + printf(" state testname 5\n"); + printf(" state testname 7 end of your ban\n"); + printf(" block \n"); + printf(" unblock \n"); + ladmin_log("Invalid value for the state of an account ('state', 'block' or 'unblock' command)." RETCODE); + } + return 151; + } + + if (verify_accountname(name) == 0) { + return 102; + } + + if (state != 7) { + strcpy(error_message, "-"); + } else { + if (strlen(error_message) < 1) { + if (defaultlanguage == 'F') { + printf("Message d'erreur trop court. Entrez un message de 1-19 caract鑽es.\n"); + ladmin_log("Message d'erreur trop court. Entrez un message de 1-19 caract鑽es." RETCODE); + } else { + printf("Error message is too short. Please input a message of 1-19 bytes.\n"); + ladmin_log("Error message is too short. Please input a message of 1-19 bytes." RETCODE); + } + return 102; + } + if (strlen(error_message) > 19) { + if (defaultlanguage == 'F') { + printf("Message d'erreur trop long. Entrez un message de 1-19 caract鑽es.\n"); + ladmin_log("Message d'erreur trop long. Entrez un message de 1-19 caract鑽es." RETCODE); + } else { + printf("Error message is too long. Please input a message of 1-19 bytes.\n"); + ladmin_log("Error message is too long. Please input a message of 1-19 bytes." RETCODE); + } + return 102; + } + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour changer un statut." RETCODE); + } else { + ladmin_log("Request to login-server to change a state." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7936; + memcpy(WFIFOP(login_fd,2), name, 24); + WFIFOL(login_fd,26) = state; + memcpy(WFIFOP(login_fd,30), error_message, 20); + WFIFOSET(login_fd,50); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------------------- +// Sub-function: Asking to modify the state of an account +//------------------------------------------------------- +int changestate(char* param) { + char name[1023], error_message[1023]; + int state; + + memset(name, '\0', sizeof(name)); + memset(error_message, '\0', sizeof(error_message)); + + if (sscanf(param, "\"%[^\"]\" %d %[^\r\n]", name, &state, error_message) < 2 && + sscanf(param, "'%[^']' %d %[^\r\n]", name, &state, error_message) < 2 && + sscanf(param, "%s %d %[^\r\n]", name, &state, error_message) < 2) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte et un statut svp.\n"); + printf(" state nomtest 5\n"); + printf(" state nomtest 7 fin de votre ban\n"); + printf(" block \n"); + printf(" unblock \n"); + ladmin_log("Nombre incorrect de param鑼res pour changer le statut d'un compte (commande 'state')." RETCODE); + } else { + printf("Please input an account name and a state.\n"); + printf(" state testname 5\n"); + printf(" state testname 7 end of your ban\n"); + printf(" block \n"); + printf(" unblock \n"); + ladmin_log("Incomplete parameters to change the state of an account ('state' command)." RETCODE); + } + return 136; + } + + return changestatesub(name, state, error_message); +} + +//------------------------------------------- +// Sub-function: Asking to unblock an account +//------------------------------------------- +int unblockaccount(char* param) { + char name[1023]; + + memset(name, '\0', sizeof(name)); + + if (strlen(param) == 0 || + (sscanf(param, "\"%[^\"]\"", name) < 1 && + sscanf(param, "'%[^']'", name) < 1 && + sscanf(param, "%[^\r\n]", name) < 1) || + strlen(name) == 0) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte svp.\n"); + printf(" state nomtest 5\n"); + printf(" state nomtest 7 fin de votre ban\n"); + printf(" block \n"); + printf(" unblock \n"); + ladmin_log("Nombre incorrect de param鑼res pour changer le statut d'un compte (commande 'unblock')." RETCODE); + } else { + printf("Please input an account name.\n"); + printf(" state testname 5\n"); + printf(" state testname 7 end of your ban\n"); + printf(" block \n"); + printf(" unblock \n"); + ladmin_log("Incomplete parameters to change the state of an account ('unblock' command)." RETCODE); + } + return 136; + } + + return changestatesub(name, 0, "-"); // state 0, no error message +} + +//------------------------------------------- +// Sub-function: Asking to unblock an account +//------------------------------------------- +int blockaccount(char* param) { + char name[1023]; + + memset(name, '\0', sizeof(name)); + + if (strlen(param) == 0 || + (sscanf(param, "\"%[^\"]\"", name) < 1 && + sscanf(param, "'%[^']'", name) < 1 && + sscanf(param, "%[^\r\n]", name) < 1) || + strlen(name) == 0) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte svp.\n"); + printf(" state nomtest 5\n"); + printf(" state nomtest 7 fin de votre ban\n"); + printf(" block \n"); + printf(" unblock \n"); + ladmin_log("Nombre incorrect de param鑼res pour changer le statut d'un compte (commande 'block')." RETCODE); + } else { + printf("Please input an account name.\n"); + printf(" state testname 5\n"); + printf(" state testname 7 end of your ban\n"); + printf(" block \n"); + printf(" unblock \n"); + ladmin_log("Incomplete parameters to change the state of an account ('block' command)." RETCODE); + } + return 136; + } + + return changestatesub(name, 5, "-"); // state 5, no error message +} + +//--------------------------------------------------------------------- +// Sub-function: Add/substract time to the validity limit of an account +//--------------------------------------------------------------------- +int timeaddaccount(char* param) { + char name[1023], modif[1023]; + int year, month, day, hour, minute, second; + char * p_modif; + int value, i; + WFIFOHEAD(login_fd,38); + + memset(name, '\0', sizeof(name)); + memset(modif, '\0', sizeof(modif)); + year = month = day = hour = minute = second = 0; + + if (sscanf(param, "\"%[^\"]\" %[^\r\n]", name, modif) < 2 && + sscanf(param, "'%[^']' %[^\r\n]", name, modif) < 2 && + sscanf(param, "%s %[^\r\n]", name, modif) < 2) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte et un modificateur svp.\n"); + printf(" timeadd nomtest +1m-2mn1s-6y\n"); + printf(" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); + printf(" et 6 ans dans le m麥e temps.\n"); + ladmin_log("Nombre incorrect de param鑼res pour modifier une date limite d'utilisation (commande 'timeadd')." RETCODE); + } else { + printf("Please input an account name and a modifier.\n"); + printf(" : timeadd testname +1m-2mn1s-6y\n"); + printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf(" and 6 years at the same time.\n"); + ladmin_log("Incomplete parameters to modify a limit time ('timeadd' command)." RETCODE); + } + return 136; + } + if (verify_accountname(name) == 0) { + return 102; + } + + // lowercase for modif + for (i = 0; modif[i]; i++) + modif[i] = tolower(modif[i]); + p_modif = modif; + while (strlen(p_modif) > 0) { + value = atoi(p_modif); + if (value == 0) { + p_modif++; + } else { + if (p_modif[0] == '-' || p_modif[0] == '+') + p_modif++; + while (strlen(p_modif) > 0 && p_modif[0] >= '0' && p_modif[0] <= '9') { + p_modif++; + } + if (p_modif[0] == 's') { + second = value; + p_modif++; + } else if (p_modif[0] == 'm' && p_modif[1] == 'n') { + minute = value; + p_modif += 2; + } else if (p_modif[0] == 'h') { + hour = value; + p_modif++; + } else if (p_modif[0] == 'd' || p_modif[0] == 'j') { + day = value; + p_modif += 2; + } else if (p_modif[0] == 'm') { + month = value; + p_modif++; + } else if (p_modif[0] == 'y' || p_modif[0] == 'a') { + year = value; + p_modif++; + } else { + p_modif++; + } + } + } + + if (defaultlanguage == 'F') { + printf(" ann馥: %d\n", year); + printf(" mois: %d\n", month); + printf(" jour: %d\n", day); + printf(" heure: %d\n", hour); + printf(" minute: %d\n", minute); + printf(" seconde: %d\n", second); + } else { + printf(" year: %d\n", year); + printf(" month: %d\n", month); + printf(" day: %d\n", day); + printf(" hour: %d\n", hour); + printf(" minute: %d\n", minute); + printf(" second: %d\n", second); + } + + if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0) { + if (defaultlanguage == 'F') { + printf("Vous devez entrer un ajustement avec cette commande, svp:\n"); + printf(" Valeur d'ajustement (-1, 1, +1, etc...)\n"); + printf(" El駑ent modifi:\n"); + printf(" a ou y: ann馥\n"); + printf(" m: mois\n"); + printf(" j ou d: jour\n"); + printf(" h: heure\n"); + printf(" mn: minute\n"); + printf(" s: seconde\n"); + printf(" timeadd nomtest +1m-2mn1s-6y\n"); + printf(" Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n"); + printf(" et 6 ans dans le m麥e temps.\n"); + ladmin_log("Aucun ajustement n'est pas un ajustement (commande 'timeadd')." RETCODE); + } else { + printf("Please give an adjustment with this command:\n"); + printf(" Adjustment value (-1, 1, +1, etc...)\n"); + printf(" Modified element:\n"); + printf(" a or y: year\n"); + printf(" m: month\n"); + printf(" j or d: day\n"); + printf(" h: hour\n"); + printf(" mn: minute\n"); + printf(" s: second\n"); + printf(" timeadd testname +1m-2mn1s-6y\n"); + printf(" this example adds 1 month and 1 second, and substracts 2 minutes\n"); + printf(" and 6 years at the same time.\n"); + ladmin_log("No adjustment isn't an adjustment ('timeadd' command)." RETCODE); + } + return 137; + } + if (year > 127 || year < -127) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement d'ann馥s correct (de -127 127), svp.\n"); + ladmin_log("Ajustement de l'ann馥 hors norme ('timeadd' command)." RETCODE); + } else { + printf("Please give a correct adjustment for the years (from -127 to 127).\n"); + ladmin_log("Abnormal adjustement for the year ('timeadd' command)." RETCODE); + } + return 137; + } + if (month > 255 || month < -255) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement de mois correct (de -255 255), svp.\n"); + ladmin_log("Ajustement du mois hors norme ('timeadd' command)." RETCODE); + } else { + printf("Please give a correct adjustment for the months (from -255 to 255).\n"); + ladmin_log("Abnormal adjustement for the month ('timeadd' command)." RETCODE); + } + return 137; + } + if (day > 32767 || day < -32767) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement de jours correct (de -32767 32767), svp.\n"); + ladmin_log("Ajustement des jours hors norme ('timeadd' command)." RETCODE); + } else { + printf("Please give a correct adjustment for the days (from -32767 to 32767).\n"); + ladmin_log("Abnormal adjustement for the days ('timeadd' command)." RETCODE); + } + return 137; + } + if (hour > 32767 || hour < -32767) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement d'heures correct (de -32767 32767), svp.\n"); + ladmin_log("Ajustement des heures hors norme ('timeadd' command)." RETCODE); + } else { + printf("Please give a correct adjustment for the hours (from -32767 to 32767).\n"); + ladmin_log("Abnormal adjustement for the hours ('timeadd' command)." RETCODE); + } + return 137; + } + if (minute > 32767 || minute < -32767) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement de minutes correct (de -32767 32767), svp.\n"); + ladmin_log("Ajustement des minutes hors norme ('timeadd' command)." RETCODE); + } else { + printf("Please give a correct adjustment for the minutes (from -32767 to 32767).\n"); + ladmin_log("Abnormal adjustement for the minutes ('timeadd' command)." RETCODE); + } + return 137; + } + if (second > 32767 || second < -32767) { + if (defaultlanguage == 'F') { + printf("Entrez un ajustement de secondes correct (de -32767 32767), svp.\n"); + ladmin_log("Ajustement des secondes hors norme ('timeadd' command)." RETCODE); + } else { + printf("Please give a correct adjustment for the seconds (from -32767 to 32767).\n"); + ladmin_log("Abnormal adjustement for the seconds ('timeadd' command)." RETCODE); + } + return 137; + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour modifier une date limite d'utilisation." RETCODE); + } else { + ladmin_log("Request to login-server to modify a time limit." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7950; + memcpy(WFIFOP(login_fd,2), name, 24); + WFIFOW(login_fd,26) = (short)year; + WFIFOW(login_fd,28) = (short)month; + WFIFOW(login_fd,30) = (short)day; + WFIFOW(login_fd,32) = (short)hour; + WFIFOW(login_fd,34) = (short)minute; + WFIFOW(login_fd,36) = (short)second; + WFIFOSET(login_fd,38); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------------- +// Sub-function: Set a validity limit of an account +//------------------------------------------------- +int timesetaccount(char* param) { + char name[1023], date[1023], time[1023]; + int year, month, day, hour, minute, second; + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + struct tm *tmtime; + WFIFOHEAD(login_fd,30); + + memset(name, '\0', sizeof(name)); + memset(date, '\0', sizeof(date)); + memset(time, '\0', sizeof(time)); + year = month = day = hour = minute = second = 0; + connect_until_time = 0; + tmtime = localtime(&connect_until_time); // initialize + + if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void + sscanf(param, "'%[^']' %s %[^\r\n]", name, date, time) < 2 && // if date = 0, time can be void + sscanf(param, "%s %s %[^\r\n]", name, date, time) < 2) { // if date = 0, time can be void + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte, une date et une heure svp.\n"); + printf(": timeset aaaa/mm/jj [hh:mm:ss]\n"); + printf(" timeset 0 (0 = illimit)\n"); + printf(" Heure par d馭aut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Nombre incorrect de param鑼res pour fixer une date limite d'utilisation (commande 'timeset')." RETCODE); + } else { + printf("Please input an account name, a date and a hour.\n"); + printf(": timeset yyyy/mm/dd [hh:mm:ss]\n"); + printf(" timeset 0 (0 = unlimited)\n"); + printf(" Default time [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Incomplete parameters to set a limit time ('timeset' command)." RETCODE); + } + return 136; + } + if (verify_accountname(name) == 0) { + return 102; + } + + if (time[0] == '\0') + strcpy(time, "23:59:59"); + + if (atoi(date) != 0 && + ((sscanf(date, "%d/%d/%d", &year, &month, &day) < 3 && + sscanf(date, "%d-%d-%d", &year, &month, &day) < 3 && + sscanf(date, "%d.%d.%d", &year, &month, &day) < 3 && + sscanf(date, "%d'%d'%d", &year, &month, &day) < 3) || + sscanf(time, "%d:%d:%d", &hour, &minute, &second) < 3)) { + if (defaultlanguage == 'F') { + printf("Entrez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n"); + ladmin_log("Format incorrect pour la date/heure ('timeset' command)." RETCODE); + } else { + printf("Please input 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"); + ladmin_log("Invalid format for the date/time ('timeset' command)." RETCODE); + } + return 102; + } + + if (atoi(date) == 0) { + connect_until_time = 0; + } else { + if (year < 70) { + year = year + 100; + } + if (year >= 1900) { + year = year - 1900; + } + if (month < 1 || month > 12) { + if (defaultlanguage == 'F') { + printf("Entrez un mois correct svp (entre 1 et 12).\n"); + ladmin_log("Mois incorrect pour la date ('timeset' command)." RETCODE); + } else { + printf("Please give a correct value for the month (from 1 to 12).\n"); + ladmin_log("Invalid month for the date ('timeset' command)." RETCODE); + } + return 102; + } + month = month - 1; + if (day < 1 || day > 31) { + if (defaultlanguage == 'F') { + printf("Entrez un jour correct svp (entre 1 et 31).\n"); + ladmin_log("Jour incorrect pour la date ('timeset' command)." RETCODE); + } else { + printf("Please give a correct value for the day (from 1 to 31).\n"); + ladmin_log("Invalid day for the date ('timeset' command)." RETCODE); + } + return 102; + } + if (((month == 3 || month == 5 || month == 8 || month == 10) && day > 30) || + (month == 1 && day > 29)) { + if (defaultlanguage == 'F') { + printf("Entrez un jour correct en fonction du mois (%d) svp.\n", month); + ladmin_log("Jour incorrect pour ce mois correspondant ('timeset' command)." RETCODE); + } else { + printf("Please give a correct value for a day of this month (%d).\n", month); + ladmin_log("Invalid day for this month ('timeset' command)." RETCODE); + } + return 102; + } + if (hour < 0 || hour > 23) { + if (defaultlanguage == 'F') { + printf("Entrez une heure correcte svp (entre 0 et 23).\n"); + ladmin_log("Heure incorrecte pour l'heure ('timeset' command)." RETCODE); + } else { + printf("Please give a correct value for the hour (from 0 to 23).\n"); + ladmin_log("Invalid hour for the time ('timeset' command)." RETCODE); + } + return 102; + } + if (minute < 0 || minute > 59) { + if (defaultlanguage == 'F') { + printf("Entrez des minutes correctes svp (entre 0 et 59).\n"); + ladmin_log("Minute incorrecte pour l'heure ('timeset' command)." RETCODE); + } else { + printf("Please give a correct value for the minutes (from 0 to 59).\n"); + ladmin_log("Invalid minute for the time ('timeset' command)." RETCODE); + } + return 102; + } + if (second < 0 || second > 59) { + if (defaultlanguage == 'F') { + printf("Entrez des secondes correctes svp (entre 0 et 59).\n"); + ladmin_log("Seconde incorrecte pour l'heure ('timeset' command)." RETCODE); + } else { + printf("Please give a correct value for the seconds (from 0 to 59).\n"); + ladmin_log("Invalid second for the time ('timeset' command)." RETCODE); + } + return 102; + } + tmtime->tm_year = year; + tmtime->tm_mon = month; + tmtime->tm_mday = day; + tmtime->tm_hour = hour; + tmtime->tm_min = minute; + tmtime->tm_sec = second; + tmtime->tm_isdst = -1; // -1: no winter/summer time modification + connect_until_time = mktime(tmtime); + if (connect_until_time == -1) { + if (defaultlanguage == 'F') { + printf("Date incorrecte.\n"); + printf("Ajoutez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n"); + ladmin_log("Date incorrecte. ('timeset' command)." RETCODE); + } else { + printf("Invalid date.\n"); + printf("Please add 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n"); + ladmin_log("Invalid date. ('timeset' command)." RETCODE); + } + return 102; + } + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour fixer une date limite d'utilisation." RETCODE); + } else { + ladmin_log("Request to login-server to set a time limit." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7948; + memcpy(WFIFOP(login_fd,2), name, 24); + WFIFOL(login_fd,26) = (int)connect_until_time; + WFIFOSET(login_fd,30); + bytes_to_read = 1; + + return 0; +} + +//------------------------------------------------------------------------------ +// Sub-function: Asking to displaying information about an account (by its name) +//------------------------------------------------------------------------------ +int whoaccount(char* param) { + char name[1023]; + WFIFOHEAD(login_fd,26); + + memset(name, '\0', sizeof(name)); + + if (strlen(param) == 0 || + (sscanf(param, "\"%[^\"]\"", name) < 1 && + sscanf(param, "'%[^']'", name) < 1 && + sscanf(param, "%[^\r\n]", name) < 1) || + strlen(name) == 0) { + if (defaultlanguage == 'F') { + printf("Entrez un nom de compte svp.\n"); + printf(" who nomtest\n"); + ladmin_log("Aucun nom n'a 騁 donn pour trouver le compte." RETCODE); + } else { + printf("Please input an account name.\n"); + printf(" who testname\n"); + ladmin_log("No name was given to found the account." RETCODE); + } + return 136; + } + if (verify_accountname(name) == 0) { + return 102; + } + + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir le information d'un compte (par le nom)." RETCODE); + } else { + ladmin_log("Request to login-server to obtain information about an account (by its name)." RETCODE); + } + + WFIFOW(login_fd,0) = 0x7952; + memcpy(WFIFOP(login_fd,2), name, 24); + WFIFOSET(login_fd,26); + bytes_to_read = 1; + + return 0; +} + +//-------------------------------------------------------- +// Sub-function: Asking of the version of the login-server +//-------------------------------------------------------- +int checkloginversion(void) { + WFIFOHEAD(login_fd,2); + if (defaultlanguage == 'F') + ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir sa version." RETCODE); + else + ladmin_log("Request to login-server to obtain its version." RETCODE); + + WFIFOW(login_fd,0) = 0x7530; + WFIFOSET(login_fd,2); + bytes_to_read = 1; + + return 0; +} + +//--------------------------------------------- +// Prompt function +// this function wait until user type a command +// and analyse the command. +//--------------------------------------------- +int prompt(void) { + int i, j; + char buf[1024]; + char *p; + + // while we don't wait new packets + while (bytes_to_read == 0) { + // for help with the console colors look here: + // http://www.edoceo.com/liberum/?doc=printf-with-color + // some code explanation (used here): + // \033[2J : clear screen and go up/left (0, 0 position) + // \033[K : clear line from actual position to end of the line + // \033[0m : reset color parameter + // \033[1m : use bold for font + printf("\n"); + if (defaultlanguage == 'F') + printf("\033[32mPour afficher les commandes, tapez 'Entr馥'.\033[0m\n"); + else + printf("\033[32mTo list the commands, type 'enter'.\033[0m\n"); + printf("\033[0;36mLadmin-> \033[0m"); + printf("\033[1m"); + fflush(stdout); + + // get command and parameter + memset(buf, '\0', sizeof(buf)); + fflush(stdin); + fgets(buf, 1023, stdin); + buf[1023] = '\0'; + + printf("\033[0m"); + fflush(stdout); + + // remove final \n + if((p = strrchr(buf, '\n')) != NULL) + p[0] = '\0'; + // remove all control char + for (i = 0; buf[i]; i++) + if (buf[i] < 32) { + // remove cursor control. + if (buf[i] == 27 && buf[i+1] == '[' && + (buf[i+2] == 'H' || // home position (cursor) + buf[i+2] == 'J' || // clear screen + buf[i+2] == 'A' || // up 1 line + buf[i+2] == 'B' || // down 1 line + buf[i+2] == 'C' || // right 1 position + buf[i+2] == 'D' || // left 1 position + buf[i+2] == 'G')) { // center cursor (windows) + for (j = i; buf[j]; j++) + buf[j] = buf[j+3]; + } else if (buf[i] == 27 && buf[i+1] == '[' && buf[i+2] == '2' && buf[i+3] == 'J') { // clear screen + for (j = i; buf[j]; j++) + buf[j] = buf[j+4]; + } else if (buf[i] == 27 && buf[i+1] == '[' && buf[i+3] == '~' && + (buf[i+2] == '1' || // home (windows) + buf[i+2] == '2' || // insert (windows) + buf[i+2] == '3' || // del (windows) + buf[i+2] == '4' || // end (windows) + buf[i+2] == '5' || // pgup (windows) + buf[i+2] == '6')) { // pgdown (windows) + for (j = i; buf[j]; j++) + buf[j] = buf[j+4]; + } else { + // remove other control char. + for (j = i; buf[j]; j++) + buf[j] = buf[j+1]; + } + i--; + } + + // extract command name and parameters + memset(command, '\0', sizeof(command)); + memset(parameters, '\0', sizeof(parameters)); + sscanf(buf, "%1023s %[^\n]", command, parameters); + command[1023] = '\0'; + parameters[1023] = '\0'; + + // lowercase for command line + for (i = 0; command[i]; i++) + command[i] = tolower(command[i]); + + if (command[0] == '?' || strlen(command) == 0) { + if (defaultlanguage == 'F') { + strcpy(buf, "aide"); + strcpy(command, "aide"); + } else { + strcpy(buf, "help"); + strcpy(command, "help"); + } + } + + // Analyse of the command + check_command(command); // give complete name to the command + + if (strlen(parameters) == 0) { + if (defaultlanguage == 'F') { + ladmin_log("Commande: '%s' (sans param鑼re)" RETCODE, command, parameters); + } else { + ladmin_log("Command: '%s' (without parameters)" RETCODE, command, parameters); + } + } else { + if (defaultlanguage == 'F') { + ladmin_log("Commande: '%s', param鑼res: '%s'" RETCODE, command, parameters); + } else { + ladmin_log("Command: '%s', parameters: '%s'" RETCODE, command, parameters); + } + } + + // Analyse of the command +// help + if (strcmp(command, "aide") == 0) { + display_help(parameters, 1); // 1: french + } else if (strcmp(command, "help") == 0 ) { + display_help(parameters, 0); // 0: english +// general commands + } else if (strcmp(command, "add") == 0) { + addaccount(parameters, 0); // 0: no email + } else if (strcmp(command, "ban") == 0) { + banaccount(parameters); + } else if (strcmp(command, "banadd") == 0) { + banaddaccount(parameters); + } else if (strcmp(command, "banset") == 0) { + bansetaccount(parameters); + } else if (strcmp(command, "block") == 0) { + blockaccount(parameters); + } else if (strcmp(command, "check") == 0) { + checkaccount(parameters); + } else if (strcmp(command, "create") == 0) { + addaccount(parameters, 1); // 1: with email + } else if (strcmp(command, "delete") == 0) { + delaccount(parameters); + } else if (strcmp(command, "email") == 0) { + changeemail(parameters); + } else if (strcmp(command, "getcount") == 0) { + getlogincount(); + } else if (strcmp(command, "gm") == 0) { + changegmlevel(parameters); + } else if (strcmp(command, "id") == 0) { + idaccount(parameters); + } else if (strcmp(command, "info") == 0) { + infoaccount(atoi(parameters)); + } else if (strcmp(command, "kami") == 0) { + sendbroadcast(0, parameters); // flag for normal + } else if (strcmp(command, "kamib") == 0) { + sendbroadcast(0x10, parameters); // flag for blue + } else if (strcmp(command, "language") == 0) { + changelanguage(parameters); + } else if (strcmp(command, "list") == 0) { + listaccount(parameters, 0); // 0: to list all + } else if (strcmp(command, "listban") == 0) { + listaccount(parameters, 3); // 3: to list only accounts with state or bannished + } else if (strcmp(command, "listgm") == 0) { + listaccount(parameters, 1); // 1: to list only GM + } else if (strcmp(command, "listok") == 0) { + listaccount(parameters, 4); // 4: to list only accounts without state and not bannished + } else if (strcmp(command, "memo") == 0) { + changememo(parameters); + } else if (strcmp(command, "name") == 0) { + nameaccount(atoi(parameters)); + } else if (strcmp(command, "password") == 0) { + changepasswd(parameters); + } else if (strcmp(command, "reloadgm") == 0) { + reloadGM(); + } else if (strcmp(command, "search") == 0) { // no regex in C version + listaccount(parameters, 2); // 2: to list with pattern + } else if (strcmp(command, "sex") == 0) { + changesex(parameters); + } else if (strcmp(command, "state") == 0) { + changestate(parameters); + } else if (strcmp(command, "timeadd") == 0) { + timeaddaccount(parameters); + } else if (strcmp(command, "timeset") == 0) { + timesetaccount(parameters); + } else if (strcmp(command, "unban") == 0) { + unbanaccount(parameters); + } else if (strcmp(command, "unblock") == 0) { + unblockaccount(parameters); + } else if (strcmp(command, "version") == 0) { + checkloginversion(); + } else if (strcmp(command, "who") == 0) { + whoaccount(parameters); +// quit + } else if (strcmp(command, "quit") == 0 || + strcmp(command, "exit") == 0 || + strcmp(command, "end") == 0) { + if (defaultlanguage == 'F') { + printf("Au revoir.\n"); + } else { + printf("Bye.\n"); + } + exit(0); +// unknown command + } else { + if (defaultlanguage == 'F') { + printf("Commande inconnue [%s].\n", buf); + ladmin_log("Commande inconnue [%s]." RETCODE, buf); + } else { + printf("Unknown command [%s].\n", buf); + ladmin_log("Unknown command [%s]." RETCODE, buf); + } + } + } + + return 0; +} + +//------------------------------------------------------------- +// Function: Parse receiving informations from the login-server +//------------------------------------------------------------- +int parse_fromlogin(int fd) { + struct char_session_data *sd; + int id; + RFIFOHEAD(fd); + if (session[fd]->eof) { + if (defaultlanguage == 'F') { + printf("Impossible de se connecter au serveur de login [%s:%d] !\n", loginserverip, loginserverport); + ladmin_log("Impossible de se connecter au serveur de login [%s:%d] !" RETCODE, loginserverip, loginserverport); + } else { + printf("Impossible to have a connection with the login-server [%s:%d] !\n", loginserverip, loginserverport); + ladmin_log("Impossible to have a connection with the login-server [%s:%d] !" RETCODE, loginserverip, loginserverport); + } + close(fd); + delete_session(fd); + exit (0); + } + +// printf("parse_fromlogin : %d %d %d\n", fd, RFIFOREST(fd), RFIFOW(fd,0)); + sd = (struct char_session_data*)session[fd]->session_data; + + while(RFIFOREST(fd) >= 2) { + switch(RFIFOW(fd,0)) { + case 0x7919: // answer of a connection request + if (RFIFOREST(fd) < 3) + return 0; + if (RFIFOB(fd,2) != 0) { + if (defaultlanguage == 'F') { + printf("Erreur de login:\n"); + printf(" - mot de passe incorrect,\n"); + printf(" - syst鑪e d'administration non activ, ou\n"); + printf(" - IP non autoris馥.\n"); + ladmin_log("Erreur de login: mot de passe incorrect, syst鑪e d'administration non activ, ou IP non autoris馥." RETCODE); + } else { + printf("Error at login:\n"); + printf(" - incorrect password,\n"); + printf(" - administration system not activated, or\n"); + printf(" - unauthorised IP.\n"); + ladmin_log("Error at login: incorrect password, administration system not activated, or unauthorised IP." RETCODE); + } + session[fd]->eof = 1; + //bytes_to_read = 1; // not stop at prompt + } else { + if (defaultlanguage == 'F') { + printf("Connexion 騁ablie.\n"); + ladmin_log("Connexion 騁ablie." RETCODE); + printf("Lecture de la version du serveur de login...\n"); + ladmin_log("Lecture de la version du serveur de login..." RETCODE); + } else { + printf("Established connection.\n"); + ladmin_log("Established connection." RETCODE); + printf("Reading of the version of the login-server...\n"); + ladmin_log("Reading of the version of the login-server..." RETCODE); + } + //bytes_to_read = 1; // unchanged + checkloginversion(); + } + RFIFOSKIP(fd,3); + break; + +#ifdef PASSWORDENC + case 0x01dc: // answer of a coding key request + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + char md5str[64] = "", md5bin[32]; + WFIFOHEAD(login_fd, 20); + if (passenc == 1) { + strncpy(md5str, (const char*)RFIFOP(fd,4), RFIFOW(fd,2) - 4); + strcat(md5str, loginserveradminpassword); + } else if (passenc == 2) { + strncpy(md5str, loginserveradminpassword, sizeof(loginserveradminpassword)); + strcat(md5str, (const char*)RFIFOP(fd,4)); + } + MD5_String2binary(md5str, md5bin); + WFIFOW(login_fd,0) = 0x7918; // Request for administation login (encrypted password) + WFIFOW(login_fd,2) = passenc; // Encrypted type + memcpy(WFIFOP(login_fd,4), md5bin, 16); + WFIFOSET(login_fd,20); + if (defaultlanguage == 'F') { + printf("R馗eption de la clef MD5.\n"); + ladmin_log("R馗eption de la clef MD5." RETCODE); + printf("Envoi du mot de passe crypt...\n"); + ladmin_log("Envoi du mot de passe crypt..." RETCODE); + } else { + printf("Receiving of the MD5 key.\n"); + ladmin_log("Receiving of the MD5 key." RETCODE); + printf("Sending of the encrypted password...\n"); + ladmin_log("Sending of the encrypted password..." RETCODE); + } + } + bytes_to_read = 1; + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; +#endif + + case 0x7531: // Displaying of the version of the login-server + if (RFIFOREST(fd) < 10) + return 0; + printf(" Login-Server [%s:%d]\n", loginserverip, loginserverport); + if (((int)RFIFOB(login_fd,5)) == 0) { + printf(" eAthena version stable-%d.%d", (int)RFIFOB(login_fd,2), (int)RFIFOB(login_fd,3)); + } else { + printf(" eAthena version dev-%d.%d", (int)RFIFOB(login_fd,2), (int)RFIFOB(login_fd,3)); + } + if (((int)RFIFOB(login_fd,4)) == 0) + printf(" revision %d", (int)RFIFOB(login_fd,4)); + if (((int)RFIFOB(login_fd,6)) == 0) + printf("%d.\n", RFIFOW(login_fd,8)); + else + printf("-mod%d.\n", RFIFOW(login_fd,8)); + bytes_to_read = 0; + RFIFOSKIP(fd,10); + break; + + case 0x7921: // Displaying of the list of accounts + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + if (RFIFOW(fd,2) < 5) { + if (defaultlanguage == 'F') { + ladmin_log(" R馗eption d'une liste des comptes vide." RETCODE); + if (list_count == 0) + printf("Aucun compte trouv.\n"); + else if (list_count == 1) + printf("1 compte trouv.\n"); + else + printf("%d comptes trouv駸.\n", list_count); + } else { + ladmin_log(" Receiving of a void accounts list." RETCODE); + if (list_count == 0) + printf("No account found.\n"); + else if (list_count == 1) + printf("1 account found.\n"); + else + printf("%d accounts found.\n", list_count); + } + bytes_to_read = 0; + } else { + int i; + WFIFOHEAD(login_fd,10); + if (defaultlanguage == 'F') + ladmin_log(" R馗eption d'une liste des comptes." RETCODE); + else + ladmin_log(" Receiving of a accounts list." RETCODE); + for(i = 4; i < RFIFOW(fd,2); i += 38) { + int j; + char userid[24]; + char lower_userid[24]; + memcpy(userid, RFIFOP(fd,i + 5), sizeof(userid)); + userid[sizeof(userid)-1] = '\0'; + memset(lower_userid, '\0', sizeof(lower_userid)); + for (j = 0; userid[j]; j++) + lower_userid[j] = tolower(userid[j]); + list_first = RFIFOL(fd,i) + 1; + // here are checks... + if (list_type == 0 || + (list_type == 1 && RFIFOB(fd,i+4) > 0) || + (list_type == 2 && strstr(lower_userid, parameters) != NULL) || + (list_type == 3 && RFIFOL(fd,i+34) != 0) || + (list_type == 4 && RFIFOL(fd,i+34) == 0)) { + printf("%10d ", (int)RFIFOL(fd,i)); + if (RFIFOB(fd,i+4) == 0) + printf(" "); + else + printf("%2d ", (int)RFIFOB(fd,i+4)); + printf("%-24s", userid); + if (defaultlanguage == 'F') { + if (RFIFOB(fd,i+29) == 0) + printf("%-5s ", "Femme"); + else if (RFIFOB(fd,i+29) == 1) + printf("%-5s ", "Male"); + else + printf("%-5s ", "Servr"); + } else { + if (RFIFOB(fd,i+29) == 0) + printf("%-5s ", "Femal"); + else if (RFIFOB(fd,i+29) == 1) + printf("%-5s ", "Male"); + else + printf("%-5s ", "Servr"); + } + printf("%6d ", (int)RFIFOL(fd,i+30)); + switch(RFIFOL(fd,i+34)) { + case 0: + if (defaultlanguage == 'F') + printf("%-27s\n", "Compte Ok"); + else + printf("%-27s\n", "Account OK"); + break; + case 1: + printf("%-27s\n", "Unregistered ID"); + break; + case 2: + printf("%-27s\n", "Incorrect Password"); + break; + case 3: + printf("%-27s\n", "This ID is expired"); + break; + case 4: + printf("%-27s\n", "Rejected from Server"); + break; + case 5: + printf("%-27s\n", "Blocked by the GM Team"); // You have been blocked by the GM Team + break; + case 6: + printf("%-27s\n", "Your EXE file is too old"); // Your Game's EXE file is not the latest version + break; + case 7: + printf("%-27s\n", "Banishement or"); + printf(" Prohibited to login until...\n"); // You are Prohibited to log in until %s + break; + case 8: + printf("%-27s\n", "Server is over populated"); + break; + case 9: + printf("%-27s\n", "No MSG"); + break; + default: // 100 + printf("%-27s\n", "This ID is totally erased"); // This ID has been totally erased + break; + } + list_count++; + } + } + // asking of the following acounts + if (defaultlanguage == 'F') + ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir la liste des comptes de %d %d (compl駑ent)." RETCODE, list_first, list_last); + else + ladmin_log("Request to login-server to obtain the list of accounts from %d to %d (complement)." RETCODE, list_first, list_last); + WFIFOW(login_fd,0) = 0x7920; + WFIFOL(login_fd,2) = list_first; + WFIFOL(login_fd,6) = list_last; + WFIFOSET(login_fd,10); + bytes_to_read = 1; + } + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + + case 0x7931: // Answer of login-server about an account creation + if (RFIFOREST(fd) < 30) + return 0; + id=RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Echec la cr饌tion du compte [%s]. Un compte identique existe d駛.\n", RFIFOP(fd,6)); + ladmin_log("Echec la cr饌tion du compte [%s]. Un compte identique existe d駛." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] creation failed. Same account already exists.\n", RFIFOP(fd,6)); + ladmin_log("Account [%s] creation failed. Same account already exists." RETCODE, RFIFOP(fd,6)); + } + } else { + if (defaultlanguage == 'F') { + printf("Compte [%s] cr鳬 avec succ鑚 [id: %d].\n", RFIFOP(fd,6), id); + ladmin_log("Compte [%s] cr鳬 avec succ鑚 [id: %d]." RETCODE, RFIFOP(fd,6), id); + } else { + printf("Account [%s] is successfully created [id: %d].\n", RFIFOP(fd,6), id); + ladmin_log("Account [%s] is successfully created [id: %d]." RETCODE, RFIFOP(fd,6), id); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7933: // Answer of login-server about an account deletion + if (RFIFOREST(fd) < 30) + return 0; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec de la suppression du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec de la suppression du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] deletion failed. Account doesn't exist.\n", RFIFOP(fd,6)); + ladmin_log("Account [%s] deletion failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); + } + } else { + if (defaultlanguage == 'F') { + printf("Compte [%s][id: %d] SUPPRIME avec succ鑚.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2)); + ladmin_log("Compte [%s][id: %d] SUPPRIME avec succ鑚." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Account [%s][id: %d] is successfully DELETED.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2)); + ladmin_log("Account [%s][id: %d] is successfully DELETED." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7935: // answer of the change of an account password + if (RFIFOREST(fd) < 30) + return 0; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec de la modification du mot de passe du compte [%s].\n", RFIFOP(fd,6)); + printf("Le compte [%s] n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec de la modification du mot de passe du compte. Le compte [%s] n'existe pas." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] password changing failed.\n", RFIFOP(fd,6)); + printf("Account [%s] doesn't exist.\n", RFIFOP(fd,6)); + ladmin_log("Account password changing failed. The compte [%s] doesn't exist." RETCODE, RFIFOP(fd,6)); + } + } else { + if (defaultlanguage == 'F') { + printf("Modification du mot de passe du compte [%s][id: %d] r騏ssie.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2)); + ladmin_log("Modification du mot de passe du compte [%s][id: %d] r騏ssie." RETCODE, RFIFOP(fd,6), (int)RFIFOL(fd,2)); + } else { + printf("Account [%s][id: %d] password successfully changed.\n", RFIFOP(fd,6), (int)RFIFOL(fd,2)); + ladmin_log("Account [%s][id: %d] password successfully changed." RETCODE, RFIFOP(fd,6), (int)RFIFOL(fd,2)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7937: // answer of the change of an account state + if (RFIFOREST(fd) < 34) + return 0; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec du changement du statut du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec du changement du statut du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] state changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); + ladmin_log("Account [%s] state changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); + } + } else { + char tmpstr[256]; + if (defaultlanguage == 'F') { + sprintf(tmpstr, "Statut du compte [%s] chang avec succ鑚 en [", RFIFOP(fd,6)); + } else { + sprintf(tmpstr, "Account [%s] state successfully changed in [", RFIFOP(fd,6)); + } + switch(RFIFOL(fd,30)) { + case 0: + if (defaultlanguage == 'F') + strcat(tmpstr, "0: Compte Ok"); + else + strcat(tmpstr, "0: Account OK"); + break; + case 1: + strcat(tmpstr, "1: Unregistered ID"); + break; + case 2: + strcat(tmpstr, "2: Incorrect Password"); + break; + case 3: + strcat(tmpstr, "3: This ID is expired"); + break; + case 4: + strcat(tmpstr, "4: Rejected from Server"); + break; + case 5: + strcat(tmpstr, "5: You have been blocked by the GM Team"); + break; + case 6: + strcat(tmpstr, "6: [Your Game's EXE file is not the latest version"); + break; + case 7: + strcat(tmpstr, "7: You are Prohibited to log in until..."); + break; + case 8: + strcat(tmpstr, "8: Server is jammed due to over populated"); + break; + case 9: + strcat(tmpstr, "9: No MSG"); + break; + default: // 100 + strcat(tmpstr, "100: This ID is totally erased"); + break; + } + strcat(tmpstr, "]"); + printf("%s\n", tmpstr); + ladmin_log("%s%s", tmpstr, RETCODE); + } + bytes_to_read = 0; + RFIFOSKIP(fd,34); + break; + + case 0x7939: // answer of the number of online players + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + // Get length of the received packet + int i; + char name[20]; + if (defaultlanguage == 'F') { + ladmin_log(" R馗eption du nombre de joueurs en ligne." RETCODE); + } else { + ladmin_log(" Receiving of the number of online players." RETCODE); + } + // Read information of the servers + if (RFIFOW(fd,2) < 5) { + if (defaultlanguage == 'F') { + printf(" Aucun serveur n'est connect au login serveur.\n"); + } else { + printf(" No server is connected to the login-server.\n"); + } + } else { + if (defaultlanguage == 'F') { + printf(" Nombre de joueurs en ligne (serveur: nb):\n"); + } else { + printf(" Number of online players (server: number).\n"); + } + // Displaying of result + for(i = 4; i < RFIFOW(fd,2); i += 32) { + memcpy(name, RFIFOP(fd,i+6), sizeof(name)); + name[sizeof(name) - 1] = '\0'; + printf(" %-20s : %5d\n", name, RFIFOW(fd,i+26)); + } + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + + case 0x793b: // answer of the check of a password + if (RFIFOREST(fd) < 30) + return 0; + id = RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Le compte [%s] n'existe pas ou le mot de passe est incorrect.\n", RFIFOP(fd,6)); + ladmin_log("Le compte [%s] n'existe pas ou le mot de passe est incorrect." RETCODE, RFIFOP(fd,6)); + } else { + printf("The account [%s] doesn't exist or the password is incorrect.\n", RFIFOP(fd,6)); + ladmin_log("The account [%s] doesn't exist or the password is incorrect." RETCODE, RFIFOP(fd,6)); + } + } else { + if (defaultlanguage == 'F') { + printf("Le mot de passe donn correspond bien au compte [%s][id: %d].\n", RFIFOP(fd,6), id); + ladmin_log("Le mot de passe donn correspond bien au compte [%s][id: %d]." RETCODE, RFIFOP(fd,6), id); + } else { + printf("The proposed password is correct for the account [%s][id: %d].\n", RFIFOP(fd,6), id); + ladmin_log("The proposed password is correct for the account [%s][id: %d]." RETCODE, RFIFOP(fd,6), id); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x793d: // answer of the change of an account sex + if (RFIFOREST(fd) < 30) + return 0; + id = RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Echec de la modification du sexe du compte [%s].\n", RFIFOP(fd,6)); + printf("Le compte [%s] n'existe pas ou le sexe est d駛 celui demand.\n", RFIFOP(fd,6)); + ladmin_log("Echec de la modification du sexe du compte. Le compte [%s] n'existe pas ou le sexe est d駛 celui demand." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] sex changing failed.\n", RFIFOP(fd,6)); + printf("Account [%s] doesn't exist or the sex is already the good sex.\n", RFIFOP(fd,6)); + ladmin_log("Account sex changing failed. The compte [%s] doesn't exist or the sex is already the good sex." RETCODE, RFIFOP(fd,6)); + } + } else { + if (defaultlanguage == 'F') { + printf("Sexe du compte [%s][id: %d] chang avec succ鑚.\n", RFIFOP(fd,6), id); + ladmin_log("Sexe du compte [%s][id: %d] chang avec succ鑚." RETCODE, RFIFOP(fd,6), id); + } else { + printf("Account [%s][id: %d] sex successfully changed.\n", RFIFOP(fd,6), id); + ladmin_log("Account [%s][id: %d] sex successfully changed." RETCODE, RFIFOP(fd,6), id); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x793f: // answer of the change of an account GM level + if (RFIFOREST(fd) < 30) + return 0; + id = RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Echec de la modification du niveau de GM du compte [%s].\n", RFIFOP(fd,6)); + printf("Le compte [%s] n'existe pas, le niveau de GM est d駛 celui demand饅n", RFIFOP(fd,6)); + printf("ou il est impossible de modifier le fichier des comptes GM.\n"); + ladmin_log("Echec de la modification du niveau de GM du compte. Le compte [%s] n'existe pas, le niveau de GM est d駛 celui demand ou il est impossible de modifier le fichier des comptes GM." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] GM level changing failed.\n", RFIFOP(fd,6)); + printf("Account [%s] doesn't exist, the GM level is already the good GM level\n", RFIFOP(fd,6)); + printf("or it's impossible to modify the GM accounts file.\n"); + ladmin_log("Account GM level changing failed. The compte [%s] doesn't exist, the GM level is already the good sex or it's impossible to modify the GM accounts file." RETCODE, RFIFOP(fd,6)); + } + } else { + if (defaultlanguage == 'F') { + printf("Niveau de GM du compte [%s][id: %d] chang avec succ鑚.\n", RFIFOP(fd,6), id); + ladmin_log("Niveau de GM du compte [%s][id: %d] chang avec succ鑚." RETCODE, RFIFOP(fd,6), id); + } else { + printf("Account [%s][id: %d] GM level successfully changed.\n", RFIFOP(fd,6), id); + ladmin_log("Account [%s][id: %d] GM level successfully changed." RETCODE, RFIFOP(fd,6), id); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7941: // answer of the change of an account email + if (RFIFOREST(fd) < 30) + return 0; + id = RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Echec de la modification de l'e-mail du compte [%s].\n", RFIFOP(fd,6)); + printf("Le compte [%s] n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec de la modification de l'e-mail du compte. Le compte [%s] n'existe pas." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] e-mail changing failed.\n", RFIFOP(fd,6)); + printf("Account [%s] doesn't exist.\n", RFIFOP(fd,6)); + ladmin_log("Account e-mail changing failed. The compte [%s] doesn't exist." RETCODE, RFIFOP(fd,6)); + } + } else { + if (defaultlanguage == 'F') { + printf("Modification de l'e-mail du compte [%s][id: %d] r騏ssie.\n", RFIFOP(fd,6), id); + ladmin_log("Modification de l'e-mail du compte [%s][id: %d] r騏ssie." RETCODE, RFIFOP(fd,6), id); + } else { + printf("Account [%s][id: %d] e-mail successfully changed.\n", RFIFOP(fd,6), id); + ladmin_log("Account [%s][id: %d] e-mail successfully changed." RETCODE, RFIFOP(fd,6), id); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7943: // answer of the change of an account memo + if (RFIFOREST(fd) < 30) + return 0; + id = RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Echec du changement du m駑o du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec du changement du m駑o du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] memo changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); + ladmin_log("Account [%s] memo changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); + } + } else { + if (defaultlanguage == 'F') { + printf("M駑o du compte [%s][id: %d] chang avec succ鑚.\n", RFIFOP(fd,6), id); + ladmin_log("M駑o du compte [%s][id: %d] chang avec succ鑚." RETCODE, RFIFOP(fd,6), id); + } else { + printf("Account [%s][id: %d] memo successfully changed.\n", RFIFOP(fd,6), id); + ladmin_log("Account [%s][id: %d] memo successfully changed." RETCODE, RFIFOP(fd,6), id); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7945: // answer of an account id search + if (RFIFOREST(fd) < 30) + return 0; + id = RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Impossible de trouver l'id du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Impossible de trouver l'id du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); + } else { + printf("Unable to find the account [%s] id. Account doesn't exist.\n", RFIFOP(fd,6)); + ladmin_log("Unable to find the account [%s] id. Account doesn't exist." RETCODE, RFIFOP(fd,6)); + } + } else { + if (defaultlanguage == 'F') { + printf("Le compte [%s] a pour id: %d.\n", RFIFOP(fd,6), id); + ladmin_log("Le compte [%s] a pour id: %d." RETCODE, RFIFOP(fd,6), id); + } else { + printf("The account [%s] have the id: %d.\n", RFIFOP(fd,6), id); + ladmin_log("The account [%s] have the id: %d." RETCODE, RFIFOP(fd,6), id); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7947: // answer of an account name search + if (RFIFOREST(fd) < 30) + return 0; + id = RFIFOL(fd,2); + if (strcmp((const char*)RFIFOP(fd,6), "") == 0) { + if (defaultlanguage == 'F') { + printf("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas.\n", id); + ladmin_log("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas." RETCODE, id); + } else { + printf("Unable to find the account [%d] name. Account doesn't exist.\n", id); + ladmin_log("Unable to find the account [%d] name. Account doesn't exist." RETCODE, id); + } + } else { + if (defaultlanguage == 'F') { + printf("Le compte [id: %d] a pour nom: %s.\n", id, RFIFOP(fd,6)); + ladmin_log("Le compte [id: %d] a pour nom: %s." RETCODE, id, RFIFOP(fd,6)); + } else { + printf("The account [id: %d] have the name: %s.\n", id, RFIFOP(fd,6)); + ladmin_log("The account [id: %d] have the name: %s." RETCODE, id, RFIFOP(fd,6)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7949: // answer of an account validity limit set + if (RFIFOREST(fd) < 34) + return 0; + id = RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Echec du changement de la validit du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec du changement de la validit du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] validity limit changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); + ladmin_log("Account [%s] validity limit changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); + } + } else { + time_t timestamp = RFIFOL(fd,30); + if (timestamp == 0) { + if (defaultlanguage == 'F') { + printf("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 en [illimit饐.\n", RFIFOP(fd,6), id); + ladmin_log("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 en [illimit饐." RETCODE, RFIFOP(fd,6), id); + } else { + printf("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n", RFIFOP(fd,6), id); + ladmin_log("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited]." RETCODE, RFIFOP(fd,6), id); + } + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + if (defaultlanguage == 'F') { + printf("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), id, tmpstr); + ladmin_log("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr); + } else { + printf("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr); + ladmin_log("Validity Limit of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,34); + break; + + case 0x794b: // answer of an account ban set + if (RFIFOREST(fd) < 34) + return 0; + id = RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); + ladmin_log("Account [%s] final date of banishment changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); + } + } else { + time_t timestamp = RFIFOL(fd,30); + if (timestamp == 0) { + if (defaultlanguage == 'F') { + printf("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 en [d-bannie].\n", RFIFOP(fd,6), id); + ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 en [d-bannie]." RETCODE, RFIFOP(fd,6), id); + } else { + printf("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", RFIFOP(fd,6), id); + ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished]." RETCODE, RFIFOP(fd,6), id); + } + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + if (defaultlanguage == 'F') { + printf("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), id, tmpstr); + ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr); + } else { + printf("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr); + ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,34); + break; + + case 0x794d: // answer of an account ban date/time changing + if (RFIFOREST(fd) < 34) + return 0; + id = RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] final date of banishment changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); + ladmin_log("Account [%s] final date of banishment changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); + } + } else { + time_t timestamp = RFIFOL(fd,30); + if (timestamp == 0) { + if (defaultlanguage == 'F') { + printf("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 en [d-bannie].\n", RFIFOP(fd,6), id); + ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 en [d-bannie]." RETCODE, RFIFOP(fd,6), id); + } else { + printf("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", RFIFOP(fd,6), id); + ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished]." RETCODE, RFIFOP(fd,6), id); + } + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + if (defaultlanguage == 'F') { + printf("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), id, tmpstr); + ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr); + } else { + printf("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr); + ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,34); + break; + + case 0x794f: // answer of a broadcast + if (RFIFOREST(fd) < 4) + return 0; + if (RFIFOW(fd,2) == (unsigned short)-1) { + if (defaultlanguage == 'F') { + printf("Echec de l'envoi du message. Aucun server de char en ligne.\n"); + ladmin_log("Echec de l'envoi du message. Aucun server de char en ligne." RETCODE); + } else { + printf("Message sending failed. No online char-server.\n"); + ladmin_log("Message sending failed. No online char-server." RETCODE); + } + } else { + if (defaultlanguage == 'F') { + printf("Message transmis au server de logins avec succ鑚.\n"); + ladmin_log("Message transmis au server de logins avec succ鑚." RETCODE); + } else { + printf("Message successfully sended to login-server.\n"); + ladmin_log("Message successfully sended to login-server." RETCODE); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,4); + break; + + case 0x7951: // answer of an account validity limit changing + if (RFIFOREST(fd) < 34) + return 0; + id = RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Echec du changement de la validit du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec du changement de la validit du compte [%s]. Le compte n'existe pas." RETCODE, RFIFOP(fd,6)); + } else { + printf("Account [%s] validity limit changing failed. Account doesn't exist.\n", RFIFOP(fd,6)); + ladmin_log("Account [%s] validity limit changing failed. Account doesn't exist." RETCODE, RFIFOP(fd,6)); + } + } else { + time_t timestamp = RFIFOL(fd,30); + if (timestamp == 0) { + if (defaultlanguage == 'F') { + printf("Limite de validit du compte [%s][id: %d] inchang馥.\n", RFIFOP(fd,6), id); + printf("Le compte a une validit illimit馥 ou\n"); + printf("la modification est impossible avec les ajustements demand駸.\n"); + ladmin_log("Limite de validit du compte [%s][id: %d] inchang馥. Le compte a une validit illimit馥 ou la modification est impossible avec les ajustements demand駸." RETCODE, RFIFOP(fd,6), id); + } else { + printf("Validity limit of the account [%s][id: %d] unchanged.\n", RFIFOP(fd,6), id); + printf("The account have an unlimited validity limit or\n"); + printf("the changing is impossible with the proposed adjustments.\n"); + ladmin_log("Validity limit of the account [%s][id: %d] unchanged. The account have an unlimited validity limit or the changing is impossible with the proposed adjustments." RETCODE, RFIFOP(fd,6), id); + } + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + if (defaultlanguage == 'F') { + printf("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), id, tmpstr); + ladmin_log("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), id, tmpstr); + } else { + printf("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), id, tmpstr); + ladmin_log("Validity limit of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), id, tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,34); + break; + + case 0x7953: // answer of a request about informations of an account (by account name/id) + if (RFIFOREST(fd) < 150 || RFIFOREST(fd) < (150 + RFIFOW(fd,148))) + return 0; + { + char userid[24], error_message[20], lastlogin[24], last_ip[16], email[40], memo[255]; + time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + memcpy(userid, RFIFOP(fd,7), sizeof(userid)); + userid[sizeof(userid)-1] = '\0'; + memcpy(error_message, RFIFOP(fd,40), sizeof(error_message)); + error_message[sizeof(error_message)-1] = '\0'; + memcpy(lastlogin, RFIFOP(fd,60), sizeof(lastlogin)); + lastlogin[sizeof(lastlogin)-1] = '\0'; + memcpy(last_ip, RFIFOP(fd,84), sizeof(last_ip)); + last_ip[sizeof(last_ip)-1] = '\0'; + memcpy(email, RFIFOP(fd,100), sizeof(email)); + email[sizeof(email)-1] = '\0'; + connect_until_time = (time_t)RFIFOL(fd,140); + ban_until_time = (time_t)RFIFOL(fd,144); + memset(memo, '\0', sizeof(memo)); + strncpy(memo, (const char*)RFIFOP(fd,150), RFIFOW(fd,148)); + id = RFIFOL(fd,2); + if (id == -1) { + if (defaultlanguage == 'F') { + printf("Impossible de trouver le compte [%s]. Le compte n'existe pas.\n", parameters); + ladmin_log("Impossible de trouver le compte [%s]. Le compte n'existe pas." RETCODE, parameters); + } else { + printf("Unabled to find the account [%s]. Account doesn't exist.\n", parameters); + ladmin_log("Unabled to find the account [%s]. Account doesn't exist." RETCODE, parameters); + } + } else if (strlen(userid) == 0) { + if (defaultlanguage == 'F') { + printf("Impossible de trouver le compte [id: %s]. Le compte n'existe pas.\n", parameters); + ladmin_log("Impossible de trouver le compte [id: %s]. Le compte n'existe pas." RETCODE, parameters); + } else { + printf("Unabled to find the account [id: %s]. Account doesn't exist.\n", parameters); + ladmin_log("Unabled to find the account [id: %s]. Account doesn't exist." RETCODE, parameters); + } + } else { + if (defaultlanguage == 'F') { + ladmin_log("R馗eption d'information concernant un compte." RETCODE); + printf("Le compte a les caract駻istiques suivantes:\n"); + } else { + ladmin_log("Receiving information about an account." RETCODE); + printf("The account is set with:\n"); + } + if (RFIFOB(fd,6) == 0) { + printf(" Id: %d (non-GM)\n", id); + } else { + if (defaultlanguage == 'F') { + printf(" Id: %d (GM niveau %d)\n", id, (int)RFIFOB(fd,6)); + } else { + printf(" Id: %d (GM level %d)\n", id, (int)RFIFOB(fd,6)); + } + } + if (defaultlanguage == 'F') { + printf(" Nom: '%s'\n", userid); + if (RFIFOB(fd,31) == 0) + printf(" Sexe: Femme\n"); + else if (RFIFOB(fd,31) == 1) + printf(" Sexe: Male\n"); + else + printf(" Sexe: Serveur\n"); + } else { + printf(" Name: '%s'\n", userid); + if (RFIFOB(fd,31) == 0) + printf(" Sex: Female\n"); + else if (RFIFOB(fd,31) == 1) + printf(" Sex: Male\n"); + else + printf(" Sex: Server\n"); + } + printf(" E-mail: %s\n", email); + switch(RFIFOL(fd,36)) { + case 0: + if (defaultlanguage == 'F') + printf(" Statut: 0 [Compte Ok]\n"); + else + printf(" Statut: 0 [Account OK]\n"); + break; + case 1: + printf(" Statut: 1 [Unregistered ID]\n"); + break; + case 2: + printf(" Statut: 2 [Incorrect Password]\n"); + break; + case 3: + printf(" Statut: 3 [This ID is expired]\n"); + break; + case 4: + printf(" Statut: 4 [Rejected from Server]\n"); + break; + case 5: + printf(" Statut: 5 [You have been blocked by the GM Team]\n"); + break; + case 6: + printf(" Statut: 6 [Your Game's EXE file is not the latest version]\n"); + break; + case 7: + printf(" Statut: 7 [You are Prohibited to log in until %s]\n", error_message); + break; + case 8: + printf(" Statut: 8 [Server is jammed due to over populated]\n"); + break; + case 9: + printf(" Statut: 9 [No MSG]\n"); + break; + default: // 100 + printf(" Statut: %d [This ID is totally erased]\n", (int)RFIFOL(fd,36)); + break; + } + if (defaultlanguage == 'F') { + if (ban_until_time == 0) { + printf(" Banissement: non banni.\n"); + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(&ban_until_time)); + printf(" Banissement: jusqu'au %s.\n", tmpstr); + } + if (RFIFOL(fd,32) > 1) + printf(" Compteur: %d connexions.\n", (int)RFIFOL(fd,32)); + else + printf(" Compteur: %d connexion.\n", (int)RFIFOL(fd,32)); + printf(" Derni鑽e connexion le: %s (ip: %s)\n", lastlogin, last_ip); + if (connect_until_time == 0) { + printf(" Limite de validit: illimit.\n"); + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(&connect_until_time)); + printf(" Limite de validit: jusqu'au %s.\n", tmpstr); + } + } else { + if (ban_until_time == 0) { + printf(" Banishment: not banished.\n"); + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(&ban_until_time)); + printf(" Banishment: until %s.\n", tmpstr); + } + if (RFIFOL(fd,32) > 1) + printf(" Count: %d connections.\n", (int)RFIFOL(fd,32)); + else + printf(" Count: %d connection.\n", (int)RFIFOL(fd,32)); + printf(" Last connection at: %s (ip: %s)\n", lastlogin, last_ip); + if (connect_until_time == 0) { + printf(" Validity limit: unlimited.\n"); + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(&connect_until_time)); + printf(" Validity limit: until %s.\n", tmpstr); + } + } + printf(" Memo: '%s'\n", memo); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,150 + RFIFOW(fd,148)); + break; + + default: + printf("Remote administration has been disconnected (unknown packet).\n"); + ladmin_log("'End of connection, unknown packet." RETCODE); + session[fd]->eof = 1; + return 0; + } + } + + // if we don't wait new packets, do the prompt + prompt(); + + return 0; +} + +//------------------------------------ +// Function to connect to login-server +//------------------------------------ +int Connect_login_server(void) { + if (defaultlanguage == 'F') { + printf("Essai de connection au server de logins...\n"); + ladmin_log("Essai de connection au server de logins..." RETCODE); + } else { + printf("Attempt to connect to login-server...\n"); + ladmin_log("Attempt to connect to login-server..." RETCODE); + } + + login_fd = make_connection(login_ip, loginserverport); + if (login_fd == -1) + { //Might not be the most elegant way to handle this, but I've never used ladmin so I dunno what else you could do. [Skotlex] + printf("Error: Failed to connect to Login Server\n"); + exit(1); + } +#ifdef PASSWORDENC + if (passenc == 0) { +#endif + WFIFOHEAD(login_fd,28); + WFIFOW(login_fd,0) = 0x7918; // Request for administation login + WFIFOW(login_fd,2) = 0; // no encrypted + memcpy(WFIFOP(login_fd,4), loginserveradminpassword, 24); + WFIFOSET(login_fd,28); + bytes_to_read = 1; + if (defaultlanguage == 'F') { + printf("Envoi du mot de passe...\n"); + ladmin_log("Envoi du mot de passe..." RETCODE); + } else { + printf("Sending of the password...\n"); + ladmin_log("Sending of the password..." RETCODE); + } +#ifdef PASSWORDENC + } else { + WFIFOHEAD(login_fd,2); + WFIFOW(login_fd,0) = 0x791a; // Sending request about the coding key + WFIFOSET(login_fd,2); + bytes_to_read = 1; + if (defaultlanguage == 'F') { + printf("Demande de la clef MD5...\n"); + ladmin_log("Demande de la clef MD5..." RETCODE); + } else { + printf("Request about the MD5 key...\n"); + ladmin_log("Request about the MD5 key..." RETCODE); + } + } +#endif + + return 0; +} + +//------------------------------------------------- +// Return numerical value of a switch configuration +// on/off, english, fran軋is, deutsch, espaol +//------------------------------------------------- +int config_switch(const char *str) { + if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) + return 1; + if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) + return 0; + + return atoi(str); +} + +//----------------------------------- +// Reading general configuration file +//----------------------------------- +int ladmin_config_read(const char *cfgName) { + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp = fopen(cfgName, "r"); + if (fp == NULL) { + if (defaultlanguage == 'F') { + printf("\033[0mFichier de configuration (%s) non trouv.\n", cfgName); + } else { + printf("\033[0mConfiguration file (%s) not found.\n", cfgName); + } + return 1; + } + + if (defaultlanguage == 'F') { + printf("\033[0m---D饕ut de lecture du fichier de configuration Ladmin (%s)\n", cfgName); + } else { + printf("\033[0m---Start reading of Ladmin configuration file (%s)\n", cfgName); + } + while(fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof(line)-1] = '\0'; + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { + remove_control_chars((unsigned char *) w1); + remove_control_chars((unsigned char *) w2); + + if(strcmpi(w1,"login_ip")==0){ + struct hostent *h = gethostbyname (w2); + if (h != NULL) { + if (defaultlanguage == 'F') { + printf("Adresse du serveur de logins: %s -> %d.%d.%d.%d\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + } else { + printf("Login server IP address: %s -> %d.%d.%d.%d\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + } + sprintf(loginserverip, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + } else + memcpy(loginserverip, w2, 16); + } else if (strcmpi(w1, "login_port") == 0) { + loginserverport = atoi(w2); + } else if (strcmpi(w1, "admin_pass") == 0) { + strncpy(loginserveradminpassword, w2, sizeof(loginserveradminpassword)); + loginserveradminpassword[sizeof(loginserveradminpassword)-1] = '\0'; +#ifdef PASSWORDENC + } else if (strcmpi(w1, "passenc") == 0) { + passenc = atoi(w2); + if (passenc < 0 || passenc > 2) + passenc = 0; +#endif + } else if (strcmpi(w1, "defaultlanguage") == 0) { + if (w2[0] == 'F' || w2[0] == 'E') + defaultlanguage = w2[0]; + } else if (strcmpi(w1, "ladmin_log_filename") == 0) { + strncpy(ladmin_log_filename, w2, sizeof(ladmin_log_filename)); + ladmin_log_filename[sizeof(ladmin_log_filename)-1] = '\0'; + } else if (strcmpi(w1, "date_format") == 0) { // note: never have more than 19 char for the date! + switch (atoi(w2)) { + case 0: + strcpy(date_format, "%d-%m-%Y %H:%M:%S"); // 31-12-2004 23:59:59 + break; + case 1: + strcpy(date_format, "%m-%d-%Y %H:%M:%S"); // 12-31-2004 23:59:59 + break; + case 2: + strcpy(date_format, "%Y-%d-%m %H:%M:%S"); // 2004-31-12 23:59:59 + break; + case 3: + strcpy(date_format, "%Y-%m-%d %H:%M:%S"); // 2004-12-31 23:59:59 + break; + } + } else if (strcmpi(w1, "import") == 0) { + ladmin_config_read(w2); + } + } + } + fclose(fp); + + login_ip = inet_addr(loginserverip); + + if (defaultlanguage == 'F') { + printf("---Lecture du fichier de configuration Ladmin termin馥.\n"); + } else { + printf("---End reading of Ladmin configuration file.\n"); + } + + return 0; +} + +//-------------------------------------- +// Function called at exit of the server +//-------------------------------------- +void do_final(void) { + + if (already_exit_function == 0) { + delete_session(login_fd); + + if (defaultlanguage == 'F') { + printf("\033[0m----Fin de Ladmin (fin normale avec fermeture de tous les fichiers).\n"); + ladmin_log("----Fin de Ladmin (fin normale avec fermeture de tous les fichiers)." RETCODE); + } else { + printf("\033[0m----End of Ladmin (normal end with closing of all files).\n"); + ladmin_log("----End of Ladmin (normal end with closing of all files)." RETCODE); + } + + already_exit_function = 1; + } +} + +//------------------------ +// Main function of ladmin +//------------------------ +int do_init(int argc, char **argv) +{ + int next; + socket_init(); + + // read ladmin configuration + ladmin_config_read((argc > 1) ? argv[1] : LADMIN_CONF_NAME); + + ladmin_log(""); + if (defaultlanguage == 'F') { + ladmin_log("Fichier de configuration lu." RETCODE); + } else { + ladmin_log("Configuration file readed." RETCODE); + } + + srand(time(NULL)); + + set_defaultparse(parse_fromlogin); + + if (defaultlanguage == 'F') { + printf("Outil d'administration distance de eAthena.\n"); + printf("(pour eAthena version %d.%d.%d.)\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION); + } else { + printf("EAthena login-server administration tool.\n"); + printf("(for eAthena version %d.%d.%d.)\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION); + } + + if (defaultlanguage == 'F') { + ladmin_log("Ladmin est pr黎." RETCODE); + printf("Ladmin est \033[1;32mpr黎\033[0m.\n\n"); + } else { + ladmin_log("Ladmin is ready." RETCODE); + printf("Ladmin is \033[1;32mready\033[0m.\n\n"); + } + + Connect_login_server(); + + // minimalist core doesn't have sockets parsing, + // so we have to do this ourselves + while (runflag) { + next = do_timer(gettick_nocache()); + do_sendrecv(next); +#ifndef TURBO + do_parsepacket(); +#endif + } + + return 0; +} diff --git a/src/ladmin/ladmin.h b/src/ladmin/ladmin.h index 5a1e8311a..64a67146e 100644 --- a/src/ladmin/ladmin.h +++ b/src/ladmin/ladmin.h @@ -1,13 +1,13 @@ -// (c) eAthena Dev Team - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _LADMIN_H_ -#define _LADMIN_H_ - -#define LADMIN_CONF_NAME "conf/ladmin_athena.conf" -#define PASSWORDENC 3 // A definition is given when making an encryption password correspond. - // It is 1 at the time of passwordencrypt. - // It is made into 2 at the time of passwordencrypt2. - // When it is made 3, it corresponds to both. - -#endif +// (c) eAthena Dev Team - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _LADMIN_H_ +#define _LADMIN_H_ + +#define LADMIN_CONF_NAME "conf/ladmin_athena.conf" +#define PASSWORDENC 3 // A definition is given when making an encryption password correspond. + // It is 1 at the time of passwordencrypt. + // It is made into 2 at the time of passwordencrypt2. + // When it is made 3, it corresponds to both. + +#endif diff --git a/src/ladmin/md5calc.c b/src/ladmin/md5calc.c index f0acb4679..b50071ee3 100644 --- a/src/ladmin/md5calc.c +++ b/src/ladmin/md5calc.c @@ -1,239 +1,239 @@ -// (c) eAthena Dev Team - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -/*********************************************************** - * md5 calculation algorithm - * - * The source code referred to the following URL. - * http://www.geocities.co.jp/SiliconValley-Oakland/8878/lab17/lab17.html - * - ***********************************************************/ - -#include "md5calc.h" -#include -#include - -#ifndef UINT_MAX -#define UINT_MAX 4294967295U -#endif - -// Global variable -static unsigned int *pX; - -// Stirng Table -static const unsigned int T[] = { - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0 - 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4 - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8 - 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12 - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16 - 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, //20 - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24 - 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28 - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32 - 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36 - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, //40 - 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44 - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48 - 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52 - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56 - 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 //60 -}; - -// ROTATE_LEFT The left is made to rotate x [ n-bit ]. This is diverted as it is from RFC. -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - -// The function used for other calculation -static unsigned int F(unsigned int X, unsigned int Y, unsigned int Z) -{ - return (X & Y) | (~X & Z); -} -static unsigned int G(unsigned int X, unsigned int Y, unsigned int Z) -{ - return (X & Z) | (Y & ~Z); -} -static unsigned int H(unsigned int X, unsigned int Y, unsigned int Z) -{ - return X ^ Y ^ Z; -} -static unsigned int I(unsigned int X, unsigned int Y, unsigned int Z) -{ - return Y ^ (X | ~Z); -} - -static unsigned int Round(unsigned int a, unsigned int b, unsigned int FGHI, - unsigned int k, unsigned int s, unsigned int i) -{ - return b + ROTATE_LEFT(a + FGHI + pX[k] + T[i], s); -} - -static void Round1(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, F(b,c,d), k, s, i); -} -static void Round2(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, G(b,c,d), k, s, i); -} -static void Round3(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, H(b,c,d), k, s, i); -} -static void Round4(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, I(b,c,d), k, s, i); -} - -static void MD5_Round_Calculate(const unsigned char *block, - unsigned int *A2, unsigned int *B2, unsigned int *C2, unsigned int *D2) -{ - //create X It is since it is required. - unsigned int X[16]; //512bit 64byte - int j,k; - - //Save A as AA, B as BB, C as CC, and and D as DD (saving of A, B, C, and D) - unsigned int A=*A2, B=*B2, C=*C2, D=*D2; - unsigned int AA = A,BB = B,CC = C,DD = D; - - //It is a large region variable reluctantly because of calculation of a round. . . for Round1...4 - pX = X; - - //Copy block(padding_message) i into X - for (j=0,k=0; j<64; j+=4,k++) - X[k] = ( (unsigned int )block[j] ) // 8byte*4 -> 32byte conversion - | ( ((unsigned int )block[j+1]) << 8 ) // A function called Decode as used in the field of RFC - | ( ((unsigned int )block[j+2]) << 16 ) - | ( ((unsigned int )block[j+3]) << 24 ); - - - //Round 1 - Round1(&A,B,C,D, 0, 7, 0); Round1(&D,A,B,C, 1, 12, 1); Round1(&C,D,A,B, 2, 17, 2); Round1(&B,C,D,A, 3, 22, 3); - Round1(&A,B,C,D, 4, 7, 4); Round1(&D,A,B,C, 5, 12, 5); Round1(&C,D,A,B, 6, 17, 6); Round1(&B,C,D,A, 7, 22, 7); - Round1(&A,B,C,D, 8, 7, 8); Round1(&D,A,B,C, 9, 12, 9); Round1(&C,D,A,B, 10, 17, 10); Round1(&B,C,D,A, 11, 22, 11); - Round1(&A,B,C,D, 12, 7, 12); Round1(&D,A,B,C, 13, 12, 13); Round1(&C,D,A,B, 14, 17, 14); Round1(&B,C,D,A, 15, 22, 15); - - //Round 2 - Round2(&A,B,C,D, 1, 5, 16); Round2(&D,A,B,C, 6, 9, 17); Round2(&C,D,A,B, 11, 14, 18); Round2(&B,C,D,A, 0, 20, 19); - Round2(&A,B,C,D, 5, 5, 20); Round2(&D,A,B,C, 10, 9, 21); Round2(&C,D,A,B, 15, 14, 22); Round2(&B,C,D,A, 4, 20, 23); - Round2(&A,B,C,D, 9, 5, 24); Round2(&D,A,B,C, 14, 9, 25); Round2(&C,D,A,B, 3, 14, 26); Round2(&B,C,D,A, 8, 20, 27); - Round2(&A,B,C,D, 13, 5, 28); Round2(&D,A,B,C, 2, 9, 29); Round2(&C,D,A,B, 7, 14, 30); Round2(&B,C,D,A, 12, 20, 31); - - //Round 3 - Round3(&A,B,C,D, 5, 4, 32); Round3(&D,A,B,C, 8, 11, 33); Round3(&C,D,A,B, 11, 16, 34); Round3(&B,C,D,A, 14, 23, 35); - Round3(&A,B,C,D, 1, 4, 36); Round3(&D,A,B,C, 4, 11, 37); Round3(&C,D,A,B, 7, 16, 38); Round3(&B,C,D,A, 10, 23, 39); - Round3(&A,B,C,D, 13, 4, 40); Round3(&D,A,B,C, 0, 11, 41); Round3(&C,D,A,B, 3, 16, 42); Round3(&B,C,D,A, 6, 23, 43); - Round3(&A,B,C,D, 9, 4, 44); Round3(&D,A,B,C, 12, 11, 45); Round3(&C,D,A,B, 15, 16, 46); Round3(&B,C,D,A, 2, 23, 47); - - //Round 4 - Round4(&A,B,C,D, 0, 6, 48); Round4(&D,A,B,C, 7, 10, 49); Round4(&C,D,A,B, 14, 15, 50); Round4(&B,C,D,A, 5, 21, 51); - Round4(&A,B,C,D, 12, 6, 52); Round4(&D,A,B,C, 3, 10, 53); Round4(&C,D,A,B, 10, 15, 54); Round4(&B,C,D,A, 1, 21, 55); - Round4(&A,B,C,D, 8, 6, 56); Round4(&D,A,B,C, 15, 10, 57); Round4(&C,D,A,B, 6, 15, 58); Round4(&B,C,D,A, 13, 21, 59); - Round4(&A,B,C,D, 4, 6, 60); Round4(&D,A,B,C, 11, 10, 61); Round4(&C,D,A,B, 2, 15, 62); Round4(&B,C,D,A, 9, 21, 63); - - // Then perform the following additions. (let's add) - *A2 = A + AA; - *B2 = B + BB; - *C2 = C + CC; - *D2 = D + DD; - - //The clearance of confidential information - memset(pX, 0, sizeof(X)); -} - -//------------------------------------------------------------------- -// The function for the exteriors - -/** output is the coded binary in the character sequence which wants to code string. */ -void MD5_String2binary(const char * string, char * output) -{ -//var - /*8bit*/ - unsigned char padding_message[64]; //Extended message 512bit 64byte - unsigned char *pstring; //The position of string in the present scanning notes is held. - -// unsigned char digest[16]; - /*32bit*/ - unsigned int string_byte_len, //The byte chief of string is held. - string_bit_len, //The bit length of string is held. - copy_len, //The number of bytes which is used by 1-3 and which remained - msg_digest[4]; //Message digest 128bit 4byte - unsigned int *A = &msg_digest[0], //The message digest in accordance with RFC (reference) - *B = &msg_digest[1], - *C = &msg_digest[2], - *D = &msg_digest[3]; - int i; - -//prog - //Step 3.Initialize MD Buffer (although it is the initialization; step 3 of A, B, C, and D -- unavoidable -- a head) - *A = 0x67452301; - *B = 0xefcdab89; - *C = 0x98badcfe; - *D = 0x10325476; - - //Step 1.Append Padding Bits (extension of a mark bit) - //1-1 - string_byte_len = strlen(string); //The byte chief of a character sequence is acquired. - pstring = (unsigned char *)string; //The position of the present character sequence is set. - - //1-2 Repeat calculation until length becomes less than 64 bytes. - for (i=string_byte_len; 64<=i; i-=64,pstring+=64) - MD5_Round_Calculate(pstring, A,B,C,D); - - //1-3 - copy_len = string_byte_len % 64; //The number of bytes which remained is computed. - strncpy((char *)padding_message, (char *)pstring, copy_len); //A message is copied to an extended bit sequence. - memset(padding_message+copy_len, 0, 64 - copy_len); //It buries by 0 until it becomes extended bit length. - padding_message[copy_len] |= 0x80; //The next of a message is 1. - - //1-4 - //If 56 bytes or more (less than 64 bytes) of remainder becomes, it will calculate by extending to 64 bytes. - if (56 <= copy_len) { - MD5_Round_Calculate(padding_message, A,B,C,D); - memset(padding_message, 0, 56); //56 bytes is newly fill uped with 0. - } - - - //Step 2.Append Length (the information on length is added) - string_bit_len = string_byte_len * 8; //From the byte chief to bit length (32 bytes of low rank) - memcpy(&padding_message[56], &string_bit_len, 4); //32 bytes of low rank is set. - - //When bit length cannot be expressed in 32 bytes of low rank, it is a beam raising to a higher rank. - if (UINT_MAX / 8 < string_byte_len) { - unsigned int high = (string_byte_len - UINT_MAX / 8) * 8; - memcpy(&padding_message[60], &high, 4); - } else - memset(&padding_message[60], 0, 4); //In this case, it is good for a higher rank at 0. - - //Step 4.Process Message in 16-Word Blocks (calculation of MD5) - MD5_Round_Calculate(padding_message, A,B,C,D); - - - //Step 5.Output (output) - memcpy(output,msg_digest,16); -// memcpy (digest, msg_digest, and 16); //8 byte*4 < - 32byte conversion A function called Encode as used in the field of RFC -/* sprintf(output, - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - digest[ 0], digest[ 1], digest[ 2], digest[ 3], - digest[ 4], digest[ 5], digest[ 6], digest[ 7], - digest[ 8], digest[ 9], digest[10], digest[11], - digest[12], digest[13], digest[14], digest[15]);*/ -} - -/** output is the coded character sequence in the character sequence which wants to code string. */ -void MD5_String(const char * string, char * output) -{ - unsigned char digest[16]; - - MD5_String2binary(string,(char*)digest); - sprintf(output, - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - digest[ 0], digest[ 1], digest[ 2], digest[ 3], - digest[ 4], digest[ 5], digest[ 6], digest[ 7], - digest[ 8], digest[ 9], digest[10], digest[11], - digest[12], digest[13], digest[14], digest[15]); -} - +// (c) eAthena Dev Team - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +/*********************************************************** + * md5 calculation algorithm + * + * The source code referred to the following URL. + * http://www.geocities.co.jp/SiliconValley-Oakland/8878/lab17/lab17.html + * + ***********************************************************/ + +#include "md5calc.h" +#include +#include + +#ifndef UINT_MAX +#define UINT_MAX 4294967295U +#endif + +// Global variable +static unsigned int *pX; + +// Stirng Table +static const unsigned int T[] = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0 + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4 + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8 + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12 + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16 + 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, //20 + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24 + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28 + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32 + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36 + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, //40 + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44 + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48 + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52 + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56 + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 //60 +}; + +// ROTATE_LEFT The left is made to rotate x [ n-bit ]. This is diverted as it is from RFC. +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +// The function used for other calculation +static unsigned int F(unsigned int X, unsigned int Y, unsigned int Z) +{ + return (X & Y) | (~X & Z); +} +static unsigned int G(unsigned int X, unsigned int Y, unsigned int Z) +{ + return (X & Z) | (Y & ~Z); +} +static unsigned int H(unsigned int X, unsigned int Y, unsigned int Z) +{ + return X ^ Y ^ Z; +} +static unsigned int I(unsigned int X, unsigned int Y, unsigned int Z) +{ + return Y ^ (X | ~Z); +} + +static unsigned int Round(unsigned int a, unsigned int b, unsigned int FGHI, + unsigned int k, unsigned int s, unsigned int i) +{ + return b + ROTATE_LEFT(a + FGHI + pX[k] + T[i], s); +} + +static void Round1(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, F(b,c,d), k, s, i); +} +static void Round2(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, G(b,c,d), k, s, i); +} +static void Round3(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, H(b,c,d), k, s, i); +} +static void Round4(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, I(b,c,d), k, s, i); +} + +static void MD5_Round_Calculate(const unsigned char *block, + unsigned int *A2, unsigned int *B2, unsigned int *C2, unsigned int *D2) +{ + //create X It is since it is required. + unsigned int X[16]; //512bit 64byte + int j,k; + + //Save A as AA, B as BB, C as CC, and and D as DD (saving of A, B, C, and D) + unsigned int A=*A2, B=*B2, C=*C2, D=*D2; + unsigned int AA = A,BB = B,CC = C,DD = D; + + //It is a large region variable reluctantly because of calculation of a round. . . for Round1...4 + pX = X; + + //Copy block(padding_message) i into X + for (j=0,k=0; j<64; j+=4,k++) + X[k] = ( (unsigned int )block[j] ) // 8byte*4 -> 32byte conversion + | ( ((unsigned int )block[j+1]) << 8 ) // A function called Decode as used in the field of RFC + | ( ((unsigned int )block[j+2]) << 16 ) + | ( ((unsigned int )block[j+3]) << 24 ); + + + //Round 1 + Round1(&A,B,C,D, 0, 7, 0); Round1(&D,A,B,C, 1, 12, 1); Round1(&C,D,A,B, 2, 17, 2); Round1(&B,C,D,A, 3, 22, 3); + Round1(&A,B,C,D, 4, 7, 4); Round1(&D,A,B,C, 5, 12, 5); Round1(&C,D,A,B, 6, 17, 6); Round1(&B,C,D,A, 7, 22, 7); + Round1(&A,B,C,D, 8, 7, 8); Round1(&D,A,B,C, 9, 12, 9); Round1(&C,D,A,B, 10, 17, 10); Round1(&B,C,D,A, 11, 22, 11); + Round1(&A,B,C,D, 12, 7, 12); Round1(&D,A,B,C, 13, 12, 13); Round1(&C,D,A,B, 14, 17, 14); Round1(&B,C,D,A, 15, 22, 15); + + //Round 2 + Round2(&A,B,C,D, 1, 5, 16); Round2(&D,A,B,C, 6, 9, 17); Round2(&C,D,A,B, 11, 14, 18); Round2(&B,C,D,A, 0, 20, 19); + Round2(&A,B,C,D, 5, 5, 20); Round2(&D,A,B,C, 10, 9, 21); Round2(&C,D,A,B, 15, 14, 22); Round2(&B,C,D,A, 4, 20, 23); + Round2(&A,B,C,D, 9, 5, 24); Round2(&D,A,B,C, 14, 9, 25); Round2(&C,D,A,B, 3, 14, 26); Round2(&B,C,D,A, 8, 20, 27); + Round2(&A,B,C,D, 13, 5, 28); Round2(&D,A,B,C, 2, 9, 29); Round2(&C,D,A,B, 7, 14, 30); Round2(&B,C,D,A, 12, 20, 31); + + //Round 3 + Round3(&A,B,C,D, 5, 4, 32); Round3(&D,A,B,C, 8, 11, 33); Round3(&C,D,A,B, 11, 16, 34); Round3(&B,C,D,A, 14, 23, 35); + Round3(&A,B,C,D, 1, 4, 36); Round3(&D,A,B,C, 4, 11, 37); Round3(&C,D,A,B, 7, 16, 38); Round3(&B,C,D,A, 10, 23, 39); + Round3(&A,B,C,D, 13, 4, 40); Round3(&D,A,B,C, 0, 11, 41); Round3(&C,D,A,B, 3, 16, 42); Round3(&B,C,D,A, 6, 23, 43); + Round3(&A,B,C,D, 9, 4, 44); Round3(&D,A,B,C, 12, 11, 45); Round3(&C,D,A,B, 15, 16, 46); Round3(&B,C,D,A, 2, 23, 47); + + //Round 4 + Round4(&A,B,C,D, 0, 6, 48); Round4(&D,A,B,C, 7, 10, 49); Round4(&C,D,A,B, 14, 15, 50); Round4(&B,C,D,A, 5, 21, 51); + Round4(&A,B,C,D, 12, 6, 52); Round4(&D,A,B,C, 3, 10, 53); Round4(&C,D,A,B, 10, 15, 54); Round4(&B,C,D,A, 1, 21, 55); + Round4(&A,B,C,D, 8, 6, 56); Round4(&D,A,B,C, 15, 10, 57); Round4(&C,D,A,B, 6, 15, 58); Round4(&B,C,D,A, 13, 21, 59); + Round4(&A,B,C,D, 4, 6, 60); Round4(&D,A,B,C, 11, 10, 61); Round4(&C,D,A,B, 2, 15, 62); Round4(&B,C,D,A, 9, 21, 63); + + // Then perform the following additions. (let's add) + *A2 = A + AA; + *B2 = B + BB; + *C2 = C + CC; + *D2 = D + DD; + + //The clearance of confidential information + memset(pX, 0, sizeof(X)); +} + +//------------------------------------------------------------------- +// The function for the exteriors + +/** output is the coded binary in the character sequence which wants to code string. */ +void MD5_String2binary(const char * string, char * output) +{ +//var + /*8bit*/ + unsigned char padding_message[64]; //Extended message 512bit 64byte + unsigned char *pstring; //The position of string in the present scanning notes is held. + +// unsigned char digest[16]; + /*32bit*/ + unsigned int string_byte_len, //The byte chief of string is held. + string_bit_len, //The bit length of string is held. + copy_len, //The number of bytes which is used by 1-3 and which remained + msg_digest[4]; //Message digest 128bit 4byte + unsigned int *A = &msg_digest[0], //The message digest in accordance with RFC (reference) + *B = &msg_digest[1], + *C = &msg_digest[2], + *D = &msg_digest[3]; + int i; + +//prog + //Step 3.Initialize MD Buffer (although it is the initialization; step 3 of A, B, C, and D -- unavoidable -- a head) + *A = 0x67452301; + *B = 0xefcdab89; + *C = 0x98badcfe; + *D = 0x10325476; + + //Step 1.Append Padding Bits (extension of a mark bit) + //1-1 + string_byte_len = strlen(string); //The byte chief of a character sequence is acquired. + pstring = (unsigned char *)string; //The position of the present character sequence is set. + + //1-2 Repeat calculation until length becomes less than 64 bytes. + for (i=string_byte_len; 64<=i; i-=64,pstring+=64) + MD5_Round_Calculate(pstring, A,B,C,D); + + //1-3 + copy_len = string_byte_len % 64; //The number of bytes which remained is computed. + strncpy((char *)padding_message, (char *)pstring, copy_len); //A message is copied to an extended bit sequence. + memset(padding_message+copy_len, 0, 64 - copy_len); //It buries by 0 until it becomes extended bit length. + padding_message[copy_len] |= 0x80; //The next of a message is 1. + + //1-4 + //If 56 bytes or more (less than 64 bytes) of remainder becomes, it will calculate by extending to 64 bytes. + if (56 <= copy_len) { + MD5_Round_Calculate(padding_message, A,B,C,D); + memset(padding_message, 0, 56); //56 bytes is newly fill uped with 0. + } + + + //Step 2.Append Length (the information on length is added) + string_bit_len = string_byte_len * 8; //From the byte chief to bit length (32 bytes of low rank) + memcpy(&padding_message[56], &string_bit_len, 4); //32 bytes of low rank is set. + + //When bit length cannot be expressed in 32 bytes of low rank, it is a beam raising to a higher rank. + if (UINT_MAX / 8 < string_byte_len) { + unsigned int high = (string_byte_len - UINT_MAX / 8) * 8; + memcpy(&padding_message[60], &high, 4); + } else + memset(&padding_message[60], 0, 4); //In this case, it is good for a higher rank at 0. + + //Step 4.Process Message in 16-Word Blocks (calculation of MD5) + MD5_Round_Calculate(padding_message, A,B,C,D); + + + //Step 5.Output (output) + memcpy(output,msg_digest,16); +// memcpy (digest, msg_digest, and 16); //8 byte*4 < - 32byte conversion A function called Encode as used in the field of RFC +/* sprintf(output, + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + digest[ 0], digest[ 1], digest[ 2], digest[ 3], + digest[ 4], digest[ 5], digest[ 6], digest[ 7], + digest[ 8], digest[ 9], digest[10], digest[11], + digest[12], digest[13], digest[14], digest[15]);*/ +} + +/** output is the coded character sequence in the character sequence which wants to code string. */ +void MD5_String(const char * string, char * output) +{ + unsigned char digest[16]; + + MD5_String2binary(string,(char*)digest); + sprintf(output, + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + digest[ 0], digest[ 1], digest[ 2], digest[ 3], + digest[ 4], digest[ 5], digest[ 6], digest[ 7], + digest[ 8], digest[ 9], digest[10], digest[11], + digest[12], digest[13], digest[14], digest[15]); +} + diff --git a/src/ladmin/md5calc.h b/src/ladmin/md5calc.h index 1c42b16d9..ad46af760 100644 --- a/src/ladmin/md5calc.h +++ b/src/ladmin/md5calc.h @@ -1,10 +1,10 @@ -// (c) eAthena Dev Team - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _MD5CALC_H_ -#define _MD5CALC_H_ - -void MD5_String(const char * string, char * output); -void MD5_String2binary(const char * string, char * output); - -#endif +// (c) eAthena Dev Team - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _MD5CALC_H_ +#define _MD5CALC_H_ + +void MD5_String(const char * string, char * output); +void MD5_String2binary(const char * string, char * output); + +#endif diff --git a/src/login/login.c b/src/login/login.c index 4582a9c00..e30b44f3e 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -1,4198 +1,4198 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -// new version of the login-server by [Yor] - -#include -#ifdef __WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#else -#include -#include -#include -#include -#endif -#include -#include -#include // for stat/lstat/fstat -#include -#include -#include -#include - -#include "../common/core.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/mmo.h" -#include "../common/showmsg.h" -#include "../common/version.h" -#include "../common/db.h" -#include "../common/lock.h" -#include "../common/malloc.h" -#include "../common/strlib.h" -#include "../common/showmsg.h" -#include "login.h" - -#ifdef PASSWORDENC -#include "md5calc.h" -#endif - -int account_id_count = START_ACCOUNT_NUM; -int server_num; -int new_account_flag = 0; -in_addr_t bind_ip= 0; -char bind_ip_str[128]; -int login_port = 6900; - -// Advanced subnet check [LuzZza] -struct _subnet { - long subnet; - long mask; - long char_ip; - long map_ip; -} subnet[16]; - -int subnet_count = 0; - -int use_dnsbl=0; // [Zido] -char dnsbl_servs[1024]; // [Zido] - -char account_filename[1024] = "save/account.txt"; -char GM_account_filename[1024] = "conf/GM_account.txt"; -char login_log_filename[1024] = "log/login.log"; -FILE *log_fp = NULL; -char login_log_unknown_packets_filename[1024] = "log/login_unknown_packets.log"; -char date_format[32] = "%Y-%m-%d %H:%M:%S"; -int save_unknown_packets = 0; -long creation_time_GM_account_file; -int gm_account_filename_check_timer = 15; // Timer to check if GM_account file has been changed and reload GM account automaticaly (in seconds; default: 15) - -int log_login = 1; - -int display_parse_login = 0; // 0: no, 1: yes -int display_parse_admin = 0; // 0: no, 1: yes -int display_parse_fromchar = 0; // 0: no, 1: yes (without packet 0x2714), 2: all packets - -struct mmo_char_server server[MAX_SERVERS]; -int server_fd[MAX_SERVERS]; - -int login_fd; - -static int online_check=1; //When set to 1, login server rejects incoming players that are already registered as online. [Skotlex] -//Account flood protection [Kevin] -unsigned int new_reg_tick=0; -int allowed_regs=1; -int num_regs=0; -int time_allowed=10; //Init this to 10 seconds. [Skotlex] - -enum { - ACO_DENY_ALLOW = 0, - ACO_ALLOW_DENY, - ACO_MUTUAL_FAILTURE, - ACO_STRSIZE = 128, -}; - -int access_order = ACO_DENY_ALLOW; -int access_allownum = 0; -int access_denynum = 0; -char *access_allow = NULL; -char *access_deny = NULL; - -int access_ladmin_allownum = 0; -char *access_ladmin_allow = NULL; - -int min_level_to_connect = 0; // minimum level of player/GM (0: player, 1-99: gm) to connect on the server -int add_to_unlimited_account = 0; // Give possibility or not to adjust (ladmin command: timeadd) the time of an unlimited account. -int start_limited_time = -1; // Starting additional sec from now for the limited time at creation of accounts (-1: unlimited time, 0 or more: additional sec from now) -int check_ip_flag = 1; // It's to check IP of a player between login-server and char-server (part of anti-hacking system) - -int check_client_version = 0; //Client version check ON/OFF .. (sirius) -int client_version_to_connect = 20; //Client version needed to connect ..(sirius) -static int ip_sync_interval = 0; - - -struct login_session_data { - unsigned int md5keylen; - char md5key[20]; -}; - -#define AUTH_FIFO_SIZE 256 -struct { - int account_id, login_id1, login_id2; - int ip, sex, delflag; -} auth_fifo[AUTH_FIFO_SIZE]; -int auth_fifo_pos = 0; - -struct online_login_data { - int account_id; - short char_server; - short waiting_disconnect; -}; - -struct auth_dat { - int account_id, sex; - char userid[24], pass[33], lastlogin[24]; // 33 for 32 + NULL terminated - int logincount; - int state; // packet 0x006a value + 1 (0: compte OK) - char email[40]; // e-mail (by default: a@a.com) - char error_message[20]; // Message of error code #6 = Your are Prohibited to log in until %s (packet 0x006a) - time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - char last_ip[16]; // save of last IP of connection - char memo[255]; // a memo field - int account_reg2_num; - struct global_reg account_reg2[ACCOUNT_REG2_NUM]; -} *auth_dat = NULL; - -unsigned int auth_num = 0, auth_max = 0; - -// define the number of times that some players must authentify them before to save account file. -// it's just about normal authentification. If an account is created or modified, save is immediatly done. -// An authentification just change last connected IP and date. It already save in log file. -// set minimum auth change before save: -#define AUTH_BEFORE_SAVE_FILE 10 -// set divider of auth_num to found number of change before save -#define AUTH_SAVE_FILE_DIVIDER 50 -int auth_before_save_file = 0; // Counter. First save when 1st char-server do connection. - -int admin_state = 0; -char admin_pass[24] = ""; -unsigned int GM_num; -unsigned int GM_max=256; -char gm_pass[64] = ""; -int level_new_gm = 60; - -struct gm_account *gm_account_db; - -static struct dbt *online_db; - -int dynamic_pass_failure_ban = 1; -int dynamic_pass_failure_ban_time = 5; -int dynamic_pass_failure_ban_how_many = 3; -int dynamic_pass_failure_ban_how_long = 1; - -int use_md5_passwds = 0; - -int console = 0; - -int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len); - -//------------------------------ -// Writing function of logs file -//------------------------------ -int login_log(char *fmt, ...) { - if (log_login) { - va_list ap; - time_t raw_time; - char tmpstr[2048]; - - if(!log_fp) - log_fp = fopen(login_log_filename, "a"); - - if (log_fp) { - if (fmt[0] == '\0') // jump a line if no message - fprintf(log_fp, RETCODE); - else { - va_start(ap, fmt); - // Platform/Compiler dependant clock() for time check is removed. [Lance] - // clock() is originally used to track processing ticks on program execution. - time(&raw_time); - strftime(tmpstr, 24, date_format, localtime(&raw_time)); - sprintf(tmpstr + strlen(tmpstr), ": %s", fmt); - vfprintf(log_fp, tmpstr, ap); - va_end(ap); - } - fflush(log_fp); // under cygwin or windows, if software is stopped, data are not written in the file -> fflush at every line - } - } - - return 0; -} - -static void* create_online_user(DBKey key, va_list args) { - struct online_login_data *p; - p = aCalloc(1, sizeof(struct online_login_data)); - p->account_id = key.i; - p->char_server = -1; - return p; -} -//----------------------------------------------------- -// Online User Database [Wizputer] -//----------------------------------------------------- - -void add_online_user (int char_server, int account_id) { - struct online_login_data *p; - if (!online_check) - return; - p = idb_ensure(online_db, account_id, create_online_user); - p->char_server = char_server; - p->waiting_disconnect = 0; -} -int is_user_online (int account_id) { - return (idb_get(online_db, account_id) != NULL); -} -void remove_online_user (int account_id) { - if(!online_check) - return; - if (account_id == 99) { // reset all to offline - online_db->clear(online_db, NULL); // purge db - return; - } - idb_remove(online_db,account_id); -} - -int waiting_disconnect_timer(int tid, unsigned int tick, int id, int data) -{ - struct online_login_data *p; - if ((p= idb_get(online_db, id)) != NULL && p->waiting_disconnect) - remove_online_user(p->account_id); - return 0; -} - -static int sync_ip_addresses(int tid, unsigned int tick, int id, int data){ - unsigned char buf[2]; - ShowInfo("IP Sync in progress...\n"); - WBUFW(buf,0) = 0x2735; - charif_sendallwos(-1, buf, 2); - return 0; -} - -//---------------------------------------------------------------------- -// Determine if an account (id) is a GM account -// and returns its level (or 0 if it isn't a GM account or if not found) -//---------------------------------------------------------------------- -int isGM(int account_id) { - unsigned int i; - for(i=0; i < GM_num; i++) - if(gm_account_db[i].account_id == account_id) - return gm_account_db[i].level; - return 0; -} - -//---------------------------------------------------------------------- -// Adds a new GM using acc id and level -//---------------------------------------------------------------------- -void addGM(int account_id, int level) { - unsigned int i; - int do_add = 0; - for(i = 0; i < auth_num; i++) { - if (auth_dat[i].account_id==account_id) { - do_add = 1; - break; - } - } - for(i = 0; i < GM_num; i++) - if (gm_account_db[i].account_id == account_id) { - if (gm_account_db[i].level == level) - ShowWarning("addGM: GM account %d defined twice (same level: %d).\n", account_id, level); - else { - ShowWarning("addGM: GM account %d defined twice (levels: %d and %d).\n", account_id, gm_account_db[i].level, level); - gm_account_db[i].level = level; - } - return; - } - - // if new account - if (i == GM_num && do_add) { - if (GM_num >= GM_max) { - GM_max += 256; - gm_account_db = (struct gm_account*)aRealloc(gm_account_db, sizeof(struct gm_account) * GM_max); - memset(gm_account_db + (GM_max - 256), 0, sizeof(struct gm_account) * 256); - } - gm_account_db[GM_num].account_id = account_id; - gm_account_db[GM_num].level = level; - GM_num++; - if (GM_num >= 4000) { - ShowWarning("4000 GM accounts found. Next GM accounts are not read.\n"); - login_log("***WARNING: 4000 GM accounts found. Next GM accounts are not read." RETCODE); - } - } -} - -//------------------------------------------------------- -// Reading function of GM accounts file (and their level) -//------------------------------------------------------- -int read_gm_account(void) { - char line[512]; - FILE *fp; - int account_id, level; - int line_counter; - struct stat file_stat; - int start_range = 0, end_range = 0, is_range = 0, current_id = 0; - - if(gm_account_db) aFree(gm_account_db); - GM_num = 0; - if(GM_max < 0) GM_max = 256; - gm_account_db = (struct gm_account*)aCalloc(GM_max, sizeof(struct gm_account)); - - // get last modify time/date - if (stat(GM_account_filename, &file_stat)) - creation_time_GM_account_file = 0; // error - else - creation_time_GM_account_file = (long)file_stat.st_mtime; - - if ((fp = fopen(GM_account_filename, "r")) == NULL) { - ShowError("read_gm_account: GM accounts file [%s] not found.\n", GM_account_filename); - ShowError(" Actually, there is no GM accounts on the server.\n"); - login_log("read_gm_account: GM accounts file [%s] not found." RETCODE, GM_account_filename); - login_log(" Actually, there is no GM accounts on the server." RETCODE); - return 1; - } - - line_counter = 0; - // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???) - // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) - while(fgets(line, sizeof(line)-1, fp) && GM_num < 4000) { - line_counter++; - if ((line[0] == '/' && line[1] == '/') || line[0] == '\0' || line[0] == '\n' || line[0] == '\r') - continue; - is_range = (sscanf(line, "%d%*[-~]%d %d",&start_range,&end_range,&level)==3); // ID Range [MC Cameri] - if (!is_range && sscanf(line, "%d %d", &account_id, &level) != 2 && sscanf(line, "%d: %d", &account_id, &level) != 2) - ShowError("read_gm_account: file [%s], invalid 'acount_id|range level' format (line #%d).\n", GM_account_filename, line_counter); - else if (level <= 0) - ShowError("read_gm_account: file [%s] %dth account (line #%d) (invalid level [0 or negative]: %d).\n", GM_account_filename, GM_num+1, line_counter, level); - else { - if (level > 99) { - ShowNotice("read_gm_account: file [%s] %dth account (invalid level, but corrected: %d->99).\n", GM_account_filename, GM_num+1, level); - level = 99; - } - if (is_range) { - if (start_range==end_range) - ShowError("read_gm_account: file [%s] invalid range, beginning of range is equal to end of range (line #%d).\n", GM_account_filename, line_counter); - else if (start_range>end_range) - ShowError("read_gm_account: file [%s] invalid range, beginning of range must be lower than end of range (line #%d).\n", GM_account_filename, line_counter); - else - for (current_id = start_range;current_id<=end_range;current_id++) - addGM(current_id,level); - } else { - addGM(account_id,level); - } - } - } - fclose(fp); - - ShowStatus("read_gm_account: file '%s' read (%d GM accounts found).\n", GM_account_filename, GM_num); - login_log("read_gm_account: file '%s' read (%d GM accounts found)." RETCODE, GM_account_filename, GM_num); - - return 0; -} - -//-------------------------------------------------------------- -// Test of the IP mask -// (ip: IP to be tested, str: mask x.x.x.x/# or x.x.x.x/y.y.y.y) -//-------------------------------------------------------------- -int check_ipmask(unsigned int ip, const unsigned char *str) { - unsigned int mask = 0, i = 0, m, ip2, a0, a1, a2, a3; - unsigned char *p = (unsigned char *)&ip2, *p2 = (unsigned char *)&mask; - - if (sscanf((const char*)str, "%d.%d.%d.%d/%n", &a0, &a1, &a2, &a3, &i) != 4 || i == 0) - return 0; - p[0] = a0; p[1] = a1; p[2] = a2; p[3] = a3; - - if (sscanf((const char*)str+i, "%d.%d.%d.%d", &a0, &a1, &a2, &a3) == 4) { - p2[0] = a0; p2[1] = a1; p2[2] = a2; p2[3] = a3; - mask = ntohl(mask); - } else if (sscanf((const char*)(str+i), "%d", &m) == 1 && m >= 0 && m <= 32) { - for(i = 0; i < m && i < 32; i++) - mask = (mask >> 1) | 0x80000000; - } else { - ShowError("check_ipmask: invalid mask [%s].\n", str); - return 0; - } - -// printf("Tested IP: %08x, network: %08x, network mask: %08x\n", -// (unsigned int)ntohl(ip), (unsigned int)ntohl(ip2), (unsigned int)mask); - return ((ntohl(ip) & mask) == (ntohl(ip2) & mask)); -} - -//--------------------- -// Access control by IP -//--------------------- -int check_ip(unsigned int ip) { - int i; - unsigned char *p = (unsigned char *)&ip; - char buf[20]; - char * access_ip; - enum { ACF_DEF, ACF_ALLOW, ACF_DENY } flag = ACF_DEF; - - if (access_allownum == 0 && access_denynum == 0) - return 1; // When there is no restriction, all IP are authorised. - -// + 012.345.: front match form, or -// all: all IP are matched, or -// 012.345.678.901/24: network form (mask with # of bits), or -// 012.345.678.901/255.255.255.0: network form (mask with ip mask) -// + Note about the DNS resolution (like www.ne.jp, etc.): -// There is no guarantee to have an answer. -// If we have an answer, there is no guarantee to have a 100% correct value. -// And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. -// So, DNS notation isn't authorised for ip checking. - sprintf(buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); - - for(i = 0; i < access_allownum; i++) { - access_ip = access_allow + i * ACO_STRSIZE; - if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, (unsigned char*)access_ip)) { - if(access_order == ACO_ALLOW_DENY) - return 1; // With 'allow, deny' (deny if not allow), allow has priority - flag = ACF_ALLOW; - break; - } - } - - for(i = 0; i < access_denynum; i++) { - access_ip = access_deny + i * ACO_STRSIZE; - if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, (unsigned char*)access_ip)) { - //flag = ACF_DENY; // not necessary to define flag - return 0; // At this point, if it's 'deny', we refuse connection. - } - } - - return (flag == ACF_ALLOW || access_order == ACO_DENY_ALLOW) ? 1:0; - // With 'mutual-failture', only 'allow' and non 'deny' IP are authorised. - // A non 'allow' (even non 'deny') IP is not authorised. It's like: if allowed and not denied, it's authorised. - // So, it's disapproval if you have no description at the time of 'mutual-failture'. - // With 'deny,allow' (allow if not deny), because here it's not deny, we authorise. -} - -//-------------------------------- -// Access control by IP for ladmin -//-------------------------------- -int check_ladminip(unsigned int ip) { - int i; - unsigned char *p = (unsigned char *)&ip; - char buf[20]; - char * access_ip; - - if (access_ladmin_allownum == 0) - return 1; // When there is no restriction, all IP are authorised. - -// + 012.345.: front match form, or -// all: all IP are matched, or -// 012.345.678.901/24: network form (mask with # of bits), or -// 012.345.678.901/255.255.255.0: network form (mask with ip mask) -// + Note about the DNS resolution (like www.ne.jp, etc.): -// There is no guarantee to have an answer. -// If we have an answer, there is no guarantee to have a 100% correct value. -// And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. -// So, DNS notation isn't authorised for ip checking. - sprintf(buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); - - for(i = 0; i < access_ladmin_allownum; i++) { - access_ip = access_ladmin_allow + i * ACO_STRSIZE; - if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, (unsigned char*)access_ip)) { - return 1; - } - } - - return 0; -} - -//--------------------------------------------------- -// E-mail check: return 0 (not correct) or 1 (valid). -//--------------------------------------------------- -int e_mail_check(char *email) { - char ch; - char* last_arobas; - - // athena limits - if (strlen(email) < 3 || strlen(email) > 39) - return 0; - - // part of RFC limits (official reference of e-mail description) - if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') - return 0; - - if (email[strlen(email)-1] == '.') - return 0; - - last_arobas = strrchr(email, '@'); - - if (strstr(last_arobas, "@.") != NULL || - strstr(last_arobas, "..") != NULL) - return 0; - - for(ch = 1; ch < 32; ch++) - if (strchr(last_arobas, ch) != NULL) - return 0; - - if (strchr(last_arobas, ' ') != NULL || - strchr(last_arobas, ';') != NULL) - return 0; - - // all correct - return 1; -} - -//----------------------------------------------- -// Search an account id -// (return account index or -1 (if not found)) -// If exact account name is not found, -// the function checks without case sensitive -// and returns index if only 1 account is found -// and similar to the searched name. -//----------------------------------------------- -int search_account_index(char* account_name) { - unsigned int i, quantity; - int index; - - quantity = 0; - index = -1; - - for(i = 0; i < auth_num; i++) { - // Without case sensitive check (increase the number of similar account names found) - if (stricmp(auth_dat[i].userid, account_name) == 0) { - // Strict comparison (if found, we finish the function immediatly with correct value) - if (strcmp(auth_dat[i].userid, account_name) == 0) - return i; - quantity++; - index = i; - } - } - // Here, the exact account name is not found - // We return the found index of a similar account ONLY if there is 1 similar account - if (quantity == 1) - return index; - - // Exact account name is not found and 0 or more than 1 similar accounts have been found ==> we say not found - return -1; -} - -//-------------------------------------------------------- -// Create a string to save the account in the account file -//-------------------------------------------------------- -int mmo_auth_tostr(char *str, struct auth_dat *p) { - int i; - char *str_p = str; - - str_p += sprintf(str_p, "%d\t%s\t%s\t%s\t%c\t%d\t%d\t" - "%s\t%s\t%ld\t%s\t%s\t%ld\t", - p->account_id, p->userid, p->pass, p->lastlogin, - (p->sex == 2) ? 'S' : (p->sex ? 'M' : 'F'), - p->logincount, p->state, - p->email, p->error_message, - (long)p->connect_until_time, p->last_ip, p->memo, (long)p->ban_until_time); - - for(i = 0; i < p->account_reg2_num; i++) - if (p->account_reg2[i].str[0]) - str_p += sprintf(str_p, "%s,%s ", p->account_reg2[i].str, p->account_reg2[i].value); - - return 0; -} - -//--------------------------------- -// Reading of the accounts database -//--------------------------------- -int mmo_auth_init(void) { - FILE *fp; - int account_id, logincount, state, n, i; - unsigned int j; - char line[2048], *p, userid[2048], pass[2048], lastlogin[2048], sex, email[2048], error_message[2048], last_ip[2048], memo[2048]; - long ban_until_time; - long connect_until_time; - char str[2048]; - char v[2048]; - int GM_count = 0; - int server_count = 0; - - auth_max = 256; - auth_dat = (struct auth_dat*)aCalloc(auth_max, sizeof(struct auth_dat)); - - if ((fp = fopen(account_filename, "r")) == NULL) { - // no account file -> no account -> no login, including char-server (ERROR) - ShowError(CL_RED"mmmo_auth_init: Accounts file [%s] not found."CL_RESET"\n", account_filename); - return 0; - } - - while(fgets(line, sizeof(line)-1, fp) != NULL) { - if (line[0] == '/' && line[1] == '/') - continue; - line[sizeof(line)-1] = '\0'; - // remove carriage return if exist - while(line[0] != '\0' && (line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r')) - line[strlen(line)-1] = '\0'; - p = line; - - memset(userid, 0, sizeof(userid)); - memset(pass, 0, sizeof(pass)); - memset(lastlogin, 0, sizeof(lastlogin)); - memset(email, 0, sizeof(email)); - memset(error_message, 0, sizeof(error_message)); - memset(last_ip, 0, sizeof(last_ip)); - memset(memo, 0, sizeof(memo)); - - // database version reading (v2) - if (((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" - "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]\t%ld%n", - &account_id, userid, pass, lastlogin, &sex, &logincount, &state, - email, error_message, &connect_until_time, last_ip, memo, &ban_until_time, &n)) == 13 && line[n] == '\t') || - ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" - "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]%n", - &account_id, userid, pass, lastlogin, &sex, &logincount, &state, - email, error_message, &connect_until_time, last_ip, memo, &n)) == 12 && line[n] == '\t')) { - n = n + 1; - - // Some checks - if (account_id > END_ACCOUNT_NUM) { - ShowError(CL_RED"mmmo_auth_init: an account has an id higher than %d\n", END_ACCOUNT_NUM); - ShowError(" account id #%d -> account not read (saved in log file)."CL_RESET"\n", account_id); - login_log("mmmo_auth_init: ******Error: an account has an id higher than %d." RETCODE, END_ACCOUNT_NUM); - login_log(" account id #%d -> account not read (saved in next line):" RETCODE, account_id); - login_log("%s", line); - continue; - } - userid[23] = '\0'; - remove_control_chars((unsigned char *)userid); - for(j = 0; j < auth_num; j++) { - if (auth_dat[j].account_id == account_id) { - ShowError(CL_RED"mmmo_auth_init: an account has an identical id to another.\n"); - ShowError(" account id #%d -> new account not read (saved in log file)."CL_RED"\n", account_id); - login_log("mmmo_auth_init: ******Error: an account has an identical id to another." RETCODE); - login_log(" account id #%d -> new account not read (saved in next line):" RETCODE, account_id); - login_log("%s", line); - break; - } else if (strcmp(auth_dat[j].userid, userid) == 0) { - ShowError(CL_RED"mmmo_auth_init: account name already exists.\n"); - ShowError(" account name '%s' -> new account not read (saved in log file)."CL_RESET"\n", userid); // 2 lines, account name can be long. - login_log("mmmo_auth_init: ******Error: an account has an identical name to another." RETCODE); - login_log(" account name '%s' -> new account not read (saved in next line):" RETCODE, userid); - login_log("%s", line); - break; - } - } - if (j != auth_num) - continue; - - if (auth_num >= auth_max) { - auth_max += 256; - auth_dat = (struct auth_dat*)aRealloc(auth_dat, sizeof(struct auth_dat) * auth_max); - } - - memset(&auth_dat[auth_num], '\0', sizeof(struct auth_dat)); - - auth_dat[auth_num].account_id = account_id; - - strncpy(auth_dat[auth_num].userid, userid, 24); - - pass[23] = '\0'; - remove_control_chars((unsigned char *)pass); - strncpy(auth_dat[auth_num].pass, pass, 24); - - lastlogin[23] = '\0'; - remove_control_chars((unsigned char *)lastlogin); - strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24); - - auth_dat[auth_num].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); - - if (logincount >= 0) - auth_dat[auth_num].logincount = logincount; - else - auth_dat[auth_num].logincount = 0; - - if (state > 255) - auth_dat[auth_num].state = 100; - else if (state < 0) - auth_dat[auth_num].state = 0; - else - auth_dat[auth_num].state = state; - - if (e_mail_check(email) == 0) { - ShowNotice("Account %s (%d): invalid e-mail (replaced par a@a.com).\n", auth_dat[auth_num].userid, auth_dat[auth_num].account_id); - strncpy(auth_dat[auth_num].email, "a@a.com", 40); - } else { - remove_control_chars((unsigned char *)email); - strncpy(auth_dat[auth_num].email, email, 40); - } - - error_message[19] = '\0'; - remove_control_chars((unsigned char *)error_message); - if (error_message[0] == '\0' || state != 7) { // 7, because state is packet 0x006a value + 1 - strncpy(auth_dat[auth_num].error_message, "-", 20); - } else { - strncpy(auth_dat[auth_num].error_message, error_message, 20); - } - - if (i == 13) - auth_dat[auth_num].ban_until_time = (time_t)ban_until_time; - else - auth_dat[auth_num].ban_until_time = 0; - - auth_dat[auth_num].connect_until_time = (time_t)connect_until_time; - - last_ip[15] = '\0'; - remove_control_chars((unsigned char *)last_ip); - strncpy(auth_dat[auth_num].last_ip, last_ip, 16); - - memo[254] = '\0'; - remove_control_chars((unsigned char *)memo); - strncpy(auth_dat[auth_num].memo, memo, 255); - - for(j = 0; j < ACCOUNT_REG2_NUM; j++) { - p += n; - if (sscanf(p, "%[^\t,],%[^\t ] %n", str, v, &n) != 2) { - // We must check if a str is void. If it's, we can continue to read other REG2. - // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good) - if (p[0] == ',' && sscanf(p, ",%[^\t ] %n", v, &n) == 1) { - j--; - continue; - } else - break; - } - str[31] = '\0'; - remove_control_chars((unsigned char *)str); - strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32); - strncpy(auth_dat[auth_num].account_reg2[j].value,v,256); - } - auth_dat[auth_num].account_reg2_num = j; - - if (isGM(account_id) > 0) - GM_count++; - if (auth_dat[auth_num].sex == 2) - server_count++; - - auth_num++; - if (account_id >= account_id_count) - account_id_count = account_id + 1; - - // Old athena database version reading (v1) - } else if ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t%n", - &account_id, userid, pass, lastlogin, &sex, &logincount, &state, &n)) >= 5) { - if (account_id > END_ACCOUNT_NUM) { - ShowError(CL_RED"mmmo_auth_init: an account has an id higher than %d\n", END_ACCOUNT_NUM); - ShowError(" account id #%d -> account not read (saved in log file)."CL_RESET"\n", account_id); - login_log("mmmo_auth_init: ******Error: an account has an id higher than %d." RETCODE, END_ACCOUNT_NUM); - login_log(" account id #%d -> account not read (saved in next line):" RETCODE, account_id); - login_log("%s", line); - continue; - } - userid[23] = '\0'; - remove_control_chars((unsigned char *)userid); - for(j = 0; j < auth_num; j++) { - if (auth_dat[j].account_id == account_id) { - ShowError(CL_RED"mmo_auth_init: an account has an identical id to another.\n"); - ShowError(" account id #%d -> new account not read (saved in log file)."CL_RESET"\n", account_id); - login_log("mmmo_auth_init: ******Error: an account has an identical id to another." RETCODE); - login_log(" account id #%d -> new account not read (saved in next line):" RETCODE, account_id); - login_log("%s", line); - break; - } else if (strcmp(auth_dat[j].userid, userid) == 0) { - ShowError(CL_RED"mmo_auth_init: account name already exists.\n"); - ShowError(" account name '%s' -> new account not read (saved in log file)."CL_RESET"\n", userid); - login_log("mmmo_auth_init: ******Error: an account has an identical id to another." RETCODE); - login_log(" account id #%d -> new account not read (saved in next line):" RETCODE, account_id); - login_log("%s", line); - break; - } - } - if (j != auth_num) - continue; - - if (auth_num >= auth_max) { - auth_max += 256; - auth_dat = (struct auth_dat*)aRealloc(auth_dat, sizeof(struct auth_dat) * auth_max); - } - - memset(&auth_dat[auth_num], '\0', sizeof(struct auth_dat)); - - auth_dat[auth_num].account_id = account_id; - - strncpy(auth_dat[auth_num].userid, userid, 24); - - pass[23] = '\0'; - remove_control_chars((unsigned char *)pass); - strncpy(auth_dat[auth_num].pass, pass, 24); - - lastlogin[23] = '\0'; - remove_control_chars((unsigned char *)lastlogin); - strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24); - - auth_dat[auth_num].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); - - if (i >= 6) { - if (logincount >= 0) - auth_dat[auth_num].logincount = logincount; - else - auth_dat[auth_num].logincount = 0; - } else - auth_dat[auth_num].logincount = 0; - - if (i >= 7) { - if (state > 255) - auth_dat[auth_num].state = 100; - else if (state < 0) - auth_dat[auth_num].state = 0; - else - auth_dat[auth_num].state = state; - } else - auth_dat[auth_num].state = 0; - - // Initialization of new data - strncpy(auth_dat[auth_num].email, "a@a.com", 40); - strncpy(auth_dat[auth_num].error_message, "-", 20); - auth_dat[auth_num].ban_until_time = 0; - auth_dat[auth_num].connect_until_time = 0; - strncpy(auth_dat[auth_num].last_ip, "-", 16); - strncpy(auth_dat[auth_num].memo, "-", 255); - - for(j = 0; j < ACCOUNT_REG2_NUM; j++) { - p += n; - if (sscanf(p, "%[^\t,],%[^\t ] %n", str, v, &n) != 2) { - // We must check if a str is void. If it's, we can continue to read other REG2. - // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good) - if (p[0] == ',' && sscanf(p, ",%[^\t ] %n", v, &n) == 1) { - j--; - continue; - } else - break; - } - str[31] = '\0'; - remove_control_chars((unsigned char *)str); - strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32); - strncpy(auth_dat[auth_num].account_reg2[j].value,v,256); - } - auth_dat[auth_num].account_reg2_num = j; - - if (isGM(account_id) > 0) - GM_count++; - if (auth_dat[auth_num].sex == 2) - server_count++; - - auth_num++; - if (account_id >= account_id_count) - account_id_count = account_id + 1; - - } else { - i = 0; - if (sscanf(line, "%d\t%%newid%%\n%n", &account_id, &i) == 1 && - i > 0 && account_id > account_id_count) - account_id_count = account_id; - } - } - fclose(fp); - - if (auth_num == 0) { - ShowNotice("mmo_auth_init: No account found in %s.\n", account_filename); - sprintf(line, "No account found in %s.", account_filename); - } else { - if (auth_num == 1) { - ShowStatus("mmo_auth_init: 1 account read in %s,\n", account_filename); - sprintf(line, "1 account read in %s,", account_filename); - } else { - ShowStatus("mmo_auth_init: %d accounts read in %s,\n", auth_num, account_filename); - sprintf(line, "%d accounts read in %s,", auth_num, account_filename); - } - if (GM_count == 0) { - ShowStatus(" of which is no GM account, and "); - sprintf(str, "%s of which is no GM account and", line); - } else if (GM_count == 1) { - ShowStatus(" of which is 1 GM account, and "); - sprintf(str, "%s of which is 1 GM account and", line); - } else { - ShowStatus(" of which is %d GM accounts, and ", GM_count); - sprintf(str, "%s of which is %d GM accounts and", line, GM_count); - } - if (server_count == 0) { - printf("no server account ('S').\n"); - sprintf(line, "%s no server account ('S').", str); - } else if (server_count == 1) { - printf("1 server account ('S').\n"); - sprintf(line, "%s 1 server account ('S').", str); - } else { - printf("%d server accounts ('S').\n", server_count); - sprintf(line, "%s %d server accounts ('S').", str, server_count); - } - } - login_log("%s" RETCODE, line); - - return 0; -} - -//------------------------------------------ -// Writing of the accounts database file -// (accounts are sorted by id before save) -//------------------------------------------ -void mmo_auth_sync(void) { - FILE *fp; - unsigned int i, j, k; - int lock; - int account_id; - //int id[auth_num]; - //int *id = (int *)aCalloc(auth_num, sizeof(int)); - CREATE_BUFFER(id, int, auth_num); - char line[65536]; - - // Sorting before save - for(i = 0; i < auth_num; i++) { - id[i] = i; - account_id = auth_dat[i].account_id; - for(j = 0; j < i; j++) { - if (account_id < auth_dat[id[j]].account_id) { - for(k = i; k > j; k--) - id[k] = id[k-1]; - id[j] = i; // id[i] - break; - } - } - } - - // Data save - if ((fp = lock_fopen(account_filename, &lock)) == NULL) { - //if (id) aFree(id); // aFree, right? - DELETE_BUFFER(id); - return; - } - - fprintf(fp, "// Accounts file: here are saved all information about the accounts.\n"); - fprintf(fp, "// Structure: ID, account name, password, last login time, sex, # of logins, state, email, error message for state 7, validity time, last (accepted) login ip, memo field, ban timestamp, repeated(register text, register value)\n"); - fprintf(fp, "// Some explanations:\n"); - fprintf(fp, "// account name : between 4 to 23 char for a normal account (standard client can't send less than 4 char).\n"); - fprintf(fp, "// account password: between 4 to 23 char\n"); - fprintf(fp, "// sex : M or F for normal accounts, S for server accounts\n"); - fprintf(fp, "// state : 0: account is ok, 1 to 256: error code of packet 0x006a + 1\n"); - fprintf(fp, "// email : between 3 to 39 char (a@a.com is like no email)\n"); - fprintf(fp, "// error message : text for the state 7: 'Your are Prohibited to login until '. Max 19 char\n"); - fprintf(fp, "// valitidy time : 0: unlimited account, : date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n"); - fprintf(fp, "// memo field : max 254 char\n"); - fprintf(fp, "// ban time : 0: no ban, : banned until the date: date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n"); - for(i = 0; i < auth_num; i++) { - k = id[i]; // use of sorted index - if (auth_dat[k].account_id < 0) - continue; - - mmo_auth_tostr(line, &auth_dat[k]); - fprintf(fp, "%s" RETCODE, line); - } - fprintf(fp, "%d\t%%newid%%\n", account_id_count); - - lock_fclose(fp, account_filename, &lock); - - // set new counter to minimum number of auth before save - auth_before_save_file = auth_num / AUTH_SAVE_FILE_DIVIDER; // Re-initialise counter. We have save. - if (auth_before_save_file < AUTH_BEFORE_SAVE_FILE) - auth_before_save_file = AUTH_BEFORE_SAVE_FILE; - - //if (id) aFree(id); - DELETE_BUFFER(id); - - return; -} - -//----------------------------------------------------- -// Check if we must save accounts file or not -// every minute, we check if we must save because we -// have do some authentifications without arrive to -// the minimum of authentifications for the save. -// Note: all other modification of accounts (deletion, -// change of some informations excepted lastip/ -// lastlogintime, creation) are always save -// immediatly and set the minimum of -// authentifications to its initialization value. -//----------------------------------------------------- -int check_auth_sync(int tid, unsigned int tick, int id, int data) { - // we only save if necessary: - // we have do some authentifications without do saving - if (auth_before_save_file < AUTH_BEFORE_SAVE_FILE || - auth_before_save_file < (int)(auth_num / AUTH_SAVE_FILE_DIVIDER)) - mmo_auth_sync(); - - return 0; -} - -//-------------------------------------------------------------------- -// Packet send to all char-servers, except one (wos: without our self) -//-------------------------------------------------------------------- -int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { - int i, c, fd; - - for(i = 0, c = 0; i < MAX_SERVERS; i++) { - if ((fd = server_fd[i]) >= 0 && fd != sfd) { - WFIFOHEAD(fd, len); - if (WFIFOSPACE(fd) < len) //Increase buffer size. - realloc_writefifo(fd, len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd, len); - c++; - } - } - return c; -} - -//----------------------------------------------------- -// Send GM accounts to all char-server -//----------------------------------------------------- -void send_GM_accounts(void) { - unsigned int i; - unsigned char buf[32767]; - int len; - - len = 4; - WBUFW(buf,0) = 0x2732; - for(i = 0; i < GM_num; i++) - // send only existing accounts. We can not create a GM account when server is online. - if (gm_account_db[i].level > 0) { - WBUFL(buf,len) = gm_account_db[i].account_id; - WBUFB(buf,len+4) = (unsigned char)gm_account_db[i].level; - len += 5; - if (len >= 32000) { - ShowWarning("send_GM_accounts: Too many accounts! Only %d out of %d were sent.\n", i, GM_num); - break; - } - } - WBUFW(buf,2) = len; - charif_sendallwos(-1, buf, len); - - return; -} - -//----------------------------------------------------- -// Check if GM file account have been changed -//----------------------------------------------------- -int check_GM_file(int tid, unsigned int tick, int id, int data) { - struct stat file_stat; - long new_time; - - // if we would not check - if (gm_account_filename_check_timer < 1) - return 0; - - // get last modify time/date - if (stat(GM_account_filename, &file_stat)) - new_time = 0; // error - else - new_time = (long)file_stat.st_mtime; - - if (new_time != creation_time_GM_account_file) { - read_gm_account(); - send_GM_accounts(); - } - - return 0; -} - -//------------------------------------- -// Account creation (with e-mail check) -//------------------------------------- -int mmo_auth_new(struct mmo_account* account, char sex, char* email) { - time_t timestamp, timestamp_temp; - struct tm *tmtime; - int i = auth_num; - - if (auth_num >= auth_max) { - auth_max += 256; - auth_dat = (struct auth_dat*)aRealloc(auth_dat, sizeof(struct auth_dat) * auth_max); - } - - memset(&auth_dat[i], '\0', sizeof(struct auth_dat)); - - while (isGM(account_id_count) > 0) - account_id_count++; - - auth_dat[i].account_id = account_id_count++; - - strncpy(auth_dat[i].userid, account->userid, NAME_LENGTH); - auth_dat[i].userid[23] = '\0'; - - strncpy(auth_dat[i].pass, account->passwd, NAME_LENGTH); - auth_dat[i].pass[23] = '\0'; - - memcpy(auth_dat[i].lastlogin, "-", 2); - - auth_dat[i].sex = (sex == 'M' || sex == 'm'); - - auth_dat[i].logincount = 0; - - auth_dat[i].state = 0; - - if (e_mail_check(email) == 0) - strncpy(auth_dat[i].email, "a@a.com", 40); - else - strncpy(auth_dat[i].email, email, 40); - - strncpy(auth_dat[i].error_message, "-", 20); - - auth_dat[i].ban_until_time = 0; - - if (start_limited_time < 0) - auth_dat[i].connect_until_time = 0; // unlimited - else { // limited time - timestamp = time(NULL) + start_limited_time; - // double conversion to be sure that it is possible - tmtime = localtime(×tamp); - timestamp_temp = mktime(tmtime); - if (timestamp_temp != -1 && (timestamp_temp + 3600) >= timestamp) // check possible value and overflow (and avoid summer/winter hour) - auth_dat[i].connect_until_time = timestamp_temp; - else - auth_dat[i].connect_until_time = 0; // unlimited - } - - strncpy(auth_dat[i].last_ip, "-", 16); - - strncpy(auth_dat[i].memo, "-", 255); - - auth_dat[i].account_reg2_num = 0; - - auth_num++; - - return (account_id_count - 1); -} - -//--------------------------------------- -// Check/authentification of a connection -//--------------------------------------- -int mmo_auth(struct mmo_account* account, int fd) { - char *dnsbl_serv; - unsigned int i; - time_t raw_time; - char tmpstr[256]; - int len, newaccount = 0; -#ifdef PASSWORDENC - struct login_session_data *ld; -#endif - int encpasswdok; - char md5str[64], md5bin[32]; - char ip[16]; - unsigned char *sin_addr = (unsigned char *)&session[fd]->client_addr.sin_addr; - char user_password[256]; - char r_ip[16]; // [Zido] - char ip_dnsbl[256]; // [Zido] - - sprintf(ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], sin_addr[2], sin_addr[3]); - - // Start DNS Blacklist check [Zido] - if(use_dnsbl) { - sprintf(r_ip, "%d.%d.%d.%d", sin_addr[3], sin_addr[2], sin_addr[1], sin_addr[0]); - - dnsbl_serv=strtok(dnsbl_servs,","); - sprintf(ip_dnsbl,"%s.%s",r_ip,dnsbl_serv); -// Using directly gethostbyname should be quicker. [Skotlex] -// if(resolve_hostbyname(ip_dnsbl, NULL, NULL)) { - if(gethostbyname(ip_dnsbl)) { - ShowInfo("DNSBL: (%s) Blacklisted. User Kicked.\n",ip); - return 3; - } - - while((dnsbl_serv=strtok(dnsbl_servs,","))) { - sprintf(ip_dnsbl,"%s.%s",r_ip,dnsbl_serv); -// Using directly gethostbyname should be quicker. [Skotlex] -// if(resolve_hostbyname(ip_dnsbl,NULL,NULL)!=0) { - if(gethostbyname(ip_dnsbl)) { - ShowInfo("DNSBL: (%s) Blacklisted. User Kicked.\n",ip); - return 3; - } - } - - } - // End DNS Blacklist check [Zido] - - - len = strlen(account->userid) - 2; - // Account creation with _M/_F - if (account->passwdenc == 0 && account->userid[len] == '_' && - (account->userid[len+1] == 'F' || account->userid[len+1] == 'M' || - account->userid[len+1] == 'f' || account->userid[len+1] == 'm') - && new_account_flag && account_id_count <= END_ACCOUNT_NUM && len >= 4 && strlen(account->passwd) >= 4) { - - //only continue if amount in this time limit is allowed (account registration flood protection)[Kevin] - if(gettick() <= new_reg_tick && num_regs >= allowed_regs) { - ShowNotice("Account registration denied (registration limit exceeded) to %s!\n", ip); - login_log("Notice: Account registration denied (registration limit exceeded) to %s!", ip); - return 3; - } else { - num_regs=0; - } - - newaccount = 1; - account->userid[len] = '\0'; - } - - //EXE Version check [Sirius] - if (check_client_version == 1 && account->version != 0 && - account->version != client_version_to_connect) - return 5; - - // Strict account search - for(i = 0; i < auth_num; i++) { - if (strcmp(account->userid, auth_dat[i].userid) == 0) - break; - } - // if there is no creation request and strict account search fails, we do a no sensitive case research for index - if (!newaccount && i == auth_num) { - i = search_account_index(account->userid); - if (i == -1) - i = auth_num; - else - memcpy(account->userid, auth_dat[i].userid, NAME_LENGTH); // for the possible tests/checks afterwards (copy correcte sensitive case). - } - - if (i != auth_num) { - if (newaccount) { - login_log("Attempt of creation of an already existant account (account: %s_%c, pass: %s, received pass: %s, ip: %s)" RETCODE, - account->userid, account->userid[len+1], auth_dat[i].pass, account->passwd, ip); - return 1; // 1 = Incorrect Password - } - if(use_md5_passwds) - MD5_String(account->passwd, user_password); - else - memcpy(user_password, account->passwd, NAME_LENGTH); - encpasswdok = 0; -#ifdef PASSWORDENC - ld = (struct login_session_data*)session[fd]->session_data; - if (account->passwdenc > 0) { - int j = account->passwdenc; - if (!ld) { - login_log("Md5 key not created (account: %s, ip: %s)" RETCODE, account->userid, ip); - return 1; // 1 = Incorrect Password - } - if (j > 2) - j = 1; - do { - if (j == 1) { - sprintf(md5str, "%s%s", ld->md5key, auth_dat[i].pass); // 20 + 24 - } else if (j == 2) { - sprintf(md5str, "%s%s", auth_dat[i].pass, ld->md5key); // 24 + 20 - } else - md5str[0] = '\0'; - md5str[sizeof(md5str)-1] = '\0'; // 64 - MD5_String2binary(md5str, md5bin); - encpasswdok = (memcmp(account->passwd, md5bin, 16) == 0); - } while (j < 2 && !encpasswdok && (j++) != account->passwdenc); -// printf("key[%s] md5 [%s] ", md5key, md5); -// printf("client [%s] accountpass [%s]\n", account->passwd, auth_dat[i].pass); - } -#endif - if ((strcmp(account->passwd, auth_dat[i].pass) && !encpasswdok)) { - if (account->passwdenc == 0) - login_log("Invalid password (account: %s, pass: %s, received pass: %s, ip: %s)" RETCODE, account->userid, auth_dat[i].pass, account->passwd, ip); -#ifdef PASSWORDENC - else { - char logbuf[512], *p = logbuf; - unsigned int j; - p += sprintf(p, "Invalid password (account: %s, received md5[", account->userid); - for(j = 0; j < 16; j++) - p += sprintf(p, "%02x", ((unsigned char *)account->passwd)[j]); - p += sprintf(p,"] calculated md5["); - for(j = 0; j < 16; j++) - p += sprintf(p, "%02x", ((unsigned char *)md5bin)[j]); - p += sprintf(p, "] md5 key["); - for(j = 0; j < ld->md5keylen; j++) - p += sprintf(p, "%02x", ((unsigned char *)ld->md5key)[j]); - p += sprintf(p, "], ip: %s)" RETCODE, ip); - login_log(logbuf); - } -#endif - return 1; // 1 = Incorrect Password - } - - if (auth_dat[i].state) { - login_log("Connection refused (account: %s, pass: %s, state: %d, ip: %s)" RETCODE, - account->userid, account->passwd, auth_dat[i].state, ip); - switch(auth_dat[i].state) { // packet 0x006a value + 1 - case 1: // 0 = Unregistered ID - case 2: // 1 = Incorrect Password - case 3: // 2 = This ID is expired - case 4: // 3 = Rejected from Server - case 5: // 4 = You have been blocked by the GM Team - case 6: // 5 = Your Game's EXE file is not the latest version - case 7: // 6 = Your are Prohibited to log in until %s - case 8: // 7 = Server is jammed due to over populated - case 9: // 8 = No more accounts may be connected from this company - case 10: // 9 = MSI_REFUSE_BAN_BY_DBA - case 11: // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED - case 12: // 11 = MSI_REFUSE_BAN_BY_GM - case 13: // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK - case 14: // 13 = MSI_REFUSE_SELF_LOCK - case 15: // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP - case 16: // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP - case 100: // 99 = This ID has been totally erased - case 101: // 100 = Login information remains at %s. - case 102: // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information - case 103: // 102 = This account has been temporarily prohibited from login due to a bug-related investigation - case 104: // 103 = This character is being deleted. Login is temporarily unavailable for the time being - case 105: // 104 = Your spouse character is being deleted. Login is temporarily unavailable for the time being - return auth_dat[i].state - 1; - default: - return 99; // 99 = ID has been totally erased - } - } - - if (online_check) { - unsigned char buf[8]; - struct online_login_data* data = idb_get(online_db,auth_dat[i].account_id); - if (data && data->char_server > -1) { - //Request char servers to kick this account out. [Skotlex] - ShowWarning("User [%d] is already online - Rejected.\n",auth_dat[i].account_id); - WBUFW(buf,0) = 0x2734; - WBUFL(buf,2) = auth_dat[i].account_id; - charif_sendallwos(-1, buf, 6); - if (!data->waiting_disconnect) - add_timer(gettick()+30000, waiting_disconnect_timer,auth_dat[i].account_id, 0); - data->waiting_disconnect = 1; - return 3; // Rejected - } - } - - if (auth_dat[i].ban_until_time != 0) { // if account is banned - strftime(tmpstr, 20, date_format, localtime(&auth_dat[i].ban_until_time)); - tmpstr[19] = '\0'; - if (auth_dat[i].ban_until_time > time(NULL)) { // always banned - login_log("Connection refused (account: %s, pass: %s, banned until %s, ip: %s)" RETCODE, - account->userid, account->passwd, tmpstr, ip); - return 6; // 6 = Your are Prohibited to log in until %s - } else { // ban is finished - login_log("End of ban (account: %s, pass: %s, previously banned until %s -> not more banned, ip: %s)" RETCODE, - account->userid, account->passwd, tmpstr, ip); - auth_dat[i].ban_until_time = 0; // reset the ban time - } - } - - if (auth_dat[i].connect_until_time != 0 && auth_dat[i].connect_until_time < time(NULL)) { - login_log("Connection refused (account: %s, pass: %s, expired ID, ip: %s)" RETCODE, - account->userid, account->passwd, ip); - return 2; // 2 = This ID is expired - } - - login_log("Authentification accepted (account: %s (id: %d), ip: %s)" RETCODE, account->userid, auth_dat[i].account_id, ip); - } else { - if (!newaccount) { - login_log("Unknown account (account: %s, received pass: %s, ip: %s)" RETCODE, - account->userid, account->passwd, ip); - return 0; // 0 = Unregistered ID - } else { - int new_id = mmo_auth_new(account, account->userid[len+1], "a@a.com"); - login_log("Account creation and authentification accepted (account %s (id: %d), pass: %s, sex: %c, connection with _F/_M, ip: %s)" RETCODE, - account->userid, new_id, account->passwd, account->userid[len+1], ip); - auth_before_save_file = 0; // Creation of an account -> save accounts file immediatly - - //restart ticker (account registration flood protection)[Kevin] - if(num_regs==0) { - new_reg_tick=gettick()+time_allowed*1000; - } - num_regs++; - } - } - - // auth start : time seed - // Platform/Compiler dependant clock() for time check is removed. [Lance] - // clock() is originally used to track processing ticks on program execution. - time(&raw_time); - strftime(tmpstr, 24, "%Y-%m-%d %H:%M:%S",localtime(&raw_time)); - - account->account_id = auth_dat[i].account_id; - account->login_id1 = rand(); - account->login_id2 = rand(); - memcpy(account->lastlogin, auth_dat[i].lastlogin, 24); - memcpy(auth_dat[i].lastlogin, tmpstr, 24); - account->sex = auth_dat[i].sex; - if (account->sex != 2 && account->account_id < 700000) - ShowWarning("Account %s has account id %d! Account IDs must be over 700000 to work properly!\n", account->userid, account->account_id); - - strncpy(auth_dat[i].last_ip, ip, 16); - auth_dat[i].logincount++; - - // Save until for change ip/time of auth is not very useful => limited save for that - // Save there informations isnot necessary, because they are saved in log file. - if (--auth_before_save_file <= 0) // Reduce counter. 0 or less, we save - mmo_auth_sync(); - - return -1; // account OK -} - -static int online_db_setoffline(DBKey key, void* data, va_list ap) { - struct online_login_data *p = (struct online_login_data *)data; - int server = va_arg(ap, int); - if (server == -1) { - p->char_server = -1; - p->waiting_disconnect = 0; - } else if (p->char_server == server) - p->char_server = -2; //Char server disconnected. - return 0; -} - -//-------------------------------- -// Packet parsing for char-servers -//-------------------------------- -int parse_fromchar(int fd) { - unsigned int i; - int j, id; - unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; - char ip[16]; - int acc; - RFIFOHEAD(fd); - - sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - - for(id = 0; id < MAX_SERVERS; id++) - if (server_fd[id] == fd) - break; - if (id == MAX_SERVERS) - session[fd]->eof = 1; - if(session[fd]->eof) { - if (id < MAX_SERVERS) { - ShowStatus("Char-server '%s' has disconnected.\n", server[id].name); - login_log("Char-server '%s' has disconnected (ip: %s)." RETCODE, - server[id].name, ip); - server_fd[id] = -1; - memset(&server[id], 0, sizeof(struct mmo_char_server)); - online_db->foreach(online_db,online_db_setoffline,id); //Set all chars from this char server to offline. - } - do_close(fd); - return 0; - } - - while (RFIFOREST(fd) >= 2) { - - if (display_parse_fromchar == 2 || (display_parse_fromchar == 1 && RFIFOW(fd,0) != 0x2714)) // 0x2714 is done very often (number of players) - ShowDebug("parse_fromchar: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); - - switch (RFIFOW(fd,0)) { - // request from map-server via char-server to reload GM accounts (by Yor). - case 0x2709: - login_log("Char-server '%s': Request to re-load GM configuration file (ip: %s)." RETCODE, server[id].name, ip); - read_gm_account(); - // send GM accounts to all char-servers - send_GM_accounts(); - RFIFOSKIP(fd,2); - break; - - case 0x2712: // request from char-server to authentify an account - if (RFIFOREST(fd) < 19) - return 0; - { - int acc; - acc = RFIFOL(fd,2); // speed up - for(i = 0; i < AUTH_FIFO_SIZE; i++) { - if (auth_fifo[i].account_id == acc && - auth_fifo[i].login_id1 == RFIFOL(fd,6) && -#if CMP_AUTHFIFO_LOGIN2 != 0 - auth_fifo[i].login_id2 == RFIFOL(fd,10) && // relate to the versions higher than 18 -#endif - auth_fifo[i].sex == RFIFOB(fd,14) && - (!check_ip_flag || auth_fifo[i].ip == RFIFOL(fd,15)) && - !auth_fifo[i].delflag) { - unsigned int k; - time_t connect_until_time = 0; - char email[40] = ""; - WFIFOHEAD(fd,51); - auth_fifo[i].delflag = 1; - login_log("Char-server '%s': authentification of the account %d accepted (ip: %s)." RETCODE, - server[id].name, acc, ip); -// printf("%d\n", i); - for(k = 0; k < auth_num; k++) { - if (auth_dat[k].account_id == acc) { - strcpy(email, auth_dat[k].email); - connect_until_time = auth_dat[k].connect_until_time; - break; - } - } - WFIFOW(fd,0) = 0x2713; - WFIFOL(fd,2) = acc; - WFIFOB(fd,6) = 0; - memcpy(WFIFOP(fd, 7), email, 40); - WFIFOL(fd,47) = (unsigned long)connect_until_time; - WFIFOSET(fd,51); - break; - } - } - // authentification not found - if (i == AUTH_FIFO_SIZE) { - login_log("Char-server '%s': authentification of the account %d REFUSED (ip: %s)." RETCODE, - server[id].name, acc, ip); - WFIFOHEAD(fd, 51); - WFIFOW(fd,0) = 0x2713; - WFIFOL(fd,2) = acc; - WFIFOB(fd,6) = 1; - // It is unnecessary to send email - // It is unnecessary to send validity date of the account - WFIFOSET(fd,51); - } - } - RFIFOSKIP(fd,19); - break; - - case 0x2714: - if (RFIFOREST(fd) < 6) - return 0; - //printf("parse_fromchar: Receiving of the users number of the server '%s': %d\n", server[id].name, RFIFOL(fd,2)); - server[id].users = RFIFOL(fd,2); - // send some answer - WFIFOHEAD(fd, 2); - WFIFOW(fd,0) = 0x2718; - WFIFOSET(fd,2); - - RFIFOSKIP(fd,6); - break; - - // we receive a e-mail creation of an account with a default e-mail (no answer) - case 0x2715: - if (RFIFOREST(fd) < 46) - return 0; - { - char email[40]; - acc = RFIFOL(fd,2); // speed up - memcpy(email, RFIFOP(fd,6), 40); - email[39] = '\0'; - remove_control_chars((unsigned char *)email); - //printf("parse_fromchar: an e-mail creation of an account with a default e-mail: server '%s', account: %d, e-mail: '%s'.\n", server[id].name, acc, RFIFOP(fd,6)); - if (e_mail_check(email) == 0) - login_log("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - e-mail is invalid (account: %d, ip: %s)" RETCODE, - server[id].name, acc, ip); - else { - for(i = 0; i < auth_num; i++) { - if (auth_dat[i].account_id == acc && (strcmp(auth_dat[i].email, "a@a.com") == 0 || auth_dat[i].email[0] == '\0')) { - memcpy(auth_dat[i].email, email, 40); - login_log("Char-server '%s': Create an e-mail on an account with a default e-mail (account: %d, new e-mail: %s, ip: %s)." RETCODE, - server[id].name, acc, email, ip); - // Save - mmo_auth_sync(); - break; - } - } - if (i == auth_num) - login_log("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - account doesn't exist or e-mail of account isn't default e-mail (account: %d, ip: %s)." RETCODE, - server[id].name, acc, ip); - } - } - RFIFOSKIP(fd,46); - break; - - // We receive an e-mail/limited time request, because a player comes back from a map-server to the char-server - case 0x2716: - if (RFIFOREST(fd) < 6) - return 0; - //printf("parse_fromchar: E-mail/limited time request from '%s' server (concerned account: %d)\n", server[id].name, RFIFOL(fd,2)); - for(i = 0; i < auth_num; i++) { - if (auth_dat[i].account_id == RFIFOL(fd,2)) { - login_log("Char-server '%s': e-mail of the account %d found (ip: %s)." RETCODE, - server[id].name, RFIFOL(fd,2), ip); - WFIFOW(fd,0) = 0x2717; - WFIFOL(fd,2) = RFIFOL(fd,2); - memcpy(WFIFOP(fd, 6), auth_dat[i].email, 40); - WFIFOL(fd,46) = (unsigned long)auth_dat[i].connect_until_time; - WFIFOSET(fd,50); - break; - } - } - if (i == auth_num) - login_log("Char-server '%s': e-mail of the account %d NOT found (ip: %s)." RETCODE, - server[id].name, RFIFOL(fd,2), ip); - RFIFOSKIP(fd,6); - break; - - case 0x2720: // To become GM request - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { - unsigned char buf[10]; - FILE *fp; - acc = RFIFOL(fd,4); - //printf("parse_fromchar: Request to become a GM acount from %d account.\n", acc); - WBUFW(buf,0) = 0x2721; - WBUFL(buf,2) = acc; - WBUFL(buf,6) = 0; - if (strcmp((char*)RFIFOP(fd,8), gm_pass) == 0) { - // only non-GM can become GM - if (isGM(acc) == 0) { - // if we autorise creation - if (level_new_gm > 0) { - // if we can open the file to add the new GM - if ((fp = fopen(GM_account_filename, "a")) != NULL) { - char tmpstr[24]; - time_t raw_time; - time(&raw_time); - strftime(tmpstr, 23, date_format, localtime(&raw_time)); - fprintf(fp, RETCODE "// %s: @GM command on account %d" RETCODE "%d %d" RETCODE, tmpstr, acc, acc, level_new_gm); - fclose(fp); - WBUFL(buf,6) = level_new_gm; - read_gm_account(); - send_GM_accounts(); - ShowNotice("GM Change of the account %d: level 0 -> %d.\n", acc, level_new_gm); - login_log("Char-server '%s': GM Change of the account %d: level 0 -> %d (ip: %s)." RETCODE, - server[id].name, acc, level_new_gm, ip); - } else { - ShowError("Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file)\n", acc); - login_log("Char-server '%s': Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file, ip: %s)." RETCODE, - server[id].name, acc, ip); - } - } else { - ShowError("Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0))\n", acc); - login_log("Char-server '%s': Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0), ip: %s)." RETCODE, - server[id].name, acc, ip); - } - } else { - ShowError("Error of GM change (suggested account: %d (already GM), correct password).\n", acc); - login_log("Char-server '%s': Error of GM change (suggested account: %d (already GM), correct password, ip: %s)." RETCODE, - server[id].name, acc, ip); - } - } else { - ShowError("Error of GM change (suggested account: %d, invalid password).\n", acc); - login_log("Char-server '%s': Error of GM change (suggested account: %d, invalid password, ip: %s)." RETCODE, - server[id].name, acc, ip); - } - charif_sendallwos(-1, buf, 10); - } - RFIFOSKIP(fd, RFIFOW(fd,2)); - return 0; - - // Map server send information to change an email of an account via char-server - case 0x2722: // 0x2722 .L .40B .40B - if (RFIFOREST(fd) < 86) - return 0; - { - char actual_email[40], new_email[40]; - acc = RFIFOL(fd,2); - memcpy(actual_email, RFIFOP(fd,6), 40); - actual_email[39] = '\0'; - remove_control_chars((unsigned char *)actual_email); - memcpy(new_email, RFIFOP(fd,46), 40); - new_email[39] = '\0'; - remove_control_chars((unsigned char *)new_email); - if (e_mail_check(actual_email) == 0) - login_log("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)" RETCODE, - server[id].name, acc, ip); - else if (e_mail_check(new_email) == 0) - login_log("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a invalid new e-mail (account: %d, ip: %s)" RETCODE, - server[id].name, acc, ip); - else if (strcmpi(new_email, "a@a.com") == 0) - login_log("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a default e-mail (account: %d, ip: %s)" RETCODE, - server[id].name, acc, ip); - else { - for(i = 0; i < auth_num; i++) { - if (auth_dat[i].account_id == acc) { - if (strcmpi(auth_dat[i].email, actual_email) == 0) { - memcpy(auth_dat[i].email, new_email, 40); - login_log("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s)." RETCODE, - server[id].name, acc, auth_dat[i].userid, new_email, ip); - // Save - mmo_auth_sync(); - } else - login_log("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual e-mail is incorrect (account: %d (%s), actual e-mail: %s, proposed e-mail: %s, ip: %s)." RETCODE, - server[id].name, acc, auth_dat[i].userid, auth_dat[i].email, actual_email, ip); - break; - } - } - if (i == auth_num) - login_log("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but account doesn't exist (account: %d, ip: %s)." RETCODE, - server[id].name, acc, ip); - } - } - RFIFOSKIP(fd, 86); - break; - - // Receiving of map-server via char-server a status change resquest (by Yor) - case 0x2724: - if (RFIFOREST(fd) < 10) - return 0; - { - int acc, statut; - acc = RFIFOL(fd,2); - statut = RFIFOL(fd,6); - for(i = 0; i < auth_num; i++) { - if (auth_dat[i].account_id == acc) { - if (auth_dat[i].state != statut) { - login_log("Char-server '%s': Status change (account: %d, new status %d, ip: %s)." RETCODE, - server[id].name, acc, statut, ip); - if (statut != 0) { - unsigned char buf[16]; - WBUFW(buf,0) = 0x2731; - WBUFL(buf,2) = acc; - WBUFB(buf,6) = 0; // 0: change of statut, 1: ban - WBUFL(buf,7) = statut; // status or final date of a banishment - charif_sendallwos(-1, buf, 11); - for(j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == acc) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - } - auth_dat[i].state = statut; - // Save - mmo_auth_sync(); - } else - login_log("Char-server '%s': Error of Status change - actual status is already the good status (account: %d, status %d, ip: %s)." RETCODE, - server[id].name, acc, statut, ip); - break; - } - } - if (i == auth_num) { - login_log("Char-server '%s': Error of Status change (account: %d not found, suggested status %d, ip: %s)." RETCODE, - server[id].name, acc, statut, ip); - } - RFIFOSKIP(fd,10); - } - return 0; - - case 0x2725: // Receiving of map-server via char-server a ban resquest (by Yor) - if (RFIFOREST(fd) < 18) - return 0; - { - acc = RFIFOL(fd,2); - for(i = 0; i < auth_num; i++) { - if (auth_dat[i].account_id == acc) { - time_t timestamp; - struct tm *tmtime; - if (auth_dat[i].ban_until_time == 0 || auth_dat[i].ban_until_time < time(NULL)) - timestamp = time(NULL); - else - timestamp = auth_dat[i].ban_until_time; - tmtime = localtime(×tamp); - tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,6); - tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,8); - tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,10); - tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,12); - tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,14); - tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,16); - timestamp = mktime(tmtime); - if (timestamp != -1) { - if (timestamp <= time(NULL)) - timestamp = 0; - if (auth_dat[i].ban_until_time != timestamp) { - if (timestamp != 0) { - unsigned char buf[16]; - char tmpstr[2048]; - strftime(tmpstr, 24, date_format, localtime(×tamp)); - login_log("Char-server '%s': Ban request (account: %d, new final date of banishment: %d (%s), ip: %s)." RETCODE, - server[id].name, acc, timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip); - WBUFW(buf,0) = 0x2731; - WBUFL(buf,2) = auth_dat[i].account_id; - WBUFB(buf,6) = 1; // 0: change of statut, 1: ban - WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment - charif_sendallwos(-1, buf, 11); - for(j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == acc) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - } else { - login_log("Char-server '%s': Error of ban request (account: %d, new date unbans the account, ip: %s)." RETCODE, - server[id].name, acc, ip); - } - auth_dat[i].ban_until_time = timestamp; - // Save - mmo_auth_sync(); - } else { - login_log("Char-server '%s': Error of ban request (account: %d, no change for ban date, ip: %s)." RETCODE, - server[id].name, acc, ip); - } - } else { - login_log("Char-server '%s': Error of ban request (account: %d, invalid date, ip: %s)." RETCODE, - server[id].name, acc, ip); - } - break; - } - } - if (i == auth_num) - login_log("Char-server '%s': Error of ban request (account: %d not found, ip: %s)." RETCODE, - server[id].name, acc, ip); - RFIFOSKIP(fd,18); - } - return 0; - - case 0x2727: // Change of sex (sex is reversed) - if (RFIFOREST(fd) < 6) - return 0; - { - int sex; - acc = RFIFOL(fd,2); - for(i = 0; i < auth_num; i++) { -// printf("%d,", auth_dat[i].account_id); - if (auth_dat[i].account_id == acc) { - if (auth_dat[i].sex == 2) - login_log("Char-server '%s': Error of sex change - Server account (suggested account: %d, actual sex %d (Server), ip: %s)." RETCODE, - server[id].name, acc, auth_dat[i].sex, ip); - else { - unsigned char buf[16]; - if (auth_dat[i].sex == 0) - sex = 1; - else - sex = 0; - login_log("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s)." RETCODE, - server[id].name, acc, (sex == 2) ? 'S' : (sex ? 'M' : 'F'), ip); - for(j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == acc) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - auth_dat[i].sex = sex; - WBUFW(buf,0) = 0x2723; - WBUFL(buf,2) = acc; - WBUFB(buf,6) = sex; - charif_sendallwos(-1, buf, 7); - // Save - mmo_auth_sync(); - } - break; - } - } - if (i == auth_num) - login_log("Char-server '%s': Error of sex change (account: %d not found, sex would be reversed, ip: %s)." RETCODE, - server[id].name, acc, ip); - RFIFOSKIP(fd,6); - } - return 0; - - case 0x2728: // We receive account_reg2 from a char-server, and we send them to other map-servers. - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { - int p; - acc = RFIFOL(fd,4); - for(i = 0; i < auth_num; i++) { - if (auth_dat[i].account_id == acc) { - //unsigned char buf[rfifow(fd,2)+1]; - unsigned char *buf; - int len; - buf = (unsigned char*)aCalloc(RFIFOW(fd,2)+1, sizeof(unsigned char)); - login_log("char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s)." RETCODE, - server[id].name, acc, ip); - for(j=0,p=13;jforeach(online_db,online_db_setoffline,id); //Set all chars from this char-server offline first - users = RFIFOW(fd,4); - for (i = 0; i < users; i++) { - aid = RFIFOL(fd,6+i*4); - p = idb_ensure(online_db, aid, create_online_user); - p->char_server = id; - p->waiting_disconnect = 0; - } - RFIFOSKIP(fd,RFIFOW(fd,2)); - break; - } - case 0x272e: //Request account_reg2 for a character. - if (RFIFOREST(fd) < 10) - return 0; - { - int account_id = RFIFOL(fd, 2); - int char_id = RFIFOL(fd, 6); - int p; - RFIFOSKIP(fd,10); - WFIFOW(fd,0) = 0x2729; - WFIFOL(fd,4) = account_id; - WFIFOL(fd,8) = char_id; - WFIFOB(fd,12) = 1; //Type 1 for Account2 registry - for(i = 0; i < auth_num && auth_dat[i].account_id != account_id; i++); - if (i == auth_num) { - //Account not found? Send at least empty data, map servers need a reply! - WFIFOW(fd,2) = 13; - WFIFOSET(fd,WFIFOW(fd,2)); - break; - } - for(p = 13,j=0;jforeach(online_db,online_db_setoffline,id); - RFIFOSKIP(fd,2); - break; - - case 0x3000: //change sex for chrif_changesex() - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - { - unsigned int sex; - acc = RFIFOL(fd,4); - sex = RFIFOB(fd,8); - if (sex != 0 && sex != 1) - sex = 0; - for(i = 0; i < auth_num; i++) { - if (auth_dat[i].account_id == acc) { - unsigned char buf[16]; - login_log("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s)." RETCODE, - server[id].name, acc, (sex == 2) ? 'S' : (sex ? 'M' : 'F'), ip); - auth_fifo[i].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - auth_dat[i].sex = sex; - WBUFW(buf,0) = 0x2723; - WBUFL(buf,2) = acc; - WBUFB(buf,6) = sex; - charif_sendallwos(-1, buf, 7); - break; - } - } - if (i == auth_num) { - login_log("Char-server '%s': Error of Sex change (account: %d not found, suggested sex %c, ip: %s)." RETCODE, - server[id].name, acc, (sex == 2) ? 'S' : (sex ? 'M' : 'F'), ip); - } - RFIFOSKIP(fd,RFIFOW(fd,2)); - } - return 0; - - default: - { - FILE *logfp; - char tmpstr[24]; - time_t raw_time; - logfp = fopen(login_log_unknown_packets_filename, "a"); - if (logfp) { - time(&raw_time); - strftime(tmpstr, 23, date_format, localtime(&raw_time)); - fprintf(logfp, "%s: receiving of an unknown packet -> disconnection" RETCODE, tmpstr); - fprintf(logfp, "parse_fromchar: connection #%d (ip: %s), packet: 0x%x (with being read: %d)." RETCODE, fd, ip, RFIFOW(fd,0), RFIFOREST(fd)); - fprintf(logfp, "Detail (in hex):" RETCODE); - fprintf(logfp, "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F" RETCODE); - memset(tmpstr, '\0', sizeof(tmpstr)); - for(i = 0; i < RFIFOREST(fd); i++) { - if ((i & 15) == 0) - fprintf(logfp, "%04X ",i); - fprintf(logfp, "%02x ", RFIFOB(fd,i)); - if (RFIFOB(fd,i) > 0x1f) - tmpstr[i % 16] = RFIFOB(fd,i); - else - tmpstr[i % 16] = '.'; - if ((i - 7) % 16 == 0) // -8 + 1 - fprintf(logfp, " "); - else if ((i + 1) % 16 == 0) { - fprintf(logfp, " %s" RETCODE, tmpstr); - memset(tmpstr, '\0', sizeof(tmpstr)); - } - } - if (i % 16 != 0) { - for(j = i; j % 16 != 0; j++) { - fprintf(logfp, " "); - if ((j - 7) % 16 == 0) // -8 + 1 - fprintf(logfp, " "); - } - fprintf(logfp, " %s" RETCODE, tmpstr); - } - fprintf(logfp, RETCODE); - fclose(logfp); - } - } - ShowWarning("parse_fromchar: Unknown packet 0x%x (from a char-server)! -> disconnection.\n", RFIFOW(fd,0)); - session[fd]->eof = 1; - ShowStatus("Char-server has been disconnected (unknown packet).\n"); - return 0; - } - } - RFIFOSKIP(fd,RFIFOREST(fd)); - return 0; -} - -//--------------------------------------- -// Packet parsing for administation login -//--------------------------------------- -int parse_admin(int fd) { - unsigned int i, j; - unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; - char* account_name; - char ip[16]; - RFIFOHEAD(fd); - - sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - - if (session[fd]->eof) { - do_close(fd); - ShowInfo("Remote administration has disconnected (session #%d).\n", fd); - return 0; - } - - while(RFIFOREST(fd) >= 2) { - if (display_parse_admin == 1) { - - ShowDebug("parse_admin: connection #%d, packet: 0x%x (with being read: %d).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); - } - - switch(RFIFOW(fd,0)) { - case 0x7530: // Request of the server version - login_log("'ladmin': Sending of the server version (ip: %s)" RETCODE, ip); - WFIFOHEAD(fd, 10); - WFIFOW(fd,0) = 0x7531; - WFIFOB(fd,2) = ATHENA_MAJOR_VERSION; - WFIFOB(fd,3) = ATHENA_MINOR_VERSION; - WFIFOB(fd,4) = ATHENA_REVISION; - WFIFOB(fd,5) = ATHENA_RELEASE_FLAG; - WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG; - WFIFOB(fd,7) = ATHENA_SERVER_LOGIN; - WFIFOW(fd,8) = ATHENA_MOD_VERSION; - WFIFOSET(fd,10); - RFIFOSKIP(fd,2); - break; - - case 0x7532: // Request of end of connection - login_log("'ladmin': End of connection (ip: %s)" RETCODE, ip); - RFIFOSKIP(fd,2); - session[fd]->eof = 1; - break; - - case 0x7920: // Request of an accounts list - if (RFIFOREST(fd) < 10) - return 0; - { - int st, ed, len; - //int id[auth_num]; - //int *id=(int *)aCalloc(auth_num, sizeof(int)); - CREATE_BUFFER(id, int, auth_num); - st = RFIFOL(fd,2); - ed = RFIFOL(fd,6); - RFIFOSKIP(fd,10); - WFIFOW(fd,0) = 0x7921; - if (st < 0) - st = 0; - if (ed > END_ACCOUNT_NUM || ed < st || ed <= 0) - ed = END_ACCOUNT_NUM; - login_log("'ladmin': Sending an accounts list (ask: from %d to %d, ip: %s)" RETCODE, st, ed, ip); - // Sort before send - for(i = 0; i < auth_num; i++) { - unsigned int k; - id[i] = i; - for(j = 0; j < i; j++) { - if (auth_dat[id[i]].account_id < auth_dat[id[j]].account_id) { - for(k = i; k > j; k--) { - id[k] = id[k-1]; - } - id[j] = i; // id[i] - break; - } - } - } - // Sending accounts information - len = 4; - for(i = 0; i < auth_num && len < 30000; i++) { - int account_id = auth_dat[id[i]].account_id; // use sorted index - if (account_id >= st && account_id <= ed) { - j = id[i]; - WFIFOL(fd,len) = account_id; - WFIFOB(fd,len+4) = (unsigned char)isGM(account_id); - memcpy(WFIFOP(fd,len+5), auth_dat[j].userid, 24); - WFIFOB(fd,len+29) = auth_dat[j].sex; - WFIFOL(fd,len+30) = auth_dat[j].logincount; - if (auth_dat[j].state == 0 && auth_dat[j].ban_until_time != 0) // if no state and banished - WFIFOL(fd,len+34) = 7; // 6 = Your are Prohibited to log in until %s - else - WFIFOL(fd,len+34) = auth_dat[j].state; - len += 38; - } - } - WFIFOW(fd,2) = len; - WFIFOSET(fd,len); - //if (id) free(id); - DELETE_BUFFER(id); - } - break; - - case 0x7930: // Request for an account creation - if (RFIFOREST(fd) < 91) - return 0; - { - struct mmo_account ma; - memcpy(ma.userid,RFIFOP(fd, 2),NAME_LENGTH); - ma.userid[23] = '\0'; - memcpy(ma.passwd, RFIFOP(fd, 26), NAME_LENGTH); - ma.passwd[23] = '\0'; - memcpy(ma.lastlogin, "-", 2); - ma.sex = RFIFOB(fd,50); - WFIFOW(fd,0) = 0x7931; - WFIFOL(fd,2) = 0xffffffff; - memcpy(WFIFOP(fd,6), RFIFOP(fd,2), 24); - if (strlen(ma.userid) < 4 || strlen(ma.passwd) < 4) { - login_log("'ladmin': Attempt to create an invalid account (account or pass is too short, ip: %s)" RETCODE, - ip); - } else if (ma.sex != 'F' && ma.sex != 'M') { - login_log("'ladmin': Attempt to create an invalid account (account: %s, received pass: %s, invalid sex, ip: %s)" RETCODE, - ma.userid, ma.passwd, ip); - } else if (account_id_count > END_ACCOUNT_NUM) { - login_log("'ladmin': Attempt to create an account, but there is no more available id number (account: %s, pass: %s, sex: %c, ip: %s)" RETCODE, - ma.userid, ma.passwd, ma.sex, ip); - } else { - remove_control_chars((unsigned char *)ma.userid); - remove_control_chars((unsigned char *)ma.passwd); - for(i = 0; i < auth_num; i++) { - if (strncmp(auth_dat[i].userid, ma.userid, 24) == 0) { - login_log("'ladmin': Attempt to create an already existing account (account: %s, pass: %s, received pass: %s, ip: %s)" RETCODE, - auth_dat[i].userid, auth_dat[i].pass, ma.passwd, ip); - break; - } - } - if (i == auth_num) { - int new_id; - char email[40]; - memcpy(email, RFIFOP(fd,51), 40); - email[39] = '\0'; - remove_control_chars((unsigned char *)email); - new_id = mmo_auth_new(&ma, ma.sex, email); - login_log("'ladmin': Account creation (account: %s (id: %d), pass: %s, sex: %c, email: %s, ip: %s)" RETCODE, - ma.userid, new_id, ma.passwd, ma.sex, auth_dat[i].email, ip); - WFIFOL(fd,2) = new_id; - mmo_auth_sync(); - } - } - WFIFOSET(fd,30); - RFIFOSKIP(fd,91); - } - break; - - case 0x7932: // Request for an account deletion - if (RFIFOREST(fd) < 26) - return 0; - WFIFOW(fd,0) = 0x7933; - WFIFOL(fd,2) = 0xFFFFFFFF; - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - i = search_account_index(account_name); - if (i != -1) { - // Char-server is notified of deletion (for characters deletion). - unsigned char buf[65535]; - WBUFW(buf,0) = 0x2730; - WBUFL(buf,2) = auth_dat[i].account_id; - charif_sendallwos(-1, buf, 6); - // send answer - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - WFIFOL(fd,2) = auth_dat[i].account_id; - // save deleted account in log file - login_log("'ladmin': Account deletion (account: %s, id: %d, ip: %s) - saved in next line:" RETCODE, - auth_dat[i].userid, auth_dat[i].account_id, ip); - mmo_auth_tostr((char*)buf, &auth_dat[i]); - login_log("%s" RETCODE, buf); - // delete account - memset(auth_dat[i].userid, '\0', sizeof(auth_dat[i].userid)); - auth_dat[i].account_id = -1; - mmo_auth_sync(); - } else { - memcpy(WFIFOP(fd,6), account_name, 24); - login_log("'ladmin': Attempt to delete an unknown account (account: %s, ip: %s)" RETCODE, - account_name, ip); - } - WFIFOSET(fd,30); - RFIFOSKIP(fd,26); - break; - - case 0x7934: // Request to change a password - if (RFIFOREST(fd) < 50) - return 0; - WFIFOW(fd,0) = 0x7935; - WFIFOL(fd,2) = 0xFFFFFFFF; /// WTF??? an unsigned being set to a -1 - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - i = search_account_index(account_name); - if (i != -1) { - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - memcpy(auth_dat[i].pass, RFIFOP(fd,26), 24); - auth_dat[i].pass[23] = '\0'; - remove_control_chars((unsigned char *)auth_dat[i].pass); - WFIFOL(fd,2) = auth_dat[i].account_id; - login_log("'ladmin': Modification of a password (account: %s, new password: %s, ip: %s)" RETCODE, - auth_dat[i].userid, auth_dat[i].pass, ip); - mmo_auth_sync(); - } else { - memcpy(WFIFOP(fd,6), account_name, 24); - login_log("'ladmin': Attempt to modify the password of an unknown account (account: %s, ip: %s)" RETCODE, - account_name, ip); - } - WFIFOSET(fd,30); - RFIFOSKIP(fd,50); - break; - - case 0x7936: // Request to modify a state - if (RFIFOREST(fd) < 50) - return 0; - { - char error_message[20]; - int statut; - WFIFOW(fd,0) = 0x7937; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - statut = RFIFOL(fd,26); - memcpy(error_message, RFIFOP(fd,30), 20); - error_message[19] = '\0'; - remove_control_chars((unsigned char *)error_message); - if (statut != 7 || error_message[0] == '\0') { // 7: // 6 = Your are Prohibited to log in until %s - strcpy(error_message, "-"); - } - i = search_account_index(account_name); - if (i != -1) { - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - WFIFOL(fd,2) = auth_dat[i].account_id; - if (auth_dat[i].state == statut && strcmp(auth_dat[i].error_message, error_message) == 0) - login_log("'ladmin': Modification of a state, but the state of the account is already the good state (account: %s, received state: %d, ip: %s)" RETCODE, - account_name, statut, ip); - else { - if (statut == 7) - login_log("'ladmin': Modification of a state (account: %s, new state: %d - prohibited to login until '%s', ip: %s)" RETCODE, - auth_dat[i].userid, statut, error_message, ip); - else - login_log("'ladmin': Modification of a state (account: %s, new state: %d, ip: %s)" RETCODE, - auth_dat[i].userid, statut, ip); - if (auth_dat[i].state == 0) { - unsigned char buf[16]; - WBUFW(buf,0) = 0x2731; - WBUFL(buf,2) = auth_dat[i].account_id; - WBUFB(buf,6) = 0; // 0: change of statut, 1: ban - WBUFL(buf,7) = statut; // status or final date of a banishment - charif_sendallwos(-1, buf, 11); - for(j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == auth_dat[i].account_id) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - } - auth_dat[i].state = statut; - memcpy(auth_dat[i].error_message, error_message, 20); - mmo_auth_sync(); - } - } else { - memcpy(WFIFOP(fd,6), account_name, 24); - login_log("'ladmin': Attempt to modify the state of an unknown account (account: %s, received state: %d, ip: %s)" RETCODE, - account_name, statut, ip); - } - WFIFOL(fd,30) = statut; - } - WFIFOSET(fd,34); - RFIFOSKIP(fd,50); - break; - - case 0x7938: // Request for servers list and # of online players - login_log("'ladmin': Sending of servers list (ip: %s)" RETCODE, ip); - server_num = 0; - for(i = 0; i < MAX_SERVERS; i++) { - if (server_fd[i] >= 0) { - WFIFOL(fd,4+server_num*32) = server[i].ip; - WFIFOW(fd,4+server_num*32+4) = server[i].port; - memcpy(WFIFOP(fd,4+server_num*32+6), server[i].name, 20); - WFIFOW(fd,4+server_num*32+26) = server[i].users; - WFIFOW(fd,4+server_num*32+28) = server[i].maintenance; - WFIFOW(fd,4+server_num*32+30) = server[i].new_; - server_num++; - } - } - WFIFOW(fd,0) = 0x7939; - WFIFOW(fd,2) = 4 + 32 * server_num; - WFIFOSET(fd,4+32*server_num); - RFIFOSKIP(fd,2); - break; - - case 0x793a: // Request to password check - if (RFIFOREST(fd) < 50) - return 0; - WFIFOW(fd,0) = 0x793b; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - i = search_account_index(account_name); - if (i != -1) { - char pass[25]; - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - memcpy(pass, RFIFOP(fd,26), 24); - pass[24] = '\0'; - remove_control_chars((unsigned char *)pass); - if (strcmp(auth_dat[i].pass, pass) == 0) { - WFIFOL(fd,2) = auth_dat[i].account_id; - login_log("'ladmin': Check of password OK (account: %s, password: %s, ip: %s)" RETCODE, - auth_dat[i].userid, auth_dat[i].pass, ip); - } else { - login_log("'ladmin': Failure of password check (account: %s, proposed pass: %s, ip: %s)" RETCODE, - auth_dat[i].userid, pass, ip); - } - } else { - memcpy(WFIFOP(fd,6), account_name, 24); - login_log("'ladmin': Attempt to check the password of an unknown account (account: %s, ip: %s)" RETCODE, - account_name, ip); - } - WFIFOSET(fd,30); - RFIFOSKIP(fd,50); - break; - - case 0x793c: // Request to modify sex - if (RFIFOREST(fd) < 27) - return 0; - WFIFOW(fd,0) = 0x793d; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - memcpy(WFIFOP(fd,6), account_name, 24); - { - char sex; - sex = RFIFOB(fd,26); - if (sex != 'F' && sex != 'M') { - if (sex > 31) - login_log("'ladmin': Attempt to give an invalid sex (account: %s, received sex: %c, ip: %s)" RETCODE, - account_name, sex, ip); - else - login_log("'ladmin': Attempt to give an invalid sex (account: %s, received sex: 'control char', ip: %s)" RETCODE, - account_name, ip); - } else { - i = search_account_index(account_name); - if (i != -1) { - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - if (auth_dat[i].sex != ((sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'))) { - unsigned char buf[16]; - WFIFOL(fd,2) = auth_dat[i].account_id; - for(j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == auth_dat[i].account_id) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - auth_dat[i].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); - login_log("'ladmin': Modification of a sex (account: %s, new sex: %c, ip: %s)" RETCODE, - auth_dat[i].userid, sex, ip); - mmo_auth_sync(); - // send to all char-server the change - WBUFW(buf,0) = 0x2723; - WBUFL(buf,2) = auth_dat[i].account_id; - WBUFB(buf,6) = auth_dat[i].sex; - charif_sendallwos(-1, buf, 7); - } else { - login_log("'ladmin': Modification of a sex, but the sex is already the good sex (account: %s, sex: %c, ip: %s)" RETCODE, - auth_dat[i].userid, sex, ip); - } - } else { - login_log("'ladmin': Attempt to modify the sex of an unknown account (account: %s, received sex: %c, ip: %s)" RETCODE, - account_name, sex, ip); - } - } - } - WFIFOSET(fd,30); - RFIFOSKIP(fd,27); - break; - - case 0x793e: // Request to modify GM level - if (RFIFOREST(fd) < 27) - return 0; - WFIFOW(fd,0) = 0x793f; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - memcpy(WFIFOP(fd,6), account_name, 24); - { - char new_gm_level; - new_gm_level = RFIFOB(fd,26); - if (new_gm_level < 0 || new_gm_level > 99) { - login_log("'ladmin': Attempt to give an invalid GM level (account: %s, received GM level: %d, ip: %s)" RETCODE, - account_name, (int)new_gm_level, ip); - } else { - i = search_account_index(account_name); - if (i != -1) { - int acc = auth_dat[i].account_id; - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - if (isGM(acc) != new_gm_level) { - // modification of the file - FILE *fp, *fp2; - int lock; - char line[512]; - int GM_account, GM_level; - int modify_flag; - char tmpstr[24]; - time_t raw_time; - if ((fp2 = lock_fopen(GM_account_filename, &lock)) != NULL) { - if ((fp = fopen(GM_account_filename, "r")) != NULL) { - time(&raw_time); - strftime(tmpstr, 23, date_format, localtime(&raw_time)); - modify_flag = 0; - // read/write GM file - while(fgets(line, sizeof(line)-1, fp)) { - while(line[0] != '\0' && (line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r')) - line[strlen(line)-1] = '\0'; - if ((line[0] == '/' && line[1] == '/') || line[0] == '\0') - fprintf(fp2, "%s" RETCODE, line); - else { - if (sscanf(line, "%d %d", &GM_account, &GM_level) != 2 && sscanf(line, "%d: %d", &GM_account, &GM_level) != 2) - fprintf(fp2, "%s" RETCODE, line); - else if (GM_account != acc) - fprintf(fp2, "%s" RETCODE, line); - else if (new_gm_level < 1) { - fprintf(fp2, "// %s: 'ladmin' GM level removed on account %d '%s' (previous level: %d)" RETCODE "//%d %d" RETCODE, tmpstr, acc, auth_dat[i].userid, GM_level, acc, new_gm_level); - modify_flag = 1; - } else { - fprintf(fp2, "// %s: 'ladmin' GM level on account %d '%s' (previous level: %d)" RETCODE "%d %d" RETCODE, tmpstr, acc, auth_dat[i].userid, GM_level, acc, new_gm_level); - modify_flag = 1; - } - } - } - if (modify_flag == 0) - fprintf(fp2, "// %s: 'ladmin' GM level on account %d '%s' (previous level: 0)" RETCODE "%d %d" RETCODE, tmpstr, acc, auth_dat[i].userid, acc, new_gm_level); - fclose(fp); - } else { - login_log("'ladmin': Attempt to modify of a GM level - impossible to read GM accounts file (account: %s (%d), received GM level: %d, ip: %s)" RETCODE, - auth_dat[i].userid, acc, (int)new_gm_level, ip); - } - if (lock_fclose(fp2, GM_account_filename, &lock) == 0) { - WFIFOL(fd,2) = acc; - login_log("'ladmin': Modification of a GM level (account: %s (%d), new GM level: %d, ip: %s)" RETCODE, - auth_dat[i].userid, acc, (int)new_gm_level, ip); - // read and send new GM informations - read_gm_account(); - send_GM_accounts(); - } else { - login_log("'ladmin': Attempt to modify of a GM level - impossible to write GM accounts file (account: %s (%d), received GM level: %d, ip: %s)" RETCODE, - auth_dat[i].userid, acc, (int)new_gm_level, ip); - } - } else { - login_log("'ladmin': Attempt to modify of a GM level - impossible to write GM accounts file (account: %s (%d), received GM level: %d, ip: %s)" RETCODE, - auth_dat[i].userid, acc, (int)new_gm_level, ip); - } - } else { - login_log("'ladmin': Attempt to modify of a GM level, but the GM level is already the good GM level (account: %s (%d), GM level: %d, ip: %s)" RETCODE, - auth_dat[i].userid, acc, (int)new_gm_level, ip); - } - } else { - login_log("'ladmin': Attempt to modify the GM level of an unknown account (account: %s, received GM level: %d, ip: %s)" RETCODE, - account_name, (int)new_gm_level, ip); - } - } - } - WFIFOSET(fd,30); - RFIFOSKIP(fd,27); - break; - - case 0x7940: // Request to modify e-mail - if (RFIFOREST(fd) < 66) - return 0; - WFIFOW(fd,0) = 0x7941; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - memcpy(WFIFOP(fd,6), account_name, 24); - { - char email[40]; - memcpy(email, RFIFOP(fd,26), 40); - if (e_mail_check(email) == 0) { - login_log("'ladmin': Attempt to give an invalid e-mail (account: %s, ip: %s)" RETCODE, - account_name, ip); - } else { - remove_control_chars((unsigned char *)email); - i = search_account_index(account_name); - if (i != -1) { - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - memcpy(auth_dat[i].email, email, 40); - WFIFOL(fd,2) = auth_dat[i].account_id; - login_log("'ladmin': Modification of an email (account: %s, new e-mail: %s, ip: %s)" RETCODE, - auth_dat[i].userid, email, ip); - mmo_auth_sync(); - } else { - login_log("'ladmin': Attempt to modify the e-mail of an unknown account (account: %s, received e-mail: %s, ip: %s)" RETCODE, - account_name, email, ip); - } - } - } - WFIFOSET(fd,30); - RFIFOSKIP(fd,66); - break; - - case 0x7942: // Request to modify memo field - if ((int)RFIFOREST(fd) < 28 || (int)RFIFOREST(fd) < (28 + RFIFOW(fd,26))) - return 0; - WFIFOW(fd,0) = 0x7943; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - i = search_account_index(account_name); - if (i != -1) { - int size_of_memo = sizeof(auth_dat[i].memo); - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - memset(auth_dat[i].memo, '\0', size_of_memo); - if (RFIFOW(fd,26) == 0) { - strncpy(auth_dat[i].memo, "-", size_of_memo); - } else if (RFIFOW(fd,26) > size_of_memo - 1) { - memcpy(auth_dat[i].memo, RFIFOP(fd,28), size_of_memo - 1); - } else { - memcpy(auth_dat[i].memo, RFIFOP(fd,28), RFIFOW(fd,26)); - } - auth_dat[i].memo[size_of_memo - 1] = '\0'; - remove_control_chars((unsigned char *)auth_dat[i].memo); - WFIFOL(fd,2) = auth_dat[i].account_id; - login_log("'ladmin': Modification of a memo field (account: %s, new memo: %s, ip: %s)" RETCODE, - auth_dat[i].userid, auth_dat[i].memo, ip); - mmo_auth_sync(); - } else { - memcpy(WFIFOP(fd,6), account_name, 24); - login_log("'ladmin': Attempt to modify the memo field of an unknown account (account: %s, ip: %s)" RETCODE, - account_name, ip); - } - WFIFOSET(fd,30); - RFIFOSKIP(fd,28 + RFIFOW(fd,26)); - break; - - case 0x7944: // Request to found an account id - if (RFIFOREST(fd) < 26) - return 0; - WFIFOW(fd,0) = 0x7945; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - i = search_account_index(account_name); - if (i != -1) { - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - WFIFOL(fd,2) = auth_dat[i].account_id; - login_log("'ladmin': Request (by the name) of an account id (account: %s, id: %d, ip: %s)" RETCODE, - auth_dat[i].userid, auth_dat[i].account_id, ip); - } else { - memcpy(WFIFOP(fd,6), account_name, 24); - login_log("'ladmin': ID request (by the name) of an unknown account (account: %s, ip: %s)" RETCODE, - account_name, ip); - } - WFIFOSET(fd,30); - RFIFOSKIP(fd,26); - break; - - case 0x7946: // Request to found an account name - if (RFIFOREST(fd) < 6) - return 0; - WFIFOW(fd,0) = 0x7947; - WFIFOL(fd,2) = RFIFOL(fd,2); - memset(WFIFOP(fd,6), '\0', 24); - for(i = 0; i < auth_num; i++) { - if (auth_dat[i].account_id == RFIFOL(fd,2)) { - strncpy((char*)WFIFOP(fd,6), auth_dat[i].userid, 24); - login_log("'ladmin': Request (by id) of an account name (account: %s, id: %d, ip: %s)" RETCODE, - auth_dat[i].userid, RFIFOL(fd,2), ip); - break; - } - } - if (i == auth_num) { - login_log("'ladmin': Name request (by id) of an unknown account (id: %d, ip: %s)" RETCODE, - RFIFOL(fd,2), ip); - strncpy((char*)WFIFOP(fd,6), "", 24); - } - WFIFOSET(fd,30); - RFIFOSKIP(fd,6); - break; - - case 0x7948: // Request to change the validity limit (timestamp) (absolute value) - if (RFIFOREST(fd) < 30) - return 0; - { - time_t timestamp; - char tmpstr[2048]; - WFIFOW(fd,0) = 0x7949; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - timestamp = (time_t)RFIFOL(fd,26); - strftime(tmpstr, 24, date_format, localtime(×tamp)); - i = search_account_index(account_name); - if (i != -1) { - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - login_log("'ladmin': Change of a validity limit (account: %s, new validity: %d (%s), ip: %s)" RETCODE, - auth_dat[i].userid, timestamp, (timestamp == 0 ? "unlimited" : tmpstr), ip); - auth_dat[i].connect_until_time = timestamp; - WFIFOL(fd,2) = auth_dat[i].account_id; - mmo_auth_sync(); - } else { - memcpy(WFIFOP(fd,6), account_name, 24); - login_log("'ladmin': Attempt to change the validity limit of an unknown account (account: %s, received validity: %d (%s), ip: %s)" RETCODE, - account_name, timestamp, (timestamp == 0 ? "unlimited" : tmpstr), ip); - } - WFIFOL(fd,30) = (unsigned int)timestamp; - } - WFIFOSET(fd,34); - RFIFOSKIP(fd,30); - break; - - case 0x794a: // Request to change the final date of a banishment (timestamp) (absolute value) - if (RFIFOREST(fd) < 30) - return 0; - { - time_t timestamp; - char tmpstr[2048]; - WFIFOW(fd,0) = 0x794b; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - timestamp = (time_t)RFIFOL(fd,26); - if (timestamp <= time(NULL)) - timestamp = 0; - strftime(tmpstr, 24, date_format, localtime(×tamp)); - i = search_account_index(account_name); - if (i != -1) { - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - WFIFOL(fd,2) = auth_dat[i].account_id; - login_log("'ladmin': Change of the final date of a banishment (account: %s, new final date of banishment: %d (%s), ip: %s)" RETCODE, - auth_dat[i].userid, timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip); - if (auth_dat[i].ban_until_time != timestamp) { - if (timestamp != 0) { - unsigned char buf[16]; - WBUFW(buf,0) = 0x2731; - WBUFL(buf,2) = auth_dat[i].account_id; - WBUFB(buf,6) = 1; // 0: change of statut, 1: ban - WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment - charif_sendallwos(-1, buf, 11); - for(j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == auth_dat[i].account_id) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - } - auth_dat[i].ban_until_time = timestamp; - mmo_auth_sync(); - } - } else { - memcpy(WFIFOP(fd,6), account_name, 24); - login_log("'ladmin': Attempt to change the final date of a banishment of an unknown account (account: %s, received final date of banishment: %d (%s), ip: %s)" RETCODE, - account_name, timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip); - } - WFIFOL(fd,30) = (unsigned int)timestamp; - } - WFIFOSET(fd,34); - RFIFOSKIP(fd,30); - break; - - case 0x794c: // Request to change the final date of a banishment (timestamp) (relative change) - if (RFIFOREST(fd) < 38) - return 0; - { - time_t timestamp; - struct tm *tmtime; - char tmpstr[2048]; - WFIFOW(fd,0) = 0x794d; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - i = search_account_index(account_name); - if (i != -1) { - WFIFOL(fd,2) = auth_dat[i].account_id; - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - if (auth_dat[i].ban_until_time == 0 || auth_dat[i].ban_until_time < time(NULL)) - timestamp = time(NULL); - else - timestamp = auth_dat[i].ban_until_time; - tmtime = localtime(×tamp); - tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,26); - tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,28); - tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,30); - tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,32); - tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,34); - tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,36); - timestamp = mktime(tmtime); - if (timestamp != -1) { - if (timestamp <= time(NULL)) - timestamp = 0; - strftime(tmpstr, 24, date_format, localtime(×tamp)); - login_log("'ladmin': Adjustment of a final date of a banishment (account: %s, (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)" RETCODE, - auth_dat[i].userid, (short)RFIFOW(fd,26), (short)RFIFOW(fd,28), (short)RFIFOW(fd,30), (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip); - if (auth_dat[i].ban_until_time != timestamp) { - if (timestamp != 0) { - unsigned char buf[16]; - WBUFW(buf,0) = 0x2731; - WBUFL(buf,2) = auth_dat[i].account_id; - WBUFB(buf,6) = 1; // 0: change of statut, 1: ban - WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment - charif_sendallwos(-1, buf, 11); - for(j = 0; j < AUTH_FIFO_SIZE; j++) - if (auth_fifo[j].account_id == auth_dat[i].account_id) - auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) - } - auth_dat[i].ban_until_time = timestamp; - mmo_auth_sync(); - } - } else { - strftime(tmpstr, 24, date_format, localtime(&auth_dat[i].ban_until_time)); - login_log("'ladmin': Impossible to adjust the final date of a banishment (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)" RETCODE, - auth_dat[i].userid, auth_dat[i].ban_until_time, (auth_dat[i].ban_until_time == 0 ? "no banishment" : tmpstr), (short)RFIFOW(fd,26), (short)RFIFOW(fd,28), (short)RFIFOW(fd,30), (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), ip); - } - WFIFOL(fd,30) = (unsigned long)auth_dat[i].ban_until_time; - } else { - memcpy(WFIFOP(fd,6), account_name, 24); - login_log("'ladmin': Attempt to adjust the final date of a banishment of an unknown account (account: %s, ip: %s)" RETCODE, - account_name, ip); - WFIFOL(fd,30) = 0; - } - } - WFIFOSET(fd,34); - RFIFOSKIP(fd,38); - break; - - case 0x794e: // Request to send a broadcast message - if (RFIFOREST(fd) < 8 || RFIFOREST(fd) < (8 + RFIFOL(fd,4))) - return 0; - WFIFOW(fd,0) = 0x794f; - WFIFOW(fd,2) = 0xFFFF; // WTF??? - if (RFIFOL(fd,4) < 1) { - login_log("'ladmin': Receiving a message for broadcast, but message is void (ip: %s)" RETCODE, - ip); - } else { - // at least 1 char-server - for(i = 0; i < MAX_SERVERS; i++) - if (server_fd[i] >= 0) - break; - if (i == MAX_SERVERS) { - login_log("'ladmin': Receiving a message for broadcast, but no char-server is online (ip: %s)" RETCODE, - ip); - } else { - unsigned char buf[32000]; - char message[32000]; - WFIFOW(fd,2) = 0; - memset(message, '\0', sizeof(message)); - memcpy(message, RFIFOP(fd,8), RFIFOL(fd,4)); - message[sizeof(message)-1] = '\0'; - remove_control_chars((unsigned char *)message); - if (RFIFOW(fd,2) == 0) - login_log("'ladmin': Receiving a message for broadcast (message (in yellow): %s, ip: %s)" RETCODE, - message, ip); - else - login_log("'ladmin': Receiving a message for broadcast (message (in blue): %s, ip: %s)" RETCODE, - message, ip); - // send same message to all char-servers (no answer) - memcpy(WBUFP(buf,0), RFIFOP(fd,0), 8 + RFIFOL(fd,4)); - WBUFW(buf,0) = 0x2726; - charif_sendallwos(-1, buf, 8 + RFIFOL(fd,4)); - } - } - WFIFOSET(fd,4); - RFIFOSKIP(fd,8 + RFIFOL(fd,4)); - break; - - case 0x7950: // Request to change the validity limite (timestamp) (relative change) - if (RFIFOREST(fd) < 38) - return 0; - { - time_t timestamp; - struct tm *tmtime; - char tmpstr[2048]; - char tmpstr2[2048]; - WFIFOW(fd,0) = 0x7951; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - i = search_account_index(account_name); - if (i != -1) { - WFIFOL(fd,2) = auth_dat[i].account_id; - memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - timestamp = auth_dat[i].connect_until_time; - if (add_to_unlimited_account == 0 && timestamp == 0) { - login_log("'ladmin': Attempt to adjust the validity limit of an unlimited account (account: %s, ip: %s)" RETCODE, - auth_dat[i].userid, ip); - WFIFOL(fd,30) = 0; - } else { - if (timestamp == 0 || timestamp < time(NULL)) - timestamp = time(NULL); - tmtime = localtime(×tamp); - tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,26); - tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,28); - tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,30); - tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,32); - tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,34); - tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,36); - timestamp = mktime(tmtime); - if (timestamp != -1) { - strftime(tmpstr, 24, date_format, localtime(&auth_dat[i].connect_until_time)); - strftime(tmpstr2, 24, date_format, localtime(×tamp)); - login_log("'ladmin': Adjustment of a validity limit (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)" RETCODE, - auth_dat[i].userid, auth_dat[i].connect_until_time, (auth_dat[i].connect_until_time == 0 ? "unlimited" : tmpstr), (short)RFIFOW(fd,26), (short)RFIFOW(fd,28), (short)RFIFOW(fd,30), (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), timestamp, (timestamp == 0 ? "unlimited" : tmpstr2), ip); - auth_dat[i].connect_until_time = timestamp; - mmo_auth_sync(); - WFIFOL(fd,30) = (unsigned long)auth_dat[i].connect_until_time; - } else { - strftime(tmpstr, 24, date_format, localtime(&auth_dat[i].connect_until_time)); - login_log("'ladmin': Impossible to adjust a validity limit (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)" RETCODE, - auth_dat[i].userid, auth_dat[i].connect_until_time, (auth_dat[i].connect_until_time == 0 ? "unlimited" : tmpstr), (short)RFIFOW(fd,26), (short)RFIFOW(fd,28), (short)RFIFOW(fd,30), (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), ip); - WFIFOL(fd,30) = 0; - } - } - } else { - memcpy(WFIFOP(fd,6), account_name, 24); - login_log("'ladmin': Attempt to adjust the validity limit of an unknown account (account: %s, ip: %s)" RETCODE, - account_name, ip); - WFIFOL(fd,30) = 0; - } - } - WFIFOSET(fd,34); - RFIFOSKIP(fd,38); - break; - - case 0x7952: // Request about informations of an account (by account name) - if (RFIFOREST(fd) < 26) - return 0; - WFIFOW(fd,0) = 0x7953; - WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? - account_name = (char*)RFIFOP(fd,2); - account_name[23] = '\0'; - remove_control_chars((unsigned char *)account_name); - i = search_account_index(account_name); - if (i != -1) { - WFIFOL(fd,2) = auth_dat[i].account_id; - WFIFOB(fd,6) = (unsigned char)isGM(auth_dat[i].account_id); - memcpy(WFIFOP(fd,7), auth_dat[i].userid, 24); - WFIFOB(fd,31) = auth_dat[i].sex; - WFIFOL(fd,32) = auth_dat[i].logincount; - WFIFOL(fd,36) = auth_dat[i].state; - memcpy(WFIFOP(fd,40), auth_dat[i].error_message, 20); - memcpy(WFIFOP(fd,60), auth_dat[i].lastlogin, 24); - memcpy(WFIFOP(fd,84), auth_dat[i].last_ip, 16); - memcpy(WFIFOP(fd,100), auth_dat[i].email, 40); - WFIFOL(fd,140) = (unsigned long)auth_dat[i].connect_until_time; - WFIFOL(fd,144) = (unsigned long)auth_dat[i].ban_until_time; - WFIFOW(fd,148) = strlen(auth_dat[i].memo); - if (auth_dat[i].memo[0]) { - memcpy(WFIFOP(fd,150), auth_dat[i].memo, strlen(auth_dat[i].memo)); - } - login_log("'ladmin': Sending information of an account (request by the name; account: %s, id: %d, ip: %s)" RETCODE, - auth_dat[i].userid, auth_dat[i].account_id, ip); - WFIFOSET(fd,150+strlen(auth_dat[i].memo)); - } else { - memcpy(WFIFOP(fd,7), account_name, 24); - WFIFOW(fd,148) = 0; - login_log("'ladmin': Attempt to obtain information (by the name) of an unknown account (account: %s, ip: %s)" RETCODE, - account_name, ip); - WFIFOSET(fd,150); - } - RFIFOSKIP(fd,26); - break; - - case 0x7954: // Request about information of an account (by account id) - if (RFIFOREST(fd) < 6) - return 0; - WFIFOW(fd,0) = 0x7953; - WFIFOL(fd,2) = RFIFOL(fd,2); - memset(WFIFOP(fd,7), '\0', 24); - for(i = 0; i < auth_num; i++) { - if (auth_dat[i].account_id == RFIFOL(fd,2)) { - login_log("'ladmin': Sending information of an account (request by the id; account: %s, id: %d, ip: %s)" RETCODE, - auth_dat[i].userid, RFIFOL(fd,2), ip); - WFIFOB(fd,6) = (unsigned char)isGM(auth_dat[i].account_id); - memcpy(WFIFOP(fd,7), auth_dat[i].userid, 24); - WFIFOB(fd,31) = auth_dat[i].sex; - WFIFOL(fd,32) = auth_dat[i].logincount; - WFIFOL(fd,36) = auth_dat[i].state; - memcpy(WFIFOP(fd,40), auth_dat[i].error_message, 20); - memcpy(WFIFOP(fd,60), auth_dat[i].lastlogin, 24); - memcpy(WFIFOP(fd,84), auth_dat[i].last_ip, 16); - memcpy(WFIFOP(fd,100), auth_dat[i].email, 40); - WFIFOL(fd,140) = (unsigned long)auth_dat[i].connect_until_time; - WFIFOL(fd,144) = (unsigned long)auth_dat[i].ban_until_time; - WFIFOW(fd,148) = strlen(auth_dat[i].memo); - if (auth_dat[i].memo[0]) { - memcpy(WFIFOP(fd,150), auth_dat[i].memo, strlen(auth_dat[i].memo)); - } - WFIFOSET(fd,150+strlen(auth_dat[i].memo)); - break; - } - } - if (i == auth_num) { - login_log("'ladmin': Attempt to obtain information (by the id) of an unknown account (id: %d, ip: %s)" RETCODE, - RFIFOL(fd,2), ip); - strncpy((char*)WFIFOP(fd,7), "", 24); - WFIFOW(fd,148) = 0; - WFIFOSET(fd,150); - } - RFIFOSKIP(fd,6); - break; - - case 0x7955: // Request to reload GM file (no answer) - login_log("'ladmin': Request to re-load GM configuration file (ip: %s)." RETCODE, ip); - read_gm_account(); - // send GM accounts to all char-servers - send_GM_accounts(); - RFIFOSKIP(fd,2); - break; - - default: - { - FILE *logfp; - char tmpstr[24]; - time_t raw_time; - logfp = fopen(login_log_unknown_packets_filename, "a"); - if (logfp) { - time(&raw_time); - strftime(tmpstr, 23, date_format, localtime(&raw_time)); - fprintf(logfp, "%s: receiving of an unknown packet -> disconnection" RETCODE, tmpstr); - fprintf(logfp, "parse_admin: connection #%d (ip: %s), packet: 0x%x (with being read: %d)." RETCODE, fd, ip, RFIFOW(fd,0), RFIFOREST(fd)); - fprintf(logfp, "Detail (in hex):" RETCODE); - fprintf(logfp, "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F" RETCODE); - memset(tmpstr, '\0', sizeof(tmpstr)); - for(i = 0; i < RFIFOREST(fd); i++) { - if ((i & 15) == 0) - fprintf(logfp, "%04X ",i); - fprintf(logfp, "%02x ", RFIFOB(fd,i)); - if (RFIFOB(fd,i) > 0x1f) - tmpstr[i % 16] = RFIFOB(fd,i); - else - tmpstr[i % 16] = '.'; - if ((i - 7) % 16 == 0) // -8 + 1 - fprintf(logfp, " "); - else if ((i + 1) % 16 == 0) { - fprintf(logfp, " %s" RETCODE, tmpstr); - memset(tmpstr, '\0', sizeof(tmpstr)); - } - } - if (i % 16 != 0) { - for(j = i; j % 16 != 0; j++) { - fprintf(logfp, " "); - if ((j - 7) % 16 == 0) // -8 + 1 - fprintf(logfp, " "); - } - fprintf(logfp, " %s" RETCODE, tmpstr); - } - fprintf(logfp, RETCODE); - fclose(logfp); - } - } - login_log("'ladmin': End of connection, unknown packet (ip: %s)" RETCODE, ip); - session[fd]->eof = 1; - ShowWarning("Remote administration has been disconnected (unknown packet).\n"); - return 0; - } - //WFIFOW(fd,0) = 0x791f; - //WFIFOSET(fd,2); - } - RFIFOSKIP(fd,RFIFOREST(fd)); - return 0; -} - -//-------------------------------------------- -// Test to know if an IP come from LAN or WAN. -// Rewrote: Adnvanced subnet check [LuzZza] -//-------------------------------------------- -int lan_subnetcheck(long *p) { - - int i; - unsigned char *sbn, *msk, *src = (unsigned char *)p; - - for(i=0; iclient_addr.sin_addr; - char ip[16]; - long subnet_char_ip; - - RFIFOHEAD(fd); - - sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - - memset(&account, 0, sizeof(account)); - - if (session[fd]->eof) { - do_close(fd); - return 0; - } - - while(RFIFOREST(fd) >= 2) { - if (display_parse_login == 1) { - if (RFIFOW(fd,0) == 0x64 || RFIFOW(fd,0) == 0x01dd) { - if ((int)RFIFOREST(fd) >= ((RFIFOW(fd,0) == 0x64) ? 55 : 47)) - ShowDebug("parse_login: connection #%d, packet: 0x%x (with being read: %d), account: %s.\n", fd, RFIFOW(fd,0), RFIFOREST(fd), RFIFOP(fd,6)); - } else if (RFIFOW(fd,0) == 0x2710) { - if (RFIFOREST(fd) >= 86) - ShowDebug("parse_login: connection #%d, packet: 0x%x (with being read: %d), server: %s.\n", fd, RFIFOW(fd,0), RFIFOREST(fd), RFIFOP(fd,60)); - } else - ShowDebug("parse_login: connection #%d, packet: 0x%x (with being read: %d).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); - } - - switch(RFIFOW(fd,0)) { - case 0x200: // New alive packet: structure: 0x200 .24B. used to verify if client is always alive. - if (RFIFOREST(fd) < 26) - return 0; - RFIFOSKIP(fd,26); - break; - - case 0x204: // New alive packet: structure: 0x204 .16B. (new ragexe from 22 june 2004) - if (RFIFOREST(fd) < 18) - return 0; - RFIFOSKIP(fd,18); - break; - - case 0x277: // New login packet - case 0x64: // request client login - case 0x01dd: // request client login with encrypt - { - int packet_len = RFIFOREST(fd); - - switch(RFIFOW(fd, 0)){ - case 0x64: - if(packet_len < 55) - return 0; - break; - case 0x01dd: - if(packet_len < 47) - return 0; - break; - case 0x277: - if(packet_len < 84) - return 0; - break; - } - - account.version = RFIFOL(fd, 2); //for exe version check [Sirius] - if (!account.version) account.version = 1; //Force some version... - memcpy(account.userid,RFIFOP(fd,6),NAME_LENGTH); - account.userid[23] = '\0'; - remove_control_chars((unsigned char *)account.userid); - if (RFIFOW(fd,0) != 0x01dd) { - login_log("Request for connection (non encryption mode) of %s (ip: %s)." RETCODE, account.userid, ip); - memcpy(account.passwd, RFIFOP(fd,30), NAME_LENGTH); - account.passwd[23] = '\0'; - remove_control_chars((unsigned char *)account.passwd); - } else { - login_log("Request for connection (encryption mode) of %s (ip: %s)." RETCODE, account.userid, ip); - // If remove control characters from received password encrypted by md5, - // there would be a wrong result and failed to authentication. [End_of_exam] - memcpy(account.passwd, RFIFOP(fd,30), 16); - account.passwd[16] = '\0'; - } -#ifdef PASSWORDENC - account.passwdenc = (RFIFOW(fd,0) != 0x01dd) ? 0 : PASSWORDENC; -#else - account.passwdenc = 0; -#endif - - if (!check_ip(session[fd]->client_addr.sin_addr.s_addr)) { - login_log("Connection refused: IP isn't authorised (deny/allow, ip: %s)." RETCODE, ip); - WFIFOHEAD(fd, 23); - WFIFOW(fd,0) = 0x6a; - WFIFOB(fd,2) = 3; // 3 = Rejected from Server - WFIFOSET(fd,23); - RFIFOSKIP(fd,packet_len); - break; - } - - result = mmo_auth(&account, fd); - if (result == -1) { - int gm_level = isGM(account.account_id); - if (min_level_to_connect > gm_level) { - login_log("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d, ip: %s)." RETCODE, - min_level_to_connect, account.userid, gm_level, ip); - WFIFOHEAD(fd, 3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - } else { - if (gm_level) - ShowInfo("Connection of the GM (level:%d) account '%s' accepted.\n", gm_level, account.userid); - else - ShowInfo("Connection of the account '%s' accepted.\n", account.userid); - server_num = 0; - WFIFOHEAD(fd, 47+32*MAX_SERVERS); - for(i = 0; i < MAX_SERVERS; i++) { - if (server_fd[i] >= 0) { - // Andvanced subnet check [LuzZza] - if((subnet_char_ip = lan_subnetcheck((long*)p))) - WFIFOL(fd,47+server_num*32) = subnet_char_ip; - else - WFIFOL(fd,47+server_num*32) = server[i].ip; - WFIFOW(fd,47+server_num*32+4) = server[i].port; - memcpy(WFIFOP(fd,47+server_num*32+6), server[i].name, 20); - WFIFOW(fd,47+server_num*32+26) = server[i].users; - WFIFOW(fd,47+server_num*32+28) = server[i].maintenance; - WFIFOW(fd,47+server_num*32+30) = server[i].new_; - server_num++; - } - } - // if at least 1 char-server - if (server_num > 0) { - WFIFOW(fd,0) = 0x69; - WFIFOW(fd,2) = 47+32*server_num; - WFIFOL(fd,4) = account.login_id1; - WFIFOL(fd,8) = account.account_id; - WFIFOL(fd,12) = account.login_id2; - WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used) - memcpy(WFIFOP(fd,20), account.lastlogin, 24); // in old version, that was for name (not more used) - WFIFOB(fd,46) = account.sex; - WFIFOSET(fd,47+32*server_num); - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - auth_fifo[auth_fifo_pos].account_id = account.account_id; - auth_fifo[auth_fifo_pos].login_id1 = account.login_id1; - auth_fifo[auth_fifo_pos].login_id2 = account.login_id2; - auth_fifo[auth_fifo_pos].sex = account.sex; - auth_fifo[auth_fifo_pos].delflag = 0; - auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr.sin_addr.s_addr; - auth_fifo_pos++; - // if no char-server, don't send void list of servers, just disconnect the player with proper message - } else { - login_log("Connection refused: there is no char-server online (account: %s, ip: %s)." RETCODE, - account.userid, ip); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - } - } - } else { - WFIFOHEAD(fd, 23); - memset(WFIFOP(fd,0), '\0', 23); - WFIFOW(fd,0) = 0x6a; - WFIFOB(fd,2) = result; - if (result == 6) { // 6 = Your are Prohibited to log in until %s - i = search_account_index(account.userid); - if (i != -1) { - if (auth_dat[i].ban_until_time != 0) { // if account is banned, we send ban timestamp - char tmpstr[256]; - strftime(tmpstr, 20, date_format, localtime(&auth_dat[i].ban_until_time)); - tmpstr[19] = '\0'; - memcpy(WFIFOP(fd,3), tmpstr, 20); - } else { // we send error message - memcpy(WFIFOP(fd,3), auth_dat[i].error_message, 20); - } - } - } - WFIFOSET(fd,23); - } - RFIFOSKIP(fd,packet_len); - break; - } - case 0x01db: // Sending request of the coding key - case 0x791a: // Sending request of the coding key (administration packet) - { - struct login_session_data *ld; - if (session[fd]->session_data) { - ShowWarning("login: abnormal request of MD5 key (already opened session).\n"); - session[fd]->eof = 1; - return 0; - } - ld = (struct login_session_data*)aCalloc(1, sizeof(struct login_session_data)); - session[fd]->session_data = ld; - if (!ld) { - ShowFatalError("login: Request for md5 key: memory allocation failure (malloc)!\n"); - session[fd]->eof = 1; - return 0; - } - if (RFIFOW(fd,0) == 0x01db) - login_log("Sending request of the coding key (ip: %s)" RETCODE, ip); - else - login_log("'ladmin': Sending request of the coding key (ip: %s)" RETCODE, ip); - // Creation of the coding key - memset(ld->md5key, '\0', sizeof(ld->md5key)); - ld->md5keylen = rand() % 4 + 12; - for(i = 0; i < ld->md5keylen; i++) - ld->md5key[i] = rand() % 255 + 1; - RFIFOSKIP(fd,2); - WFIFOHEAD(fd, 4 + ld->md5keylen); - WFIFOW(fd,0) = 0x01dc; - WFIFOW(fd,2) = 4 + ld->md5keylen; - memcpy(WFIFOP(fd,4), ld->md5key, ld->md5keylen); - WFIFOSET(fd,WFIFOW(fd,2)); - } - break; - - case 0x2710: // Connection request of a char-server - if (RFIFOREST(fd) < 86) - return 0; - { - int GM_value, len; - char* server_name; - WFIFOHEAD(fd, 3); - memcpy(account.userid,RFIFOP(fd,2),NAME_LENGTH); - account.userid[23] = '\0'; - remove_control_chars((unsigned char *)account.userid); - memcpy(account.passwd, RFIFOP(fd,26), NAME_LENGTH); - account.passwd[23] = '\0'; - remove_control_chars((unsigned char *)account.passwd); - account.passwdenc = 0; - server_name = (char*)RFIFOP(fd,60); - server_name[20] = '\0'; - remove_control_chars((unsigned char *)server_name); - login_log("Connection request of the char-server '%s' @ %d.%d.%d.%d:%d (ip: %s)" RETCODE, - server_name, RFIFOB(fd,54), RFIFOB(fd,55), RFIFOB(fd,56), RFIFOB(fd,57), RFIFOW(fd,58), ip); - result = mmo_auth(&account, fd); - if (result == -1 && account.sex == 2 && account.account_id < MAX_SERVERS && server_fd[account.account_id] == -1) { - login_log("Connection of the char-server '%s' accepted (account: %s, pass: %s, ip: %s)" RETCODE, - server_name, account.userid, account.passwd, ip); - ShowStatus("Connection of the char-server '%s' accepted.\n", server_name); - memset(&server[account.account_id], 0, sizeof(struct mmo_char_server)); - server[account.account_id].ip = RFIFOL(fd,54); - server[account.account_id].port = RFIFOW(fd,58); - memcpy(server[account.account_id].name, server_name, 20); - server[account.account_id].users = 0; - server[account.account_id].maintenance = RFIFOW(fd,82); - server[account.account_id].new_ = RFIFOW(fd,84); - server_fd[account.account_id] = fd; - WFIFOW(fd,0) = 0x2711; - WFIFOB(fd,2) = 0; - WFIFOSET(fd,3); - session[fd]->func_parse = parse_fromchar; - realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); - // send GM account to char-server - len = 4; - WFIFOW(fd,0) = 0x2732; - for(i = 0; i < auth_num; i++) - // send only existing accounts. We can not create a GM account when server is online. - if ((GM_value = isGM(auth_dat[i].account_id)) > 0) { - WFIFOL(fd,len) = auth_dat[i].account_id; - WFIFOB(fd,len+4) = (unsigned char)GM_value; - len += 5; - } - WFIFOW(fd,2) = len; - WFIFOSET(fd,len); - } else { - if (server_fd[account.account_id] != -1) { - ShowNotice("Connection of the char-server '%s' REFUSED - already connected (account: %ld-%s, pass: %s, ip: %s)\n", - server_name, account.account_id, account.userid, account.passwd, ip); - login_log("Connexion of the char-server '%s' REFUSED - already connected (account: %ld-%s, pass: %s, ip: %s)" RETCODE, - server_name, account.account_id, account.userid, account.passwd, ip); - } else { - ShowNotice("Connection of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s).\n", server_name, account.userid, account.passwd, ip); - login_log("Connexion of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s)" RETCODE, - server_name, account.userid, account.passwd, ip); - } - WFIFOHEAD(fd, 3); - WFIFOW(fd,0) = 0x2711; - WFIFOB(fd,2) = 3; - WFIFOSET(fd,3); - } - } - RFIFOSKIP(fd,86); - return 0; - - case 0x7530: // Request of the server version - login_log("Sending of the server version (ip: %s)" RETCODE, ip); - WFIFOHEAD(fd, 10); - WFIFOW(fd,0) = 0x7531; - WFIFOB(fd,2) = ATHENA_MAJOR_VERSION; - WFIFOB(fd,3) = ATHENA_MINOR_VERSION; - WFIFOB(fd,4) = ATHENA_REVISION; - WFIFOB(fd,5) = ATHENA_RELEASE_FLAG; - WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG; - WFIFOB(fd,7) = ATHENA_SERVER_LOGIN; - WFIFOW(fd,8) = ATHENA_MOD_VERSION; - WFIFOSET(fd,10); - RFIFOSKIP(fd,2); - break; - - case 0x7532: // Request to end connection - login_log("End of connection (ip: %s)" RETCODE, ip); - session[fd]->eof = 1; - return 0; - - case 0x7918: // Request for administation login - if ((int)RFIFOREST(fd) < 4 || (int)RFIFOREST(fd) < ((RFIFOW(fd,2) == 0) ? 28 : 20)) - return 0; - WFIFOW(fd,0) = 0x7919; - WFIFOB(fd,2) = 1; - if (!check_ladminip(session[fd]->client_addr.sin_addr.s_addr)) { - login_log("'ladmin'-login: Connection in administration mode refused: IP isn't authorised (ladmin_allow, ip: %s)." RETCODE, ip); - } else { - struct login_session_data *ld = (struct login_session_data*)session[fd]->session_data; - if (RFIFOW(fd,2) == 0) { // non encrypted password - char password[25]; - memcpy(password, RFIFOP(fd,4), 24); - password[24] = '\0'; - remove_control_chars((unsigned char *)password); - // If remote administration is enabled and password sent by client matches password read from login server configuration file - if ((admin_state == 1) && (strcmp(password, admin_pass) == 0)) { - login_log("'ladmin'-login: Connection in administration mode accepted (non encrypted password: %s, ip: %s)" RETCODE, password, ip); - ShowNotice("Connection of a remote administration accepted (non encrypted password).\n"); - WFIFOB(fd,2) = 0; - session[fd]->func_parse = parse_admin; - } else if (admin_state != 1) - login_log("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (non encrypted password: %s, ip: %s)" RETCODE, password, ip); - else - login_log("'ladmin'-login: Connection in administration mode REFUSED - invalid password (non encrypted password: %s, ip: %s)" RETCODE, password, ip); - } else { // encrypted password - if (!ld) - ShowError("'ladmin'-login: error! MD5 key not created/requested for an administration login.\n"); - else { - char md5str[64] = "", md5bin[32]; - if (RFIFOW(fd,2) == 1) { - sprintf(md5str, "%s%s", ld->md5key, admin_pass); // 20 24 - } else if (RFIFOW(fd,2) == 2) { - sprintf(md5str, "%s%s", admin_pass, ld->md5key); // 24 20 - } - MD5_String2binary(md5str, md5bin); - // If remote administration is enabled and password hash sent by client matches hash of password read from login server configuration file - if ((admin_state == 1) && (memcmp(md5bin, RFIFOP(fd,4), 16) == 0)) { - login_log("'ladmin'-login: Connection in administration mode accepted (encrypted password, ip: %s)" RETCODE, ip); - ShowNotice("Connection of a remote administration accepted (encrypted password).\n"); - WFIFOB(fd,2) = 0; - session[fd]->func_parse = parse_admin; - } else if (admin_state != 1) - login_log("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (encrypted password, ip: %s)" RETCODE, ip); - else - login_log("'ladmin'-login: Connection in administration mode REFUSED - invalid password (encrypted password, ip: %s)" RETCODE, ip); - } - } - } - WFIFOSET(fd,3); - RFIFOSKIP(fd, (RFIFOW(fd,2) == 0) ? 28 : 20); - break; - - default: - if (save_unknown_packets) { - FILE *logfp; - char tmpstr[24]; - time_t raw_time; - logfp = fopen(login_log_unknown_packets_filename, "a"); - if (logfp) { - time(&raw_time); - strftime(tmpstr, 23, date_format, localtime(&raw_time)); - fprintf(logfp, "%s: receiving of an unknown packet -> disconnection" RETCODE, tmpstr); - fprintf(logfp, "parse_login: connection #%d (ip: %s), packet: 0x%x (with being read: %d)." RETCODE, fd, ip, RFIFOW(fd,0), RFIFOREST(fd)); - fprintf(logfp, "Detail (in hex):" RETCODE); - fprintf(logfp, "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F" RETCODE); - memset(tmpstr, '\0', sizeof(tmpstr)); - for(i = 0; i < RFIFOREST(fd); i++) { - if ((i & 15) == 0) - fprintf(logfp, "%04X ",i); - fprintf(logfp, "%02x ", RFIFOB(fd,i)); - if (RFIFOB(fd,i) > 0x1f) - tmpstr[i % 16] = RFIFOB(fd,i); - else - tmpstr[i % 16] = '.'; - if ((i - 7) % 16 == 0) // -8 + 1 - fprintf(logfp, " "); - else if ((i + 1) % 16 == 0) { - fprintf(logfp, " %s" RETCODE, tmpstr); - memset(tmpstr, '\0', sizeof(tmpstr)); - } - } - if (i % 16 != 0) { - for(j = i; j % 16 != 0; j++) { - fprintf(logfp, " "); - if ((j - 7) % 16 == 0) // -8 + 1 - fprintf(logfp, " "); - } - fprintf(logfp, " %s" RETCODE, tmpstr); - } - fprintf(logfp, RETCODE); - fclose(logfp); - } - } - login_log("End of connection, unknown packet (ip: %s)" RETCODE, ip); - session[fd]->eof = 1; - return 0; - } - } - RFIFOSKIP(fd,RFIFOREST(fd)); - return 0; -} - -//----------------------- -// Console Command Parser [Wizputer] -//----------------------- -int parse_console(char *buf) { - char command[256]; - - memset(command,0,sizeof(command)); - - sscanf(buf, "%[^\n]", command); - - login_log("Console command :%s" RETCODE, command); - - if(strcmpi("shutdown", command) == 0 || - strcmpi("exit", command) == 0 || - strcmpi("quit", command) == 0 || - strcmpi("end", command) == 0) - runflag = 0; - else if(strcmpi("alive", command) == 0 || - strcmpi("status", command) == 0) - ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n"); - else if(strcmpi("help", command) == 0) { - printf(CL_BOLD"Help of commands:"CL_RESET"\n"); - printf(" To shutdown the server:\n"); - printf(" 'shutdown|exit|qui|end'\n"); - printf(" To know if server is alive:\n"); - printf(" 'alive|status'\n"); - } - - return 0; -} - -static int online_data_cleanup_sub(DBKey key, void *data, va_list ap) -{ - struct online_login_data *character= (struct online_login_data*)data; - if (character->char_server == -2) //Unknown server.. set them offline - remove_online_user(character->account_id); - else if (character->char_server < 0) - //Free data from players that have not been online for a while. - db_remove(online_db, key); - return 0; -} - -static int online_data_cleanup(int tid, unsigned int tick, int id, int data) -{ - online_db->foreach(online_db, online_data_cleanup_sub); - return 0; -} -//------------------------------------------------- -// Return numerical value of a switch configuration -// on/off, english, fran軋is, deutsch, espaol -//------------------------------------------------- -int config_switch(const char *str) { - if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) - return 1; - if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) - return 0; - - return atoi(str); -} - -//---------------------------------- -// Reading Lan Support configuration -// Rewrote: Anvanced subnet check [LuzZza] -//---------------------------------- -int login_lan_config_read(const char *lancfgName) { - - FILE *fp; - int line_num = 0; - char line[1024], w1[64], w2[64], w3[64], w4[64]; - - if((fp = fopen(lancfgName, "r")) == NULL) { - ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName); - return 1; - } - - ShowInfo("Reading the configuration file %s...\n", lancfgName); - - while(fgets(line, sizeof(line)-1, fp)) { - - line_num++; - if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n') - continue; - - line[sizeof(line)-1] = '\0'; - if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4) { - - ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num); - continue; - } - - remove_control_chars((unsigned char *)w1); - remove_control_chars((unsigned char *)w2); - remove_control_chars((unsigned char *)w3); - remove_control_chars((unsigned char *)w4); - - if(strcmpi(w1, "subnet") == 0) { - - subnet[subnet_count].mask = inet_addr(w2); - subnet[subnet_count].char_ip = inet_addr(w3); - subnet[subnet_count].map_ip = inet_addr(w4); - subnet[subnet_count].subnet = subnet[subnet_count].char_ip&subnet[subnet_count].mask; - if (subnet[subnet_count].subnet != (subnet[subnet_count].map_ip&subnet[subnet_count].mask)) { - ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4); - continue; - } - - subnet_count++; - } - - ShowStatus("Read information about %d subnetworks.\n", subnet_count); - } - - fclose(fp); - return 0; -} - -//----------------------------------- -// Reading general configuration file -//----------------------------------- -int login_config_read(const char *cfgName) { - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - if ((fp = fopen(cfgName, "r")) == NULL) { - ShowError("Configuration file (%s) not found.\n", cfgName); - return 1; - } - - ShowInfo("Reading configuration file %s...\n", cfgName); - while(fgets(line, sizeof(line)-1, fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - - line[sizeof(line)-1] = '\0'; - memset(w2, 0, sizeof(w2)); - if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { - remove_control_chars((unsigned char *)w1); - remove_control_chars((unsigned char *)w2); - - if(strcmpi(w1,"timestamp_format") == 0) { - strncpy(timestamp_format, w2, 20); - } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ - stdout_with_ansisequence = config_switch(w2); - } else if(strcmpi(w1,"console_silent")==0){ - msg_silent = 0; //To always allow the next line to show up. - ShowInfo("Console Silent Setting: %d\n", atoi(w2)); - msg_silent = atoi(w2); - } else if (strcmpi(w1, "admin_state") == 0) { - admin_state = config_switch(w2); - } else if (strcmpi(w1, "admin_pass") == 0) { - memset(admin_pass, 0, sizeof(admin_pass)); - strncpy(admin_pass, w2, sizeof(admin_pass)); - admin_pass[sizeof(admin_pass)-1] = '\0'; - } else if (strcmpi(w1, "ladminallowip") == 0) { - if (strcmpi(w2, "clear") == 0) { - if (access_ladmin_allow) - aFree(access_ladmin_allow); - access_ladmin_allow = NULL; - access_ladmin_allownum = 0; - } else { - if (strcmpi(w2, "all") == 0) { - // reset all previous values - if (access_ladmin_allow) - aFree(access_ladmin_allow); - // set to all - access_ladmin_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); - access_ladmin_allownum = 1; - access_ladmin_allow[0] = '\0'; - } else if (w2[0] && !(access_ladmin_allownum == 1 && access_ladmin_allow[0] == '\0')) { // don't add IP if already 'all' - if (access_ladmin_allow) - access_ladmin_allow = (char*)aRealloc(access_ladmin_allow, (access_ladmin_allownum+1) * ACO_STRSIZE); - else - access_ladmin_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); - strncpy(access_ladmin_allow + (access_ladmin_allownum++) * ACO_STRSIZE, w2, ACO_STRSIZE); - access_ladmin_allow[access_ladmin_allownum * ACO_STRSIZE - 1] = '\0'; - } - } - } else if (strcmpi(w1, "gm_pass") == 0) { - memset(gm_pass, 0, sizeof(gm_pass)); - strncpy(gm_pass, w2, sizeof(gm_pass)); - gm_pass[sizeof(gm_pass)-1] = '\0'; - } else if (strcmpi(w1, "level_new_gm") == 0) { - level_new_gm = atoi(w2); - } else if (strcmpi(w1, "new_account") == 0) { - new_account_flag = config_switch(w2); - } else if (strcmpi(w1, "bind_ip") == 0) { - bind_ip = resolve_hostbyname(w2, NULL, bind_ip_str); - if (bind_ip) - ShowStatus("Login server binding IP address : %s -> %s\n", w2, bind_ip_str); - } else if (strcmpi(w1, "login_port") == 0) { - login_port = atoi(w2); - } else if (strcmpi(w1, "account_filename") == 0) { - memset(account_filename, 0, sizeof(account_filename)); - strncpy(account_filename, w2, sizeof(account_filename)); - account_filename[sizeof(account_filename)-1] = '\0'; - } else if (strcmpi(w1, "gm_account_filename") == 0) { - memset(GM_account_filename, 0, sizeof(GM_account_filename)); - strncpy(GM_account_filename, w2, sizeof(GM_account_filename)); - GM_account_filename[sizeof(GM_account_filename)-1] = '\0'; - } else if (strcmpi(w1, "gm_account_filename_check_timer") == 0) { - gm_account_filename_check_timer = atoi(w2); - } else if (strcmpi(w1, "use_MD5_passwords") == 0) { - use_md5_passwds = config_switch(w2); - } else if (strcmpi(w1, "login_log_filename") == 0) { - memset(login_log_filename, 0, sizeof(login_log_filename)); - strncpy(login_log_filename, w2, sizeof(login_log_filename)); - login_log_filename[sizeof(login_log_filename)-1] = '\0'; - } else if (strcmpi(w1, "log_login") == 0) { - log_login = atoi(w2); - } else if (strcmpi(w1, "login_log_unknown_packets_filename") == 0) { - memset(login_log_unknown_packets_filename, 0, sizeof(login_log_unknown_packets_filename)); - strncpy(login_log_unknown_packets_filename, w2, sizeof(login_log_unknown_packets_filename)); - login_log_unknown_packets_filename[sizeof(login_log_unknown_packets_filename)-1] = '\0'; - } else if (strcmpi(w1, "save_unknown_packets") == 0) { - save_unknown_packets = config_switch(w2); - } else if (strcmpi(w1, "display_parse_login") == 0) { - display_parse_login = config_switch(w2); // 0: no, 1: yes - } else if (strcmpi(w1, "display_parse_admin") == 0) { - display_parse_admin = config_switch(w2); // 0: no, 1: yes - } else if (strcmpi(w1, "display_parse_fromchar") == 0) { - display_parse_fromchar = config_switch(w2); // 0: no, 1: yes (without packet 0x2714), 2: all packets - } else if (strcmpi(w1, "date_format") == 0) { // note: never have more than 19 char for the date! - memset(date_format, 0, sizeof(date_format)); - switch (atoi(w2)) { - case 0: - strcpy(date_format, "%d-%m-%Y %H:%M:%S"); // 31-12-2004 23:59:59 - break; - case 1: - strcpy(date_format, "%m-%d-%Y %H:%M:%S"); // 12-31-2004 23:59:59 - break; - case 2: - strcpy(date_format, "%Y-%d-%m %H:%M:%S"); // 2004-31-12 23:59:59 - break; - case 3: - strcpy(date_format, "%Y-%m-%d %H:%M:%S"); // 2004-12-31 23:59:59 - break; - } - } else if (strcmpi(w1, "min_level_to_connect") == 0) { - min_level_to_connect = atoi(w2); - } else if (strcmpi(w1, "add_to_unlimited_account") == 0) { - add_to_unlimited_account = config_switch(w2); - } else if (strcmpi(w1, "start_limited_time") == 0) { - start_limited_time = atoi(w2); - } else if (strcmpi(w1, "check_ip_flag") == 0) { - check_ip_flag = config_switch(w2); - } else if (strcmpi(w1, "order") == 0) { - access_order = atoi(w2); - if (strcmpi(w2, "deny,allow") == 0 || - strcmpi(w2, "deny, allow") == 0) access_order = ACO_DENY_ALLOW; - if (strcmpi(w2, "allow,deny") == 0 || - strcmpi(w2, "allow, deny") == 0) access_order = ACO_ALLOW_DENY; - if (strcmpi(w2, "mutual-failture") == 0 || - strcmpi(w2, "mutual-failure") == 0) access_order = ACO_MUTUAL_FAILTURE; - } else if (strcmpi(w1, "allow") == 0) { - if (strcmpi(w2, "clear") == 0) { - if (access_allow) - aFree(access_allow); - access_allow = NULL; - access_allownum = 0; - } else { - if (strcmpi(w2, "all") == 0) { - // reset all previous values - if (access_allow) - aFree(access_allow); - // set to all - access_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); - access_allownum = 1; - access_allow[0] = '\0'; - } else if (w2[0] && !(access_allownum == 1 && access_allow[0] == '\0')) { // don't add IP if already 'all' - if (access_allow) - access_allow = (char*)aRealloc(access_allow, (access_allownum+1) * ACO_STRSIZE); - else - access_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); - strncpy(access_allow + (access_allownum++) * ACO_STRSIZE, w2, ACO_STRSIZE); - access_allow[access_allownum * ACO_STRSIZE - 1] = '\0'; - } - } - } else if (strcmpi(w1, "deny") == 0) { - if (strcmpi(w2, "clear") == 0) { - if (access_deny) - aFree(access_deny); - access_deny = NULL; - access_denynum = 0; - } else { - if (strcmpi(w2, "all") == 0) { - // reset all previous values - if (access_deny) - aFree(access_deny); - // set to all - access_deny = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); - access_denynum = 1; - access_deny[0] = '\0'; - } else if (w2[0] && !(access_denynum == 1 && access_deny[0] == '\0')) { // don't add IP if already 'all' - if (access_deny) - access_deny = (char*)aRealloc(access_deny, (access_denynum+1) * ACO_STRSIZE); - else - access_deny = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); - strncpy(access_deny + (access_denynum++) * ACO_STRSIZE, w2, ACO_STRSIZE); - access_deny[access_denynum * ACO_STRSIZE - 1] = '\0'; - } - } - // dynamic password error ban - } else if (strcmpi(w1, "dynamic_pass_failure_ban") == 0) { - dynamic_pass_failure_ban = config_switch(w2); - } else if (strcmpi(w1, "dynamic_pass_failure_ban_time") == 0) { - dynamic_pass_failure_ban_time = atoi(w2); - } else if (strcmpi(w1, "dynamic_pass_failure_ban_how_many") == 0) { - dynamic_pass_failure_ban_how_many = atoi(w2); - } else if (strcmpi(w1, "dynamic_pass_failure_ban_how_long") == 0) { - dynamic_pass_failure_ban_how_long = atoi(w2); - } else if(strcmpi(w1, "check_client_version") == 0){ //Added by Sirius for client version check - if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ){ - check_client_version = 1; - } - if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ){ - check_client_version = 0; - } - }else if(strcmpi(w1, "client_version_to_connect") == 0){ //Added by Sirius for client version check - client_version_to_connect = atoi(w2); //Added by Sirius for client version check - } else if (strcmpi(w1, "console") == 0) { - if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) - console = 1; - } else if (strcmpi(w1, "allowed_regs") == 0) { //account flood protection system [Kevin] - allowed_regs = atoi(w2); - } else if (strcmpi(w1, "time_allowed") == 0) { - time_allowed = atoi(w2); - } else if (strcmpi(w1, "online_check") == 0) { - if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) - online_check = 1; - else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ) - online_check = 0; - else - online_check = atoi(w2); - } else if (strcmpi(w1, "import") == 0) { - login_config_read(w2); - } else if(strcmpi(w1,"use_dnsbl")==0) { // [Zido] - use_dnsbl=atoi(w2); - } else if(strcmpi(w1,"dnsbl_servers")==0) { // [Zido] - strcpy(dnsbl_servs,w2); - } else if(strcmpi(w1,"ip_sync_interval")==0) { - ip_sync_interval = 1000*60*atoi(w2); //w2 comes in minutes. - } - } - } - fclose(fp); - - ShowInfo("Finished reading %s.\n", cfgName); - - return 0; -} - -//------------------------------------- -// Displaying of configuration warnings -//------------------------------------- -void display_conf_warnings(void) { - if (admin_state != 0 && admin_state != 1) { - ShowWarning("Invalid value for admin_state parameter -> setting to 0 (no remote admin).\n"); - admin_state = 0; - } - - if (admin_state == 1) { - if (admin_pass[0] == '\0') { - ShowWarning("Administrator password is void (admin_pass).\n"); - } else if (strcmp(admin_pass, "admin") == 0) { - ShowWarning("You are using the default administrator password (admin_pass).\n"); - ShowWarning(" We highly recommend that you change it.\n"); - } - } - - if (gm_pass[0] == '\0') { - ShowWarning("'To GM become' password is void (gm_pass).\n"); - ShowWarning(" We highly recommend that you set one password.\n"); - } else if (strcmp(gm_pass, "gm") == 0) { - ShowWarning("You are using the default GM password (gm_pass).\n"); - ShowWarning(" We highly recommend that you change it.\n"); - } - - if (level_new_gm < 0 || level_new_gm > 99) { - ShowWarning("Invalid value for level_new_gm parameter -> setting to 60 (default).\n"); - level_new_gm = 60; - } - - if (new_account_flag != 0 && new_account_flag != 1) { - ShowWarning("Invalid value for new_account parameter -> setting to 0 (no new account).\n"); - new_account_flag = 0; - } - - if (login_port < 1024 || login_port > 65535) { - ShowWarning("Invalid value for login_port parameter -> setting to 6900 (default).\n"); - login_port = 6900; - } - - if (gm_account_filename_check_timer < 0) { - ShowWarning("Invalid value for gm_account_filename_check_timer parameter. Setting to 15 sec (default).\n"); - gm_account_filename_check_timer = 15; - } else if (gm_account_filename_check_timer == 1) { - ShowWarning("Invalid value for gm_account_filename_check_timer parameter. Setting to 2 sec (minimum value).\n"); - gm_account_filename_check_timer = 2; - } - - if (save_unknown_packets != 0 && save_unknown_packets != 1) { - ShowWarning("Invalid value for save_unknown_packets parameter -> setting to 0-no save.\n"); - save_unknown_packets = 0; - } - - if (display_parse_login != 0 && display_parse_login != 1) { // 0: no, 1: yes - ShowWarning("Invalid value for display_parse_login parameter -> setting to 0 (no display).\n"); - display_parse_login = 0; - } - - if (display_parse_admin != 0 && display_parse_admin != 1) { // 0: no, 1: yes - ShowWarning("Invalid value for display_parse_admin parameter -> setting to 0 (no display).\n"); - display_parse_admin = 0; - } - - if (display_parse_fromchar < 0 || display_parse_fromchar > 2) { // 0: no, 1: yes (without packet 0x2714), 2: all packets - ShowWarning("Invalid value for display_parse_fromchar parameter -> setting to 0 (no display).\n"); - display_parse_fromchar = 0; - } - - if (min_level_to_connect < 0) { // 0: all players, 1-99 at least gm level x - ShowWarning("Invalid value for min_level_to_connect (%d) parameter -> setting 0 (any player).\n", min_level_to_connect); - min_level_to_connect = 0; - } else if (min_level_to_connect > 99) { // 0: all players, 1-99 at least gm level x - ShowWarning("Invalid value for min_level_to_connect (%d) parameter -> setting to 99 (only GM level 99)\n", min_level_to_connect); - min_level_to_connect = 99; - } - - if (add_to_unlimited_account != 0 && add_to_unlimited_account != 1) { // 0: no, 1: yes - ShowWarning("Invalid value for add_to_unlimited_account parameter\n"); - ShowWarning(" -> setting to 0 (impossible to add a time to an unlimited account).\n"); - add_to_unlimited_account = 0; - } - - if (start_limited_time < -1) { // -1: create unlimited account, 0 or more: additionnal sec from now to create limited time - ShowWarning("Invalid value for start_limited_time parameter\n"); - ShowWarning(" -> setting to -1 (new accounts are created with unlimited time).\n"); - start_limited_time = -1; - } - - if (check_ip_flag != 0 && check_ip_flag != 1) { // 0: no, 1: yes - ShowWarning("Invalid value for check_ip_flag parameter\n"); - ShowWarning(" -> setting to 1 (check players ip between login-server & char-server).\n"); - check_ip_flag = 1; - } - - if (access_order == ACO_DENY_ALLOW) { - if (access_denynum == 1 && access_deny[0] == '\0') { - ShowWarning("The IP security order is 'deny,allow' (allow if not deny) and you refuse ALL IP.\n"); - } - } else if (access_order == ACO_ALLOW_DENY) { - if (access_allownum == 0) { - ShowWarning("The IP security order is 'allow,deny' (deny if not allow) but, NO IP IS AUTHORISED!\n"); - } - } else { // ACO_MUTUAL_FAILTURE - if (access_allownum == 0) { - ShowWarning("The IP security order is 'mutual-failture'\n"); - ShowWarning(" (allow if in the allow list and not in the deny list).\n"); - ShowWarning(" But, NO IP IS AUTHORISED!\n"); - } else if (access_denynum == 1 && access_deny[0] == '\0') { - ShowWarning("The IP security order is mutual-failture\n"); - ShowWarning(" (allow if in the allow list and not in the deny list).\n"); - ShowWarning(" But, you refuse ALL IP!\n"); - } - } - - if (dynamic_pass_failure_ban != 0) { - if (dynamic_pass_failure_ban_time < 1) { - ShowWarning("Invalid value for dynamic_pass_failure_ban_time (%d) parameter\n", dynamic_pass_failure_ban_time); - ShowWarning(" -> setting to 5 (5 minutes to look number of invalid passwords.\n"); - dynamic_pass_failure_ban_time = 5; - } - if (dynamic_pass_failure_ban_how_many < 1) { - ShowWarning("Invalid value for dynamic_pass_failure_ban_how_many (%d) parameter\n", dynamic_pass_failure_ban_how_many); - ShowWarning(" -> setting to 3 (3 invalid passwords before to temporarily ban.\n"); - dynamic_pass_failure_ban_how_many = 3; - } - if (dynamic_pass_failure_ban_how_long < 1) { - ShowWarning("Invalid value for dynamic_pass_failure_ban_how_long (%d) parameter\n", dynamic_pass_failure_ban_how_long); - ShowWarning(" -> setting to 1 (1 minute of temporarily ban.\n"); - dynamic_pass_failure_ban_how_long = 1; - } - } - - return; -} - -//------------------------------- -// Save configuration in log file -//------------------------------- -void save_config_in_log(void) { - int i; - - // a newline in the log... - login_log(""); - login_log("The login-server starting..." RETCODE); - - // save configuration in log file - login_log("The configuration of the server is set:" RETCODE); - - if (admin_state != 1) - login_log("- with no remote administration." RETCODE); - else if (admin_pass[0] == '\0') - login_log("- with a remote administration with a VOID password." RETCODE); - else if (strcmp(admin_pass, "admin") == 0) - login_log("- with a remote administration with the DEFAULT password." RETCODE); - else - login_log("- with a remote administration with the password of %d character(s)." RETCODE, strlen(admin_pass)); - if (access_ladmin_allownum == 0 || (access_ladmin_allownum == 1 && access_ladmin_allow[0] == '\0')) { - login_log("- to accept any IP for remote administration" RETCODE); - } else { - login_log("- to accept following IP for remote administration:" RETCODE); - for(i = 0; i < access_ladmin_allownum; i++) - login_log(" %s" RETCODE, (char *)(access_ladmin_allow + i * ACO_STRSIZE)); - } - - if (gm_pass[0] == '\0') - login_log("- with a VOID 'To GM become' password (gm_pass)." RETCODE); - else if (strcmp(gm_pass, "gm") == 0) - login_log("- with the DEFAULT 'To GM become' password (gm_pass)." RETCODE); - else - login_log("- with a 'To GM become' password (gm_pass) of %d character(s)." RETCODE, strlen(gm_pass)); - if (level_new_gm == 0) - login_log("- to refuse any creation of GM with @gm." RETCODE); - else - login_log("- to create GM with level '%d' when @gm is used." RETCODE, level_new_gm); - - if (new_account_flag == 1) - login_log("- to ALLOW new users (with _F/_M)." RETCODE); - else - login_log("- to NOT ALLOW new users (with _F/_M)." RETCODE); - login_log("- with port: %d." RETCODE, login_port); - login_log("- with the accounts file name: '%s'." RETCODE, account_filename); - login_log("- with the GM accounts file name: '%s'." RETCODE, GM_account_filename); - if (gm_account_filename_check_timer == 0) - login_log("- to NOT check GM accounts file modifications." RETCODE); - else - login_log("- to check GM accounts file modifications every %d seconds." RETCODE, gm_account_filename_check_timer); - - if (use_md5_passwds == 0) - login_log("- to save password in plain text." RETCODE); - else - login_log("- to save password with MD5 encrypting." RETCODE); - - // not necessary to log the 'login_log_filename', we are inside :) - - login_log("- with the unknown packets file name: '%s'." RETCODE, login_log_unknown_packets_filename); - if (save_unknown_packets) - login_log("- to SAVE all unkown packets." RETCODE); - else - login_log("- to SAVE only unkown packets sending by a char-server or a remote administration." RETCODE); - if (display_parse_login) - login_log("- to display normal parse packets on console." RETCODE); - else - login_log("- to NOT display normal parse packets on console." RETCODE); - if (display_parse_admin) - login_log("- to display administration parse packets on console." RETCODE); - else - login_log("- to NOT display administration parse packets on console." RETCODE); - if (display_parse_fromchar) - login_log("- to display char-server parse packets on console." RETCODE); - else - login_log("- to NOT display char-server parse packets on console." RETCODE); - - if (min_level_to_connect == 0) // 0: all players, 1-99 at least gm level x - login_log("- with no minimum level for connection." RETCODE); - else if (min_level_to_connect == 99) - login_log("- to accept only GM with level 99." RETCODE); - else - login_log("- to accept only GM with level %d or more." RETCODE, min_level_to_connect); - - if (add_to_unlimited_account) - login_log("- to authorize adjustment (with timeadd ladmin) on an unlimited account." RETCODE); - else - login_log("- to refuse adjustment (with timeadd ladmin) on an unlimited account. You must use timeset (ladmin command) before." RETCODE); - - if (start_limited_time < 0) - login_log("- to create new accounts with an unlimited time." RETCODE); - else if (start_limited_time == 0) - login_log("- to create new accounts with a limited time: time of creation." RETCODE); - else - login_log("- to create new accounts with a limited time: time of creation + %d second(s)." RETCODE, start_limited_time); - - if (check_ip_flag) - login_log("- with control of players IP between login-server and char-server." RETCODE); - else - login_log("- to not check players IP between login-server and char-server." RETCODE); - - if (access_order == ACO_DENY_ALLOW) { - if (access_denynum == 0) { - login_log("- with the IP security order: 'deny,allow' (allow if not deny). You refuse no IP." RETCODE); - } else if (access_denynum == 1 && access_deny[0] == '\0') { - login_log("- with the IP security order: 'deny,allow' (allow if not deny). You refuse ALL IP." RETCODE); - } else { - login_log("- with the IP security order: 'deny,allow' (allow if not deny). Refused IP are:" RETCODE); - for(i = 0; i < access_denynum; i++) - login_log(" %s" RETCODE, (char *)(access_deny + i * ACO_STRSIZE)); - } - } else if (access_order == ACO_ALLOW_DENY) { - if (access_allownum == 0) { - login_log("- with the IP security order: 'allow,deny' (deny if not allow). But, NO IP IS AUTHORISED!" RETCODE); - } else if (access_allownum == 1 && access_allow[0] == '\0') { - login_log("- with the IP security order: 'allow,deny' (deny if not allow). You authorise ALL IP." RETCODE); - } else { - login_log("- with the IP security order: 'allow,deny' (deny if not allow). Authorised IP are:" RETCODE); - for(i = 0; i < access_allownum; i++) - login_log(" %s" RETCODE, (char *)(access_allow + i * ACO_STRSIZE)); - } - } else { // ACO_MUTUAL_FAILTURE - login_log("- with the IP security order: 'mutual-failture' (allow if in the allow list and not in the deny list)." RETCODE); - if (access_allownum == 0) { - login_log(" But, NO IP IS AUTHORISED!" RETCODE); - } else if (access_denynum == 1 && access_deny[0] == '\0') { - login_log(" But, you refuse ALL IP!" RETCODE); - } else { - if (access_allownum == 1 && access_allow[0] == '\0') { - login_log(" You authorise ALL IP." RETCODE); - } else { - login_log(" Authorised IP are:" RETCODE); - for(i = 0; i < access_allownum; i++) - login_log(" %s" RETCODE, (char *)(access_allow + i * ACO_STRSIZE)); - } - login_log(" Refused IP are:" RETCODE); - for(i = 0; i < access_denynum; i++) - login_log(" %s" RETCODE, (char *)(access_deny + i * ACO_STRSIZE)); - } - - // dynamic password error ban - if (dynamic_pass_failure_ban == 0) - login_log("- with NO dynamic password error ban." RETCODE); - else { - login_log("- with a dynamic password error ban:" RETCODE); - login_log(" After %d invalid password in %d minutes" RETCODE, dynamic_pass_failure_ban_how_many, dynamic_pass_failure_ban_time); - login_log(" IP is banned for %d minutes" RETCODE, dynamic_pass_failure_ban_how_long); - } - } -} - -//-------------------------------------- -// Function called at exit of the server -//-------------------------------------- -void do_final(void) { - int i, fd; - ShowInfo("Terminating...\n"); - fflush(stdout); - mmo_auth_sync(); - online_db->destroy(online_db, NULL); - - if(auth_dat) aFree(auth_dat); - if(gm_account_db) aFree(gm_account_db); - if(access_ladmin_allow) aFree(access_ladmin_allow); - if(access_allow) aFree(access_allow); - if(access_deny) aFree(access_deny); - for (i = 0; i < MAX_SERVERS; i++) { - if ((fd = server_fd[i]) >= 0) { - server_fd[i] = -1; - memset(&server[i], 0, sizeof(struct mmo_char_server)); - do_close(fd); - } - } - do_close(login_fd); - - login_log("----End of login-server (normal end with closing of all files)." RETCODE); - - if(log_fp) - fclose(log_fp); - ShowStatus("Finished.\n"); -} - -//------------------------------ -// Main function of login-server -//------------------------------ -void set_server_type(void) -{ - SERVER_TYPE = ATHENA_SERVER_LOGIN; -} -int do_init(int argc, char **argv) { - int i, j; - - // read login-server configuration - login_config_read((argc > 1) ? argv[1] : LOGIN_CONF_NAME); - display_conf_warnings(); // not in login_config_read, because we can use 'import' option, and display same message twice or more - save_config_in_log(); // not before, because log file name can be changed - login_lan_config_read((argc > 2) ? argv[2] : LAN_CONF_NAME); - - srand((unsigned int)time(NULL)); - - for(i = 0; i< AUTH_FIFO_SIZE; i++) - auth_fifo[i].delflag = 1; - for(i = 0; i < MAX_SERVERS; i++) - server_fd[i] = -1; - - gm_account_db = NULL; - GM_num = 0; - GM_max = 0; - mmo_auth_init(); - read_gm_account(); - set_defaultparse(parse_login); - // Online user database init - online_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); // reinitialise - add_timer_func_list(waiting_disconnect_timer, "waiting_disconnect_timer"); - - login_fd = make_listen_bind(bind_ip?bind_ip:INADDR_ANY,login_port); - - add_timer_func_list(check_auth_sync, "check_auth_sync"); - add_timer_interval(gettick() + 60000, check_auth_sync, 0, 0, 60000); // every 60 sec we check if we must save accounts file (only if necessary to save) - - // add timer to check GM accounts file modification - j = gm_account_filename_check_timer; - if (j == 0) // if we would not to check, we check every 60 sec, just to have timer (if we change timer, is was not necessary to check if timer already exists) - j = 60; - - add_timer_func_list(check_GM_file, "check_GM_file"); - add_timer_interval(gettick() + j * 1000, check_GM_file, 0, 0, j * 1000); // every x sec we check if gm file has been changed - - - add_timer_func_list(online_data_cleanup, "online_data_cleanup"); - add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600*1000); // every 10 minutes cleanup online account db. - - if (ip_sync_interval) { - add_timer_func_list(sync_ip_addresses, "sync_ip_addresses"); - add_timer_interval(gettick() + ip_sync_interval, sync_ip_addresses, 0, 0, ip_sync_interval); - } - if(console) { - set_defaultconsoleparse(parse_console); - start_console(); - } - - login_log("The login-server is ready (Server is listening on the port %d)." RETCODE, login_port); - ShowStatus("The login-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", login_port); - - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +// new version of the login-server by [Yor] + +#include +#ifdef __WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#include +#include +#include +#endif +#include +#include +#include // for stat/lstat/fstat +#include +#include +#include +#include + +#include "../common/core.h" +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/mmo.h" +#include "../common/showmsg.h" +#include "../common/version.h" +#include "../common/db.h" +#include "../common/lock.h" +#include "../common/malloc.h" +#include "../common/strlib.h" +#include "../common/showmsg.h" +#include "login.h" + +#ifdef PASSWORDENC +#include "md5calc.h" +#endif + +int account_id_count = START_ACCOUNT_NUM; +int server_num; +int new_account_flag = 0; +in_addr_t bind_ip= 0; +char bind_ip_str[128]; +int login_port = 6900; + +// Advanced subnet check [LuzZza] +struct _subnet { + long subnet; + long mask; + long char_ip; + long map_ip; +} subnet[16]; + +int subnet_count = 0; + +int use_dnsbl=0; // [Zido] +char dnsbl_servs[1024]; // [Zido] + +char account_filename[1024] = "save/account.txt"; +char GM_account_filename[1024] = "conf/GM_account.txt"; +char login_log_filename[1024] = "log/login.log"; +FILE *log_fp = NULL; +char login_log_unknown_packets_filename[1024] = "log/login_unknown_packets.log"; +char date_format[32] = "%Y-%m-%d %H:%M:%S"; +int save_unknown_packets = 0; +long creation_time_GM_account_file; +int gm_account_filename_check_timer = 15; // Timer to check if GM_account file has been changed and reload GM account automaticaly (in seconds; default: 15) + +int log_login = 1; + +int display_parse_login = 0; // 0: no, 1: yes +int display_parse_admin = 0; // 0: no, 1: yes +int display_parse_fromchar = 0; // 0: no, 1: yes (without packet 0x2714), 2: all packets + +struct mmo_char_server server[MAX_SERVERS]; +int server_fd[MAX_SERVERS]; + +int login_fd; + +static int online_check=1; //When set to 1, login server rejects incoming players that are already registered as online. [Skotlex] +//Account flood protection [Kevin] +unsigned int new_reg_tick=0; +int allowed_regs=1; +int num_regs=0; +int time_allowed=10; //Init this to 10 seconds. [Skotlex] + +enum { + ACO_DENY_ALLOW = 0, + ACO_ALLOW_DENY, + ACO_MUTUAL_FAILTURE, + ACO_STRSIZE = 128, +}; + +int access_order = ACO_DENY_ALLOW; +int access_allownum = 0; +int access_denynum = 0; +char *access_allow = NULL; +char *access_deny = NULL; + +int access_ladmin_allownum = 0; +char *access_ladmin_allow = NULL; + +int min_level_to_connect = 0; // minimum level of player/GM (0: player, 1-99: gm) to connect on the server +int add_to_unlimited_account = 0; // Give possibility or not to adjust (ladmin command: timeadd) the time of an unlimited account. +int start_limited_time = -1; // Starting additional sec from now for the limited time at creation of accounts (-1: unlimited time, 0 or more: additional sec from now) +int check_ip_flag = 1; // It's to check IP of a player between login-server and char-server (part of anti-hacking system) + +int check_client_version = 0; //Client version check ON/OFF .. (sirius) +int client_version_to_connect = 20; //Client version needed to connect ..(sirius) +static int ip_sync_interval = 0; + + +struct login_session_data { + unsigned int md5keylen; + char md5key[20]; +}; + +#define AUTH_FIFO_SIZE 256 +struct { + int account_id, login_id1, login_id2; + int ip, sex, delflag; +} auth_fifo[AUTH_FIFO_SIZE]; +int auth_fifo_pos = 0; + +struct online_login_data { + int account_id; + short char_server; + short waiting_disconnect; +}; + +struct auth_dat { + int account_id, sex; + char userid[24], pass[33], lastlogin[24]; // 33 for 32 + NULL terminated + int logincount; + int state; // packet 0x006a value + 1 (0: compte OK) + char email[40]; // e-mail (by default: a@a.com) + char error_message[20]; // Message of error code #6 = Your are Prohibited to log in until %s (packet 0x006a) + time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + char last_ip[16]; // save of last IP of connection + char memo[255]; // a memo field + int account_reg2_num; + struct global_reg account_reg2[ACCOUNT_REG2_NUM]; +} *auth_dat = NULL; + +unsigned int auth_num = 0, auth_max = 0; + +// define the number of times that some players must authentify them before to save account file. +// it's just about normal authentification. If an account is created or modified, save is immediatly done. +// An authentification just change last connected IP and date. It already save in log file. +// set minimum auth change before save: +#define AUTH_BEFORE_SAVE_FILE 10 +// set divider of auth_num to found number of change before save +#define AUTH_SAVE_FILE_DIVIDER 50 +int auth_before_save_file = 0; // Counter. First save when 1st char-server do connection. + +int admin_state = 0; +char admin_pass[24] = ""; +unsigned int GM_num; +unsigned int GM_max=256; +char gm_pass[64] = ""; +int level_new_gm = 60; + +struct gm_account *gm_account_db; + +static struct dbt *online_db; + +int dynamic_pass_failure_ban = 1; +int dynamic_pass_failure_ban_time = 5; +int dynamic_pass_failure_ban_how_many = 3; +int dynamic_pass_failure_ban_how_long = 1; + +int use_md5_passwds = 0; + +int console = 0; + +int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len); + +//------------------------------ +// Writing function of logs file +//------------------------------ +int login_log(char *fmt, ...) { + if (log_login) { + va_list ap; + time_t raw_time; + char tmpstr[2048]; + + if(!log_fp) + log_fp = fopen(login_log_filename, "a"); + + if (log_fp) { + if (fmt[0] == '\0') // jump a line if no message + fprintf(log_fp, RETCODE); + else { + va_start(ap, fmt); + // Platform/Compiler dependant clock() for time check is removed. [Lance] + // clock() is originally used to track processing ticks on program execution. + time(&raw_time); + strftime(tmpstr, 24, date_format, localtime(&raw_time)); + sprintf(tmpstr + strlen(tmpstr), ": %s", fmt); + vfprintf(log_fp, tmpstr, ap); + va_end(ap); + } + fflush(log_fp); // under cygwin or windows, if software is stopped, data are not written in the file -> fflush at every line + } + } + + return 0; +} + +static void* create_online_user(DBKey key, va_list args) { + struct online_login_data *p; + p = aCalloc(1, sizeof(struct online_login_data)); + p->account_id = key.i; + p->char_server = -1; + return p; +} +//----------------------------------------------------- +// Online User Database [Wizputer] +//----------------------------------------------------- + +void add_online_user (int char_server, int account_id) { + struct online_login_data *p; + if (!online_check) + return; + p = idb_ensure(online_db, account_id, create_online_user); + p->char_server = char_server; + p->waiting_disconnect = 0; +} +int is_user_online (int account_id) { + return (idb_get(online_db, account_id) != NULL); +} +void remove_online_user (int account_id) { + if(!online_check) + return; + if (account_id == 99) { // reset all to offline + online_db->clear(online_db, NULL); // purge db + return; + } + idb_remove(online_db,account_id); +} + +int waiting_disconnect_timer(int tid, unsigned int tick, int id, int data) +{ + struct online_login_data *p; + if ((p= idb_get(online_db, id)) != NULL && p->waiting_disconnect) + remove_online_user(p->account_id); + return 0; +} + +static int sync_ip_addresses(int tid, unsigned int tick, int id, int data){ + unsigned char buf[2]; + ShowInfo("IP Sync in progress...\n"); + WBUFW(buf,0) = 0x2735; + charif_sendallwos(-1, buf, 2); + return 0; +} + +//---------------------------------------------------------------------- +// Determine if an account (id) is a GM account +// and returns its level (or 0 if it isn't a GM account or if not found) +//---------------------------------------------------------------------- +int isGM(int account_id) { + unsigned int i; + for(i=0; i < GM_num; i++) + if(gm_account_db[i].account_id == account_id) + return gm_account_db[i].level; + return 0; +} + +//---------------------------------------------------------------------- +// Adds a new GM using acc id and level +//---------------------------------------------------------------------- +void addGM(int account_id, int level) { + unsigned int i; + int do_add = 0; + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id==account_id) { + do_add = 1; + break; + } + } + for(i = 0; i < GM_num; i++) + if (gm_account_db[i].account_id == account_id) { + if (gm_account_db[i].level == level) + ShowWarning("addGM: GM account %d defined twice (same level: %d).\n", account_id, level); + else { + ShowWarning("addGM: GM account %d defined twice (levels: %d and %d).\n", account_id, gm_account_db[i].level, level); + gm_account_db[i].level = level; + } + return; + } + + // if new account + if (i == GM_num && do_add) { + if (GM_num >= GM_max) { + GM_max += 256; + gm_account_db = (struct gm_account*)aRealloc(gm_account_db, sizeof(struct gm_account) * GM_max); + memset(gm_account_db + (GM_max - 256), 0, sizeof(struct gm_account) * 256); + } + gm_account_db[GM_num].account_id = account_id; + gm_account_db[GM_num].level = level; + GM_num++; + if (GM_num >= 4000) { + ShowWarning("4000 GM accounts found. Next GM accounts are not read.\n"); + login_log("***WARNING: 4000 GM accounts found. Next GM accounts are not read." RETCODE); + } + } +} + +//------------------------------------------------------- +// Reading function of GM accounts file (and their level) +//------------------------------------------------------- +int read_gm_account(void) { + char line[512]; + FILE *fp; + int account_id, level; + int line_counter; + struct stat file_stat; + int start_range = 0, end_range = 0, is_range = 0, current_id = 0; + + if(gm_account_db) aFree(gm_account_db); + GM_num = 0; + if(GM_max < 0) GM_max = 256; + gm_account_db = (struct gm_account*)aCalloc(GM_max, sizeof(struct gm_account)); + + // get last modify time/date + if (stat(GM_account_filename, &file_stat)) + creation_time_GM_account_file = 0; // error + else + creation_time_GM_account_file = (long)file_stat.st_mtime; + + if ((fp = fopen(GM_account_filename, "r")) == NULL) { + ShowError("read_gm_account: GM accounts file [%s] not found.\n", GM_account_filename); + ShowError(" Actually, there is no GM accounts on the server.\n"); + login_log("read_gm_account: GM accounts file [%s] not found." RETCODE, GM_account_filename); + login_log(" Actually, there is no GM accounts on the server." RETCODE); + return 1; + } + + line_counter = 0; + // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???) + // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) + while(fgets(line, sizeof(line)-1, fp) && GM_num < 4000) { + line_counter++; + if ((line[0] == '/' && line[1] == '/') || line[0] == '\0' || line[0] == '\n' || line[0] == '\r') + continue; + is_range = (sscanf(line, "%d%*[-~]%d %d",&start_range,&end_range,&level)==3); // ID Range [MC Cameri] + if (!is_range && sscanf(line, "%d %d", &account_id, &level) != 2 && sscanf(line, "%d: %d", &account_id, &level) != 2) + ShowError("read_gm_account: file [%s], invalid 'acount_id|range level' format (line #%d).\n", GM_account_filename, line_counter); + else if (level <= 0) + ShowError("read_gm_account: file [%s] %dth account (line #%d) (invalid level [0 or negative]: %d).\n", GM_account_filename, GM_num+1, line_counter, level); + else { + if (level > 99) { + ShowNotice("read_gm_account: file [%s] %dth account (invalid level, but corrected: %d->99).\n", GM_account_filename, GM_num+1, level); + level = 99; + } + if (is_range) { + if (start_range==end_range) + ShowError("read_gm_account: file [%s] invalid range, beginning of range is equal to end of range (line #%d).\n", GM_account_filename, line_counter); + else if (start_range>end_range) + ShowError("read_gm_account: file [%s] invalid range, beginning of range must be lower than end of range (line #%d).\n", GM_account_filename, line_counter); + else + for (current_id = start_range;current_id<=end_range;current_id++) + addGM(current_id,level); + } else { + addGM(account_id,level); + } + } + } + fclose(fp); + + ShowStatus("read_gm_account: file '%s' read (%d GM accounts found).\n", GM_account_filename, GM_num); + login_log("read_gm_account: file '%s' read (%d GM accounts found)." RETCODE, GM_account_filename, GM_num); + + return 0; +} + +//-------------------------------------------------------------- +// Test of the IP mask +// (ip: IP to be tested, str: mask x.x.x.x/# or x.x.x.x/y.y.y.y) +//-------------------------------------------------------------- +int check_ipmask(unsigned int ip, const unsigned char *str) { + unsigned int mask = 0, i = 0, m, ip2, a0, a1, a2, a3; + unsigned char *p = (unsigned char *)&ip2, *p2 = (unsigned char *)&mask; + + if (sscanf((const char*)str, "%d.%d.%d.%d/%n", &a0, &a1, &a2, &a3, &i) != 4 || i == 0) + return 0; + p[0] = a0; p[1] = a1; p[2] = a2; p[3] = a3; + + if (sscanf((const char*)str+i, "%d.%d.%d.%d", &a0, &a1, &a2, &a3) == 4) { + p2[0] = a0; p2[1] = a1; p2[2] = a2; p2[3] = a3; + mask = ntohl(mask); + } else if (sscanf((const char*)(str+i), "%d", &m) == 1 && m >= 0 && m <= 32) { + for(i = 0; i < m && i < 32; i++) + mask = (mask >> 1) | 0x80000000; + } else { + ShowError("check_ipmask: invalid mask [%s].\n", str); + return 0; + } + +// printf("Tested IP: %08x, network: %08x, network mask: %08x\n", +// (unsigned int)ntohl(ip), (unsigned int)ntohl(ip2), (unsigned int)mask); + return ((ntohl(ip) & mask) == (ntohl(ip2) & mask)); +} + +//--------------------- +// Access control by IP +//--------------------- +int check_ip(unsigned int ip) { + int i; + unsigned char *p = (unsigned char *)&ip; + char buf[20]; + char * access_ip; + enum { ACF_DEF, ACF_ALLOW, ACF_DENY } flag = ACF_DEF; + + if (access_allownum == 0 && access_denynum == 0) + return 1; // When there is no restriction, all IP are authorised. + +// + 012.345.: front match form, or +// all: all IP are matched, or +// 012.345.678.901/24: network form (mask with # of bits), or +// 012.345.678.901/255.255.255.0: network form (mask with ip mask) +// + Note about the DNS resolution (like www.ne.jp, etc.): +// There is no guarantee to have an answer. +// If we have an answer, there is no guarantee to have a 100% correct value. +// And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. +// So, DNS notation isn't authorised for ip checking. + sprintf(buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); + + for(i = 0; i < access_allownum; i++) { + access_ip = access_allow + i * ACO_STRSIZE; + if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, (unsigned char*)access_ip)) { + if(access_order == ACO_ALLOW_DENY) + return 1; // With 'allow, deny' (deny if not allow), allow has priority + flag = ACF_ALLOW; + break; + } + } + + for(i = 0; i < access_denynum; i++) { + access_ip = access_deny + i * ACO_STRSIZE; + if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, (unsigned char*)access_ip)) { + //flag = ACF_DENY; // not necessary to define flag + return 0; // At this point, if it's 'deny', we refuse connection. + } + } + + return (flag == ACF_ALLOW || access_order == ACO_DENY_ALLOW) ? 1:0; + // With 'mutual-failture', only 'allow' and non 'deny' IP are authorised. + // A non 'allow' (even non 'deny') IP is not authorised. It's like: if allowed and not denied, it's authorised. + // So, it's disapproval if you have no description at the time of 'mutual-failture'. + // With 'deny,allow' (allow if not deny), because here it's not deny, we authorise. +} + +//-------------------------------- +// Access control by IP for ladmin +//-------------------------------- +int check_ladminip(unsigned int ip) { + int i; + unsigned char *p = (unsigned char *)&ip; + char buf[20]; + char * access_ip; + + if (access_ladmin_allownum == 0) + return 1; // When there is no restriction, all IP are authorised. + +// + 012.345.: front match form, or +// all: all IP are matched, or +// 012.345.678.901/24: network form (mask with # of bits), or +// 012.345.678.901/255.255.255.0: network form (mask with ip mask) +// + Note about the DNS resolution (like www.ne.jp, etc.): +// There is no guarantee to have an answer. +// If we have an answer, there is no guarantee to have a 100% correct value. +// And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. +// So, DNS notation isn't authorised for ip checking. + sprintf(buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); + + for(i = 0; i < access_ladmin_allownum; i++) { + access_ip = access_ladmin_allow + i * ACO_STRSIZE; + if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, (unsigned char*)access_ip)) { + return 1; + } + } + + return 0; +} + +//--------------------------------------------------- +// E-mail check: return 0 (not correct) or 1 (valid). +//--------------------------------------------------- +int e_mail_check(char *email) { + char ch; + char* last_arobas; + + // athena limits + if (strlen(email) < 3 || strlen(email) > 39) + return 0; + + // part of RFC limits (official reference of e-mail description) + if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') + return 0; + + if (email[strlen(email)-1] == '.') + return 0; + + last_arobas = strrchr(email, '@'); + + if (strstr(last_arobas, "@.") != NULL || + strstr(last_arobas, "..") != NULL) + return 0; + + for(ch = 1; ch < 32; ch++) + if (strchr(last_arobas, ch) != NULL) + return 0; + + if (strchr(last_arobas, ' ') != NULL || + strchr(last_arobas, ';') != NULL) + return 0; + + // all correct + return 1; +} + +//----------------------------------------------- +// Search an account id +// (return account index or -1 (if not found)) +// If exact account name is not found, +// the function checks without case sensitive +// and returns index if only 1 account is found +// and similar to the searched name. +//----------------------------------------------- +int search_account_index(char* account_name) { + unsigned int i, quantity; + int index; + + quantity = 0; + index = -1; + + for(i = 0; i < auth_num; i++) { + // Without case sensitive check (increase the number of similar account names found) + if (stricmp(auth_dat[i].userid, account_name) == 0) { + // Strict comparison (if found, we finish the function immediatly with correct value) + if (strcmp(auth_dat[i].userid, account_name) == 0) + return i; + quantity++; + index = i; + } + } + // Here, the exact account name is not found + // We return the found index of a similar account ONLY if there is 1 similar account + if (quantity == 1) + return index; + + // Exact account name is not found and 0 or more than 1 similar accounts have been found ==> we say not found + return -1; +} + +//-------------------------------------------------------- +// Create a string to save the account in the account file +//-------------------------------------------------------- +int mmo_auth_tostr(char *str, struct auth_dat *p) { + int i; + char *str_p = str; + + str_p += sprintf(str_p, "%d\t%s\t%s\t%s\t%c\t%d\t%d\t" + "%s\t%s\t%ld\t%s\t%s\t%ld\t", + p->account_id, p->userid, p->pass, p->lastlogin, + (p->sex == 2) ? 'S' : (p->sex ? 'M' : 'F'), + p->logincount, p->state, + p->email, p->error_message, + (long)p->connect_until_time, p->last_ip, p->memo, (long)p->ban_until_time); + + for(i = 0; i < p->account_reg2_num; i++) + if (p->account_reg2[i].str[0]) + str_p += sprintf(str_p, "%s,%s ", p->account_reg2[i].str, p->account_reg2[i].value); + + return 0; +} + +//--------------------------------- +// Reading of the accounts database +//--------------------------------- +int mmo_auth_init(void) { + FILE *fp; + int account_id, logincount, state, n, i; + unsigned int j; + char line[2048], *p, userid[2048], pass[2048], lastlogin[2048], sex, email[2048], error_message[2048], last_ip[2048], memo[2048]; + long ban_until_time; + long connect_until_time; + char str[2048]; + char v[2048]; + int GM_count = 0; + int server_count = 0; + + auth_max = 256; + auth_dat = (struct auth_dat*)aCalloc(auth_max, sizeof(struct auth_dat)); + + if ((fp = fopen(account_filename, "r")) == NULL) { + // no account file -> no account -> no login, including char-server (ERROR) + ShowError(CL_RED"mmmo_auth_init: Accounts file [%s] not found."CL_RESET"\n", account_filename); + return 0; + } + + while(fgets(line, sizeof(line)-1, fp) != NULL) { + if (line[0] == '/' && line[1] == '/') + continue; + line[sizeof(line)-1] = '\0'; + // remove carriage return if exist + while(line[0] != '\0' && (line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r')) + line[strlen(line)-1] = '\0'; + p = line; + + memset(userid, 0, sizeof(userid)); + memset(pass, 0, sizeof(pass)); + memset(lastlogin, 0, sizeof(lastlogin)); + memset(email, 0, sizeof(email)); + memset(error_message, 0, sizeof(error_message)); + memset(last_ip, 0, sizeof(last_ip)); + memset(memo, 0, sizeof(memo)); + + // database version reading (v2) + if (((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" + "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]\t%ld%n", + &account_id, userid, pass, lastlogin, &sex, &logincount, &state, + email, error_message, &connect_until_time, last_ip, memo, &ban_until_time, &n)) == 13 && line[n] == '\t') || + ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" + "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]%n", + &account_id, userid, pass, lastlogin, &sex, &logincount, &state, + email, error_message, &connect_until_time, last_ip, memo, &n)) == 12 && line[n] == '\t')) { + n = n + 1; + + // Some checks + if (account_id > END_ACCOUNT_NUM) { + ShowError(CL_RED"mmmo_auth_init: an account has an id higher than %d\n", END_ACCOUNT_NUM); + ShowError(" account id #%d -> account not read (saved in log file)."CL_RESET"\n", account_id); + login_log("mmmo_auth_init: ******Error: an account has an id higher than %d." RETCODE, END_ACCOUNT_NUM); + login_log(" account id #%d -> account not read (saved in next line):" RETCODE, account_id); + login_log("%s", line); + continue; + } + userid[23] = '\0'; + remove_control_chars((unsigned char *)userid); + for(j = 0; j < auth_num; j++) { + if (auth_dat[j].account_id == account_id) { + ShowError(CL_RED"mmmo_auth_init: an account has an identical id to another.\n"); + ShowError(" account id #%d -> new account not read (saved in log file)."CL_RED"\n", account_id); + login_log("mmmo_auth_init: ******Error: an account has an identical id to another." RETCODE); + login_log(" account id #%d -> new account not read (saved in next line):" RETCODE, account_id); + login_log("%s", line); + break; + } else if (strcmp(auth_dat[j].userid, userid) == 0) { + ShowError(CL_RED"mmmo_auth_init: account name already exists.\n"); + ShowError(" account name '%s' -> new account not read (saved in log file)."CL_RESET"\n", userid); // 2 lines, account name can be long. + login_log("mmmo_auth_init: ******Error: an account has an identical name to another." RETCODE); + login_log(" account name '%s' -> new account not read (saved in next line):" RETCODE, userid); + login_log("%s", line); + break; + } + } + if (j != auth_num) + continue; + + if (auth_num >= auth_max) { + auth_max += 256; + auth_dat = (struct auth_dat*)aRealloc(auth_dat, sizeof(struct auth_dat) * auth_max); + } + + memset(&auth_dat[auth_num], '\0', sizeof(struct auth_dat)); + + auth_dat[auth_num].account_id = account_id; + + strncpy(auth_dat[auth_num].userid, userid, 24); + + pass[23] = '\0'; + remove_control_chars((unsigned char *)pass); + strncpy(auth_dat[auth_num].pass, pass, 24); + + lastlogin[23] = '\0'; + remove_control_chars((unsigned char *)lastlogin); + strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24); + + auth_dat[auth_num].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); + + if (logincount >= 0) + auth_dat[auth_num].logincount = logincount; + else + auth_dat[auth_num].logincount = 0; + + if (state > 255) + auth_dat[auth_num].state = 100; + else if (state < 0) + auth_dat[auth_num].state = 0; + else + auth_dat[auth_num].state = state; + + if (e_mail_check(email) == 0) { + ShowNotice("Account %s (%d): invalid e-mail (replaced par a@a.com).\n", auth_dat[auth_num].userid, auth_dat[auth_num].account_id); + strncpy(auth_dat[auth_num].email, "a@a.com", 40); + } else { + remove_control_chars((unsigned char *)email); + strncpy(auth_dat[auth_num].email, email, 40); + } + + error_message[19] = '\0'; + remove_control_chars((unsigned char *)error_message); + if (error_message[0] == '\0' || state != 7) { // 7, because state is packet 0x006a value + 1 + strncpy(auth_dat[auth_num].error_message, "-", 20); + } else { + strncpy(auth_dat[auth_num].error_message, error_message, 20); + } + + if (i == 13) + auth_dat[auth_num].ban_until_time = (time_t)ban_until_time; + else + auth_dat[auth_num].ban_until_time = 0; + + auth_dat[auth_num].connect_until_time = (time_t)connect_until_time; + + last_ip[15] = '\0'; + remove_control_chars((unsigned char *)last_ip); + strncpy(auth_dat[auth_num].last_ip, last_ip, 16); + + memo[254] = '\0'; + remove_control_chars((unsigned char *)memo); + strncpy(auth_dat[auth_num].memo, memo, 255); + + for(j = 0; j < ACCOUNT_REG2_NUM; j++) { + p += n; + if (sscanf(p, "%[^\t,],%[^\t ] %n", str, v, &n) != 2) { + // We must check if a str is void. If it's, we can continue to read other REG2. + // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good) + if (p[0] == ',' && sscanf(p, ",%[^\t ] %n", v, &n) == 1) { + j--; + continue; + } else + break; + } + str[31] = '\0'; + remove_control_chars((unsigned char *)str); + strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32); + strncpy(auth_dat[auth_num].account_reg2[j].value,v,256); + } + auth_dat[auth_num].account_reg2_num = j; + + if (isGM(account_id) > 0) + GM_count++; + if (auth_dat[auth_num].sex == 2) + server_count++; + + auth_num++; + if (account_id >= account_id_count) + account_id_count = account_id + 1; + + // Old athena database version reading (v1) + } else if ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t%n", + &account_id, userid, pass, lastlogin, &sex, &logincount, &state, &n)) >= 5) { + if (account_id > END_ACCOUNT_NUM) { + ShowError(CL_RED"mmmo_auth_init: an account has an id higher than %d\n", END_ACCOUNT_NUM); + ShowError(" account id #%d -> account not read (saved in log file)."CL_RESET"\n", account_id); + login_log("mmmo_auth_init: ******Error: an account has an id higher than %d." RETCODE, END_ACCOUNT_NUM); + login_log(" account id #%d -> account not read (saved in next line):" RETCODE, account_id); + login_log("%s", line); + continue; + } + userid[23] = '\0'; + remove_control_chars((unsigned char *)userid); + for(j = 0; j < auth_num; j++) { + if (auth_dat[j].account_id == account_id) { + ShowError(CL_RED"mmo_auth_init: an account has an identical id to another.\n"); + ShowError(" account id #%d -> new account not read (saved in log file)."CL_RESET"\n", account_id); + login_log("mmmo_auth_init: ******Error: an account has an identical id to another." RETCODE); + login_log(" account id #%d -> new account not read (saved in next line):" RETCODE, account_id); + login_log("%s", line); + break; + } else if (strcmp(auth_dat[j].userid, userid) == 0) { + ShowError(CL_RED"mmo_auth_init: account name already exists.\n"); + ShowError(" account name '%s' -> new account not read (saved in log file)."CL_RESET"\n", userid); + login_log("mmmo_auth_init: ******Error: an account has an identical id to another." RETCODE); + login_log(" account id #%d -> new account not read (saved in next line):" RETCODE, account_id); + login_log("%s", line); + break; + } + } + if (j != auth_num) + continue; + + if (auth_num >= auth_max) { + auth_max += 256; + auth_dat = (struct auth_dat*)aRealloc(auth_dat, sizeof(struct auth_dat) * auth_max); + } + + memset(&auth_dat[auth_num], '\0', sizeof(struct auth_dat)); + + auth_dat[auth_num].account_id = account_id; + + strncpy(auth_dat[auth_num].userid, userid, 24); + + pass[23] = '\0'; + remove_control_chars((unsigned char *)pass); + strncpy(auth_dat[auth_num].pass, pass, 24); + + lastlogin[23] = '\0'; + remove_control_chars((unsigned char *)lastlogin); + strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24); + + auth_dat[auth_num].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); + + if (i >= 6) { + if (logincount >= 0) + auth_dat[auth_num].logincount = logincount; + else + auth_dat[auth_num].logincount = 0; + } else + auth_dat[auth_num].logincount = 0; + + if (i >= 7) { + if (state > 255) + auth_dat[auth_num].state = 100; + else if (state < 0) + auth_dat[auth_num].state = 0; + else + auth_dat[auth_num].state = state; + } else + auth_dat[auth_num].state = 0; + + // Initialization of new data + strncpy(auth_dat[auth_num].email, "a@a.com", 40); + strncpy(auth_dat[auth_num].error_message, "-", 20); + auth_dat[auth_num].ban_until_time = 0; + auth_dat[auth_num].connect_until_time = 0; + strncpy(auth_dat[auth_num].last_ip, "-", 16); + strncpy(auth_dat[auth_num].memo, "-", 255); + + for(j = 0; j < ACCOUNT_REG2_NUM; j++) { + p += n; + if (sscanf(p, "%[^\t,],%[^\t ] %n", str, v, &n) != 2) { + // We must check if a str is void. If it's, we can continue to read other REG2. + // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good) + if (p[0] == ',' && sscanf(p, ",%[^\t ] %n", v, &n) == 1) { + j--; + continue; + } else + break; + } + str[31] = '\0'; + remove_control_chars((unsigned char *)str); + strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32); + strncpy(auth_dat[auth_num].account_reg2[j].value,v,256); + } + auth_dat[auth_num].account_reg2_num = j; + + if (isGM(account_id) > 0) + GM_count++; + if (auth_dat[auth_num].sex == 2) + server_count++; + + auth_num++; + if (account_id >= account_id_count) + account_id_count = account_id + 1; + + } else { + i = 0; + if (sscanf(line, "%d\t%%newid%%\n%n", &account_id, &i) == 1 && + i > 0 && account_id > account_id_count) + account_id_count = account_id; + } + } + fclose(fp); + + if (auth_num == 0) { + ShowNotice("mmo_auth_init: No account found in %s.\n", account_filename); + sprintf(line, "No account found in %s.", account_filename); + } else { + if (auth_num == 1) { + ShowStatus("mmo_auth_init: 1 account read in %s,\n", account_filename); + sprintf(line, "1 account read in %s,", account_filename); + } else { + ShowStatus("mmo_auth_init: %d accounts read in %s,\n", auth_num, account_filename); + sprintf(line, "%d accounts read in %s,", auth_num, account_filename); + } + if (GM_count == 0) { + ShowStatus(" of which is no GM account, and "); + sprintf(str, "%s of which is no GM account and", line); + } else if (GM_count == 1) { + ShowStatus(" of which is 1 GM account, and "); + sprintf(str, "%s of which is 1 GM account and", line); + } else { + ShowStatus(" of which is %d GM accounts, and ", GM_count); + sprintf(str, "%s of which is %d GM accounts and", line, GM_count); + } + if (server_count == 0) { + printf("no server account ('S').\n"); + sprintf(line, "%s no server account ('S').", str); + } else if (server_count == 1) { + printf("1 server account ('S').\n"); + sprintf(line, "%s 1 server account ('S').", str); + } else { + printf("%d server accounts ('S').\n", server_count); + sprintf(line, "%s %d server accounts ('S').", str, server_count); + } + } + login_log("%s" RETCODE, line); + + return 0; +} + +//------------------------------------------ +// Writing of the accounts database file +// (accounts are sorted by id before save) +//------------------------------------------ +void mmo_auth_sync(void) { + FILE *fp; + unsigned int i, j, k; + int lock; + int account_id; + //int id[auth_num]; + //int *id = (int *)aCalloc(auth_num, sizeof(int)); + CREATE_BUFFER(id, int, auth_num); + char line[65536]; + + // Sorting before save + for(i = 0; i < auth_num; i++) { + id[i] = i; + account_id = auth_dat[i].account_id; + for(j = 0; j < i; j++) { + if (account_id < auth_dat[id[j]].account_id) { + for(k = i; k > j; k--) + id[k] = id[k-1]; + id[j] = i; // id[i] + break; + } + } + } + + // Data save + if ((fp = lock_fopen(account_filename, &lock)) == NULL) { + //if (id) aFree(id); // aFree, right? + DELETE_BUFFER(id); + return; + } + + fprintf(fp, "// Accounts file: here are saved all information about the accounts.\n"); + fprintf(fp, "// Structure: ID, account name, password, last login time, sex, # of logins, state, email, error message for state 7, validity time, last (accepted) login ip, memo field, ban timestamp, repeated(register text, register value)\n"); + fprintf(fp, "// Some explanations:\n"); + fprintf(fp, "// account name : between 4 to 23 char for a normal account (standard client can't send less than 4 char).\n"); + fprintf(fp, "// account password: between 4 to 23 char\n"); + fprintf(fp, "// sex : M or F for normal accounts, S for server accounts\n"); + fprintf(fp, "// state : 0: account is ok, 1 to 256: error code of packet 0x006a + 1\n"); + fprintf(fp, "// email : between 3 to 39 char (a@a.com is like no email)\n"); + fprintf(fp, "// error message : text for the state 7: 'Your are Prohibited to login until '. Max 19 char\n"); + fprintf(fp, "// valitidy time : 0: unlimited account, : date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n"); + fprintf(fp, "// memo field : max 254 char\n"); + fprintf(fp, "// ban time : 0: no ban, : banned until the date: date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n"); + for(i = 0; i < auth_num; i++) { + k = id[i]; // use of sorted index + if (auth_dat[k].account_id < 0) + continue; + + mmo_auth_tostr(line, &auth_dat[k]); + fprintf(fp, "%s" RETCODE, line); + } + fprintf(fp, "%d\t%%newid%%\n", account_id_count); + + lock_fclose(fp, account_filename, &lock); + + // set new counter to minimum number of auth before save + auth_before_save_file = auth_num / AUTH_SAVE_FILE_DIVIDER; // Re-initialise counter. We have save. + if (auth_before_save_file < AUTH_BEFORE_SAVE_FILE) + auth_before_save_file = AUTH_BEFORE_SAVE_FILE; + + //if (id) aFree(id); + DELETE_BUFFER(id); + + return; +} + +//----------------------------------------------------- +// Check if we must save accounts file or not +// every minute, we check if we must save because we +// have do some authentifications without arrive to +// the minimum of authentifications for the save. +// Note: all other modification of accounts (deletion, +// change of some informations excepted lastip/ +// lastlogintime, creation) are always save +// immediatly and set the minimum of +// authentifications to its initialization value. +//----------------------------------------------------- +int check_auth_sync(int tid, unsigned int tick, int id, int data) { + // we only save if necessary: + // we have do some authentifications without do saving + if (auth_before_save_file < AUTH_BEFORE_SAVE_FILE || + auth_before_save_file < (int)(auth_num / AUTH_SAVE_FILE_DIVIDER)) + mmo_auth_sync(); + + return 0; +} + +//-------------------------------------------------------------------- +// Packet send to all char-servers, except one (wos: without our self) +//-------------------------------------------------------------------- +int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { + int i, c, fd; + + for(i = 0, c = 0; i < MAX_SERVERS; i++) { + if ((fd = server_fd[i]) >= 0 && fd != sfd) { + WFIFOHEAD(fd, len); + if (WFIFOSPACE(fd) < len) //Increase buffer size. + realloc_writefifo(fd, len); + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd, len); + c++; + } + } + return c; +} + +//----------------------------------------------------- +// Send GM accounts to all char-server +//----------------------------------------------------- +void send_GM_accounts(void) { + unsigned int i; + unsigned char buf[32767]; + int len; + + len = 4; + WBUFW(buf,0) = 0x2732; + for(i = 0; i < GM_num; i++) + // send only existing accounts. We can not create a GM account when server is online. + if (gm_account_db[i].level > 0) { + WBUFL(buf,len) = gm_account_db[i].account_id; + WBUFB(buf,len+4) = (unsigned char)gm_account_db[i].level; + len += 5; + if (len >= 32000) { + ShowWarning("send_GM_accounts: Too many accounts! Only %d out of %d were sent.\n", i, GM_num); + break; + } + } + WBUFW(buf,2) = len; + charif_sendallwos(-1, buf, len); + + return; +} + +//----------------------------------------------------- +// Check if GM file account have been changed +//----------------------------------------------------- +int check_GM_file(int tid, unsigned int tick, int id, int data) { + struct stat file_stat; + long new_time; + + // if we would not check + if (gm_account_filename_check_timer < 1) + return 0; + + // get last modify time/date + if (stat(GM_account_filename, &file_stat)) + new_time = 0; // error + else + new_time = (long)file_stat.st_mtime; + + if (new_time != creation_time_GM_account_file) { + read_gm_account(); + send_GM_accounts(); + } + + return 0; +} + +//------------------------------------- +// Account creation (with e-mail check) +//------------------------------------- +int mmo_auth_new(struct mmo_account* account, char sex, char* email) { + time_t timestamp, timestamp_temp; + struct tm *tmtime; + int i = auth_num; + + if (auth_num >= auth_max) { + auth_max += 256; + auth_dat = (struct auth_dat*)aRealloc(auth_dat, sizeof(struct auth_dat) * auth_max); + } + + memset(&auth_dat[i], '\0', sizeof(struct auth_dat)); + + while (isGM(account_id_count) > 0) + account_id_count++; + + auth_dat[i].account_id = account_id_count++; + + strncpy(auth_dat[i].userid, account->userid, NAME_LENGTH); + auth_dat[i].userid[23] = '\0'; + + strncpy(auth_dat[i].pass, account->passwd, NAME_LENGTH); + auth_dat[i].pass[23] = '\0'; + + memcpy(auth_dat[i].lastlogin, "-", 2); + + auth_dat[i].sex = (sex == 'M' || sex == 'm'); + + auth_dat[i].logincount = 0; + + auth_dat[i].state = 0; + + if (e_mail_check(email) == 0) + strncpy(auth_dat[i].email, "a@a.com", 40); + else + strncpy(auth_dat[i].email, email, 40); + + strncpy(auth_dat[i].error_message, "-", 20); + + auth_dat[i].ban_until_time = 0; + + if (start_limited_time < 0) + auth_dat[i].connect_until_time = 0; // unlimited + else { // limited time + timestamp = time(NULL) + start_limited_time; + // double conversion to be sure that it is possible + tmtime = localtime(×tamp); + timestamp_temp = mktime(tmtime); + if (timestamp_temp != -1 && (timestamp_temp + 3600) >= timestamp) // check possible value and overflow (and avoid summer/winter hour) + auth_dat[i].connect_until_time = timestamp_temp; + else + auth_dat[i].connect_until_time = 0; // unlimited + } + + strncpy(auth_dat[i].last_ip, "-", 16); + + strncpy(auth_dat[i].memo, "-", 255); + + auth_dat[i].account_reg2_num = 0; + + auth_num++; + + return (account_id_count - 1); +} + +//--------------------------------------- +// Check/authentification of a connection +//--------------------------------------- +int mmo_auth(struct mmo_account* account, int fd) { + char *dnsbl_serv; + unsigned int i; + time_t raw_time; + char tmpstr[256]; + int len, newaccount = 0; +#ifdef PASSWORDENC + struct login_session_data *ld; +#endif + int encpasswdok; + char md5str[64], md5bin[32]; + char ip[16]; + unsigned char *sin_addr = (unsigned char *)&session[fd]->client_addr.sin_addr; + char user_password[256]; + char r_ip[16]; // [Zido] + char ip_dnsbl[256]; // [Zido] + + sprintf(ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], sin_addr[2], sin_addr[3]); + + // Start DNS Blacklist check [Zido] + if(use_dnsbl) { + sprintf(r_ip, "%d.%d.%d.%d", sin_addr[3], sin_addr[2], sin_addr[1], sin_addr[0]); + + dnsbl_serv=strtok(dnsbl_servs,","); + sprintf(ip_dnsbl,"%s.%s",r_ip,dnsbl_serv); +// Using directly gethostbyname should be quicker. [Skotlex] +// if(resolve_hostbyname(ip_dnsbl, NULL, NULL)) { + if(gethostbyname(ip_dnsbl)) { + ShowInfo("DNSBL: (%s) Blacklisted. User Kicked.\n",ip); + return 3; + } + + while((dnsbl_serv=strtok(dnsbl_servs,","))) { + sprintf(ip_dnsbl,"%s.%s",r_ip,dnsbl_serv); +// Using directly gethostbyname should be quicker. [Skotlex] +// if(resolve_hostbyname(ip_dnsbl,NULL,NULL)!=0) { + if(gethostbyname(ip_dnsbl)) { + ShowInfo("DNSBL: (%s) Blacklisted. User Kicked.\n",ip); + return 3; + } + } + + } + // End DNS Blacklist check [Zido] + + + len = strlen(account->userid) - 2; + // Account creation with _M/_F + if (account->passwdenc == 0 && account->userid[len] == '_' && + (account->userid[len+1] == 'F' || account->userid[len+1] == 'M' || + account->userid[len+1] == 'f' || account->userid[len+1] == 'm') + && new_account_flag && account_id_count <= END_ACCOUNT_NUM && len >= 4 && strlen(account->passwd) >= 4) { + + //only continue if amount in this time limit is allowed (account registration flood protection)[Kevin] + if(gettick() <= new_reg_tick && num_regs >= allowed_regs) { + ShowNotice("Account registration denied (registration limit exceeded) to %s!\n", ip); + login_log("Notice: Account registration denied (registration limit exceeded) to %s!", ip); + return 3; + } else { + num_regs=0; + } + + newaccount = 1; + account->userid[len] = '\0'; + } + + //EXE Version check [Sirius] + if (check_client_version == 1 && account->version != 0 && + account->version != client_version_to_connect) + return 5; + + // Strict account search + for(i = 0; i < auth_num; i++) { + if (strcmp(account->userid, auth_dat[i].userid) == 0) + break; + } + // if there is no creation request and strict account search fails, we do a no sensitive case research for index + if (!newaccount && i == auth_num) { + i = search_account_index(account->userid); + if (i == -1) + i = auth_num; + else + memcpy(account->userid, auth_dat[i].userid, NAME_LENGTH); // for the possible tests/checks afterwards (copy correcte sensitive case). + } + + if (i != auth_num) { + if (newaccount) { + login_log("Attempt of creation of an already existant account (account: %s_%c, pass: %s, received pass: %s, ip: %s)" RETCODE, + account->userid, account->userid[len+1], auth_dat[i].pass, account->passwd, ip); + return 1; // 1 = Incorrect Password + } + if(use_md5_passwds) + MD5_String(account->passwd, user_password); + else + memcpy(user_password, account->passwd, NAME_LENGTH); + encpasswdok = 0; +#ifdef PASSWORDENC + ld = (struct login_session_data*)session[fd]->session_data; + if (account->passwdenc > 0) { + int j = account->passwdenc; + if (!ld) { + login_log("Md5 key not created (account: %s, ip: %s)" RETCODE, account->userid, ip); + return 1; // 1 = Incorrect Password + } + if (j > 2) + j = 1; + do { + if (j == 1) { + sprintf(md5str, "%s%s", ld->md5key, auth_dat[i].pass); // 20 + 24 + } else if (j == 2) { + sprintf(md5str, "%s%s", auth_dat[i].pass, ld->md5key); // 24 + 20 + } else + md5str[0] = '\0'; + md5str[sizeof(md5str)-1] = '\0'; // 64 + MD5_String2binary(md5str, md5bin); + encpasswdok = (memcmp(account->passwd, md5bin, 16) == 0); + } while (j < 2 && !encpasswdok && (j++) != account->passwdenc); +// printf("key[%s] md5 [%s] ", md5key, md5); +// printf("client [%s] accountpass [%s]\n", account->passwd, auth_dat[i].pass); + } +#endif + if ((strcmp(account->passwd, auth_dat[i].pass) && !encpasswdok)) { + if (account->passwdenc == 0) + login_log("Invalid password (account: %s, pass: %s, received pass: %s, ip: %s)" RETCODE, account->userid, auth_dat[i].pass, account->passwd, ip); +#ifdef PASSWORDENC + else { + char logbuf[512], *p = logbuf; + unsigned int j; + p += sprintf(p, "Invalid password (account: %s, received md5[", account->userid); + for(j = 0; j < 16; j++) + p += sprintf(p, "%02x", ((unsigned char *)account->passwd)[j]); + p += sprintf(p,"] calculated md5["); + for(j = 0; j < 16; j++) + p += sprintf(p, "%02x", ((unsigned char *)md5bin)[j]); + p += sprintf(p, "] md5 key["); + for(j = 0; j < ld->md5keylen; j++) + p += sprintf(p, "%02x", ((unsigned char *)ld->md5key)[j]); + p += sprintf(p, "], ip: %s)" RETCODE, ip); + login_log(logbuf); + } +#endif + return 1; // 1 = Incorrect Password + } + + if (auth_dat[i].state) { + login_log("Connection refused (account: %s, pass: %s, state: %d, ip: %s)" RETCODE, + account->userid, account->passwd, auth_dat[i].state, ip); + switch(auth_dat[i].state) { // packet 0x006a value + 1 + case 1: // 0 = Unregistered ID + case 2: // 1 = Incorrect Password + case 3: // 2 = This ID is expired + case 4: // 3 = Rejected from Server + case 5: // 4 = You have been blocked by the GM Team + case 6: // 5 = Your Game's EXE file is not the latest version + case 7: // 6 = Your are Prohibited to log in until %s + case 8: // 7 = Server is jammed due to over populated + case 9: // 8 = No more accounts may be connected from this company + case 10: // 9 = MSI_REFUSE_BAN_BY_DBA + case 11: // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED + case 12: // 11 = MSI_REFUSE_BAN_BY_GM + case 13: // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK + case 14: // 13 = MSI_REFUSE_SELF_LOCK + case 15: // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP + case 16: // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP + case 100: // 99 = This ID has been totally erased + case 101: // 100 = Login information remains at %s. + case 102: // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information + case 103: // 102 = This account has been temporarily prohibited from login due to a bug-related investigation + case 104: // 103 = This character is being deleted. Login is temporarily unavailable for the time being + case 105: // 104 = Your spouse character is being deleted. Login is temporarily unavailable for the time being + return auth_dat[i].state - 1; + default: + return 99; // 99 = ID has been totally erased + } + } + + if (online_check) { + unsigned char buf[8]; + struct online_login_data* data = idb_get(online_db,auth_dat[i].account_id); + if (data && data->char_server > -1) { + //Request char servers to kick this account out. [Skotlex] + ShowWarning("User [%d] is already online - Rejected.\n",auth_dat[i].account_id); + WBUFW(buf,0) = 0x2734; + WBUFL(buf,2) = auth_dat[i].account_id; + charif_sendallwos(-1, buf, 6); + if (!data->waiting_disconnect) + add_timer(gettick()+30000, waiting_disconnect_timer,auth_dat[i].account_id, 0); + data->waiting_disconnect = 1; + return 3; // Rejected + } + } + + if (auth_dat[i].ban_until_time != 0) { // if account is banned + strftime(tmpstr, 20, date_format, localtime(&auth_dat[i].ban_until_time)); + tmpstr[19] = '\0'; + if (auth_dat[i].ban_until_time > time(NULL)) { // always banned + login_log("Connection refused (account: %s, pass: %s, banned until %s, ip: %s)" RETCODE, + account->userid, account->passwd, tmpstr, ip); + return 6; // 6 = Your are Prohibited to log in until %s + } else { // ban is finished + login_log("End of ban (account: %s, pass: %s, previously banned until %s -> not more banned, ip: %s)" RETCODE, + account->userid, account->passwd, tmpstr, ip); + auth_dat[i].ban_until_time = 0; // reset the ban time + } + } + + if (auth_dat[i].connect_until_time != 0 && auth_dat[i].connect_until_time < time(NULL)) { + login_log("Connection refused (account: %s, pass: %s, expired ID, ip: %s)" RETCODE, + account->userid, account->passwd, ip); + return 2; // 2 = This ID is expired + } + + login_log("Authentification accepted (account: %s (id: %d), ip: %s)" RETCODE, account->userid, auth_dat[i].account_id, ip); + } else { + if (!newaccount) { + login_log("Unknown account (account: %s, received pass: %s, ip: %s)" RETCODE, + account->userid, account->passwd, ip); + return 0; // 0 = Unregistered ID + } else { + int new_id = mmo_auth_new(account, account->userid[len+1], "a@a.com"); + login_log("Account creation and authentification accepted (account %s (id: %d), pass: %s, sex: %c, connection with _F/_M, ip: %s)" RETCODE, + account->userid, new_id, account->passwd, account->userid[len+1], ip); + auth_before_save_file = 0; // Creation of an account -> save accounts file immediatly + + //restart ticker (account registration flood protection)[Kevin] + if(num_regs==0) { + new_reg_tick=gettick()+time_allowed*1000; + } + num_regs++; + } + } + + // auth start : time seed + // Platform/Compiler dependant clock() for time check is removed. [Lance] + // clock() is originally used to track processing ticks on program execution. + time(&raw_time); + strftime(tmpstr, 24, "%Y-%m-%d %H:%M:%S",localtime(&raw_time)); + + account->account_id = auth_dat[i].account_id; + account->login_id1 = rand(); + account->login_id2 = rand(); + memcpy(account->lastlogin, auth_dat[i].lastlogin, 24); + memcpy(auth_dat[i].lastlogin, tmpstr, 24); + account->sex = auth_dat[i].sex; + if (account->sex != 2 && account->account_id < 700000) + ShowWarning("Account %s has account id %d! Account IDs must be over 700000 to work properly!\n", account->userid, account->account_id); + + strncpy(auth_dat[i].last_ip, ip, 16); + auth_dat[i].logincount++; + + // Save until for change ip/time of auth is not very useful => limited save for that + // Save there informations isnot necessary, because they are saved in log file. + if (--auth_before_save_file <= 0) // Reduce counter. 0 or less, we save + mmo_auth_sync(); + + return -1; // account OK +} + +static int online_db_setoffline(DBKey key, void* data, va_list ap) { + struct online_login_data *p = (struct online_login_data *)data; + int server = va_arg(ap, int); + if (server == -1) { + p->char_server = -1; + p->waiting_disconnect = 0; + } else if (p->char_server == server) + p->char_server = -2; //Char server disconnected. + return 0; +} + +//-------------------------------- +// Packet parsing for char-servers +//-------------------------------- +int parse_fromchar(int fd) { + unsigned int i; + int j, id; + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; + char ip[16]; + int acc; + RFIFOHEAD(fd); + + sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + for(id = 0; id < MAX_SERVERS; id++) + if (server_fd[id] == fd) + break; + if (id == MAX_SERVERS) + session[fd]->eof = 1; + if(session[fd]->eof) { + if (id < MAX_SERVERS) { + ShowStatus("Char-server '%s' has disconnected.\n", server[id].name); + login_log("Char-server '%s' has disconnected (ip: %s)." RETCODE, + server[id].name, ip); + server_fd[id] = -1; + memset(&server[id], 0, sizeof(struct mmo_char_server)); + online_db->foreach(online_db,online_db_setoffline,id); //Set all chars from this char server to offline. + } + do_close(fd); + return 0; + } + + while (RFIFOREST(fd) >= 2) { + + if (display_parse_fromchar == 2 || (display_parse_fromchar == 1 && RFIFOW(fd,0) != 0x2714)) // 0x2714 is done very often (number of players) + ShowDebug("parse_fromchar: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + + switch (RFIFOW(fd,0)) { + // request from map-server via char-server to reload GM accounts (by Yor). + case 0x2709: + login_log("Char-server '%s': Request to re-load GM configuration file (ip: %s)." RETCODE, server[id].name, ip); + read_gm_account(); + // send GM accounts to all char-servers + send_GM_accounts(); + RFIFOSKIP(fd,2); + break; + + case 0x2712: // request from char-server to authentify an account + if (RFIFOREST(fd) < 19) + return 0; + { + int acc; + acc = RFIFOL(fd,2); // speed up + for(i = 0; i < AUTH_FIFO_SIZE; i++) { + if (auth_fifo[i].account_id == acc && + auth_fifo[i].login_id1 == RFIFOL(fd,6) && +#if CMP_AUTHFIFO_LOGIN2 != 0 + auth_fifo[i].login_id2 == RFIFOL(fd,10) && // relate to the versions higher than 18 +#endif + auth_fifo[i].sex == RFIFOB(fd,14) && + (!check_ip_flag || auth_fifo[i].ip == RFIFOL(fd,15)) && + !auth_fifo[i].delflag) { + unsigned int k; + time_t connect_until_time = 0; + char email[40] = ""; + WFIFOHEAD(fd,51); + auth_fifo[i].delflag = 1; + login_log("Char-server '%s': authentification of the account %d accepted (ip: %s)." RETCODE, + server[id].name, acc, ip); +// printf("%d\n", i); + for(k = 0; k < auth_num; k++) { + if (auth_dat[k].account_id == acc) { + strcpy(email, auth_dat[k].email); + connect_until_time = auth_dat[k].connect_until_time; + break; + } + } + WFIFOW(fd,0) = 0x2713; + WFIFOL(fd,2) = acc; + WFIFOB(fd,6) = 0; + memcpy(WFIFOP(fd, 7), email, 40); + WFIFOL(fd,47) = (unsigned long)connect_until_time; + WFIFOSET(fd,51); + break; + } + } + // authentification not found + if (i == AUTH_FIFO_SIZE) { + login_log("Char-server '%s': authentification of the account %d REFUSED (ip: %s)." RETCODE, + server[id].name, acc, ip); + WFIFOHEAD(fd, 51); + WFIFOW(fd,0) = 0x2713; + WFIFOL(fd,2) = acc; + WFIFOB(fd,6) = 1; + // It is unnecessary to send email + // It is unnecessary to send validity date of the account + WFIFOSET(fd,51); + } + } + RFIFOSKIP(fd,19); + break; + + case 0x2714: + if (RFIFOREST(fd) < 6) + return 0; + //printf("parse_fromchar: Receiving of the users number of the server '%s': %d\n", server[id].name, RFIFOL(fd,2)); + server[id].users = RFIFOL(fd,2); + // send some answer + WFIFOHEAD(fd, 2); + WFIFOW(fd,0) = 0x2718; + WFIFOSET(fd,2); + + RFIFOSKIP(fd,6); + break; + + // we receive a e-mail creation of an account with a default e-mail (no answer) + case 0x2715: + if (RFIFOREST(fd) < 46) + return 0; + { + char email[40]; + acc = RFIFOL(fd,2); // speed up + memcpy(email, RFIFOP(fd,6), 40); + email[39] = '\0'; + remove_control_chars((unsigned char *)email); + //printf("parse_fromchar: an e-mail creation of an account with a default e-mail: server '%s', account: %d, e-mail: '%s'.\n", server[id].name, acc, RFIFOP(fd,6)); + if (e_mail_check(email) == 0) + login_log("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - e-mail is invalid (account: %d, ip: %s)" RETCODE, + server[id].name, acc, ip); + else { + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == acc && (strcmp(auth_dat[i].email, "a@a.com") == 0 || auth_dat[i].email[0] == '\0')) { + memcpy(auth_dat[i].email, email, 40); + login_log("Char-server '%s': Create an e-mail on an account with a default e-mail (account: %d, new e-mail: %s, ip: %s)." RETCODE, + server[id].name, acc, email, ip); + // Save + mmo_auth_sync(); + break; + } + } + if (i == auth_num) + login_log("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - account doesn't exist or e-mail of account isn't default e-mail (account: %d, ip: %s)." RETCODE, + server[id].name, acc, ip); + } + } + RFIFOSKIP(fd,46); + break; + + // We receive an e-mail/limited time request, because a player comes back from a map-server to the char-server + case 0x2716: + if (RFIFOREST(fd) < 6) + return 0; + //printf("parse_fromchar: E-mail/limited time request from '%s' server (concerned account: %d)\n", server[id].name, RFIFOL(fd,2)); + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == RFIFOL(fd,2)) { + login_log("Char-server '%s': e-mail of the account %d found (ip: %s)." RETCODE, + server[id].name, RFIFOL(fd,2), ip); + WFIFOW(fd,0) = 0x2717; + WFIFOL(fd,2) = RFIFOL(fd,2); + memcpy(WFIFOP(fd, 6), auth_dat[i].email, 40); + WFIFOL(fd,46) = (unsigned long)auth_dat[i].connect_until_time; + WFIFOSET(fd,50); + break; + } + } + if (i == auth_num) + login_log("Char-server '%s': e-mail of the account %d NOT found (ip: %s)." RETCODE, + server[id].name, RFIFOL(fd,2), ip); + RFIFOSKIP(fd,6); + break; + + case 0x2720: // To become GM request + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + unsigned char buf[10]; + FILE *fp; + acc = RFIFOL(fd,4); + //printf("parse_fromchar: Request to become a GM acount from %d account.\n", acc); + WBUFW(buf,0) = 0x2721; + WBUFL(buf,2) = acc; + WBUFL(buf,6) = 0; + if (strcmp((char*)RFIFOP(fd,8), gm_pass) == 0) { + // only non-GM can become GM + if (isGM(acc) == 0) { + // if we autorise creation + if (level_new_gm > 0) { + // if we can open the file to add the new GM + if ((fp = fopen(GM_account_filename, "a")) != NULL) { + char tmpstr[24]; + time_t raw_time; + time(&raw_time); + strftime(tmpstr, 23, date_format, localtime(&raw_time)); + fprintf(fp, RETCODE "// %s: @GM command on account %d" RETCODE "%d %d" RETCODE, tmpstr, acc, acc, level_new_gm); + fclose(fp); + WBUFL(buf,6) = level_new_gm; + read_gm_account(); + send_GM_accounts(); + ShowNotice("GM Change of the account %d: level 0 -> %d.\n", acc, level_new_gm); + login_log("Char-server '%s': GM Change of the account %d: level 0 -> %d (ip: %s)." RETCODE, + server[id].name, acc, level_new_gm, ip); + } else { + ShowError("Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file)\n", acc); + login_log("Char-server '%s': Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file, ip: %s)." RETCODE, + server[id].name, acc, ip); + } + } else { + ShowError("Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0))\n", acc); + login_log("Char-server '%s': Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0), ip: %s)." RETCODE, + server[id].name, acc, ip); + } + } else { + ShowError("Error of GM change (suggested account: %d (already GM), correct password).\n", acc); + login_log("Char-server '%s': Error of GM change (suggested account: %d (already GM), correct password, ip: %s)." RETCODE, + server[id].name, acc, ip); + } + } else { + ShowError("Error of GM change (suggested account: %d, invalid password).\n", acc); + login_log("Char-server '%s': Error of GM change (suggested account: %d, invalid password, ip: %s)." RETCODE, + server[id].name, acc, ip); + } + charif_sendallwos(-1, buf, 10); + } + RFIFOSKIP(fd, RFIFOW(fd,2)); + return 0; + + // Map server send information to change an email of an account via char-server + case 0x2722: // 0x2722 .L .40B .40B + if (RFIFOREST(fd) < 86) + return 0; + { + char actual_email[40], new_email[40]; + acc = RFIFOL(fd,2); + memcpy(actual_email, RFIFOP(fd,6), 40); + actual_email[39] = '\0'; + remove_control_chars((unsigned char *)actual_email); + memcpy(new_email, RFIFOP(fd,46), 40); + new_email[39] = '\0'; + remove_control_chars((unsigned char *)new_email); + if (e_mail_check(actual_email) == 0) + login_log("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)" RETCODE, + server[id].name, acc, ip); + else if (e_mail_check(new_email) == 0) + login_log("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a invalid new e-mail (account: %d, ip: %s)" RETCODE, + server[id].name, acc, ip); + else if (strcmpi(new_email, "a@a.com") == 0) + login_log("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a default e-mail (account: %d, ip: %s)" RETCODE, + server[id].name, acc, ip); + else { + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == acc) { + if (strcmpi(auth_dat[i].email, actual_email) == 0) { + memcpy(auth_dat[i].email, new_email, 40); + login_log("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s)." RETCODE, + server[id].name, acc, auth_dat[i].userid, new_email, ip); + // Save + mmo_auth_sync(); + } else + login_log("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual e-mail is incorrect (account: %d (%s), actual e-mail: %s, proposed e-mail: %s, ip: %s)." RETCODE, + server[id].name, acc, auth_dat[i].userid, auth_dat[i].email, actual_email, ip); + break; + } + } + if (i == auth_num) + login_log("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but account doesn't exist (account: %d, ip: %s)." RETCODE, + server[id].name, acc, ip); + } + } + RFIFOSKIP(fd, 86); + break; + + // Receiving of map-server via char-server a status change resquest (by Yor) + case 0x2724: + if (RFIFOREST(fd) < 10) + return 0; + { + int acc, statut; + acc = RFIFOL(fd,2); + statut = RFIFOL(fd,6); + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == acc) { + if (auth_dat[i].state != statut) { + login_log("Char-server '%s': Status change (account: %d, new status %d, ip: %s)." RETCODE, + server[id].name, acc, statut, ip); + if (statut != 0) { + unsigned char buf[16]; + WBUFW(buf,0) = 0x2731; + WBUFL(buf,2) = acc; + WBUFB(buf,6) = 0; // 0: change of statut, 1: ban + WBUFL(buf,7) = statut; // status or final date of a banishment + charif_sendallwos(-1, buf, 11); + for(j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == acc) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + } + auth_dat[i].state = statut; + // Save + mmo_auth_sync(); + } else + login_log("Char-server '%s': Error of Status change - actual status is already the good status (account: %d, status %d, ip: %s)." RETCODE, + server[id].name, acc, statut, ip); + break; + } + } + if (i == auth_num) { + login_log("Char-server '%s': Error of Status change (account: %d not found, suggested status %d, ip: %s)." RETCODE, + server[id].name, acc, statut, ip); + } + RFIFOSKIP(fd,10); + } + return 0; + + case 0x2725: // Receiving of map-server via char-server a ban resquest (by Yor) + if (RFIFOREST(fd) < 18) + return 0; + { + acc = RFIFOL(fd,2); + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == acc) { + time_t timestamp; + struct tm *tmtime; + if (auth_dat[i].ban_until_time == 0 || auth_dat[i].ban_until_time < time(NULL)) + timestamp = time(NULL); + else + timestamp = auth_dat[i].ban_until_time; + tmtime = localtime(×tamp); + tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,6); + tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,8); + tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,10); + tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,12); + tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,14); + tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,16); + timestamp = mktime(tmtime); + if (timestamp != -1) { + if (timestamp <= time(NULL)) + timestamp = 0; + if (auth_dat[i].ban_until_time != timestamp) { + if (timestamp != 0) { + unsigned char buf[16]; + char tmpstr[2048]; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + login_log("Char-server '%s': Ban request (account: %d, new final date of banishment: %d (%s), ip: %s)." RETCODE, + server[id].name, acc, timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip); + WBUFW(buf,0) = 0x2731; + WBUFL(buf,2) = auth_dat[i].account_id; + WBUFB(buf,6) = 1; // 0: change of statut, 1: ban + WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment + charif_sendallwos(-1, buf, 11); + for(j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == acc) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + } else { + login_log("Char-server '%s': Error of ban request (account: %d, new date unbans the account, ip: %s)." RETCODE, + server[id].name, acc, ip); + } + auth_dat[i].ban_until_time = timestamp; + // Save + mmo_auth_sync(); + } else { + login_log("Char-server '%s': Error of ban request (account: %d, no change for ban date, ip: %s)." RETCODE, + server[id].name, acc, ip); + } + } else { + login_log("Char-server '%s': Error of ban request (account: %d, invalid date, ip: %s)." RETCODE, + server[id].name, acc, ip); + } + break; + } + } + if (i == auth_num) + login_log("Char-server '%s': Error of ban request (account: %d not found, ip: %s)." RETCODE, + server[id].name, acc, ip); + RFIFOSKIP(fd,18); + } + return 0; + + case 0x2727: // Change of sex (sex is reversed) + if (RFIFOREST(fd) < 6) + return 0; + { + int sex; + acc = RFIFOL(fd,2); + for(i = 0; i < auth_num; i++) { +// printf("%d,", auth_dat[i].account_id); + if (auth_dat[i].account_id == acc) { + if (auth_dat[i].sex == 2) + login_log("Char-server '%s': Error of sex change - Server account (suggested account: %d, actual sex %d (Server), ip: %s)." RETCODE, + server[id].name, acc, auth_dat[i].sex, ip); + else { + unsigned char buf[16]; + if (auth_dat[i].sex == 0) + sex = 1; + else + sex = 0; + login_log("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s)." RETCODE, + server[id].name, acc, (sex == 2) ? 'S' : (sex ? 'M' : 'F'), ip); + for(j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == acc) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + auth_dat[i].sex = sex; + WBUFW(buf,0) = 0x2723; + WBUFL(buf,2) = acc; + WBUFB(buf,6) = sex; + charif_sendallwos(-1, buf, 7); + // Save + mmo_auth_sync(); + } + break; + } + } + if (i == auth_num) + login_log("Char-server '%s': Error of sex change (account: %d not found, sex would be reversed, ip: %s)." RETCODE, + server[id].name, acc, ip); + RFIFOSKIP(fd,6); + } + return 0; + + case 0x2728: // We receive account_reg2 from a char-server, and we send them to other map-servers. + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + int p; + acc = RFIFOL(fd,4); + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == acc) { + //unsigned char buf[rfifow(fd,2)+1]; + unsigned char *buf; + int len; + buf = (unsigned char*)aCalloc(RFIFOW(fd,2)+1, sizeof(unsigned char)); + login_log("char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s)." RETCODE, + server[id].name, acc, ip); + for(j=0,p=13;jforeach(online_db,online_db_setoffline,id); //Set all chars from this char-server offline first + users = RFIFOW(fd,4); + for (i = 0; i < users; i++) { + aid = RFIFOL(fd,6+i*4); + p = idb_ensure(online_db, aid, create_online_user); + p->char_server = id; + p->waiting_disconnect = 0; + } + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + } + case 0x272e: //Request account_reg2 for a character. + if (RFIFOREST(fd) < 10) + return 0; + { + int account_id = RFIFOL(fd, 2); + int char_id = RFIFOL(fd, 6); + int p; + RFIFOSKIP(fd,10); + WFIFOW(fd,0) = 0x2729; + WFIFOL(fd,4) = account_id; + WFIFOL(fd,8) = char_id; + WFIFOB(fd,12) = 1; //Type 1 for Account2 registry + for(i = 0; i < auth_num && auth_dat[i].account_id != account_id; i++); + if (i == auth_num) { + //Account not found? Send at least empty data, map servers need a reply! + WFIFOW(fd,2) = 13; + WFIFOSET(fd,WFIFOW(fd,2)); + break; + } + for(p = 13,j=0;jforeach(online_db,online_db_setoffline,id); + RFIFOSKIP(fd,2); + break; + + case 0x3000: //change sex for chrif_changesex() + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + unsigned int sex; + acc = RFIFOL(fd,4); + sex = RFIFOB(fd,8); + if (sex != 0 && sex != 1) + sex = 0; + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == acc) { + unsigned char buf[16]; + login_log("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s)." RETCODE, + server[id].name, acc, (sex == 2) ? 'S' : (sex ? 'M' : 'F'), ip); + auth_fifo[i].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + auth_dat[i].sex = sex; + WBUFW(buf,0) = 0x2723; + WBUFL(buf,2) = acc; + WBUFB(buf,6) = sex; + charif_sendallwos(-1, buf, 7); + break; + } + } + if (i == auth_num) { + login_log("Char-server '%s': Error of Sex change (account: %d not found, suggested sex %c, ip: %s)." RETCODE, + server[id].name, acc, (sex == 2) ? 'S' : (sex ? 'M' : 'F'), ip); + } + RFIFOSKIP(fd,RFIFOW(fd,2)); + } + return 0; + + default: + { + FILE *logfp; + char tmpstr[24]; + time_t raw_time; + logfp = fopen(login_log_unknown_packets_filename, "a"); + if (logfp) { + time(&raw_time); + strftime(tmpstr, 23, date_format, localtime(&raw_time)); + fprintf(logfp, "%s: receiving of an unknown packet -> disconnection" RETCODE, tmpstr); + fprintf(logfp, "parse_fromchar: connection #%d (ip: %s), packet: 0x%x (with being read: %d)." RETCODE, fd, ip, RFIFOW(fd,0), RFIFOREST(fd)); + fprintf(logfp, "Detail (in hex):" RETCODE); + fprintf(logfp, "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F" RETCODE); + memset(tmpstr, '\0', sizeof(tmpstr)); + for(i = 0; i < RFIFOREST(fd); i++) { + if ((i & 15) == 0) + fprintf(logfp, "%04X ",i); + fprintf(logfp, "%02x ", RFIFOB(fd,i)); + if (RFIFOB(fd,i) > 0x1f) + tmpstr[i % 16] = RFIFOB(fd,i); + else + tmpstr[i % 16] = '.'; + if ((i - 7) % 16 == 0) // -8 + 1 + fprintf(logfp, " "); + else if ((i + 1) % 16 == 0) { + fprintf(logfp, " %s" RETCODE, tmpstr); + memset(tmpstr, '\0', sizeof(tmpstr)); + } + } + if (i % 16 != 0) { + for(j = i; j % 16 != 0; j++) { + fprintf(logfp, " "); + if ((j - 7) % 16 == 0) // -8 + 1 + fprintf(logfp, " "); + } + fprintf(logfp, " %s" RETCODE, tmpstr); + } + fprintf(logfp, RETCODE); + fclose(logfp); + } + } + ShowWarning("parse_fromchar: Unknown packet 0x%x (from a char-server)! -> disconnection.\n", RFIFOW(fd,0)); + session[fd]->eof = 1; + ShowStatus("Char-server has been disconnected (unknown packet).\n"); + return 0; + } + } + RFIFOSKIP(fd,RFIFOREST(fd)); + return 0; +} + +//--------------------------------------- +// Packet parsing for administation login +//--------------------------------------- +int parse_admin(int fd) { + unsigned int i, j; + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; + char* account_name; + char ip[16]; + RFIFOHEAD(fd); + + sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + if (session[fd]->eof) { + do_close(fd); + ShowInfo("Remote administration has disconnected (session #%d).\n", fd); + return 0; + } + + while(RFIFOREST(fd) >= 2) { + if (display_parse_admin == 1) { + + ShowDebug("parse_admin: connection #%d, packet: 0x%x (with being read: %d).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + } + + switch(RFIFOW(fd,0)) { + case 0x7530: // Request of the server version + login_log("'ladmin': Sending of the server version (ip: %s)" RETCODE, ip); + WFIFOHEAD(fd, 10); + WFIFOW(fd,0) = 0x7531; + WFIFOB(fd,2) = ATHENA_MAJOR_VERSION; + WFIFOB(fd,3) = ATHENA_MINOR_VERSION; + WFIFOB(fd,4) = ATHENA_REVISION; + WFIFOB(fd,5) = ATHENA_RELEASE_FLAG; + WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG; + WFIFOB(fd,7) = ATHENA_SERVER_LOGIN; + WFIFOW(fd,8) = ATHENA_MOD_VERSION; + WFIFOSET(fd,10); + RFIFOSKIP(fd,2); + break; + + case 0x7532: // Request of end of connection + login_log("'ladmin': End of connection (ip: %s)" RETCODE, ip); + RFIFOSKIP(fd,2); + session[fd]->eof = 1; + break; + + case 0x7920: // Request of an accounts list + if (RFIFOREST(fd) < 10) + return 0; + { + int st, ed, len; + //int id[auth_num]; + //int *id=(int *)aCalloc(auth_num, sizeof(int)); + CREATE_BUFFER(id, int, auth_num); + st = RFIFOL(fd,2); + ed = RFIFOL(fd,6); + RFIFOSKIP(fd,10); + WFIFOW(fd,0) = 0x7921; + if (st < 0) + st = 0; + if (ed > END_ACCOUNT_NUM || ed < st || ed <= 0) + ed = END_ACCOUNT_NUM; + login_log("'ladmin': Sending an accounts list (ask: from %d to %d, ip: %s)" RETCODE, st, ed, ip); + // Sort before send + for(i = 0; i < auth_num; i++) { + unsigned int k; + id[i] = i; + for(j = 0; j < i; j++) { + if (auth_dat[id[i]].account_id < auth_dat[id[j]].account_id) { + for(k = i; k > j; k--) { + id[k] = id[k-1]; + } + id[j] = i; // id[i] + break; + } + } + } + // Sending accounts information + len = 4; + for(i = 0; i < auth_num && len < 30000; i++) { + int account_id = auth_dat[id[i]].account_id; // use sorted index + if (account_id >= st && account_id <= ed) { + j = id[i]; + WFIFOL(fd,len) = account_id; + WFIFOB(fd,len+4) = (unsigned char)isGM(account_id); + memcpy(WFIFOP(fd,len+5), auth_dat[j].userid, 24); + WFIFOB(fd,len+29) = auth_dat[j].sex; + WFIFOL(fd,len+30) = auth_dat[j].logincount; + if (auth_dat[j].state == 0 && auth_dat[j].ban_until_time != 0) // if no state and banished + WFIFOL(fd,len+34) = 7; // 6 = Your are Prohibited to log in until %s + else + WFIFOL(fd,len+34) = auth_dat[j].state; + len += 38; + } + } + WFIFOW(fd,2) = len; + WFIFOSET(fd,len); + //if (id) free(id); + DELETE_BUFFER(id); + } + break; + + case 0x7930: // Request for an account creation + if (RFIFOREST(fd) < 91) + return 0; + { + struct mmo_account ma; + memcpy(ma.userid,RFIFOP(fd, 2),NAME_LENGTH); + ma.userid[23] = '\0'; + memcpy(ma.passwd, RFIFOP(fd, 26), NAME_LENGTH); + ma.passwd[23] = '\0'; + memcpy(ma.lastlogin, "-", 2); + ma.sex = RFIFOB(fd,50); + WFIFOW(fd,0) = 0x7931; + WFIFOL(fd,2) = 0xffffffff; + memcpy(WFIFOP(fd,6), RFIFOP(fd,2), 24); + if (strlen(ma.userid) < 4 || strlen(ma.passwd) < 4) { + login_log("'ladmin': Attempt to create an invalid account (account or pass is too short, ip: %s)" RETCODE, + ip); + } else if (ma.sex != 'F' && ma.sex != 'M') { + login_log("'ladmin': Attempt to create an invalid account (account: %s, received pass: %s, invalid sex, ip: %s)" RETCODE, + ma.userid, ma.passwd, ip); + } else if (account_id_count > END_ACCOUNT_NUM) { + login_log("'ladmin': Attempt to create an account, but there is no more available id number (account: %s, pass: %s, sex: %c, ip: %s)" RETCODE, + ma.userid, ma.passwd, ma.sex, ip); + } else { + remove_control_chars((unsigned char *)ma.userid); + remove_control_chars((unsigned char *)ma.passwd); + for(i = 0; i < auth_num; i++) { + if (strncmp(auth_dat[i].userid, ma.userid, 24) == 0) { + login_log("'ladmin': Attempt to create an already existing account (account: %s, pass: %s, received pass: %s, ip: %s)" RETCODE, + auth_dat[i].userid, auth_dat[i].pass, ma.passwd, ip); + break; + } + } + if (i == auth_num) { + int new_id; + char email[40]; + memcpy(email, RFIFOP(fd,51), 40); + email[39] = '\0'; + remove_control_chars((unsigned char *)email); + new_id = mmo_auth_new(&ma, ma.sex, email); + login_log("'ladmin': Account creation (account: %s (id: %d), pass: %s, sex: %c, email: %s, ip: %s)" RETCODE, + ma.userid, new_id, ma.passwd, ma.sex, auth_dat[i].email, ip); + WFIFOL(fd,2) = new_id; + mmo_auth_sync(); + } + } + WFIFOSET(fd,30); + RFIFOSKIP(fd,91); + } + break; + + case 0x7932: // Request for an account deletion + if (RFIFOREST(fd) < 26) + return 0; + WFIFOW(fd,0) = 0x7933; + WFIFOL(fd,2) = 0xFFFFFFFF; + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + i = search_account_index(account_name); + if (i != -1) { + // Char-server is notified of deletion (for characters deletion). + unsigned char buf[65535]; + WBUFW(buf,0) = 0x2730; + WBUFL(buf,2) = auth_dat[i].account_id; + charif_sendallwos(-1, buf, 6); + // send answer + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + WFIFOL(fd,2) = auth_dat[i].account_id; + // save deleted account in log file + login_log("'ladmin': Account deletion (account: %s, id: %d, ip: %s) - saved in next line:" RETCODE, + auth_dat[i].userid, auth_dat[i].account_id, ip); + mmo_auth_tostr((char*)buf, &auth_dat[i]); + login_log("%s" RETCODE, buf); + // delete account + memset(auth_dat[i].userid, '\0', sizeof(auth_dat[i].userid)); + auth_dat[i].account_id = -1; + mmo_auth_sync(); + } else { + memcpy(WFIFOP(fd,6), account_name, 24); + login_log("'ladmin': Attempt to delete an unknown account (account: %s, ip: %s)" RETCODE, + account_name, ip); + } + WFIFOSET(fd,30); + RFIFOSKIP(fd,26); + break; + + case 0x7934: // Request to change a password + if (RFIFOREST(fd) < 50) + return 0; + WFIFOW(fd,0) = 0x7935; + WFIFOL(fd,2) = 0xFFFFFFFF; /// WTF??? an unsigned being set to a -1 + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + memcpy(auth_dat[i].pass, RFIFOP(fd,26), 24); + auth_dat[i].pass[23] = '\0'; + remove_control_chars((unsigned char *)auth_dat[i].pass); + WFIFOL(fd,2) = auth_dat[i].account_id; + login_log("'ladmin': Modification of a password (account: %s, new password: %s, ip: %s)" RETCODE, + auth_dat[i].userid, auth_dat[i].pass, ip); + mmo_auth_sync(); + } else { + memcpy(WFIFOP(fd,6), account_name, 24); + login_log("'ladmin': Attempt to modify the password of an unknown account (account: %s, ip: %s)" RETCODE, + account_name, ip); + } + WFIFOSET(fd,30); + RFIFOSKIP(fd,50); + break; + + case 0x7936: // Request to modify a state + if (RFIFOREST(fd) < 50) + return 0; + { + char error_message[20]; + int statut; + WFIFOW(fd,0) = 0x7937; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + statut = RFIFOL(fd,26); + memcpy(error_message, RFIFOP(fd,30), 20); + error_message[19] = '\0'; + remove_control_chars((unsigned char *)error_message); + if (statut != 7 || error_message[0] == '\0') { // 7: // 6 = Your are Prohibited to log in until %s + strcpy(error_message, "-"); + } + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + WFIFOL(fd,2) = auth_dat[i].account_id; + if (auth_dat[i].state == statut && strcmp(auth_dat[i].error_message, error_message) == 0) + login_log("'ladmin': Modification of a state, but the state of the account is already the good state (account: %s, received state: %d, ip: %s)" RETCODE, + account_name, statut, ip); + else { + if (statut == 7) + login_log("'ladmin': Modification of a state (account: %s, new state: %d - prohibited to login until '%s', ip: %s)" RETCODE, + auth_dat[i].userid, statut, error_message, ip); + else + login_log("'ladmin': Modification of a state (account: %s, new state: %d, ip: %s)" RETCODE, + auth_dat[i].userid, statut, ip); + if (auth_dat[i].state == 0) { + unsigned char buf[16]; + WBUFW(buf,0) = 0x2731; + WBUFL(buf,2) = auth_dat[i].account_id; + WBUFB(buf,6) = 0; // 0: change of statut, 1: ban + WBUFL(buf,7) = statut; // status or final date of a banishment + charif_sendallwos(-1, buf, 11); + for(j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == auth_dat[i].account_id) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + } + auth_dat[i].state = statut; + memcpy(auth_dat[i].error_message, error_message, 20); + mmo_auth_sync(); + } + } else { + memcpy(WFIFOP(fd,6), account_name, 24); + login_log("'ladmin': Attempt to modify the state of an unknown account (account: %s, received state: %d, ip: %s)" RETCODE, + account_name, statut, ip); + } + WFIFOL(fd,30) = statut; + } + WFIFOSET(fd,34); + RFIFOSKIP(fd,50); + break; + + case 0x7938: // Request for servers list and # of online players + login_log("'ladmin': Sending of servers list (ip: %s)" RETCODE, ip); + server_num = 0; + for(i = 0; i < MAX_SERVERS; i++) { + if (server_fd[i] >= 0) { + WFIFOL(fd,4+server_num*32) = server[i].ip; + WFIFOW(fd,4+server_num*32+4) = server[i].port; + memcpy(WFIFOP(fd,4+server_num*32+6), server[i].name, 20); + WFIFOW(fd,4+server_num*32+26) = server[i].users; + WFIFOW(fd,4+server_num*32+28) = server[i].maintenance; + WFIFOW(fd,4+server_num*32+30) = server[i].new_; + server_num++; + } + } + WFIFOW(fd,0) = 0x7939; + WFIFOW(fd,2) = 4 + 32 * server_num; + WFIFOSET(fd,4+32*server_num); + RFIFOSKIP(fd,2); + break; + + case 0x793a: // Request to password check + if (RFIFOREST(fd) < 50) + return 0; + WFIFOW(fd,0) = 0x793b; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + i = search_account_index(account_name); + if (i != -1) { + char pass[25]; + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + memcpy(pass, RFIFOP(fd,26), 24); + pass[24] = '\0'; + remove_control_chars((unsigned char *)pass); + if (strcmp(auth_dat[i].pass, pass) == 0) { + WFIFOL(fd,2) = auth_dat[i].account_id; + login_log("'ladmin': Check of password OK (account: %s, password: %s, ip: %s)" RETCODE, + auth_dat[i].userid, auth_dat[i].pass, ip); + } else { + login_log("'ladmin': Failure of password check (account: %s, proposed pass: %s, ip: %s)" RETCODE, + auth_dat[i].userid, pass, ip); + } + } else { + memcpy(WFIFOP(fd,6), account_name, 24); + login_log("'ladmin': Attempt to check the password of an unknown account (account: %s, ip: %s)" RETCODE, + account_name, ip); + } + WFIFOSET(fd,30); + RFIFOSKIP(fd,50); + break; + + case 0x793c: // Request to modify sex + if (RFIFOREST(fd) < 27) + return 0; + WFIFOW(fd,0) = 0x793d; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + memcpy(WFIFOP(fd,6), account_name, 24); + { + char sex; + sex = RFIFOB(fd,26); + if (sex != 'F' && sex != 'M') { + if (sex > 31) + login_log("'ladmin': Attempt to give an invalid sex (account: %s, received sex: %c, ip: %s)" RETCODE, + account_name, sex, ip); + else + login_log("'ladmin': Attempt to give an invalid sex (account: %s, received sex: 'control char', ip: %s)" RETCODE, + account_name, ip); + } else { + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + if (auth_dat[i].sex != ((sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'))) { + unsigned char buf[16]; + WFIFOL(fd,2) = auth_dat[i].account_id; + for(j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == auth_dat[i].account_id) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + auth_dat[i].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); + login_log("'ladmin': Modification of a sex (account: %s, new sex: %c, ip: %s)" RETCODE, + auth_dat[i].userid, sex, ip); + mmo_auth_sync(); + // send to all char-server the change + WBUFW(buf,0) = 0x2723; + WBUFL(buf,2) = auth_dat[i].account_id; + WBUFB(buf,6) = auth_dat[i].sex; + charif_sendallwos(-1, buf, 7); + } else { + login_log("'ladmin': Modification of a sex, but the sex is already the good sex (account: %s, sex: %c, ip: %s)" RETCODE, + auth_dat[i].userid, sex, ip); + } + } else { + login_log("'ladmin': Attempt to modify the sex of an unknown account (account: %s, received sex: %c, ip: %s)" RETCODE, + account_name, sex, ip); + } + } + } + WFIFOSET(fd,30); + RFIFOSKIP(fd,27); + break; + + case 0x793e: // Request to modify GM level + if (RFIFOREST(fd) < 27) + return 0; + WFIFOW(fd,0) = 0x793f; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + memcpy(WFIFOP(fd,6), account_name, 24); + { + char new_gm_level; + new_gm_level = RFIFOB(fd,26); + if (new_gm_level < 0 || new_gm_level > 99) { + login_log("'ladmin': Attempt to give an invalid GM level (account: %s, received GM level: %d, ip: %s)" RETCODE, + account_name, (int)new_gm_level, ip); + } else { + i = search_account_index(account_name); + if (i != -1) { + int acc = auth_dat[i].account_id; + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + if (isGM(acc) != new_gm_level) { + // modification of the file + FILE *fp, *fp2; + int lock; + char line[512]; + int GM_account, GM_level; + int modify_flag; + char tmpstr[24]; + time_t raw_time; + if ((fp2 = lock_fopen(GM_account_filename, &lock)) != NULL) { + if ((fp = fopen(GM_account_filename, "r")) != NULL) { + time(&raw_time); + strftime(tmpstr, 23, date_format, localtime(&raw_time)); + modify_flag = 0; + // read/write GM file + while(fgets(line, sizeof(line)-1, fp)) { + while(line[0] != '\0' && (line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r')) + line[strlen(line)-1] = '\0'; + if ((line[0] == '/' && line[1] == '/') || line[0] == '\0') + fprintf(fp2, "%s" RETCODE, line); + else { + if (sscanf(line, "%d %d", &GM_account, &GM_level) != 2 && sscanf(line, "%d: %d", &GM_account, &GM_level) != 2) + fprintf(fp2, "%s" RETCODE, line); + else if (GM_account != acc) + fprintf(fp2, "%s" RETCODE, line); + else if (new_gm_level < 1) { + fprintf(fp2, "// %s: 'ladmin' GM level removed on account %d '%s' (previous level: %d)" RETCODE "//%d %d" RETCODE, tmpstr, acc, auth_dat[i].userid, GM_level, acc, new_gm_level); + modify_flag = 1; + } else { + fprintf(fp2, "// %s: 'ladmin' GM level on account %d '%s' (previous level: %d)" RETCODE "%d %d" RETCODE, tmpstr, acc, auth_dat[i].userid, GM_level, acc, new_gm_level); + modify_flag = 1; + } + } + } + if (modify_flag == 0) + fprintf(fp2, "// %s: 'ladmin' GM level on account %d '%s' (previous level: 0)" RETCODE "%d %d" RETCODE, tmpstr, acc, auth_dat[i].userid, acc, new_gm_level); + fclose(fp); + } else { + login_log("'ladmin': Attempt to modify of a GM level - impossible to read GM accounts file (account: %s (%d), received GM level: %d, ip: %s)" RETCODE, + auth_dat[i].userid, acc, (int)new_gm_level, ip); + } + if (lock_fclose(fp2, GM_account_filename, &lock) == 0) { + WFIFOL(fd,2) = acc; + login_log("'ladmin': Modification of a GM level (account: %s (%d), new GM level: %d, ip: %s)" RETCODE, + auth_dat[i].userid, acc, (int)new_gm_level, ip); + // read and send new GM informations + read_gm_account(); + send_GM_accounts(); + } else { + login_log("'ladmin': Attempt to modify of a GM level - impossible to write GM accounts file (account: %s (%d), received GM level: %d, ip: %s)" RETCODE, + auth_dat[i].userid, acc, (int)new_gm_level, ip); + } + } else { + login_log("'ladmin': Attempt to modify of a GM level - impossible to write GM accounts file (account: %s (%d), received GM level: %d, ip: %s)" RETCODE, + auth_dat[i].userid, acc, (int)new_gm_level, ip); + } + } else { + login_log("'ladmin': Attempt to modify of a GM level, but the GM level is already the good GM level (account: %s (%d), GM level: %d, ip: %s)" RETCODE, + auth_dat[i].userid, acc, (int)new_gm_level, ip); + } + } else { + login_log("'ladmin': Attempt to modify the GM level of an unknown account (account: %s, received GM level: %d, ip: %s)" RETCODE, + account_name, (int)new_gm_level, ip); + } + } + } + WFIFOSET(fd,30); + RFIFOSKIP(fd,27); + break; + + case 0x7940: // Request to modify e-mail + if (RFIFOREST(fd) < 66) + return 0; + WFIFOW(fd,0) = 0x7941; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + memcpy(WFIFOP(fd,6), account_name, 24); + { + char email[40]; + memcpy(email, RFIFOP(fd,26), 40); + if (e_mail_check(email) == 0) { + login_log("'ladmin': Attempt to give an invalid e-mail (account: %s, ip: %s)" RETCODE, + account_name, ip); + } else { + remove_control_chars((unsigned char *)email); + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + memcpy(auth_dat[i].email, email, 40); + WFIFOL(fd,2) = auth_dat[i].account_id; + login_log("'ladmin': Modification of an email (account: %s, new e-mail: %s, ip: %s)" RETCODE, + auth_dat[i].userid, email, ip); + mmo_auth_sync(); + } else { + login_log("'ladmin': Attempt to modify the e-mail of an unknown account (account: %s, received e-mail: %s, ip: %s)" RETCODE, + account_name, email, ip); + } + } + } + WFIFOSET(fd,30); + RFIFOSKIP(fd,66); + break; + + case 0x7942: // Request to modify memo field + if ((int)RFIFOREST(fd) < 28 || (int)RFIFOREST(fd) < (28 + RFIFOW(fd,26))) + return 0; + WFIFOW(fd,0) = 0x7943; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + i = search_account_index(account_name); + if (i != -1) { + int size_of_memo = sizeof(auth_dat[i].memo); + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + memset(auth_dat[i].memo, '\0', size_of_memo); + if (RFIFOW(fd,26) == 0) { + strncpy(auth_dat[i].memo, "-", size_of_memo); + } else if (RFIFOW(fd,26) > size_of_memo - 1) { + memcpy(auth_dat[i].memo, RFIFOP(fd,28), size_of_memo - 1); + } else { + memcpy(auth_dat[i].memo, RFIFOP(fd,28), RFIFOW(fd,26)); + } + auth_dat[i].memo[size_of_memo - 1] = '\0'; + remove_control_chars((unsigned char *)auth_dat[i].memo); + WFIFOL(fd,2) = auth_dat[i].account_id; + login_log("'ladmin': Modification of a memo field (account: %s, new memo: %s, ip: %s)" RETCODE, + auth_dat[i].userid, auth_dat[i].memo, ip); + mmo_auth_sync(); + } else { + memcpy(WFIFOP(fd,6), account_name, 24); + login_log("'ladmin': Attempt to modify the memo field of an unknown account (account: %s, ip: %s)" RETCODE, + account_name, ip); + } + WFIFOSET(fd,30); + RFIFOSKIP(fd,28 + RFIFOW(fd,26)); + break; + + case 0x7944: // Request to found an account id + if (RFIFOREST(fd) < 26) + return 0; + WFIFOW(fd,0) = 0x7945; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + WFIFOL(fd,2) = auth_dat[i].account_id; + login_log("'ladmin': Request (by the name) of an account id (account: %s, id: %d, ip: %s)" RETCODE, + auth_dat[i].userid, auth_dat[i].account_id, ip); + } else { + memcpy(WFIFOP(fd,6), account_name, 24); + login_log("'ladmin': ID request (by the name) of an unknown account (account: %s, ip: %s)" RETCODE, + account_name, ip); + } + WFIFOSET(fd,30); + RFIFOSKIP(fd,26); + break; + + case 0x7946: // Request to found an account name + if (RFIFOREST(fd) < 6) + return 0; + WFIFOW(fd,0) = 0x7947; + WFIFOL(fd,2) = RFIFOL(fd,2); + memset(WFIFOP(fd,6), '\0', 24); + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == RFIFOL(fd,2)) { + strncpy((char*)WFIFOP(fd,6), auth_dat[i].userid, 24); + login_log("'ladmin': Request (by id) of an account name (account: %s, id: %d, ip: %s)" RETCODE, + auth_dat[i].userid, RFIFOL(fd,2), ip); + break; + } + } + if (i == auth_num) { + login_log("'ladmin': Name request (by id) of an unknown account (id: %d, ip: %s)" RETCODE, + RFIFOL(fd,2), ip); + strncpy((char*)WFIFOP(fd,6), "", 24); + } + WFIFOSET(fd,30); + RFIFOSKIP(fd,6); + break; + + case 0x7948: // Request to change the validity limit (timestamp) (absolute value) + if (RFIFOREST(fd) < 30) + return 0; + { + time_t timestamp; + char tmpstr[2048]; + WFIFOW(fd,0) = 0x7949; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + timestamp = (time_t)RFIFOL(fd,26); + strftime(tmpstr, 24, date_format, localtime(×tamp)); + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + login_log("'ladmin': Change of a validity limit (account: %s, new validity: %d (%s), ip: %s)" RETCODE, + auth_dat[i].userid, timestamp, (timestamp == 0 ? "unlimited" : tmpstr), ip); + auth_dat[i].connect_until_time = timestamp; + WFIFOL(fd,2) = auth_dat[i].account_id; + mmo_auth_sync(); + } else { + memcpy(WFIFOP(fd,6), account_name, 24); + login_log("'ladmin': Attempt to change the validity limit of an unknown account (account: %s, received validity: %d (%s), ip: %s)" RETCODE, + account_name, timestamp, (timestamp == 0 ? "unlimited" : tmpstr), ip); + } + WFIFOL(fd,30) = (unsigned int)timestamp; + } + WFIFOSET(fd,34); + RFIFOSKIP(fd,30); + break; + + case 0x794a: // Request to change the final date of a banishment (timestamp) (absolute value) + if (RFIFOREST(fd) < 30) + return 0; + { + time_t timestamp; + char tmpstr[2048]; + WFIFOW(fd,0) = 0x794b; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + timestamp = (time_t)RFIFOL(fd,26); + if (timestamp <= time(NULL)) + timestamp = 0; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + WFIFOL(fd,2) = auth_dat[i].account_id; + login_log("'ladmin': Change of the final date of a banishment (account: %s, new final date of banishment: %d (%s), ip: %s)" RETCODE, + auth_dat[i].userid, timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip); + if (auth_dat[i].ban_until_time != timestamp) { + if (timestamp != 0) { + unsigned char buf[16]; + WBUFW(buf,0) = 0x2731; + WBUFL(buf,2) = auth_dat[i].account_id; + WBUFB(buf,6) = 1; // 0: change of statut, 1: ban + WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment + charif_sendallwos(-1, buf, 11); + for(j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == auth_dat[i].account_id) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + } + auth_dat[i].ban_until_time = timestamp; + mmo_auth_sync(); + } + } else { + memcpy(WFIFOP(fd,6), account_name, 24); + login_log("'ladmin': Attempt to change the final date of a banishment of an unknown account (account: %s, received final date of banishment: %d (%s), ip: %s)" RETCODE, + account_name, timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip); + } + WFIFOL(fd,30) = (unsigned int)timestamp; + } + WFIFOSET(fd,34); + RFIFOSKIP(fd,30); + break; + + case 0x794c: // Request to change the final date of a banishment (timestamp) (relative change) + if (RFIFOREST(fd) < 38) + return 0; + { + time_t timestamp; + struct tm *tmtime; + char tmpstr[2048]; + WFIFOW(fd,0) = 0x794d; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + i = search_account_index(account_name); + if (i != -1) { + WFIFOL(fd,2) = auth_dat[i].account_id; + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + if (auth_dat[i].ban_until_time == 0 || auth_dat[i].ban_until_time < time(NULL)) + timestamp = time(NULL); + else + timestamp = auth_dat[i].ban_until_time; + tmtime = localtime(×tamp); + tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,26); + tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,28); + tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,30); + tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,32); + tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,34); + tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,36); + timestamp = mktime(tmtime); + if (timestamp != -1) { + if (timestamp <= time(NULL)) + timestamp = 0; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + login_log("'ladmin': Adjustment of a final date of a banishment (account: %s, (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)" RETCODE, + auth_dat[i].userid, (short)RFIFOW(fd,26), (short)RFIFOW(fd,28), (short)RFIFOW(fd,30), (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip); + if (auth_dat[i].ban_until_time != timestamp) { + if (timestamp != 0) { + unsigned char buf[16]; + WBUFW(buf,0) = 0x2731; + WBUFL(buf,2) = auth_dat[i].account_id; + WBUFB(buf,6) = 1; // 0: change of statut, 1: ban + WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment + charif_sendallwos(-1, buf, 11); + for(j = 0; j < AUTH_FIFO_SIZE; j++) + if (auth_fifo[j].account_id == auth_dat[i].account_id) + auth_fifo[j].login_id1++; // to avoid reconnection error when come back from map-server (char-server will ask again the authentification) + } + auth_dat[i].ban_until_time = timestamp; + mmo_auth_sync(); + } + } else { + strftime(tmpstr, 24, date_format, localtime(&auth_dat[i].ban_until_time)); + login_log("'ladmin': Impossible to adjust the final date of a banishment (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)" RETCODE, + auth_dat[i].userid, auth_dat[i].ban_until_time, (auth_dat[i].ban_until_time == 0 ? "no banishment" : tmpstr), (short)RFIFOW(fd,26), (short)RFIFOW(fd,28), (short)RFIFOW(fd,30), (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), ip); + } + WFIFOL(fd,30) = (unsigned long)auth_dat[i].ban_until_time; + } else { + memcpy(WFIFOP(fd,6), account_name, 24); + login_log("'ladmin': Attempt to adjust the final date of a banishment of an unknown account (account: %s, ip: %s)" RETCODE, + account_name, ip); + WFIFOL(fd,30) = 0; + } + } + WFIFOSET(fd,34); + RFIFOSKIP(fd,38); + break; + + case 0x794e: // Request to send a broadcast message + if (RFIFOREST(fd) < 8 || RFIFOREST(fd) < (8 + RFIFOL(fd,4))) + return 0; + WFIFOW(fd,0) = 0x794f; + WFIFOW(fd,2) = 0xFFFF; // WTF??? + if (RFIFOL(fd,4) < 1) { + login_log("'ladmin': Receiving a message for broadcast, but message is void (ip: %s)" RETCODE, + ip); + } else { + // at least 1 char-server + for(i = 0; i < MAX_SERVERS; i++) + if (server_fd[i] >= 0) + break; + if (i == MAX_SERVERS) { + login_log("'ladmin': Receiving a message for broadcast, but no char-server is online (ip: %s)" RETCODE, + ip); + } else { + unsigned char buf[32000]; + char message[32000]; + WFIFOW(fd,2) = 0; + memset(message, '\0', sizeof(message)); + memcpy(message, RFIFOP(fd,8), RFIFOL(fd,4)); + message[sizeof(message)-1] = '\0'; + remove_control_chars((unsigned char *)message); + if (RFIFOW(fd,2) == 0) + login_log("'ladmin': Receiving a message for broadcast (message (in yellow): %s, ip: %s)" RETCODE, + message, ip); + else + login_log("'ladmin': Receiving a message for broadcast (message (in blue): %s, ip: %s)" RETCODE, + message, ip); + // send same message to all char-servers (no answer) + memcpy(WBUFP(buf,0), RFIFOP(fd,0), 8 + RFIFOL(fd,4)); + WBUFW(buf,0) = 0x2726; + charif_sendallwos(-1, buf, 8 + RFIFOL(fd,4)); + } + } + WFIFOSET(fd,4); + RFIFOSKIP(fd,8 + RFIFOL(fd,4)); + break; + + case 0x7950: // Request to change the validity limite (timestamp) (relative change) + if (RFIFOREST(fd) < 38) + return 0; + { + time_t timestamp; + struct tm *tmtime; + char tmpstr[2048]; + char tmpstr2[2048]; + WFIFOW(fd,0) = 0x7951; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + i = search_account_index(account_name); + if (i != -1) { + WFIFOL(fd,2) = auth_dat[i].account_id; + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + timestamp = auth_dat[i].connect_until_time; + if (add_to_unlimited_account == 0 && timestamp == 0) { + login_log("'ladmin': Attempt to adjust the validity limit of an unlimited account (account: %s, ip: %s)" RETCODE, + auth_dat[i].userid, ip); + WFIFOL(fd,30) = 0; + } else { + if (timestamp == 0 || timestamp < time(NULL)) + timestamp = time(NULL); + tmtime = localtime(×tamp); + tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,26); + tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,28); + tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,30); + tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,32); + tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,34); + tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,36); + timestamp = mktime(tmtime); + if (timestamp != -1) { + strftime(tmpstr, 24, date_format, localtime(&auth_dat[i].connect_until_time)); + strftime(tmpstr2, 24, date_format, localtime(×tamp)); + login_log("'ladmin': Adjustment of a validity limit (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)" RETCODE, + auth_dat[i].userid, auth_dat[i].connect_until_time, (auth_dat[i].connect_until_time == 0 ? "unlimited" : tmpstr), (short)RFIFOW(fd,26), (short)RFIFOW(fd,28), (short)RFIFOW(fd,30), (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), timestamp, (timestamp == 0 ? "unlimited" : tmpstr2), ip); + auth_dat[i].connect_until_time = timestamp; + mmo_auth_sync(); + WFIFOL(fd,30) = (unsigned long)auth_dat[i].connect_until_time; + } else { + strftime(tmpstr, 24, date_format, localtime(&auth_dat[i].connect_until_time)); + login_log("'ladmin': Impossible to adjust a validity limit (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)" RETCODE, + auth_dat[i].userid, auth_dat[i].connect_until_time, (auth_dat[i].connect_until_time == 0 ? "unlimited" : tmpstr), (short)RFIFOW(fd,26), (short)RFIFOW(fd,28), (short)RFIFOW(fd,30), (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), ip); + WFIFOL(fd,30) = 0; + } + } + } else { + memcpy(WFIFOP(fd,6), account_name, 24); + login_log("'ladmin': Attempt to adjust the validity limit of an unknown account (account: %s, ip: %s)" RETCODE, + account_name, ip); + WFIFOL(fd,30) = 0; + } + } + WFIFOSET(fd,34); + RFIFOSKIP(fd,38); + break; + + case 0x7952: // Request about informations of an account (by account name) + if (RFIFOREST(fd) < 26) + return 0; + WFIFOW(fd,0) = 0x7953; + WFIFOL(fd,2) = 0xFFFFFFFF; // WTF??? + account_name = (char*)RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars((unsigned char *)account_name); + i = search_account_index(account_name); + if (i != -1) { + WFIFOL(fd,2) = auth_dat[i].account_id; + WFIFOB(fd,6) = (unsigned char)isGM(auth_dat[i].account_id); + memcpy(WFIFOP(fd,7), auth_dat[i].userid, 24); + WFIFOB(fd,31) = auth_dat[i].sex; + WFIFOL(fd,32) = auth_dat[i].logincount; + WFIFOL(fd,36) = auth_dat[i].state; + memcpy(WFIFOP(fd,40), auth_dat[i].error_message, 20); + memcpy(WFIFOP(fd,60), auth_dat[i].lastlogin, 24); + memcpy(WFIFOP(fd,84), auth_dat[i].last_ip, 16); + memcpy(WFIFOP(fd,100), auth_dat[i].email, 40); + WFIFOL(fd,140) = (unsigned long)auth_dat[i].connect_until_time; + WFIFOL(fd,144) = (unsigned long)auth_dat[i].ban_until_time; + WFIFOW(fd,148) = strlen(auth_dat[i].memo); + if (auth_dat[i].memo[0]) { + memcpy(WFIFOP(fd,150), auth_dat[i].memo, strlen(auth_dat[i].memo)); + } + login_log("'ladmin': Sending information of an account (request by the name; account: %s, id: %d, ip: %s)" RETCODE, + auth_dat[i].userid, auth_dat[i].account_id, ip); + WFIFOSET(fd,150+strlen(auth_dat[i].memo)); + } else { + memcpy(WFIFOP(fd,7), account_name, 24); + WFIFOW(fd,148) = 0; + login_log("'ladmin': Attempt to obtain information (by the name) of an unknown account (account: %s, ip: %s)" RETCODE, + account_name, ip); + WFIFOSET(fd,150); + } + RFIFOSKIP(fd,26); + break; + + case 0x7954: // Request about information of an account (by account id) + if (RFIFOREST(fd) < 6) + return 0; + WFIFOW(fd,0) = 0x7953; + WFIFOL(fd,2) = RFIFOL(fd,2); + memset(WFIFOP(fd,7), '\0', 24); + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == RFIFOL(fd,2)) { + login_log("'ladmin': Sending information of an account (request by the id; account: %s, id: %d, ip: %s)" RETCODE, + auth_dat[i].userid, RFIFOL(fd,2), ip); + WFIFOB(fd,6) = (unsigned char)isGM(auth_dat[i].account_id); + memcpy(WFIFOP(fd,7), auth_dat[i].userid, 24); + WFIFOB(fd,31) = auth_dat[i].sex; + WFIFOL(fd,32) = auth_dat[i].logincount; + WFIFOL(fd,36) = auth_dat[i].state; + memcpy(WFIFOP(fd,40), auth_dat[i].error_message, 20); + memcpy(WFIFOP(fd,60), auth_dat[i].lastlogin, 24); + memcpy(WFIFOP(fd,84), auth_dat[i].last_ip, 16); + memcpy(WFIFOP(fd,100), auth_dat[i].email, 40); + WFIFOL(fd,140) = (unsigned long)auth_dat[i].connect_until_time; + WFIFOL(fd,144) = (unsigned long)auth_dat[i].ban_until_time; + WFIFOW(fd,148) = strlen(auth_dat[i].memo); + if (auth_dat[i].memo[0]) { + memcpy(WFIFOP(fd,150), auth_dat[i].memo, strlen(auth_dat[i].memo)); + } + WFIFOSET(fd,150+strlen(auth_dat[i].memo)); + break; + } + } + if (i == auth_num) { + login_log("'ladmin': Attempt to obtain information (by the id) of an unknown account (id: %d, ip: %s)" RETCODE, + RFIFOL(fd,2), ip); + strncpy((char*)WFIFOP(fd,7), "", 24); + WFIFOW(fd,148) = 0; + WFIFOSET(fd,150); + } + RFIFOSKIP(fd,6); + break; + + case 0x7955: // Request to reload GM file (no answer) + login_log("'ladmin': Request to re-load GM configuration file (ip: %s)." RETCODE, ip); + read_gm_account(); + // send GM accounts to all char-servers + send_GM_accounts(); + RFIFOSKIP(fd,2); + break; + + default: + { + FILE *logfp; + char tmpstr[24]; + time_t raw_time; + logfp = fopen(login_log_unknown_packets_filename, "a"); + if (logfp) { + time(&raw_time); + strftime(tmpstr, 23, date_format, localtime(&raw_time)); + fprintf(logfp, "%s: receiving of an unknown packet -> disconnection" RETCODE, tmpstr); + fprintf(logfp, "parse_admin: connection #%d (ip: %s), packet: 0x%x (with being read: %d)." RETCODE, fd, ip, RFIFOW(fd,0), RFIFOREST(fd)); + fprintf(logfp, "Detail (in hex):" RETCODE); + fprintf(logfp, "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F" RETCODE); + memset(tmpstr, '\0', sizeof(tmpstr)); + for(i = 0; i < RFIFOREST(fd); i++) { + if ((i & 15) == 0) + fprintf(logfp, "%04X ",i); + fprintf(logfp, "%02x ", RFIFOB(fd,i)); + if (RFIFOB(fd,i) > 0x1f) + tmpstr[i % 16] = RFIFOB(fd,i); + else + tmpstr[i % 16] = '.'; + if ((i - 7) % 16 == 0) // -8 + 1 + fprintf(logfp, " "); + else if ((i + 1) % 16 == 0) { + fprintf(logfp, " %s" RETCODE, tmpstr); + memset(tmpstr, '\0', sizeof(tmpstr)); + } + } + if (i % 16 != 0) { + for(j = i; j % 16 != 0; j++) { + fprintf(logfp, " "); + if ((j - 7) % 16 == 0) // -8 + 1 + fprintf(logfp, " "); + } + fprintf(logfp, " %s" RETCODE, tmpstr); + } + fprintf(logfp, RETCODE); + fclose(logfp); + } + } + login_log("'ladmin': End of connection, unknown packet (ip: %s)" RETCODE, ip); + session[fd]->eof = 1; + ShowWarning("Remote administration has been disconnected (unknown packet).\n"); + return 0; + } + //WFIFOW(fd,0) = 0x791f; + //WFIFOSET(fd,2); + } + RFIFOSKIP(fd,RFIFOREST(fd)); + return 0; +} + +//-------------------------------------------- +// Test to know if an IP come from LAN or WAN. +// Rewrote: Adnvanced subnet check [LuzZza] +//-------------------------------------------- +int lan_subnetcheck(long *p) { + + int i; + unsigned char *sbn, *msk, *src = (unsigned char *)p; + + for(i=0; iclient_addr.sin_addr; + char ip[16]; + long subnet_char_ip; + + RFIFOHEAD(fd); + + sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + memset(&account, 0, sizeof(account)); + + if (session[fd]->eof) { + do_close(fd); + return 0; + } + + while(RFIFOREST(fd) >= 2) { + if (display_parse_login == 1) { + if (RFIFOW(fd,0) == 0x64 || RFIFOW(fd,0) == 0x01dd) { + if ((int)RFIFOREST(fd) >= ((RFIFOW(fd,0) == 0x64) ? 55 : 47)) + ShowDebug("parse_login: connection #%d, packet: 0x%x (with being read: %d), account: %s.\n", fd, RFIFOW(fd,0), RFIFOREST(fd), RFIFOP(fd,6)); + } else if (RFIFOW(fd,0) == 0x2710) { + if (RFIFOREST(fd) >= 86) + ShowDebug("parse_login: connection #%d, packet: 0x%x (with being read: %d), server: %s.\n", fd, RFIFOW(fd,0), RFIFOREST(fd), RFIFOP(fd,60)); + } else + ShowDebug("parse_login: connection #%d, packet: 0x%x (with being read: %d).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + } + + switch(RFIFOW(fd,0)) { + case 0x200: // New alive packet: structure: 0x200 .24B. used to verify if client is always alive. + if (RFIFOREST(fd) < 26) + return 0; + RFIFOSKIP(fd,26); + break; + + case 0x204: // New alive packet: structure: 0x204 .16B. (new ragexe from 22 june 2004) + if (RFIFOREST(fd) < 18) + return 0; + RFIFOSKIP(fd,18); + break; + + case 0x277: // New login packet + case 0x64: // request client login + case 0x01dd: // request client login with encrypt + { + int packet_len = RFIFOREST(fd); + + switch(RFIFOW(fd, 0)){ + case 0x64: + if(packet_len < 55) + return 0; + break; + case 0x01dd: + if(packet_len < 47) + return 0; + break; + case 0x277: + if(packet_len < 84) + return 0; + break; + } + + account.version = RFIFOL(fd, 2); //for exe version check [Sirius] + if (!account.version) account.version = 1; //Force some version... + memcpy(account.userid,RFIFOP(fd,6),NAME_LENGTH); + account.userid[23] = '\0'; + remove_control_chars((unsigned char *)account.userid); + if (RFIFOW(fd,0) != 0x01dd) { + login_log("Request for connection (non encryption mode) of %s (ip: %s)." RETCODE, account.userid, ip); + memcpy(account.passwd, RFIFOP(fd,30), NAME_LENGTH); + account.passwd[23] = '\0'; + remove_control_chars((unsigned char *)account.passwd); + } else { + login_log("Request for connection (encryption mode) of %s (ip: %s)." RETCODE, account.userid, ip); + // If remove control characters from received password encrypted by md5, + // there would be a wrong result and failed to authentication. [End_of_exam] + memcpy(account.passwd, RFIFOP(fd,30), 16); + account.passwd[16] = '\0'; + } +#ifdef PASSWORDENC + account.passwdenc = (RFIFOW(fd,0) != 0x01dd) ? 0 : PASSWORDENC; +#else + account.passwdenc = 0; +#endif + + if (!check_ip(session[fd]->client_addr.sin_addr.s_addr)) { + login_log("Connection refused: IP isn't authorised (deny/allow, ip: %s)." RETCODE, ip); + WFIFOHEAD(fd, 23); + WFIFOW(fd,0) = 0x6a; + WFIFOB(fd,2) = 3; // 3 = Rejected from Server + WFIFOSET(fd,23); + RFIFOSKIP(fd,packet_len); + break; + } + + result = mmo_auth(&account, fd); + if (result == -1) { + int gm_level = isGM(account.account_id); + if (min_level_to_connect > gm_level) { + login_log("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d, ip: %s)." RETCODE, + min_level_to_connect, account.userid, gm_level, ip); + WFIFOHEAD(fd, 3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + } else { + if (gm_level) + ShowInfo("Connection of the GM (level:%d) account '%s' accepted.\n", gm_level, account.userid); + else + ShowInfo("Connection of the account '%s' accepted.\n", account.userid); + server_num = 0; + WFIFOHEAD(fd, 47+32*MAX_SERVERS); + for(i = 0; i < MAX_SERVERS; i++) { + if (server_fd[i] >= 0) { + // Andvanced subnet check [LuzZza] + if((subnet_char_ip = lan_subnetcheck((long*)p))) + WFIFOL(fd,47+server_num*32) = subnet_char_ip; + else + WFIFOL(fd,47+server_num*32) = server[i].ip; + WFIFOW(fd,47+server_num*32+4) = server[i].port; + memcpy(WFIFOP(fd,47+server_num*32+6), server[i].name, 20); + WFIFOW(fd,47+server_num*32+26) = server[i].users; + WFIFOW(fd,47+server_num*32+28) = server[i].maintenance; + WFIFOW(fd,47+server_num*32+30) = server[i].new_; + server_num++; + } + } + // if at least 1 char-server + if (server_num > 0) { + WFIFOW(fd,0) = 0x69; + WFIFOW(fd,2) = 47+32*server_num; + WFIFOL(fd,4) = account.login_id1; + WFIFOL(fd,8) = account.account_id; + WFIFOL(fd,12) = account.login_id2; + WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used) + memcpy(WFIFOP(fd,20), account.lastlogin, 24); // in old version, that was for name (not more used) + WFIFOB(fd,46) = account.sex; + WFIFOSET(fd,47+32*server_num); + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + auth_fifo[auth_fifo_pos].account_id = account.account_id; + auth_fifo[auth_fifo_pos].login_id1 = account.login_id1; + auth_fifo[auth_fifo_pos].login_id2 = account.login_id2; + auth_fifo[auth_fifo_pos].sex = account.sex; + auth_fifo[auth_fifo_pos].delflag = 0; + auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr.sin_addr.s_addr; + auth_fifo_pos++; + // if no char-server, don't send void list of servers, just disconnect the player with proper message + } else { + login_log("Connection refused: there is no char-server online (account: %s, ip: %s)." RETCODE, + account.userid, ip); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + } + } + } else { + WFIFOHEAD(fd, 23); + memset(WFIFOP(fd,0), '\0', 23); + WFIFOW(fd,0) = 0x6a; + WFIFOB(fd,2) = result; + if (result == 6) { // 6 = Your are Prohibited to log in until %s + i = search_account_index(account.userid); + if (i != -1) { + if (auth_dat[i].ban_until_time != 0) { // if account is banned, we send ban timestamp + char tmpstr[256]; + strftime(tmpstr, 20, date_format, localtime(&auth_dat[i].ban_until_time)); + tmpstr[19] = '\0'; + memcpy(WFIFOP(fd,3), tmpstr, 20); + } else { // we send error message + memcpy(WFIFOP(fd,3), auth_dat[i].error_message, 20); + } + } + } + WFIFOSET(fd,23); + } + RFIFOSKIP(fd,packet_len); + break; + } + case 0x01db: // Sending request of the coding key + case 0x791a: // Sending request of the coding key (administration packet) + { + struct login_session_data *ld; + if (session[fd]->session_data) { + ShowWarning("login: abnormal request of MD5 key (already opened session).\n"); + session[fd]->eof = 1; + return 0; + } + ld = (struct login_session_data*)aCalloc(1, sizeof(struct login_session_data)); + session[fd]->session_data = ld; + if (!ld) { + ShowFatalError("login: Request for md5 key: memory allocation failure (malloc)!\n"); + session[fd]->eof = 1; + return 0; + } + if (RFIFOW(fd,0) == 0x01db) + login_log("Sending request of the coding key (ip: %s)" RETCODE, ip); + else + login_log("'ladmin': Sending request of the coding key (ip: %s)" RETCODE, ip); + // Creation of the coding key + memset(ld->md5key, '\0', sizeof(ld->md5key)); + ld->md5keylen = rand() % 4 + 12; + for(i = 0; i < ld->md5keylen; i++) + ld->md5key[i] = rand() % 255 + 1; + RFIFOSKIP(fd,2); + WFIFOHEAD(fd, 4 + ld->md5keylen); + WFIFOW(fd,0) = 0x01dc; + WFIFOW(fd,2) = 4 + ld->md5keylen; + memcpy(WFIFOP(fd,4), ld->md5key, ld->md5keylen); + WFIFOSET(fd,WFIFOW(fd,2)); + } + break; + + case 0x2710: // Connection request of a char-server + if (RFIFOREST(fd) < 86) + return 0; + { + int GM_value, len; + char* server_name; + WFIFOHEAD(fd, 3); + memcpy(account.userid,RFIFOP(fd,2),NAME_LENGTH); + account.userid[23] = '\0'; + remove_control_chars((unsigned char *)account.userid); + memcpy(account.passwd, RFIFOP(fd,26), NAME_LENGTH); + account.passwd[23] = '\0'; + remove_control_chars((unsigned char *)account.passwd); + account.passwdenc = 0; + server_name = (char*)RFIFOP(fd,60); + server_name[20] = '\0'; + remove_control_chars((unsigned char *)server_name); + login_log("Connection request of the char-server '%s' @ %d.%d.%d.%d:%d (ip: %s)" RETCODE, + server_name, RFIFOB(fd,54), RFIFOB(fd,55), RFIFOB(fd,56), RFIFOB(fd,57), RFIFOW(fd,58), ip); + result = mmo_auth(&account, fd); + if (result == -1 && account.sex == 2 && account.account_id < MAX_SERVERS && server_fd[account.account_id] == -1) { + login_log("Connection of the char-server '%s' accepted (account: %s, pass: %s, ip: %s)" RETCODE, + server_name, account.userid, account.passwd, ip); + ShowStatus("Connection of the char-server '%s' accepted.\n", server_name); + memset(&server[account.account_id], 0, sizeof(struct mmo_char_server)); + server[account.account_id].ip = RFIFOL(fd,54); + server[account.account_id].port = RFIFOW(fd,58); + memcpy(server[account.account_id].name, server_name, 20); + server[account.account_id].users = 0; + server[account.account_id].maintenance = RFIFOW(fd,82); + server[account.account_id].new_ = RFIFOW(fd,84); + server_fd[account.account_id] = fd; + WFIFOW(fd,0) = 0x2711; + WFIFOB(fd,2) = 0; + WFIFOSET(fd,3); + session[fd]->func_parse = parse_fromchar; + realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + // send GM account to char-server + len = 4; + WFIFOW(fd,0) = 0x2732; + for(i = 0; i < auth_num; i++) + // send only existing accounts. We can not create a GM account when server is online. + if ((GM_value = isGM(auth_dat[i].account_id)) > 0) { + WFIFOL(fd,len) = auth_dat[i].account_id; + WFIFOB(fd,len+4) = (unsigned char)GM_value; + len += 5; + } + WFIFOW(fd,2) = len; + WFIFOSET(fd,len); + } else { + if (server_fd[account.account_id] != -1) { + ShowNotice("Connection of the char-server '%s' REFUSED - already connected (account: %ld-%s, pass: %s, ip: %s)\n", + server_name, account.account_id, account.userid, account.passwd, ip); + login_log("Connexion of the char-server '%s' REFUSED - already connected (account: %ld-%s, pass: %s, ip: %s)" RETCODE, + server_name, account.account_id, account.userid, account.passwd, ip); + } else { + ShowNotice("Connection of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s).\n", server_name, account.userid, account.passwd, ip); + login_log("Connexion of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s)" RETCODE, + server_name, account.userid, account.passwd, ip); + } + WFIFOHEAD(fd, 3); + WFIFOW(fd,0) = 0x2711; + WFIFOB(fd,2) = 3; + WFIFOSET(fd,3); + } + } + RFIFOSKIP(fd,86); + return 0; + + case 0x7530: // Request of the server version + login_log("Sending of the server version (ip: %s)" RETCODE, ip); + WFIFOHEAD(fd, 10); + WFIFOW(fd,0) = 0x7531; + WFIFOB(fd,2) = ATHENA_MAJOR_VERSION; + WFIFOB(fd,3) = ATHENA_MINOR_VERSION; + WFIFOB(fd,4) = ATHENA_REVISION; + WFIFOB(fd,5) = ATHENA_RELEASE_FLAG; + WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG; + WFIFOB(fd,7) = ATHENA_SERVER_LOGIN; + WFIFOW(fd,8) = ATHENA_MOD_VERSION; + WFIFOSET(fd,10); + RFIFOSKIP(fd,2); + break; + + case 0x7532: // Request to end connection + login_log("End of connection (ip: %s)" RETCODE, ip); + session[fd]->eof = 1; + return 0; + + case 0x7918: // Request for administation login + if ((int)RFIFOREST(fd) < 4 || (int)RFIFOREST(fd) < ((RFIFOW(fd,2) == 0) ? 28 : 20)) + return 0; + WFIFOW(fd,0) = 0x7919; + WFIFOB(fd,2) = 1; + if (!check_ladminip(session[fd]->client_addr.sin_addr.s_addr)) { + login_log("'ladmin'-login: Connection in administration mode refused: IP isn't authorised (ladmin_allow, ip: %s)." RETCODE, ip); + } else { + struct login_session_data *ld = (struct login_session_data*)session[fd]->session_data; + if (RFIFOW(fd,2) == 0) { // non encrypted password + char password[25]; + memcpy(password, RFIFOP(fd,4), 24); + password[24] = '\0'; + remove_control_chars((unsigned char *)password); + // If remote administration is enabled and password sent by client matches password read from login server configuration file + if ((admin_state == 1) && (strcmp(password, admin_pass) == 0)) { + login_log("'ladmin'-login: Connection in administration mode accepted (non encrypted password: %s, ip: %s)" RETCODE, password, ip); + ShowNotice("Connection of a remote administration accepted (non encrypted password).\n"); + WFIFOB(fd,2) = 0; + session[fd]->func_parse = parse_admin; + } else if (admin_state != 1) + login_log("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (non encrypted password: %s, ip: %s)" RETCODE, password, ip); + else + login_log("'ladmin'-login: Connection in administration mode REFUSED - invalid password (non encrypted password: %s, ip: %s)" RETCODE, password, ip); + } else { // encrypted password + if (!ld) + ShowError("'ladmin'-login: error! MD5 key not created/requested for an administration login.\n"); + else { + char md5str[64] = "", md5bin[32]; + if (RFIFOW(fd,2) == 1) { + sprintf(md5str, "%s%s", ld->md5key, admin_pass); // 20 24 + } else if (RFIFOW(fd,2) == 2) { + sprintf(md5str, "%s%s", admin_pass, ld->md5key); // 24 20 + } + MD5_String2binary(md5str, md5bin); + // If remote administration is enabled and password hash sent by client matches hash of password read from login server configuration file + if ((admin_state == 1) && (memcmp(md5bin, RFIFOP(fd,4), 16) == 0)) { + login_log("'ladmin'-login: Connection in administration mode accepted (encrypted password, ip: %s)" RETCODE, ip); + ShowNotice("Connection of a remote administration accepted (encrypted password).\n"); + WFIFOB(fd,2) = 0; + session[fd]->func_parse = parse_admin; + } else if (admin_state != 1) + login_log("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (encrypted password, ip: %s)" RETCODE, ip); + else + login_log("'ladmin'-login: Connection in administration mode REFUSED - invalid password (encrypted password, ip: %s)" RETCODE, ip); + } + } + } + WFIFOSET(fd,3); + RFIFOSKIP(fd, (RFIFOW(fd,2) == 0) ? 28 : 20); + break; + + default: + if (save_unknown_packets) { + FILE *logfp; + char tmpstr[24]; + time_t raw_time; + logfp = fopen(login_log_unknown_packets_filename, "a"); + if (logfp) { + time(&raw_time); + strftime(tmpstr, 23, date_format, localtime(&raw_time)); + fprintf(logfp, "%s: receiving of an unknown packet -> disconnection" RETCODE, tmpstr); + fprintf(logfp, "parse_login: connection #%d (ip: %s), packet: 0x%x (with being read: %d)." RETCODE, fd, ip, RFIFOW(fd,0), RFIFOREST(fd)); + fprintf(logfp, "Detail (in hex):" RETCODE); + fprintf(logfp, "---- 00-01-02-03-04-05-06-07 08-09-0A-0B-0C-0D-0E-0F" RETCODE); + memset(tmpstr, '\0', sizeof(tmpstr)); + for(i = 0; i < RFIFOREST(fd); i++) { + if ((i & 15) == 0) + fprintf(logfp, "%04X ",i); + fprintf(logfp, "%02x ", RFIFOB(fd,i)); + if (RFIFOB(fd,i) > 0x1f) + tmpstr[i % 16] = RFIFOB(fd,i); + else + tmpstr[i % 16] = '.'; + if ((i - 7) % 16 == 0) // -8 + 1 + fprintf(logfp, " "); + else if ((i + 1) % 16 == 0) { + fprintf(logfp, " %s" RETCODE, tmpstr); + memset(tmpstr, '\0', sizeof(tmpstr)); + } + } + if (i % 16 != 0) { + for(j = i; j % 16 != 0; j++) { + fprintf(logfp, " "); + if ((j - 7) % 16 == 0) // -8 + 1 + fprintf(logfp, " "); + } + fprintf(logfp, " %s" RETCODE, tmpstr); + } + fprintf(logfp, RETCODE); + fclose(logfp); + } + } + login_log("End of connection, unknown packet (ip: %s)" RETCODE, ip); + session[fd]->eof = 1; + return 0; + } + } + RFIFOSKIP(fd,RFIFOREST(fd)); + return 0; +} + +//----------------------- +// Console Command Parser [Wizputer] +//----------------------- +int parse_console(char *buf) { + char command[256]; + + memset(command,0,sizeof(command)); + + sscanf(buf, "%[^\n]", command); + + login_log("Console command :%s" RETCODE, command); + + if(strcmpi("shutdown", command) == 0 || + strcmpi("exit", command) == 0 || + strcmpi("quit", command) == 0 || + strcmpi("end", command) == 0) + runflag = 0; + else if(strcmpi("alive", command) == 0 || + strcmpi("status", command) == 0) + ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n"); + else if(strcmpi("help", command) == 0) { + printf(CL_BOLD"Help of commands:"CL_RESET"\n"); + printf(" To shutdown the server:\n"); + printf(" 'shutdown|exit|qui|end'\n"); + printf(" To know if server is alive:\n"); + printf(" 'alive|status'\n"); + } + + return 0; +} + +static int online_data_cleanup_sub(DBKey key, void *data, va_list ap) +{ + struct online_login_data *character= (struct online_login_data*)data; + if (character->char_server == -2) //Unknown server.. set them offline + remove_online_user(character->account_id); + else if (character->char_server < 0) + //Free data from players that have not been online for a while. + db_remove(online_db, key); + return 0; +} + +static int online_data_cleanup(int tid, unsigned int tick, int id, int data) +{ + online_db->foreach(online_db, online_data_cleanup_sub); + return 0; +} +//------------------------------------------------- +// Return numerical value of a switch configuration +// on/off, english, fran軋is, deutsch, espaol +//------------------------------------------------- +int config_switch(const char *str) { + if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) + return 1; + if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) + return 0; + + return atoi(str); +} + +//---------------------------------- +// Reading Lan Support configuration +// Rewrote: Anvanced subnet check [LuzZza] +//---------------------------------- +int login_lan_config_read(const char *lancfgName) { + + FILE *fp; + int line_num = 0; + char line[1024], w1[64], w2[64], w3[64], w4[64]; + + if((fp = fopen(lancfgName, "r")) == NULL) { + ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName); + return 1; + } + + ShowInfo("Reading the configuration file %s...\n", lancfgName); + + while(fgets(line, sizeof(line)-1, fp)) { + + line_num++; + if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n') + continue; + + line[sizeof(line)-1] = '\0'; + if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4) { + + ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num); + continue; + } + + remove_control_chars((unsigned char *)w1); + remove_control_chars((unsigned char *)w2); + remove_control_chars((unsigned char *)w3); + remove_control_chars((unsigned char *)w4); + + if(strcmpi(w1, "subnet") == 0) { + + subnet[subnet_count].mask = inet_addr(w2); + subnet[subnet_count].char_ip = inet_addr(w3); + subnet[subnet_count].map_ip = inet_addr(w4); + subnet[subnet_count].subnet = subnet[subnet_count].char_ip&subnet[subnet_count].mask; + if (subnet[subnet_count].subnet != (subnet[subnet_count].map_ip&subnet[subnet_count].mask)) { + ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4); + continue; + } + + subnet_count++; + } + + ShowStatus("Read information about %d subnetworks.\n", subnet_count); + } + + fclose(fp); + return 0; +} + +//----------------------------------- +// Reading general configuration file +//----------------------------------- +int login_config_read(const char *cfgName) { + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + if ((fp = fopen(cfgName, "r")) == NULL) { + ShowError("Configuration file (%s) not found.\n", cfgName); + return 1; + } + + ShowInfo("Reading configuration file %s...\n", cfgName); + while(fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof(line)-1] = '\0'; + memset(w2, 0, sizeof(w2)); + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { + remove_control_chars((unsigned char *)w1); + remove_control_chars((unsigned char *)w2); + + if(strcmpi(w1,"timestamp_format") == 0) { + strncpy(timestamp_format, w2, 20); + } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ + stdout_with_ansisequence = config_switch(w2); + } else if(strcmpi(w1,"console_silent")==0){ + msg_silent = 0; //To always allow the next line to show up. + ShowInfo("Console Silent Setting: %d\n", atoi(w2)); + msg_silent = atoi(w2); + } else if (strcmpi(w1, "admin_state") == 0) { + admin_state = config_switch(w2); + } else if (strcmpi(w1, "admin_pass") == 0) { + memset(admin_pass, 0, sizeof(admin_pass)); + strncpy(admin_pass, w2, sizeof(admin_pass)); + admin_pass[sizeof(admin_pass)-1] = '\0'; + } else if (strcmpi(w1, "ladminallowip") == 0) { + if (strcmpi(w2, "clear") == 0) { + if (access_ladmin_allow) + aFree(access_ladmin_allow); + access_ladmin_allow = NULL; + access_ladmin_allownum = 0; + } else { + if (strcmpi(w2, "all") == 0) { + // reset all previous values + if (access_ladmin_allow) + aFree(access_ladmin_allow); + // set to all + access_ladmin_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); + access_ladmin_allownum = 1; + access_ladmin_allow[0] = '\0'; + } else if (w2[0] && !(access_ladmin_allownum == 1 && access_ladmin_allow[0] == '\0')) { // don't add IP if already 'all' + if (access_ladmin_allow) + access_ladmin_allow = (char*)aRealloc(access_ladmin_allow, (access_ladmin_allownum+1) * ACO_STRSIZE); + else + access_ladmin_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); + strncpy(access_ladmin_allow + (access_ladmin_allownum++) * ACO_STRSIZE, w2, ACO_STRSIZE); + access_ladmin_allow[access_ladmin_allownum * ACO_STRSIZE - 1] = '\0'; + } + } + } else if (strcmpi(w1, "gm_pass") == 0) { + memset(gm_pass, 0, sizeof(gm_pass)); + strncpy(gm_pass, w2, sizeof(gm_pass)); + gm_pass[sizeof(gm_pass)-1] = '\0'; + } else if (strcmpi(w1, "level_new_gm") == 0) { + level_new_gm = atoi(w2); + } else if (strcmpi(w1, "new_account") == 0) { + new_account_flag = config_switch(w2); + } else if (strcmpi(w1, "bind_ip") == 0) { + bind_ip = resolve_hostbyname(w2, NULL, bind_ip_str); + if (bind_ip) + ShowStatus("Login server binding IP address : %s -> %s\n", w2, bind_ip_str); + } else if (strcmpi(w1, "login_port") == 0) { + login_port = atoi(w2); + } else if (strcmpi(w1, "account_filename") == 0) { + memset(account_filename, 0, sizeof(account_filename)); + strncpy(account_filename, w2, sizeof(account_filename)); + account_filename[sizeof(account_filename)-1] = '\0'; + } else if (strcmpi(w1, "gm_account_filename") == 0) { + memset(GM_account_filename, 0, sizeof(GM_account_filename)); + strncpy(GM_account_filename, w2, sizeof(GM_account_filename)); + GM_account_filename[sizeof(GM_account_filename)-1] = '\0'; + } else if (strcmpi(w1, "gm_account_filename_check_timer") == 0) { + gm_account_filename_check_timer = atoi(w2); + } else if (strcmpi(w1, "use_MD5_passwords") == 0) { + use_md5_passwds = config_switch(w2); + } else if (strcmpi(w1, "login_log_filename") == 0) { + memset(login_log_filename, 0, sizeof(login_log_filename)); + strncpy(login_log_filename, w2, sizeof(login_log_filename)); + login_log_filename[sizeof(login_log_filename)-1] = '\0'; + } else if (strcmpi(w1, "log_login") == 0) { + log_login = atoi(w2); + } else if (strcmpi(w1, "login_log_unknown_packets_filename") == 0) { + memset(login_log_unknown_packets_filename, 0, sizeof(login_log_unknown_packets_filename)); + strncpy(login_log_unknown_packets_filename, w2, sizeof(login_log_unknown_packets_filename)); + login_log_unknown_packets_filename[sizeof(login_log_unknown_packets_filename)-1] = '\0'; + } else if (strcmpi(w1, "save_unknown_packets") == 0) { + save_unknown_packets = config_switch(w2); + } else if (strcmpi(w1, "display_parse_login") == 0) { + display_parse_login = config_switch(w2); // 0: no, 1: yes + } else if (strcmpi(w1, "display_parse_admin") == 0) { + display_parse_admin = config_switch(w2); // 0: no, 1: yes + } else if (strcmpi(w1, "display_parse_fromchar") == 0) { + display_parse_fromchar = config_switch(w2); // 0: no, 1: yes (without packet 0x2714), 2: all packets + } else if (strcmpi(w1, "date_format") == 0) { // note: never have more than 19 char for the date! + memset(date_format, 0, sizeof(date_format)); + switch (atoi(w2)) { + case 0: + strcpy(date_format, "%d-%m-%Y %H:%M:%S"); // 31-12-2004 23:59:59 + break; + case 1: + strcpy(date_format, "%m-%d-%Y %H:%M:%S"); // 12-31-2004 23:59:59 + break; + case 2: + strcpy(date_format, "%Y-%d-%m %H:%M:%S"); // 2004-31-12 23:59:59 + break; + case 3: + strcpy(date_format, "%Y-%m-%d %H:%M:%S"); // 2004-12-31 23:59:59 + break; + } + } else if (strcmpi(w1, "min_level_to_connect") == 0) { + min_level_to_connect = atoi(w2); + } else if (strcmpi(w1, "add_to_unlimited_account") == 0) { + add_to_unlimited_account = config_switch(w2); + } else if (strcmpi(w1, "start_limited_time") == 0) { + start_limited_time = atoi(w2); + } else if (strcmpi(w1, "check_ip_flag") == 0) { + check_ip_flag = config_switch(w2); + } else if (strcmpi(w1, "order") == 0) { + access_order = atoi(w2); + if (strcmpi(w2, "deny,allow") == 0 || + strcmpi(w2, "deny, allow") == 0) access_order = ACO_DENY_ALLOW; + if (strcmpi(w2, "allow,deny") == 0 || + strcmpi(w2, "allow, deny") == 0) access_order = ACO_ALLOW_DENY; + if (strcmpi(w2, "mutual-failture") == 0 || + strcmpi(w2, "mutual-failure") == 0) access_order = ACO_MUTUAL_FAILTURE; + } else if (strcmpi(w1, "allow") == 0) { + if (strcmpi(w2, "clear") == 0) { + if (access_allow) + aFree(access_allow); + access_allow = NULL; + access_allownum = 0; + } else { + if (strcmpi(w2, "all") == 0) { + // reset all previous values + if (access_allow) + aFree(access_allow); + // set to all + access_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); + access_allownum = 1; + access_allow[0] = '\0'; + } else if (w2[0] && !(access_allownum == 1 && access_allow[0] == '\0')) { // don't add IP if already 'all' + if (access_allow) + access_allow = (char*)aRealloc(access_allow, (access_allownum+1) * ACO_STRSIZE); + else + access_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); + strncpy(access_allow + (access_allownum++) * ACO_STRSIZE, w2, ACO_STRSIZE); + access_allow[access_allownum * ACO_STRSIZE - 1] = '\0'; + } + } + } else if (strcmpi(w1, "deny") == 0) { + if (strcmpi(w2, "clear") == 0) { + if (access_deny) + aFree(access_deny); + access_deny = NULL; + access_denynum = 0; + } else { + if (strcmpi(w2, "all") == 0) { + // reset all previous values + if (access_deny) + aFree(access_deny); + // set to all + access_deny = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); + access_denynum = 1; + access_deny[0] = '\0'; + } else if (w2[0] && !(access_denynum == 1 && access_deny[0] == '\0')) { // don't add IP if already 'all' + if (access_deny) + access_deny = (char*)aRealloc(access_deny, (access_denynum+1) * ACO_STRSIZE); + else + access_deny = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); + strncpy(access_deny + (access_denynum++) * ACO_STRSIZE, w2, ACO_STRSIZE); + access_deny[access_denynum * ACO_STRSIZE - 1] = '\0'; + } + } + // dynamic password error ban + } else if (strcmpi(w1, "dynamic_pass_failure_ban") == 0) { + dynamic_pass_failure_ban = config_switch(w2); + } else if (strcmpi(w1, "dynamic_pass_failure_ban_time") == 0) { + dynamic_pass_failure_ban_time = atoi(w2); + } else if (strcmpi(w1, "dynamic_pass_failure_ban_how_many") == 0) { + dynamic_pass_failure_ban_how_many = atoi(w2); + } else if (strcmpi(w1, "dynamic_pass_failure_ban_how_long") == 0) { + dynamic_pass_failure_ban_how_long = atoi(w2); + } else if(strcmpi(w1, "check_client_version") == 0){ //Added by Sirius for client version check + if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ){ + check_client_version = 1; + } + if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ){ + check_client_version = 0; + } + }else if(strcmpi(w1, "client_version_to_connect") == 0){ //Added by Sirius for client version check + client_version_to_connect = atoi(w2); //Added by Sirius for client version check + } else if (strcmpi(w1, "console") == 0) { + if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) + console = 1; + } else if (strcmpi(w1, "allowed_regs") == 0) { //account flood protection system [Kevin] + allowed_regs = atoi(w2); + } else if (strcmpi(w1, "time_allowed") == 0) { + time_allowed = atoi(w2); + } else if (strcmpi(w1, "online_check") == 0) { + if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) + online_check = 1; + else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ) + online_check = 0; + else + online_check = atoi(w2); + } else if (strcmpi(w1, "import") == 0) { + login_config_read(w2); + } else if(strcmpi(w1,"use_dnsbl")==0) { // [Zido] + use_dnsbl=atoi(w2); + } else if(strcmpi(w1,"dnsbl_servers")==0) { // [Zido] + strcpy(dnsbl_servs,w2); + } else if(strcmpi(w1,"ip_sync_interval")==0) { + ip_sync_interval = 1000*60*atoi(w2); //w2 comes in minutes. + } + } + } + fclose(fp); + + ShowInfo("Finished reading %s.\n", cfgName); + + return 0; +} + +//------------------------------------- +// Displaying of configuration warnings +//------------------------------------- +void display_conf_warnings(void) { + if (admin_state != 0 && admin_state != 1) { + ShowWarning("Invalid value for admin_state parameter -> setting to 0 (no remote admin).\n"); + admin_state = 0; + } + + if (admin_state == 1) { + if (admin_pass[0] == '\0') { + ShowWarning("Administrator password is void (admin_pass).\n"); + } else if (strcmp(admin_pass, "admin") == 0) { + ShowWarning("You are using the default administrator password (admin_pass).\n"); + ShowWarning(" We highly recommend that you change it.\n"); + } + } + + if (gm_pass[0] == '\0') { + ShowWarning("'To GM become' password is void (gm_pass).\n"); + ShowWarning(" We highly recommend that you set one password.\n"); + } else if (strcmp(gm_pass, "gm") == 0) { + ShowWarning("You are using the default GM password (gm_pass).\n"); + ShowWarning(" We highly recommend that you change it.\n"); + } + + if (level_new_gm < 0 || level_new_gm > 99) { + ShowWarning("Invalid value for level_new_gm parameter -> setting to 60 (default).\n"); + level_new_gm = 60; + } + + if (new_account_flag != 0 && new_account_flag != 1) { + ShowWarning("Invalid value for new_account parameter -> setting to 0 (no new account).\n"); + new_account_flag = 0; + } + + if (login_port < 1024 || login_port > 65535) { + ShowWarning("Invalid value for login_port parameter -> setting to 6900 (default).\n"); + login_port = 6900; + } + + if (gm_account_filename_check_timer < 0) { + ShowWarning("Invalid value for gm_account_filename_check_timer parameter. Setting to 15 sec (default).\n"); + gm_account_filename_check_timer = 15; + } else if (gm_account_filename_check_timer == 1) { + ShowWarning("Invalid value for gm_account_filename_check_timer parameter. Setting to 2 sec (minimum value).\n"); + gm_account_filename_check_timer = 2; + } + + if (save_unknown_packets != 0 && save_unknown_packets != 1) { + ShowWarning("Invalid value for save_unknown_packets parameter -> setting to 0-no save.\n"); + save_unknown_packets = 0; + } + + if (display_parse_login != 0 && display_parse_login != 1) { // 0: no, 1: yes + ShowWarning("Invalid value for display_parse_login parameter -> setting to 0 (no display).\n"); + display_parse_login = 0; + } + + if (display_parse_admin != 0 && display_parse_admin != 1) { // 0: no, 1: yes + ShowWarning("Invalid value for display_parse_admin parameter -> setting to 0 (no display).\n"); + display_parse_admin = 0; + } + + if (display_parse_fromchar < 0 || display_parse_fromchar > 2) { // 0: no, 1: yes (without packet 0x2714), 2: all packets + ShowWarning("Invalid value for display_parse_fromchar parameter -> setting to 0 (no display).\n"); + display_parse_fromchar = 0; + } + + if (min_level_to_connect < 0) { // 0: all players, 1-99 at least gm level x + ShowWarning("Invalid value for min_level_to_connect (%d) parameter -> setting 0 (any player).\n", min_level_to_connect); + min_level_to_connect = 0; + } else if (min_level_to_connect > 99) { // 0: all players, 1-99 at least gm level x + ShowWarning("Invalid value for min_level_to_connect (%d) parameter -> setting to 99 (only GM level 99)\n", min_level_to_connect); + min_level_to_connect = 99; + } + + if (add_to_unlimited_account != 0 && add_to_unlimited_account != 1) { // 0: no, 1: yes + ShowWarning("Invalid value for add_to_unlimited_account parameter\n"); + ShowWarning(" -> setting to 0 (impossible to add a time to an unlimited account).\n"); + add_to_unlimited_account = 0; + } + + if (start_limited_time < -1) { // -1: create unlimited account, 0 or more: additionnal sec from now to create limited time + ShowWarning("Invalid value for start_limited_time parameter\n"); + ShowWarning(" -> setting to -1 (new accounts are created with unlimited time).\n"); + start_limited_time = -1; + } + + if (check_ip_flag != 0 && check_ip_flag != 1) { // 0: no, 1: yes + ShowWarning("Invalid value for check_ip_flag parameter\n"); + ShowWarning(" -> setting to 1 (check players ip between login-server & char-server).\n"); + check_ip_flag = 1; + } + + if (access_order == ACO_DENY_ALLOW) { + if (access_denynum == 1 && access_deny[0] == '\0') { + ShowWarning("The IP security order is 'deny,allow' (allow if not deny) and you refuse ALL IP.\n"); + } + } else if (access_order == ACO_ALLOW_DENY) { + if (access_allownum == 0) { + ShowWarning("The IP security order is 'allow,deny' (deny if not allow) but, NO IP IS AUTHORISED!\n"); + } + } else { // ACO_MUTUAL_FAILTURE + if (access_allownum == 0) { + ShowWarning("The IP security order is 'mutual-failture'\n"); + ShowWarning(" (allow if in the allow list and not in the deny list).\n"); + ShowWarning(" But, NO IP IS AUTHORISED!\n"); + } else if (access_denynum == 1 && access_deny[0] == '\0') { + ShowWarning("The IP security order is mutual-failture\n"); + ShowWarning(" (allow if in the allow list and not in the deny list).\n"); + ShowWarning(" But, you refuse ALL IP!\n"); + } + } + + if (dynamic_pass_failure_ban != 0) { + if (dynamic_pass_failure_ban_time < 1) { + ShowWarning("Invalid value for dynamic_pass_failure_ban_time (%d) parameter\n", dynamic_pass_failure_ban_time); + ShowWarning(" -> setting to 5 (5 minutes to look number of invalid passwords.\n"); + dynamic_pass_failure_ban_time = 5; + } + if (dynamic_pass_failure_ban_how_many < 1) { + ShowWarning("Invalid value for dynamic_pass_failure_ban_how_many (%d) parameter\n", dynamic_pass_failure_ban_how_many); + ShowWarning(" -> setting to 3 (3 invalid passwords before to temporarily ban.\n"); + dynamic_pass_failure_ban_how_many = 3; + } + if (dynamic_pass_failure_ban_how_long < 1) { + ShowWarning("Invalid value for dynamic_pass_failure_ban_how_long (%d) parameter\n", dynamic_pass_failure_ban_how_long); + ShowWarning(" -> setting to 1 (1 minute of temporarily ban.\n"); + dynamic_pass_failure_ban_how_long = 1; + } + } + + return; +} + +//------------------------------- +// Save configuration in log file +//------------------------------- +void save_config_in_log(void) { + int i; + + // a newline in the log... + login_log(""); + login_log("The login-server starting..." RETCODE); + + // save configuration in log file + login_log("The configuration of the server is set:" RETCODE); + + if (admin_state != 1) + login_log("- with no remote administration." RETCODE); + else if (admin_pass[0] == '\0') + login_log("- with a remote administration with a VOID password." RETCODE); + else if (strcmp(admin_pass, "admin") == 0) + login_log("- with a remote administration with the DEFAULT password." RETCODE); + else + login_log("- with a remote administration with the password of %d character(s)." RETCODE, strlen(admin_pass)); + if (access_ladmin_allownum == 0 || (access_ladmin_allownum == 1 && access_ladmin_allow[0] == '\0')) { + login_log("- to accept any IP for remote administration" RETCODE); + } else { + login_log("- to accept following IP for remote administration:" RETCODE); + for(i = 0; i < access_ladmin_allownum; i++) + login_log(" %s" RETCODE, (char *)(access_ladmin_allow + i * ACO_STRSIZE)); + } + + if (gm_pass[0] == '\0') + login_log("- with a VOID 'To GM become' password (gm_pass)." RETCODE); + else if (strcmp(gm_pass, "gm") == 0) + login_log("- with the DEFAULT 'To GM become' password (gm_pass)." RETCODE); + else + login_log("- with a 'To GM become' password (gm_pass) of %d character(s)." RETCODE, strlen(gm_pass)); + if (level_new_gm == 0) + login_log("- to refuse any creation of GM with @gm." RETCODE); + else + login_log("- to create GM with level '%d' when @gm is used." RETCODE, level_new_gm); + + if (new_account_flag == 1) + login_log("- to ALLOW new users (with _F/_M)." RETCODE); + else + login_log("- to NOT ALLOW new users (with _F/_M)." RETCODE); + login_log("- with port: %d." RETCODE, login_port); + login_log("- with the accounts file name: '%s'." RETCODE, account_filename); + login_log("- with the GM accounts file name: '%s'." RETCODE, GM_account_filename); + if (gm_account_filename_check_timer == 0) + login_log("- to NOT check GM accounts file modifications." RETCODE); + else + login_log("- to check GM accounts file modifications every %d seconds." RETCODE, gm_account_filename_check_timer); + + if (use_md5_passwds == 0) + login_log("- to save password in plain text." RETCODE); + else + login_log("- to save password with MD5 encrypting." RETCODE); + + // not necessary to log the 'login_log_filename', we are inside :) + + login_log("- with the unknown packets file name: '%s'." RETCODE, login_log_unknown_packets_filename); + if (save_unknown_packets) + login_log("- to SAVE all unkown packets." RETCODE); + else + login_log("- to SAVE only unkown packets sending by a char-server or a remote administration." RETCODE); + if (display_parse_login) + login_log("- to display normal parse packets on console." RETCODE); + else + login_log("- to NOT display normal parse packets on console." RETCODE); + if (display_parse_admin) + login_log("- to display administration parse packets on console." RETCODE); + else + login_log("- to NOT display administration parse packets on console." RETCODE); + if (display_parse_fromchar) + login_log("- to display char-server parse packets on console." RETCODE); + else + login_log("- to NOT display char-server parse packets on console." RETCODE); + + if (min_level_to_connect == 0) // 0: all players, 1-99 at least gm level x + login_log("- with no minimum level for connection." RETCODE); + else if (min_level_to_connect == 99) + login_log("- to accept only GM with level 99." RETCODE); + else + login_log("- to accept only GM with level %d or more." RETCODE, min_level_to_connect); + + if (add_to_unlimited_account) + login_log("- to authorize adjustment (with timeadd ladmin) on an unlimited account." RETCODE); + else + login_log("- to refuse adjustment (with timeadd ladmin) on an unlimited account. You must use timeset (ladmin command) before." RETCODE); + + if (start_limited_time < 0) + login_log("- to create new accounts with an unlimited time." RETCODE); + else if (start_limited_time == 0) + login_log("- to create new accounts with a limited time: time of creation." RETCODE); + else + login_log("- to create new accounts with a limited time: time of creation + %d second(s)." RETCODE, start_limited_time); + + if (check_ip_flag) + login_log("- with control of players IP between login-server and char-server." RETCODE); + else + login_log("- to not check players IP between login-server and char-server." RETCODE); + + if (access_order == ACO_DENY_ALLOW) { + if (access_denynum == 0) { + login_log("- with the IP security order: 'deny,allow' (allow if not deny). You refuse no IP." RETCODE); + } else if (access_denynum == 1 && access_deny[0] == '\0') { + login_log("- with the IP security order: 'deny,allow' (allow if not deny). You refuse ALL IP." RETCODE); + } else { + login_log("- with the IP security order: 'deny,allow' (allow if not deny). Refused IP are:" RETCODE); + for(i = 0; i < access_denynum; i++) + login_log(" %s" RETCODE, (char *)(access_deny + i * ACO_STRSIZE)); + } + } else if (access_order == ACO_ALLOW_DENY) { + if (access_allownum == 0) { + login_log("- with the IP security order: 'allow,deny' (deny if not allow). But, NO IP IS AUTHORISED!" RETCODE); + } else if (access_allownum == 1 && access_allow[0] == '\0') { + login_log("- with the IP security order: 'allow,deny' (deny if not allow). You authorise ALL IP." RETCODE); + } else { + login_log("- with the IP security order: 'allow,deny' (deny if not allow). Authorised IP are:" RETCODE); + for(i = 0; i < access_allownum; i++) + login_log(" %s" RETCODE, (char *)(access_allow + i * ACO_STRSIZE)); + } + } else { // ACO_MUTUAL_FAILTURE + login_log("- with the IP security order: 'mutual-failture' (allow if in the allow list and not in the deny list)." RETCODE); + if (access_allownum == 0) { + login_log(" But, NO IP IS AUTHORISED!" RETCODE); + } else if (access_denynum == 1 && access_deny[0] == '\0') { + login_log(" But, you refuse ALL IP!" RETCODE); + } else { + if (access_allownum == 1 && access_allow[0] == '\0') { + login_log(" You authorise ALL IP." RETCODE); + } else { + login_log(" Authorised IP are:" RETCODE); + for(i = 0; i < access_allownum; i++) + login_log(" %s" RETCODE, (char *)(access_allow + i * ACO_STRSIZE)); + } + login_log(" Refused IP are:" RETCODE); + for(i = 0; i < access_denynum; i++) + login_log(" %s" RETCODE, (char *)(access_deny + i * ACO_STRSIZE)); + } + + // dynamic password error ban + if (dynamic_pass_failure_ban == 0) + login_log("- with NO dynamic password error ban." RETCODE); + else { + login_log("- with a dynamic password error ban:" RETCODE); + login_log(" After %d invalid password in %d minutes" RETCODE, dynamic_pass_failure_ban_how_many, dynamic_pass_failure_ban_time); + login_log(" IP is banned for %d minutes" RETCODE, dynamic_pass_failure_ban_how_long); + } + } +} + +//-------------------------------------- +// Function called at exit of the server +//-------------------------------------- +void do_final(void) { + int i, fd; + ShowInfo("Terminating...\n"); + fflush(stdout); + mmo_auth_sync(); + online_db->destroy(online_db, NULL); + + if(auth_dat) aFree(auth_dat); + if(gm_account_db) aFree(gm_account_db); + if(access_ladmin_allow) aFree(access_ladmin_allow); + if(access_allow) aFree(access_allow); + if(access_deny) aFree(access_deny); + for (i = 0; i < MAX_SERVERS; i++) { + if ((fd = server_fd[i]) >= 0) { + server_fd[i] = -1; + memset(&server[i], 0, sizeof(struct mmo_char_server)); + do_close(fd); + } + } + do_close(login_fd); + + login_log("----End of login-server (normal end with closing of all files)." RETCODE); + + if(log_fp) + fclose(log_fp); + ShowStatus("Finished.\n"); +} + +//------------------------------ +// Main function of login-server +//------------------------------ +void set_server_type(void) +{ + SERVER_TYPE = ATHENA_SERVER_LOGIN; +} +int do_init(int argc, char **argv) { + int i, j; + + // read login-server configuration + login_config_read((argc > 1) ? argv[1] : LOGIN_CONF_NAME); + display_conf_warnings(); // not in login_config_read, because we can use 'import' option, and display same message twice or more + save_config_in_log(); // not before, because log file name can be changed + login_lan_config_read((argc > 2) ? argv[2] : LAN_CONF_NAME); + + srand((unsigned int)time(NULL)); + + for(i = 0; i< AUTH_FIFO_SIZE; i++) + auth_fifo[i].delflag = 1; + for(i = 0; i < MAX_SERVERS; i++) + server_fd[i] = -1; + + gm_account_db = NULL; + GM_num = 0; + GM_max = 0; + mmo_auth_init(); + read_gm_account(); + set_defaultparse(parse_login); + // Online user database init + online_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); // reinitialise + add_timer_func_list(waiting_disconnect_timer, "waiting_disconnect_timer"); + + login_fd = make_listen_bind(bind_ip?bind_ip:INADDR_ANY,login_port); + + add_timer_func_list(check_auth_sync, "check_auth_sync"); + add_timer_interval(gettick() + 60000, check_auth_sync, 0, 0, 60000); // every 60 sec we check if we must save accounts file (only if necessary to save) + + // add timer to check GM accounts file modification + j = gm_account_filename_check_timer; + if (j == 0) // if we would not to check, we check every 60 sec, just to have timer (if we change timer, is was not necessary to check if timer already exists) + j = 60; + + add_timer_func_list(check_GM_file, "check_GM_file"); + add_timer_interval(gettick() + j * 1000, check_GM_file, 0, 0, j * 1000); // every x sec we check if gm file has been changed + + + add_timer_func_list(online_data_cleanup, "online_data_cleanup"); + add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600*1000); // every 10 minutes cleanup online account db. + + if (ip_sync_interval) { + add_timer_func_list(sync_ip_addresses, "sync_ip_addresses"); + add_timer_interval(gettick() + ip_sync_interval, sync_ip_addresses, 0, 0, ip_sync_interval); + } + if(console) { + set_defaultconsoleparse(parse_console); + start_console(); + } + + login_log("The login-server is ready (Server is listening on the port %d)." RETCODE, login_port); + ShowStatus("The login-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", login_port); + + return 0; +} diff --git a/src/login/md5calc.c b/src/login/md5calc.c index 5c52670c7..fd8ffd5d3 100644 --- a/src/login/md5calc.c +++ b/src/login/md5calc.c @@ -1,236 +1,236 @@ -/*********************************************************** - * md5 calculation algorithm - * - * The source code referred to the following URL. - * http://www.geocities.co.jp/SiliconValley-Oakland/8878/lab17/lab17.html - * - ***********************************************************/ - -#include "md5calc.h" -#include -#include - -#ifndef UINT_MAX -#define UINT_MAX 4294967295U -#endif - -// Global variable -static unsigned int *pX; - -// String Table -static const unsigned int T[] = { - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0 - 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4 - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8 - 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12 - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16 - 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, //20 - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24 - 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28 - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32 - 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36 - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, //40 - 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44 - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48 - 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52 - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56 - 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 //60 -}; - -// ROTATE_LEFT The left is made to rotate x [ n-bit ]. This is diverted as it is from RFC. -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - -// The function used for other calculation -static unsigned int F(unsigned int X, unsigned int Y, unsigned int Z) -{ - return (X & Y) | (~X & Z); -} -static unsigned int G(unsigned int X, unsigned int Y, unsigned int Z) -{ - return (X & Z) | (Y & ~Z); -} -static unsigned int H(unsigned int X, unsigned int Y, unsigned int Z) -{ - return X ^ Y ^ Z; -} -static unsigned int I(unsigned int X, unsigned int Y, unsigned int Z) -{ - return Y ^ (X | ~Z); -} - -static unsigned int Round(unsigned int a, unsigned int b, unsigned int FGHI, - unsigned int k, unsigned int s, unsigned int i) -{ - return b + ROTATE_LEFT(a + FGHI + pX[k] + T[i], s); -} - -static void Round1(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, F(b,c,d), k, s, i); -} -static void Round2(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, G(b,c,d), k, s, i); -} -static void Round3(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, H(b,c,d), k, s, i); -} -static void Round4(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, I(b,c,d), k, s, i); -} - -static void MD5_Round_Calculate(const unsigned char *block, - unsigned int *A2, unsigned int *B2, unsigned int *C2, unsigned int *D2) -{ - //create X It is since it is required. - unsigned int X[16]; //512bit 64byte - int j,k; - - //Save A as AA, B as BB, C as CC, and and D as DD (saving of A, B, C, and D) - unsigned int A=*A2, B=*B2, C=*C2, D=*D2; - unsigned int AA = A,BB = B,CC = C,DD = D; - - //It is a large region variable reluctantly because of calculation of a round. . . for Round1...4 - pX = X; - - //Copy block(padding_message) i into X - for (j=0,k=0; j<64; j+=4,k++) - X[k] = ( (unsigned int )block[j] ) // 8byte*4 -> 32byte conversion - | ( ((unsigned int )block[j+1]) << 8 ) // A function called Decode as used in the field of RFC - | ( ((unsigned int )block[j+2]) << 16 ) - | ( ((unsigned int )block[j+3]) << 24 ); - - - //Round 1 - Round1(&A,B,C,D, 0, 7, 0); Round1(&D,A,B,C, 1, 12, 1); Round1(&C,D,A,B, 2, 17, 2); Round1(&B,C,D,A, 3, 22, 3); - Round1(&A,B,C,D, 4, 7, 4); Round1(&D,A,B,C, 5, 12, 5); Round1(&C,D,A,B, 6, 17, 6); Round1(&B,C,D,A, 7, 22, 7); - Round1(&A,B,C,D, 8, 7, 8); Round1(&D,A,B,C, 9, 12, 9); Round1(&C,D,A,B, 10, 17, 10); Round1(&B,C,D,A, 11, 22, 11); - Round1(&A,B,C,D, 12, 7, 12); Round1(&D,A,B,C, 13, 12, 13); Round1(&C,D,A,B, 14, 17, 14); Round1(&B,C,D,A, 15, 22, 15); - - //Round 2 - Round2(&A,B,C,D, 1, 5, 16); Round2(&D,A,B,C, 6, 9, 17); Round2(&C,D,A,B, 11, 14, 18); Round2(&B,C,D,A, 0, 20, 19); - Round2(&A,B,C,D, 5, 5, 20); Round2(&D,A,B,C, 10, 9, 21); Round2(&C,D,A,B, 15, 14, 22); Round2(&B,C,D,A, 4, 20, 23); - Round2(&A,B,C,D, 9, 5, 24); Round2(&D,A,B,C, 14, 9, 25); Round2(&C,D,A,B, 3, 14, 26); Round2(&B,C,D,A, 8, 20, 27); - Round2(&A,B,C,D, 13, 5, 28); Round2(&D,A,B,C, 2, 9, 29); Round2(&C,D,A,B, 7, 14, 30); Round2(&B,C,D,A, 12, 20, 31); - - //Round 3 - Round3(&A,B,C,D, 5, 4, 32); Round3(&D,A,B,C, 8, 11, 33); Round3(&C,D,A,B, 11, 16, 34); Round3(&B,C,D,A, 14, 23, 35); - Round3(&A,B,C,D, 1, 4, 36); Round3(&D,A,B,C, 4, 11, 37); Round3(&C,D,A,B, 7, 16, 38); Round3(&B,C,D,A, 10, 23, 39); - Round3(&A,B,C,D, 13, 4, 40); Round3(&D,A,B,C, 0, 11, 41); Round3(&C,D,A,B, 3, 16, 42); Round3(&B,C,D,A, 6, 23, 43); - Round3(&A,B,C,D, 9, 4, 44); Round3(&D,A,B,C, 12, 11, 45); Round3(&C,D,A,B, 15, 16, 46); Round3(&B,C,D,A, 2, 23, 47); - - //Round 4 - Round4(&A,B,C,D, 0, 6, 48); Round4(&D,A,B,C, 7, 10, 49); Round4(&C,D,A,B, 14, 15, 50); Round4(&B,C,D,A, 5, 21, 51); - Round4(&A,B,C,D, 12, 6, 52); Round4(&D,A,B,C, 3, 10, 53); Round4(&C,D,A,B, 10, 15, 54); Round4(&B,C,D,A, 1, 21, 55); - Round4(&A,B,C,D, 8, 6, 56); Round4(&D,A,B,C, 15, 10, 57); Round4(&C,D,A,B, 6, 15, 58); Round4(&B,C,D,A, 13, 21, 59); - Round4(&A,B,C,D, 4, 6, 60); Round4(&D,A,B,C, 11, 10, 61); Round4(&C,D,A,B, 2, 15, 62); Round4(&B,C,D,A, 9, 21, 63); - - // Then perform the following additions. (let's add) - *A2 = A + AA; - *B2 = B + BB; - *C2 = C + CC; - *D2 = D + DD; - - //The clearance of confidential information - memset(pX, 0, sizeof(X)); -} - -//------------------------------------------------------------------- -// The function for the exteriors - -/** output is the coded binary in the character sequence which wants to code string. */ -void MD5_String2binary(const char * string, char * output) -{ -//var - /*8bit*/ - unsigned char padding_message[64]; //Extended message 512bit 64byte - unsigned char *pstring; //The position of string in the present scanning notes is held. - -// unsigned char digest[16]; - /*32bit*/ - unsigned int string_byte_len, //The byte chief of string is held. - string_bit_len, //The bit length of string is held. - copy_len, //The number of bytes which is used by 1-3 and which remained - msg_digest[4]; //Message digest 128bit 4byte - unsigned int *A = &msg_digest[0], //The message digest in accordance with RFC (reference) - *B = &msg_digest[1], - *C = &msg_digest[2], - *D = &msg_digest[3]; - int i; - -//prog - //Step 3.Initialize MD Buffer (although it is the initialization; step 3 of A, B, C, and D -- unavoidable -- a head) - *A = 0x67452301; - *B = 0xefcdab89; - *C = 0x98badcfe; - *D = 0x10325476; - - //Step 1.Append Padding Bits (extension of a mark bit) - //1-1 - string_byte_len = strlen(string); //The byte chief of a character sequence is acquired. - pstring = (unsigned char *)string; //The position of the present character sequence is set. - - //1-2 Repeat calculation until length becomes less than 64 bytes. - for (i=string_byte_len; 64<=i; i-=64,pstring+=64) - MD5_Round_Calculate(pstring, A,B,C,D); - - //1-3 - copy_len = string_byte_len % 64; //The number of bytes which remained is computed. - strncpy((char *)padding_message, (char *)pstring, copy_len); //A message is copied to an extended bit sequence. - memset(padding_message+copy_len, 0, 64 - copy_len); //It buries by 0 until it becomes extended bit length. - padding_message[copy_len] |= 0x80; //The next of a message is 1. - - //1-4 - //If 56 bytes or more (less than 64 bytes) of remainder becomes, it will calculate by extending to 64 bytes. - if (56 <= copy_len) { - MD5_Round_Calculate(padding_message, A,B,C,D); - memset(padding_message, 0, 56); //56 bytes is newly fill uped with 0. - } - - - //Step 2.Append Length (the information on length is added) - string_bit_len = string_byte_len * 8; //From the byte chief to bit length (32 bytes of low rank) - memcpy(&padding_message[56], &string_bit_len, 4); //32 bytes of low rank is set. - - //When bit length cannot be expressed in 32 bytes of low rank, it is a beam raising to a higher rank. - if (UINT_MAX / 8 < string_byte_len) { - unsigned int high = (string_byte_len - UINT_MAX / 8) * 8; - memcpy(&padding_message[60], &high, 4); - } else - memset(&padding_message[60], 0, 4); //In this case, it is good for a higher rank at 0. - - //Step 4.Process Message in 16-Word Blocks (calculation of MD5) - MD5_Round_Calculate(padding_message, A,B,C,D); - - - //Step 5.Output (output) - memcpy(output,msg_digest,16); -// memcpy (digest, msg_digest, and 16); //8 byte*4 < - 32byte conversion A function called Encode as used in the field of RFC -/* sprintf(output, - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - digest[ 0], digest[ 1], digest[ 2], digest[ 3], - digest[ 4], digest[ 5], digest[ 6], digest[ 7], - digest[ 8], digest[ 9], digest[10], digest[11], - digest[12], digest[13], digest[14], digest[15]);*/ -} - -/** output is the coded character sequence in the character sequence which wants to code string. */ -void MD5_String(const char * string, char * output) -{ - unsigned char digest[16]; - - MD5_String2binary(string,(char*)digest); - sprintf(output, - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - digest[ 0], digest[ 1], digest[ 2], digest[ 3], - digest[ 4], digest[ 5], digest[ 6], digest[ 7], - digest[ 8], digest[ 9], digest[10], digest[11], - digest[12], digest[13], digest[14], digest[15]); -} - +/*********************************************************** + * md5 calculation algorithm + * + * The source code referred to the following URL. + * http://www.geocities.co.jp/SiliconValley-Oakland/8878/lab17/lab17.html + * + ***********************************************************/ + +#include "md5calc.h" +#include +#include + +#ifndef UINT_MAX +#define UINT_MAX 4294967295U +#endif + +// Global variable +static unsigned int *pX; + +// String Table +static const unsigned int T[] = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0 + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4 + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8 + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12 + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16 + 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, //20 + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24 + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28 + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32 + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36 + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, //40 + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44 + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48 + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52 + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56 + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 //60 +}; + +// ROTATE_LEFT The left is made to rotate x [ n-bit ]. This is diverted as it is from RFC. +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +// The function used for other calculation +static unsigned int F(unsigned int X, unsigned int Y, unsigned int Z) +{ + return (X & Y) | (~X & Z); +} +static unsigned int G(unsigned int X, unsigned int Y, unsigned int Z) +{ + return (X & Z) | (Y & ~Z); +} +static unsigned int H(unsigned int X, unsigned int Y, unsigned int Z) +{ + return X ^ Y ^ Z; +} +static unsigned int I(unsigned int X, unsigned int Y, unsigned int Z) +{ + return Y ^ (X | ~Z); +} + +static unsigned int Round(unsigned int a, unsigned int b, unsigned int FGHI, + unsigned int k, unsigned int s, unsigned int i) +{ + return b + ROTATE_LEFT(a + FGHI + pX[k] + T[i], s); +} + +static void Round1(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, F(b,c,d), k, s, i); +} +static void Round2(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, G(b,c,d), k, s, i); +} +static void Round3(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, H(b,c,d), k, s, i); +} +static void Round4(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, I(b,c,d), k, s, i); +} + +static void MD5_Round_Calculate(const unsigned char *block, + unsigned int *A2, unsigned int *B2, unsigned int *C2, unsigned int *D2) +{ + //create X It is since it is required. + unsigned int X[16]; //512bit 64byte + int j,k; + + //Save A as AA, B as BB, C as CC, and and D as DD (saving of A, B, C, and D) + unsigned int A=*A2, B=*B2, C=*C2, D=*D2; + unsigned int AA = A,BB = B,CC = C,DD = D; + + //It is a large region variable reluctantly because of calculation of a round. . . for Round1...4 + pX = X; + + //Copy block(padding_message) i into X + for (j=0,k=0; j<64; j+=4,k++) + X[k] = ( (unsigned int )block[j] ) // 8byte*4 -> 32byte conversion + | ( ((unsigned int )block[j+1]) << 8 ) // A function called Decode as used in the field of RFC + | ( ((unsigned int )block[j+2]) << 16 ) + | ( ((unsigned int )block[j+3]) << 24 ); + + + //Round 1 + Round1(&A,B,C,D, 0, 7, 0); Round1(&D,A,B,C, 1, 12, 1); Round1(&C,D,A,B, 2, 17, 2); Round1(&B,C,D,A, 3, 22, 3); + Round1(&A,B,C,D, 4, 7, 4); Round1(&D,A,B,C, 5, 12, 5); Round1(&C,D,A,B, 6, 17, 6); Round1(&B,C,D,A, 7, 22, 7); + Round1(&A,B,C,D, 8, 7, 8); Round1(&D,A,B,C, 9, 12, 9); Round1(&C,D,A,B, 10, 17, 10); Round1(&B,C,D,A, 11, 22, 11); + Round1(&A,B,C,D, 12, 7, 12); Round1(&D,A,B,C, 13, 12, 13); Round1(&C,D,A,B, 14, 17, 14); Round1(&B,C,D,A, 15, 22, 15); + + //Round 2 + Round2(&A,B,C,D, 1, 5, 16); Round2(&D,A,B,C, 6, 9, 17); Round2(&C,D,A,B, 11, 14, 18); Round2(&B,C,D,A, 0, 20, 19); + Round2(&A,B,C,D, 5, 5, 20); Round2(&D,A,B,C, 10, 9, 21); Round2(&C,D,A,B, 15, 14, 22); Round2(&B,C,D,A, 4, 20, 23); + Round2(&A,B,C,D, 9, 5, 24); Round2(&D,A,B,C, 14, 9, 25); Round2(&C,D,A,B, 3, 14, 26); Round2(&B,C,D,A, 8, 20, 27); + Round2(&A,B,C,D, 13, 5, 28); Round2(&D,A,B,C, 2, 9, 29); Round2(&C,D,A,B, 7, 14, 30); Round2(&B,C,D,A, 12, 20, 31); + + //Round 3 + Round3(&A,B,C,D, 5, 4, 32); Round3(&D,A,B,C, 8, 11, 33); Round3(&C,D,A,B, 11, 16, 34); Round3(&B,C,D,A, 14, 23, 35); + Round3(&A,B,C,D, 1, 4, 36); Round3(&D,A,B,C, 4, 11, 37); Round3(&C,D,A,B, 7, 16, 38); Round3(&B,C,D,A, 10, 23, 39); + Round3(&A,B,C,D, 13, 4, 40); Round3(&D,A,B,C, 0, 11, 41); Round3(&C,D,A,B, 3, 16, 42); Round3(&B,C,D,A, 6, 23, 43); + Round3(&A,B,C,D, 9, 4, 44); Round3(&D,A,B,C, 12, 11, 45); Round3(&C,D,A,B, 15, 16, 46); Round3(&B,C,D,A, 2, 23, 47); + + //Round 4 + Round4(&A,B,C,D, 0, 6, 48); Round4(&D,A,B,C, 7, 10, 49); Round4(&C,D,A,B, 14, 15, 50); Round4(&B,C,D,A, 5, 21, 51); + Round4(&A,B,C,D, 12, 6, 52); Round4(&D,A,B,C, 3, 10, 53); Round4(&C,D,A,B, 10, 15, 54); Round4(&B,C,D,A, 1, 21, 55); + Round4(&A,B,C,D, 8, 6, 56); Round4(&D,A,B,C, 15, 10, 57); Round4(&C,D,A,B, 6, 15, 58); Round4(&B,C,D,A, 13, 21, 59); + Round4(&A,B,C,D, 4, 6, 60); Round4(&D,A,B,C, 11, 10, 61); Round4(&C,D,A,B, 2, 15, 62); Round4(&B,C,D,A, 9, 21, 63); + + // Then perform the following additions. (let's add) + *A2 = A + AA; + *B2 = B + BB; + *C2 = C + CC; + *D2 = D + DD; + + //The clearance of confidential information + memset(pX, 0, sizeof(X)); +} + +//------------------------------------------------------------------- +// The function for the exteriors + +/** output is the coded binary in the character sequence which wants to code string. */ +void MD5_String2binary(const char * string, char * output) +{ +//var + /*8bit*/ + unsigned char padding_message[64]; //Extended message 512bit 64byte + unsigned char *pstring; //The position of string in the present scanning notes is held. + +// unsigned char digest[16]; + /*32bit*/ + unsigned int string_byte_len, //The byte chief of string is held. + string_bit_len, //The bit length of string is held. + copy_len, //The number of bytes which is used by 1-3 and which remained + msg_digest[4]; //Message digest 128bit 4byte + unsigned int *A = &msg_digest[0], //The message digest in accordance with RFC (reference) + *B = &msg_digest[1], + *C = &msg_digest[2], + *D = &msg_digest[3]; + int i; + +//prog + //Step 3.Initialize MD Buffer (although it is the initialization; step 3 of A, B, C, and D -- unavoidable -- a head) + *A = 0x67452301; + *B = 0xefcdab89; + *C = 0x98badcfe; + *D = 0x10325476; + + //Step 1.Append Padding Bits (extension of a mark bit) + //1-1 + string_byte_len = strlen(string); //The byte chief of a character sequence is acquired. + pstring = (unsigned char *)string; //The position of the present character sequence is set. + + //1-2 Repeat calculation until length becomes less than 64 bytes. + for (i=string_byte_len; 64<=i; i-=64,pstring+=64) + MD5_Round_Calculate(pstring, A,B,C,D); + + //1-3 + copy_len = string_byte_len % 64; //The number of bytes which remained is computed. + strncpy((char *)padding_message, (char *)pstring, copy_len); //A message is copied to an extended bit sequence. + memset(padding_message+copy_len, 0, 64 - copy_len); //It buries by 0 until it becomes extended bit length. + padding_message[copy_len] |= 0x80; //The next of a message is 1. + + //1-4 + //If 56 bytes or more (less than 64 bytes) of remainder becomes, it will calculate by extending to 64 bytes. + if (56 <= copy_len) { + MD5_Round_Calculate(padding_message, A,B,C,D); + memset(padding_message, 0, 56); //56 bytes is newly fill uped with 0. + } + + + //Step 2.Append Length (the information on length is added) + string_bit_len = string_byte_len * 8; //From the byte chief to bit length (32 bytes of low rank) + memcpy(&padding_message[56], &string_bit_len, 4); //32 bytes of low rank is set. + + //When bit length cannot be expressed in 32 bytes of low rank, it is a beam raising to a higher rank. + if (UINT_MAX / 8 < string_byte_len) { + unsigned int high = (string_byte_len - UINT_MAX / 8) * 8; + memcpy(&padding_message[60], &high, 4); + } else + memset(&padding_message[60], 0, 4); //In this case, it is good for a higher rank at 0. + + //Step 4.Process Message in 16-Word Blocks (calculation of MD5) + MD5_Round_Calculate(padding_message, A,B,C,D); + + + //Step 5.Output (output) + memcpy(output,msg_digest,16); +// memcpy (digest, msg_digest, and 16); //8 byte*4 < - 32byte conversion A function called Encode as used in the field of RFC +/* sprintf(output, + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + digest[ 0], digest[ 1], digest[ 2], digest[ 3], + digest[ 4], digest[ 5], digest[ 6], digest[ 7], + digest[ 8], digest[ 9], digest[10], digest[11], + digest[12], digest[13], digest[14], digest[15]);*/ +} + +/** output is the coded character sequence in the character sequence which wants to code string. */ +void MD5_String(const char * string, char * output) +{ + unsigned char digest[16]; + + MD5_String2binary(string,(char*)digest); + sprintf(output, + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + digest[ 0], digest[ 1], digest[ 2], digest[ 3], + digest[ 4], digest[ 5], digest[ 6], digest[ 7], + digest[ 8], digest[ 9], digest[10], digest[11], + digest[12], digest[13], digest[14], digest[15]); +} + diff --git a/src/login/md5calc.h b/src/login/md5calc.h index 04fb2d8c5..9bc554f69 100644 --- a/src/login/md5calc.h +++ b/src/login/md5calc.h @@ -1,7 +1,7 @@ -#ifndef _MD5CALC_H_ -#define _MD5CALC_H_ - -void MD5_String(const char * string, char * output); -void MD5_String2binary(const char * string, char * output); - -#endif +#ifndef _MD5CALC_H_ +#define _MD5CALC_H_ + +void MD5_String(const char * string, char * output); +void MD5_String2binary(const char * string, char * output); + +#endif diff --git a/src/login_sql/login.c b/src/login_sql/login.c index a0b3770ed..c37329694 100644 --- a/src/login_sql/login.c +++ b/src/login_sql/login.c @@ -1,2398 +1,2398 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include - -#ifdef LCCWIN32 -#include -#else -#ifdef __WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -#else -#include -#include -#include -#include -#endif -#endif - -#include -#include -#include // for stat/lstat/fstat -#include -#include -#include - -//add include for DBMS(mysql) -#include - -#include "../common/core.h" -#include "../common/socket.h" -#include "../common/malloc.h" -#include "../common/db.h" -#include "../common/timer.h" -#include "../common/strlib.h" -#include "../common/mmo.h" -#include "../common/showmsg.h" -#include "../common/version.h" -#include "../common/cbasetypes.h" -#include "login.h" - -#ifdef PASSWORDENC -#include "md5calc.h" -#endif - -#define J_MAX_MALLOC_SIZE 65535 - -//----------------------------------------------------- -// global variable -//----------------------------------------------------- -int use_dnsbl=0; // [Zido] -char dnsbl_servs[1024]; -int server_num; -int new_account_flag = 0; //Set from config too XD [Sirius] -in_addr_t bind_ip= 0; -char bind_ip_str[128]; -int login_port = 6900; - -// Advanced subnet check [LuzZza] -struct _subnet { - long subnet; - long mask; - long char_ip; - long map_ip; -} subnet[16]; - -int subnet_count = 0; - -struct mmo_char_server server[MAX_SERVERS]; -int server_fd[MAX_SERVERS]; - -int login_fd; - -//Account flood protection [Kevin] -unsigned int new_reg_tick=0; -int allowed_regs=1; -int num_regs=0; -int time_allowed=10; //Init this to 10 secs, not 10K secs [Skotlex] - -char date_format[32] = "%Y-%m-%d %H:%M:%S"; -unsigned int auth_num = 0, auth_max = 0; - -int min_level_to_connect = 0; // minimum level of player/GM (0: player, 1-99: gm) to connect on the server -int check_ip_flag = 1; // It's to check IP of a player between login-server and char-server (part of anti-hacking system) -int check_client_version = 0; //Client version check ON/OFF .. (sirius) -int client_version_to_connect = 20; //Client version needed to connect ..(sirius) -static int online_check=1; //When set to 1, login server rejects incoming players that are already registered as online. [Skotlex] -static int ip_sync_interval = 0; - -MYSQL mysql_handle; - -int ipban = 1; -int dynamic_account_ban = 1; -int dynamic_account_ban_class = 0; -int dynamic_pass_failure_ban = 1; -int dynamic_pass_failure_ban_time = 5; -int dynamic_pass_failure_ban_how_many = 3; -int dynamic_pass_failure_ban_how_long = 60; - -int login_server_port = 3306; -char login_server_ip[32] = "127.0.0.1"; -char login_server_id[32] = "ragnarok"; -char login_server_pw[32] = "ragnarok"; -char login_server_db[32] = "ragnarok"; -char default_codepage[32] = ""; //Feature by irmin. -int use_md5_passwds = 0; -char login_db[256] = "login"; -int log_login=1; //Whether to log the logins or not. [Skotlex] -char loginlog_db[256] = "loginlog"; -bool login_gm_read = true; -int connection_ping_interval = 0; - -// added to help out custom login tables, without having to recompile -// source so options are kept in the login_athena.conf or the inter_athena.conf -char login_db_account_id[256] = "account_id"; -char login_db_userid[256] = "userid"; -char login_db_user_pass[256] = "user_pass"; -char login_db_level[256] = "level"; - -char gm_db[256] = "gm_accounts"; - -char reg_db[256] = "global_reg_value"; - -struct gm_account *gm_account_db; -int GM_num; -char tmpsql[65535], tmp_sql[65535]; - -int console = 0; - -int case_sensitive = 1; - -//----------------------------------------------------- - -#define AUTH_FIFO_SIZE 256 -struct { - int account_id,login_id1,login_id2; - int ip,sex,delflag; -} auth_fifo[AUTH_FIFO_SIZE]; - -int auth_fifo_pos = 0; - -struct online_login_data { - int account_id; - short char_server; - short waiting_disconnect; -}; - -//----------------------------------------------------- - -static char md5key[20], md5keylen = 16; - -struct dbt *online_db; - -static void* create_online_user(DBKey key, va_list args) { - struct online_login_data *p; - p = aCalloc(1, sizeof(struct online_login_data)); - p->account_id = key.i; - p->char_server = -1; - return p; -} - -int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len); - -//----------------------------------------------------- -// Online User Database [Wizputer] -//----------------------------------------------------- - -void add_online_user(int char_server, int account_id) { - struct online_login_data *p; - if (!online_check) - return; - p = idb_ensure(online_db, account_id, create_online_user); - p->char_server = char_server; - p->waiting_disconnect = 0; -} - -int is_user_online(int account_id) { - return (idb_get(online_db, account_id) != NULL); -} - -void remove_online_user(int account_id) { - if(!online_check) - return; - if (account_id == 99) { // reset all to offline - online_db->clear(online_db, NULL); - return; - } - idb_remove(online_db,account_id); -} - -int waiting_disconnect_timer(int tid, unsigned int tick, int id, int data) -{ - struct online_login_data *p; - if ((p= idb_get(online_db, id)) != NULL && p->waiting_disconnect) - remove_online_user(id); - return 0; -} - -static int sync_ip_addresses(int tid, unsigned int tick, int id, int data){ - unsigned char buf[2]; - ShowInfo("IP Sync in progress...\n"); - WBUFW(buf,0) = 0x2735; - charif_sendallwos(-1, buf, 2); - return 0; -} - -//----------------------------------------------------- -// Read GM accounts -//----------------------------------------------------- -void read_gm_account(void) { - MYSQL_RES* sql_res ; - MYSQL_ROW sql_row; - - if(!login_gm_read) - return; - sprintf(tmp_sql, "SELECT `%s`,`%s` FROM `%s` WHERE `%s`> '0'",login_db_account_id,login_db_level,login_db,login_db_level); - if (mysql_query(&mysql_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return; //Failed to read GM list! - } - - if (gm_account_db != NULL) - { - aFree(gm_account_db); - gm_account_db = NULL; - } - GM_num = 0; - - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - gm_account_db = (struct gm_account*)aCalloc((size_t)mysql_num_rows(sql_res), sizeof(struct gm_account)); - while ((sql_row = mysql_fetch_row(sql_res))) { - gm_account_db[GM_num].account_id = atoi(sql_row[0]); - gm_account_db[GM_num].level = atoi(sql_row[1]); - GM_num++; - } - mysql_free_result(sql_res); - } -} - -//----------------------------------------------------- -// Send GM accounts to all char-server -//----------------------------------------------------- -void send_GM_accounts(int fd) { - int i; - unsigned char buf[32767]; - int len; - - if(!login_gm_read) - return; - len = 4; - WBUFW(buf,0) = 0x2732; - for(i = 0; i < GM_num; i++) - // send only existing accounts. We can not create a GM account when server is online. - if (gm_account_db[i].level > 0) { - WBUFL(buf,len) = gm_account_db[i].account_id; - WBUFB(buf,len+4) = (unsigned char)gm_account_db[i].level; - len += 5; - if (len >= 32000) { - ShowWarning("send_GM_accounts: Too many accounts! Only %d out of %d were sent.\n", i, GM_num); - break; - } - } - WBUFW(buf,2) = len; - if (fd == -1) - charif_sendallwos(-1, buf, len); - else - { - WFIFOHEAD(fd, len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd, len); - } - return; -} - -//----------------------------------------------------- -// check user level -//----------------------------------------------------- -/* -int isGM(int account_id) { - int level; - - MYSQL_RES* sql_res; - MYSQL_ROW sql_row; - level = 0; - sprintf(tmpsql,"SELECT `%s` FROM `%s` WHERE `%s`='%d'", login_db_level, login_db, login_db_account_id, account_id); - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - sql_row = mysql_fetch_row(sql_res); - level = atoi(sql_row[0]); - if (level > 99) - level = 99; - } - - if (level == 0) { - return 0; - //not GM - } - - mysql_free_result(sql_res); - - return level; -} -*/ - -//--------------------------------------------------- -// E-mail check: return 0 (not correct) or 1 (valid). -//--------------------------------------------------- -int e_mail_check(char *email) { - char ch; - char* last_arobas; - - // athena limits - if (strlen(email) < 3 || strlen(email) > 39) - return 0; - - // part of RFC limits (official reference of e-mail description) - if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') - return 0; - - if (email[strlen(email)-1] == '.') - return 0; - - last_arobas = strrchr(email, '@'); - - if (strstr(last_arobas, "@.") != NULL || - strstr(last_arobas, "..") != NULL) - return 0; - - for(ch = 1; ch < 32; ch++) { - if (strchr(last_arobas, ch) != NULL) { - return 0; - break; - } - } - - if (strchr(last_arobas, ' ') != NULL || - strchr(last_arobas, ';') != NULL) - return 0; - - // all correct - return 1; -} - -/*====================================================== - * Does a mysql_ping to all connection handles. [Skotlex] - *------------------------------------------------------ - */ -int login_sql_ping(int tid, unsigned int tick, int id, int data) -{ - ShowInfo("Pinging SQL server to keep connection alive...\n"); - mysql_ping(&mysql_handle); - return 0; -} - -//----------------------------------------------------- -// Read Account database - mysql db -//----------------------------------------------------- -int mmo_auth_sqldb_init(void) { - - ShowStatus("Login server init....\n"); - - // memory initialize - ShowStatus("memory initialize....\n"); - - mysql_init(&mysql_handle); - - // DB connection start - ShowStatus("Connect Login Database Server....\n"); - if (!mysql_real_connect(&mysql_handle, login_server_ip, login_server_id, login_server_pw, - login_server_db, login_server_port, (char *)NULL, 0)) { - // pointer check - ShowFatalError("%s\n", mysql_error(&mysql_handle)); - exit(1); - } else { - ShowStatus("Connect success!\n"); - } - if( strlen(default_codepage) > 0 ) { - sprintf( tmpsql, "SET NAMES %s", default_codepage ); - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } - - if (log_login) - { - sprintf(tmpsql, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '0', 'lserver','100','login server started')", loginlog_db); - - //query - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } - - if (connection_ping_interval) { - add_timer_func_list(login_sql_ping, "login_sql_ping"); - add_timer_interval(gettick()+connection_ping_interval*60*60*1000, - login_sql_ping, 0, 0, connection_ping_interval*60*60*1000); - } - return 0; -} - -//----------------------------------------------------- -// DB server connect check -//----------------------------------------------------- -void mmo_auth_sqldb_sync(void) { - // db connect check? or close? - // ping pong DB server -if losted? then connect try. else crash. -} - -//----------------------------------------------------- -// close DB -//----------------------------------------------------- -void mmo_db_close(void) { - int i, fd; - - //set log. - if (log_login) - { - sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '0', 'lserver','100', 'login server shutdown')", loginlog_db); - - //query - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } -/* - //delete all server status - sprintf(tmpsql,"DELETE FROM `sstatus`"); - //query - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - mysql_close(&mysql_handle); - ShowStatus("close DB connect....\n"); -*/ - - for (i = 0; i < MAX_SERVERS; i++) { - if ((fd = server_fd[i]) >= 0) - { //Clean only data related to servers we are connected to. [Skotlex] - sprintf(tmpsql,"DELETE FROM `sstatus` WHERE `index` = '%d'", i); - if (mysql_query(&mysql_handle, tmpsql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - delete_session(fd); - } - } - mysql_close(&mysql_handle); - ShowStatus("close DB connect....\n"); - delete_session(login_fd); -} - -//----------------------------------------------------- -// Make new account -//----------------------------------------------------- -int mmo_auth_new(struct mmo_account* account, char sex) -{ - MYSQL_RES* sql_res; - unsigned int tick = gettick(); - char user_password[256]; - //Account Registration Flood Protection by [Kevin] - if(tick <= new_reg_tick && num_regs >= allowed_regs) { - ShowNotice("Account registration denied (registration limit exceeded)\n"); - return 3; - } - - //Check for preexisting account - sprintf(tmp_sql, "SELECT `%s` FROM `%s` WHERE `userid` = '%s'", login_db_userid, login_db, account->userid); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 1; //Return Incorrect user/pass? - } - - sql_res = mysql_store_result(&mysql_handle); - if(mysql_num_rows(sql_res) > 0){ - mysql_free_result(sql_res); - return 1; //Already exists, return incorrect user/pass. - } - mysql_free_result(sql_res); //Only needed for the already-exists check... - - mysql_real_escape_string(&mysql_handle, account->userid, account->userid, strlen(account->userid)); - mysql_real_escape_string(&mysql_handle, account->passwd, account->passwd, strlen(account->passwd)); - - if (sex == 'f') sex = 'F'; - else if (sex == 'm') sex = 'M'; - if (use_md5_passwds) - MD5_String(account->passwd,user_password); - else - jstrescapecpy(user_password, account->passwd); - - ShowInfo("New account: user: %s with passwd: %s sex: %c\n", account->userid, user_password, sex); - - sprintf(tmp_sql, "INSERT INTO `%s` (`%s`, `%s`, `sex`, `email`) VALUES ('%s', '%s', '%c', '%s')", login_db, login_db_userid, login_db_user_pass, account->userid, user_password, sex, "a@a.com"); - - if(mysql_query(&mysql_handle, tmp_sql)){ - //Failed to insert new acc :/ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 1; - } - - if(mysql_field_count(&mysql_handle) == 0 && - mysql_insert_id(&mysql_handle) < START_ACCOUNT_NUM) { - //Invalid Account ID! Must update it. - int id = (int)mysql_insert_id(&mysql_handle); - sprintf(tmp_sql, "UPDATE `%s` SET `%s`='%d' WHERE `%s`='%d'", login_db, login_db_account_id, START_ACCOUNT_NUM, login_db_account_id, id); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowError("New account %s has an invalid account ID [%d] which could not be updated (account_id must be %d or higher).", account->userid, id, START_ACCOUNT_NUM); - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - //Just delete it and fail. - sprintf(tmp_sql, "DELETE FROM `%s` WHERE `%s`='%d'", login_db, login_db_account_id, id); - if(mysql_query(&mysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - return 1; - } - ShowNotice("Updated New account %s's ID %d->%d (account_id must be %d or higher).", account->userid, id, START_ACCOUNT_NUM, START_ACCOUNT_NUM); - } - if(tick > new_reg_tick) - { //Update the registration check. - num_regs=0; - new_reg_tick=gettick()+time_allowed*1000; - } - num_regs++; - - return 0; -} - -// Send to char -int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { - int i, c; - int fd; - - c = 0; - for(i = 0; i < MAX_SERVERS; i++) { - if ((fd = server_fd[i]) > 0 && fd != sfd) { - WFIFOHEAD(fd,len); - if (WFIFOSPACE(fd) < len) //Increase buffer size. - realloc_writefifo(fd, len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - c++; - } - } - - return c; -} - -//----------------------------------------------------- -// Auth -//----------------------------------------------------- -int mmo_auth( struct mmo_account* account , int fd){ - time_t ban_until_time, raw_time; - char tmpstr[256]; - char t_uid[256], t_pass[256]; - char user_password[256]; - char *dnsbl_serv; - - //added for account creation _M _F - int len; - - MYSQL_RES* sql_res; - MYSQL_ROW sql_row; - //int sql_fields, sql_cnt; - char md5str[64], md5bin[32]; - - char ip[16]; - - unsigned char * sin_addr = (unsigned char *)&session[fd]->client_addr.sin_addr.s_addr; - - char r_ip[16]; // [Zido] - char ip_dnsbl[256]; // [Zido] - - // Start DNS Blacklist check [Zido] - if(use_dnsbl) { - sprintf(r_ip, "%d.%d.%d.%d", sin_addr[3], sin_addr[2], sin_addr[1], sin_addr[0]); - - dnsbl_serv=strtok(dnsbl_servs,","); - sprintf(ip_dnsbl,"%s.%s",r_ip,dnsbl_serv); -// Using directly gethostbyname should be quicker. [Skotlex] -// if(resolve_hostbyname(ip_dnsbl, NULL, NULL)) { - if(gethostbyname(ip_dnsbl)) { - ShowInfo("DNSBL: (%s) Blacklisted. User Kicked.\n",ip); - return 3; - } - - while((dnsbl_serv=strtok(dnsbl_servs,","))!=NULL) { - sprintf(ip_dnsbl,"%s.%s",r_ip,dnsbl_serv); -// Using directly gethostbyname should be quicker. [Skotlex] -// if(resolve_hostbyname(ip_dnsbl, NULL, NULL)) { - if(gethostbyname(ip_dnsbl)) { - ShowInfo("DNSBL: (%s) Blacklisted. User Kicked.\n",ip); - return 3; - } - } - - } - // End DNS Blacklist check [Zido] - - sprintf(ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], sin_addr[2], sin_addr[3]); - //ShowInfo("auth start for %s...\n", ip); - - //accountreg with _M/_F .. [Sirius] - len = strlen(account->userid) -2; - - if (account->passwdenc == 0 && account->userid[len] == '_' && - (account->userid[len+1] == 'F' || account->userid[len+1] == 'M' || - account->userid[len+1] == 'f' || account->userid[len+1] == 'm') && - new_account_flag == 1 && - len >= 4 && strlen(account->passwd) >= 4) - { - int result; - account->userid[len] = '\0'; //Terminating the name. - if ((result = mmo_auth_new(account, account->userid[len+1]))) - return result; //Failed to make account. [Skotlex]. - } - - // auth start : time seed - // Platform/Compiler dependant clock() for time check is removed. [Lance] - // clock() is originally used to track processing ticks on program execution. - time(&raw_time); - strftime(tmpstr, 24, "%Y-%m-%d %H:%M:%S",localtime(&raw_time)); - - jstrescapecpy(t_uid,account->userid); - - if (account->passwdenc==PASSWORDENC) { - memcpy(t_pass, account->passwd, NAME_LENGTH); - t_pass[NAME_LENGTH] = '\0'; - } else - jstrescapecpy(t_pass, account->passwd); - - - // make query - sprintf(tmpsql, "SELECT `%s`,`%s`,`%s`,`lastlogin`,`logincount`,`sex`,`connect_until`,`last_ip`,`ban_until`,`state`,`%s`" - " FROM `%s` WHERE `%s`= %s '%s'", login_db_account_id, login_db_userid, login_db_user_pass, login_db_level, login_db, login_db_userid, case_sensitive ? "BINARY" : "", t_uid); - //login {0-account_id/1-userid/2-user_pass/3-lastlogin/4-logincount/5-sex/6-connect_untl/7-last_ip/8-ban_until/9-state/10-level} - - // query - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res) { - sql_row = mysql_fetch_row(sql_res); //row fetching - if (!sql_row) { - //there's no id. - ShowNotice("auth failed: no such account %s %s %s\n", tmpstr, account->userid, account->passwd); - mysql_free_result(sql_res); - return 0; - } - } else { - ShowError("mmo_auth DB result error ! \n"); - return 0; - } - - //Client Version check[Sirius] - if(check_client_version == 1 && account->version != 0){ - if(account->version != client_version_to_connect){ - mysql_free_result(sql_res); - return 5; - } - } - - // Documented by CLOWNISIUS || LLRO || Gunstar lead this one with me - // IF changed to diferent returns~ you get diferent responses from your msgstringtable.txt - //Ireturn 2 == line 9 - //Ireturn 5 == line 311 - //Ireturn 6 == line 450 - //Ireturn 7 == line 440 - //Ireturn 8 == line 682 - //Ireturn 9 == line 704 - //Ireturn 10 == line 705 - //Ireturn 11 == line 706 - //Ireturn 12 == line 707 - //Ireturn 13 == line 708 - //Ireturn 14 == line 709 - //Ireturn 15 == line 710 - //Ireturn -1 == line 010 - // Check status - { - int encpasswdok = 0; - - if (atoi(sql_row[9]) == -3) { - //id is banned - mysql_free_result(sql_res); - return -3; - } else if (atoi(sql_row[9]) == -2) { //dynamic ban - //id is banned - mysql_free_result(sql_res); - //add IP list. - return -2; - } - - if (use_md5_passwds) { - MD5_String(account->passwd,user_password); - } else { - jstrescapecpy(user_password, account->passwd); - } - //ShowInfo("account id ok encval:%d\n",account->passwdenc); -#ifdef PASSWORDENC - if (account->passwdenc > 0) { - int j = account->passwdenc; - //ShowInfo("start md5calc..\n"); - if (j > 2) - j = 1; - do { - if (j == 1) { - sprintf(md5str, "%s%s", md5key,sql_row[2]); - } else if (j == 2) { - sprintf(md5str, "%s%s", sql_row[2], md5key); - } else - md5str[0] = 0; - //ShowDebug("j:%d mdstr:%s\n", j, md5str); - MD5_String2binary(md5str, md5bin); - encpasswdok = (memcmp(user_password, md5bin, 16) == 0); - } while (j < 2 && !encpasswdok && (j++) != account->passwdenc); - //printf("key[%s] md5 [%s] ", md5key, md5); - //ShowInfo("client [%s] accountpass [%s]\n", user_password, sql_row[2]); - //ShowInfo("end md5calc..\n"); - } -#endif - if ((strcmp(user_password, sql_row[2]) && !encpasswdok)) { - if (account->passwdenc == 0) { - ShowNotice("auth failed pass error %s %s %s" RETCODE, tmpstr, account->userid, user_password); -#ifdef PASSWORDENC - } else { - char logbuf[1024], *p = logbuf; - int j; - p += sprintf(p, "auth failed pass error %s %s recv-md5[", tmpstr, account->userid); - for(j = 0; j < 16; j++) - p += sprintf(p, "%02x", ((unsigned char *)user_password)[j]); - p += sprintf(p, "] calc-md5["); - for(j = 0; j < 16; j++) - p += sprintf(p, "%02x", ((unsigned char *)md5bin)[j]); - p += sprintf(p, "] md5key["); - for(j = 0; j < md5keylen; j++) - p += sprintf(p, "%02x", ((unsigned char *)md5key)[j]); - p += sprintf(p, "]" RETCODE); - ShowNotice("%s\n", p); -#endif - } - return 1; - } - //ShowInfo("auth ok %s %s" RETCODE, tmpstr, account->userid); - } - -/* -// do not remove this section. this is meant for future, and current forums usage -// as a login manager and CP for login server. [CLOWNISIUS] - if (atoi(sql_row[10]) == 1) { - return 4; - } - - if (atoi(sql_row[10]) >= 5) { - switch(atoi(sql_row[10])) { - case 5: - return 5; - break; - case 6: - return 7; - break; - case 7: - return 9; - break; - case 8: - return 10; - break; - case 9: - return 11; - break; - default: - return 10; - break; - } - } -*/ - ban_until_time = atol(sql_row[8]); - - //login {0-account_id/1-userid/2-user_pass/3-lastlogin/4-logincount/5-sex/6-connect_untl/7-last_ip/8-ban_until/9-state} - if (ban_until_time != 0) { // if account is banned - if (ban_until_time > time(NULL)) // always banned - return 6; // 6 = Your are Prohibited to log in until %s - - sprintf(tmpsql, "UPDATE `%s` SET `ban_until`='0' WHERE `%s`= %s '%s'", login_db, login_db_userid, case_sensitive ? "BINARY" : "", t_uid); - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } - - if (atoi(sql_row[9])) { - switch(atoi(sql_row[9])) { // packet 0x006a value + 1 - case 1: // 0 = Unregistered ID - case 2: // 1 = Incorrect Password - case 3: // 2 = This ID is expired - case 4: // 3 = Rejected from Server - case 5: // 4 = You have been blocked by the GM Team - case 6: // 5 = Your Game's EXE file is not the latest version - case 7: // 6 = Your are Prohibited to log in until %s - case 8: // 7 = Server is jammed due to over populated - case 9: // 8 = No more accounts may be connected from this company - case 10: // 9 = MSI_REFUSE_BAN_BY_DBA - case 11: // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED - case 12: // 11 = MSI_REFUSE_BAN_BY_GM - case 13: // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK - case 14: // 13 = MSI_REFUSE_SELF_LOCK - case 15: // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP - case 16: // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP - case 100: // 99 = This ID has been totally erased - case 101: // 100 = Login information remains at %s. - case 102: // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information - case 103: // 102 = This account has been temporarily prohibited from login due to a bug-related investigation - case 104: // 103 = This character is being deleted. Login is temporarily unavailable for the time being - case 105: // 104 = Your spouse character is being deleted. Login is temporarily unavailable for the time being - ShowNotice("Auth Error #%d\n", atoi(sql_row[9])); - return atoi(sql_row[9]) - 1; - break; - default: - return 99; // 99 = ID has been totally erased - break; - } - } - - if (atol(sql_row[6]) != 0 && atol(sql_row[6]) < time(NULL)) { - return 2; // 2 = This ID is expired - } - - if (online_check) { - struct online_login_data* data = idb_get(online_db,atoi(sql_row[0])); - unsigned char buf[8]; - if (data && data->char_server > -1) { - //Request char servers to kick this account out. [Skotlex] - ShowWarning("User [%s] is already online - Rejected.\n",sql_row[1]); - WBUFW(buf,0) = 0x2734; - WBUFL(buf,2) = atol(sql_row[0]); - charif_sendallwos(-1, buf, 6); - if (!data->waiting_disconnect) - add_timer(gettick()+30000, waiting_disconnect_timer, atol(sql_row[0]), 0); - data->waiting_disconnect = 1; - return 3; // Rejected - } - } - - account->account_id = atoi(sql_row[0]); - account->login_id1 = rand(); - account->login_id2 = rand(); - memcpy(tmpstr, sql_row[3], 19); - memcpy(account->lastlogin, tmpstr, 24); - account->sex = sql_row[5][0] == 'S' ? 2 : sql_row[5][0]=='M'; - account->level = atoi(sql_row[10]) > 99 ? 99 : atoi(sql_row[10]); // as was in isGM() [zzo] - - if (account->sex != 2 && account->account_id < START_ACCOUNT_NUM) - ShowWarning("Account %s has account id %d! Account IDs must be over %d to work properly!\n", account->userid, account->account_id, START_ACCOUNT_NUM); - sprintf(tmpsql, "UPDATE `%s` SET `lastlogin` = NOW(), `logincount`=`logincount` +1, `last_ip`='%s' WHERE `%s` = %s '%s'", - login_db, ip, login_db_userid, case_sensitive ? "BINARY" : "", sql_row[1]); - mysql_free_result(sql_res) ; //resource free - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - - return -1; -} - -static int online_db_setoffline(DBKey key, void* data, va_list ap) { - struct online_login_data *p = (struct online_login_data *)data; - int server = va_arg(ap, int); - if (server == -1) { - p->char_server = -1; - p->waiting_disconnect = 0; - } else if (p->char_server == server) - p->char_server = -2; //Char server disconnected. - return 0; -} - -//----------------------------------------------------- -// char-server packet parse -//----------------------------------------------------- -int parse_fromchar(int fd){ - int i, id; - MYSQL_RES* sql_res; - MYSQL_ROW sql_row = NULL; - - unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr.s_addr; - unsigned long ipl = session[fd]->client_addr.sin_addr.s_addr; - char ip[16]; - RFIFOHEAD(fd); - - sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - - for(id = 0; id < MAX_SERVERS; id++) - if (server_fd[id] == fd) - break; - - if (id == MAX_SERVERS) - session[fd]->eof = 1; - if(session[fd]->eof) { - if (id < MAX_SERVERS) { - ShowStatus("Char-server '%s' has disconnected.\n", server[id].name); - server_fd[id] = -1; - memset(&server[id], 0, sizeof(struct mmo_char_server)); - online_db->foreach(online_db,online_db_setoffline,id); //Set all chars from this char server to offline. - // server delete - sprintf(tmpsql, "DELETE FROM `sstatus` WHERE `index`='%d'", id); - // query - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } - do_close(fd); - return 0; - } - - while(RFIFOREST(fd) >= 2) { -// printf("char_parse: %d %d packet case=%x\n", fd, RFIFOREST(fd), RFIFOW(fd, 0)); - - switch (RFIFOW(fd,0)) { - case 0x2709: - if (log_login) - { - sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`log`) VALUES (NOW(), '%u', '%s', 'GM reload request')", loginlog_db, (unsigned int)ntohl(ipl),server[id].name); - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } - read_gm_account(); - // send GM accounts to all char-servers - send_GM_accounts(-1); - RFIFOSKIP(fd,2); - break; - - case 0x2712: - if (RFIFOREST(fd) < 19) - return 0; - { - int account_id; - WFIFOHEAD(fd,51); - account_id = RFIFOL(fd,2); // speed up - for(i=0;i %d\n", i); - break; - } - } - - if (i != AUTH_FIFO_SIZE && account_id > 0) { // send ack - time_t connect_until_time = 0; - char email[40] = ""; - account_id=RFIFOL(fd,2); - sprintf(tmpsql, "SELECT `email`,`connect_until` FROM `%s` WHERE `%s`='%d'", login_db, login_db_account_id, account_id); - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res) { - sql_row = mysql_fetch_row(sql_res); - connect_until_time = atol(sql_row[1]); - strcpy(email, sql_row[0]); - mysql_free_result(sql_res); - } - WFIFOW(fd,0) = 0x2713; - WFIFOL(fd,2) = account_id; - WFIFOB(fd,6) = 0; - memcpy(WFIFOP(fd, 7), email, 40); - WFIFOL(fd,47) = (unsigned long) connect_until_time; - WFIFOSET(fd,51); - } else { - WFIFOW(fd,0) = 0x2713; - WFIFOL(fd,2) = account_id; - WFIFOB(fd,6) = 1; - WFIFOSET(fd,51); - } - RFIFOSKIP(fd,19); - break; - } - - case 0x2714: - if (RFIFOREST(fd) < 6) - return 0; - // how many users on world? (update) - if (server[id].users != RFIFOL(fd,2)) - { - ShowStatus("set users %s : %d\n", server[id].name, RFIFOL(fd,2)); - - server[id].users = RFIFOL(fd,2); - sprintf(tmpsql,"UPDATE `sstatus` SET `user` = '%d' WHERE `index` = '%d'", server[id].users, id); - // query - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } - { // send some answer - WFIFOHEAD(fd,6); - WFIFOW(fd,0) = 0x2718; - WFIFOSET(fd,2); - } - RFIFOSKIP(fd,6); - break; - - // We receive an e-mail/limited time request, because a player comes back from a map-server to the char-server - case 0x2716: - if (RFIFOREST(fd) < 6) - return 0; - { - int account_id; - time_t connect_until_time = 0; - char email[40] = ""; - WFIFOHEAD(fd,50); - account_id=RFIFOL(fd,2); - sprintf(tmpsql,"SELECT `email`,`connect_until` FROM `%s` WHERE `%s`='%d'",login_db, login_db_account_id, account_id); - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - sql_res = mysql_store_result(&mysql_handle) ; - if (sql_res) { - sql_row = mysql_fetch_row(sql_res); - connect_until_time = atol(sql_row[1]); - strcpy(email, sql_row[0]); - } - mysql_free_result(sql_res); - //printf("parse_fromchar: E-mail/limited time request from '%s' server (concerned account: %d)\n", server[id].name, RFIFOL(fd,2)); - WFIFOW(fd,0) = 0x2717; - WFIFOL(fd,2) = RFIFOL(fd,2); - memcpy(WFIFOP(fd, 6), email, 40); - WFIFOL(fd,46) = (unsigned long) connect_until_time; - WFIFOSET(fd,50); - } - RFIFOSKIP(fd,6); - break; - - case 0x2720: // GM - if (RFIFOREST(fd) < 4) - return 0; - if (RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - //oldacc = RFIFOL(fd,4); - ShowWarning("change GM isn't supported in this login server version.\n"); - ShowError("change GM error 0 %s\n", RFIFOP(fd, 8)); - - RFIFOSKIP(fd, RFIFOW(fd, 2)); - { - WFIFOHEAD(fd, 10); - WFIFOW(fd, 0) = 0x2721; - WFIFOL(fd, 2) = RFIFOL(fd,4); // oldacc; - WFIFOL(fd, 6) = 0; // newacc; - WFIFOSET(fd, 10); - } - return 0; - - // Map server send information to change an email of an account via char-server - case 0x2722: // 0x2722 .L .40B .40B - if (RFIFOREST(fd) < 86) - return 0; - { - int acc; - char actual_email[40], new_email[40]; - acc = RFIFOL(fd,2); - memcpy(actual_email, RFIFOP(fd,6), 40); - memcpy(new_email, RFIFOP(fd,46), 40); - if (e_mail_check(actual_email) == 0) - ShowWarning("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)" RETCODE, - server[id].name, acc, ip); - else if (e_mail_check(new_email) == 0) - ShowWarning("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a invalid new e-mail (account: %d, ip: %s)" RETCODE, - server[id].name, acc, ip); - else if (strcmpi(new_email, "a@a.com") == 0) - ShowWarning("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a default e-mail (account: %d, ip: %s)" RETCODE, - server[id].name, acc, ip); - else { - sprintf(tmpsql, "SELECT `%s`,`email` FROM `%s` WHERE `%s` = '%d'", login_db_userid, login_db, login_db_account_id, acc); - if (mysql_query(&mysql_handle, tmpsql)) - { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - sql_row = mysql_fetch_row(sql_res); //row fetching - - if (strcmpi(sql_row[1], actual_email) == 0) { - sprintf(tmpsql, "UPDATE `%s` SET `email` = '%s' WHERE `%s` = '%d'", login_db, new_email, login_db_account_id, acc); - // query - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - ShowInfo("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s)." RETCODE, - server[id].name, acc, sql_row[0], actual_email, ip); - } - } - - } - RFIFOSKIP(fd, 86); - break; - } - - case 0x2724: // Receiving of map-server via char-server a status change resquest (by Yor) - if (RFIFOREST(fd) < 10) - return 0; - { - int acc, statut; - acc = RFIFOL(fd,2); - statut = RFIFOL(fd,6); - sprintf(tmpsql, "SELECT `state` FROM `%s` WHERE `%s` = '%d'", login_db, login_db_account_id, acc); - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - sql_row = mysql_fetch_row(sql_res); // row fetching - } - if (atoi(sql_row[0]) != statut && statut != 0) { - unsigned char buf[16]; - WBUFW(buf,0) = 0x2731; - WBUFL(buf,2) = acc; - WBUFB(buf,6) = 0; // 0: change of statut, 1: ban - WBUFL(buf,7) = statut; // status or final date of a banishment - charif_sendallwos(-1, buf, 11); - } - sprintf(tmpsql,"UPDATE `%s` SET `state` = '%d' WHERE `%s` = '%d'", login_db, statut,login_db_account_id,acc); - //query - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - RFIFOSKIP(fd,10); - break; - } - - case 0x2725: // Receiving of map-server via char-server a ban resquest (by Yor) - if (RFIFOREST(fd) < 18) - return 0; - { - int acc; - struct tm *tmtime; - time_t timestamp, tmptime; - acc = RFIFOL(fd,2); - sprintf(tmpsql, "SELECT `ban_until` FROM `%s` WHERE `%s` = '%d'",login_db,login_db_account_id,acc); - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - sql_res = mysql_store_result(&mysql_handle); - if (sql_res) { - sql_row = mysql_fetch_row(sql_res); // row fetching - } - tmptime = atol(sql_row[0]); - if (tmptime == 0 || tmptime < time(NULL)) - timestamp = time(NULL); - else - timestamp = tmptime; - tmtime = localtime(×tamp); - tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,6); - tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,8); - tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,10); - tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,12); - tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,14); - tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,16); - timestamp = mktime(tmtime); - if (timestamp != -1) { - if (timestamp <= time(NULL)) - timestamp = 0; - if (tmptime != timestamp) { - if (timestamp != 0) { - unsigned char buf[16]; - WBUFW(buf,0) = 0x2731; - WBUFL(buf,2) = acc; - WBUFB(buf,6) = 1; // 0: change of statut, 1: ban - WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment - charif_sendallwos(-1, buf, 11); - } - ShowNotice("Account: %d Banned until: %ld\n", acc, timestamp); - sprintf(tmpsql, "UPDATE `%s` SET `ban_until` = '%ld' WHERE `%s` = '%d'", login_db, (unsigned long)timestamp, login_db_account_id, acc); - // query - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } - } - RFIFOSKIP(fd,18); - break; - } - - case 0x2727: - if (RFIFOREST(fd) < 6) - return 0; - { - int acc,sex; - unsigned char buf[16]; - acc=RFIFOL(fd,2); - sprintf(tmpsql,"SELECT `sex` FROM `%s` WHERE `%s` = '%d'",login_db,login_db_account_id,acc); - - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - return 0; - } - - sql_res = mysql_store_result(&mysql_handle) ; - - if (sql_res) { - if (mysql_num_rows(sql_res) == 0) { - mysql_free_result(sql_res); - return 0; - } - sql_row = mysql_fetch_row(sql_res); //row fetching - } - - if (strcmpi(sql_row[0], "M") == 0) - sex = 0; //Change to female - else - sex = 1; //Change to make - sprintf(tmpsql,"UPDATE `%s` SET `sex` = '%c' WHERE `%s` = '%d'", login_db, (sex?'M':'F'), login_db_account_id, acc); - //query - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - WBUFW(buf,0) = 0x2723; - WBUFL(buf,2) = acc; - WBUFB(buf,6) = sex; - charif_sendallwos(-1, buf, 7); - RFIFOSKIP(fd,6); - break; - } - - case 0x2728: // save account_reg2 - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - if (RFIFOL(fd,4) > 0) { - int acc,p,j,len; - char str[32]; - char temp_str[64]; //Needs twice as much space as the original string. - char temp_str2[512]; - char value[256]; - unsigned char *buf; - acc=RFIFOL(fd,4); - buf = (unsigned char*)aCalloc(RFIFOW(fd,2)+1, sizeof(unsigned char)); - //Delete all global account variables.... - sprintf(tmpsql,"DELETE FROM `%s` WHERE `type`='1' AND `account_id`='%d';",reg_db,acc); - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - //Proceed to insert them.... - for(j=0,p=13;j 0) { //Found a match - sprintf(tmpsql,"UPDATE `%s` SET `ban_until` = '0' WHERE `%s` = '%d'", login_db,login_db_account_id,acc); - //query - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } - if (sql_res) mysql_free_result(sql_res); - RFIFOSKIP(fd,6); - } - return 0; - - case 0x272b: // Set account_id to online [Wizputer] - if (RFIFOREST(fd) < 6) - return 0; - add_online_user(id, RFIFOL(fd,2)); - RFIFOSKIP(fd,6); - break; - - case 0x272c: // Set account_id to offline [Wizputer] - if (RFIFOREST(fd) < 6) - return 0; - remove_online_user(RFIFOL(fd,2)); - RFIFOSKIP(fd,6); - break; - case 0x272d: // Receive list of all online accounts. [Skotlex] - if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) - return 0; - if (!online_check) { - RFIFOSKIP(fd,RFIFOW(fd,2)); - break; - } - { - struct online_login_data *p; - int aid, users; - online_db->foreach(online_db,online_db_setoffline,id); //Set all chars from this char-server offline first - users = RFIFOW(fd,4); - for (i = 0; i < users; i++) { - aid = RFIFOL(fd,6+i*4); - p = idb_ensure(online_db, aid, create_online_user); - p->char_server = id; - } - RFIFOSKIP(fd,RFIFOW(fd,2)); - break; - } - case 0x272e: //Request account_reg2 for a character. - if (RFIFOREST(fd) < 10) - return 0; - { - int account_id = RFIFOL(fd, 2); - int char_id = RFIFOL(fd, 6); - int p; - WFIFOHEAD(fd,10000); - RFIFOSKIP(fd,10); - sprintf(tmpsql, "SELECT `str`,`value` FROM `%s` WHERE `type`='1' AND `account_id`='%d'",reg_db, account_id); - if (mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - break; - } - sql_res = mysql_store_result(&mysql_handle) ; - if (!sql_res) { - break; - } - WFIFOW(fd,0) = 0x2729; - WFIFOL(fd,4) = account_id; - WFIFOL(fd,8) = char_id; - WFIFOB(fd,12) = 1; //Type 1 for Account2 registry - for(p = 13; (sql_row = mysql_fetch_row(sql_res)) && p < 9000;){ - if (sql_row[0][0]) { - p+= sprintf(WFIFOP(fd,p), "%s", sql_row[0])+1; //We add 1 to consider the '\0' in place. - p+= sprintf(WFIFOP(fd,p), "%s", sql_row[1])+1; - } - } - if (p >= 9000) - ShowWarning("Too many account2 registries for AID %d. Some registries were not sent.\n", account_id); - WFIFOW(fd,2) = p; - WFIFOSET(fd,WFIFOW(fd,2)); - mysql_free_result(sql_res); - } - break; - - case 0x2736: // WAN IP update from char-server - if (RFIFOREST(fd) < 6) - return 0; - ShowInfo("Updated IP of Server #%d to %d.%d.%d.%d.\n",id, - (int)RFIFOB(fd,2),(int)RFIFOB(fd,3), - (int)RFIFOB(fd,4),(int)RFIFOB(fd,5)); - server[id].ip = RFIFOL(fd,2); - RFIFOSKIP(fd,6); - break; - - case 0x2737: //Request to set all offline. - ShowInfo("Setting accounts from char-server %d offline.\n", id); - online_db->foreach(online_db,online_db_setoffline,id); - RFIFOSKIP(fd,2); - break; - - default: - ShowError("login: unknown packet %x! (from char).\n", RFIFOW(fd,0)); - session[fd]->eof = 1; - return 0; - } - } - - RFIFOSKIP(fd,RFIFOREST(fd)); - return 0; -} - -//-------------------------------------------- -// Test to know if an IP come from LAN or WAN. -// Rewrote: Adnvanced subnet check [LuzZza] -//-------------------------------------------- -int lan_subnetcheck(long p) { - - int i; - unsigned char *sbn, *msk, *src = (unsigned char *)&p; - - for(i=0; iclient_addr.sin_addr.s_addr; - unsigned long ipl = session[fd]->client_addr.sin_addr.s_addr; - char ip[16]; - RFIFOHEAD(fd); - - sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - - memset(&account, 0, sizeof(account)); - - if (session[fd]->eof) { - for(i = 0; i < MAX_SERVERS; i++) - if (server_fd[i] == fd) - server_fd[i] = -1; - do_close(fd); - return 0; - } - - while(RFIFOREST(fd)>=2 && !session[fd]->eof){ -// ShowDebug("parse_login : %d %d packet case=%x\n", fd, RFIFOREST(fd), RFIFOW(fd,0)); - - switch(RFIFOW(fd,0)){ - case 0x200: // New alive packet: structure: 0x200 .24B. used to verify if client is always alive. - if (RFIFOREST(fd) < 26) - return 0; - RFIFOSKIP(fd,26); - break; - - case 0x204: // New alive packet: structure: 0x204 .16B. (new ragexe from 22 june 2004) - if (RFIFOREST(fd) < 18) - return 0; - RFIFOSKIP(fd,18); - break; - - case 0x277: // New login packet - case 0x64: // request client login - case 0x01dd: // request client login with encrypt - - packet_len = RFIFOREST(fd); - - //Perform ip-ban check ONLY on login packets - if (ipban > 0 && login_ip_ban_check(p,ipl)) - { - RFIFOSKIP(fd,packet_len); - session[fd]->eof = 1; - break; - } - - switch(RFIFOW(fd,0)){ - case 0x64: - if(packet_len < 55) - return 0; - break; - case 0x01dd: - if(packet_len < 47) - return 0; - break; - case 0x277: - if(packet_len < 84) - return 0; - break; - } - - account.version = RFIFOL(fd, 2); - if (!account.version) account.version = 1; //Force some version... - memcpy(account.userid,RFIFOP(fd, 6),NAME_LENGTH); - account.userid[23] = '\0'; - memcpy(account.passwd,RFIFOP(fd, 30),NAME_LENGTH); - account.passwd[23] = '\0'; - - ShowInfo("client connection request %s from %d.%d.%d.%d\n", RFIFOP(fd, 6), p[0], p[1], p[2], p[3]); -#ifdef PASSWORDENC - account.passwdenc= (RFIFOW(fd,0)!=0x01dd)?0:PASSWORDENC; -#else - account.passwdenc=0; -#endif - result=mmo_auth(&account, fd); - - - jstrescapecpy(t_uid,account.userid); - if(result==-1){ - // as we have queried account level earlier in mmo_auth anyway, no need to do this again [zzo] - // int gm_level = isGM(account.account_id); // removed by [zzo] - - if (min_level_to_connect > account.level) { - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - } else { - WFIFOHEAD(fd,47+32*MAX_SERVERS); - if (p[0] != 127 && log_login) { - sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s','100', 'login ok')", loginlog_db, (unsigned int)ntohl(ipl), t_uid); - //query - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } - if (account.level) - ShowStatus("Connection of the GM (level:%d) account '%s' accepted.\n", account.level, account.userid); - else - ShowStatus("Connection of the account '%s' accepted.\n", account.userid); - server_num=0; - for(i = 0; i < MAX_SERVERS; i++) { - if (server_fd[i] >= 0) { - // Advanced subnet check [LuzZza] - if((subnet_char_ip = lan_subnetcheck(ipl))) - WFIFOL(fd,47+server_num*32) = subnet_char_ip; - else - WFIFOL(fd,47+server_num*32) = server[i].ip; - WFIFOW(fd,47+server_num*32+4) = server[i].port; - memcpy(WFIFOP(fd,47+server_num*32+6), server[i].name, 20); - WFIFOW(fd,47+server_num*32+26) = server[i].users; - WFIFOW(fd,47+server_num*32+28) = server[i].maintenance; - WFIFOW(fd,47+server_num*32+30) = server[i].new_; - server_num++; - } - } - // if at least 1 char-server - if (server_num > 0) { - WFIFOW(fd,0)=0x69; - WFIFOW(fd,2)=47+32*server_num; - WFIFOL(fd,4)=account.login_id1; - WFIFOL(fd,8)=account.account_id; - WFIFOL(fd,12)=account.login_id2; - WFIFOL(fd,16)=0; - memcpy(WFIFOP(fd,20),account.lastlogin,24); - WFIFOB(fd,46)=account.sex; - WFIFOSET(fd,47+32*server_num); - if(auth_fifo_pos>=AUTH_FIFO_SIZE) - auth_fifo_pos=0; - auth_fifo[auth_fifo_pos].account_id=account.account_id; - auth_fifo[auth_fifo_pos].login_id1=account.login_id1; - auth_fifo[auth_fifo_pos].login_id2=account.login_id2; - auth_fifo[auth_fifo_pos].sex=account.sex; - auth_fifo[auth_fifo_pos].delflag=0; - auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr.sin_addr.s_addr; - auth_fifo_pos++; - } else { - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - } - } - } else { - char tmp_sql[512]; - char error[64]; - WFIFOHEAD(fd,23); - if (log_login) - { - sprintf(tmp_sql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s', '%d','login failed : %%s')", loginlog_db, (unsigned int)ntohl(ipl), t_uid, result); - switch((result + 1)) { - case -2: //-3 = Account Banned - sprintf(tmpsql,tmp_sql,"Account banned."); - sprintf(error,"Account banned."); - break; - case -1: //-2 = Dynamic Ban - sprintf(tmpsql,tmp_sql,"dynamic ban (ip and account)."); - sprintf(error,"dynamic ban (ip and account)."); - break; - case 1: // 0 = Unregistered ID - sprintf(tmpsql,tmp_sql,"Unregisterd ID."); - sprintf(error,"Unregisterd ID."); - break; - case 2: // 1 = Incorrect Password - sprintf(tmpsql,tmp_sql,"Incorrect Password."); - sprintf(error,"Incorrect Password."); - break; - case 3: // 2 = This ID is expired - sprintf(tmpsql,tmp_sql,"Account Expired."); - sprintf(error,"Account Expired."); - break; - case 4: // 3 = Rejected from Server - sprintf(tmpsql,tmp_sql,"Rejected from server."); - sprintf(error,"Rejected from server."); - break; - case 5: // 4 = You have been blocked by the GM Team - sprintf(tmpsql,tmp_sql,"Blocked by GM."); - sprintf(error,"Blocked by GM."); - break; - case 6: // 5 = Your Game's EXE file is not the latest version - sprintf(tmpsql,tmp_sql,"Not latest game EXE."); - sprintf(error,"Not latest game EXE."); - break; - case 7: // 6 = Your are Prohibited to log in until %s - sprintf(tmpsql,tmp_sql,"Banned."); - sprintf(error,"Banned."); - break; - case 8: // 7 = Server is jammed due to over populated - sprintf(tmpsql,tmp_sql,"Server Over-population."); - sprintf(error,"Server Over-population."); - break; - case 9: // 8 = No more accounts may be connected from this company - sprintf(tmpsql,tmp_sql,"Account limit from company"); - sprintf(error,"Account limit from company"); - break; - case 10: // 9 = MSI_REFUSE_BAN_BY_DBA - sprintf(tmpsql,tmp_sql,"Ban by DBA"); - sprintf(error,"Ban by DBA"); - break; - case 11: // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED - sprintf(tmpsql,tmp_sql,"Email not confirmed"); - sprintf(error,"Email not confirmed"); - break; - case 12: // 11 = MSI_REFUSE_BAN_BY_GM - sprintf(tmpsql,tmp_sql,"Ban by GM"); - sprintf(error,"Ban by GM"); - break; - case 13: // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK - sprintf(tmpsql,tmp_sql,"Working in DB"); - sprintf(error,"Working in DB"); - break; - case 14: // 13 = MSI_REFUSE_SELF_LOCK - sprintf(tmpsql,tmp_sql,"Self Lock"); - sprintf(error,"Self Lock"); - break; - case 15: // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP - sprintf(tmpsql,tmp_sql,"Not Permitted Group"); - sprintf(error,"Not Permitted Group"); - break; - case 16: // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP - sprintf(tmpsql,tmp_sql,"Not Permitted Group"); - sprintf(error,"Not Permitted Group"); - break; - case 100: // 99 = This ID has been totally erased - sprintf(tmpsql,tmp_sql,"Account gone."); - sprintf(error,"Account gone."); - break; - case 101: // 100 = Login information remains at %s - sprintf(tmpsql,tmp_sql,"Login info remains."); - sprintf(error,"Login info remains."); - break; - case 102: // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information - sprintf(tmpsql,tmp_sql,"Hacking investigation."); - sprintf(error,"Hacking investigation."); - break; - case 103: // 102 = This account has been temporarily prohibited from login due to a bug-related investigation - sprintf(tmpsql,tmp_sql,"Bug investigation."); - sprintf(error,"Bug investigation."); - break; - case 104: // 103 = This character is being deleted. Login is temporarily unavailable for the time being - sprintf(tmpsql,tmp_sql,"Deleting char."); - sprintf(error,"Deleting char."); - break; - case 105: // 104 = This character is being deleted. Login is temporarily unavailable for the time being - sprintf(tmpsql,tmp_sql,"Deleting spouse char."); - sprintf(error,"Deleting spouse char."); - break; - default: - sprintf(tmpsql,tmp_sql,"Unknown Error."); - sprintf(error,"Unknown Error."); - break; - } - //query - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } //End login log of error. - if ((result == 1) && (dynamic_pass_failure_ban != 0) && log_login){ // failed password - sprintf(tmpsql,"SELECT count(*) FROM `%s` WHERE `ip` = '%u' AND `rcode` = '1' AND `time` > NOW() - INTERVAL %d MINUTE", - loginlog_db,(unsigned int)ntohl(ipl), dynamic_pass_failure_ban_time); //how many times filed account? in one ip. - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - //check query result - sql_res = mysql_store_result(&mysql_handle) ; - sql_row = sql_res?mysql_fetch_row(sql_res):NULL; //row fetching - - if (sql_row && atoi(sql_row[0]) >= dynamic_pass_failure_ban_how_many ) { - sprintf(tmpsql,"INSERT INTO `ipbanlist`(`list`,`btime`,`rtime`,`reason`) VALUES ('%d.%d.%d.*', NOW() , NOW() + INTERVAL %d MINUTE ,'Password error ban: %s')", p[0], p[1], p[2], dynamic_pass_failure_ban_how_long, t_uid); - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } - if(sql_res) mysql_free_result(sql_res); - } - else if (result == -2){ //dynamic banned - add ip to ban list. - sprintf(tmpsql,"INSERT INTO `ipbanlist`(`list`,`btime`,`rtime`,`reason`) VALUES ('%d.%d.%d.*', NOW() , NOW() + INTERVAL 1 MONTH ,'Dynamic banned user id : %s')", p[0], p[1], p[2], t_uid); - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - result = -3; - }else if(result == 6){ //not lastet version .. - //result = 5; - } - - sprintf(tmpsql,"SELECT `ban_until` FROM `%s` WHERE `%s` = %s '%s'",login_db, login_db_userid, case_sensitive ? "BINARY" : "", t_uid); - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - sql_res = mysql_store_result(&mysql_handle) ; - sql_row = sql_res?mysql_fetch_row(sql_res):NULL; //row fetching - //cannot connect login failed - memset(WFIFOP(fd,0),'\0',23); - WFIFOW(fd,0)=0x6a; - WFIFOB(fd,2)=result; - if (result == 6) { // 6 = Your are Prohibited to log in until %s - if (sql_row && atol(sql_row[0]) != 0) { // if account is banned, we send ban timestamp - char tmpstr[256]; - time_t ban_until_time; - ban_until_time = atol(sql_row[0]); - strftime(tmpstr, 20, date_format, localtime(&ban_until_time)); - tmpstr[19] = '\0'; - memcpy(WFIFOP(fd,3), tmpstr, 20); - } else { // we send error message - memcpy(WFIFOP(fd,3), error, 20); - } - } - WFIFOSET(fd,23); - } - RFIFOSKIP(fd,packet_len); - break; - - case 0x01db: // request password key - if (session[fd]->session_data) { - ShowWarning("login: abnormal request of MD5 key (already opened session).\n"); - session[fd]->eof = 1; - return 0; - } - { - WFIFOHEAD(fd,4+md5keylen); - WFIFOW(fd,0)=0x01dc; - WFIFOW(fd,2)=4+md5keylen; - memcpy(WFIFOP(fd,4),md5key,md5keylen); - WFIFOSET(fd,WFIFOW(fd,2)); - - ShowDebug("Request Password key -%s\n",md5key); - RFIFOSKIP(fd,2); - } - break; - - case 0x2710: // request Char-server connection - if(RFIFOREST(fd)<86) - return 0; - { - unsigned char* server_name; - WFIFOHEAD(fd, 3); - memcpy(account.userid,RFIFOP(fd, 2),NAME_LENGTH); - account.userid[23] = '\0'; - memcpy(account.passwd,RFIFOP(fd, 26),NAME_LENGTH); - account.passwd[23] = '\0'; - account.passwdenc = 0; - server_name = RFIFOP(fd,60); - server_name[20] = '\0'; - ShowInfo("server connection request %s @ %d.%d.%d.%d:%d (%d.%d.%d.%d)\n", - server_name, RFIFOB(fd, 54), RFIFOB(fd, 55), RFIFOB(fd, 56), RFIFOB(fd, 57), RFIFOW(fd, 58), - p[0], p[1], p[2], p[3]); - jstrescapecpy(t_uid,server_name); - if (log_login) - { - char t_login[50]; - jstrescapecpy(t_login,account.userid); - sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s@%s','100', 'charserver - %s@%d.%d.%d.%d:%d')", - loginlog_db, (unsigned int)ntohl(ipl), - t_login, t_uid, t_uid, - RFIFOB(fd, 54), RFIFOB(fd, 55), RFIFOB(fd, 56), RFIFOB(fd, 57), - RFIFOW(fd, 58)); - - //query - if(mysql_query(&mysql_handle, tmpsql)) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); - } - } - result = mmo_auth(&account, fd); - //printf("Result: %d - Sex: %d - Account ID: %d\n",result,account.sex,(int) account.account_id); - - if(result == -1 && account.sex==2 && account.account_idfunc_parse=parse_fromchar; - realloc_fifo(fd,FIFOSIZE_SERVERLINK,FIFOSIZE_SERVERLINK); - // send GM account to char-server - send_GM_accounts(fd); - } else { - WFIFOW(fd, 0) =0x2711; - WFIFOB(fd, 2)=3; - WFIFOSET(fd, 3); - } - } - RFIFOSKIP(fd, 86); - return 0; - - case 0x7530: // request Athena information - { - WFIFOHEAD(fd,10); - WFIFOW(fd,0)=0x7531; - WFIFOB(fd,2)=ATHENA_MAJOR_VERSION; - WFIFOB(fd,3)=ATHENA_MINOR_VERSION; - WFIFOB(fd,4)=ATHENA_REVISION; - WFIFOB(fd,5)=ATHENA_RELEASE_FLAG; - WFIFOB(fd,6)=ATHENA_OFFICIAL_FLAG; - WFIFOB(fd,7)=ATHENA_SERVER_LOGIN; - WFIFOW(fd,8)=ATHENA_MOD_VERSION; - WFIFOSET(fd,10); - RFIFOSKIP(fd,2); - ShowInfo ("Athena version check...\n"); - break; - } - case 0x7532: - ShowStatus ("End of connection (ip: %s)" RETCODE, ip); - session[fd]->eof = 1; - break; - default: - ShowStatus ("Abnormal end of connection (ip: %s): Unknown packet 0x%x " RETCODE, ip, RFIFOW(fd,0)); - session[fd]->eof = 1; - return 0; - } - } - - RFIFOSKIP(fd,RFIFOREST(fd)); - return 0; -} - -// Console Command Parser [Wizputer] -int parse_console(char *buf) { - char *type,*command; - - type = (char *)aMalloc(64); - command = (char *)aMalloc(64); - - memset(type,0,64); - memset(command,0,64); - - ShowInfo("Console: %s\n",buf); - - if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 ) - sscanf(buf,"%[^\n]",type); - - ShowInfo("Type of command: %s || Command: %s \n",type,command); - - if(buf) aFree(buf); - if(type) aFree(type); - if(command) aFree(command); - - return 0; -} - -static int online_data_cleanup_sub(DBKey key, void *data, va_list ap) -{ - struct online_login_data *character= (struct online_login_data*)data; - if (character->char_server == -2) //Unknown server.. set them offline - remove_online_user(character->account_id); - else if (character->char_server < 0) - //Free data from players that have not been online for a while. - db_remove(online_db, key); - return 0; -} - -static int online_data_cleanup(int tid, unsigned int tick, int id, int data) -{ - online_db->foreach(online_db, online_data_cleanup_sub); - return 0; -} - -//------------------------------------------------- -// Return numerical value of a switch configuration -// on/off, english, fran軋is, deutsch, espaol -//------------------------------------------------- -int config_switch(const char *str) { - if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) - return 1; - if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) - return 0; - - return atoi(str); -} - - -//---------------------------------- -// Reading Lan Support configuration -// Rewrote: Anvanced subnet check [LuzZza] -//---------------------------------- -int login_lan_config_read(const char *lancfgName) { - - FILE *fp; - int line_num = 0; - char line[1024], w1[64], w2[64], w3[64], w4[64]; - - if((fp = fopen(lancfgName, "r")) == NULL) { - ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName); - return 1; - } - - ShowInfo("Reading the configuration file %s...\n", lancfgName); - - while(fgets(line, sizeof(line)-1, fp)) { - - line_num++; - if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n') - continue; - - line[sizeof(line)-1] = '\0'; - if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4) { - - ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num); - continue; - } - - remove_control_chars((unsigned char *)w1); - remove_control_chars((unsigned char *)w2); - remove_control_chars((unsigned char *)w3); - remove_control_chars((unsigned char *)w4); - - if(strcmpi(w1, "subnet") == 0) { - - subnet[subnet_count].mask = inet_addr(w2); - subnet[subnet_count].char_ip = inet_addr(w3); - subnet[subnet_count].map_ip = inet_addr(w4); - subnet[subnet_count].subnet = subnet[subnet_count].char_ip&subnet[subnet_count].mask; - if (subnet[subnet_count].subnet != (subnet[subnet_count].map_ip&subnet[subnet_count].mask)) { - ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4); - continue; - } - - subnet_count++; - } - - ShowStatus("Read information about %d subnetworks.\n", subnet_count); - } - - fclose(fp); - return 0; -} - -//----------------------------------------------------- -//BANNED IP CHECK. -//----------------------------------------------------- -int ip_ban_check(int tid, unsigned int tick, int id, int data){ - - //query - if(mysql_query(&mysql_handle, "DELETE FROM `ipbanlist` WHERE `rtime` <= NOW()")) { - ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - ShowDebug("at %s:%d - DELETE FROM `ipbanlist` WHERE `rtime` <= NOW()\n", __FILE__,__LINE__); - } - - return 0; -} - -//----------------------------------------------------- -// reading configuration -//----------------------------------------------------- -int login_config_read(const char *cfgName){ - int i; - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - fp=fopen(cfgName,"r"); - - if(fp==NULL){ - ShowError("Configuration file (%s) not found.\n", cfgName); - return 1; - } - ShowInfo("reading configuration file %s...\n", cfgName); - while(fgets(line, sizeof(line)-1, fp)){ - if(line[0] == '/' && line[1] == '/') - continue; - - i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2); - if(i!=2) - continue; - - remove_control_chars((unsigned char *) w1); - remove_control_chars((unsigned char *) w2); - if(strcmpi(w1,"timestamp_format") == 0) { - strncpy(timestamp_format, w2, 20); - } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ - stdout_with_ansisequence = config_switch(w2); - } else if(strcmpi(w1,"console_silent")==0){ - msg_silent = 0; //To always allow the next line to show up. - ShowInfo("Console Silent Setting: %d\n", atoi(w2)); - msg_silent = atoi(w2); - } else if (strcmpi(w1, "bind_ip") == 0) { - bind_ip = resolve_hostbyname(w2, NULL, bind_ip_str); - if (bind_ip) - ShowStatus("Login server binding IP address : %s -> %s\n", w2, bind_ip_str); - } else if(strcmpi(w1,"login_port")==0){ - login_port=atoi(w2); - ShowStatus("set login_port : %s\n",w2); - } - else if(strcmpi(w1,"ipban")==0){ - ipban=atoi(w2); - ShowStatus("set ipban : %d\n",ipban); - } - //account ban -> ip ban - else if(strcmpi(w1,"dynamic_account_ban")==0){ - dynamic_account_ban=atoi(w2); - ShowStatus("set dynamic_account_ban : %d\n",dynamic_account_ban); - } - else if(strcmpi(w1,"dynamic_account_ban_class")==0){ - dynamic_account_ban_class=atoi(w2); - ShowStatus("set dynamic_account_ban_class : %d\n",dynamic_account_ban_class); - } - //dynamic password error ban - else if(strcmpi(w1,"dynamic_pass_failure_ban")==0){ - dynamic_pass_failure_ban=atoi(w2); - ShowStatus("set dynamic_pass_failure_ban : %d\n",dynamic_pass_failure_ban); - } - else if(strcmpi(w1,"dynamic_pass_failure_ban_time")==0){ - dynamic_pass_failure_ban_time=atoi(w2); - ShowStatus("set dynamic_pass_failure_ban_time : %d\n",dynamic_pass_failure_ban_time); - } - else if(strcmpi(w1,"dynamic_pass_failure_ban_how_many")==0){ - dynamic_pass_failure_ban_how_many=atoi(w2); - ShowStatus("set dynamic_pass_failure_ban_how_many : %d\n",dynamic_pass_failure_ban_how_many); - } - else if(strcmpi(w1,"dynamic_pass_failure_ban_how_long")==0){ - dynamic_pass_failure_ban_how_long=atoi(w2); - ShowStatus("set dynamic_pass_failure_ban_how_long : %d\n",dynamic_pass_failure_ban_how_long); - } else if(strcmpi(w1, "new_account") == 0){ //Added by Sirius for new account _M/_F - new_account_flag = atoi(w2); //Added by Sirius for new account _M/_F - } else if(strcmpi(w1, "check_client_version") == 0){ //Added by Sirius for client version check - //check_client_version = config_switch(w2); //Added by Sirius for client version check - if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ){ - check_client_version = 1; - } else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ){ - check_client_version = 0; - } - } else if(strcmpi(w1, "client_version_to_connect") == 0){ //Added by Sirius for client version check - client_version_to_connect = atoi(w2); //Added by SIrius for client version check - } else if(strcmpi(w1,"use_MD5_passwords")==0){ - if (!strcmpi(w2,"yes")) { - use_md5_passwds=1; - } else if (!strcmpi(w2,"no")){ - use_md5_passwds=0; - } - ShowStatus("Using MD5 Passwords: %s \n",w2); - } - else if (strcmpi(w1, "date_format") == 0) { // note: never have more than 19 char for the date! - switch (atoi(w2)) { - case 0: - strcpy(date_format, "%d-%m-%Y %H:%M:%S"); // 31-12-2004 23:59:59 - break; - case 1: - strcpy(date_format, "%m-%d-%Y %H:%M:%S"); // 12-31-2004 23:59:59 - break; - case 2: - strcpy(date_format, "%Y-%d-%m %H:%M:%S"); // 2004-31-12 23:59:59 - break; - case 3: - strcpy(date_format, "%Y-%m-%d %H:%M:%S"); // 2004-12-31 23:59:59 - break; - } - } - else if (strcmpi(w1, "min_level_to_connect") == 0) { - min_level_to_connect = atoi(w2); - } - else if (strcmpi(w1, "check_ip_flag") == 0) { - check_ip_flag = config_switch(w2); - } - else if (strcmpi(w1, "console") == 0) { - if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) - console = 1; - } - else if (strcmpi(w1, "case_sensitive") == 0) { - if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) - case_sensitive = 1; - if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ) - case_sensitive = 0; - else - case_sensitive = atoi(w2); - } else if (strcmpi(w1, "allowed_regs") == 0) { //account flood protection system [Kevin] - allowed_regs = atoi(w2); - } else if (strcmpi(w1, "time_allowed") == 0) { - time_allowed = atoi(w2); - } else if (strcmpi(w1, "online_check") == 0) { - if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) - online_check = 1; - else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ) - online_check = 0; - else - online_check = atoi(w2); - } else if (strcmpi(w1, "log_login") == 0) { - if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) - log_login = 1; - else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ) - log_login = 0; - else - log_login = atoi(w2); - } else if (strcmpi(w1, "import") == 0) { - login_config_read(w2); - } else if(strcmpi(w1,"use_dnsbl")==0) { // [Zido] - use_dnsbl=atoi(w2); - } else if(strcmpi(w1,"dnsbl_servers")==0) { // [Zido] - strcpy(dnsbl_servs,w2); - } else if(strcmpi(w1,"ip_sync_interval")==0) { - ip_sync_interval = 1000*60*atoi(w2); //w2 comes in minutes. - } - } - fclose(fp); - ShowInfo("done reading %s.\n", cfgName); - return 0; -} - -void sql_config_read(const char *cfgName){ /* Kalaspuff, to get login_db */ - int i; - char line[1024], w1[1024], w2[1024]; - FILE *fp=fopen(cfgName,"r"); - if(fp==NULL){ - ShowFatalError("file not found: %s\n",cfgName); - exit(1); - } - ShowInfo("reading configuration file %s...\n", cfgName); - while(fgets(line, sizeof(line)-1, fp)){ - if(line[0] == '/' && line[1] == '/') - continue; - i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2); - if(i!=2) - continue; - if(strcmpi(w1, "gm_read_method") == 0) { - if(atoi(w2) == 0) - login_gm_read = true; - else - login_gm_read = false; - } else if(strcmpi(w1, "gm_db") == 0) { - strcpy(gm_db, w2); - } else if (strcmpi(w1, "login_db") == 0) { - strcpy(login_db, w2); - } - //add for DB connection - else if(strcmpi(w1,"login_server_ip")==0){ - strcpy(login_server_ip, w2); - ShowStatus ("set login_server_ip : %s\n",w2); - } - else if(strcmpi(w1,"login_server_port")==0){ - login_server_port=atoi(w2); - ShowStatus ("set login_server_port : %s\n",w2); - } - else if(strcmpi(w1,"login_server_id")==0){ - strcpy(login_server_id, w2); - ShowStatus ("set login_server_id : %s\n",w2); - } - else if(strcmpi(w1,"login_server_pw")==0){ - strcpy(login_server_pw, w2); - ShowStatus ("set login_server_pw : %s\n",w2); - } - else if(strcmpi(w1,"login_server_db")==0){ - strcpy(login_server_db, w2); - ShowStatus ("set login_server_db : %s\n",w2); - } - else if(strcmpi(w1,"connection_ping_interval")==0) { - connection_ping_interval = atoi(w2); - } - else if(strcmpi(w1,"default_codepage")==0){ - strcpy(default_codepage, w2); - ShowStatus ("set default_codepage : %s\n",w2); - } - //added for custom column names for custom login table - else if(strcmpi(w1,"login_db_account_id")==0){ - strcpy(login_db_account_id, w2); - } - else if(strcmpi(w1,"login_db_userid")==0){ - strcpy(login_db_userid, w2); - } - else if(strcmpi(w1,"login_db_user_pass")==0){ - strcpy(login_db_user_pass, w2); - } - else if(strcmpi(w1,"login_db_level")==0){ - strcpy(login_db_level, w2); - } - else if (strcmpi(w1, "loginlog_db") == 0) { - strcpy(loginlog_db, w2); - } - else if (strcmpi(w1, "reg_db") == 0) { - strcpy(reg_db, w2); - } - //support the import command, just like any other config - else if(strcmpi(w1,"import")==0){ - sql_config_read(w2); - } - } - fclose(fp); - ShowInfo("done reading %s.\n", cfgName); -} - -//-------------------------------------- -// Function called at exit of the server -//-------------------------------------- -void do_final(void) { - //sync account when terminating. - //but no need when you using DBMS (mysql) - ShowStatus("Terminating...\n"); - mmo_db_close(); - online_db->destroy(online_db, NULL); - if (gm_account_db) - aFree(gm_account_db); -} - -void set_server_type(void) -{ - SERVER_TYPE = ATHENA_SERVER_LOGIN; -} -int do_init(int argc,char **argv){ - //initialize login server - int i; - - //read login configue - login_config_read( (argc>1)?argv[1]:LOGIN_CONF_NAME ); - sql_config_read(SQL_CONF_NAME); - login_lan_config_read((argc > 2) ? argv[2] : LAN_CONF_NAME); - //Generate Passworded Key. - ShowInfo("Initializing md5key...\n"); - memset(md5key, 0, sizeof(md5key)); - md5keylen=rand()%4+12; - for(i=0;i + +#ifdef LCCWIN32 +#include +#else +#ifdef __WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#else +#include +#include +#include +#include +#endif +#endif + +#include +#include +#include // for stat/lstat/fstat +#include +#include +#include + +//add include for DBMS(mysql) +#include + +#include "../common/core.h" +#include "../common/socket.h" +#include "../common/malloc.h" +#include "../common/db.h" +#include "../common/timer.h" +#include "../common/strlib.h" +#include "../common/mmo.h" +#include "../common/showmsg.h" +#include "../common/version.h" +#include "../common/cbasetypes.h" +#include "login.h" + +#ifdef PASSWORDENC +#include "md5calc.h" +#endif + +#define J_MAX_MALLOC_SIZE 65535 + +//----------------------------------------------------- +// global variable +//----------------------------------------------------- +int use_dnsbl=0; // [Zido] +char dnsbl_servs[1024]; +int server_num; +int new_account_flag = 0; //Set from config too XD [Sirius] +in_addr_t bind_ip= 0; +char bind_ip_str[128]; +int login_port = 6900; + +// Advanced subnet check [LuzZza] +struct _subnet { + long subnet; + long mask; + long char_ip; + long map_ip; +} subnet[16]; + +int subnet_count = 0; + +struct mmo_char_server server[MAX_SERVERS]; +int server_fd[MAX_SERVERS]; + +int login_fd; + +//Account flood protection [Kevin] +unsigned int new_reg_tick=0; +int allowed_regs=1; +int num_regs=0; +int time_allowed=10; //Init this to 10 secs, not 10K secs [Skotlex] + +char date_format[32] = "%Y-%m-%d %H:%M:%S"; +unsigned int auth_num = 0, auth_max = 0; + +int min_level_to_connect = 0; // minimum level of player/GM (0: player, 1-99: gm) to connect on the server +int check_ip_flag = 1; // It's to check IP of a player between login-server and char-server (part of anti-hacking system) +int check_client_version = 0; //Client version check ON/OFF .. (sirius) +int client_version_to_connect = 20; //Client version needed to connect ..(sirius) +static int online_check=1; //When set to 1, login server rejects incoming players that are already registered as online. [Skotlex] +static int ip_sync_interval = 0; + +MYSQL mysql_handle; + +int ipban = 1; +int dynamic_account_ban = 1; +int dynamic_account_ban_class = 0; +int dynamic_pass_failure_ban = 1; +int dynamic_pass_failure_ban_time = 5; +int dynamic_pass_failure_ban_how_many = 3; +int dynamic_pass_failure_ban_how_long = 60; + +int login_server_port = 3306; +char login_server_ip[32] = "127.0.0.1"; +char login_server_id[32] = "ragnarok"; +char login_server_pw[32] = "ragnarok"; +char login_server_db[32] = "ragnarok"; +char default_codepage[32] = ""; //Feature by irmin. +int use_md5_passwds = 0; +char login_db[256] = "login"; +int log_login=1; //Whether to log the logins or not. [Skotlex] +char loginlog_db[256] = "loginlog"; +bool login_gm_read = true; +int connection_ping_interval = 0; + +// added to help out custom login tables, without having to recompile +// source so options are kept in the login_athena.conf or the inter_athena.conf +char login_db_account_id[256] = "account_id"; +char login_db_userid[256] = "userid"; +char login_db_user_pass[256] = "user_pass"; +char login_db_level[256] = "level"; + +char gm_db[256] = "gm_accounts"; + +char reg_db[256] = "global_reg_value"; + +struct gm_account *gm_account_db; +int GM_num; +char tmpsql[65535], tmp_sql[65535]; + +int console = 0; + +int case_sensitive = 1; + +//----------------------------------------------------- + +#define AUTH_FIFO_SIZE 256 +struct { + int account_id,login_id1,login_id2; + int ip,sex,delflag; +} auth_fifo[AUTH_FIFO_SIZE]; + +int auth_fifo_pos = 0; + +struct online_login_data { + int account_id; + short char_server; + short waiting_disconnect; +}; + +//----------------------------------------------------- + +static char md5key[20], md5keylen = 16; + +struct dbt *online_db; + +static void* create_online_user(DBKey key, va_list args) { + struct online_login_data *p; + p = aCalloc(1, sizeof(struct online_login_data)); + p->account_id = key.i; + p->char_server = -1; + return p; +} + +int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len); + +//----------------------------------------------------- +// Online User Database [Wizputer] +//----------------------------------------------------- + +void add_online_user(int char_server, int account_id) { + struct online_login_data *p; + if (!online_check) + return; + p = idb_ensure(online_db, account_id, create_online_user); + p->char_server = char_server; + p->waiting_disconnect = 0; +} + +int is_user_online(int account_id) { + return (idb_get(online_db, account_id) != NULL); +} + +void remove_online_user(int account_id) { + if(!online_check) + return; + if (account_id == 99) { // reset all to offline + online_db->clear(online_db, NULL); + return; + } + idb_remove(online_db,account_id); +} + +int waiting_disconnect_timer(int tid, unsigned int tick, int id, int data) +{ + struct online_login_data *p; + if ((p= idb_get(online_db, id)) != NULL && p->waiting_disconnect) + remove_online_user(id); + return 0; +} + +static int sync_ip_addresses(int tid, unsigned int tick, int id, int data){ + unsigned char buf[2]; + ShowInfo("IP Sync in progress...\n"); + WBUFW(buf,0) = 0x2735; + charif_sendallwos(-1, buf, 2); + return 0; +} + +//----------------------------------------------------- +// Read GM accounts +//----------------------------------------------------- +void read_gm_account(void) { + MYSQL_RES* sql_res ; + MYSQL_ROW sql_row; + + if(!login_gm_read) + return; + sprintf(tmp_sql, "SELECT `%s`,`%s` FROM `%s` WHERE `%s`> '0'",login_db_account_id,login_db_level,login_db,login_db_level); + if (mysql_query(&mysql_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return; //Failed to read GM list! + } + + if (gm_account_db != NULL) + { + aFree(gm_account_db); + gm_account_db = NULL; + } + GM_num = 0; + + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + gm_account_db = (struct gm_account*)aCalloc((size_t)mysql_num_rows(sql_res), sizeof(struct gm_account)); + while ((sql_row = mysql_fetch_row(sql_res))) { + gm_account_db[GM_num].account_id = atoi(sql_row[0]); + gm_account_db[GM_num].level = atoi(sql_row[1]); + GM_num++; + } + mysql_free_result(sql_res); + } +} + +//----------------------------------------------------- +// Send GM accounts to all char-server +//----------------------------------------------------- +void send_GM_accounts(int fd) { + int i; + unsigned char buf[32767]; + int len; + + if(!login_gm_read) + return; + len = 4; + WBUFW(buf,0) = 0x2732; + for(i = 0; i < GM_num; i++) + // send only existing accounts. We can not create a GM account when server is online. + if (gm_account_db[i].level > 0) { + WBUFL(buf,len) = gm_account_db[i].account_id; + WBUFB(buf,len+4) = (unsigned char)gm_account_db[i].level; + len += 5; + if (len >= 32000) { + ShowWarning("send_GM_accounts: Too many accounts! Only %d out of %d were sent.\n", i, GM_num); + break; + } + } + WBUFW(buf,2) = len; + if (fd == -1) + charif_sendallwos(-1, buf, len); + else + { + WFIFOHEAD(fd, len); + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd, len); + } + return; +} + +//----------------------------------------------------- +// check user level +//----------------------------------------------------- +/* +int isGM(int account_id) { + int level; + + MYSQL_RES* sql_res; + MYSQL_ROW sql_row; + level = 0; + sprintf(tmpsql,"SELECT `%s` FROM `%s` WHERE `%s`='%d'", login_db_level, login_db, login_db_account_id, account_id); + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); + level = atoi(sql_row[0]); + if (level > 99) + level = 99; + } + + if (level == 0) { + return 0; + //not GM + } + + mysql_free_result(sql_res); + + return level; +} +*/ + +//--------------------------------------------------- +// E-mail check: return 0 (not correct) or 1 (valid). +//--------------------------------------------------- +int e_mail_check(char *email) { + char ch; + char* last_arobas; + + // athena limits + if (strlen(email) < 3 || strlen(email) > 39) + return 0; + + // part of RFC limits (official reference of e-mail description) + if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') + return 0; + + if (email[strlen(email)-1] == '.') + return 0; + + last_arobas = strrchr(email, '@'); + + if (strstr(last_arobas, "@.") != NULL || + strstr(last_arobas, "..") != NULL) + return 0; + + for(ch = 1; ch < 32; ch++) { + if (strchr(last_arobas, ch) != NULL) { + return 0; + break; + } + } + + if (strchr(last_arobas, ' ') != NULL || + strchr(last_arobas, ';') != NULL) + return 0; + + // all correct + return 1; +} + +/*====================================================== + * Does a mysql_ping to all connection handles. [Skotlex] + *------------------------------------------------------ + */ +int login_sql_ping(int tid, unsigned int tick, int id, int data) +{ + ShowInfo("Pinging SQL server to keep connection alive...\n"); + mysql_ping(&mysql_handle); + return 0; +} + +//----------------------------------------------------- +// Read Account database - mysql db +//----------------------------------------------------- +int mmo_auth_sqldb_init(void) { + + ShowStatus("Login server init....\n"); + + // memory initialize + ShowStatus("memory initialize....\n"); + + mysql_init(&mysql_handle); + + // DB connection start + ShowStatus("Connect Login Database Server....\n"); + if (!mysql_real_connect(&mysql_handle, login_server_ip, login_server_id, login_server_pw, + login_server_db, login_server_port, (char *)NULL, 0)) { + // pointer check + ShowFatalError("%s\n", mysql_error(&mysql_handle)); + exit(1); + } else { + ShowStatus("Connect success!\n"); + } + if( strlen(default_codepage) > 0 ) { + sprintf( tmpsql, "SET NAMES %s", default_codepage ); + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } + + if (log_login) + { + sprintf(tmpsql, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '0', 'lserver','100','login server started')", loginlog_db); + + //query + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } + + if (connection_ping_interval) { + add_timer_func_list(login_sql_ping, "login_sql_ping"); + add_timer_interval(gettick()+connection_ping_interval*60*60*1000, + login_sql_ping, 0, 0, connection_ping_interval*60*60*1000); + } + return 0; +} + +//----------------------------------------------------- +// DB server connect check +//----------------------------------------------------- +void mmo_auth_sqldb_sync(void) { + // db connect check? or close? + // ping pong DB server -if losted? then connect try. else crash. +} + +//----------------------------------------------------- +// close DB +//----------------------------------------------------- +void mmo_db_close(void) { + int i, fd; + + //set log. + if (log_login) + { + sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '0', 'lserver','100', 'login server shutdown')", loginlog_db); + + //query + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } +/* + //delete all server status + sprintf(tmpsql,"DELETE FROM `sstatus`"); + //query + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + mysql_close(&mysql_handle); + ShowStatus("close DB connect....\n"); +*/ + + for (i = 0; i < MAX_SERVERS; i++) { + if ((fd = server_fd[i]) >= 0) + { //Clean only data related to servers we are connected to. [Skotlex] + sprintf(tmpsql,"DELETE FROM `sstatus` WHERE `index` = '%d'", i); + if (mysql_query(&mysql_handle, tmpsql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + delete_session(fd); + } + } + mysql_close(&mysql_handle); + ShowStatus("close DB connect....\n"); + delete_session(login_fd); +} + +//----------------------------------------------------- +// Make new account +//----------------------------------------------------- +int mmo_auth_new(struct mmo_account* account, char sex) +{ + MYSQL_RES* sql_res; + unsigned int tick = gettick(); + char user_password[256]; + //Account Registration Flood Protection by [Kevin] + if(tick <= new_reg_tick && num_regs >= allowed_regs) { + ShowNotice("Account registration denied (registration limit exceeded)\n"); + return 3; + } + + //Check for preexisting account + sprintf(tmp_sql, "SELECT `%s` FROM `%s` WHERE `userid` = '%s'", login_db_userid, login_db, account->userid); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 1; //Return Incorrect user/pass? + } + + sql_res = mysql_store_result(&mysql_handle); + if(mysql_num_rows(sql_res) > 0){ + mysql_free_result(sql_res); + return 1; //Already exists, return incorrect user/pass. + } + mysql_free_result(sql_res); //Only needed for the already-exists check... + + mysql_real_escape_string(&mysql_handle, account->userid, account->userid, strlen(account->userid)); + mysql_real_escape_string(&mysql_handle, account->passwd, account->passwd, strlen(account->passwd)); + + if (sex == 'f') sex = 'F'; + else if (sex == 'm') sex = 'M'; + if (use_md5_passwds) + MD5_String(account->passwd,user_password); + else + jstrescapecpy(user_password, account->passwd); + + ShowInfo("New account: user: %s with passwd: %s sex: %c\n", account->userid, user_password, sex); + + sprintf(tmp_sql, "INSERT INTO `%s` (`%s`, `%s`, `sex`, `email`) VALUES ('%s', '%s', '%c', '%s')", login_db, login_db_userid, login_db_user_pass, account->userid, user_password, sex, "a@a.com"); + + if(mysql_query(&mysql_handle, tmp_sql)){ + //Failed to insert new acc :/ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 1; + } + + if(mysql_field_count(&mysql_handle) == 0 && + mysql_insert_id(&mysql_handle) < START_ACCOUNT_NUM) { + //Invalid Account ID! Must update it. + int id = (int)mysql_insert_id(&mysql_handle); + sprintf(tmp_sql, "UPDATE `%s` SET `%s`='%d' WHERE `%s`='%d'", login_db, login_db_account_id, START_ACCOUNT_NUM, login_db_account_id, id); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowError("New account %s has an invalid account ID [%d] which could not be updated (account_id must be %d or higher).", account->userid, id, START_ACCOUNT_NUM); + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + //Just delete it and fail. + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `%s`='%d'", login_db, login_db_account_id, id); + if(mysql_query(&mysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + return 1; + } + ShowNotice("Updated New account %s's ID %d->%d (account_id must be %d or higher).", account->userid, id, START_ACCOUNT_NUM, START_ACCOUNT_NUM); + } + if(tick > new_reg_tick) + { //Update the registration check. + num_regs=0; + new_reg_tick=gettick()+time_allowed*1000; + } + num_regs++; + + return 0; +} + +// Send to char +int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { + int i, c; + int fd; + + c = 0; + for(i = 0; i < MAX_SERVERS; i++) { + if ((fd = server_fd[i]) > 0 && fd != sfd) { + WFIFOHEAD(fd,len); + if (WFIFOSPACE(fd) < len) //Increase buffer size. + realloc_writefifo(fd, len); + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd,len); + c++; + } + } + + return c; +} + +//----------------------------------------------------- +// Auth +//----------------------------------------------------- +int mmo_auth( struct mmo_account* account , int fd){ + time_t ban_until_time, raw_time; + char tmpstr[256]; + char t_uid[256], t_pass[256]; + char user_password[256]; + char *dnsbl_serv; + + //added for account creation _M _F + int len; + + MYSQL_RES* sql_res; + MYSQL_ROW sql_row; + //int sql_fields, sql_cnt; + char md5str[64], md5bin[32]; + + char ip[16]; + + unsigned char * sin_addr = (unsigned char *)&session[fd]->client_addr.sin_addr.s_addr; + + char r_ip[16]; // [Zido] + char ip_dnsbl[256]; // [Zido] + + // Start DNS Blacklist check [Zido] + if(use_dnsbl) { + sprintf(r_ip, "%d.%d.%d.%d", sin_addr[3], sin_addr[2], sin_addr[1], sin_addr[0]); + + dnsbl_serv=strtok(dnsbl_servs,","); + sprintf(ip_dnsbl,"%s.%s",r_ip,dnsbl_serv); +// Using directly gethostbyname should be quicker. [Skotlex] +// if(resolve_hostbyname(ip_dnsbl, NULL, NULL)) { + if(gethostbyname(ip_dnsbl)) { + ShowInfo("DNSBL: (%s) Blacklisted. User Kicked.\n",ip); + return 3; + } + + while((dnsbl_serv=strtok(dnsbl_servs,","))!=NULL) { + sprintf(ip_dnsbl,"%s.%s",r_ip,dnsbl_serv); +// Using directly gethostbyname should be quicker. [Skotlex] +// if(resolve_hostbyname(ip_dnsbl, NULL, NULL)) { + if(gethostbyname(ip_dnsbl)) { + ShowInfo("DNSBL: (%s) Blacklisted. User Kicked.\n",ip); + return 3; + } + } + + } + // End DNS Blacklist check [Zido] + + sprintf(ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], sin_addr[2], sin_addr[3]); + //ShowInfo("auth start for %s...\n", ip); + + //accountreg with _M/_F .. [Sirius] + len = strlen(account->userid) -2; + + if (account->passwdenc == 0 && account->userid[len] == '_' && + (account->userid[len+1] == 'F' || account->userid[len+1] == 'M' || + account->userid[len+1] == 'f' || account->userid[len+1] == 'm') && + new_account_flag == 1 && + len >= 4 && strlen(account->passwd) >= 4) + { + int result; + account->userid[len] = '\0'; //Terminating the name. + if ((result = mmo_auth_new(account, account->userid[len+1]))) + return result; //Failed to make account. [Skotlex]. + } + + // auth start : time seed + // Platform/Compiler dependant clock() for time check is removed. [Lance] + // clock() is originally used to track processing ticks on program execution. + time(&raw_time); + strftime(tmpstr, 24, "%Y-%m-%d %H:%M:%S",localtime(&raw_time)); + + jstrescapecpy(t_uid,account->userid); + + if (account->passwdenc==PASSWORDENC) { + memcpy(t_pass, account->passwd, NAME_LENGTH); + t_pass[NAME_LENGTH] = '\0'; + } else + jstrescapecpy(t_pass, account->passwd); + + + // make query + sprintf(tmpsql, "SELECT `%s`,`%s`,`%s`,`lastlogin`,`logincount`,`sex`,`connect_until`,`last_ip`,`ban_until`,`state`,`%s`" + " FROM `%s` WHERE `%s`= %s '%s'", login_db_account_id, login_db_userid, login_db_user_pass, login_db_level, login_db, login_db_userid, case_sensitive ? "BINARY" : "", t_uid); + //login {0-account_id/1-userid/2-user_pass/3-lastlogin/4-logincount/5-sex/6-connect_untl/7-last_ip/8-ban_until/9-state/10-level} + + // query + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); //row fetching + if (!sql_row) { + //there's no id. + ShowNotice("auth failed: no such account %s %s %s\n", tmpstr, account->userid, account->passwd); + mysql_free_result(sql_res); + return 0; + } + } else { + ShowError("mmo_auth DB result error ! \n"); + return 0; + } + + //Client Version check[Sirius] + if(check_client_version == 1 && account->version != 0){ + if(account->version != client_version_to_connect){ + mysql_free_result(sql_res); + return 5; + } + } + + // Documented by CLOWNISIUS || LLRO || Gunstar lead this one with me + // IF changed to diferent returns~ you get diferent responses from your msgstringtable.txt + //Ireturn 2 == line 9 + //Ireturn 5 == line 311 + //Ireturn 6 == line 450 + //Ireturn 7 == line 440 + //Ireturn 8 == line 682 + //Ireturn 9 == line 704 + //Ireturn 10 == line 705 + //Ireturn 11 == line 706 + //Ireturn 12 == line 707 + //Ireturn 13 == line 708 + //Ireturn 14 == line 709 + //Ireturn 15 == line 710 + //Ireturn -1 == line 010 + // Check status + { + int encpasswdok = 0; + + if (atoi(sql_row[9]) == -3) { + //id is banned + mysql_free_result(sql_res); + return -3; + } else if (atoi(sql_row[9]) == -2) { //dynamic ban + //id is banned + mysql_free_result(sql_res); + //add IP list. + return -2; + } + + if (use_md5_passwds) { + MD5_String(account->passwd,user_password); + } else { + jstrescapecpy(user_password, account->passwd); + } + //ShowInfo("account id ok encval:%d\n",account->passwdenc); +#ifdef PASSWORDENC + if (account->passwdenc > 0) { + int j = account->passwdenc; + //ShowInfo("start md5calc..\n"); + if (j > 2) + j = 1; + do { + if (j == 1) { + sprintf(md5str, "%s%s", md5key,sql_row[2]); + } else if (j == 2) { + sprintf(md5str, "%s%s", sql_row[2], md5key); + } else + md5str[0] = 0; + //ShowDebug("j:%d mdstr:%s\n", j, md5str); + MD5_String2binary(md5str, md5bin); + encpasswdok = (memcmp(user_password, md5bin, 16) == 0); + } while (j < 2 && !encpasswdok && (j++) != account->passwdenc); + //printf("key[%s] md5 [%s] ", md5key, md5); + //ShowInfo("client [%s] accountpass [%s]\n", user_password, sql_row[2]); + //ShowInfo("end md5calc..\n"); + } +#endif + if ((strcmp(user_password, sql_row[2]) && !encpasswdok)) { + if (account->passwdenc == 0) { + ShowNotice("auth failed pass error %s %s %s" RETCODE, tmpstr, account->userid, user_password); +#ifdef PASSWORDENC + } else { + char logbuf[1024], *p = logbuf; + int j; + p += sprintf(p, "auth failed pass error %s %s recv-md5[", tmpstr, account->userid); + for(j = 0; j < 16; j++) + p += sprintf(p, "%02x", ((unsigned char *)user_password)[j]); + p += sprintf(p, "] calc-md5["); + for(j = 0; j < 16; j++) + p += sprintf(p, "%02x", ((unsigned char *)md5bin)[j]); + p += sprintf(p, "] md5key["); + for(j = 0; j < md5keylen; j++) + p += sprintf(p, "%02x", ((unsigned char *)md5key)[j]); + p += sprintf(p, "]" RETCODE); + ShowNotice("%s\n", p); +#endif + } + return 1; + } + //ShowInfo("auth ok %s %s" RETCODE, tmpstr, account->userid); + } + +/* +// do not remove this section. this is meant for future, and current forums usage +// as a login manager and CP for login server. [CLOWNISIUS] + if (atoi(sql_row[10]) == 1) { + return 4; + } + + if (atoi(sql_row[10]) >= 5) { + switch(atoi(sql_row[10])) { + case 5: + return 5; + break; + case 6: + return 7; + break; + case 7: + return 9; + break; + case 8: + return 10; + break; + case 9: + return 11; + break; + default: + return 10; + break; + } + } +*/ + ban_until_time = atol(sql_row[8]); + + //login {0-account_id/1-userid/2-user_pass/3-lastlogin/4-logincount/5-sex/6-connect_untl/7-last_ip/8-ban_until/9-state} + if (ban_until_time != 0) { // if account is banned + if (ban_until_time > time(NULL)) // always banned + return 6; // 6 = Your are Prohibited to log in until %s + + sprintf(tmpsql, "UPDATE `%s` SET `ban_until`='0' WHERE `%s`= %s '%s'", login_db, login_db_userid, case_sensitive ? "BINARY" : "", t_uid); + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } + + if (atoi(sql_row[9])) { + switch(atoi(sql_row[9])) { // packet 0x006a value + 1 + case 1: // 0 = Unregistered ID + case 2: // 1 = Incorrect Password + case 3: // 2 = This ID is expired + case 4: // 3 = Rejected from Server + case 5: // 4 = You have been blocked by the GM Team + case 6: // 5 = Your Game's EXE file is not the latest version + case 7: // 6 = Your are Prohibited to log in until %s + case 8: // 7 = Server is jammed due to over populated + case 9: // 8 = No more accounts may be connected from this company + case 10: // 9 = MSI_REFUSE_BAN_BY_DBA + case 11: // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED + case 12: // 11 = MSI_REFUSE_BAN_BY_GM + case 13: // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK + case 14: // 13 = MSI_REFUSE_SELF_LOCK + case 15: // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP + case 16: // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP + case 100: // 99 = This ID has been totally erased + case 101: // 100 = Login information remains at %s. + case 102: // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information + case 103: // 102 = This account has been temporarily prohibited from login due to a bug-related investigation + case 104: // 103 = This character is being deleted. Login is temporarily unavailable for the time being + case 105: // 104 = Your spouse character is being deleted. Login is temporarily unavailable for the time being + ShowNotice("Auth Error #%d\n", atoi(sql_row[9])); + return atoi(sql_row[9]) - 1; + break; + default: + return 99; // 99 = ID has been totally erased + break; + } + } + + if (atol(sql_row[6]) != 0 && atol(sql_row[6]) < time(NULL)) { + return 2; // 2 = This ID is expired + } + + if (online_check) { + struct online_login_data* data = idb_get(online_db,atoi(sql_row[0])); + unsigned char buf[8]; + if (data && data->char_server > -1) { + //Request char servers to kick this account out. [Skotlex] + ShowWarning("User [%s] is already online - Rejected.\n",sql_row[1]); + WBUFW(buf,0) = 0x2734; + WBUFL(buf,2) = atol(sql_row[0]); + charif_sendallwos(-1, buf, 6); + if (!data->waiting_disconnect) + add_timer(gettick()+30000, waiting_disconnect_timer, atol(sql_row[0]), 0); + data->waiting_disconnect = 1; + return 3; // Rejected + } + } + + account->account_id = atoi(sql_row[0]); + account->login_id1 = rand(); + account->login_id2 = rand(); + memcpy(tmpstr, sql_row[3], 19); + memcpy(account->lastlogin, tmpstr, 24); + account->sex = sql_row[5][0] == 'S' ? 2 : sql_row[5][0]=='M'; + account->level = atoi(sql_row[10]) > 99 ? 99 : atoi(sql_row[10]); // as was in isGM() [zzo] + + if (account->sex != 2 && account->account_id < START_ACCOUNT_NUM) + ShowWarning("Account %s has account id %d! Account IDs must be over %d to work properly!\n", account->userid, account->account_id, START_ACCOUNT_NUM); + sprintf(tmpsql, "UPDATE `%s` SET `lastlogin` = NOW(), `logincount`=`logincount` +1, `last_ip`='%s' WHERE `%s` = %s '%s'", + login_db, ip, login_db_userid, case_sensitive ? "BINARY" : "", sql_row[1]); + mysql_free_result(sql_res) ; //resource free + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + + return -1; +} + +static int online_db_setoffline(DBKey key, void* data, va_list ap) { + struct online_login_data *p = (struct online_login_data *)data; + int server = va_arg(ap, int); + if (server == -1) { + p->char_server = -1; + p->waiting_disconnect = 0; + } else if (p->char_server == server) + p->char_server = -2; //Char server disconnected. + return 0; +} + +//----------------------------------------------------- +// char-server packet parse +//----------------------------------------------------- +int parse_fromchar(int fd){ + int i, id; + MYSQL_RES* sql_res; + MYSQL_ROW sql_row = NULL; + + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr.s_addr; + unsigned long ipl = session[fd]->client_addr.sin_addr.s_addr; + char ip[16]; + RFIFOHEAD(fd); + + sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + for(id = 0; id < MAX_SERVERS; id++) + if (server_fd[id] == fd) + break; + + if (id == MAX_SERVERS) + session[fd]->eof = 1; + if(session[fd]->eof) { + if (id < MAX_SERVERS) { + ShowStatus("Char-server '%s' has disconnected.\n", server[id].name); + server_fd[id] = -1; + memset(&server[id], 0, sizeof(struct mmo_char_server)); + online_db->foreach(online_db,online_db_setoffline,id); //Set all chars from this char server to offline. + // server delete + sprintf(tmpsql, "DELETE FROM `sstatus` WHERE `index`='%d'", id); + // query + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } + do_close(fd); + return 0; + } + + while(RFIFOREST(fd) >= 2) { +// printf("char_parse: %d %d packet case=%x\n", fd, RFIFOREST(fd), RFIFOW(fd, 0)); + + switch (RFIFOW(fd,0)) { + case 0x2709: + if (log_login) + { + sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`log`) VALUES (NOW(), '%u', '%s', 'GM reload request')", loginlog_db, (unsigned int)ntohl(ipl),server[id].name); + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } + read_gm_account(); + // send GM accounts to all char-servers + send_GM_accounts(-1); + RFIFOSKIP(fd,2); + break; + + case 0x2712: + if (RFIFOREST(fd) < 19) + return 0; + { + int account_id; + WFIFOHEAD(fd,51); + account_id = RFIFOL(fd,2); // speed up + for(i=0;i %d\n", i); + break; + } + } + + if (i != AUTH_FIFO_SIZE && account_id > 0) { // send ack + time_t connect_until_time = 0; + char email[40] = ""; + account_id=RFIFOL(fd,2); + sprintf(tmpsql, "SELECT `email`,`connect_until` FROM `%s` WHERE `%s`='%d'", login_db, login_db_account_id, account_id); + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); + connect_until_time = atol(sql_row[1]); + strcpy(email, sql_row[0]); + mysql_free_result(sql_res); + } + WFIFOW(fd,0) = 0x2713; + WFIFOL(fd,2) = account_id; + WFIFOB(fd,6) = 0; + memcpy(WFIFOP(fd, 7), email, 40); + WFIFOL(fd,47) = (unsigned long) connect_until_time; + WFIFOSET(fd,51); + } else { + WFIFOW(fd,0) = 0x2713; + WFIFOL(fd,2) = account_id; + WFIFOB(fd,6) = 1; + WFIFOSET(fd,51); + } + RFIFOSKIP(fd,19); + break; + } + + case 0x2714: + if (RFIFOREST(fd) < 6) + return 0; + // how many users on world? (update) + if (server[id].users != RFIFOL(fd,2)) + { + ShowStatus("set users %s : %d\n", server[id].name, RFIFOL(fd,2)); + + server[id].users = RFIFOL(fd,2); + sprintf(tmpsql,"UPDATE `sstatus` SET `user` = '%d' WHERE `index` = '%d'", server[id].users, id); + // query + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } + { // send some answer + WFIFOHEAD(fd,6); + WFIFOW(fd,0) = 0x2718; + WFIFOSET(fd,2); + } + RFIFOSKIP(fd,6); + break; + + // We receive an e-mail/limited time request, because a player comes back from a map-server to the char-server + case 0x2716: + if (RFIFOREST(fd) < 6) + return 0; + { + int account_id; + time_t connect_until_time = 0; + char email[40] = ""; + WFIFOHEAD(fd,50); + account_id=RFIFOL(fd,2); + sprintf(tmpsql,"SELECT `email`,`connect_until` FROM `%s` WHERE `%s`='%d'",login_db, login_db_account_id, account_id); + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); + connect_until_time = atol(sql_row[1]); + strcpy(email, sql_row[0]); + } + mysql_free_result(sql_res); + //printf("parse_fromchar: E-mail/limited time request from '%s' server (concerned account: %d)\n", server[id].name, RFIFOL(fd,2)); + WFIFOW(fd,0) = 0x2717; + WFIFOL(fd,2) = RFIFOL(fd,2); + memcpy(WFIFOP(fd, 6), email, 40); + WFIFOL(fd,46) = (unsigned long) connect_until_time; + WFIFOSET(fd,50); + } + RFIFOSKIP(fd,6); + break; + + case 0x2720: // GM + if (RFIFOREST(fd) < 4) + return 0; + if (RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + //oldacc = RFIFOL(fd,4); + ShowWarning("change GM isn't supported in this login server version.\n"); + ShowError("change GM error 0 %s\n", RFIFOP(fd, 8)); + + RFIFOSKIP(fd, RFIFOW(fd, 2)); + { + WFIFOHEAD(fd, 10); + WFIFOW(fd, 0) = 0x2721; + WFIFOL(fd, 2) = RFIFOL(fd,4); // oldacc; + WFIFOL(fd, 6) = 0; // newacc; + WFIFOSET(fd, 10); + } + return 0; + + // Map server send information to change an email of an account via char-server + case 0x2722: // 0x2722 .L .40B .40B + if (RFIFOREST(fd) < 86) + return 0; + { + int acc; + char actual_email[40], new_email[40]; + acc = RFIFOL(fd,2); + memcpy(actual_email, RFIFOP(fd,6), 40); + memcpy(new_email, RFIFOP(fd,46), 40); + if (e_mail_check(actual_email) == 0) + ShowWarning("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)" RETCODE, + server[id].name, acc, ip); + else if (e_mail_check(new_email) == 0) + ShowWarning("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a invalid new e-mail (account: %d, ip: %s)" RETCODE, + server[id].name, acc, ip); + else if (strcmpi(new_email, "a@a.com") == 0) + ShowWarning("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a default e-mail (account: %d, ip: %s)" RETCODE, + server[id].name, acc, ip); + else { + sprintf(tmpsql, "SELECT `%s`,`email` FROM `%s` WHERE `%s` = '%d'", login_db_userid, login_db, login_db_account_id, acc); + if (mysql_query(&mysql_handle, tmpsql)) + { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); //row fetching + + if (strcmpi(sql_row[1], actual_email) == 0) { + sprintf(tmpsql, "UPDATE `%s` SET `email` = '%s' WHERE `%s` = '%d'", login_db, new_email, login_db_account_id, acc); + // query + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + ShowInfo("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s)." RETCODE, + server[id].name, acc, sql_row[0], actual_email, ip); + } + } + + } + RFIFOSKIP(fd, 86); + break; + } + + case 0x2724: // Receiving of map-server via char-server a status change resquest (by Yor) + if (RFIFOREST(fd) < 10) + return 0; + { + int acc, statut; + acc = RFIFOL(fd,2); + statut = RFIFOL(fd,6); + sprintf(tmpsql, "SELECT `state` FROM `%s` WHERE `%s` = '%d'", login_db, login_db_account_id, acc); + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); // row fetching + } + if (atoi(sql_row[0]) != statut && statut != 0) { + unsigned char buf[16]; + WBUFW(buf,0) = 0x2731; + WBUFL(buf,2) = acc; + WBUFB(buf,6) = 0; // 0: change of statut, 1: ban + WBUFL(buf,7) = statut; // status or final date of a banishment + charif_sendallwos(-1, buf, 11); + } + sprintf(tmpsql,"UPDATE `%s` SET `state` = '%d' WHERE `%s` = '%d'", login_db, statut,login_db_account_id,acc); + //query + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + RFIFOSKIP(fd,10); + break; + } + + case 0x2725: // Receiving of map-server via char-server a ban resquest (by Yor) + if (RFIFOREST(fd) < 18) + return 0; + { + int acc; + struct tm *tmtime; + time_t timestamp, tmptime; + acc = RFIFOL(fd,2); + sprintf(tmpsql, "SELECT `ban_until` FROM `%s` WHERE `%s` = '%d'",login_db,login_db_account_id,acc); + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); // row fetching + } + tmptime = atol(sql_row[0]); + if (tmptime == 0 || tmptime < time(NULL)) + timestamp = time(NULL); + else + timestamp = tmptime; + tmtime = localtime(×tamp); + tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,6); + tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,8); + tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,10); + tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,12); + tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,14); + tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,16); + timestamp = mktime(tmtime); + if (timestamp != -1) { + if (timestamp <= time(NULL)) + timestamp = 0; + if (tmptime != timestamp) { + if (timestamp != 0) { + unsigned char buf[16]; + WBUFW(buf,0) = 0x2731; + WBUFL(buf,2) = acc; + WBUFB(buf,6) = 1; // 0: change of statut, 1: ban + WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment + charif_sendallwos(-1, buf, 11); + } + ShowNotice("Account: %d Banned until: %ld\n", acc, timestamp); + sprintf(tmpsql, "UPDATE `%s` SET `ban_until` = '%ld' WHERE `%s` = '%d'", login_db, (unsigned long)timestamp, login_db_account_id, acc); + // query + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } + } + RFIFOSKIP(fd,18); + break; + } + + case 0x2727: + if (RFIFOREST(fd) < 6) + return 0; + { + int acc,sex; + unsigned char buf[16]; + acc=RFIFOL(fd,2); + sprintf(tmpsql,"SELECT `sex` FROM `%s` WHERE `%s` = '%d'",login_db,login_db_account_id,acc); + + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + return 0; + } + + sql_res = mysql_store_result(&mysql_handle) ; + + if (sql_res) { + if (mysql_num_rows(sql_res) == 0) { + mysql_free_result(sql_res); + return 0; + } + sql_row = mysql_fetch_row(sql_res); //row fetching + } + + if (strcmpi(sql_row[0], "M") == 0) + sex = 0; //Change to female + else + sex = 1; //Change to make + sprintf(tmpsql,"UPDATE `%s` SET `sex` = '%c' WHERE `%s` = '%d'", login_db, (sex?'M':'F'), login_db_account_id, acc); + //query + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + WBUFW(buf,0) = 0x2723; + WBUFL(buf,2) = acc; + WBUFB(buf,6) = sex; + charif_sendallwos(-1, buf, 7); + RFIFOSKIP(fd,6); + break; + } + + case 0x2728: // save account_reg2 + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + if (RFIFOL(fd,4) > 0) { + int acc,p,j,len; + char str[32]; + char temp_str[64]; //Needs twice as much space as the original string. + char temp_str2[512]; + char value[256]; + unsigned char *buf; + acc=RFIFOL(fd,4); + buf = (unsigned char*)aCalloc(RFIFOW(fd,2)+1, sizeof(unsigned char)); + //Delete all global account variables.... + sprintf(tmpsql,"DELETE FROM `%s` WHERE `type`='1' AND `account_id`='%d';",reg_db,acc); + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + //Proceed to insert them.... + for(j=0,p=13;j 0) { //Found a match + sprintf(tmpsql,"UPDATE `%s` SET `ban_until` = '0' WHERE `%s` = '%d'", login_db,login_db_account_id,acc); + //query + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } + if (sql_res) mysql_free_result(sql_res); + RFIFOSKIP(fd,6); + } + return 0; + + case 0x272b: // Set account_id to online [Wizputer] + if (RFIFOREST(fd) < 6) + return 0; + add_online_user(id, RFIFOL(fd,2)); + RFIFOSKIP(fd,6); + break; + + case 0x272c: // Set account_id to offline [Wizputer] + if (RFIFOREST(fd) < 6) + return 0; + remove_online_user(RFIFOL(fd,2)); + RFIFOSKIP(fd,6); + break; + case 0x272d: // Receive list of all online accounts. [Skotlex] + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + if (!online_check) { + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + } + { + struct online_login_data *p; + int aid, users; + online_db->foreach(online_db,online_db_setoffline,id); //Set all chars from this char-server offline first + users = RFIFOW(fd,4); + for (i = 0; i < users; i++) { + aid = RFIFOL(fd,6+i*4); + p = idb_ensure(online_db, aid, create_online_user); + p->char_server = id; + } + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + } + case 0x272e: //Request account_reg2 for a character. + if (RFIFOREST(fd) < 10) + return 0; + { + int account_id = RFIFOL(fd, 2); + int char_id = RFIFOL(fd, 6); + int p; + WFIFOHEAD(fd,10000); + RFIFOSKIP(fd,10); + sprintf(tmpsql, "SELECT `str`,`value` FROM `%s` WHERE `type`='1' AND `account_id`='%d'",reg_db, account_id); + if (mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + break; + } + sql_res = mysql_store_result(&mysql_handle) ; + if (!sql_res) { + break; + } + WFIFOW(fd,0) = 0x2729; + WFIFOL(fd,4) = account_id; + WFIFOL(fd,8) = char_id; + WFIFOB(fd,12) = 1; //Type 1 for Account2 registry + for(p = 13; (sql_row = mysql_fetch_row(sql_res)) && p < 9000;){ + if (sql_row[0][0]) { + p+= sprintf(WFIFOP(fd,p), "%s", sql_row[0])+1; //We add 1 to consider the '\0' in place. + p+= sprintf(WFIFOP(fd,p), "%s", sql_row[1])+1; + } + } + if (p >= 9000) + ShowWarning("Too many account2 registries for AID %d. Some registries were not sent.\n", account_id); + WFIFOW(fd,2) = p; + WFIFOSET(fd,WFIFOW(fd,2)); + mysql_free_result(sql_res); + } + break; + + case 0x2736: // WAN IP update from char-server + if (RFIFOREST(fd) < 6) + return 0; + ShowInfo("Updated IP of Server #%d to %d.%d.%d.%d.\n",id, + (int)RFIFOB(fd,2),(int)RFIFOB(fd,3), + (int)RFIFOB(fd,4),(int)RFIFOB(fd,5)); + server[id].ip = RFIFOL(fd,2); + RFIFOSKIP(fd,6); + break; + + case 0x2737: //Request to set all offline. + ShowInfo("Setting accounts from char-server %d offline.\n", id); + online_db->foreach(online_db,online_db_setoffline,id); + RFIFOSKIP(fd,2); + break; + + default: + ShowError("login: unknown packet %x! (from char).\n", RFIFOW(fd,0)); + session[fd]->eof = 1; + return 0; + } + } + + RFIFOSKIP(fd,RFIFOREST(fd)); + return 0; +} + +//-------------------------------------------- +// Test to know if an IP come from LAN or WAN. +// Rewrote: Adnvanced subnet check [LuzZza] +//-------------------------------------------- +int lan_subnetcheck(long p) { + + int i; + unsigned char *sbn, *msk, *src = (unsigned char *)&p; + + for(i=0; iclient_addr.sin_addr.s_addr; + unsigned long ipl = session[fd]->client_addr.sin_addr.s_addr; + char ip[16]; + RFIFOHEAD(fd); + + sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + memset(&account, 0, sizeof(account)); + + if (session[fd]->eof) { + for(i = 0; i < MAX_SERVERS; i++) + if (server_fd[i] == fd) + server_fd[i] = -1; + do_close(fd); + return 0; + } + + while(RFIFOREST(fd)>=2 && !session[fd]->eof){ +// ShowDebug("parse_login : %d %d packet case=%x\n", fd, RFIFOREST(fd), RFIFOW(fd,0)); + + switch(RFIFOW(fd,0)){ + case 0x200: // New alive packet: structure: 0x200 .24B. used to verify if client is always alive. + if (RFIFOREST(fd) < 26) + return 0; + RFIFOSKIP(fd,26); + break; + + case 0x204: // New alive packet: structure: 0x204 .16B. (new ragexe from 22 june 2004) + if (RFIFOREST(fd) < 18) + return 0; + RFIFOSKIP(fd,18); + break; + + case 0x277: // New login packet + case 0x64: // request client login + case 0x01dd: // request client login with encrypt + + packet_len = RFIFOREST(fd); + + //Perform ip-ban check ONLY on login packets + if (ipban > 0 && login_ip_ban_check(p,ipl)) + { + RFIFOSKIP(fd,packet_len); + session[fd]->eof = 1; + break; + } + + switch(RFIFOW(fd,0)){ + case 0x64: + if(packet_len < 55) + return 0; + break; + case 0x01dd: + if(packet_len < 47) + return 0; + break; + case 0x277: + if(packet_len < 84) + return 0; + break; + } + + account.version = RFIFOL(fd, 2); + if (!account.version) account.version = 1; //Force some version... + memcpy(account.userid,RFIFOP(fd, 6),NAME_LENGTH); + account.userid[23] = '\0'; + memcpy(account.passwd,RFIFOP(fd, 30),NAME_LENGTH); + account.passwd[23] = '\0'; + + ShowInfo("client connection request %s from %d.%d.%d.%d\n", RFIFOP(fd, 6), p[0], p[1], p[2], p[3]); +#ifdef PASSWORDENC + account.passwdenc= (RFIFOW(fd,0)!=0x01dd)?0:PASSWORDENC; +#else + account.passwdenc=0; +#endif + result=mmo_auth(&account, fd); + + + jstrescapecpy(t_uid,account.userid); + if(result==-1){ + // as we have queried account level earlier in mmo_auth anyway, no need to do this again [zzo] + // int gm_level = isGM(account.account_id); // removed by [zzo] + + if (min_level_to_connect > account.level) { + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + } else { + WFIFOHEAD(fd,47+32*MAX_SERVERS); + if (p[0] != 127 && log_login) { + sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s','100', 'login ok')", loginlog_db, (unsigned int)ntohl(ipl), t_uid); + //query + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } + if (account.level) + ShowStatus("Connection of the GM (level:%d) account '%s' accepted.\n", account.level, account.userid); + else + ShowStatus("Connection of the account '%s' accepted.\n", account.userid); + server_num=0; + for(i = 0; i < MAX_SERVERS; i++) { + if (server_fd[i] >= 0) { + // Advanced subnet check [LuzZza] + if((subnet_char_ip = lan_subnetcheck(ipl))) + WFIFOL(fd,47+server_num*32) = subnet_char_ip; + else + WFIFOL(fd,47+server_num*32) = server[i].ip; + WFIFOW(fd,47+server_num*32+4) = server[i].port; + memcpy(WFIFOP(fd,47+server_num*32+6), server[i].name, 20); + WFIFOW(fd,47+server_num*32+26) = server[i].users; + WFIFOW(fd,47+server_num*32+28) = server[i].maintenance; + WFIFOW(fd,47+server_num*32+30) = server[i].new_; + server_num++; + } + } + // if at least 1 char-server + if (server_num > 0) { + WFIFOW(fd,0)=0x69; + WFIFOW(fd,2)=47+32*server_num; + WFIFOL(fd,4)=account.login_id1; + WFIFOL(fd,8)=account.account_id; + WFIFOL(fd,12)=account.login_id2; + WFIFOL(fd,16)=0; + memcpy(WFIFOP(fd,20),account.lastlogin,24); + WFIFOB(fd,46)=account.sex; + WFIFOSET(fd,47+32*server_num); + if(auth_fifo_pos>=AUTH_FIFO_SIZE) + auth_fifo_pos=0; + auth_fifo[auth_fifo_pos].account_id=account.account_id; + auth_fifo[auth_fifo_pos].login_id1=account.login_id1; + auth_fifo[auth_fifo_pos].login_id2=account.login_id2; + auth_fifo[auth_fifo_pos].sex=account.sex; + auth_fifo[auth_fifo_pos].delflag=0; + auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr.sin_addr.s_addr; + auth_fifo_pos++; + } else { + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + } + } + } else { + char tmp_sql[512]; + char error[64]; + WFIFOHEAD(fd,23); + if (log_login) + { + sprintf(tmp_sql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s', '%d','login failed : %%s')", loginlog_db, (unsigned int)ntohl(ipl), t_uid, result); + switch((result + 1)) { + case -2: //-3 = Account Banned + sprintf(tmpsql,tmp_sql,"Account banned."); + sprintf(error,"Account banned."); + break; + case -1: //-2 = Dynamic Ban + sprintf(tmpsql,tmp_sql,"dynamic ban (ip and account)."); + sprintf(error,"dynamic ban (ip and account)."); + break; + case 1: // 0 = Unregistered ID + sprintf(tmpsql,tmp_sql,"Unregisterd ID."); + sprintf(error,"Unregisterd ID."); + break; + case 2: // 1 = Incorrect Password + sprintf(tmpsql,tmp_sql,"Incorrect Password."); + sprintf(error,"Incorrect Password."); + break; + case 3: // 2 = This ID is expired + sprintf(tmpsql,tmp_sql,"Account Expired."); + sprintf(error,"Account Expired."); + break; + case 4: // 3 = Rejected from Server + sprintf(tmpsql,tmp_sql,"Rejected from server."); + sprintf(error,"Rejected from server."); + break; + case 5: // 4 = You have been blocked by the GM Team + sprintf(tmpsql,tmp_sql,"Blocked by GM."); + sprintf(error,"Blocked by GM."); + break; + case 6: // 5 = Your Game's EXE file is not the latest version + sprintf(tmpsql,tmp_sql,"Not latest game EXE."); + sprintf(error,"Not latest game EXE."); + break; + case 7: // 6 = Your are Prohibited to log in until %s + sprintf(tmpsql,tmp_sql,"Banned."); + sprintf(error,"Banned."); + break; + case 8: // 7 = Server is jammed due to over populated + sprintf(tmpsql,tmp_sql,"Server Over-population."); + sprintf(error,"Server Over-population."); + break; + case 9: // 8 = No more accounts may be connected from this company + sprintf(tmpsql,tmp_sql,"Account limit from company"); + sprintf(error,"Account limit from company"); + break; + case 10: // 9 = MSI_REFUSE_BAN_BY_DBA + sprintf(tmpsql,tmp_sql,"Ban by DBA"); + sprintf(error,"Ban by DBA"); + break; + case 11: // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED + sprintf(tmpsql,tmp_sql,"Email not confirmed"); + sprintf(error,"Email not confirmed"); + break; + case 12: // 11 = MSI_REFUSE_BAN_BY_GM + sprintf(tmpsql,tmp_sql,"Ban by GM"); + sprintf(error,"Ban by GM"); + break; + case 13: // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK + sprintf(tmpsql,tmp_sql,"Working in DB"); + sprintf(error,"Working in DB"); + break; + case 14: // 13 = MSI_REFUSE_SELF_LOCK + sprintf(tmpsql,tmp_sql,"Self Lock"); + sprintf(error,"Self Lock"); + break; + case 15: // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP + sprintf(tmpsql,tmp_sql,"Not Permitted Group"); + sprintf(error,"Not Permitted Group"); + break; + case 16: // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP + sprintf(tmpsql,tmp_sql,"Not Permitted Group"); + sprintf(error,"Not Permitted Group"); + break; + case 100: // 99 = This ID has been totally erased + sprintf(tmpsql,tmp_sql,"Account gone."); + sprintf(error,"Account gone."); + break; + case 101: // 100 = Login information remains at %s + sprintf(tmpsql,tmp_sql,"Login info remains."); + sprintf(error,"Login info remains."); + break; + case 102: // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information + sprintf(tmpsql,tmp_sql,"Hacking investigation."); + sprintf(error,"Hacking investigation."); + break; + case 103: // 102 = This account has been temporarily prohibited from login due to a bug-related investigation + sprintf(tmpsql,tmp_sql,"Bug investigation."); + sprintf(error,"Bug investigation."); + break; + case 104: // 103 = This character is being deleted. Login is temporarily unavailable for the time being + sprintf(tmpsql,tmp_sql,"Deleting char."); + sprintf(error,"Deleting char."); + break; + case 105: // 104 = This character is being deleted. Login is temporarily unavailable for the time being + sprintf(tmpsql,tmp_sql,"Deleting spouse char."); + sprintf(error,"Deleting spouse char."); + break; + default: + sprintf(tmpsql,tmp_sql,"Unknown Error."); + sprintf(error,"Unknown Error."); + break; + } + //query + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } //End login log of error. + if ((result == 1) && (dynamic_pass_failure_ban != 0) && log_login){ // failed password + sprintf(tmpsql,"SELECT count(*) FROM `%s` WHERE `ip` = '%u' AND `rcode` = '1' AND `time` > NOW() - INTERVAL %d MINUTE", + loginlog_db,(unsigned int)ntohl(ipl), dynamic_pass_failure_ban_time); //how many times filed account? in one ip. + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + //check query result + sql_res = mysql_store_result(&mysql_handle) ; + sql_row = sql_res?mysql_fetch_row(sql_res):NULL; //row fetching + + if (sql_row && atoi(sql_row[0]) >= dynamic_pass_failure_ban_how_many ) { + sprintf(tmpsql,"INSERT INTO `ipbanlist`(`list`,`btime`,`rtime`,`reason`) VALUES ('%d.%d.%d.*', NOW() , NOW() + INTERVAL %d MINUTE ,'Password error ban: %s')", p[0], p[1], p[2], dynamic_pass_failure_ban_how_long, t_uid); + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } + if(sql_res) mysql_free_result(sql_res); + } + else if (result == -2){ //dynamic banned - add ip to ban list. + sprintf(tmpsql,"INSERT INTO `ipbanlist`(`list`,`btime`,`rtime`,`reason`) VALUES ('%d.%d.%d.*', NOW() , NOW() + INTERVAL 1 MONTH ,'Dynamic banned user id : %s')", p[0], p[1], p[2], t_uid); + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + result = -3; + }else if(result == 6){ //not lastet version .. + //result = 5; + } + + sprintf(tmpsql,"SELECT `ban_until` FROM `%s` WHERE `%s` = %s '%s'",login_db, login_db_userid, case_sensitive ? "BINARY" : "", t_uid); + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + sql_res = mysql_store_result(&mysql_handle) ; + sql_row = sql_res?mysql_fetch_row(sql_res):NULL; //row fetching + //cannot connect login failed + memset(WFIFOP(fd,0),'\0',23); + WFIFOW(fd,0)=0x6a; + WFIFOB(fd,2)=result; + if (result == 6) { // 6 = Your are Prohibited to log in until %s + if (sql_row && atol(sql_row[0]) != 0) { // if account is banned, we send ban timestamp + char tmpstr[256]; + time_t ban_until_time; + ban_until_time = atol(sql_row[0]); + strftime(tmpstr, 20, date_format, localtime(&ban_until_time)); + tmpstr[19] = '\0'; + memcpy(WFIFOP(fd,3), tmpstr, 20); + } else { // we send error message + memcpy(WFIFOP(fd,3), error, 20); + } + } + WFIFOSET(fd,23); + } + RFIFOSKIP(fd,packet_len); + break; + + case 0x01db: // request password key + if (session[fd]->session_data) { + ShowWarning("login: abnormal request of MD5 key (already opened session).\n"); + session[fd]->eof = 1; + return 0; + } + { + WFIFOHEAD(fd,4+md5keylen); + WFIFOW(fd,0)=0x01dc; + WFIFOW(fd,2)=4+md5keylen; + memcpy(WFIFOP(fd,4),md5key,md5keylen); + WFIFOSET(fd,WFIFOW(fd,2)); + + ShowDebug("Request Password key -%s\n",md5key); + RFIFOSKIP(fd,2); + } + break; + + case 0x2710: // request Char-server connection + if(RFIFOREST(fd)<86) + return 0; + { + unsigned char* server_name; + WFIFOHEAD(fd, 3); + memcpy(account.userid,RFIFOP(fd, 2),NAME_LENGTH); + account.userid[23] = '\0'; + memcpy(account.passwd,RFIFOP(fd, 26),NAME_LENGTH); + account.passwd[23] = '\0'; + account.passwdenc = 0; + server_name = RFIFOP(fd,60); + server_name[20] = '\0'; + ShowInfo("server connection request %s @ %d.%d.%d.%d:%d (%d.%d.%d.%d)\n", + server_name, RFIFOB(fd, 54), RFIFOB(fd, 55), RFIFOB(fd, 56), RFIFOB(fd, 57), RFIFOW(fd, 58), + p[0], p[1], p[2], p[3]); + jstrescapecpy(t_uid,server_name); + if (log_login) + { + char t_login[50]; + jstrescapecpy(t_login,account.userid); + sprintf(tmpsql,"INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s@%s','100', 'charserver - %s@%d.%d.%d.%d:%d')", + loginlog_db, (unsigned int)ntohl(ipl), + t_login, t_uid, t_uid, + RFIFOB(fd, 54), RFIFOB(fd, 55), RFIFOB(fd, 56), RFIFOB(fd, 57), + RFIFOW(fd, 58)); + + //query + if(mysql_query(&mysql_handle, tmpsql)) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmpsql); + } + } + result = mmo_auth(&account, fd); + //printf("Result: %d - Sex: %d - Account ID: %d\n",result,account.sex,(int) account.account_id); + + if(result == -1 && account.sex==2 && account.account_idfunc_parse=parse_fromchar; + realloc_fifo(fd,FIFOSIZE_SERVERLINK,FIFOSIZE_SERVERLINK); + // send GM account to char-server + send_GM_accounts(fd); + } else { + WFIFOW(fd, 0) =0x2711; + WFIFOB(fd, 2)=3; + WFIFOSET(fd, 3); + } + } + RFIFOSKIP(fd, 86); + return 0; + + case 0x7530: // request Athena information + { + WFIFOHEAD(fd,10); + WFIFOW(fd,0)=0x7531; + WFIFOB(fd,2)=ATHENA_MAJOR_VERSION; + WFIFOB(fd,3)=ATHENA_MINOR_VERSION; + WFIFOB(fd,4)=ATHENA_REVISION; + WFIFOB(fd,5)=ATHENA_RELEASE_FLAG; + WFIFOB(fd,6)=ATHENA_OFFICIAL_FLAG; + WFIFOB(fd,7)=ATHENA_SERVER_LOGIN; + WFIFOW(fd,8)=ATHENA_MOD_VERSION; + WFIFOSET(fd,10); + RFIFOSKIP(fd,2); + ShowInfo ("Athena version check...\n"); + break; + } + case 0x7532: + ShowStatus ("End of connection (ip: %s)" RETCODE, ip); + session[fd]->eof = 1; + break; + default: + ShowStatus ("Abnormal end of connection (ip: %s): Unknown packet 0x%x " RETCODE, ip, RFIFOW(fd,0)); + session[fd]->eof = 1; + return 0; + } + } + + RFIFOSKIP(fd,RFIFOREST(fd)); + return 0; +} + +// Console Command Parser [Wizputer] +int parse_console(char *buf) { + char *type,*command; + + type = (char *)aMalloc(64); + command = (char *)aMalloc(64); + + memset(type,0,64); + memset(command,0,64); + + ShowInfo("Console: %s\n",buf); + + if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 ) + sscanf(buf,"%[^\n]",type); + + ShowInfo("Type of command: %s || Command: %s \n",type,command); + + if(buf) aFree(buf); + if(type) aFree(type); + if(command) aFree(command); + + return 0; +} + +static int online_data_cleanup_sub(DBKey key, void *data, va_list ap) +{ + struct online_login_data *character= (struct online_login_data*)data; + if (character->char_server == -2) //Unknown server.. set them offline + remove_online_user(character->account_id); + else if (character->char_server < 0) + //Free data from players that have not been online for a while. + db_remove(online_db, key); + return 0; +} + +static int online_data_cleanup(int tid, unsigned int tick, int id, int data) +{ + online_db->foreach(online_db, online_data_cleanup_sub); + return 0; +} + +//------------------------------------------------- +// Return numerical value of a switch configuration +// on/off, english, fran軋is, deutsch, espaol +//------------------------------------------------- +int config_switch(const char *str) { + if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) + return 1; + if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) + return 0; + + return atoi(str); +} + + +//---------------------------------- +// Reading Lan Support configuration +// Rewrote: Anvanced subnet check [LuzZza] +//---------------------------------- +int login_lan_config_read(const char *lancfgName) { + + FILE *fp; + int line_num = 0; + char line[1024], w1[64], w2[64], w3[64], w4[64]; + + if((fp = fopen(lancfgName, "r")) == NULL) { + ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName); + return 1; + } + + ShowInfo("Reading the configuration file %s...\n", lancfgName); + + while(fgets(line, sizeof(line)-1, fp)) { + + line_num++; + if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n') + continue; + + line[sizeof(line)-1] = '\0'; + if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4) { + + ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num); + continue; + } + + remove_control_chars((unsigned char *)w1); + remove_control_chars((unsigned char *)w2); + remove_control_chars((unsigned char *)w3); + remove_control_chars((unsigned char *)w4); + + if(strcmpi(w1, "subnet") == 0) { + + subnet[subnet_count].mask = inet_addr(w2); + subnet[subnet_count].char_ip = inet_addr(w3); + subnet[subnet_count].map_ip = inet_addr(w4); + subnet[subnet_count].subnet = subnet[subnet_count].char_ip&subnet[subnet_count].mask; + if (subnet[subnet_count].subnet != (subnet[subnet_count].map_ip&subnet[subnet_count].mask)) { + ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4); + continue; + } + + subnet_count++; + } + + ShowStatus("Read information about %d subnetworks.\n", subnet_count); + } + + fclose(fp); + return 0; +} + +//----------------------------------------------------- +//BANNED IP CHECK. +//----------------------------------------------------- +int ip_ban_check(int tid, unsigned int tick, int id, int data){ + + //query + if(mysql_query(&mysql_handle, "DELETE FROM `ipbanlist` WHERE `rtime` <= NOW()")) { + ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); + ShowDebug("at %s:%d - DELETE FROM `ipbanlist` WHERE `rtime` <= NOW()\n", __FILE__,__LINE__); + } + + return 0; +} + +//----------------------------------------------------- +// reading configuration +//----------------------------------------------------- +int login_config_read(const char *cfgName){ + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp=fopen(cfgName,"r"); + + if(fp==NULL){ + ShowError("Configuration file (%s) not found.\n", cfgName); + return 1; + } + ShowInfo("reading configuration file %s...\n", cfgName); + while(fgets(line, sizeof(line)-1, fp)){ + if(line[0] == '/' && line[1] == '/') + continue; + + i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2); + if(i!=2) + continue; + + remove_control_chars((unsigned char *) w1); + remove_control_chars((unsigned char *) w2); + if(strcmpi(w1,"timestamp_format") == 0) { + strncpy(timestamp_format, w2, 20); + } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ + stdout_with_ansisequence = config_switch(w2); + } else if(strcmpi(w1,"console_silent")==0){ + msg_silent = 0; //To always allow the next line to show up. + ShowInfo("Console Silent Setting: %d\n", atoi(w2)); + msg_silent = atoi(w2); + } else if (strcmpi(w1, "bind_ip") == 0) { + bind_ip = resolve_hostbyname(w2, NULL, bind_ip_str); + if (bind_ip) + ShowStatus("Login server binding IP address : %s -> %s\n", w2, bind_ip_str); + } else if(strcmpi(w1,"login_port")==0){ + login_port=atoi(w2); + ShowStatus("set login_port : %s\n",w2); + } + else if(strcmpi(w1,"ipban")==0){ + ipban=atoi(w2); + ShowStatus("set ipban : %d\n",ipban); + } + //account ban -> ip ban + else if(strcmpi(w1,"dynamic_account_ban")==0){ + dynamic_account_ban=atoi(w2); + ShowStatus("set dynamic_account_ban : %d\n",dynamic_account_ban); + } + else if(strcmpi(w1,"dynamic_account_ban_class")==0){ + dynamic_account_ban_class=atoi(w2); + ShowStatus("set dynamic_account_ban_class : %d\n",dynamic_account_ban_class); + } + //dynamic password error ban + else if(strcmpi(w1,"dynamic_pass_failure_ban")==0){ + dynamic_pass_failure_ban=atoi(w2); + ShowStatus("set dynamic_pass_failure_ban : %d\n",dynamic_pass_failure_ban); + } + else if(strcmpi(w1,"dynamic_pass_failure_ban_time")==0){ + dynamic_pass_failure_ban_time=atoi(w2); + ShowStatus("set dynamic_pass_failure_ban_time : %d\n",dynamic_pass_failure_ban_time); + } + else if(strcmpi(w1,"dynamic_pass_failure_ban_how_many")==0){ + dynamic_pass_failure_ban_how_many=atoi(w2); + ShowStatus("set dynamic_pass_failure_ban_how_many : %d\n",dynamic_pass_failure_ban_how_many); + } + else if(strcmpi(w1,"dynamic_pass_failure_ban_how_long")==0){ + dynamic_pass_failure_ban_how_long=atoi(w2); + ShowStatus("set dynamic_pass_failure_ban_how_long : %d\n",dynamic_pass_failure_ban_how_long); + } else if(strcmpi(w1, "new_account") == 0){ //Added by Sirius for new account _M/_F + new_account_flag = atoi(w2); //Added by Sirius for new account _M/_F + } else if(strcmpi(w1, "check_client_version") == 0){ //Added by Sirius for client version check + //check_client_version = config_switch(w2); //Added by Sirius for client version check + if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ){ + check_client_version = 1; + } else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ){ + check_client_version = 0; + } + } else if(strcmpi(w1, "client_version_to_connect") == 0){ //Added by Sirius for client version check + client_version_to_connect = atoi(w2); //Added by SIrius for client version check + } else if(strcmpi(w1,"use_MD5_passwords")==0){ + if (!strcmpi(w2,"yes")) { + use_md5_passwds=1; + } else if (!strcmpi(w2,"no")){ + use_md5_passwds=0; + } + ShowStatus("Using MD5 Passwords: %s \n",w2); + } + else if (strcmpi(w1, "date_format") == 0) { // note: never have more than 19 char for the date! + switch (atoi(w2)) { + case 0: + strcpy(date_format, "%d-%m-%Y %H:%M:%S"); // 31-12-2004 23:59:59 + break; + case 1: + strcpy(date_format, "%m-%d-%Y %H:%M:%S"); // 12-31-2004 23:59:59 + break; + case 2: + strcpy(date_format, "%Y-%d-%m %H:%M:%S"); // 2004-31-12 23:59:59 + break; + case 3: + strcpy(date_format, "%Y-%m-%d %H:%M:%S"); // 2004-12-31 23:59:59 + break; + } + } + else if (strcmpi(w1, "min_level_to_connect") == 0) { + min_level_to_connect = atoi(w2); + } + else if (strcmpi(w1, "check_ip_flag") == 0) { + check_ip_flag = config_switch(w2); + } + else if (strcmpi(w1, "console") == 0) { + if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) + console = 1; + } + else if (strcmpi(w1, "case_sensitive") == 0) { + if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) + case_sensitive = 1; + if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ) + case_sensitive = 0; + else + case_sensitive = atoi(w2); + } else if (strcmpi(w1, "allowed_regs") == 0) { //account flood protection system [Kevin] + allowed_regs = atoi(w2); + } else if (strcmpi(w1, "time_allowed") == 0) { + time_allowed = atoi(w2); + } else if (strcmpi(w1, "online_check") == 0) { + if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) + online_check = 1; + else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ) + online_check = 0; + else + online_check = atoi(w2); + } else if (strcmpi(w1, "log_login") == 0) { + if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 ) + log_login = 1; + else if(strcmpi(w2,"off") == 0 || strcmpi(w2,"no") == 0 ) + log_login = 0; + else + log_login = atoi(w2); + } else if (strcmpi(w1, "import") == 0) { + login_config_read(w2); + } else if(strcmpi(w1,"use_dnsbl")==0) { // [Zido] + use_dnsbl=atoi(w2); + } else if(strcmpi(w1,"dnsbl_servers")==0) { // [Zido] + strcpy(dnsbl_servs,w2); + } else if(strcmpi(w1,"ip_sync_interval")==0) { + ip_sync_interval = 1000*60*atoi(w2); //w2 comes in minutes. + } + } + fclose(fp); + ShowInfo("done reading %s.\n", cfgName); + return 0; +} + +void sql_config_read(const char *cfgName){ /* Kalaspuff, to get login_db */ + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp=fopen(cfgName,"r"); + if(fp==NULL){ + ShowFatalError("file not found: %s\n",cfgName); + exit(1); + } + ShowInfo("reading configuration file %s...\n", cfgName); + while(fgets(line, sizeof(line)-1, fp)){ + if(line[0] == '/' && line[1] == '/') + continue; + i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2); + if(i!=2) + continue; + if(strcmpi(w1, "gm_read_method") == 0) { + if(atoi(w2) == 0) + login_gm_read = true; + else + login_gm_read = false; + } else if(strcmpi(w1, "gm_db") == 0) { + strcpy(gm_db, w2); + } else if (strcmpi(w1, "login_db") == 0) { + strcpy(login_db, w2); + } + //add for DB connection + else if(strcmpi(w1,"login_server_ip")==0){ + strcpy(login_server_ip, w2); + ShowStatus ("set login_server_ip : %s\n",w2); + } + else if(strcmpi(w1,"login_server_port")==0){ + login_server_port=atoi(w2); + ShowStatus ("set login_server_port : %s\n",w2); + } + else if(strcmpi(w1,"login_server_id")==0){ + strcpy(login_server_id, w2); + ShowStatus ("set login_server_id : %s\n",w2); + } + else if(strcmpi(w1,"login_server_pw")==0){ + strcpy(login_server_pw, w2); + ShowStatus ("set login_server_pw : %s\n",w2); + } + else if(strcmpi(w1,"login_server_db")==0){ + strcpy(login_server_db, w2); + ShowStatus ("set login_server_db : %s\n",w2); + } + else if(strcmpi(w1,"connection_ping_interval")==0) { + connection_ping_interval = atoi(w2); + } + else if(strcmpi(w1,"default_codepage")==0){ + strcpy(default_codepage, w2); + ShowStatus ("set default_codepage : %s\n",w2); + } + //added for custom column names for custom login table + else if(strcmpi(w1,"login_db_account_id")==0){ + strcpy(login_db_account_id, w2); + } + else if(strcmpi(w1,"login_db_userid")==0){ + strcpy(login_db_userid, w2); + } + else if(strcmpi(w1,"login_db_user_pass")==0){ + strcpy(login_db_user_pass, w2); + } + else if(strcmpi(w1,"login_db_level")==0){ + strcpy(login_db_level, w2); + } + else if (strcmpi(w1, "loginlog_db") == 0) { + strcpy(loginlog_db, w2); + } + else if (strcmpi(w1, "reg_db") == 0) { + strcpy(reg_db, w2); + } + //support the import command, just like any other config + else if(strcmpi(w1,"import")==0){ + sql_config_read(w2); + } + } + fclose(fp); + ShowInfo("done reading %s.\n", cfgName); +} + +//-------------------------------------- +// Function called at exit of the server +//-------------------------------------- +void do_final(void) { + //sync account when terminating. + //but no need when you using DBMS (mysql) + ShowStatus("Terminating...\n"); + mmo_db_close(); + online_db->destroy(online_db, NULL); + if (gm_account_db) + aFree(gm_account_db); +} + +void set_server_type(void) +{ + SERVER_TYPE = ATHENA_SERVER_LOGIN; +} +int do_init(int argc,char **argv){ + //initialize login server + int i; + + //read login configue + login_config_read( (argc>1)?argv[1]:LOGIN_CONF_NAME ); + sql_config_read(SQL_CONF_NAME); + login_lan_config_read((argc > 2) ? argv[2] : LAN_CONF_NAME); + //Generate Passworded Key. + ShowInfo("Initializing md5key...\n"); + memset(md5key, 0, sizeof(md5key)); + md5keylen=rand()%4+12; + for(i=0;i -#include - -#ifndef UINT_MAX -#define UINT_MAX 4294967295U -#endif - -// Global variable -static unsigned int *pX; - -// Stirng Table -static const unsigned int T[] = { - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0 - 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4 - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8 - 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12 - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16 - 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, //20 - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24 - 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28 - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32 - 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36 - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, //40 - 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44 - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48 - 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52 - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56 - 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 //60 -}; - -// ROTATE_LEFT The left is made to rotate x [ n-bit ]. This is diverted as it is from RFC. -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - -// The function used for other calculation -static unsigned int F(unsigned int X, unsigned int Y, unsigned int Z) -{ - return (X & Y) | (~X & Z); -} -static unsigned int G(unsigned int X, unsigned int Y, unsigned int Z) -{ - return (X & Z) | (Y & ~Z); -} -static unsigned int H(unsigned int X, unsigned int Y, unsigned int Z) -{ - return X ^ Y ^ Z; -} -static unsigned int I(unsigned int X, unsigned int Y, unsigned int Z) -{ - return Y ^ (X | ~Z); -} - -static unsigned int Round(unsigned int a, unsigned int b, unsigned int FGHI, - unsigned int k, unsigned int s, unsigned int i) -{ - return b + ROTATE_LEFT(a + FGHI + pX[k] + T[i], s); -} - -static void Round1(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, F(b,c,d), k, s, i); -} -static void Round2(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, G(b,c,d), k, s, i); -} -static void Round3(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, H(b,c,d), k, s, i); -} -static void Round4(unsigned int *a, unsigned int b, unsigned int c, - unsigned int d,unsigned int k, unsigned int s, unsigned int i) -{ - *a = Round(*a, b, I(b,c,d), k, s, i); -} - -static void MD5_Round_Calculate(const unsigned char *block, - unsigned int *A2, unsigned int *B2, unsigned int *C2, unsigned int *D2) -{ - //create X It is since it is required. - unsigned int X[16]; //512bit 64byte - int j,k; - - //Save A as AA, B as BB, C as CC, and and D as DD (saving of A, B, C, and D) - unsigned int A=*A2, B=*B2, C=*C2, D=*D2; - unsigned int AA = A,BB = B,CC = C,DD = D; - - //It is a large region variable reluctantly because of calculation of a round. . . for Round1...4 - pX = X; - - //Copy block(padding_message) i into X - for (j=0,k=0; j<64; j+=4,k++) - X[k] = ( (unsigned int )block[j] ) // 8byte*4 -> 32byte conversion - | ( ((unsigned int )block[j+1]) << 8 ) // A function called Decode as used in the field of RFC - | ( ((unsigned int )block[j+2]) << 16 ) - | ( ((unsigned int )block[j+3]) << 24 ); - - - //Round 1 - Round1(&A,B,C,D, 0, 7, 0); Round1(&D,A,B,C, 1, 12, 1); Round1(&C,D,A,B, 2, 17, 2); Round1(&B,C,D,A, 3, 22, 3); - Round1(&A,B,C,D, 4, 7, 4); Round1(&D,A,B,C, 5, 12, 5); Round1(&C,D,A,B, 6, 17, 6); Round1(&B,C,D,A, 7, 22, 7); - Round1(&A,B,C,D, 8, 7, 8); Round1(&D,A,B,C, 9, 12, 9); Round1(&C,D,A,B, 10, 17, 10); Round1(&B,C,D,A, 11, 22, 11); - Round1(&A,B,C,D, 12, 7, 12); Round1(&D,A,B,C, 13, 12, 13); Round1(&C,D,A,B, 14, 17, 14); Round1(&B,C,D,A, 15, 22, 15); - - //Round 2 - Round2(&A,B,C,D, 1, 5, 16); Round2(&D,A,B,C, 6, 9, 17); Round2(&C,D,A,B, 11, 14, 18); Round2(&B,C,D,A, 0, 20, 19); - Round2(&A,B,C,D, 5, 5, 20); Round2(&D,A,B,C, 10, 9, 21); Round2(&C,D,A,B, 15, 14, 22); Round2(&B,C,D,A, 4, 20, 23); - Round2(&A,B,C,D, 9, 5, 24); Round2(&D,A,B,C, 14, 9, 25); Round2(&C,D,A,B, 3, 14, 26); Round2(&B,C,D,A, 8, 20, 27); - Round2(&A,B,C,D, 13, 5, 28); Round2(&D,A,B,C, 2, 9, 29); Round2(&C,D,A,B, 7, 14, 30); Round2(&B,C,D,A, 12, 20, 31); - - //Round 3 - Round3(&A,B,C,D, 5, 4, 32); Round3(&D,A,B,C, 8, 11, 33); Round3(&C,D,A,B, 11, 16, 34); Round3(&B,C,D,A, 14, 23, 35); - Round3(&A,B,C,D, 1, 4, 36); Round3(&D,A,B,C, 4, 11, 37); Round3(&C,D,A,B, 7, 16, 38); Round3(&B,C,D,A, 10, 23, 39); - Round3(&A,B,C,D, 13, 4, 40); Round3(&D,A,B,C, 0, 11, 41); Round3(&C,D,A,B, 3, 16, 42); Round3(&B,C,D,A, 6, 23, 43); - Round3(&A,B,C,D, 9, 4, 44); Round3(&D,A,B,C, 12, 11, 45); Round3(&C,D,A,B, 15, 16, 46); Round3(&B,C,D,A, 2, 23, 47); - - //Round 4 - Round4(&A,B,C,D, 0, 6, 48); Round4(&D,A,B,C, 7, 10, 49); Round4(&C,D,A,B, 14, 15, 50); Round4(&B,C,D,A, 5, 21, 51); - Round4(&A,B,C,D, 12, 6, 52); Round4(&D,A,B,C, 3, 10, 53); Round4(&C,D,A,B, 10, 15, 54); Round4(&B,C,D,A, 1, 21, 55); - Round4(&A,B,C,D, 8, 6, 56); Round4(&D,A,B,C, 15, 10, 57); Round4(&C,D,A,B, 6, 15, 58); Round4(&B,C,D,A, 13, 21, 59); - Round4(&A,B,C,D, 4, 6, 60); Round4(&D,A,B,C, 11, 10, 61); Round4(&C,D,A,B, 2, 15, 62); Round4(&B,C,D,A, 9, 21, 63); - - // Then perform the following additions. (let's add) - *A2 = A + AA; - *B2 = B + BB; - *C2 = C + CC; - *D2 = D + DD; - - //The clearance of confidential information - memset(pX, 0, sizeof(X)); -} - -//------------------------------------------------------------------- -// The function for the exteriors - -/** output is the coded binary in the character sequence which wants to code string. */ -void MD5_String2binary(const char * string, char * output) -{ -//var - /*8bit*/ - unsigned char padding_message[64]; //Extended message 512bit 64byte - unsigned char *pstring; //The position of string in the present scanning notes is held. - -// unsigned char digest[16]; - /*32bit*/ - unsigned int string_byte_len, //The byte chief of string is held. - string_bit_len, //The bit length of string is held. - copy_len, //The number of bytes which is used by 1-3 and which remained - msg_digest[4]; //Message digest 128bit 4byte - unsigned int *A = &msg_digest[0], //The message digest in accordance with RFC (reference) - *B = &msg_digest[1], - *C = &msg_digest[2], - *D = &msg_digest[3]; - int i; - -//prog - //Step 3.Initialize MD Buffer (although it is the initialization; step 3 of A, B, C, and D -- unavoidable -- a head) - *A = 0x67452301; - *B = 0xefcdab89; - *C = 0x98badcfe; - *D = 0x10325476; - - //Step 1.Append Padding Bits (extension of a mark bit) - //1-1 - string_byte_len = strlen(string); //The byte chief of a character sequence is acquired. - pstring = (unsigned char *)string; //The position of the present character sequence is set. - - //1-2 Repeat calculation until length becomes less than 64 bytes. - for (i=string_byte_len; 64<=i; i-=64,pstring+=64) - MD5_Round_Calculate(pstring, A,B,C,D); - - //1-3 - copy_len = string_byte_len % 64; //The number of bytes which remained is computed. - strncpy((char *)padding_message, (char *)pstring, copy_len); //A message is copied to an extended bit sequence. - memset(padding_message+copy_len, 0, 64 - copy_len); //It buries by 0 until it becomes extended bit length. - padding_message[copy_len] |= 0x80; //The next of a message is 1. - - //1-4 - //If 56 bytes or more (less than 64 bytes) of remainder becomes, it will calculate by extending to 64 bytes. - if (56 <= copy_len) { - MD5_Round_Calculate(padding_message, A,B,C,D); - memset(padding_message, 0, 56); //56 bytes is newly fill uped with 0. - } - - - //Step 2.Append Length (the information on length is added) - string_bit_len = string_byte_len * 8; //From the byte chief to bit length (32 bytes of low rank) - memcpy(&padding_message[56], &string_bit_len, 4); //32 bytes of low rank is set. - - //When bit length cannot be expressed in 32 bytes of low rank, it is a beam raising to a higher rank. - if (UINT_MAX / 8 < string_byte_len) { - unsigned int high = (string_byte_len - UINT_MAX / 8) * 8; - memcpy(&padding_message[60], &high, 4); - } else - memset(&padding_message[60], 0, 4); //In this case, it is good for a higher rank at 0. - - //Step 4.Process Message in 16-Word Blocks (calculation of MD5) - MD5_Round_Calculate(padding_message, A,B,C,D); - - - //Step 5.Output (output) - memcpy(output,msg_digest,16); -// memcpy (digest, msg_digest, and 16); //8 byte*4 < - 32byte conversion A function called Encode as used in the field of RFC -/* sprintf(output, - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - digest[ 0], digest[ 1], digest[ 2], digest[ 3], - digest[ 4], digest[ 5], digest[ 6], digest[ 7], - digest[ 8], digest[ 9], digest[10], digest[11], - digest[12], digest[13], digest[14], digest[15]);*/ -} - -/** output is the coded character sequence in the character sequence which wants to code string. */ -void MD5_String(const char * string, char * output) -{ - unsigned char digest[16]; - - MD5_String2binary(string,(char*)digest); - sprintf(output, - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - digest[ 0], digest[ 1], digest[ 2], digest[ 3], - digest[ 4], digest[ 5], digest[ 6], digest[ 7], - digest[ 8], digest[ 9], digest[10], digest[11], - digest[12], digest[13], digest[14], digest[15]); -} - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +/*********************************************************** + * md5 calculation algorithm + * + * The source code referred to the following URL. + * http://www.geocities.co.jp/SiliconValley-Oakland/8878/lab17/lab17.html + * + ***********************************************************/ + +#include "md5calc.h" +#include +#include + +#ifndef UINT_MAX +#define UINT_MAX 4294967295U +#endif + +// Global variable +static unsigned int *pX; + +// Stirng Table +static const unsigned int T[] = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0 + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4 + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8 + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12 + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16 + 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, //20 + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24 + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28 + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32 + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36 + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, //40 + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44 + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48 + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52 + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56 + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 //60 +}; + +// ROTATE_LEFT The left is made to rotate x [ n-bit ]. This is diverted as it is from RFC. +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +// The function used for other calculation +static unsigned int F(unsigned int X, unsigned int Y, unsigned int Z) +{ + return (X & Y) | (~X & Z); +} +static unsigned int G(unsigned int X, unsigned int Y, unsigned int Z) +{ + return (X & Z) | (Y & ~Z); +} +static unsigned int H(unsigned int X, unsigned int Y, unsigned int Z) +{ + return X ^ Y ^ Z; +} +static unsigned int I(unsigned int X, unsigned int Y, unsigned int Z) +{ + return Y ^ (X | ~Z); +} + +static unsigned int Round(unsigned int a, unsigned int b, unsigned int FGHI, + unsigned int k, unsigned int s, unsigned int i) +{ + return b + ROTATE_LEFT(a + FGHI + pX[k] + T[i], s); +} + +static void Round1(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, F(b,c,d), k, s, i); +} +static void Round2(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, G(b,c,d), k, s, i); +} +static void Round3(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, H(b,c,d), k, s, i); +} +static void Round4(unsigned int *a, unsigned int b, unsigned int c, + unsigned int d,unsigned int k, unsigned int s, unsigned int i) +{ + *a = Round(*a, b, I(b,c,d), k, s, i); +} + +static void MD5_Round_Calculate(const unsigned char *block, + unsigned int *A2, unsigned int *B2, unsigned int *C2, unsigned int *D2) +{ + //create X It is since it is required. + unsigned int X[16]; //512bit 64byte + int j,k; + + //Save A as AA, B as BB, C as CC, and and D as DD (saving of A, B, C, and D) + unsigned int A=*A2, B=*B2, C=*C2, D=*D2; + unsigned int AA = A,BB = B,CC = C,DD = D; + + //It is a large region variable reluctantly because of calculation of a round. . . for Round1...4 + pX = X; + + //Copy block(padding_message) i into X + for (j=0,k=0; j<64; j+=4,k++) + X[k] = ( (unsigned int )block[j] ) // 8byte*4 -> 32byte conversion + | ( ((unsigned int )block[j+1]) << 8 ) // A function called Decode as used in the field of RFC + | ( ((unsigned int )block[j+2]) << 16 ) + | ( ((unsigned int )block[j+3]) << 24 ); + + + //Round 1 + Round1(&A,B,C,D, 0, 7, 0); Round1(&D,A,B,C, 1, 12, 1); Round1(&C,D,A,B, 2, 17, 2); Round1(&B,C,D,A, 3, 22, 3); + Round1(&A,B,C,D, 4, 7, 4); Round1(&D,A,B,C, 5, 12, 5); Round1(&C,D,A,B, 6, 17, 6); Round1(&B,C,D,A, 7, 22, 7); + Round1(&A,B,C,D, 8, 7, 8); Round1(&D,A,B,C, 9, 12, 9); Round1(&C,D,A,B, 10, 17, 10); Round1(&B,C,D,A, 11, 22, 11); + Round1(&A,B,C,D, 12, 7, 12); Round1(&D,A,B,C, 13, 12, 13); Round1(&C,D,A,B, 14, 17, 14); Round1(&B,C,D,A, 15, 22, 15); + + //Round 2 + Round2(&A,B,C,D, 1, 5, 16); Round2(&D,A,B,C, 6, 9, 17); Round2(&C,D,A,B, 11, 14, 18); Round2(&B,C,D,A, 0, 20, 19); + Round2(&A,B,C,D, 5, 5, 20); Round2(&D,A,B,C, 10, 9, 21); Round2(&C,D,A,B, 15, 14, 22); Round2(&B,C,D,A, 4, 20, 23); + Round2(&A,B,C,D, 9, 5, 24); Round2(&D,A,B,C, 14, 9, 25); Round2(&C,D,A,B, 3, 14, 26); Round2(&B,C,D,A, 8, 20, 27); + Round2(&A,B,C,D, 13, 5, 28); Round2(&D,A,B,C, 2, 9, 29); Round2(&C,D,A,B, 7, 14, 30); Round2(&B,C,D,A, 12, 20, 31); + + //Round 3 + Round3(&A,B,C,D, 5, 4, 32); Round3(&D,A,B,C, 8, 11, 33); Round3(&C,D,A,B, 11, 16, 34); Round3(&B,C,D,A, 14, 23, 35); + Round3(&A,B,C,D, 1, 4, 36); Round3(&D,A,B,C, 4, 11, 37); Round3(&C,D,A,B, 7, 16, 38); Round3(&B,C,D,A, 10, 23, 39); + Round3(&A,B,C,D, 13, 4, 40); Round3(&D,A,B,C, 0, 11, 41); Round3(&C,D,A,B, 3, 16, 42); Round3(&B,C,D,A, 6, 23, 43); + Round3(&A,B,C,D, 9, 4, 44); Round3(&D,A,B,C, 12, 11, 45); Round3(&C,D,A,B, 15, 16, 46); Round3(&B,C,D,A, 2, 23, 47); + + //Round 4 + Round4(&A,B,C,D, 0, 6, 48); Round4(&D,A,B,C, 7, 10, 49); Round4(&C,D,A,B, 14, 15, 50); Round4(&B,C,D,A, 5, 21, 51); + Round4(&A,B,C,D, 12, 6, 52); Round4(&D,A,B,C, 3, 10, 53); Round4(&C,D,A,B, 10, 15, 54); Round4(&B,C,D,A, 1, 21, 55); + Round4(&A,B,C,D, 8, 6, 56); Round4(&D,A,B,C, 15, 10, 57); Round4(&C,D,A,B, 6, 15, 58); Round4(&B,C,D,A, 13, 21, 59); + Round4(&A,B,C,D, 4, 6, 60); Round4(&D,A,B,C, 11, 10, 61); Round4(&C,D,A,B, 2, 15, 62); Round4(&B,C,D,A, 9, 21, 63); + + // Then perform the following additions. (let's add) + *A2 = A + AA; + *B2 = B + BB; + *C2 = C + CC; + *D2 = D + DD; + + //The clearance of confidential information + memset(pX, 0, sizeof(X)); +} + +//------------------------------------------------------------------- +// The function for the exteriors + +/** output is the coded binary in the character sequence which wants to code string. */ +void MD5_String2binary(const char * string, char * output) +{ +//var + /*8bit*/ + unsigned char padding_message[64]; //Extended message 512bit 64byte + unsigned char *pstring; //The position of string in the present scanning notes is held. + +// unsigned char digest[16]; + /*32bit*/ + unsigned int string_byte_len, //The byte chief of string is held. + string_bit_len, //The bit length of string is held. + copy_len, //The number of bytes which is used by 1-3 and which remained + msg_digest[4]; //Message digest 128bit 4byte + unsigned int *A = &msg_digest[0], //The message digest in accordance with RFC (reference) + *B = &msg_digest[1], + *C = &msg_digest[2], + *D = &msg_digest[3]; + int i; + +//prog + //Step 3.Initialize MD Buffer (although it is the initialization; step 3 of A, B, C, and D -- unavoidable -- a head) + *A = 0x67452301; + *B = 0xefcdab89; + *C = 0x98badcfe; + *D = 0x10325476; + + //Step 1.Append Padding Bits (extension of a mark bit) + //1-1 + string_byte_len = strlen(string); //The byte chief of a character sequence is acquired. + pstring = (unsigned char *)string; //The position of the present character sequence is set. + + //1-2 Repeat calculation until length becomes less than 64 bytes. + for (i=string_byte_len; 64<=i; i-=64,pstring+=64) + MD5_Round_Calculate(pstring, A,B,C,D); + + //1-3 + copy_len = string_byte_len % 64; //The number of bytes which remained is computed. + strncpy((char *)padding_message, (char *)pstring, copy_len); //A message is copied to an extended bit sequence. + memset(padding_message+copy_len, 0, 64 - copy_len); //It buries by 0 until it becomes extended bit length. + padding_message[copy_len] |= 0x80; //The next of a message is 1. + + //1-4 + //If 56 bytes or more (less than 64 bytes) of remainder becomes, it will calculate by extending to 64 bytes. + if (56 <= copy_len) { + MD5_Round_Calculate(padding_message, A,B,C,D); + memset(padding_message, 0, 56); //56 bytes is newly fill uped with 0. + } + + + //Step 2.Append Length (the information on length is added) + string_bit_len = string_byte_len * 8; //From the byte chief to bit length (32 bytes of low rank) + memcpy(&padding_message[56], &string_bit_len, 4); //32 bytes of low rank is set. + + //When bit length cannot be expressed in 32 bytes of low rank, it is a beam raising to a higher rank. + if (UINT_MAX / 8 < string_byte_len) { + unsigned int high = (string_byte_len - UINT_MAX / 8) * 8; + memcpy(&padding_message[60], &high, 4); + } else + memset(&padding_message[60], 0, 4); //In this case, it is good for a higher rank at 0. + + //Step 4.Process Message in 16-Word Blocks (calculation of MD5) + MD5_Round_Calculate(padding_message, A,B,C,D); + + + //Step 5.Output (output) + memcpy(output,msg_digest,16); +// memcpy (digest, msg_digest, and 16); //8 byte*4 < - 32byte conversion A function called Encode as used in the field of RFC +/* sprintf(output, + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + digest[ 0], digest[ 1], digest[ 2], digest[ 3], + digest[ 4], digest[ 5], digest[ 6], digest[ 7], + digest[ 8], digest[ 9], digest[10], digest[11], + digest[12], digest[13], digest[14], digest[15]);*/ +} + +/** output is the coded character sequence in the character sequence which wants to code string. */ +void MD5_String(const char * string, char * output) +{ + unsigned char digest[16]; + + MD5_String2binary(string,(char*)digest); + sprintf(output, + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + digest[ 0], digest[ 1], digest[ 2], digest[ 3], + digest[ 4], digest[ 5], digest[ 6], digest[ 7], + digest[ 8], digest[ 9], digest[10], digest[11], + digest[12], digest[13], digest[14], digest[15]); +} + diff --git a/src/login_sql/md5calc.h b/src/login_sql/md5calc.h index b3735788c..505959c6a 100644 --- a/src/login_sql/md5calc.h +++ b/src/login_sql/md5calc.h @@ -1,10 +1,10 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _MD5CALC_H_ -#define _MD5CALC_H_ - -void MD5_String(const char * string, char * output); -void MD5_String2binary(const char * string, char * output); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _MD5CALC_H_ +#define _MD5CALC_H_ + +void MD5_String(const char * string, char * output); +void MD5_String2binary(const char * string, char * output); + +#endif diff --git a/src/map/atcommand.h b/src/map/atcommand.h index aebe49965..f3be9b0a0 100644 --- a/src/map/atcommand.h +++ b/src/map/atcommand.h @@ -1,335 +1,335 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _ATCOMMAND_H_ -#define _ATCOMMAND_H_ - -enum AtCommandType { - AtCommand_None = -1, - AtCommand_Broadcast = 0, - AtCommand_LocalBroadcast, - AtCommand_MapMove, - AtCommand_ResetState, - AtCommand_RuraP, - AtCommand_Rura, - AtCommand_Warp, - AtCommand_Where, - AtCommand_JumpTo, - AtCommand_Jump, - AtCommand_Who, - AtCommand_Who2, - AtCommand_Who3, - AtCommand_WhoMap, - AtCommand_WhoMap2, - AtCommand_WhoMap3, - AtCommand_WhoGM, - AtCommand_Save, - AtCommand_Load, - AtCommand_Speed, - AtCommand_CharSpeed, - AtCommand_Storage, - AtCommand_GuildStorage, - AtCommand_Option, - AtCommand_Hide, - AtCommand_JobChange, - AtCommand_JobChange2, - AtCommand_JobChange3, - AtCommand_Die, - AtCommand_Kill, - AtCommand_Alive, - AtCommand_Kami, - AtCommand_KamiB, - AtCommand_KamiC, //LuzZza - AtCommand_Heal, - AtCommand_Item, - AtCommand_Item2, - AtCommand_ItemReset, - AtCommand_BaseLevelUp, - AtCommand_JobLevelUp, - AtCommand_H, - AtCommand_Help, - AtCommand_H2, - AtCommand_Help2, - AtCommand_GM, - AtCommand_PvPOff, - AtCommand_PvPOn, - AtCommand_GvGOff, - AtCommand_GvGOn, - AtCommand_Model, - AtCommand_Go, - AtCommand_Spawn, - //AtCommand_Monster, // removed for Skots [Reddozen] - AtCommand_MonsterSmall, - AtCommand_MonsterBig, - AtCommand_KillMonster, - AtCommand_KillMonster2, - AtCommand_Refine, - AtCommand_Produce, - AtCommand_Memo, - AtCommand_GAT, - AtCommand_Packet, - AtCommand_WaterLevel, - AtCommand_StatusPoint, - AtCommand_SkillPoint, - AtCommand_Zeny, - AtCommand_Param, - AtCommand_Strength, - AtCommand_Agility, - AtCommand_Vitality, - AtCommand_Intelligence, - AtCommand_Dexterity, - AtCommand_Luck, - AtCommand_GuildLevelUp, - AtCommand_MakeEgg, - AtCommand_PetFriendly, - AtCommand_PetHungry, - AtCommand_PetRename, - AtCommand_Recall, - AtCommand_Revive, - AtCommand_CharacterStatsAll, - AtCommand_CharacterLoad, - AtCommand_Night, - AtCommand_Day, - AtCommand_Doom, - AtCommand_DoomMap, - AtCommand_Raise, - AtCommand_RaiseMap, - AtCommand_Kick, - AtCommand_KickAll, - AtCommand_AllSkill, - AtCommand_QuestSkill, - AtCommand_LostSkill, - AtCommand_SpiritBall, - AtCommand_Party, - AtCommand_Guild, - AtCommand_AgitStart, - AtCommand_AgitEnd, - AtCommand_MapExit, - AtCommand_IDSearch, - AtCommand_RecallAll, - AtCommand_ReloadItemDB, - AtCommand_ReloadMobDB, - AtCommand_ReloadSkillDB, - AtCommand_ReloadScript, - AtCommand_ReloadGMDB, - AtCommand_ReloadAtcommand, - AtCommand_ReloadBattleConf, - AtCommand_ReloadStatusDB, - AtCommand_ReloadPcDB, - AtCommand_ReloadMOTD, // [Valaris] - AtCommand_MapInfo, - AtCommand_Dye, - AtCommand_Hstyle, - AtCommand_Hcolor, - AtCommand_StatAll, - AtCommand_CharBlock, // by Yor - AtCommand_CharBan, // by Yor - AtCommand_CharUnBlock, // by Yor - AtCommand_CharUnBan, // by Yor - AtCommand_MountPeco, // by Valaris - AtCommand_CharMountPeco, // by Yor - AtCommand_GuildSpy, // [Syrus22] - AtCommand_PartySpy, // [Syrus22] - AtCommand_RepairAll, // [Valaris] - AtCommand_GuildRecall, // by Yor - AtCommand_PartyRecall, // by Yor - AtCommand_Nuke, // [Valaris] - AtCommand_Shownpc, - AtCommand_Hidenpc, - AtCommand_Loadnpc, - AtCommand_Unloadnpc, - AtCommand_ServerTime, // by Yor - AtCommand_CharDelItem, // by Yor - AtCommand_Jail, // by Yor - AtCommand_UnJail, // by Yor - AtCommand_JailFor, // Meruru - AtCommand_JailTime, // Coltaro - AtCommand_CharJailTime, // Coltaro - AtCommand_Disguise, // [Valaris] - AtCommand_UnDisguise, // by Yor - AtCommand_CharDisguise, // Kalaspuff - AtCommand_CharUnDisguise, // Kalaspuff - AtCommand_EMail, // by Yor - AtCommand_Hatch, - AtCommand_Effect, // by Apple - AtCommand_Char_Cart_List, // by Yor - AtCommand_AddWarp, // by MouseJstr - AtCommand_Follow, // by MouseJstr - AtCommand_SkillOn, // by MouseJstr - AtCommand_SkillOff, // by MouseJstr - AtCommand_Killer, // by MouseJstr - AtCommand_NpcMove, // by MouseJstr - AtCommand_Killable, // by MouseJstr - AtCommand_CharKillable, // by MouseJstr - AtCommand_Dropall, // by MouseJstr - AtCommand_Chardropall, // by MouseJstr - AtCommand_Storeall, // by MouseJstr - AtCommand_Charstoreall, // by MouseJstr - AtCommand_Skillid, // by MouseJstr - AtCommand_Useskill, // by MouseJstr - AtCommand_Summon, - AtCommand_Rain, - AtCommand_Snow, - AtCommand_Sakura, - AtCommand_Clouds, - AtCommand_Clouds2, // [Valaris] - AtCommand_Fog, - AtCommand_Fireworks, - AtCommand_Leaves, - AtCommand_AdjGmLvl, // MouseJstr - AtCommand_AdjCmdLvl, // MouseJstr - AtCommand_Trade, // MouseJstr - AtCommand_Send, - AtCommand_SetBattleFlag, - AtCommand_UnMute, - AtCommand_Clearweather, // by Dexity - AtCommand_UpTime, // by MC Cameri - AtCommand_ChangeSex, // by MC Cameri - AtCommand_Mute, // [celest] - AtCommand_WhoZeny, // [Valaris] <-- LOL...(MC Cameri) worth it. - AtCommand_HappyHappyJoyJoy, // [Valaris] - AtCommand_Refresh, // by MC Cameri - AtCommand_PetId, // by MC Cameri - AtCommand_Identify, // by MC Cameri - AtCommand_Gmotd, // Added by MC Cameri, created by davidsiaw - AtCommand_MiscEffect, // by MC Cameri - AtCommand_MobSearch, - AtCommand_CleanMap, - AtCommand_NpcTalk, - AtCommand_PetTalk, - AtCommand_Users, - // SQL-only commands start -#ifndef TXT_ONLY - AtCommand_CheckMail, // [Valaris] - AtCommand_ListMail, // [Valaris] - AtCommand_ListNewMail, // [Valaris] - AtCommand_ReadMail, // [Valaris] - AtCommand_SendMail, // [Valaris] - AtCommand_DeleteMail, // [Valaris] - AtCommand_SendPriorityMail, // [Valaris] - AtCommand_RefreshOnline, // [Valaris] - // SQL-only commands end -#endif - AtCommand_SkillTree, // by MouseJstr - AtCommand_Marry, // by MouseJstr - AtCommand_Divorce, // by MouseJstr - AtCommand_Grind, // by MouseJstr - AtCommand_Grind2, // by MouseJstr - - AtCommand_Me, //added by massdriller, code by lordalfa - - AtCommand_DMStart, // by MouseJstr - AtCommand_DMTick, // by MouseJstr - - AtCommand_JumpToId, // by Dino9021 - AtCommand_JumpToId2, // by Dino9021 - AtCommand_RecallId, // by Dino9021 - AtCommand_RecallId2, // by Dino9021 - AtCommand_KickId, // by Dino9021 - AtCommand_KickId2, // by Dino9021 - AtCommand_ReviveId, // by Dino9021 - AtCommand_ReviveId2, // by Dino9021 - AtCommand_KillId, // by Dino9021 - AtCommand_KillId2, // by Dino9021 - AtCommand_CharKillableId, // by Dino9021 - AtCommand_CharKillableId2, // by Dino9021 - AtCommand_Sound, - AtCommand_UndisguiseAll, - AtCommand_DisguiseAll, - AtCommand_ChangeLook, - AtCommand_AutoLoot, //by Upa-Kun - AtCommand_MobInfo, //by Lupus - AtCommand_Exp, // by Skotlex - AtCommand_Adopt, // by Veider - AtCommand_Version, // by Ancyker - - AtCommand_MuteArea, // MouseJstr - AtCommand_Shuffle, // MouseJstr - AtCommand_Rates, // MouseJstr - - AtCommand_ItemInfo, // Lupus - AtCommand_WhoDrops, // Skotlex - AtCommand_MapFlag, // Lupus - AtCommand_MonsterIgnore, // [Valaris] - AtCommand_FakeName, // [Valaris] - AtCommand_Size, // [Valaris] - AtCommand_ShowDelay, - AtCommand_ShowExp, - AtCommand_ShowZeny, - AtCommand_AutoTrade,//durf - AtCommand_ChangeGM,//durf - AtCommand_ChangeLeader, - AtCommand_PartyOption, - - AtCommand_Invite, // By LuzZza - AtCommand_Duel, // By LuzZza - AtCommand_Leave, // By LuzZza - AtCommand_Accept, // By LuzZza - AtCommand_Reject, // By LuzZza - - AtCommand_Away, // LuzZza - AtCommand_Main, // LuzZza - - AtCommand_Clone, // [Valaris] - AtCommand_ToNPC, // LuzZza - AtCommand_Commands, // [Skotlex] - AtCommand_NoAsk, // [LuzZza] - AtCommand_Request, // [Skotlex], supposedly taken from Freya (heard the command was there, but I haven't seen the code yet) - AtCommand_HomLevel, //[orn] - AtCommand_HomEvolution, //[orn] - AtCommand_MakeHomun, //[orn] - AtCommand_HomFriendly, //[orn] - AtCommand_HomHungry, //[orn] - AtCommand_HomTalk, //[orn] - AtCommand_HomInfo, //[Toms] - AtCommand_ShowMobs, //KarLaeda - // end <- Ahem, guys, don't place AtCommands after AtCommand_Unknown! [Skotlex] - AtCommand_Unknown, - AtCommand_MAX -}; - -typedef enum AtCommandType AtCommandType; - -typedef struct AtCommandInfo { - AtCommandType type; - const char* command; - int level; - int (*proc)(const int, struct map_session_data*, - const char* command, const char* message); -} AtCommandInfo; - -AtCommandType -is_atcommand(const int fd, struct map_session_data* sd, const char* message); -AtCommandType -atcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl); - -AtCommandType atcommand( - struct map_session_data *sd, - const int level, const char* message, AtCommandInfo* info); -int get_atcommand_level(const AtCommandType type); - -char * msg_txt(int msg_number); // [Yor] -char * player_title_txt(int level); // [Lupus] - -void do_init_atcommand(void); -void do_final_atcommand(void); - -int atcommand_item(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Valaris] -int atcommand_rura(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Yor] -int atcommand_jumpto(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor] -int atcommand_recall(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor] -int atcommand_monster(const int fd, struct map_session_data* sd, const char* command, const char* message); - -int atcommand_config_read(const char *cfgName); -int msg_config_read(const char *cfgName); -void do_final_msg(void); - -char *estr_lower(char *str); - -int e_mail_check(char *email); - -#define MAX_MSG 1000 -extern char *msg_table[MAX_MSG]; - -#endif - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _ATCOMMAND_H_ +#define _ATCOMMAND_H_ + +enum AtCommandType { + AtCommand_None = -1, + AtCommand_Broadcast = 0, + AtCommand_LocalBroadcast, + AtCommand_MapMove, + AtCommand_ResetState, + AtCommand_RuraP, + AtCommand_Rura, + AtCommand_Warp, + AtCommand_Where, + AtCommand_JumpTo, + AtCommand_Jump, + AtCommand_Who, + AtCommand_Who2, + AtCommand_Who3, + AtCommand_WhoMap, + AtCommand_WhoMap2, + AtCommand_WhoMap3, + AtCommand_WhoGM, + AtCommand_Save, + AtCommand_Load, + AtCommand_Speed, + AtCommand_CharSpeed, + AtCommand_Storage, + AtCommand_GuildStorage, + AtCommand_Option, + AtCommand_Hide, + AtCommand_JobChange, + AtCommand_JobChange2, + AtCommand_JobChange3, + AtCommand_Die, + AtCommand_Kill, + AtCommand_Alive, + AtCommand_Kami, + AtCommand_KamiB, + AtCommand_KamiC, //LuzZza + AtCommand_Heal, + AtCommand_Item, + AtCommand_Item2, + AtCommand_ItemReset, + AtCommand_BaseLevelUp, + AtCommand_JobLevelUp, + AtCommand_H, + AtCommand_Help, + AtCommand_H2, + AtCommand_Help2, + AtCommand_GM, + AtCommand_PvPOff, + AtCommand_PvPOn, + AtCommand_GvGOff, + AtCommand_GvGOn, + AtCommand_Model, + AtCommand_Go, + AtCommand_Spawn, + //AtCommand_Monster, // removed for Skots [Reddozen] + AtCommand_MonsterSmall, + AtCommand_MonsterBig, + AtCommand_KillMonster, + AtCommand_KillMonster2, + AtCommand_Refine, + AtCommand_Produce, + AtCommand_Memo, + AtCommand_GAT, + AtCommand_Packet, + AtCommand_WaterLevel, + AtCommand_StatusPoint, + AtCommand_SkillPoint, + AtCommand_Zeny, + AtCommand_Param, + AtCommand_Strength, + AtCommand_Agility, + AtCommand_Vitality, + AtCommand_Intelligence, + AtCommand_Dexterity, + AtCommand_Luck, + AtCommand_GuildLevelUp, + AtCommand_MakeEgg, + AtCommand_PetFriendly, + AtCommand_PetHungry, + AtCommand_PetRename, + AtCommand_Recall, + AtCommand_Revive, + AtCommand_CharacterStatsAll, + AtCommand_CharacterLoad, + AtCommand_Night, + AtCommand_Day, + AtCommand_Doom, + AtCommand_DoomMap, + AtCommand_Raise, + AtCommand_RaiseMap, + AtCommand_Kick, + AtCommand_KickAll, + AtCommand_AllSkill, + AtCommand_QuestSkill, + AtCommand_LostSkill, + AtCommand_SpiritBall, + AtCommand_Party, + AtCommand_Guild, + AtCommand_AgitStart, + AtCommand_AgitEnd, + AtCommand_MapExit, + AtCommand_IDSearch, + AtCommand_RecallAll, + AtCommand_ReloadItemDB, + AtCommand_ReloadMobDB, + AtCommand_ReloadSkillDB, + AtCommand_ReloadScript, + AtCommand_ReloadGMDB, + AtCommand_ReloadAtcommand, + AtCommand_ReloadBattleConf, + AtCommand_ReloadStatusDB, + AtCommand_ReloadPcDB, + AtCommand_ReloadMOTD, // [Valaris] + AtCommand_MapInfo, + AtCommand_Dye, + AtCommand_Hstyle, + AtCommand_Hcolor, + AtCommand_StatAll, + AtCommand_CharBlock, // by Yor + AtCommand_CharBan, // by Yor + AtCommand_CharUnBlock, // by Yor + AtCommand_CharUnBan, // by Yor + AtCommand_MountPeco, // by Valaris + AtCommand_CharMountPeco, // by Yor + AtCommand_GuildSpy, // [Syrus22] + AtCommand_PartySpy, // [Syrus22] + AtCommand_RepairAll, // [Valaris] + AtCommand_GuildRecall, // by Yor + AtCommand_PartyRecall, // by Yor + AtCommand_Nuke, // [Valaris] + AtCommand_Shownpc, + AtCommand_Hidenpc, + AtCommand_Loadnpc, + AtCommand_Unloadnpc, + AtCommand_ServerTime, // by Yor + AtCommand_CharDelItem, // by Yor + AtCommand_Jail, // by Yor + AtCommand_UnJail, // by Yor + AtCommand_JailFor, // Meruru + AtCommand_JailTime, // Coltaro + AtCommand_CharJailTime, // Coltaro + AtCommand_Disguise, // [Valaris] + AtCommand_UnDisguise, // by Yor + AtCommand_CharDisguise, // Kalaspuff + AtCommand_CharUnDisguise, // Kalaspuff + AtCommand_EMail, // by Yor + AtCommand_Hatch, + AtCommand_Effect, // by Apple + AtCommand_Char_Cart_List, // by Yor + AtCommand_AddWarp, // by MouseJstr + AtCommand_Follow, // by MouseJstr + AtCommand_SkillOn, // by MouseJstr + AtCommand_SkillOff, // by MouseJstr + AtCommand_Killer, // by MouseJstr + AtCommand_NpcMove, // by MouseJstr + AtCommand_Killable, // by MouseJstr + AtCommand_CharKillable, // by MouseJstr + AtCommand_Dropall, // by MouseJstr + AtCommand_Chardropall, // by MouseJstr + AtCommand_Storeall, // by MouseJstr + AtCommand_Charstoreall, // by MouseJstr + AtCommand_Skillid, // by MouseJstr + AtCommand_Useskill, // by MouseJstr + AtCommand_Summon, + AtCommand_Rain, + AtCommand_Snow, + AtCommand_Sakura, + AtCommand_Clouds, + AtCommand_Clouds2, // [Valaris] + AtCommand_Fog, + AtCommand_Fireworks, + AtCommand_Leaves, + AtCommand_AdjGmLvl, // MouseJstr + AtCommand_AdjCmdLvl, // MouseJstr + AtCommand_Trade, // MouseJstr + AtCommand_Send, + AtCommand_SetBattleFlag, + AtCommand_UnMute, + AtCommand_Clearweather, // by Dexity + AtCommand_UpTime, // by MC Cameri + AtCommand_ChangeSex, // by MC Cameri + AtCommand_Mute, // [celest] + AtCommand_WhoZeny, // [Valaris] <-- LOL...(MC Cameri) worth it. + AtCommand_HappyHappyJoyJoy, // [Valaris] + AtCommand_Refresh, // by MC Cameri + AtCommand_PetId, // by MC Cameri + AtCommand_Identify, // by MC Cameri + AtCommand_Gmotd, // Added by MC Cameri, created by davidsiaw + AtCommand_MiscEffect, // by MC Cameri + AtCommand_MobSearch, + AtCommand_CleanMap, + AtCommand_NpcTalk, + AtCommand_PetTalk, + AtCommand_Users, + // SQL-only commands start +#ifndef TXT_ONLY + AtCommand_CheckMail, // [Valaris] + AtCommand_ListMail, // [Valaris] + AtCommand_ListNewMail, // [Valaris] + AtCommand_ReadMail, // [Valaris] + AtCommand_SendMail, // [Valaris] + AtCommand_DeleteMail, // [Valaris] + AtCommand_SendPriorityMail, // [Valaris] + AtCommand_RefreshOnline, // [Valaris] + // SQL-only commands end +#endif + AtCommand_SkillTree, // by MouseJstr + AtCommand_Marry, // by MouseJstr + AtCommand_Divorce, // by MouseJstr + AtCommand_Grind, // by MouseJstr + AtCommand_Grind2, // by MouseJstr + + AtCommand_Me, //added by massdriller, code by lordalfa + + AtCommand_DMStart, // by MouseJstr + AtCommand_DMTick, // by MouseJstr + + AtCommand_JumpToId, // by Dino9021 + AtCommand_JumpToId2, // by Dino9021 + AtCommand_RecallId, // by Dino9021 + AtCommand_RecallId2, // by Dino9021 + AtCommand_KickId, // by Dino9021 + AtCommand_KickId2, // by Dino9021 + AtCommand_ReviveId, // by Dino9021 + AtCommand_ReviveId2, // by Dino9021 + AtCommand_KillId, // by Dino9021 + AtCommand_KillId2, // by Dino9021 + AtCommand_CharKillableId, // by Dino9021 + AtCommand_CharKillableId2, // by Dino9021 + AtCommand_Sound, + AtCommand_UndisguiseAll, + AtCommand_DisguiseAll, + AtCommand_ChangeLook, + AtCommand_AutoLoot, //by Upa-Kun + AtCommand_MobInfo, //by Lupus + AtCommand_Exp, // by Skotlex + AtCommand_Adopt, // by Veider + AtCommand_Version, // by Ancyker + + AtCommand_MuteArea, // MouseJstr + AtCommand_Shuffle, // MouseJstr + AtCommand_Rates, // MouseJstr + + AtCommand_ItemInfo, // Lupus + AtCommand_WhoDrops, // Skotlex + AtCommand_MapFlag, // Lupus + AtCommand_MonsterIgnore, // [Valaris] + AtCommand_FakeName, // [Valaris] + AtCommand_Size, // [Valaris] + AtCommand_ShowDelay, + AtCommand_ShowExp, + AtCommand_ShowZeny, + AtCommand_AutoTrade,//durf + AtCommand_ChangeGM,//durf + AtCommand_ChangeLeader, + AtCommand_PartyOption, + + AtCommand_Invite, // By LuzZza + AtCommand_Duel, // By LuzZza + AtCommand_Leave, // By LuzZza + AtCommand_Accept, // By LuzZza + AtCommand_Reject, // By LuzZza + + AtCommand_Away, // LuzZza + AtCommand_Main, // LuzZza + + AtCommand_Clone, // [Valaris] + AtCommand_ToNPC, // LuzZza + AtCommand_Commands, // [Skotlex] + AtCommand_NoAsk, // [LuzZza] + AtCommand_Request, // [Skotlex], supposedly taken from Freya (heard the command was there, but I haven't seen the code yet) + AtCommand_HomLevel, //[orn] + AtCommand_HomEvolution, //[orn] + AtCommand_MakeHomun, //[orn] + AtCommand_HomFriendly, //[orn] + AtCommand_HomHungry, //[orn] + AtCommand_HomTalk, //[orn] + AtCommand_HomInfo, //[Toms] + AtCommand_ShowMobs, //KarLaeda + // end <- Ahem, guys, don't place AtCommands after AtCommand_Unknown! [Skotlex] + AtCommand_Unknown, + AtCommand_MAX +}; + +typedef enum AtCommandType AtCommandType; + +typedef struct AtCommandInfo { + AtCommandType type; + const char* command; + int level; + int (*proc)(const int, struct map_session_data*, + const char* command, const char* message); +} AtCommandInfo; + +AtCommandType +is_atcommand(const int fd, struct map_session_data* sd, const char* message); +AtCommandType +atcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl); + +AtCommandType atcommand( + struct map_session_data *sd, + const int level, const char* message, AtCommandInfo* info); +int get_atcommand_level(const AtCommandType type); + +char * msg_txt(int msg_number); // [Yor] +char * player_title_txt(int level); // [Lupus] + +void do_init_atcommand(void); +void do_final_atcommand(void); + +int atcommand_item(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Valaris] +int atcommand_rura(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Yor] +int atcommand_jumpto(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor] +int atcommand_recall(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor] +int atcommand_monster(const int fd, struct map_session_data* sd, const char* command, const char* message); + +int atcommand_config_read(const char *cfgName); +int msg_config_read(const char *cfgName); +void do_final_msg(void); + +char *estr_lower(char *str); + +int e_mail_check(char *email); + +#define MAX_MSG 1000 +extern char *msg_table[MAX_MSG]; + +#endif + diff --git a/src/map/battle.h b/src/map/battle.h index 709cc3c78..94ced24f7 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -1,455 +1,455 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _BATTLE_H_ -#define _BATTLE_H_ - -// ダメージ -struct Damage { - int damage,damage2; - int type,div_; - int amotion,dmotion; - int blewcount; - int flag; - int dmg_lv; //ATK_LUCKY,ATK_FLEE,ATK_DEF -}; - -// 属性表(読み込みはpc.c、battle_attr_fixで使用) -extern int attr_fix_table[4][10][10]; - -struct map_session_data; -struct mob_data; -struct block_list; - -// ダメージ計算 - -struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag); - -int battle_calc_return_damage(struct block_list *bl, int skill, int *damage, int flag); - -void battle_drain(struct map_session_data *sd, struct block_list *tbl, int rdamage, int ldamage, int race, int boss); - -int battle_attr_fix(struct block_list *src, struct block_list *target, int damage,int atk_elem,int def_type, int def_lv); - -// ダメージ最終計算 -int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag); -int battle_calc_gvg_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag); - -enum { // 最終計算のフラグ - BF_WEAPON = 0x0001, - BF_MAGIC = 0x0002, - BF_MISC = 0x0004, - BF_SHORT = 0x0010, - BF_LONG = 0x0040, - BF_SKILL = 0x0100, - BF_NORMAL = 0x0200, - BF_WEAPONMASK=0x000f, - BF_RANGEMASK= 0x00f0, - BF_SKILLMASK= 0x0f00, -}; - -int battle_delay_damage (unsigned int tick, struct block_list *src, struct block_list *target, int attack_type, int skill_id, int skill_lv, int damage, int dmg_lv, int ddelay); - -// 通常攻撃処理まとめ -int battle_weapon_attack( struct block_list *bl,struct block_list *target, - unsigned int tick,int flag); - -// 各種パラメータを得る -struct block_list* battle_get_master(struct block_list *src); -struct block_list* battle_gettargeted(struct block_list *target); -int battle_gettarget(struct block_list *bl); -int battle_getcurrentskill(struct block_list *bl); - -//New definitions [Skotlex] -#define BCT_ENEMY 0x020000 -//This should be (~BCT_ENEMY&BCT_ALL) -#define BCT_NOENEMY 0x1d0000 -#define BCT_PARTY 0x040000 -//This should be (~BCT_PARTY&BCT_ALL) -#define BCT_NOPARTY 0x1b0000 -#define BCT_GUILD 0x080000 -//This should be (~BCT_GUILD&BCT_ALL) -#define BCT_NOGUILD 0x170000 -#define BCT_ALL 0x1f0000 -#define BCT_NOONE 0x000000 -#define BCT_SELF 0x010000 -#define BCT_NEUTRAL 0x100000 - -#define is_boss(bl) (status_get_mode(bl)&MD_BOSS) // Can refine later [Aru] - -int battle_check_undead(int race,int element); -int battle_check_target(struct block_list *src, struct block_list *target,int flag); -int battle_check_range(struct block_list *src,struct block_list *bl,int range); - -void battle_consume_ammo(struct map_session_data* sd, int skill, int lv); -// 設定 - -int battle_config_switch(const char *str); // [Valaris] - -extern struct Battle_Config { - unsigned short warp_point_debug; - unsigned short enable_critical; - unsigned short mob_critical_rate; - unsigned short critical_rate; - unsigned short enable_baseatk; - unsigned short enable_perfect_flee; - unsigned short cast_rate,delay_rate,delay_dependon_agi; - unsigned short sdelay_attack_enable; - unsigned short left_cardfix_to_right; - unsigned short skill_add_range; - unsigned short skill_out_range_consume; - unsigned short skillrange_by_distance; //[Skotlex] - unsigned short use_weapon_skill_range; //[Skotlex] - unsigned short pc_damage_delay_rate; - unsigned short defnotenemy; - unsigned short vs_traps_bctall; - unsigned short traps_setting; - unsigned short summon_flora; //[Skotlex] - unsigned short clear_unit_ondeath; //[Skotlex] - unsigned short clear_unit_onwarp; //[Skotlex] - unsigned short random_monster_checklv; - unsigned short attr_recover; - unsigned short flooritem_lifetime; - unsigned short item_auto_get; - int item_first_get_time; - int item_second_get_time; - int item_third_get_time; - int mvp_item_first_get_time; - int mvp_item_second_get_time; - int mvp_item_third_get_time; - int base_exp_rate,job_exp_rate; - unsigned short drop_rate0item; - unsigned short death_penalty_type; - unsigned short death_penalty_base,death_penalty_job; - unsigned short pvp_exp; // [MouseJstr] - unsigned short gtb_sc_immunity; - int zeny_penalty; - unsigned short restart_hp_rate; - unsigned short restart_sp_rate; - int mvp_exp_rate; - unsigned short mvp_hp_rate; - unsigned short monster_hp_rate; - unsigned short monster_max_aspd; - unsigned short view_range_rate; - unsigned short chase_range_rate; - unsigned short atc_gmonly; - unsigned short atc_spawn_quantity_limit; - unsigned short atc_slave_clone_limit; - unsigned short partial_name_scan; - unsigned short gm_allskill; - unsigned short gm_allequip; - unsigned short gm_skilluncond; - unsigned short gm_join_chat; - unsigned short gm_kick_chat; - unsigned short skillfree; - unsigned short skillup_limit; - unsigned short wp_rate; - unsigned short pp_rate; - unsigned short monster_active_enable; - unsigned short monster_damage_delay_rate; - unsigned short monster_loot_type; - unsigned short mob_skill_rate; //[Skotlex] - unsigned short mob_skill_delay; //[Skotlex] - unsigned short mob_count_rate; - unsigned short no_spawn_on_player; //[Skotlex] - unsigned short force_random_spawn; //[Skotlex] - unsigned short mob_spawn_delay, plant_spawn_delay, boss_spawn_delay; // [Skotlex] - unsigned short slaves_inherit_mode; - unsigned short slaves_inherit_speed; - unsigned short summons_trigger_autospells; - unsigned short pc_walk_delay_rate; //Adjusts can't walk delay after being hit for players. [Skotlex] - unsigned short walk_delay_rate; //Adjusts can't walk delay after being hit. [Skotlex] - unsigned short multihit_delay; //Adjusts can't walk delay per hit on multi-hitting skills. [Skotlex] - unsigned short quest_skill_learn; - unsigned short quest_skill_reset; - unsigned short basic_skill_check; - unsigned short guild_emperium_check; - unsigned short guild_exp_limit; - unsigned short guild_max_castles; - unsigned short emergency_call; - unsigned short guild_aura; - unsigned short pc_invincible_time; - unsigned short pet_catch_rate; - unsigned short pet_rename; - unsigned short pet_friendly_rate; - unsigned short pet_hungry_delay_rate; - unsigned short pet_hungry_friendly_decrease; - unsigned short pet_status_support; - unsigned short pet_attack_support; - unsigned short pet_damage_support; - unsigned short pet_support_min_friendly; //[Skotlex] - unsigned short pet_support_rate; - unsigned short pet_attack_exp_to_master; - unsigned short pet_attack_exp_rate; - unsigned short pet_lv_rate; //[Skotlex] - unsigned short pet_max_stats; //[Skotlex] - unsigned short pet_max_atk1; //[Skotlex] - unsigned short pet_max_atk2; //[Skotlex] - unsigned short pet_no_gvg; //Disables pets in gvg. [Skotlex] - unsigned short skill_min_damage; - unsigned short finger_offensive_type; - unsigned short heal_exp; - unsigned short max_heal_lv; - int max_heal; //Mitternacht - unsigned short resurrection_exp; - unsigned short shop_exp; - unsigned short combo_delay_rate; - unsigned short item_check; - unsigned short item_use_interval; //[Skotlex] - unsigned short wedding_modifydisplay; - unsigned short wedding_ignorepalette; //[Skotlex] - unsigned short xmas_ignorepalette; // [Valaris] - int natural_healhp_interval; - int natural_healsp_interval; - int natural_heal_skill_interval; - unsigned short natural_heal_weight_rate; - unsigned short item_name_override_grffile; - unsigned short indoors_override_grffile; // [Celest] - unsigned short skill_sp_override_grffile; // [Celest] - unsigned short cardillust_read_grffile; - unsigned short item_equip_override_grffile; - unsigned short item_slots_override_grffile; - unsigned short arrow_decrement; - unsigned short max_aspd; - unsigned short max_walk_speed; //Maximum walking speed after buffs [Skotlex] - int max_hp; - int max_sp; - unsigned short max_lv, aura_lv; - unsigned short max_parameter, max_baby_parameter; - int max_cart_weight; - unsigned short skill_log; - unsigned short battle_log; - unsigned short save_log; - unsigned short error_log; - unsigned short etc_log; - unsigned short save_clothcolor; - unsigned short undead_detect_type; - unsigned short auto_counter_type; - unsigned short min_hitrate; //[Skotlex] - unsigned short max_hitrate; //[Skotlex] - unsigned short agi_penalty_target; - unsigned short agi_penalty_type; - unsigned short agi_penalty_count; - unsigned short agi_penalty_num; - unsigned short vit_penalty_target; - unsigned short vit_penalty_type; - unsigned short vit_penalty_count; - unsigned short vit_penalty_num; - unsigned short weapon_defense_type; - unsigned short magic_defense_type; - unsigned short skill_reiteration; - unsigned short skill_nofootset; - unsigned short pc_cloak_check_type; - unsigned short monster_cloak_check_type; - unsigned short estimation_type; - unsigned short gvg_short_damage_rate; - unsigned short gvg_long_damage_rate; - unsigned short gvg_weapon_damage_rate; - unsigned short gvg_magic_damage_rate; - unsigned short gvg_misc_damage_rate; - unsigned short gvg_flee_penalty; - int gvg_eliminate_time; - unsigned short pk_short_damage_rate; - unsigned short pk_long_damage_rate; - unsigned short pk_weapon_damage_rate; - unsigned short pk_magic_damage_rate; - unsigned short pk_misc_damage_rate; - unsigned short mob_changetarget_byskill; - unsigned short attack_direction_change; - unsigned short land_skill_limit; - unsigned short party_skill_penalty; - unsigned short monster_class_change_full_recover; - unsigned short produce_item_name_input; - unsigned short produce_potion_name_input; - unsigned short making_arrow_name_input; - unsigned short holywater_name_input; - unsigned short cdp_name_input; - unsigned short display_skill_fail; - unsigned short chat_warpportal; - unsigned short mob_warp; - unsigned short dead_branch_active; - unsigned int vending_max_value; - unsigned short show_steal_in_same_party; - unsigned short party_share_type; - unsigned short party_hp_mode; - unsigned short party_show_share_picker; - unsigned short attack_attr_none; - int item_rate_mvp, item_rate_common, item_rate_common_boss, item_rate_card, item_rate_card_boss, // added support for MVP drops [Reddozen] - item_rate_equip, item_rate_equip_boss, item_rate_heal, item_rate_heal_boss, item_rate_use, - item_rate_use_boss, item_rate_treasure, // Added by RoVeRT, Additional Heal and Usable item rate by Val - item_rate_adddrop; - - unsigned short logarithmic_drops; - unsigned short item_drop_common_min,item_drop_common_max; // Added by TyrNemesis^ - unsigned short item_drop_card_min,item_drop_card_max; - unsigned short item_drop_equip_min,item_drop_equip_max; - unsigned short item_drop_mvp_min,item_drop_mvp_max; // End Addition - unsigned short item_drop_heal_min,item_drop_heal_max; // Added by Valatris - unsigned short item_drop_use_min,item_drop_use_max; //End - unsigned short item_drop_treasure_min,item_drop_treasure_max; //by [Skotlex] - unsigned short item_drop_adddrop_min,item_drop_adddrop_max; //[Skotlex] - unsigned short prevent_logout; // Added by RoVeRT - - unsigned short alchemist_summon_reward; // [Valaris] - unsigned short drops_by_luk; - unsigned short drops_by_luk2; - unsigned short equip_natural_break_rate; //Base Natural break rate for attacks. - unsigned short equip_self_break_rate; //Natural & Penalty skills break rate - unsigned short equip_skill_break_rate; //Offensive skills break rate - unsigned short pet_equip_required; - unsigned short multi_level_up; - unsigned short max_exp_gain_rate; //Max amount of exp bar % you can get in one go. - unsigned short pk_mode; - unsigned short pk_level_range; - - unsigned short manner_system; // end additions [Valaris] - unsigned short show_mob_info; - - unsigned short agi_penalty_count_lv; - unsigned short vit_penalty_count_lv; - - unsigned short gx_allhit; - unsigned short gx_disptype; - unsigned short devotion_level_difference; - unsigned short player_skill_partner_check; - unsigned short hide_GM_session; - unsigned short invite_request_check; - unsigned short skill_removetrap_type; - unsigned short disp_experience; - unsigned short disp_zeny; - unsigned short castle_defense_rate; - unsigned short backstab_bow_penalty; - unsigned short hp_rate; - unsigned short sp_rate; - unsigned short gm_cant_drop_min_lv; - unsigned short gm_cant_drop_max_lv; - unsigned short disp_hpmeter; - unsigned short bone_drop; - unsigned short buyer_name; - -// eAthena additions - unsigned short night_at_start; // added by [Yor] - int day_duration; // added by [Yor] - int night_duration; // added by [Yor] - short ban_hack_trade; // added by [Yor] - unsigned short hack_info_GM_level; // added by [Yor] - unsigned short any_warp_GM_min_level; // added by [Yor] - unsigned short packet_ver_flag; // added by [Yor] - - unsigned short min_hair_style; // added by [MouseJstr] - unsigned short max_hair_style; // added by [MouseJstr] - unsigned short min_hair_color; // added by [MouseJstr] - unsigned short max_hair_color; // added by [MouseJstr] - unsigned short min_cloth_color; // added by [MouseJstr] - unsigned short max_cloth_color; // added by [MouseJstr] - unsigned short pet_hair_style; // added by [Skotlex] - - unsigned short castrate_dex_scale; // added by [MouseJstr] - unsigned short area_size; // added by [MouseJstr] - - unsigned short max_def, over_def_bonus; //added by [Skotlex] - - unsigned short zeny_from_mobs; // [Valaris] - unsigned short mobs_level_up; // [Valaris] - unsigned short mobs_level_up_exp_rate; // [Valaris] - unsigned short pk_min_level; // [celest] - unsigned short skill_steal_type; // [celest] - unsigned short skill_steal_rate; // [celest] - unsigned short skill_steal_max_tries; //max steal skill tries on a mob. if 0, then w/o limit [Lupus] - unsigned short motd_type; // [celest] - unsigned short finding_ore_rate; // orn - unsigned short exp_calc_type; - unsigned short exp_bonus_attacker; - unsigned short exp_bonus_max_attacker; - unsigned short min_skill_delay_limit; - unsigned short default_skill_delay; - unsigned short no_skill_delay; - unsigned short attack_walk_delay; - unsigned short require_glory_guild; - unsigned short idle_no_share; - unsigned short party_update_interval; - unsigned short party_even_share_bonus; - unsigned short delay_battle_damage; - unsigned short hide_woe_damage; - unsigned short display_version; - unsigned short who_display_aid; - - unsigned short display_hallucination; // [Skotlex] - unsigned short use_statpoint_table; // [Skotlex] - - unsigned short ignore_items_gender; //[Lupus] - - unsigned short copyskill_restrict; // [Aru] - unsigned short berserk_cancels_buffs; // [Aru] - unsigned short debuff_on_logout; // Removes a few "official" negative Scs on logout. [Skotlex] - unsigned short mob_ai; //Configures various mob_ai settings to make them smarter or dumber(official). [Skotlex] - unsigned short dynamic_mobs; // Dynamic Mobs [Wizputer] - battle_athena flag implemented by [random] - unsigned short mob_remove_damaged; // Dynamic Mobs - Remove mobs even if damaged [Wizputer] - int mob_remove_delay; // Dynamic Mobs - delay before removing mobs from a map [Skotlex] - - unsigned short show_hp_sp_drain, show_hp_sp_gain; //[Skotlex] - - unsigned short mob_npc_event_type; //Determines on who the npc_event is executed. [Skotlex] - unsigned short mob_clear_delay; // [Valaris] - - unsigned short character_size; // if riders have size=2, and baby class riders size=1 [Lupus] - unsigned short mob_max_skilllvl; // Max possible skill level [Lupus] - unsigned short rare_drop_announce; // chance <= to show rare drops global announces - - unsigned short retaliate_to_master; //Whether when a mob is attacked by another mob, it will retaliate versus the mob or the mob's master. [Skotlex] - unsigned short firewall_hits_on_undead; //Number of hits firewall does at a time on undead. [Skotlex] - - unsigned short title_lvl1; // Players titles [Lupus] - unsigned short title_lvl2; // Players titles [Lupus] - unsigned short title_lvl3; // Players titles [Lupus] - unsigned short title_lvl4; // Players titles [Lupus] - unsigned short title_lvl5; // Players titles [Lupus] - unsigned short title_lvl6; // Players titles [Lupus] - unsigned short title_lvl7; // Players titles [Lupus] - unsigned short title_lvl8; // Players titles [Lupus] - - unsigned short duel_enable; // [LuzZza] - unsigned short duel_allow_pvp; // [LuzZza] - unsigned short duel_allow_gvg; // [LuzZza] - unsigned short duel_allow_teleport; // [LuzZza] - unsigned short duel_autoleave_when_die; // [LuzZza] - unsigned short duel_time_interval; // [LuzZza] - unsigned short duel_only_on_same_map; // [Toms] - - unsigned short skip_teleport_lv1_menu; // possibility to disable (skip) Teleport Lv1 menu, that have only two lines `Random` and `Cancel` [LuzZza] - - unsigned short allow_skill_without_day; // [Komurka] - unsigned short allow_es_magic_pc; // [Skotlex] - unsigned short skill_wall_check; // [Skotlex] - unsigned short cell_stack_limit; // [Skotlex] - unsigned short skill_caster_check; // [Skotlex] - unsigned short sc_castcancel; // [Skotlex] - unsigned short pc_sc_def_rate; // [Skotlex] - unsigned short mob_sc_def_rate; - unsigned short pc_luk_sc_def; - unsigned short mob_luk_sc_def; - unsigned short pc_max_sc_def; - unsigned short mob_max_sc_def; - - unsigned short sg_angel_skill_ratio; - unsigned short sg_miracle_skill_ratio; - int sg_miracle_skill_duration_min; - int sg_miracle_skill_duration_max; - unsigned short autospell_stacking; //Enables autospell cards to stack. [Skotlex] - unsigned short override_mob_names; //Enables overriding spawn mob names with the mob_db names. [Skotlex] - unsigned short min_chat_delay; //Minimum time between client messages. [Skotlex] - unsigned short friend_auto_add; //When accepting friends, both get friended. [Skotlex] - unsigned int hvan_explosion_intimate ; // fix [albator] - unsigned short homunculus_show_growth ; //[orn] - unsigned short homunculus_friendly_rate; -} battle_config; - -void do_init_battle(void); -void do_final_battle(void); -extern int battle_config_read(const char *cfgName); -extern void battle_validate_conf(void); -extern void battle_set_defaults(void); -extern int battle_set_value(char *, char *); -int battle_get_value(char *); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _BATTLE_H_ +#define _BATTLE_H_ + +// ダメージ +struct Damage { + int damage,damage2; + int type,div_; + int amotion,dmotion; + int blewcount; + int flag; + int dmg_lv; //ATK_LUCKY,ATK_FLEE,ATK_DEF +}; + +// 属性表(読み込みはpc.c、battle_attr_fixで使用) +extern int attr_fix_table[4][10][10]; + +struct map_session_data; +struct mob_data; +struct block_list; + +// ダメージ計算 + +struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag); + +int battle_calc_return_damage(struct block_list *bl, int skill, int *damage, int flag); + +void battle_drain(struct map_session_data *sd, struct block_list *tbl, int rdamage, int ldamage, int race, int boss); + +int battle_attr_fix(struct block_list *src, struct block_list *target, int damage,int atk_elem,int def_type, int def_lv); + +// ダメージ最終計算 +int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag); +int battle_calc_gvg_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag); + +enum { // 最終計算のフラグ + BF_WEAPON = 0x0001, + BF_MAGIC = 0x0002, + BF_MISC = 0x0004, + BF_SHORT = 0x0010, + BF_LONG = 0x0040, + BF_SKILL = 0x0100, + BF_NORMAL = 0x0200, + BF_WEAPONMASK=0x000f, + BF_RANGEMASK= 0x00f0, + BF_SKILLMASK= 0x0f00, +}; + +int battle_delay_damage (unsigned int tick, struct block_list *src, struct block_list *target, int attack_type, int skill_id, int skill_lv, int damage, int dmg_lv, int ddelay); + +// 通常攻撃処理まとめ +int battle_weapon_attack( struct block_list *bl,struct block_list *target, + unsigned int tick,int flag); + +// 各種パラメータを得る +struct block_list* battle_get_master(struct block_list *src); +struct block_list* battle_gettargeted(struct block_list *target); +int battle_gettarget(struct block_list *bl); +int battle_getcurrentskill(struct block_list *bl); + +//New definitions [Skotlex] +#define BCT_ENEMY 0x020000 +//This should be (~BCT_ENEMY&BCT_ALL) +#define BCT_NOENEMY 0x1d0000 +#define BCT_PARTY 0x040000 +//This should be (~BCT_PARTY&BCT_ALL) +#define BCT_NOPARTY 0x1b0000 +#define BCT_GUILD 0x080000 +//This should be (~BCT_GUILD&BCT_ALL) +#define BCT_NOGUILD 0x170000 +#define BCT_ALL 0x1f0000 +#define BCT_NOONE 0x000000 +#define BCT_SELF 0x010000 +#define BCT_NEUTRAL 0x100000 + +#define is_boss(bl) (status_get_mode(bl)&MD_BOSS) // Can refine later [Aru] + +int battle_check_undead(int race,int element); +int battle_check_target(struct block_list *src, struct block_list *target,int flag); +int battle_check_range(struct block_list *src,struct block_list *bl,int range); + +void battle_consume_ammo(struct map_session_data* sd, int skill, int lv); +// 設定 + +int battle_config_switch(const char *str); // [Valaris] + +extern struct Battle_Config { + unsigned short warp_point_debug; + unsigned short enable_critical; + unsigned short mob_critical_rate; + unsigned short critical_rate; + unsigned short enable_baseatk; + unsigned short enable_perfect_flee; + unsigned short cast_rate,delay_rate,delay_dependon_agi; + unsigned short sdelay_attack_enable; + unsigned short left_cardfix_to_right; + unsigned short skill_add_range; + unsigned short skill_out_range_consume; + unsigned short skillrange_by_distance; //[Skotlex] + unsigned short use_weapon_skill_range; //[Skotlex] + unsigned short pc_damage_delay_rate; + unsigned short defnotenemy; + unsigned short vs_traps_bctall; + unsigned short traps_setting; + unsigned short summon_flora; //[Skotlex] + unsigned short clear_unit_ondeath; //[Skotlex] + unsigned short clear_unit_onwarp; //[Skotlex] + unsigned short random_monster_checklv; + unsigned short attr_recover; + unsigned short flooritem_lifetime; + unsigned short item_auto_get; + int item_first_get_time; + int item_second_get_time; + int item_third_get_time; + int mvp_item_first_get_time; + int mvp_item_second_get_time; + int mvp_item_third_get_time; + int base_exp_rate,job_exp_rate; + unsigned short drop_rate0item; + unsigned short death_penalty_type; + unsigned short death_penalty_base,death_penalty_job; + unsigned short pvp_exp; // [MouseJstr] + unsigned short gtb_sc_immunity; + int zeny_penalty; + unsigned short restart_hp_rate; + unsigned short restart_sp_rate; + int mvp_exp_rate; + unsigned short mvp_hp_rate; + unsigned short monster_hp_rate; + unsigned short monster_max_aspd; + unsigned short view_range_rate; + unsigned short chase_range_rate; + unsigned short atc_gmonly; + unsigned short atc_spawn_quantity_limit; + unsigned short atc_slave_clone_limit; + unsigned short partial_name_scan; + unsigned short gm_allskill; + unsigned short gm_allequip; + unsigned short gm_skilluncond; + unsigned short gm_join_chat; + unsigned short gm_kick_chat; + unsigned short skillfree; + unsigned short skillup_limit; + unsigned short wp_rate; + unsigned short pp_rate; + unsigned short monster_active_enable; + unsigned short monster_damage_delay_rate; + unsigned short monster_loot_type; + unsigned short mob_skill_rate; //[Skotlex] + unsigned short mob_skill_delay; //[Skotlex] + unsigned short mob_count_rate; + unsigned short no_spawn_on_player; //[Skotlex] + unsigned short force_random_spawn; //[Skotlex] + unsigned short mob_spawn_delay, plant_spawn_delay, boss_spawn_delay; // [Skotlex] + unsigned short slaves_inherit_mode; + unsigned short slaves_inherit_speed; + unsigned short summons_trigger_autospells; + unsigned short pc_walk_delay_rate; //Adjusts can't walk delay after being hit for players. [Skotlex] + unsigned short walk_delay_rate; //Adjusts can't walk delay after being hit. [Skotlex] + unsigned short multihit_delay; //Adjusts can't walk delay per hit on multi-hitting skills. [Skotlex] + unsigned short quest_skill_learn; + unsigned short quest_skill_reset; + unsigned short basic_skill_check; + unsigned short guild_emperium_check; + unsigned short guild_exp_limit; + unsigned short guild_max_castles; + unsigned short emergency_call; + unsigned short guild_aura; + unsigned short pc_invincible_time; + unsigned short pet_catch_rate; + unsigned short pet_rename; + unsigned short pet_friendly_rate; + unsigned short pet_hungry_delay_rate; + unsigned short pet_hungry_friendly_decrease; + unsigned short pet_status_support; + unsigned short pet_attack_support; + unsigned short pet_damage_support; + unsigned short pet_support_min_friendly; //[Skotlex] + unsigned short pet_support_rate; + unsigned short pet_attack_exp_to_master; + unsigned short pet_attack_exp_rate; + unsigned short pet_lv_rate; //[Skotlex] + unsigned short pet_max_stats; //[Skotlex] + unsigned short pet_max_atk1; //[Skotlex] + unsigned short pet_max_atk2; //[Skotlex] + unsigned short pet_no_gvg; //Disables pets in gvg. [Skotlex] + unsigned short skill_min_damage; + unsigned short finger_offensive_type; + unsigned short heal_exp; + unsigned short max_heal_lv; + int max_heal; //Mitternacht + unsigned short resurrection_exp; + unsigned short shop_exp; + unsigned short combo_delay_rate; + unsigned short item_check; + unsigned short item_use_interval; //[Skotlex] + unsigned short wedding_modifydisplay; + unsigned short wedding_ignorepalette; //[Skotlex] + unsigned short xmas_ignorepalette; // [Valaris] + int natural_healhp_interval; + int natural_healsp_interval; + int natural_heal_skill_interval; + unsigned short natural_heal_weight_rate; + unsigned short item_name_override_grffile; + unsigned short indoors_override_grffile; // [Celest] + unsigned short skill_sp_override_grffile; // [Celest] + unsigned short cardillust_read_grffile; + unsigned short item_equip_override_grffile; + unsigned short item_slots_override_grffile; + unsigned short arrow_decrement; + unsigned short max_aspd; + unsigned short max_walk_speed; //Maximum walking speed after buffs [Skotlex] + int max_hp; + int max_sp; + unsigned short max_lv, aura_lv; + unsigned short max_parameter, max_baby_parameter; + int max_cart_weight; + unsigned short skill_log; + unsigned short battle_log; + unsigned short save_log; + unsigned short error_log; + unsigned short etc_log; + unsigned short save_clothcolor; + unsigned short undead_detect_type; + unsigned short auto_counter_type; + unsigned short min_hitrate; //[Skotlex] + unsigned short max_hitrate; //[Skotlex] + unsigned short agi_penalty_target; + unsigned short agi_penalty_type; + unsigned short agi_penalty_count; + unsigned short agi_penalty_num; + unsigned short vit_penalty_target; + unsigned short vit_penalty_type; + unsigned short vit_penalty_count; + unsigned short vit_penalty_num; + unsigned short weapon_defense_type; + unsigned short magic_defense_type; + unsigned short skill_reiteration; + unsigned short skill_nofootset; + unsigned short pc_cloak_check_type; + unsigned short monster_cloak_check_type; + unsigned short estimation_type; + unsigned short gvg_short_damage_rate; + unsigned short gvg_long_damage_rate; + unsigned short gvg_weapon_damage_rate; + unsigned short gvg_magic_damage_rate; + unsigned short gvg_misc_damage_rate; + unsigned short gvg_flee_penalty; + int gvg_eliminate_time; + unsigned short pk_short_damage_rate; + unsigned short pk_long_damage_rate; + unsigned short pk_weapon_damage_rate; + unsigned short pk_magic_damage_rate; + unsigned short pk_misc_damage_rate; + unsigned short mob_changetarget_byskill; + unsigned short attack_direction_change; + unsigned short land_skill_limit; + unsigned short party_skill_penalty; + unsigned short monster_class_change_full_recover; + unsigned short produce_item_name_input; + unsigned short produce_potion_name_input; + unsigned short making_arrow_name_input; + unsigned short holywater_name_input; + unsigned short cdp_name_input; + unsigned short display_skill_fail; + unsigned short chat_warpportal; + unsigned short mob_warp; + unsigned short dead_branch_active; + unsigned int vending_max_value; + unsigned short show_steal_in_same_party; + unsigned short party_share_type; + unsigned short party_hp_mode; + unsigned short party_show_share_picker; + unsigned short attack_attr_none; + int item_rate_mvp, item_rate_common, item_rate_common_boss, item_rate_card, item_rate_card_boss, // added support for MVP drops [Reddozen] + item_rate_equip, item_rate_equip_boss, item_rate_heal, item_rate_heal_boss, item_rate_use, + item_rate_use_boss, item_rate_treasure, // Added by RoVeRT, Additional Heal and Usable item rate by Val + item_rate_adddrop; + + unsigned short logarithmic_drops; + unsigned short item_drop_common_min,item_drop_common_max; // Added by TyrNemesis^ + unsigned short item_drop_card_min,item_drop_card_max; + unsigned short item_drop_equip_min,item_drop_equip_max; + unsigned short item_drop_mvp_min,item_drop_mvp_max; // End Addition + unsigned short item_drop_heal_min,item_drop_heal_max; // Added by Valatris + unsigned short item_drop_use_min,item_drop_use_max; //End + unsigned short item_drop_treasure_min,item_drop_treasure_max; //by [Skotlex] + unsigned short item_drop_adddrop_min,item_drop_adddrop_max; //[Skotlex] + unsigned short prevent_logout; // Added by RoVeRT + + unsigned short alchemist_summon_reward; // [Valaris] + unsigned short drops_by_luk; + unsigned short drops_by_luk2; + unsigned short equip_natural_break_rate; //Base Natural break rate for attacks. + unsigned short equip_self_break_rate; //Natural & Penalty skills break rate + unsigned short equip_skill_break_rate; //Offensive skills break rate + unsigned short pet_equip_required; + unsigned short multi_level_up; + unsigned short max_exp_gain_rate; //Max amount of exp bar % you can get in one go. + unsigned short pk_mode; + unsigned short pk_level_range; + + unsigned short manner_system; // end additions [Valaris] + unsigned short show_mob_info; + + unsigned short agi_penalty_count_lv; + unsigned short vit_penalty_count_lv; + + unsigned short gx_allhit; + unsigned short gx_disptype; + unsigned short devotion_level_difference; + unsigned short player_skill_partner_check; + unsigned short hide_GM_session; + unsigned short invite_request_check; + unsigned short skill_removetrap_type; + unsigned short disp_experience; + unsigned short disp_zeny; + unsigned short castle_defense_rate; + unsigned short backstab_bow_penalty; + unsigned short hp_rate; + unsigned short sp_rate; + unsigned short gm_cant_drop_min_lv; + unsigned short gm_cant_drop_max_lv; + unsigned short disp_hpmeter; + unsigned short bone_drop; + unsigned short buyer_name; + +// eAthena additions + unsigned short night_at_start; // added by [Yor] + int day_duration; // added by [Yor] + int night_duration; // added by [Yor] + short ban_hack_trade; // added by [Yor] + unsigned short hack_info_GM_level; // added by [Yor] + unsigned short any_warp_GM_min_level; // added by [Yor] + unsigned short packet_ver_flag; // added by [Yor] + + unsigned short min_hair_style; // added by [MouseJstr] + unsigned short max_hair_style; // added by [MouseJstr] + unsigned short min_hair_color; // added by [MouseJstr] + unsigned short max_hair_color; // added by [MouseJstr] + unsigned short min_cloth_color; // added by [MouseJstr] + unsigned short max_cloth_color; // added by [MouseJstr] + unsigned short pet_hair_style; // added by [Skotlex] + + unsigned short castrate_dex_scale; // added by [MouseJstr] + unsigned short area_size; // added by [MouseJstr] + + unsigned short max_def, over_def_bonus; //added by [Skotlex] + + unsigned short zeny_from_mobs; // [Valaris] + unsigned short mobs_level_up; // [Valaris] + unsigned short mobs_level_up_exp_rate; // [Valaris] + unsigned short pk_min_level; // [celest] + unsigned short skill_steal_type; // [celest] + unsigned short skill_steal_rate; // [celest] + unsigned short skill_steal_max_tries; //max steal skill tries on a mob. if 0, then w/o limit [Lupus] + unsigned short motd_type; // [celest] + unsigned short finding_ore_rate; // orn + unsigned short exp_calc_type; + unsigned short exp_bonus_attacker; + unsigned short exp_bonus_max_attacker; + unsigned short min_skill_delay_limit; + unsigned short default_skill_delay; + unsigned short no_skill_delay; + unsigned short attack_walk_delay; + unsigned short require_glory_guild; + unsigned short idle_no_share; + unsigned short party_update_interval; + unsigned short party_even_share_bonus; + unsigned short delay_battle_damage; + unsigned short hide_woe_damage; + unsigned short display_version; + unsigned short who_display_aid; + + unsigned short display_hallucination; // [Skotlex] + unsigned short use_statpoint_table; // [Skotlex] + + unsigned short ignore_items_gender; //[Lupus] + + unsigned short copyskill_restrict; // [Aru] + unsigned short berserk_cancels_buffs; // [Aru] + unsigned short debuff_on_logout; // Removes a few "official" negative Scs on logout. [Skotlex] + unsigned short mob_ai; //Configures various mob_ai settings to make them smarter or dumber(official). [Skotlex] + unsigned short dynamic_mobs; // Dynamic Mobs [Wizputer] - battle_athena flag implemented by [random] + unsigned short mob_remove_damaged; // Dynamic Mobs - Remove mobs even if damaged [Wizputer] + int mob_remove_delay; // Dynamic Mobs - delay before removing mobs from a map [Skotlex] + + unsigned short show_hp_sp_drain, show_hp_sp_gain; //[Skotlex] + + unsigned short mob_npc_event_type; //Determines on who the npc_event is executed. [Skotlex] + unsigned short mob_clear_delay; // [Valaris] + + unsigned short character_size; // if riders have size=2, and baby class riders size=1 [Lupus] + unsigned short mob_max_skilllvl; // Max possible skill level [Lupus] + unsigned short rare_drop_announce; // chance <= to show rare drops global announces + + unsigned short retaliate_to_master; //Whether when a mob is attacked by another mob, it will retaliate versus the mob or the mob's master. [Skotlex] + unsigned short firewall_hits_on_undead; //Number of hits firewall does at a time on undead. [Skotlex] + + unsigned short title_lvl1; // Players titles [Lupus] + unsigned short title_lvl2; // Players titles [Lupus] + unsigned short title_lvl3; // Players titles [Lupus] + unsigned short title_lvl4; // Players titles [Lupus] + unsigned short title_lvl5; // Players titles [Lupus] + unsigned short title_lvl6; // Players titles [Lupus] + unsigned short title_lvl7; // Players titles [Lupus] + unsigned short title_lvl8; // Players titles [Lupus] + + unsigned short duel_enable; // [LuzZza] + unsigned short duel_allow_pvp; // [LuzZza] + unsigned short duel_allow_gvg; // [LuzZza] + unsigned short duel_allow_teleport; // [LuzZza] + unsigned short duel_autoleave_when_die; // [LuzZza] + unsigned short duel_time_interval; // [LuzZza] + unsigned short duel_only_on_same_map; // [Toms] + + unsigned short skip_teleport_lv1_menu; // possibility to disable (skip) Teleport Lv1 menu, that have only two lines `Random` and `Cancel` [LuzZza] + + unsigned short allow_skill_without_day; // [Komurka] + unsigned short allow_es_magic_pc; // [Skotlex] + unsigned short skill_wall_check; // [Skotlex] + unsigned short cell_stack_limit; // [Skotlex] + unsigned short skill_caster_check; // [Skotlex] + unsigned short sc_castcancel; // [Skotlex] + unsigned short pc_sc_def_rate; // [Skotlex] + unsigned short mob_sc_def_rate; + unsigned short pc_luk_sc_def; + unsigned short mob_luk_sc_def; + unsigned short pc_max_sc_def; + unsigned short mob_max_sc_def; + + unsigned short sg_angel_skill_ratio; + unsigned short sg_miracle_skill_ratio; + int sg_miracle_skill_duration_min; + int sg_miracle_skill_duration_max; + unsigned short autospell_stacking; //Enables autospell cards to stack. [Skotlex] + unsigned short override_mob_names; //Enables overriding spawn mob names with the mob_db names. [Skotlex] + unsigned short min_chat_delay; //Minimum time between client messages. [Skotlex] + unsigned short friend_auto_add; //When accepting friends, both get friended. [Skotlex] + unsigned int hvan_explosion_intimate ; // fix [albator] + unsigned short homunculus_show_growth ; //[orn] + unsigned short homunculus_friendly_rate; +} battle_config; + +void do_init_battle(void); +void do_final_battle(void); +extern int battle_config_read(const char *cfgName); +extern void battle_validate_conf(void); +extern void battle_set_defaults(void); +extern int battle_set_value(char *, char *); +int battle_get_value(char *); + +#endif diff --git a/src/map/charcommand.c b/src/map/charcommand.c index 0e796fba6..32290d8fd 100644 --- a/src/map/charcommand.c +++ b/src/map/charcommand.c @@ -1,1846 +1,1846 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include -#include -#include - -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/showmsg.h" - -#include "log.h" -#include "clif.h" -#include "chrif.h" -#include "intif.h" -#include "itemdb.h" -#include "map.h" -#include "pc.h" -#include "status.h" -#include "skill.h" -#include "mob.h" -#include "pet.h" -#include "battle.h" -#include "charcommand.h" -#include "atcommand.h" - -static char command_symbol = '#'; - -extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others) - -#define CCMD_FUNC(x) int charcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message) -CCMD_FUNC(jobchange); -CCMD_FUNC(petrename); -CCMD_FUNC(petfriendly); -CCMD_FUNC(stats); -CCMD_FUNC(option); -CCMD_FUNC(save); -CCMD_FUNC(stats_all); -CCMD_FUNC(reset); -CCMD_FUNC(spiritball); -CCMD_FUNC(itemlist); -CCMD_FUNC(effect); -CCMD_FUNC(storagelist); -CCMD_FUNC(item); -CCMD_FUNC(warp); -CCMD_FUNC(zeny); -CCMD_FUNC(fakename); -CCMD_FUNC(baselevel); -CCMD_FUNC(joblevel); -CCMD_FUNC(questskill); -CCMD_FUNC(lostskill); -CCMD_FUNC(skreset); -CCMD_FUNC(streset); -CCMD_FUNC(model); -CCMD_FUNC(stpoint); -CCMD_FUNC(skpoint); -CCMD_FUNC(changesex); -CCMD_FUNC(feelreset); -CCMD_FUNC(help); - - -/*========================================== - *CharCommandInfo charcommand_info[]構造体の定義 - *------------------------------------------ - */ - -// First char of commands is configured in charcommand_athena.conf. Leave # in this list for default value. -// to set default level, read charcommand_athena.conf first please. -static CharCommandInfo charcommand_info[] = { - { CharCommandJobChange, "#job", 60, charcommand_jobchange }, - { CharCommandJobChange, "#jobchange", 60, charcommand_jobchange }, - { CharCommandPetRename, "#petrename", 50, charcommand_petrename }, - { CharCommandPetFriendly, "#petfriendly", 50, charcommand_petfriendly }, - { CharCommandStats, "#stats", 40, charcommand_stats }, - { CharCommandOption, "#option", 60, charcommand_option }, - { CharCommandReset, "#reset", 60, charcommand_reset }, - { CharCommandSave, "#save", 60, charcommand_save }, - { CharCommandStatsAll, "#statsall", 40, charcommand_stats_all }, - { CharCommandSpiritball, "#spiritball", 40, charcommand_spiritball }, - { CharCommandItemList, "#itemlist", 40, charcommand_itemlist }, - { CharCommandEffect, "#effect", 40, charcommand_effect }, - { CharCommandStorageList, "#storagelist", 40, charcommand_storagelist }, - { CharCommandItem, "#item", 60, charcommand_item }, - { CharCommandWarp, "#warp", 60, charcommand_warp }, - { CharCommandWarp, "#rura", 60, charcommand_warp }, - { CharCommandWarp, "#rura+", 60, charcommand_warp }, - { CharCommandZeny, "#zeny", 60, charcommand_zeny }, - { CharCommandFakeName, "#fakename", 20, charcommand_fakename}, - - //*********************************Recently added commands********************************************* - { CharCommandBaseLevel, "#baselvl", 20, charcommand_baselevel}, - { CharCommandBaseLevel, "#blvl", 60, charcommand_baselevel}, - { CharCommandBaseLevel, "#baselvlup", 60, charcommand_baselevel}, - { CharCommandJobLevel, "#joblvl", 60, charcommand_joblevel}, - { CharCommandJobLevel, "#jlvl", 60, charcommand_joblevel}, - { CharCommandJobLevel, "#joblvlup", 60, charcommand_joblevel}, - { CharCommandQuestSkill, "#questskill", 60, charcommand_questskill }, - { CharCommandLostSkill, "#lostskill", 60, charcommand_lostskill }, - { CharCommandSkReset, "#skreset", 60, charcommand_skreset }, - { CharCommandStReset, "#streset", 60, charcommand_streset }, - { CharCommandModel, "#model", 50, charcommand_model }, - { CharCommandSKPoint, "#skpoint", 60, charcommand_skpoint }, - { CharCommandSTPoint, "#stpoint", 60, charcommand_stpoint }, - { CharCommandChangeSex, "#changesex", 60, charcommand_changesex }, - { CharCommandFeelReset, "#feelreset", 60, charcommand_feelreset }, - { CharCommandHelp, "#help", 20, charcommand_help }, -// add new commands before this line - { CharCommand_Unknown, NULL, 1, NULL } -}; - -int get_charcommand_level(const CharCommandType type) { - int i; - - for (i = 0; charcommand_info[i].type != CharCommand_None; i++) - if (charcommand_info[i].type == type) - return charcommand_info[i].level; - - return 100; // 100: command can not be used -} - -CharCommandType -charcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl) { - CharCommandInfo info; - CharCommandType type; - - malloc_set(&info, 0, sizeof(info)); - - type = charcommand(sd, gmlvl, str, &info); - if (type != CharCommand_None) { - char command[100]; - char output[200]; - const char* p = str; - - if (map[sd->bl.m].nocommand && - gmlvl < map[sd->bl.m].nocommand) - { //Command not allowed on this map. - sprintf(output, msg_txt(143)); - clif_displaymessage(fd, output); - return AtCommand_None; - } - - malloc_tsetdword(command, '\0', sizeof(command)); - malloc_tsetdword(output, '\0', sizeof(output)); - while (*p && !isspace(*p)) - p++; - if (p - str >= sizeof(command)) // too long - return CharCommand_Unknown; - strncpy(command, str, p - str); - while (isspace(*p)) - p++; - - if (type == CharCommand_Unknown || info.proc == NULL) { - snprintf(output, sizeof(output),msg_txt(153), command); // %s is Unknown Command. - clif_displaymessage(fd, output); - } else { - if (info.proc(fd, sd, command, p) != 0) { - // Command can not be executed - snprintf(output, sizeof(output), msg_txt(154), command); // %s failed. - clif_displaymessage(fd, output); - } - } - - return info.type; - } - - return CharCommand_None; -} - -/*========================================== - *is_charcommand @コマンドに存在するかどうか確認する - *------------------------------------------ - */ -CharCommandType -is_charcommand(const int fd, struct map_session_data* sd, const char* message) { - const char* str = message; - int s_flag = 0; - - nullpo_retr(CharCommand_None, sd); - - if (!message || !*message) - return CharCommand_None; - - str += strlen(sd->status.name); - while (*str && (isspace(*str) || (s_flag == 0 && *str == ':'))) { - if (*str == ':') - s_flag = 1; - str++; - } - - if (!*str) - return CharCommand_None; - - return charcommand_sub(fd,sd,str,pc_isGM(sd)); -} - -/*========================================== - * - *------------------------------------------ - */ -CharCommandType charcommand(struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info) { - char* p = (char *)message; - - if (!info) - return CharCommand_None; - if (battle_config.atc_gmonly != 0 && !level) // level = pc_isGM(sd) - return CharCommand_None; - if (!p || !*p) { - ShowError("char command message is empty\n"); - return CharCommand_None; - } - - if(p[0] == '|') - p += 3; - - if (*p == command_symbol) { // check first char, try to skip |00 (or something else) [Lance] - char command[101]; - int i = 0; - malloc_set(info, 0, sizeof(CharCommandInfo)); - sscanf(p, "%100s", command); - command[sizeof(command)-1] = '\0'; - - while (charcommand_info[i].type != CharCommand_Unknown) { - if (strcmpi(command+1, charcommand_info[i].command+1) == 0 && level >= charcommand_info[i].level) { - p[0] = charcommand_info[i].command[0]; // set correct first symbol for after. - break; - } - i++; - } - - if (charcommand_info[i].type == CharCommand_Unknown) { - // doesn't return Unknown if player is normal player (display the text, not display: unknown command) - if (level == 0) - return CharCommand_None; - else - return CharCommand_Unknown; - } else if((log_config.gm) && (charcommand_info[i].level >= log_config.gm)) { - log_atcommand(sd, message); - } - memcpy(info, &charcommand_info[i], sizeof charcommand_info[i]); - } else { - return CharCommand_None; - } - - return info->type; -} - - -/*========================================== - * - *------------------------------------------ - */ -static CharCommandInfo* get_charcommandinfo_byname(const char* name) { - int i; - - for (i = 0; charcommand_info[i].type != CharCommand_Unknown; i++) - if (strcmpi(charcommand_info[i].command + 1, name) == 0) - return &charcommand_info[i]; - - return NULL; -} - -/*========================================== - * - *------------------------------------------ - */ -int charcommand_config_read(const char *cfgName) { - char line[1024], w1[1024], w2[1024]; - CharCommandInfo* p; - FILE* fp; - - if ((fp = fopen(cfgName, "r")) == NULL) { - ShowError("CharCommands configuration file not found: %s\n", cfgName); - return 1; - } - - while (fgets(line, sizeof(line)-1, fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - - if (sscanf(line, "%1023[^:]:%1023s", w1, w2) != 2) - continue; - p = get_charcommandinfo_byname(w1); - if (p != NULL) { - p->level = atoi(w2); - if (p->level > 100) - p->level = 100; - else if (p->level < 0) - p->level = 0; - } - - if (strcmpi(w1, "import") == 0) - charcommand_config_read(w2); - else if (strcmpi(w1, "command_symbol") == 0 && w2[0] > 31 && - w2[0] != '/' && // symbol of standard ragnarok GM commands - w2[0] != '%' && // symbol of party chat speaking - w2[0] != '$' && // symbol of guild chat speaking - w2[0] != '@') // symbol of atcommand - command_symbol = w2[0]; - } - fclose(fp); - - return 0; -} - -/*========================================== - * 対象キャラクターを転職させる upper指定で転生や養子も可能 - *------------------------------------------ - */ -int charcommand_jobchange( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - char character[100]; - struct map_session_data* pl_sd; - int job = 0, upper = -1; - - malloc_tsetdword(character, '\0', sizeof(character)); - - if (!message || !*message) { - clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange )."); - return -1; - } - - if (sscanf(message, "%d %d %99[^\n]", &job, &upper, character) < 3) { //upper指定してある - upper = -1; - if (sscanf(message, "%d %99[^\n]", &job, character) < 2) { //upper指定してない上に何か足りない - clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange )."); - return -1; - } - } - - if ((pl_sd = map_nick2sd(character)) != NULL) { - int j; - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job only to lower or same level - if ((job >= 0 && job < MAX_PC_CLASS)) { - for (j=0; j < MAX_INVENTORY; j++) { - if(pl_sd->status.inventory[j].nameid>0 && pl_sd->status.inventory[j].equip!=0) - pc_unequipitem(pl_sd, j, 3); - } - if (pc_jobchange(pl_sd, job, upper) == 0) - clif_displaymessage(fd, msg_table[48]); // Character's job changed. - else { - clif_displaymessage(fd, msg_table[192]); // Impossible to change the character's job. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[49]); // Invalid job ID. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int charcommand_petrename( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - char character[NAME_LENGTH]; - struct map_session_data *pl_sd; - struct pet_data *pd; - - malloc_tsetdword(character, '\0', sizeof(character)); - - if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { - clif_displaymessage(fd, "Please, enter a player name (usage: #petrename )."); - return -1; - } - - if ((pl_sd = map_nick2sd(character)) == NULL) { - clif_displaymessage(fd, msg_txt(3)); // Character not found. - return -1; - } - - if (!pl_sd->status.pet_id || !pl_sd->pd) { - clif_displaymessage(fd, msg_table[191]); // Sorry, but this player has no pet. - return -1; - } - - pd = pl_sd->pd; - - if (pd->pet.rename_flag) { - clif_displaymessage(fd, msg_table[190]); // This player can already rename his/her pet. - return -1; - } - pd->pet.rename_flag = 0; - intif_save_petdata(pl_sd->status.account_id, &pd->pet); - clif_send_petstatus(pl_sd); - clif_displaymessage(fd, msg_table[189]); // This player can now rename his/her pet. - return 0; -} - - -/*========================================== - * - *------------------------------------------ - */ -int charcommand_petfriendly( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - int friendly = 0; - char character[NAME_LENGTH]; - struct map_session_data *pl_sd; - struct pet_data *pd; - - malloc_tsetdword(character, '\0', sizeof(character)); - if (!message || !*message || sscanf(message,"%d %23s",&friendly,character) < 2) { - clif_displaymessage(fd, "Please, enter a valid value (usage: " - "#petfriendly <0-1000> )."); - return -1; - } - - if (((pl_sd = map_nick2sd(character)) == NULL) || - pc_isGM(sd)status.pet_id || !pl_sd->pd) { - clif_displaymessage(fd, msg_table[191]); // Sorry, but this player has no pet. - return -1; - } - - if (friendly < 0 || friendly > 1000) { - clif_displaymessage(fd, msg_table[37]); // An invalid number was specified. - return -1; - } - - pd = pl_sd->pd; - if (friendly == pd->pet.intimate) { - clif_displaymessage(fd, msg_table[183]); // Pet friendly is already the good value. - return -1; - } - - pd->pet.intimate = friendly; - clif_send_petstatus(pl_sd); - clif_pet_emotion(pd,0); - clif_displaymessage(pl_sd->fd, msg_table[182]); // Pet friendly value changed! - clif_displaymessage(sd->fd, msg_table[182]); // Pet friendly value changed! - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int charcommand_stats( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - char character[NAME_LENGTH]; - char job_jobname[100]; - char output[200]; - struct map_session_data *pl_sd; - int i; - - malloc_tsetdword(character, '\0', sizeof(character)); - malloc_tsetdword(job_jobname, '\0', sizeof(job_jobname)); - malloc_tsetdword(output, '\0', sizeof(output)); - - if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { - clif_displaymessage(fd, "Please, enter a player name (usage: #stats )."); - return -1; - } - - if ((pl_sd = map_nick2sd(character)) != NULL) { - struct { - const char* format; - int value; - } output_table[] = { - { "Base Level - %d", 0 }, - { NULL, 0 }, - { "Hp - %d", 0 }, - { "MaxHp - %d", 0 }, - { "Sp - %d", 0 }, - { "MaxSp - %d", 0 }, - { "Str - %3d", 0 }, - { "Agi - %3d", 0 }, - { "Vit - %3d", 0 }, - { "Int - %3d", 0 }, - { "Dex - %3d", 0 }, - { "Luk - %3d", 0 }, - { "Zeny - %d", 0 }, - { NULL, 0 } - }; - //direct array initialization with variables is not standard C compliant. - output_table[0].value = pl_sd->status.base_level; - output_table[1].format = job_jobname; - output_table[1].value = pl_sd->status.job_level; - output_table[2].value = pl_sd->status.hp; - output_table[3].value = pl_sd->status.max_hp; - output_table[4].value = pl_sd->status.sp; - output_table[5].value = pl_sd->status.max_sp; - output_table[6].value = pl_sd->status.str; - output_table[7].value = pl_sd->status.agi; - output_table[8].value = pl_sd->status.vit; - output_table[9].value = pl_sd->status.int_; - output_table[10].value = pl_sd->status.dex; - output_table[11].value = pl_sd->status.luk; - output_table[12].value = pl_sd->status.zeny; - sprintf(job_jobname, "Job - %s %s", job_name(pl_sd->status.class_), "(level %d)"); - sprintf(output, msg_table[53], pl_sd->status.name); // '%s' stats: - clif_displaymessage(fd, output); - for (i = 0; output_table[i].format != NULL; i++) { - sprintf(output, output_table[i].format, output_table[i].value); - clif_displaymessage(fd, output); - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * Character Reset - *------------------------------------------ - */ -int charcommand_reset( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - char character[NAME_LENGTH]; - char output[200]; - struct map_session_data *pl_sd; - - malloc_tsetdword(character, '\0', sizeof(character)); - malloc_tsetdword(output, '\0', sizeof(output)); - - if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { - clif_displaymessage(fd, "Please, enter a player name (usage: #reset )."); - return -1; - } - - if ((pl_sd = map_nick2sd(character)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level - pc_resetstate(pl_sd); - pc_resetskill(pl_sd,1); - sprintf(output, msg_table[208], character); // '%s' skill and stats points reseted! - clif_displaymessage(fd, output); - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int charcommand_option( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - char character[NAME_LENGTH]; - int opt1 = 0, opt2 = 0, opt3 = 0; - struct map_session_data* pl_sd; - - malloc_tsetdword(character, '\0', sizeof(character)); - - if (!message || !*message || - sscanf(message, "%d %d %d %23[^\n]", &opt1, &opt2, &opt3, character) < 4 || - opt1 < 0 || opt2 < 0 || opt3 < 0) { - clif_displaymessage(fd, "Please, enter valid options and a player name (usage: #option )."); - return -1; - } - - if ((pl_sd = map_nick2sd(character)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change option only to lower or same level - pl_sd->sc.opt1 = opt1; - pl_sd->sc.opt2 = opt2; - pc_setoption(pl_sd, opt3); - clif_displaymessage(fd, msg_table[58]); // Character's options changed. - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int charcommand_save( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - char map_name[MAP_NAME_LENGTH]; - char character[NAME_LENGTH]; - struct map_session_data* pl_sd; - int x = 0, y = 0; - int m; - - malloc_tsetdword(map_name, '\0', sizeof(map_name)); - malloc_tsetdword(character, '\0', sizeof(character)); - - if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4 || x < 0 || y < 0) { - clif_displaymessage(fd, "Please, enter a valid save point and a player name (usage: #save )."); - return -1; - } - - if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat) - strcat(map_name, ".gat"); - - if ((pl_sd = map_nick2sd(character)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change save point only to lower or same gm level - m = map_mapname2mapid(map_name); - if (m < 0 && !mapindex_name2id(map_name)) { - clif_displaymessage(fd, msg_table[1]); // Map not found. - return -1; - } else { - //FIXME: What do you do if the map is in another map server with the nowarpto flag? - if (m>=0 && map[m].flag.nosave && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { - clif_displaymessage(fd, "You are not authorised to set this map as a save map."); - return -1; - } - if (m>=0) - pc_setsavepoint(pl_sd, map[m].index, x, y); - else - pc_setsavepoint(pl_sd, mapindex_name2id(map_name), x, y); - clif_displaymessage(fd, msg_table[57]); // Character's respawn point changed. - } - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -//** Character Stats All by fritz -int charcommand_stats_all(const int fd, struct map_session_data* sd, const char* command, const char* message) -{ - char output[1024], gmlevel[1024]; - int i; - int count, users; - struct map_session_data *pl_sd, **pl_allsd; - - malloc_tsetdword(output, '\0', sizeof(output)); - malloc_tsetdword(gmlevel, '\0', sizeof(gmlevel)); - - count = 0; - pl_allsd = map_getallusers(&users); - for(i = 0; i < users; i++) { - if ((pl_sd = pl_allsd[i])) - { - if (pc_isGM(pl_sd) > 0) - sprintf(gmlevel, "| GM Lvl: %d", pc_isGM(pl_sd)); - else - sprintf(gmlevel, " "); - - sprintf(output, "Name: %s | BLvl: %d | Job: %s (Lvl: %d) | HP: %d/%d | SP: %d/%d", pl_sd->status.name, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level, pl_sd->status.hp, pl_sd->status.max_hp, pl_sd->status.sp, pl_sd->status.max_sp); - clif_displaymessage(fd, output); - sprintf(output, "STR: %d | AGI: %d | VIT: %d | INT: %d | DEX: %d | LUK: %d | Zeny: %d %s", pl_sd->status.str, pl_sd->status.agi, pl_sd->status.vit, pl_sd->status.int_, pl_sd->status.dex, pl_sd->status.luk, pl_sd->status.zeny, gmlevel); - clif_displaymessage(fd, output); - clif_displaymessage(fd, "--------"); - count++; - } - } - - if (count == 0) - clif_displaymessage(fd, msg_table[28]); // No player found. - else if (count == 1) - clif_displaymessage(fd, msg_table[29]); // 1 player found. - else { - sprintf(output, msg_table[30], count); // %d players found. - clif_displaymessage(fd, output); - } - - return 0; -} - -/*========================================== - * CharSpiritBall Function by PalasX - *------------------------------------------ - */ -int charcommand_spiritball(const int fd, struct map_session_data* sd,const char* command, const char* message) -{ - struct map_session_data *pl_sd; - char character[NAME_LENGTH]; - int spirit = 0; - - malloc_tsetdword(character, '\0', sizeof(character)); - - if(!message || !*message || sscanf(message, "%d %23[^\n]", &spirit, character) < 2 || spirit < 0 || spirit > 1000) { - clif_displaymessage(fd, "Usage: @spiritball ) ."); - return -1; - } - - if((pl_sd = map_nick2sd(character)) != NULL) { - if (spirit >= 0 && spirit <= 0x7FFF) { - if (pl_sd->spiritball != spirit || spirit > 999) { - if (pl_sd->spiritball > 0) - pc_delspiritball(pl_sd, pl_sd->spiritball, 1); - pl_sd->spiritball = spirit; - clif_spiritball(pl_sd); - // no message, player can look the difference - if (spirit > 1000) - clif_displaymessage(fd, msg_table[204]); // WARNING: more than 1000 spiritballs can CRASH your server and/or client! - } else { - clif_displaymessage(fd, msg_table[205]); // You already have this number of spiritballs. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[37]); // An invalid number was specified. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - return 0; -} - -/*========================================== - * #itemlist : Displays the list of a player's items. - *------------------------------------------ - */ -int -charcommand_itemlist( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd; - struct item_data *item_data, *item_temp; - int i, j, equip, count, counter, counter2; - char character[NAME_LENGTH], output[200], equipstr[100], outputtmp[200]; - struct item *i_item; //Current inventory item. - nullpo_retr(-1, sd); - - malloc_tsetdword(character, '\0', sizeof(character)); - malloc_tsetdword(output, '\0', sizeof(output)); - malloc_tsetdword(equipstr, '\0', sizeof(equipstr)); - malloc_tsetdword(outputtmp, '\0', sizeof(outputtmp)); - - if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { - clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist )."); - return -1; - } - - if ((pl_sd = map_nick2sd(character)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level - counter = 0; - count = 0; - for (i = 0; i < MAX_INVENTORY; i++) { - i_item = &pl_sd->status.inventory[i]; - if (pl_sd->status.inventory[i].nameid > 0 && (item_data = itemdb_exists(i_item->nameid)) != NULL) { - counter = counter + i_item->amount; - count++; - if (count == 1) { - sprintf(output, "------ Items list of '%s' ------", pl_sd->status.name); - clif_displaymessage(fd, output); - } - if ((equip = i_item->equip)) { - strcpy(equipstr, "| equiped: "); - if (equip & EQP_GARMENT) - strcat(equipstr, "robe/gargment, "); - if (equip & EQP_ACC_L) - strcat(equipstr, "left accessory, "); - if (equip & EQP_ARMOR) - strcat(equipstr, "body/armor, "); - if ((equip & EQP_ARMS) == EQP_HAND_R) - strcat(equipstr, "right hand, "); - if ((equip & EQP_ARMS) == EQP_HAND_L) - strcat(equipstr, "left hand, "); - if ((equip & EQP_ARMS) == EQP_ARMS) - strcat(equipstr, "both hands, "); - if (equip & EQP_SHOES) - strcat(equipstr, "feet, "); - if (equip & EQP_ACC_R) - strcat(equipstr, "right accessory, "); - if ((equip & EQP_HELM) == EQP_HEAD_LOW) - strcat(equipstr, "lower head, "); - if ((equip & EQP_HELM) == EQP_HEAD_TOP) - strcat(equipstr, "top head, "); - if ((equip & EQP_HELM) == (EQP_HEAD_LOW|EQP_HEAD_TOP)) - strcat(equipstr, "lower/top head, "); - if ((equip & EQP_HELM) == EQP_HEAD_MID) - strcat(equipstr, "mid head, "); - if ((equip & EQP_HELM) == (EQP_HEAD_LOW|EQP_HEAD_MID)) - strcat(equipstr, "lower/mid head, "); - if ((equip & EQP_HELM) == EQP_HELM) - strcat(equipstr, "lower/mid/top head, "); - // remove final ', ' - equipstr[strlen(equipstr) - 2] = '\0'; - } else - malloc_tsetdword(equipstr, '\0', sizeof(equipstr)); - if (i_item->refine) - sprintf(output, "%d %s %+d (%s %+d, id: %d) %s", i_item->amount, item_data->name, i_item->refine, item_data->jname, i_item->refine, i_item->nameid, equipstr); - else - sprintf(output, "%d %s (%s, id: %d) %s", i_item->amount, item_data->name, item_data->jname, i_item->nameid, equipstr); - clif_displaymessage(fd, output); - malloc_tsetdword(output, '\0', sizeof(output)); - counter2 = 0; - - if(i_item->card[0]==CARD0_PET) { //pet eggs - if (i_item->card[3]) - sprintf(outputtmp, " -> (pet egg, pet id: %u, named)", (unsigned int)MakeDWord(i_item->card[1], i_item->card[2])); - else - sprintf(outputtmp, " -> (pet egg, pet id: %u, unnamed)", (unsigned int)MakeDWord(i_item->card[1], i_item->card[2])); - strcat(output, outputtmp); - } else - if(i_item->card[0]==CARD0_FORGE) { //forged items. - sprintf(outputtmp, " -> (crafted item, creator id: %u, star crumbs %d, element %d)", (unsigned int)MakeDWord(i_item->card[2], i_item->card[3]), i_item->card[1]>>8, i_item->card[1]&0x0f); - } else - if(i_item->card[0]==CARD0_CREATE) { //created items. - sprintf(outputtmp, " -> (produced item, creator id: %u)", (unsigned int)MakeDWord(i_item->card[2], i_item->card[3])); - strcat(output, outputtmp); - } else //Normal slots - for (j = 0; j < item_data->slot; j++) { - if (pl_sd->status.inventory[i].card[j]) { - if ((item_temp = itemdb_exists(i_item->card[j])) != NULL) { - if (output[0] == '\0') - sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); - else - sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); - strcat(output, outputtmp); - } - } - } - if (output[0] != '\0') { - output[strlen(output) - 2] = ')'; - output[strlen(output) - 1] = '\0'; - clif_displaymessage(fd, output); - } - } - } - if (count == 0) - clif_displaymessage(fd, "No item found on this player."); - else { - sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count); - clif_displaymessage(fd, output); - } - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * #effect by [MouseJstr] - * - * Create a effect localized on another character - *------------------------------------------ - */ -int -charcommand_effect(const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd = NULL; - char target[255]; - int type = 0; - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%d %s", &type, target) != 2) { - clif_displaymessage(fd, "usage: #effect ."); - return -1; - } - - if((pl_sd=map_nick2sd((char *) target)) == NULL) - return -1; - - clif_specialeffect(&pl_sd->bl, type, AREA); - clif_displaymessage(fd, msg_table[229]); // Your effect has changed. - - return 0; -} - -/*========================================== - * #storagelist : Displays the items list of a player's storage. - *------------------------------------------ - */ -int -charcommand_storagelist( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct storage *stor; - struct map_session_data *pl_sd; - struct item_data *item_data, *item_temp; - int i, j, count, counter, counter2; - char character[NAME_LENGTH], output[200], outputtmp[200]; - nullpo_retr(-1, sd); - - malloc_tsetdword(character, '\0', sizeof(character)); - malloc_tsetdword(output, '\0', sizeof(output)); - malloc_tsetdword(outputtmp, '\0', sizeof(outputtmp)); - - if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { - clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist )."); - return -1; - } - - if ((pl_sd = map_nick2sd(character)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level - if((stor = account2storage2(pl_sd->status.account_id)) != NULL) { - counter = 0; - count = 0; - for (i = 0; i < MAX_STORAGE; i++) { - if (stor->storage_[i].nameid > 0 && (item_data = itemdb_search(stor->storage_[i].nameid)) != NULL) { - counter = counter + stor->storage_[i].amount; - count++; - if (count == 1) { - sprintf(output, "------ Storage items list of '%s' ------", pl_sd->status.name); - clif_displaymessage(fd, output); - } - if (stor->storage_[i].refine) - sprintf(output, "%d %s %+d (%s %+d, id: %d)", stor->storage_[i].amount, item_data->name, stor->storage_[i].refine, item_data->jname, stor->storage_[i].refine, stor->storage_[i].nameid); - else - sprintf(output, "%d %s (%s, id: %d)", stor->storage_[i].amount, item_data->name, item_data->jname, stor->storage_[i].nameid); - clif_displaymessage(fd, output); - malloc_tsetdword(output, '\0', sizeof(output)); - counter2 = 0; - for (j = 0; j < item_data->slot; j++) { - if (stor->storage_[i].card[j]) { - if ((item_temp = itemdb_search(stor->storage_[i].card[j])) != NULL) { - if (output[0] == '\0') - sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); - else - sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); - strcat(output, outputtmp); - } - } - } - if (output[0] != '\0') { - output[strlen(output) - 2] = ')'; - output[strlen(output) - 1] = '\0'; - clif_displaymessage(fd, output); - } - } - } - if (count == 0) - clif_displaymessage(fd, "No item found in the storage of this player."); - else { - sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count); - clif_displaymessage(fd, output); - } - } else { - clif_displaymessage(fd, "This player has no storage."); - return 0; - } - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -static void -charcommand_giveitem_sub(struct map_session_data *sd,struct item_data *item_data,int number) -{ - int flag = 0; - int loop = 1, get_count = number,i; - struct item item_tmp; - - if(sd && item_data){ - if (item_data->type == 4 || item_data->type == 5 || - item_data->type == 7 || item_data->type == 8) { - loop = number; - get_count = 1; - } - for (i = 0; i < loop; i++) { - malloc_set(&item_tmp, 0, sizeof(item_tmp)); - item_tmp.nameid = item_data->nameid; - item_tmp.identify = 1; - - if ((flag = pc_additem((struct map_session_data*)sd, - &item_tmp, get_count))) - clif_additem((struct map_session_data*)sd, 0, 0, flag); - } - //Logs (A)dmins items [Lupus] - if(log_config.enable_logs&0x400) - log_pick_pc(sd, "A", item_tmp.nameid, number, &item_tmp); - - } -} -/*========================================== - * #item command (usage: #item ) - * by MC Cameri - *------------------------------------------ - */ -int charcommand_item( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - char item_name[100]; - char character[NAME_LENGTH]; - struct map_session_data *pl_sd; - int number = 0, item_id, flag; - struct item item_tmp; - struct item_data *item_data; - int get_count, i, pet_id; - char tmp_cmdoutput[1024]; - nullpo_retr(-1, sd); - - malloc_tsetdword(item_name, '\0', sizeof(item_name)); - - if (!message || !*message || sscanf(message, "%99s %d %23[^\n]", item_name, &number, character) < 3) { - clif_displaymessage(fd, "Please, enter an item name/id (usage: #item )."); - return -1; - } - - if (number <= 0) - number = 1; - - item_id = 0; - if ((item_data = itemdb_searchname(item_name)) != NULL || - (item_data = itemdb_exists(atoi(item_name))) != NULL) - item_id = item_data->nameid; - - if (item_id >= 500) { - get_count = number; - // check pet egg - pet_id = search_petDB_index(item_id, PET_EGG); - if (item_data->type == 4 || item_data->type == 5 || - item_data->type == 7 || item_data->type == 8) { - get_count = 1; - } - if ((pl_sd = map_nick2sd(character)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level - for (i = 0; i < number; i += get_count) { - // if pet egg - if (pet_id >= 0) { - pl_sd->catch_target_class = pet_db[pet_id].class_; - intif_create_pet(pl_sd->status.account_id, pl_sd->status.char_id, - (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv, - (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate, - 100, 0, 1, pet_db[pet_id].jname); - // if not pet egg - } else { - malloc_set(&item_tmp, 0, sizeof(item_tmp)); - item_tmp.nameid = item_id; - item_tmp.identify = 1; - - if ((flag = pc_additem(pl_sd, &item_tmp, get_count))) - clif_additem(pl_sd, 0, 0, flag); - } - } - - //Logs (A)dmins items [Lupus] - if(log_config.enable_logs&0x400) - log_pick_pc(sd, "A", item_tmp.nameid, number, &item_tmp); - - clif_displaymessage(fd, msg_table[18]); // Item created. - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else if(/* from jA's @giveitem */strcmpi(character,"all")==0 || strcmpi(character,"everyone")==0){ - struct map_session_data **pl_allsd; - int users; - pl_allsd = map_getallusers(&users); - for (i = 0; i < users; i++) { - if ((pl_sd = pl_allsd[i])) { - charcommand_giveitem_sub(pl_sd,item_data,number); - snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "You got %s %d.", item_name,number); - clif_displaymessage(pl_sd->fd, tmp_cmdoutput); - } - } - snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "%s received %s %d.","Everyone",item_name,number); - clif_displaymessage(fd, tmp_cmdoutput); - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name. - return -1; - } - - return 0; -} - -/*========================================== - * #warp/#rura/#rura+ - *------------------------------------------ - */ -int charcommand_warp( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - char map_name[MAP_NAME_LENGTH]; - char character[NAME_LENGTH]; - int x = 0, y = 0; - struct map_session_data *pl_sd; - int m; - - nullpo_retr(-1, sd); - - malloc_tsetdword(map_name, '\0', sizeof(map_name)); - malloc_tsetdword(character, '\0', sizeof(character)); - - if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4) { - clif_displaymessage(fd, "Usage: #warp/#rura/#rura+ "); - return -1; - } - - if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat) - strcat(map_name, ".gat"); - - if ((pl_sd = map_nick2sd(character)) == NULL) { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - if (pc_isGM(sd) < pc_isGM(pl_sd)) { // you can rura+ only lower or same GM level - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - m = map_mapname2mapid(map_name); - if (m < 0) { - clif_displaymessage(fd, msg_table[1]); // Map not found. - return -1; - } - if ((x || y) && map_getcell(m, x, y, CELL_CHKNOREACH)) { - clif_displaymessage(fd, msg_table[2]); // Coordinates out of range. - x = y = 0; - } - if (map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { - clif_displaymessage(fd, "You are not authorised to warp someone to this map."); - return -1; - } - if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { - clif_displaymessage(fd, "You are not authorised to warp this player from its actual map."); - return -1; - } - if (pc_setpos(pl_sd, map[m].index, x, y, 3) == 0) { - clif_displaymessage(pl_sd->fd, msg_table[0]); // Warped. - clif_displaymessage(fd, msg_table[15]); // Player warped (message sends to player too). - return 0; - } - //No error message specified...? - return -1; -} - -/*========================================== - * #zeny - *------------------------------------------ - */ -int charcommand_zeny( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd; - char character[NAME_LENGTH]; - int zeny = 0, new_zeny; - nullpo_retr(-1, sd); - - malloc_tsetdword(character, '\0', sizeof(character)); - - if (!message || !*message || sscanf(message, "%d %23[^\n]", &zeny, character) < 2 || zeny == 0) { - clif_displaymessage(fd, "Please, enter a number and a player name (usage: #zeny )."); - return -1; - } - - if ((pl_sd = map_nick2sd(character)) != NULL) { - new_zeny = pl_sd->status.zeny + zeny; - if (zeny > 0 && (zeny > MAX_ZENY || new_zeny > MAX_ZENY)) // fix positiv overflow - new_zeny = MAX_ZENY; - else if (zeny < 0 && (zeny < -MAX_ZENY || new_zeny < 0)) // fix negativ overflow - new_zeny = 0; - if (new_zeny != pl_sd->status.zeny) { - pl_sd->status.zeny = new_zeny; - clif_updatestatus(pl_sd, SP_ZENY); - clif_displaymessage(fd, msg_table[211]); // Character's number of zenys changed! - } else { - if (zeny < 0) - clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. - else - clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * #fakename - *------------------------------------------ - */ - -int charcommand_fakename( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd; - char name[NAME_LENGTH]; - char char_name[NAME_LENGTH]; - - nullpo_retr(-1, sd); - - name[0] = '\0'; //If you don't pass a second word, name is left as garbage, most definitely not a blank name! [Skotlex] - if (!message || !*message || sscanf(message, "%23s %23[^\n]", char_name, name) < 1) { - clif_displaymessage(sd->fd,"Usage: #fakename ."); - clif_displaymessage(sd->fd,"Or: #fakename to disable."); - return 0; - } - - if(!(pl_sd = map_nick2sd(char_name))) { - clif_displaymessage(sd->fd,"Character not found."); - return -1; - } - - if(strlen(name) < 1 || !name) { - if(strlen(pl_sd->fakename) > 1) { - pl_sd->fakename[0]='\0'; - clif_charnameack(0, &pl_sd->bl); - clif_displaymessage(sd->fd,"Returned to real name."); - } else { - clif_displaymessage(sd->fd,"Character does not has a fake name."); - } - return 0; - } - - if(strlen(name) < 2) { - clif_displaymessage(sd->fd,"Fake name must be at least two characters."); - return 0; - } - - memcpy(pl_sd->fakename,name, NAME_LENGTH-1); - clif_charnameack(0, &pl_sd->bl); - clif_displaymessage(sd->fd,"Fake name enabled."); - - return 0; -} - - -/*========================================== - * #baselvl <#> - * Transferred by: Kevin - *------------------------------------------ -*/ -int charcommand_baselevel( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd; - char player[NAME_LENGTH]; - int level = 0, i, status_point=0; - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) { - clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #baselvl <#> )."); - return -1; - } - - if ((pl_sd = map_nick2sd(player)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change base level only lower or same gm level - - if (level > 0) { - if (pl_sd->status.base_level == pc_maxbaselv(sd)) { // check for max level by Valaris - clif_displaymessage(fd, msg_table[91]); // Character's base level can't go any higher. - return 0; - } // End Addition - if ((unsigned int)level > pc_maxbaselv(pl_sd) || - pl_sd->status.base_level > pc_maxbaselv(pl_sd) -level) - level = pc_maxbaselv(pl_sd) - pl_sd->status.base_level; - for (i = 1; i <= level; i++) - status_point += (pl_sd->status.base_level + i + 14) / 5; - if (pl_sd->status.status_point > USHRT_MAX - status_point) - pl_sd->status.status_point = USHRT_MAX; - else - pl_sd->status.status_point += status_point; - pl_sd->status.base_level += (unsigned int)level; - clif_updatestatus(pl_sd, SP_BASELEVEL); - clif_updatestatus(pl_sd, SP_NEXTBASEEXP); - clif_updatestatus(pl_sd, SP_STATUSPOINT); - status_calc_pc(pl_sd, 0); - status_percent_heal(&pl_sd->bl, 100, 100); - clif_misceffect(&pl_sd->bl, 0); - clif_displaymessage(fd, msg_table[65]); // Character's base level raised. - } else { - if (pl_sd->status.base_level == 1) { - clif_displaymessage(fd, msg_table[193]); // Character's base level can't go any lower. - return -1; - } - level *= -1; - if ((unsigned int)level >= pl_sd->status.base_level) - level = pl_sd->status.base_level -1; - if (pl_sd->status.status_point > 0) { - for (i = 0; i > -level; i--) - status_point += (pl_sd->status.base_level +i + 14) / 5; - if (pl_sd->status.status_point < status_point) - pc_resetstate(pl_sd); - if (pl_sd->status.status_point < status_point) - pl_sd->status.status_point = 0; - else - pl_sd->status.status_point -= status_point; - clif_updatestatus(pl_sd, SP_STATUSPOINT); - } // to add: remove status points from stats - pl_sd->status.base_level -= (unsigned int)level; - clif_updatestatus(pl_sd, SP_BASELEVEL); - clif_updatestatus(pl_sd, SP_NEXTBASEEXP); - status_calc_pc(pl_sd, 0); - clif_displaymessage(fd, msg_table[66]); // Character's base level lowered. - } - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; //正常終了 -} - -/*========================================== - * #jlvl <#> - * Transferred by: Kevin - *------------------------------------------ - */ -int charcommand_joblevel( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd; - char player[NAME_LENGTH]; - int level = 0; - //転生や養子の場合の元の職業を算出する - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) { - clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #joblvl <#> )."); - return -1; - } - - if ((pl_sd = map_nick2sd(player)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job level only lower or same gm level - if (level > 0) { - if (pl_sd->status.job_level == pc_maxjoblv(pl_sd)) { - clif_displaymessage(fd, msg_table[67]); // Character's job level can't go any higher. - return -1; - } - if ((unsigned int)level > pc_maxjoblv(pl_sd) || - pl_sd->status.job_level > pc_maxjoblv(pl_sd) -level) - level = pc_maxjoblv(pl_sd) - pl_sd->status.job_level; - pl_sd->status.job_level += (unsigned int)level; - clif_updatestatus(pl_sd, SP_JOBLEVEL); - clif_updatestatus(pl_sd, SP_NEXTJOBEXP); - - if (pl_sd->status.skill_point > USHRT_MAX - level) - pl_sd->status.skill_point = USHRT_MAX; - else - pl_sd->status.skill_point += level; - clif_updatestatus(pl_sd, SP_SKILLPOINT); - status_calc_pc(pl_sd, 0); - clif_misceffect(&pl_sd->bl, 1); - clif_displaymessage(fd, msg_table[68]); // character's job level raised. - } else { - if (pl_sd->status.job_level == 1) { - clif_displaymessage(fd, msg_table[194]); // Character's job level can't go any lower. - return -1; - } - level*=-1; - if ((unsigned int)level >= pl_sd->status.job_level) - level = pl_sd->status.job_level-1; - pl_sd->status.job_level -= (unsigned int)level; - clif_updatestatus(pl_sd, SP_JOBLEVEL); - clif_updatestatus(pl_sd, SP_NEXTJOBEXP); - if (pl_sd->status.skill_point < level) - pc_resetskill(pl_sd, 0); //Need more skill points to substract - if (pl_sd->status.skill_point < level) - pl_sd->status.skill_point = 0; - else - pl_sd->status.skill_point -= level; - clif_updatestatus(pl_sd, SP_SKILLPOINT); - status_calc_pc(pl_sd, 0); - clif_displaymessage(fd, msg_table[69]); // Character's job level lowered. - } - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - - -/*========================================== - * #questskill - * Transferred by: Kevin - *------------------------------------------ - */ -int charcommand_questskill( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd; - char player[NAME_LENGTH]; - int skill_id = 0; - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) { - clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: #questskill <#:0+> )."); - return -1; - } - - if (skill_id >= 0 && skill_id < MAX_SKILL_DB) { - if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) { - if ((pl_sd = map_nick2sd(player)) != NULL) { - if (pc_checkskill(pl_sd, skill_id) == 0) { - pc_skill(pl_sd, skill_id, 1, 0); - clif_displaymessage(fd, msg_table[199]); // This player has learned the skill. - } else { - clif_displaymessage(fd, msg_table[200]); // This player already has this quest skill. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist. - return -1; - } - - return 0; -} - - -/*========================================== - * #lostskill - * Transferred by: Kevin - *------------------------------------------ - */ -int charcommand_lostskill( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd; - char player[NAME_LENGTH]; - int skill_id = 0; - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) { - clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: @charlostskill <#:0+> )."); - return -1; - } - - if (skill_id >= 0 && skill_id < MAX_SKILL) { - if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) { - if ((pl_sd = map_nick2sd(player)) != NULL) { - if (pc_checkskill(pl_sd, skill_id) > 0) { - pl_sd->status.skill[skill_id].lv = 0; - pl_sd->status.skill[skill_id].flag = 0; - clif_skillinfoblock(pl_sd); - clif_displaymessage(fd, msg_table[202]); // This player has forgotten the skill. - } else { - clif_displaymessage(fd, msg_table[203]); // This player doesn't have this quest skill. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist. - return -1; - } - - return 0; -} - -/*========================================== - * Character Skill Reset - *------------------------------------------ - */ -int charcommand_skreset( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd; - char player[NAME_LENGTH]; - char tmp_cmdoutput[1024]; - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) { - clif_displaymessage(fd, "Please, enter a player name (usage: @charskreset )."); - return -1; - } - - if ((pl_sd = map_nick2sd(player)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset skill points only lower or same gm level - pc_resetskill(pl_sd,1); - sprintf(tmp_cmdoutput, msg_table[206], player); // '%s' skill points reseted! - clif_displaymessage(fd, tmp_cmdoutput); - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * Character Stat Reset - *------------------------------------------ - */ -int charcommand_streset( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd; - char player[NAME_LENGTH]; - char tmp_cmdoutput[1024]; - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) { - clif_displaymessage(fd, "Please, enter a player name (usage: @charstreset )."); - return -1; - } - - if ((pl_sd = map_nick2sd(player)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset stats points only lower or same gm level - pc_resetstate(pl_sd); - sprintf(tmp_cmdoutput, msg_table[207], player); // '%s' stats points reseted! - clif_displaymessage(fd, tmp_cmdoutput); - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * Character Model by chbrules - *------------------------------------------ - */ -int charcommand_model( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - int hair_style = 0, hair_color = 0, cloth_color = 0; - struct map_session_data *pl_sd; - char player[NAME_LENGTH]; - char tmp_cmdoutput[1024]; - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%d %d %d %23[^\n]", &hair_style, &hair_color, &cloth_color, player) < 4 || hair_style < 0 || hair_color < 0 || cloth_color < 0) { - sprintf(tmp_cmdoutput, "Please, enter a valid model and a player name (usage: @charmodel ).", - MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR); - clif_displaymessage(fd, tmp_cmdoutput); - return -1; - } - - if ((pl_sd = map_nick2sd(player)) != NULL) { - if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE && - hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR && - cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) { - /* Removed this check for being too strange. [Skotlex] - if (cloth_color != 0 && - pl_sd->status.sex == 1 && - (pl_sd->status.class_ == JOB_ASSASSIN || pl_sd->status.class_ == JOB_ROGUE)) { - clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class. - return -1; - } else { - */ - pc_changelook(pl_sd, LOOK_HAIR, hair_style); - pc_changelook(pl_sd, LOOK_HAIR_COLOR, hair_color); - pc_changelook(pl_sd, LOOK_CLOTHES_COLOR, cloth_color); - clif_displaymessage(fd, msg_table[36]); // Appearence changed. -// } - } else { - clif_displaymessage(fd, msg_table[37]); // An invalid number was specified. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * Character Skill Point (Rewritten by [Yor]) - *------------------------------------------ - */ -int charcommand_skpoint( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd; - char player[NAME_LENGTH]; - int new_skill_point; - int point = 0; - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) { - clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charskpoint )."); - return -1; - } - - if ((pl_sd = map_nick2sd(player)) != NULL) { - if (point > 0 && pl_sd->status.skill_point > USHRT_MAX - point) - new_skill_point = USHRT_MAX; - else if (point < 0 && pl_sd->status.skill_point < -point) - new_skill_point = 0; - else - new_skill_point = pl_sd->status.skill_point + point; - if (new_skill_point != (int)pl_sd->status.skill_point) { - pl_sd->status.skill_point = new_skill_point; - clif_updatestatus(pl_sd, SP_SKILLPOINT); - clif_displaymessage(fd, msg_table[209]); // Character's number of skill points changed! - } else { - if (point < 0) - clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. - else - clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * Character Status Point (rewritten by [Yor]) - *------------------------------------------ - */ -int charcommand_stpoint( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - struct map_session_data *pl_sd; - char player[NAME_LENGTH]; - int new_status_point; - int point = 0; - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) { - clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charstpoint )."); - return -1; - } - - if ((pl_sd = map_nick2sd(player)) != NULL) { - if (point > 0 && pl_sd->status.status_point > USHRT_MAX - point) - new_status_point = USHRT_MAX; - else if (point < 0 && pl_sd->status.status_point < -point) - new_status_point = 0; - else - new_status_point = pl_sd->status.status_point + point; - if (new_status_point != (int)pl_sd->status.status_point) { - pl_sd->status.status_point = new_status_point; - clif_updatestatus(pl_sd, SP_STATUSPOINT); - clif_displaymessage(fd, msg_table[210]); // Character's number of status points changed! - } else { - if (point < 0) - clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. - else - clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * charchangesex command (usage: charchangesex ) - *------------------------------------------ - */ -int charcommand_changesex( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - char player[NAME_LENGTH]; - nullpo_retr(-1, sd); - - if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) { - clif_displaymessage(fd, "Please, enter a player name (usage: @charchangesex )."); - return -1; - } - - // check player name - if (strlen(player) < 4) { - clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. - return -1; - } else if (strlen(player) > 23) { - clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum. - return -1; - } else { - chrif_char_ask_name(sd->status.account_id, player, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex - clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it. - } - - return 0; -} - -/*========================================== - * Feel (SG save map) Reset - *------------------------------------------ - */ -int charcommand_feelreset( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - char character[NAME_LENGTH]; - char output[200]; - struct map_session_data *pl_sd; - - malloc_tsetdword(character, '\0', sizeof(character)); - malloc_tsetdword(output, '\0', sizeof(output)); - - if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { - clif_displaymessage(fd, "Please, enter a player name (usage: #feelreset )."); - return -1; - } - - if ((pl_sd = map_nick2sd(character)) != NULL) { - if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level - pc_resetfeel(pl_sd); - sprintf(output, msg_table[267], character); // '%s' designated maps reseted! - clif_displaymessage(fd, output); - } else { - clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. - return -1; - } - } else { - clif_displaymessage(fd, msg_table[3]); // Character not found. - return -1; - } - - return 0; -} - -/*========================================== - * #help - Char commands [Kayla] - *------------------------------------------ - */ -int charcommand_help( - const int fd, struct map_session_data* sd, - const char* command, const char* message) -{ - char buf[2048], w1[2048], w2[2048]; - int i, gm_level; - FILE* fp; - nullpo_retr(-1, sd); - - malloc_tsetdword(buf, '\0', sizeof(buf)); - - if ((fp = fopen(charhelp_txt, "r")) != NULL) { - clif_displaymessage(fd, msg_table[26]); /* Help commands: */ - gm_level = pc_isGM(sd); - while(fgets(buf, sizeof(buf) - 1, fp) != NULL) { - if (buf[0] == '/' && buf[1] == '/') - continue; - for (i = 0; buf[i] != '\0'; i++) { - if (buf[i] == '\r' || buf[i] == '\n') { - buf[i] = '\0'; - break; - } - } - if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2) - clif_displaymessage(fd, buf); - else if (gm_level >= atoi(w1)) - clif_displaymessage(fd, w2); - } - fclose(fp); - } else { - clif_displaymessage(fd, msg_table[27]); /* File help.txt not found. */ - return -1; - } - - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include +#include +#include + +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/nullpo.h" +#include "../common/showmsg.h" + +#include "log.h" +#include "clif.h" +#include "chrif.h" +#include "intif.h" +#include "itemdb.h" +#include "map.h" +#include "pc.h" +#include "status.h" +#include "skill.h" +#include "mob.h" +#include "pet.h" +#include "battle.h" +#include "charcommand.h" +#include "atcommand.h" + +static char command_symbol = '#'; + +extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others) + +#define CCMD_FUNC(x) int charcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message) +CCMD_FUNC(jobchange); +CCMD_FUNC(petrename); +CCMD_FUNC(petfriendly); +CCMD_FUNC(stats); +CCMD_FUNC(option); +CCMD_FUNC(save); +CCMD_FUNC(stats_all); +CCMD_FUNC(reset); +CCMD_FUNC(spiritball); +CCMD_FUNC(itemlist); +CCMD_FUNC(effect); +CCMD_FUNC(storagelist); +CCMD_FUNC(item); +CCMD_FUNC(warp); +CCMD_FUNC(zeny); +CCMD_FUNC(fakename); +CCMD_FUNC(baselevel); +CCMD_FUNC(joblevel); +CCMD_FUNC(questskill); +CCMD_FUNC(lostskill); +CCMD_FUNC(skreset); +CCMD_FUNC(streset); +CCMD_FUNC(model); +CCMD_FUNC(stpoint); +CCMD_FUNC(skpoint); +CCMD_FUNC(changesex); +CCMD_FUNC(feelreset); +CCMD_FUNC(help); + + +/*========================================== + *CharCommandInfo charcommand_info[]構造体の定義 + *------------------------------------------ + */ + +// First char of commands is configured in charcommand_athena.conf. Leave # in this list for default value. +// to set default level, read charcommand_athena.conf first please. +static CharCommandInfo charcommand_info[] = { + { CharCommandJobChange, "#job", 60, charcommand_jobchange }, + { CharCommandJobChange, "#jobchange", 60, charcommand_jobchange }, + { CharCommandPetRename, "#petrename", 50, charcommand_petrename }, + { CharCommandPetFriendly, "#petfriendly", 50, charcommand_petfriendly }, + { CharCommandStats, "#stats", 40, charcommand_stats }, + { CharCommandOption, "#option", 60, charcommand_option }, + { CharCommandReset, "#reset", 60, charcommand_reset }, + { CharCommandSave, "#save", 60, charcommand_save }, + { CharCommandStatsAll, "#statsall", 40, charcommand_stats_all }, + { CharCommandSpiritball, "#spiritball", 40, charcommand_spiritball }, + { CharCommandItemList, "#itemlist", 40, charcommand_itemlist }, + { CharCommandEffect, "#effect", 40, charcommand_effect }, + { CharCommandStorageList, "#storagelist", 40, charcommand_storagelist }, + { CharCommandItem, "#item", 60, charcommand_item }, + { CharCommandWarp, "#warp", 60, charcommand_warp }, + { CharCommandWarp, "#rura", 60, charcommand_warp }, + { CharCommandWarp, "#rura+", 60, charcommand_warp }, + { CharCommandZeny, "#zeny", 60, charcommand_zeny }, + { CharCommandFakeName, "#fakename", 20, charcommand_fakename}, + + //*********************************Recently added commands********************************************* + { CharCommandBaseLevel, "#baselvl", 20, charcommand_baselevel}, + { CharCommandBaseLevel, "#blvl", 60, charcommand_baselevel}, + { CharCommandBaseLevel, "#baselvlup", 60, charcommand_baselevel}, + { CharCommandJobLevel, "#joblvl", 60, charcommand_joblevel}, + { CharCommandJobLevel, "#jlvl", 60, charcommand_joblevel}, + { CharCommandJobLevel, "#joblvlup", 60, charcommand_joblevel}, + { CharCommandQuestSkill, "#questskill", 60, charcommand_questskill }, + { CharCommandLostSkill, "#lostskill", 60, charcommand_lostskill }, + { CharCommandSkReset, "#skreset", 60, charcommand_skreset }, + { CharCommandStReset, "#streset", 60, charcommand_streset }, + { CharCommandModel, "#model", 50, charcommand_model }, + { CharCommandSKPoint, "#skpoint", 60, charcommand_skpoint }, + { CharCommandSTPoint, "#stpoint", 60, charcommand_stpoint }, + { CharCommandChangeSex, "#changesex", 60, charcommand_changesex }, + { CharCommandFeelReset, "#feelreset", 60, charcommand_feelreset }, + { CharCommandHelp, "#help", 20, charcommand_help }, +// add new commands before this line + { CharCommand_Unknown, NULL, 1, NULL } +}; + +int get_charcommand_level(const CharCommandType type) { + int i; + + for (i = 0; charcommand_info[i].type != CharCommand_None; i++) + if (charcommand_info[i].type == type) + return charcommand_info[i].level; + + return 100; // 100: command can not be used +} + +CharCommandType +charcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl) { + CharCommandInfo info; + CharCommandType type; + + malloc_set(&info, 0, sizeof(info)); + + type = charcommand(sd, gmlvl, str, &info); + if (type != CharCommand_None) { + char command[100]; + char output[200]; + const char* p = str; + + if (map[sd->bl.m].nocommand && + gmlvl < map[sd->bl.m].nocommand) + { //Command not allowed on this map. + sprintf(output, msg_txt(143)); + clif_displaymessage(fd, output); + return AtCommand_None; + } + + malloc_tsetdword(command, '\0', sizeof(command)); + malloc_tsetdword(output, '\0', sizeof(output)); + while (*p && !isspace(*p)) + p++; + if (p - str >= sizeof(command)) // too long + return CharCommand_Unknown; + strncpy(command, str, p - str); + while (isspace(*p)) + p++; + + if (type == CharCommand_Unknown || info.proc == NULL) { + snprintf(output, sizeof(output),msg_txt(153), command); // %s is Unknown Command. + clif_displaymessage(fd, output); + } else { + if (info.proc(fd, sd, command, p) != 0) { + // Command can not be executed + snprintf(output, sizeof(output), msg_txt(154), command); // %s failed. + clif_displaymessage(fd, output); + } + } + + return info.type; + } + + return CharCommand_None; +} + +/*========================================== + *is_charcommand @コマンドに存在するかどうか確認する + *------------------------------------------ + */ +CharCommandType +is_charcommand(const int fd, struct map_session_data* sd, const char* message) { + const char* str = message; + int s_flag = 0; + + nullpo_retr(CharCommand_None, sd); + + if (!message || !*message) + return CharCommand_None; + + str += strlen(sd->status.name); + while (*str && (isspace(*str) || (s_flag == 0 && *str == ':'))) { + if (*str == ':') + s_flag = 1; + str++; + } + + if (!*str) + return CharCommand_None; + + return charcommand_sub(fd,sd,str,pc_isGM(sd)); +} + +/*========================================== + * + *------------------------------------------ + */ +CharCommandType charcommand(struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info) { + char* p = (char *)message; + + if (!info) + return CharCommand_None; + if (battle_config.atc_gmonly != 0 && !level) // level = pc_isGM(sd) + return CharCommand_None; + if (!p || !*p) { + ShowError("char command message is empty\n"); + return CharCommand_None; + } + + if(p[0] == '|') + p += 3; + + if (*p == command_symbol) { // check first char, try to skip |00 (or something else) [Lance] + char command[101]; + int i = 0; + malloc_set(info, 0, sizeof(CharCommandInfo)); + sscanf(p, "%100s", command); + command[sizeof(command)-1] = '\0'; + + while (charcommand_info[i].type != CharCommand_Unknown) { + if (strcmpi(command+1, charcommand_info[i].command+1) == 0 && level >= charcommand_info[i].level) { + p[0] = charcommand_info[i].command[0]; // set correct first symbol for after. + break; + } + i++; + } + + if (charcommand_info[i].type == CharCommand_Unknown) { + // doesn't return Unknown if player is normal player (display the text, not display: unknown command) + if (level == 0) + return CharCommand_None; + else + return CharCommand_Unknown; + } else if((log_config.gm) && (charcommand_info[i].level >= log_config.gm)) { + log_atcommand(sd, message); + } + memcpy(info, &charcommand_info[i], sizeof charcommand_info[i]); + } else { + return CharCommand_None; + } + + return info->type; +} + + +/*========================================== + * + *------------------------------------------ + */ +static CharCommandInfo* get_charcommandinfo_byname(const char* name) { + int i; + + for (i = 0; charcommand_info[i].type != CharCommand_Unknown; i++) + if (strcmpi(charcommand_info[i].command + 1, name) == 0) + return &charcommand_info[i]; + + return NULL; +} + +/*========================================== + * + *------------------------------------------ + */ +int charcommand_config_read(const char *cfgName) { + char line[1024], w1[1024], w2[1024]; + CharCommandInfo* p; + FILE* fp; + + if ((fp = fopen(cfgName, "r")) == NULL) { + ShowError("CharCommands configuration file not found: %s\n", cfgName); + return 1; + } + + while (fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + + if (sscanf(line, "%1023[^:]:%1023s", w1, w2) != 2) + continue; + p = get_charcommandinfo_byname(w1); + if (p != NULL) { + p->level = atoi(w2); + if (p->level > 100) + p->level = 100; + else if (p->level < 0) + p->level = 0; + } + + if (strcmpi(w1, "import") == 0) + charcommand_config_read(w2); + else if (strcmpi(w1, "command_symbol") == 0 && w2[0] > 31 && + w2[0] != '/' && // symbol of standard ragnarok GM commands + w2[0] != '%' && // symbol of party chat speaking + w2[0] != '$' && // symbol of guild chat speaking + w2[0] != '@') // symbol of atcommand + command_symbol = w2[0]; + } + fclose(fp); + + return 0; +} + +/*========================================== + * 対象キャラクターを転職させる upper指定で転生や養子も可能 + *------------------------------------------ + */ +int charcommand_jobchange( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + struct map_session_data* pl_sd; + int job = 0, upper = -1; + + malloc_tsetdword(character, '\0', sizeof(character)); + + if (!message || !*message) { + clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange )."); + return -1; + } + + if (sscanf(message, "%d %d %99[^\n]", &job, &upper, character) < 3) { //upper指定してある + upper = -1; + if (sscanf(message, "%d %99[^\n]", &job, character) < 2) { //upper指定してない上に何か足りない + clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange )."); + return -1; + } + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + int j; + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job only to lower or same level + if ((job >= 0 && job < MAX_PC_CLASS)) { + for (j=0; j < MAX_INVENTORY; j++) { + if(pl_sd->status.inventory[j].nameid>0 && pl_sd->status.inventory[j].equip!=0) + pc_unequipitem(pl_sd, j, 3); + } + if (pc_jobchange(pl_sd, job, upper) == 0) + clif_displaymessage(fd, msg_table[48]); // Character's job changed. + else { + clif_displaymessage(fd, msg_table[192]); // Impossible to change the character's job. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[49]); // Invalid job ID. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int charcommand_petrename( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[NAME_LENGTH]; + struct map_session_data *pl_sd; + struct pet_data *pd; + + malloc_tsetdword(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: #petrename )."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) == NULL) { + clif_displaymessage(fd, msg_txt(3)); // Character not found. + return -1; + } + + if (!pl_sd->status.pet_id || !pl_sd->pd) { + clif_displaymessage(fd, msg_table[191]); // Sorry, but this player has no pet. + return -1; + } + + pd = pl_sd->pd; + + if (pd->pet.rename_flag) { + clif_displaymessage(fd, msg_table[190]); // This player can already rename his/her pet. + return -1; + } + pd->pet.rename_flag = 0; + intif_save_petdata(pl_sd->status.account_id, &pd->pet); + clif_send_petstatus(pl_sd); + clif_displaymessage(fd, msg_table[189]); // This player can now rename his/her pet. + return 0; +} + + +/*========================================== + * + *------------------------------------------ + */ +int charcommand_petfriendly( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int friendly = 0; + char character[NAME_LENGTH]; + struct map_session_data *pl_sd; + struct pet_data *pd; + + malloc_tsetdword(character, '\0', sizeof(character)); + if (!message || !*message || sscanf(message,"%d %23s",&friendly,character) < 2) { + clif_displaymessage(fd, "Please, enter a valid value (usage: " + "#petfriendly <0-1000> )."); + return -1; + } + + if (((pl_sd = map_nick2sd(character)) == NULL) || + pc_isGM(sd)status.pet_id || !pl_sd->pd) { + clif_displaymessage(fd, msg_table[191]); // Sorry, but this player has no pet. + return -1; + } + + if (friendly < 0 || friendly > 1000) { + clif_displaymessage(fd, msg_table[37]); // An invalid number was specified. + return -1; + } + + pd = pl_sd->pd; + if (friendly == pd->pet.intimate) { + clif_displaymessage(fd, msg_table[183]); // Pet friendly is already the good value. + return -1; + } + + pd->pet.intimate = friendly; + clif_send_petstatus(pl_sd); + clif_pet_emotion(pd,0); + clif_displaymessage(pl_sd->fd, msg_table[182]); // Pet friendly value changed! + clif_displaymessage(sd->fd, msg_table[182]); // Pet friendly value changed! + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int charcommand_stats( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[NAME_LENGTH]; + char job_jobname[100]; + char output[200]; + struct map_session_data *pl_sd; + int i; + + malloc_tsetdword(character, '\0', sizeof(character)); + malloc_tsetdword(job_jobname, '\0', sizeof(job_jobname)); + malloc_tsetdword(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: #stats )."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + struct { + const char* format; + int value; + } output_table[] = { + { "Base Level - %d", 0 }, + { NULL, 0 }, + { "Hp - %d", 0 }, + { "MaxHp - %d", 0 }, + { "Sp - %d", 0 }, + { "MaxSp - %d", 0 }, + { "Str - %3d", 0 }, + { "Agi - %3d", 0 }, + { "Vit - %3d", 0 }, + { "Int - %3d", 0 }, + { "Dex - %3d", 0 }, + { "Luk - %3d", 0 }, + { "Zeny - %d", 0 }, + { NULL, 0 } + }; + //direct array initialization with variables is not standard C compliant. + output_table[0].value = pl_sd->status.base_level; + output_table[1].format = job_jobname; + output_table[1].value = pl_sd->status.job_level; + output_table[2].value = pl_sd->status.hp; + output_table[3].value = pl_sd->status.max_hp; + output_table[4].value = pl_sd->status.sp; + output_table[5].value = pl_sd->status.max_sp; + output_table[6].value = pl_sd->status.str; + output_table[7].value = pl_sd->status.agi; + output_table[8].value = pl_sd->status.vit; + output_table[9].value = pl_sd->status.int_; + output_table[10].value = pl_sd->status.dex; + output_table[11].value = pl_sd->status.luk; + output_table[12].value = pl_sd->status.zeny; + sprintf(job_jobname, "Job - %s %s", job_name(pl_sd->status.class_), "(level %d)"); + sprintf(output, msg_table[53], pl_sd->status.name); // '%s' stats: + clif_displaymessage(fd, output); + for (i = 0; output_table[i].format != NULL; i++) { + sprintf(output, output_table[i].format, output_table[i].value); + clif_displaymessage(fd, output); + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Character Reset + *------------------------------------------ + */ +int charcommand_reset( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[NAME_LENGTH]; + char output[200]; + struct map_session_data *pl_sd; + + malloc_tsetdword(character, '\0', sizeof(character)); + malloc_tsetdword(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: #reset )."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level + pc_resetstate(pl_sd); + pc_resetskill(pl_sd,1); + sprintf(output, msg_table[208], character); // '%s' skill and stats points reseted! + clif_displaymessage(fd, output); + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int charcommand_option( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[NAME_LENGTH]; + int opt1 = 0, opt2 = 0, opt3 = 0; + struct map_session_data* pl_sd; + + malloc_tsetdword(character, '\0', sizeof(character)); + + if (!message || !*message || + sscanf(message, "%d %d %d %23[^\n]", &opt1, &opt2, &opt3, character) < 4 || + opt1 < 0 || opt2 < 0 || opt3 < 0) { + clif_displaymessage(fd, "Please, enter valid options and a player name (usage: #option )."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change option only to lower or same level + pl_sd->sc.opt1 = opt1; + pl_sd->sc.opt2 = opt2; + pc_setoption(pl_sd, opt3); + clif_displaymessage(fd, msg_table[58]); // Character's options changed. + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int charcommand_save( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char map_name[MAP_NAME_LENGTH]; + char character[NAME_LENGTH]; + struct map_session_data* pl_sd; + int x = 0, y = 0; + int m; + + malloc_tsetdword(map_name, '\0', sizeof(map_name)); + malloc_tsetdword(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4 || x < 0 || y < 0) { + clif_displaymessage(fd, "Please, enter a valid save point and a player name (usage: #save )."); + return -1; + } + + if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat) + strcat(map_name, ".gat"); + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change save point only to lower or same gm level + m = map_mapname2mapid(map_name); + if (m < 0 && !mapindex_name2id(map_name)) { + clif_displaymessage(fd, msg_table[1]); // Map not found. + return -1; + } else { + //FIXME: What do you do if the map is in another map server with the nowarpto flag? + if (m>=0 && map[m].flag.nosave && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to set this map as a save map."); + return -1; + } + if (m>=0) + pc_setsavepoint(pl_sd, map[m].index, x, y); + else + pc_setsavepoint(pl_sd, mapindex_name2id(map_name), x, y); + clif_displaymessage(fd, msg_table[57]); // Character's respawn point changed. + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +//** Character Stats All by fritz +int charcommand_stats_all(const int fd, struct map_session_data* sd, const char* command, const char* message) +{ + char output[1024], gmlevel[1024]; + int i; + int count, users; + struct map_session_data *pl_sd, **pl_allsd; + + malloc_tsetdword(output, '\0', sizeof(output)); + malloc_tsetdword(gmlevel, '\0', sizeof(gmlevel)); + + count = 0; + pl_allsd = map_getallusers(&users); + for(i = 0; i < users; i++) { + if ((pl_sd = pl_allsd[i])) + { + if (pc_isGM(pl_sd) > 0) + sprintf(gmlevel, "| GM Lvl: %d", pc_isGM(pl_sd)); + else + sprintf(gmlevel, " "); + + sprintf(output, "Name: %s | BLvl: %d | Job: %s (Lvl: %d) | HP: %d/%d | SP: %d/%d", pl_sd->status.name, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level, pl_sd->status.hp, pl_sd->status.max_hp, pl_sd->status.sp, pl_sd->status.max_sp); + clif_displaymessage(fd, output); + sprintf(output, "STR: %d | AGI: %d | VIT: %d | INT: %d | DEX: %d | LUK: %d | Zeny: %d %s", pl_sd->status.str, pl_sd->status.agi, pl_sd->status.vit, pl_sd->status.int_, pl_sd->status.dex, pl_sd->status.luk, pl_sd->status.zeny, gmlevel); + clif_displaymessage(fd, output); + clif_displaymessage(fd, "--------"); + count++; + } + } + + if (count == 0) + clif_displaymessage(fd, msg_table[28]); // No player found. + else if (count == 1) + clif_displaymessage(fd, msg_table[29]); // 1 player found. + else { + sprintf(output, msg_table[30], count); // %d players found. + clif_displaymessage(fd, output); + } + + return 0; +} + +/*========================================== + * CharSpiritBall Function by PalasX + *------------------------------------------ + */ +int charcommand_spiritball(const int fd, struct map_session_data* sd,const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char character[NAME_LENGTH]; + int spirit = 0; + + malloc_tsetdword(character, '\0', sizeof(character)); + + if(!message || !*message || sscanf(message, "%d %23[^\n]", &spirit, character) < 2 || spirit < 0 || spirit > 1000) { + clif_displaymessage(fd, "Usage: @spiritball ) ."); + return -1; + } + + if((pl_sd = map_nick2sd(character)) != NULL) { + if (spirit >= 0 && spirit <= 0x7FFF) { + if (pl_sd->spiritball != spirit || spirit > 999) { + if (pl_sd->spiritball > 0) + pc_delspiritball(pl_sd, pl_sd->spiritball, 1); + pl_sd->spiritball = spirit; + clif_spiritball(pl_sd); + // no message, player can look the difference + if (spirit > 1000) + clif_displaymessage(fd, msg_table[204]); // WARNING: more than 1000 spiritballs can CRASH your server and/or client! + } else { + clif_displaymessage(fd, msg_table[205]); // You already have this number of spiritballs. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[37]); // An invalid number was specified. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + return 0; +} + +/*========================================== + * #itemlist : Displays the list of a player's items. + *------------------------------------------ + */ +int +charcommand_itemlist( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + struct item_data *item_data, *item_temp; + int i, j, equip, count, counter, counter2; + char character[NAME_LENGTH], output[200], equipstr[100], outputtmp[200]; + struct item *i_item; //Current inventory item. + nullpo_retr(-1, sd); + + malloc_tsetdword(character, '\0', sizeof(character)); + malloc_tsetdword(output, '\0', sizeof(output)); + malloc_tsetdword(equipstr, '\0', sizeof(equipstr)); + malloc_tsetdword(outputtmp, '\0', sizeof(outputtmp)); + + if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist )."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level + counter = 0; + count = 0; + for (i = 0; i < MAX_INVENTORY; i++) { + i_item = &pl_sd->status.inventory[i]; + if (pl_sd->status.inventory[i].nameid > 0 && (item_data = itemdb_exists(i_item->nameid)) != NULL) { + counter = counter + i_item->amount; + count++; + if (count == 1) { + sprintf(output, "------ Items list of '%s' ------", pl_sd->status.name); + clif_displaymessage(fd, output); + } + if ((equip = i_item->equip)) { + strcpy(equipstr, "| equiped: "); + if (equip & EQP_GARMENT) + strcat(equipstr, "robe/gargment, "); + if (equip & EQP_ACC_L) + strcat(equipstr, "left accessory, "); + if (equip & EQP_ARMOR) + strcat(equipstr, "body/armor, "); + if ((equip & EQP_ARMS) == EQP_HAND_R) + strcat(equipstr, "right hand, "); + if ((equip & EQP_ARMS) == EQP_HAND_L) + strcat(equipstr, "left hand, "); + if ((equip & EQP_ARMS) == EQP_ARMS) + strcat(equipstr, "both hands, "); + if (equip & EQP_SHOES) + strcat(equipstr, "feet, "); + if (equip & EQP_ACC_R) + strcat(equipstr, "right accessory, "); + if ((equip & EQP_HELM) == EQP_HEAD_LOW) + strcat(equipstr, "lower head, "); + if ((equip & EQP_HELM) == EQP_HEAD_TOP) + strcat(equipstr, "top head, "); + if ((equip & EQP_HELM) == (EQP_HEAD_LOW|EQP_HEAD_TOP)) + strcat(equipstr, "lower/top head, "); + if ((equip & EQP_HELM) == EQP_HEAD_MID) + strcat(equipstr, "mid head, "); + if ((equip & EQP_HELM) == (EQP_HEAD_LOW|EQP_HEAD_MID)) + strcat(equipstr, "lower/mid head, "); + if ((equip & EQP_HELM) == EQP_HELM) + strcat(equipstr, "lower/mid/top head, "); + // remove final ', ' + equipstr[strlen(equipstr) - 2] = '\0'; + } else + malloc_tsetdword(equipstr, '\0', sizeof(equipstr)); + if (i_item->refine) + sprintf(output, "%d %s %+d (%s %+d, id: %d) %s", i_item->amount, item_data->name, i_item->refine, item_data->jname, i_item->refine, i_item->nameid, equipstr); + else + sprintf(output, "%d %s (%s, id: %d) %s", i_item->amount, item_data->name, item_data->jname, i_item->nameid, equipstr); + clif_displaymessage(fd, output); + malloc_tsetdword(output, '\0', sizeof(output)); + counter2 = 0; + + if(i_item->card[0]==CARD0_PET) { //pet eggs + if (i_item->card[3]) + sprintf(outputtmp, " -> (pet egg, pet id: %u, named)", (unsigned int)MakeDWord(i_item->card[1], i_item->card[2])); + else + sprintf(outputtmp, " -> (pet egg, pet id: %u, unnamed)", (unsigned int)MakeDWord(i_item->card[1], i_item->card[2])); + strcat(output, outputtmp); + } else + if(i_item->card[0]==CARD0_FORGE) { //forged items. + sprintf(outputtmp, " -> (crafted item, creator id: %u, star crumbs %d, element %d)", (unsigned int)MakeDWord(i_item->card[2], i_item->card[3]), i_item->card[1]>>8, i_item->card[1]&0x0f); + } else + if(i_item->card[0]==CARD0_CREATE) { //created items. + sprintf(outputtmp, " -> (produced item, creator id: %u)", (unsigned int)MakeDWord(i_item->card[2], i_item->card[3])); + strcat(output, outputtmp); + } else //Normal slots + for (j = 0; j < item_data->slot; j++) { + if (pl_sd->status.inventory[i].card[j]) { + if ((item_temp = itemdb_exists(i_item->card[j])) != NULL) { + if (output[0] == '\0') + sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); + else + sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); + strcat(output, outputtmp); + } + } + } + if (output[0] != '\0') { + output[strlen(output) - 2] = ')'; + output[strlen(output) - 1] = '\0'; + clif_displaymessage(fd, output); + } + } + } + if (count == 0) + clif_displaymessage(fd, "No item found on this player."); + else { + sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count); + clif_displaymessage(fd, output); + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * #effect by [MouseJstr] + * + * Create a effect localized on another character + *------------------------------------------ + */ +int +charcommand_effect(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd = NULL; + char target[255]; + int type = 0; + nullpo_retr(-1, sd); + + if (!message || !*message || sscanf(message, "%d %s", &type, target) != 2) { + clif_displaymessage(fd, "usage: #effect ."); + return -1; + } + + if((pl_sd=map_nick2sd((char *) target)) == NULL) + return -1; + + clif_specialeffect(&pl_sd->bl, type, AREA); + clif_displaymessage(fd, msg_table[229]); // Your effect has changed. + + return 0; +} + +/*========================================== + * #storagelist : Displays the items list of a player's storage. + *------------------------------------------ + */ +int +charcommand_storagelist( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct storage *stor; + struct map_session_data *pl_sd; + struct item_data *item_data, *item_temp; + int i, j, count, counter, counter2; + char character[NAME_LENGTH], output[200], outputtmp[200]; + nullpo_retr(-1, sd); + + malloc_tsetdword(character, '\0', sizeof(character)); + malloc_tsetdword(output, '\0', sizeof(output)); + malloc_tsetdword(outputtmp, '\0', sizeof(outputtmp)); + + if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist )."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level + if((stor = account2storage2(pl_sd->status.account_id)) != NULL) { + counter = 0; + count = 0; + for (i = 0; i < MAX_STORAGE; i++) { + if (stor->storage_[i].nameid > 0 && (item_data = itemdb_search(stor->storage_[i].nameid)) != NULL) { + counter = counter + stor->storage_[i].amount; + count++; + if (count == 1) { + sprintf(output, "------ Storage items list of '%s' ------", pl_sd->status.name); + clif_displaymessage(fd, output); + } + if (stor->storage_[i].refine) + sprintf(output, "%d %s %+d (%s %+d, id: %d)", stor->storage_[i].amount, item_data->name, stor->storage_[i].refine, item_data->jname, stor->storage_[i].refine, stor->storage_[i].nameid); + else + sprintf(output, "%d %s (%s, id: %d)", stor->storage_[i].amount, item_data->name, item_data->jname, stor->storage_[i].nameid); + clif_displaymessage(fd, output); + malloc_tsetdword(output, '\0', sizeof(output)); + counter2 = 0; + for (j = 0; j < item_data->slot; j++) { + if (stor->storage_[i].card[j]) { + if ((item_temp = itemdb_search(stor->storage_[i].card[j])) != NULL) { + if (output[0] == '\0') + sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); + else + sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); + strcat(output, outputtmp); + } + } + } + if (output[0] != '\0') { + output[strlen(output) - 2] = ')'; + output[strlen(output) - 1] = '\0'; + clif_displaymessage(fd, output); + } + } + } + if (count == 0) + clif_displaymessage(fd, "No item found in the storage of this player."); + else { + sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count); + clif_displaymessage(fd, output); + } + } else { + clif_displaymessage(fd, "This player has no storage."); + return 0; + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +static void +charcommand_giveitem_sub(struct map_session_data *sd,struct item_data *item_data,int number) +{ + int flag = 0; + int loop = 1, get_count = number,i; + struct item item_tmp; + + if(sd && item_data){ + if (item_data->type == 4 || item_data->type == 5 || + item_data->type == 7 || item_data->type == 8) { + loop = number; + get_count = 1; + } + for (i = 0; i < loop; i++) { + malloc_set(&item_tmp, 0, sizeof(item_tmp)); + item_tmp.nameid = item_data->nameid; + item_tmp.identify = 1; + + if ((flag = pc_additem((struct map_session_data*)sd, + &item_tmp, get_count))) + clif_additem((struct map_session_data*)sd, 0, 0, flag); + } + //Logs (A)dmins items [Lupus] + if(log_config.enable_logs&0x400) + log_pick_pc(sd, "A", item_tmp.nameid, number, &item_tmp); + + } +} +/*========================================== + * #item command (usage: #item ) + * by MC Cameri + *------------------------------------------ + */ +int charcommand_item( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char item_name[100]; + char character[NAME_LENGTH]; + struct map_session_data *pl_sd; + int number = 0, item_id, flag; + struct item item_tmp; + struct item_data *item_data; + int get_count, i, pet_id; + char tmp_cmdoutput[1024]; + nullpo_retr(-1, sd); + + malloc_tsetdword(item_name, '\0', sizeof(item_name)); + + if (!message || !*message || sscanf(message, "%99s %d %23[^\n]", item_name, &number, character) < 3) { + clif_displaymessage(fd, "Please, enter an item name/id (usage: #item )."); + return -1; + } + + if (number <= 0) + number = 1; + + item_id = 0; + if ((item_data = itemdb_searchname(item_name)) != NULL || + (item_data = itemdb_exists(atoi(item_name))) != NULL) + item_id = item_data->nameid; + + if (item_id >= 500) { + get_count = number; + // check pet egg + pet_id = search_petDB_index(item_id, PET_EGG); + if (item_data->type == 4 || item_data->type == 5 || + item_data->type == 7 || item_data->type == 8) { + get_count = 1; + } + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level + for (i = 0; i < number; i += get_count) { + // if pet egg + if (pet_id >= 0) { + pl_sd->catch_target_class = pet_db[pet_id].class_; + intif_create_pet(pl_sd->status.account_id, pl_sd->status.char_id, + (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv, + (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate, + 100, 0, 1, pet_db[pet_id].jname); + // if not pet egg + } else { + malloc_set(&item_tmp, 0, sizeof(item_tmp)); + item_tmp.nameid = item_id; + item_tmp.identify = 1; + + if ((flag = pc_additem(pl_sd, &item_tmp, get_count))) + clif_additem(pl_sd, 0, 0, flag); + } + } + + //Logs (A)dmins items [Lupus] + if(log_config.enable_logs&0x400) + log_pick_pc(sd, "A", item_tmp.nameid, number, &item_tmp); + + clif_displaymessage(fd, msg_table[18]); // Item created. + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else if(/* from jA's @giveitem */strcmpi(character,"all")==0 || strcmpi(character,"everyone")==0){ + struct map_session_data **pl_allsd; + int users; + pl_allsd = map_getallusers(&users); + for (i = 0; i < users; i++) { + if ((pl_sd = pl_allsd[i])) { + charcommand_giveitem_sub(pl_sd,item_data,number); + snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "You got %s %d.", item_name,number); + clif_displaymessage(pl_sd->fd, tmp_cmdoutput); + } + } + snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "%s received %s %d.","Everyone",item_name,number); + clif_displaymessage(fd, tmp_cmdoutput); + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name. + return -1; + } + + return 0; +} + +/*========================================== + * #warp/#rura/#rura+ + *------------------------------------------ + */ +int charcommand_warp( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char map_name[MAP_NAME_LENGTH]; + char character[NAME_LENGTH]; + int x = 0, y = 0; + struct map_session_data *pl_sd; + int m; + + nullpo_retr(-1, sd); + + malloc_tsetdword(map_name, '\0', sizeof(map_name)); + malloc_tsetdword(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4) { + clif_displaymessage(fd, "Usage: #warp/#rura/#rura+ "); + return -1; + } + + if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat) + strcat(map_name, ".gat"); + + if ((pl_sd = map_nick2sd(character)) == NULL) { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + if (pc_isGM(sd) < pc_isGM(pl_sd)) { // you can rura+ only lower or same GM level + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + m = map_mapname2mapid(map_name); + if (m < 0) { + clif_displaymessage(fd, msg_table[1]); // Map not found. + return -1; + } + if ((x || y) && map_getcell(m, x, y, CELL_CHKNOREACH)) { + clif_displaymessage(fd, msg_table[2]); // Coordinates out of range. + x = y = 0; + } + if (map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp someone to this map."); + return -1; + } + if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp this player from its actual map."); + return -1; + } + if (pc_setpos(pl_sd, map[m].index, x, y, 3) == 0) { + clif_displaymessage(pl_sd->fd, msg_table[0]); // Warped. + clif_displaymessage(fd, msg_table[15]); // Player warped (message sends to player too). + return 0; + } + //No error message specified...? + return -1; +} + +/*========================================== + * #zeny + *------------------------------------------ + */ +int charcommand_zeny( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char character[NAME_LENGTH]; + int zeny = 0, new_zeny; + nullpo_retr(-1, sd); + + malloc_tsetdword(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%d %23[^\n]", &zeny, character) < 2 || zeny == 0) { + clif_displaymessage(fd, "Please, enter a number and a player name (usage: #zeny )."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + new_zeny = pl_sd->status.zeny + zeny; + if (zeny > 0 && (zeny > MAX_ZENY || new_zeny > MAX_ZENY)) // fix positiv overflow + new_zeny = MAX_ZENY; + else if (zeny < 0 && (zeny < -MAX_ZENY || new_zeny < 0)) // fix negativ overflow + new_zeny = 0; + if (new_zeny != pl_sd->status.zeny) { + pl_sd->status.zeny = new_zeny; + clif_updatestatus(pl_sd, SP_ZENY); + clif_displaymessage(fd, msg_table[211]); // Character's number of zenys changed! + } else { + if (zeny < 0) + clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. + else + clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * #fakename + *------------------------------------------ + */ + +int charcommand_fakename( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char name[NAME_LENGTH]; + char char_name[NAME_LENGTH]; + + nullpo_retr(-1, sd); + + name[0] = '\0'; //If you don't pass a second word, name is left as garbage, most definitely not a blank name! [Skotlex] + if (!message || !*message || sscanf(message, "%23s %23[^\n]", char_name, name) < 1) { + clif_displaymessage(sd->fd,"Usage: #fakename ."); + clif_displaymessage(sd->fd,"Or: #fakename to disable."); + return 0; + } + + if(!(pl_sd = map_nick2sd(char_name))) { + clif_displaymessage(sd->fd,"Character not found."); + return -1; + } + + if(strlen(name) < 1 || !name) { + if(strlen(pl_sd->fakename) > 1) { + pl_sd->fakename[0]='\0'; + clif_charnameack(0, &pl_sd->bl); + clif_displaymessage(sd->fd,"Returned to real name."); + } else { + clif_displaymessage(sd->fd,"Character does not has a fake name."); + } + return 0; + } + + if(strlen(name) < 2) { + clif_displaymessage(sd->fd,"Fake name must be at least two characters."); + return 0; + } + + memcpy(pl_sd->fakename,name, NAME_LENGTH-1); + clif_charnameack(0, &pl_sd->bl); + clif_displaymessage(sd->fd,"Fake name enabled."); + + return 0; +} + + +/*========================================== + * #baselvl <#> + * Transferred by: Kevin + *------------------------------------------ +*/ +int charcommand_baselevel( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char player[NAME_LENGTH]; + int level = 0, i, status_point=0; + nullpo_retr(-1, sd); + + if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) { + clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #baselvl <#> )."); + return -1; + } + + if ((pl_sd = map_nick2sd(player)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change base level only lower or same gm level + + if (level > 0) { + if (pl_sd->status.base_level == pc_maxbaselv(sd)) { // check for max level by Valaris + clif_displaymessage(fd, msg_table[91]); // Character's base level can't go any higher. + return 0; + } // End Addition + if ((unsigned int)level > pc_maxbaselv(pl_sd) || + pl_sd->status.base_level > pc_maxbaselv(pl_sd) -level) + level = pc_maxbaselv(pl_sd) - pl_sd->status.base_level; + for (i = 1; i <= level; i++) + status_point += (pl_sd->status.base_level + i + 14) / 5; + if (pl_sd->status.status_point > USHRT_MAX - status_point) + pl_sd->status.status_point = USHRT_MAX; + else + pl_sd->status.status_point += status_point; + pl_sd->status.base_level += (unsigned int)level; + clif_updatestatus(pl_sd, SP_BASELEVEL); + clif_updatestatus(pl_sd, SP_NEXTBASEEXP); + clif_updatestatus(pl_sd, SP_STATUSPOINT); + status_calc_pc(pl_sd, 0); + status_percent_heal(&pl_sd->bl, 100, 100); + clif_misceffect(&pl_sd->bl, 0); + clif_displaymessage(fd, msg_table[65]); // Character's base level raised. + } else { + if (pl_sd->status.base_level == 1) { + clif_displaymessage(fd, msg_table[193]); // Character's base level can't go any lower. + return -1; + } + level *= -1; + if ((unsigned int)level >= pl_sd->status.base_level) + level = pl_sd->status.base_level -1; + if (pl_sd->status.status_point > 0) { + for (i = 0; i > -level; i--) + status_point += (pl_sd->status.base_level +i + 14) / 5; + if (pl_sd->status.status_point < status_point) + pc_resetstate(pl_sd); + if (pl_sd->status.status_point < status_point) + pl_sd->status.status_point = 0; + else + pl_sd->status.status_point -= status_point; + clif_updatestatus(pl_sd, SP_STATUSPOINT); + } // to add: remove status points from stats + pl_sd->status.base_level -= (unsigned int)level; + clif_updatestatus(pl_sd, SP_BASELEVEL); + clif_updatestatus(pl_sd, SP_NEXTBASEEXP); + status_calc_pc(pl_sd, 0); + clif_displaymessage(fd, msg_table[66]); // Character's base level lowered. + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; //正常終了 +} + +/*========================================== + * #jlvl <#> + * Transferred by: Kevin + *------------------------------------------ + */ +int charcommand_joblevel( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char player[NAME_LENGTH]; + int level = 0; + //転生や養子の場合の元の職業を算出する + nullpo_retr(-1, sd); + + if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) { + clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #joblvl <#> )."); + return -1; + } + + if ((pl_sd = map_nick2sd(player)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job level only lower or same gm level + if (level > 0) { + if (pl_sd->status.job_level == pc_maxjoblv(pl_sd)) { + clif_displaymessage(fd, msg_table[67]); // Character's job level can't go any higher. + return -1; + } + if ((unsigned int)level > pc_maxjoblv(pl_sd) || + pl_sd->status.job_level > pc_maxjoblv(pl_sd) -level) + level = pc_maxjoblv(pl_sd) - pl_sd->status.job_level; + pl_sd->status.job_level += (unsigned int)level; + clif_updatestatus(pl_sd, SP_JOBLEVEL); + clif_updatestatus(pl_sd, SP_NEXTJOBEXP); + + if (pl_sd->status.skill_point > USHRT_MAX - level) + pl_sd->status.skill_point = USHRT_MAX; + else + pl_sd->status.skill_point += level; + clif_updatestatus(pl_sd, SP_SKILLPOINT); + status_calc_pc(pl_sd, 0); + clif_misceffect(&pl_sd->bl, 1); + clif_displaymessage(fd, msg_table[68]); // character's job level raised. + } else { + if (pl_sd->status.job_level == 1) { + clif_displaymessage(fd, msg_table[194]); // Character's job level can't go any lower. + return -1; + } + level*=-1; + if ((unsigned int)level >= pl_sd->status.job_level) + level = pl_sd->status.job_level-1; + pl_sd->status.job_level -= (unsigned int)level; + clif_updatestatus(pl_sd, SP_JOBLEVEL); + clif_updatestatus(pl_sd, SP_NEXTJOBEXP); + if (pl_sd->status.skill_point < level) + pc_resetskill(pl_sd, 0); //Need more skill points to substract + if (pl_sd->status.skill_point < level) + pl_sd->status.skill_point = 0; + else + pl_sd->status.skill_point -= level; + clif_updatestatus(pl_sd, SP_SKILLPOINT); + status_calc_pc(pl_sd, 0); + clif_displaymessage(fd, msg_table[69]); // Character's job level lowered. + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + + +/*========================================== + * #questskill + * Transferred by: Kevin + *------------------------------------------ + */ +int charcommand_questskill( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char player[NAME_LENGTH]; + int skill_id = 0; + nullpo_retr(-1, sd); + + if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) { + clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: #questskill <#:0+> )."); + return -1; + } + + if (skill_id >= 0 && skill_id < MAX_SKILL_DB) { + if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) { + if ((pl_sd = map_nick2sd(player)) != NULL) { + if (pc_checkskill(pl_sd, skill_id) == 0) { + pc_skill(pl_sd, skill_id, 1, 0); + clif_displaymessage(fd, msg_table[199]); // This player has learned the skill. + } else { + clif_displaymessage(fd, msg_table[200]); // This player already has this quest skill. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist. + return -1; + } + + return 0; +} + + +/*========================================== + * #lostskill + * Transferred by: Kevin + *------------------------------------------ + */ +int charcommand_lostskill( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char player[NAME_LENGTH]; + int skill_id = 0; + nullpo_retr(-1, sd); + + if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) { + clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: @charlostskill <#:0+> )."); + return -1; + } + + if (skill_id >= 0 && skill_id < MAX_SKILL) { + if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) { + if ((pl_sd = map_nick2sd(player)) != NULL) { + if (pc_checkskill(pl_sd, skill_id) > 0) { + pl_sd->status.skill[skill_id].lv = 0; + pl_sd->status.skill[skill_id].flag = 0; + clif_skillinfoblock(pl_sd); + clif_displaymessage(fd, msg_table[202]); // This player has forgotten the skill. + } else { + clif_displaymessage(fd, msg_table[203]); // This player doesn't have this quest skill. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist. + return -1; + } + + return 0; +} + +/*========================================== + * Character Skill Reset + *------------------------------------------ + */ +int charcommand_skreset( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char player[NAME_LENGTH]; + char tmp_cmdoutput[1024]; + nullpo_retr(-1, sd); + + if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charskreset )."); + return -1; + } + + if ((pl_sd = map_nick2sd(player)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset skill points only lower or same gm level + pc_resetskill(pl_sd,1); + sprintf(tmp_cmdoutput, msg_table[206], player); // '%s' skill points reseted! + clif_displaymessage(fd, tmp_cmdoutput); + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Character Stat Reset + *------------------------------------------ + */ +int charcommand_streset( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char player[NAME_LENGTH]; + char tmp_cmdoutput[1024]; + nullpo_retr(-1, sd); + + if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charstreset )."); + return -1; + } + + if ((pl_sd = map_nick2sd(player)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset stats points only lower or same gm level + pc_resetstate(pl_sd); + sprintf(tmp_cmdoutput, msg_table[207], player); // '%s' stats points reseted! + clif_displaymessage(fd, tmp_cmdoutput); + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Character Model by chbrules + *------------------------------------------ + */ +int charcommand_model( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int hair_style = 0, hair_color = 0, cloth_color = 0; + struct map_session_data *pl_sd; + char player[NAME_LENGTH]; + char tmp_cmdoutput[1024]; + nullpo_retr(-1, sd); + + if (!message || !*message || sscanf(message, "%d %d %d %23[^\n]", &hair_style, &hair_color, &cloth_color, player) < 4 || hair_style < 0 || hair_color < 0 || cloth_color < 0) { + sprintf(tmp_cmdoutput, "Please, enter a valid model and a player name (usage: @charmodel ).", + MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR); + clif_displaymessage(fd, tmp_cmdoutput); + return -1; + } + + if ((pl_sd = map_nick2sd(player)) != NULL) { + if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE && + hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR && + cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) { + /* Removed this check for being too strange. [Skotlex] + if (cloth_color != 0 && + pl_sd->status.sex == 1 && + (pl_sd->status.class_ == JOB_ASSASSIN || pl_sd->status.class_ == JOB_ROGUE)) { + clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class. + return -1; + } else { + */ + pc_changelook(pl_sd, LOOK_HAIR, hair_style); + pc_changelook(pl_sd, LOOK_HAIR_COLOR, hair_color); + pc_changelook(pl_sd, LOOK_CLOTHES_COLOR, cloth_color); + clif_displaymessage(fd, msg_table[36]); // Appearence changed. +// } + } else { + clif_displaymessage(fd, msg_table[37]); // An invalid number was specified. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Character Skill Point (Rewritten by [Yor]) + *------------------------------------------ + */ +int charcommand_skpoint( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char player[NAME_LENGTH]; + int new_skill_point; + int point = 0; + nullpo_retr(-1, sd); + + if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) { + clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charskpoint )."); + return -1; + } + + if ((pl_sd = map_nick2sd(player)) != NULL) { + if (point > 0 && pl_sd->status.skill_point > USHRT_MAX - point) + new_skill_point = USHRT_MAX; + else if (point < 0 && pl_sd->status.skill_point < -point) + new_skill_point = 0; + else + new_skill_point = pl_sd->status.skill_point + point; + if (new_skill_point != (int)pl_sd->status.skill_point) { + pl_sd->status.skill_point = new_skill_point; + clif_updatestatus(pl_sd, SP_SKILLPOINT); + clif_displaymessage(fd, msg_table[209]); // Character's number of skill points changed! + } else { + if (point < 0) + clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. + else + clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Character Status Point (rewritten by [Yor]) + *------------------------------------------ + */ +int charcommand_stpoint( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char player[NAME_LENGTH]; + int new_status_point; + int point = 0; + nullpo_retr(-1, sd); + + if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) { + clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charstpoint )."); + return -1; + } + + if ((pl_sd = map_nick2sd(player)) != NULL) { + if (point > 0 && pl_sd->status.status_point > USHRT_MAX - point) + new_status_point = USHRT_MAX; + else if (point < 0 && pl_sd->status.status_point < -point) + new_status_point = 0; + else + new_status_point = pl_sd->status.status_point + point; + if (new_status_point != (int)pl_sd->status.status_point) { + pl_sd->status.status_point = new_status_point; + clif_updatestatus(pl_sd, SP_STATUSPOINT); + clif_displaymessage(fd, msg_table[210]); // Character's number of status points changed! + } else { + if (point < 0) + clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. + else + clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * charchangesex command (usage: charchangesex ) + *------------------------------------------ + */ +int charcommand_changesex( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char player[NAME_LENGTH]; + nullpo_retr(-1, sd); + + if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charchangesex )."); + return -1; + } + + // check player name + if (strlen(player) < 4) { + clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. + return -1; + } else if (strlen(player) > 23) { + clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum. + return -1; + } else { + chrif_char_ask_name(sd->status.account_id, player, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex + clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it. + } + + return 0; +} + +/*========================================== + * Feel (SG save map) Reset + *------------------------------------------ + */ +int charcommand_feelreset( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[NAME_LENGTH]; + char output[200]; + struct map_session_data *pl_sd; + + malloc_tsetdword(character, '\0', sizeof(character)); + malloc_tsetdword(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: #feelreset )."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level + pc_resetfeel(pl_sd); + sprintf(output, msg_table[267], character); // '%s' designated maps reseted! + clif_displaymessage(fd, output); + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * #help - Char commands [Kayla] + *------------------------------------------ + */ +int charcommand_help( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char buf[2048], w1[2048], w2[2048]; + int i, gm_level; + FILE* fp; + nullpo_retr(-1, sd); + + malloc_tsetdword(buf, '\0', sizeof(buf)); + + if ((fp = fopen(charhelp_txt, "r")) != NULL) { + clif_displaymessage(fd, msg_table[26]); /* Help commands: */ + gm_level = pc_isGM(sd); + while(fgets(buf, sizeof(buf) - 1, fp) != NULL) { + if (buf[0] == '/' && buf[1] == '/') + continue; + for (i = 0; buf[i] != '\0'; i++) { + if (buf[i] == '\r' || buf[i] == '\n') { + buf[i] = '\0'; + break; + } + } + if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2) + clif_displaymessage(fd, buf); + else if (gm_level >= atoi(w1)) + clif_displaymessage(fd, w2); + } + fclose(fp); + } else { + clif_displaymessage(fd, msg_table[27]); /* File help.txt not found. */ + return -1; + } + + return 0; +} diff --git a/src/map/charcommand.h b/src/map/charcommand.h index 2f75f75c5..54215257e 100644 --- a/src/map/charcommand.h +++ b/src/map/charcommand.h @@ -1,74 +1,74 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _CHARCOMMAND_H_ -#define _CHARCOMMAND_H_ - -enum CharCommandType { - CharCommand_None = -1, - CharCommandJobChange, - CharCommandPetRename, - CharCommandPetFriendly, - CharCommandReset, - CharCommandStats, - CharCommandOption, - CharCommandSave, - CharCommandStatsAll, - CharCommandSpiritball, - CharCommandItemList, - CharCommandEffect, - CharCommandStorageList, - CharCommandItem, // by MC Cameri - CharCommandWarp, - CharCommandZeny, - CharCommandFakeName, - CharCommandBaseLevel, - CharCommandJobLevel, - CharCommandQuestSkill, - CharCommandLostSkill, - CharCommandSkReset, - CharCommandStReset, - CharCommandModel, - CharCommandSKPoint, - CharCommandSTPoint, - CharCommandChangeSex, - CharCommandFeelReset, // Komurka - CharCommandHelp, - -#ifdef TXT_ONLY -/* TXT_ONLY */ - -/* TXT_ONLY */ -#else -/* SQL-only */ - -/* SQL Only */ -#endif - - // End. No more commans after this line. - CharCommand_Unknown, - CharCommand_MAX -}; - -typedef enum CharCommandType CharCommandType; -typedef struct CharCommandInfo { - CharCommandType type; - const char* command; - int level; - int (*proc)(const int, struct map_session_data*, - const char* command, const char* message); -} CharCommandInfo; - -CharCommandType -is_charcommand(const int fd, struct map_session_data* sd, const char* message); -CharCommandType -charcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl); - -CharCommandType charcommand( - struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info); -int get_charcommand_level(const CharCommandType type); - -int charcommand_config_read(const char *cfgName); - -#endif - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _CHARCOMMAND_H_ +#define _CHARCOMMAND_H_ + +enum CharCommandType { + CharCommand_None = -1, + CharCommandJobChange, + CharCommandPetRename, + CharCommandPetFriendly, + CharCommandReset, + CharCommandStats, + CharCommandOption, + CharCommandSave, + CharCommandStatsAll, + CharCommandSpiritball, + CharCommandItemList, + CharCommandEffect, + CharCommandStorageList, + CharCommandItem, // by MC Cameri + CharCommandWarp, + CharCommandZeny, + CharCommandFakeName, + CharCommandBaseLevel, + CharCommandJobLevel, + CharCommandQuestSkill, + CharCommandLostSkill, + CharCommandSkReset, + CharCommandStReset, + CharCommandModel, + CharCommandSKPoint, + CharCommandSTPoint, + CharCommandChangeSex, + CharCommandFeelReset, // Komurka + CharCommandHelp, + +#ifdef TXT_ONLY +/* TXT_ONLY */ + +/* TXT_ONLY */ +#else +/* SQL-only */ + +/* SQL Only */ +#endif + + // End. No more commans after this line. + CharCommand_Unknown, + CharCommand_MAX +}; + +typedef enum CharCommandType CharCommandType; +typedef struct CharCommandInfo { + CharCommandType type; + const char* command; + int level; + int (*proc)(const int, struct map_session_data*, + const char* command, const char* message); +} CharCommandInfo; + +CharCommandType +is_charcommand(const int fd, struct map_session_data* sd, const char* message); +CharCommandType +charcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl); + +CharCommandType charcommand( + struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info); +int get_charcommand_level(const CharCommandType type); + +int charcommand_config_read(const char *cfgName); + +#endif + diff --git a/src/map/charsave.c b/src/map/charsave.c index 1be249e5a..abcd7d152 100644 --- a/src/map/charsave.c +++ b/src/map/charsave.c @@ -1,523 +1,523 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include - -#include "../common/core.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/mmo.h" -#include "../common/strlib.h" -#include "../common/showmsg.h" -#include "../common/malloc.h" - -#include "charsave.h" -#include "map.h" - -#ifndef TXT_ONLY - -struct mmo_charstatus *charsave_loadchar(int charid){ - int i,j, friends; - struct mmo_charstatus *c; - char *str_p; - double exp; - friends = 0; - - c = (struct mmo_charstatus *)aCalloc(1,sizeof(struct mmo_charstatus)); - - if(charid <= 0){ - ShowError("charsave_loadchar() charid <= 0! (%d)", charid); - aFree(c); - return NULL; - } - // add homun_id [albator] - //Tested, Mysql 4.1.9+ has no problems with the long query, the buf is 65k big and the sql server needs for it 0.00009 secs on an athlon xp 2400+ WinXP (1GB Mem) .. [Sirius] - sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, `str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`, `clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, `last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame`, `homun_id` FROM `char` WHERE `char_id` = '%d'", charid); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(c); - return NULL; - } - - charsql_res = mysql_store_result(&charsql_handle); - if(mysql_num_rows(charsql_res) <= 0){ - ShowWarning("charsave_loadchar() -> CHARACTER NOT FOUND! (id: %d)\n", charid); - mysql_free_result(charsql_res); - aFree(c); - return NULL; - } - - //fetch data - charsql_row = mysql_fetch_row(charsql_res); - - //fill with data - c->char_id = charid; - c->account_id = atoi(charsql_row[1]); - c->char_num = atoi(charsql_row[2]); - strcpy(c->name, charsql_row[3]); - c->class_ = atoi(charsql_row[4]); - c->base_level = atoi(charsql_row[5]); - c->job_level = atoi(charsql_row[6]); - exp = atof(charsql_row[7]); - c->base_exp = (unsigned int)cap_value(exp,0,UINT_MAX); - exp = atof(charsql_row[8]); - c->job_exp = (unsigned int)cap_value(exp,0,UINT_MAX); - c->zeny = atoi(charsql_row[9]); - c->str = atoi(charsql_row[10]); - c->agi = atoi(charsql_row[11]); - c->vit = atoi(charsql_row[12]); - c->int_ = atoi(charsql_row[13]); - c->dex = atoi(charsql_row[14]); - c->luk = atoi(charsql_row[15]); - c->max_hp = atoi(charsql_row[16]); - c->hp = atoi(charsql_row[17]); - c->max_sp = atoi(charsql_row[18]); - c->sp = atoi(charsql_row[19]); - c->status_point = atoi(charsql_row[20]) > USHRT_MAX? USHRT_MAX : atoi(charsql_row[20]); - c->skill_point = atoi(charsql_row[21]) > USHRT_MAX? USHRT_MAX : atoi(charsql_row[21]); - c->option = atoi(charsql_row[22]); - c->karma = atoi(charsql_row[23]); - c->manner = atoi(charsql_row[24]); - c->party_id = atoi(charsql_row[25]); - c->guild_id = atoi(charsql_row[26]); - c->pet_id = atoi(charsql_row[27]); - c->hair = atoi(charsql_row[28]); - c->hair_color = atoi(charsql_row[29]); - c->clothes_color = atoi(charsql_row[30]); - c->weapon = atoi(charsql_row[31]); - c->shield = atoi(charsql_row[32]); - c->head_top = atoi(charsql_row[33]); - c->head_mid = atoi(charsql_row[34]); - c->head_bottom = atoi(charsql_row[35]); - c->last_point.map = mapindex_name2id(charsql_row[36]); - c->last_point.x = atoi(charsql_row[37]); - c->last_point.y = atoi(charsql_row[38]); - c->save_point.map = mapindex_name2id(charsql_row[39]); - c->save_point.x = atoi(charsql_row[40]); - c->save_point.y = atoi(charsql_row[41]); - c->partner_id = atoi(charsql_row[42]); - c->father = atoi(charsql_row[43]); - c->mother = atoi(charsql_row[44]); - c->child = atoi(charsql_row[45]); - c->fame = atoi(charsql_row[46]); - c->hom_id = atoi(charsql_row[47]); // albator - - mysql_free_result(charsql_res); - - //Check for '0' Savepoint / LastPoint - if (c->last_point.x == 0 || c->last_point.y == 0 || c->last_point.map == 0){ - c->last_point.map = mapindex_name2id(MAP_PRONTERA); - c->last_point.x = 100; - c->last_point.y = 100; - } - - if (c->save_point.x == 0 || c->save_point.y == 0 || c->save_point.map == 0){ - c->save_point.map = mapindex_name2id(MAP_PRONTERA); - c->save_point.x = 100; - c->save_point.y = 100; - } - - - //read the memo points - sprintf(tmp_sql, "SELECT `memo_id`, `char_id`, `map`, `x`, `y` FROM `memo` WHERE `char_id` = '%d' ORDER BY `memo_id`", charid); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(c); - return NULL; - } - - charsql_res = mysql_store_result(&charsql_handle); - if(charsql_res){ - for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){ - c->memo_point[i].map = mapindex_name2id(charsql_row[2]); - c->memo_point[i].x = atoi(charsql_row[3]); - c->memo_point[i].y = atoi(charsql_row[4]); - } - mysql_free_result(charsql_res); - } - - //read inventory... - str_p = tmp_sql; - str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); - for (i = 0; i < MAX_SLOTS; i++) - str_p += sprintf(str_p, ", `card%d`", i); - str_p += sprintf(str_p, " FROM `inventory` WHERE `char_id` = '%d'", charid); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(c); - return NULL; - } - - charsql_res = mysql_store_result(&charsql_handle); - if(charsql_res){ - for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){ - //c->inventory[i].id = atoi(charsql_row[0]); - c->inventory[i].nameid = atoi(charsql_row[0]); - c->inventory[i].amount = atoi(charsql_row[1]); - c->inventory[i].equip = atoi(charsql_row[2]); - c->inventory[i].identify = atoi(charsql_row[3]); - c->inventory[i].refine = atoi(charsql_row[4]); - c->inventory[i].attribute = atoi(charsql_row[5]); - for (j = 0; j < MAX_SLOTS; j++) - c->inventory[i].card[j] = atoi(charsql_row[6+j]); - } - mysql_free_result(charsql_res); - } - - - //cart inventory .. - str_p = tmp_sql; - str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); - for (i = 0; i < MAX_SLOTS; i++) - str_p += sprintf(str_p, ", `card%d`", i); - str_p += sprintf(str_p, " FROM `cart_inventory` WHERE `char_id` = '%d'", charid); - - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(c); - return NULL; - } - - charsql_res = mysql_store_result(&charsql_handle); - if(charsql_res){ - for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){ - //c->cart[i].id = atoi(charsql_row[0]); - c->cart[i].nameid = atoi(charsql_row[0]); - c->cart[i].amount = atoi(charsql_row[1]); - c->cart[i].equip = atoi(charsql_row[2]); - c->cart[i].identify = atoi(charsql_row[3]); - c->cart[i].refine = atoi(charsql_row[4]); - c->cart[i].attribute = atoi(charsql_row[5]); - for (j = 0; j < MAX_SLOTS; j++) - c->cart[i].card[j] = atoi(charsql_row[6+j]); - } - mysql_free_result(charsql_res); - } - - - //Skills... - sprintf(tmp_sql, "SELECT `char_id`, `id`, `lv` FROM `skill` WHERE `char_id` = '%d'", charid); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(c); - return NULL; - } - - charsql_res = mysql_store_result(&charsql_handle); - if(charsql_res){ - while((charsql_row = mysql_fetch_row(charsql_res))){ - i = atoi(charsql_row[1]); - c->skill[i].id = i; - c->skill[i].lv = atoi(charsql_row[2]); - } - mysql_free_result(charsql_res); - } -/* Reg values are handled by the char server. - //Global REG - sprintf(tmp_sql, "SELECT `char_id`, `str`, `value` FROM `global_reg_value` WHERE `type` = '3' AND `char_id` = '%d'", charid); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - aFree(c); - return NULL; - } - - charsql_res = mysql_store_result(&charsql_handle); - if(charsql_res){ - for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){ - strcpy(c->global_reg[i].str, charsql_row[1]); - strcpy(c->global_reg[i].value, charsql_row[2]); - } - mysql_free_result(charsql_res); - c->global_reg_num = i; - } -*/ - - //Shamelessly stolen from its_sparky (ie: thanks) and then assimilated by [Skotlex] - //Friend list - sprintf(tmp_sql, "SELECT f.friend_account, f.friend_id, c.name FROM friends f LEFT JOIN `char` c ON f.friend_account=c.account_id AND f.friend_id=c.char_id WHERE f.char_id='%d'", charid); - - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - sql_res = NULL; //To avoid trying to read data. - } - else - sql_res = mysql_store_result(&charsql_handle); - - if(sql_res) - { - for(i = 0; (sql_row = mysql_fetch_row(sql_res)) && ifriends[i].account_id = atoi(sql_row[0]); - c->friends[i].char_id = atoi(sql_row[1]); - strncpy(c->friends[i].name, sql_row[2], NAME_LENGTH-1); //The -1 is to avoid losing the ending \0 [Skotlex] - } - } - mysql_free_result(sql_res); - } - - ShowInfo("charsql_loadchar(): loading of '%d' (%s) complete.\n", charid, c->name); - return c; -} - -int charsave_savechar(int charid, struct mmo_charstatus *c){ - int i,j; - char *str_p; -// char tmp_str[64]; -// char tmp_str2[512]; - //First save the 'char' - sprintf(tmp_sql ,"UPDATE `char` SET `class`='%d', `base_level`='%d', `job_level`='%d'," - "`base_exp`='%u', `job_exp`='%u', `zeny`='%d'," - "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d'," - "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d'," - "`option`='%d',`karma`='%d',`manner`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d'," - "`hair`='%d',`hair_color`='%d',`clothes_color`='%d',`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," - "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d'," - "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d', `fame`='%d', `homun_id`='%d'" - "WHERE `account_id`='%d' AND `char_id` = '%d'", - c->class_, c->base_level, c->job_level, - c->base_exp, c->job_exp, c->zeny, - c->max_hp, c->hp, c->max_sp, c->sp, c->status_point, c->skill_point, - c->str, c->agi, c->vit, c->int_, c->dex, c->luk, - c->option, c->karma, c->manner, c->party_id, c->guild_id, c->pet_id, - c->hair, c->hair_color, c->clothes_color, - c->weapon, c->shield, c->head_top, c->head_mid, c->head_bottom, - mapindex_id2name(c->last_point.map), c->last_point.x, c->last_point.y, - mapindex_id2name(c->save_point.map), c->save_point.x, c->save_point.y, c->partner_id, c->father, c->mother, - c->child, c->fame, c->hom_id, c->account_id, c->char_id - ); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - - //Save the inventory - sprintf(tmp_sql, "DELETE FROM `inventory` WHERE `char_id` = '%d'", charid); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - for(i = 0; i < MAX_INVENTORY; i++){ - if(c->inventory[i].nameid > 0){ - str_p = tmp_sql; - str_p += sprintf(str_p, "INSERT INTO `inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); - for (j = 0; j < MAX_SLOTS; j++) - str_p += sprintf(str_p, ", `card%d`", j); - - str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'", - charid, c->inventory[i].nameid, c->inventory[i].amount, c->inventory[i].equip, - c->inventory[i].identify, c->inventory[i].refine, c->inventory[i].attribute); - - for (j = 0; j < MAX_SLOTS; j++) - str_p += sprintf(str_p, ", '%d'", c->inventory[i].card[j]); - - strcat(tmp_sql,")"); - - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - } - - //Save the cart - sprintf(tmp_sql, "DELETE FROM `cart_inventory` WHERE `char_id` = '%d'", charid); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - for(i = 0; i < MAX_CART; i++){ - if(c->cart[i].nameid > 0){ - str_p = tmp_sql; - str_p += sprintf(str_p, "INSERT INTO `cart_inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); - for (j = 0; j < MAX_SLOTS; j++) - str_p += sprintf(str_p, ", `card%d`", j); - - str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'", - charid, c->cart[i].nameid, c->cart[i].amount, c->cart[i].equip, - c->cart[i].identify, c->cart[i].refine, c->cart[i].attribute); - - for (j = 0; j < MAX_SLOTS; j++) - str_p += sprintf(str_p, ", '%d'", c->cart[i].card[j]); - - strcat(tmp_sql,")"); - - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - } - - - //Save memo points - sprintf(tmp_sql, "DELETE FROM `memo` WHERE `char_id` = '%d'", charid); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - for(i = 0; i < MAX_MEMOPOINTS; i++){ - if(c->memo_point[i].map && c->memo_point[i].x > 0 && c->memo_point[i].y > 0){ - sprintf(tmp_sql, "INSERT INTO `memo` ( `char_id`, `map`, `x`, `y` ) VALUES ('%d', '%s', '%d', '%d')", charid, mapindex_id2name(c->memo_point[i].map), c->memo_point[i].x, c->memo_point[i].y); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - } - - - //Save skills - sprintf(tmp_sql, "DELETE FROM `skill` WHERE `char_id` = '%d'", charid); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - for(i = 0; i < MAX_SKILL; i++){ - if(c->skill[i].id > 0){ - sprintf(tmp_sql, "INSERT INTO `skill` (`char_id`, `id`, `lv`) VALUES ('%d', '%d', '%d')", charid, c->skill[i].id, c->skill[i].lv); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - } - - -/* Reg values are handled by the char server. - //global_reg_value saving - sprintf(tmp_sql, "DELETE FROM `global_reg_value` WHERE `type`=3 AND `char_id` = '%d'", charid); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - for(i = 0; i < c->global_reg_num; i++){ - if(c->global_reg[i].str){ - if(c->global_reg[i].value){ - //jstrescapecpy(tmp_str, c->global_reg[i].str); - sprintf(tmp_sql, "INSERT INTO `global_reg_value` (`char_id`, `str`, `value`) VALUES ('%d', '%s', '%s')", charid, jstrescapecpy(tmp_str,c->global_reg[i].str), jstrescapecpy(tmp_str2,c->global_reg[i].value)); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - } - } -*/ - - //friendlist saving - sprintf(tmp_sql, "DELETE FROM `friends` WHERE `char_id` = '%d'", charid); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - for(i = 0; i < MAX_FRIENDS; i++){ - if(c->friends[i].char_id > 0){ - sprintf(tmp_sql, "INSERT INTO `friends` (`char_id`, `friend_account`, `friend_id`) VALUES ('%d','%d','%d')", charid, c->friends[i].account_id, c->friends[i].char_id); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - } - - ShowInfo("charsql_savechar(): saving of '%d' (%s) complete.\n", charid, c->name); - return 0; -} - -int charsave_load_scdata(int account_id, int char_id) -{ //Loads character's sc_data - struct map_session_data *sd; - - sd = map_id2sd(account_id); - if (!sd) - { - ShowError("charsave_load_scdata: Player of AID %d not found!\n", account_id); - return -1; - } - if (sd->status.char_id != char_id) - { - ShowError("charsave_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", account_id, sd->status.char_id, char_id); - return -1; - } - sprintf(tmp_sql, "SELECT `type`, `tick`, `val1`, `val2`, `val3`, `val4` FROM `sc_data`" - "WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id); - - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return -1; - } - - sql_res = mysql_store_result(&charsql_handle); - if(sql_res) - { - while ((sql_row = mysql_fetch_row(sql_res))) - { - if (atoi(sql_row[1]) < 1) - { //Protection against invalid tick values. [Skotlex] - ShowWarning("charsave_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", atoi(sql_row[1]), sd->status.name); - continue; - } - - status_change_start(&sd->bl, atoi(sql_row[0]), 10000, atoi(sql_row[2]), atoi(sql_row[3]), - atoi(sql_row[4]), atoi(sql_row[5]), atoi(sql_row[1]), 15); - } - } - - //Once loaded, sc_data must be disposed. - sprintf(tmp_sql, "DELETE FROM `sc_data` WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id); - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - return 0; -} - -void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc) -{ //Saves character's sc_data. - int i,count =0; - struct TimerData *timer; - unsigned int tick = gettick(); - char *p = tmp_sql; - - p += sprintf(p, "INSERT INTO `sc_data` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES "); - - for(i = 0; i < max_sc; i++) - { - if (sc_data->data[i].timer == -1) - continue; - timer = get_timer(sc_data->data[i].timer); - if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0) - continue; - - p += sprintf(p, " ('%d','%d','%hu','%d','%d','%d','%d','%d'),", account_id, char_id, - i, DIFF_TICK(timer->tick,tick), sc_data->data[i].val1, sc_data->data[i].val2, sc_data->data[i].val3, sc_data->data[i].val4); - - count++; - } - if (count > 0) - { - *--p = '\0'; //Remove the trailing comma. - if(mysql_query(&charsql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - ShowInfo("charsql_save_scdata(): saved %d status changes of '%d:%d'.\n", count, account_id, char_id); - return; -} -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include + +#include "../common/core.h" +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/nullpo.h" +#include "../common/mmo.h" +#include "../common/strlib.h" +#include "../common/showmsg.h" +#include "../common/malloc.h" + +#include "charsave.h" +#include "map.h" + +#ifndef TXT_ONLY + +struct mmo_charstatus *charsave_loadchar(int charid){ + int i,j, friends; + struct mmo_charstatus *c; + char *str_p; + double exp; + friends = 0; + + c = (struct mmo_charstatus *)aCalloc(1,sizeof(struct mmo_charstatus)); + + if(charid <= 0){ + ShowError("charsave_loadchar() charid <= 0! (%d)", charid); + aFree(c); + return NULL; + } + // add homun_id [albator] + //Tested, Mysql 4.1.9+ has no problems with the long query, the buf is 65k big and the sql server needs for it 0.00009 secs on an athlon xp 2400+ WinXP (1GB Mem) .. [Sirius] + sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, `str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`, `clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, `last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame`, `homun_id` FROM `char` WHERE `char_id` = '%d'", charid); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(c); + return NULL; + } + + charsql_res = mysql_store_result(&charsql_handle); + if(mysql_num_rows(charsql_res) <= 0){ + ShowWarning("charsave_loadchar() -> CHARACTER NOT FOUND! (id: %d)\n", charid); + mysql_free_result(charsql_res); + aFree(c); + return NULL; + } + + //fetch data + charsql_row = mysql_fetch_row(charsql_res); + + //fill with data + c->char_id = charid; + c->account_id = atoi(charsql_row[1]); + c->char_num = atoi(charsql_row[2]); + strcpy(c->name, charsql_row[3]); + c->class_ = atoi(charsql_row[4]); + c->base_level = atoi(charsql_row[5]); + c->job_level = atoi(charsql_row[6]); + exp = atof(charsql_row[7]); + c->base_exp = (unsigned int)cap_value(exp,0,UINT_MAX); + exp = atof(charsql_row[8]); + c->job_exp = (unsigned int)cap_value(exp,0,UINT_MAX); + c->zeny = atoi(charsql_row[9]); + c->str = atoi(charsql_row[10]); + c->agi = atoi(charsql_row[11]); + c->vit = atoi(charsql_row[12]); + c->int_ = atoi(charsql_row[13]); + c->dex = atoi(charsql_row[14]); + c->luk = atoi(charsql_row[15]); + c->max_hp = atoi(charsql_row[16]); + c->hp = atoi(charsql_row[17]); + c->max_sp = atoi(charsql_row[18]); + c->sp = atoi(charsql_row[19]); + c->status_point = atoi(charsql_row[20]) > USHRT_MAX? USHRT_MAX : atoi(charsql_row[20]); + c->skill_point = atoi(charsql_row[21]) > USHRT_MAX? USHRT_MAX : atoi(charsql_row[21]); + c->option = atoi(charsql_row[22]); + c->karma = atoi(charsql_row[23]); + c->manner = atoi(charsql_row[24]); + c->party_id = atoi(charsql_row[25]); + c->guild_id = atoi(charsql_row[26]); + c->pet_id = atoi(charsql_row[27]); + c->hair = atoi(charsql_row[28]); + c->hair_color = atoi(charsql_row[29]); + c->clothes_color = atoi(charsql_row[30]); + c->weapon = atoi(charsql_row[31]); + c->shield = atoi(charsql_row[32]); + c->head_top = atoi(charsql_row[33]); + c->head_mid = atoi(charsql_row[34]); + c->head_bottom = atoi(charsql_row[35]); + c->last_point.map = mapindex_name2id(charsql_row[36]); + c->last_point.x = atoi(charsql_row[37]); + c->last_point.y = atoi(charsql_row[38]); + c->save_point.map = mapindex_name2id(charsql_row[39]); + c->save_point.x = atoi(charsql_row[40]); + c->save_point.y = atoi(charsql_row[41]); + c->partner_id = atoi(charsql_row[42]); + c->father = atoi(charsql_row[43]); + c->mother = atoi(charsql_row[44]); + c->child = atoi(charsql_row[45]); + c->fame = atoi(charsql_row[46]); + c->hom_id = atoi(charsql_row[47]); // albator + + mysql_free_result(charsql_res); + + //Check for '0' Savepoint / LastPoint + if (c->last_point.x == 0 || c->last_point.y == 0 || c->last_point.map == 0){ + c->last_point.map = mapindex_name2id(MAP_PRONTERA); + c->last_point.x = 100; + c->last_point.y = 100; + } + + if (c->save_point.x == 0 || c->save_point.y == 0 || c->save_point.map == 0){ + c->save_point.map = mapindex_name2id(MAP_PRONTERA); + c->save_point.x = 100; + c->save_point.y = 100; + } + + + //read the memo points + sprintf(tmp_sql, "SELECT `memo_id`, `char_id`, `map`, `x`, `y` FROM `memo` WHERE `char_id` = '%d' ORDER BY `memo_id`", charid); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(c); + return NULL; + } + + charsql_res = mysql_store_result(&charsql_handle); + if(charsql_res){ + for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){ + c->memo_point[i].map = mapindex_name2id(charsql_row[2]); + c->memo_point[i].x = atoi(charsql_row[3]); + c->memo_point[i].y = atoi(charsql_row[4]); + } + mysql_free_result(charsql_res); + } + + //read inventory... + str_p = tmp_sql; + str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); + for (i = 0; i < MAX_SLOTS; i++) + str_p += sprintf(str_p, ", `card%d`", i); + str_p += sprintf(str_p, " FROM `inventory` WHERE `char_id` = '%d'", charid); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(c); + return NULL; + } + + charsql_res = mysql_store_result(&charsql_handle); + if(charsql_res){ + for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){ + //c->inventory[i].id = atoi(charsql_row[0]); + c->inventory[i].nameid = atoi(charsql_row[0]); + c->inventory[i].amount = atoi(charsql_row[1]); + c->inventory[i].equip = atoi(charsql_row[2]); + c->inventory[i].identify = atoi(charsql_row[3]); + c->inventory[i].refine = atoi(charsql_row[4]); + c->inventory[i].attribute = atoi(charsql_row[5]); + for (j = 0; j < MAX_SLOTS; j++) + c->inventory[i].card[j] = atoi(charsql_row[6+j]); + } + mysql_free_result(charsql_res); + } + + + //cart inventory .. + str_p = tmp_sql; + str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); + for (i = 0; i < MAX_SLOTS; i++) + str_p += sprintf(str_p, ", `card%d`", i); + str_p += sprintf(str_p, " FROM `cart_inventory` WHERE `char_id` = '%d'", charid); + + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(c); + return NULL; + } + + charsql_res = mysql_store_result(&charsql_handle); + if(charsql_res){ + for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){ + //c->cart[i].id = atoi(charsql_row[0]); + c->cart[i].nameid = atoi(charsql_row[0]); + c->cart[i].amount = atoi(charsql_row[1]); + c->cart[i].equip = atoi(charsql_row[2]); + c->cart[i].identify = atoi(charsql_row[3]); + c->cart[i].refine = atoi(charsql_row[4]); + c->cart[i].attribute = atoi(charsql_row[5]); + for (j = 0; j < MAX_SLOTS; j++) + c->cart[i].card[j] = atoi(charsql_row[6+j]); + } + mysql_free_result(charsql_res); + } + + + //Skills... + sprintf(tmp_sql, "SELECT `char_id`, `id`, `lv` FROM `skill` WHERE `char_id` = '%d'", charid); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(c); + return NULL; + } + + charsql_res = mysql_store_result(&charsql_handle); + if(charsql_res){ + while((charsql_row = mysql_fetch_row(charsql_res))){ + i = atoi(charsql_row[1]); + c->skill[i].id = i; + c->skill[i].lv = atoi(charsql_row[2]); + } + mysql_free_result(charsql_res); + } +/* Reg values are handled by the char server. + //Global REG + sprintf(tmp_sql, "SELECT `char_id`, `str`, `value` FROM `global_reg_value` WHERE `type` = '3' AND `char_id` = '%d'", charid); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + aFree(c); + return NULL; + } + + charsql_res = mysql_store_result(&charsql_handle); + if(charsql_res){ + for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){ + strcpy(c->global_reg[i].str, charsql_row[1]); + strcpy(c->global_reg[i].value, charsql_row[2]); + } + mysql_free_result(charsql_res); + c->global_reg_num = i; + } +*/ + + //Shamelessly stolen from its_sparky (ie: thanks) and then assimilated by [Skotlex] + //Friend list + sprintf(tmp_sql, "SELECT f.friend_account, f.friend_id, c.name FROM friends f LEFT JOIN `char` c ON f.friend_account=c.account_id AND f.friend_id=c.char_id WHERE f.char_id='%d'", charid); + + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + sql_res = NULL; //To avoid trying to read data. + } + else + sql_res = mysql_store_result(&charsql_handle); + + if(sql_res) + { + for(i = 0; (sql_row = mysql_fetch_row(sql_res)) && ifriends[i].account_id = atoi(sql_row[0]); + c->friends[i].char_id = atoi(sql_row[1]); + strncpy(c->friends[i].name, sql_row[2], NAME_LENGTH-1); //The -1 is to avoid losing the ending \0 [Skotlex] + } + } + mysql_free_result(sql_res); + } + + ShowInfo("charsql_loadchar(): loading of '%d' (%s) complete.\n", charid, c->name); + return c; +} + +int charsave_savechar(int charid, struct mmo_charstatus *c){ + int i,j; + char *str_p; +// char tmp_str[64]; +// char tmp_str2[512]; + //First save the 'char' + sprintf(tmp_sql ,"UPDATE `char` SET `class`='%d', `base_level`='%d', `job_level`='%d'," + "`base_exp`='%u', `job_exp`='%u', `zeny`='%d'," + "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d'," + "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d'," + "`option`='%d',`karma`='%d',`manner`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d'," + "`hair`='%d',`hair_color`='%d',`clothes_color`='%d',`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," + "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d'," + "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d', `fame`='%d', `homun_id`='%d'" + "WHERE `account_id`='%d' AND `char_id` = '%d'", + c->class_, c->base_level, c->job_level, + c->base_exp, c->job_exp, c->zeny, + c->max_hp, c->hp, c->max_sp, c->sp, c->status_point, c->skill_point, + c->str, c->agi, c->vit, c->int_, c->dex, c->luk, + c->option, c->karma, c->manner, c->party_id, c->guild_id, c->pet_id, + c->hair, c->hair_color, c->clothes_color, + c->weapon, c->shield, c->head_top, c->head_mid, c->head_bottom, + mapindex_id2name(c->last_point.map), c->last_point.x, c->last_point.y, + mapindex_id2name(c->save_point.map), c->save_point.x, c->save_point.y, c->partner_id, c->father, c->mother, + c->child, c->fame, c->hom_id, c->account_id, c->char_id + ); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + + //Save the inventory + sprintf(tmp_sql, "DELETE FROM `inventory` WHERE `char_id` = '%d'", charid); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + for(i = 0; i < MAX_INVENTORY; i++){ + if(c->inventory[i].nameid > 0){ + str_p = tmp_sql; + str_p += sprintf(str_p, "INSERT INTO `inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); + for (j = 0; j < MAX_SLOTS; j++) + str_p += sprintf(str_p, ", `card%d`", j); + + str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'", + charid, c->inventory[i].nameid, c->inventory[i].amount, c->inventory[i].equip, + c->inventory[i].identify, c->inventory[i].refine, c->inventory[i].attribute); + + for (j = 0; j < MAX_SLOTS; j++) + str_p += sprintf(str_p, ", '%d'", c->inventory[i].card[j]); + + strcat(tmp_sql,")"); + + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + } + + //Save the cart + sprintf(tmp_sql, "DELETE FROM `cart_inventory` WHERE `char_id` = '%d'", charid); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + for(i = 0; i < MAX_CART; i++){ + if(c->cart[i].nameid > 0){ + str_p = tmp_sql; + str_p += sprintf(str_p, "INSERT INTO `cart_inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); + for (j = 0; j < MAX_SLOTS; j++) + str_p += sprintf(str_p, ", `card%d`", j); + + str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'", + charid, c->cart[i].nameid, c->cart[i].amount, c->cart[i].equip, + c->cart[i].identify, c->cart[i].refine, c->cart[i].attribute); + + for (j = 0; j < MAX_SLOTS; j++) + str_p += sprintf(str_p, ", '%d'", c->cart[i].card[j]); + + strcat(tmp_sql,")"); + + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + } + + + //Save memo points + sprintf(tmp_sql, "DELETE FROM `memo` WHERE `char_id` = '%d'", charid); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + for(i = 0; i < MAX_MEMOPOINTS; i++){ + if(c->memo_point[i].map && c->memo_point[i].x > 0 && c->memo_point[i].y > 0){ + sprintf(tmp_sql, "INSERT INTO `memo` ( `char_id`, `map`, `x`, `y` ) VALUES ('%d', '%s', '%d', '%d')", charid, mapindex_id2name(c->memo_point[i].map), c->memo_point[i].x, c->memo_point[i].y); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + } + + + //Save skills + sprintf(tmp_sql, "DELETE FROM `skill` WHERE `char_id` = '%d'", charid); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + for(i = 0; i < MAX_SKILL; i++){ + if(c->skill[i].id > 0){ + sprintf(tmp_sql, "INSERT INTO `skill` (`char_id`, `id`, `lv`) VALUES ('%d', '%d', '%d')", charid, c->skill[i].id, c->skill[i].lv); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + } + + +/* Reg values are handled by the char server. + //global_reg_value saving + sprintf(tmp_sql, "DELETE FROM `global_reg_value` WHERE `type`=3 AND `char_id` = '%d'", charid); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + for(i = 0; i < c->global_reg_num; i++){ + if(c->global_reg[i].str){ + if(c->global_reg[i].value){ + //jstrescapecpy(tmp_str, c->global_reg[i].str); + sprintf(tmp_sql, "INSERT INTO `global_reg_value` (`char_id`, `str`, `value`) VALUES ('%d', '%s', '%s')", charid, jstrescapecpy(tmp_str,c->global_reg[i].str), jstrescapecpy(tmp_str2,c->global_reg[i].value)); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + } + } +*/ + + //friendlist saving + sprintf(tmp_sql, "DELETE FROM `friends` WHERE `char_id` = '%d'", charid); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + for(i = 0; i < MAX_FRIENDS; i++){ + if(c->friends[i].char_id > 0){ + sprintf(tmp_sql, "INSERT INTO `friends` (`char_id`, `friend_account`, `friend_id`) VALUES ('%d','%d','%d')", charid, c->friends[i].account_id, c->friends[i].char_id); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + } + + ShowInfo("charsql_savechar(): saving of '%d' (%s) complete.\n", charid, c->name); + return 0; +} + +int charsave_load_scdata(int account_id, int char_id) +{ //Loads character's sc_data + struct map_session_data *sd; + + sd = map_id2sd(account_id); + if (!sd) + { + ShowError("charsave_load_scdata: Player of AID %d not found!\n", account_id); + return -1; + } + if (sd->status.char_id != char_id) + { + ShowError("charsave_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", account_id, sd->status.char_id, char_id); + return -1; + } + sprintf(tmp_sql, "SELECT `type`, `tick`, `val1`, `val2`, `val3`, `val4` FROM `sc_data`" + "WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id); + + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return -1; + } + + sql_res = mysql_store_result(&charsql_handle); + if(sql_res) + { + while ((sql_row = mysql_fetch_row(sql_res))) + { + if (atoi(sql_row[1]) < 1) + { //Protection against invalid tick values. [Skotlex] + ShowWarning("charsave_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", atoi(sql_row[1]), sd->status.name); + continue; + } + + status_change_start(&sd->bl, atoi(sql_row[0]), 10000, atoi(sql_row[2]), atoi(sql_row[3]), + atoi(sql_row[4]), atoi(sql_row[5]), atoi(sql_row[1]), 15); + } + } + + //Once loaded, sc_data must be disposed. + sprintf(tmp_sql, "DELETE FROM `sc_data` WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id); + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + return 0; +} + +void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc) +{ //Saves character's sc_data. + int i,count =0; + struct TimerData *timer; + unsigned int tick = gettick(); + char *p = tmp_sql; + + p += sprintf(p, "INSERT INTO `sc_data` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES "); + + for(i = 0; i < max_sc; i++) + { + if (sc_data->data[i].timer == -1) + continue; + timer = get_timer(sc_data->data[i].timer); + if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0) + continue; + + p += sprintf(p, " ('%d','%d','%hu','%d','%d','%d','%d','%d'),", account_id, char_id, + i, DIFF_TICK(timer->tick,tick), sc_data->data[i].val1, sc_data->data[i].val2, sc_data->data[i].val3, sc_data->data[i].val4); + + count++; + } + if (count > 0) + { + *--p = '\0'; //Remove the trailing comma. + if(mysql_query(&charsql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + ShowInfo("charsql_save_scdata(): saved %d status changes of '%d:%d'.\n", count, account_id, char_id); + return; +} +#endif diff --git a/src/map/charsave.h b/src/map/charsave.h index 049efebda..5743c1c65 100644 --- a/src/map/charsave.h +++ b/src/map/charsave.h @@ -1,21 +1,21 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _CHARSAVE_H_ -#define _CHARSAVE_H_ - -#include "status.h" - -#ifndef TXT_ONLY - int charsave_loadHomunculus(int hom_id, struct homun_data *p); - int charsave_saveHomunculus(struct homun_data *hd); - int charsave_saveHomunculusSkills(struct homun_data *hd); - int charsave_deleteHomunculus(struct homun_data *hd); - - struct mmo_charstatus *charsave_loadchar(int charid); - int charsave_savechar(int charid, struct mmo_charstatus *c); - int charsave_load_scdata(int account_id, int char_id); - void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc); -#endif - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _CHARSAVE_H_ +#define _CHARSAVE_H_ + +#include "status.h" + +#ifndef TXT_ONLY + int charsave_loadHomunculus(int hom_id, struct homun_data *p); + int charsave_saveHomunculus(struct homun_data *hd); + int charsave_saveHomunculusSkills(struct homun_data *hd); + int charsave_deleteHomunculus(struct homun_data *hd); + + struct mmo_charstatus *charsave_loadchar(int charid); + int charsave_savechar(int charid, struct mmo_charstatus *c); + int charsave_load_scdata(int account_id, int char_id); + void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc); +#endif + +#endif diff --git a/src/map/chat.c b/src/map/chat.c index 9cc06a4ec..8a0844015 100644 --- a/src/map/chat.c +++ b/src/map/chat.c @@ -1,390 +1,390 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include - -#include "../common/nullpo.h" -#include "../common/malloc.h" -#include "battle.h" -#include "chat.h" -#include "map.h" -#include "clif.h" -#include "pc.h" -#include "npc.h" -#include "atcommand.h" - -int chat_triggerevent(struct chat_data *cd); - -/*========================================== - * チャットルーム作成 - *------------------------------------------ - */ -int chat_createchat(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen) -{ - struct chat_data *cd; - - nullpo_retr(0, sd); - - if (sd->chatID) - return 0; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex] - - if (map[sd->bl.m].flag.nochat) { - clif_displaymessage (sd->fd, msg_txt(281)); - return 0; //Can't create chatrooms on this map. - } - pc_stop_walking(sd,1); - cd = (struct chat_data *) aMalloc(sizeof(struct chat_data)); - - cd->limit = limit; - cd->pub = pub; - cd->users = 1; - memcpy(cd->pass,pass,8); - cd->pass[7]= '\0'; //Overflow check... [Skotlex] - if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1; - memcpy(cd->title,title,titlelen); - cd->title[titlelen]=0; - - cd->owner = (struct block_list **)(&cd->usersd[0]); - cd->usersd[0] = sd; - cd->bl.m = sd->bl.m; - cd->bl.x = sd->bl.x; - cd->bl.y = sd->bl.y; - cd->bl.type = BL_CHAT; - cd->bl.next = cd->bl.prev = NULL; - cd->bl.id = map_addobject(&cd->bl); - if(cd->bl.id==0){ - clif_createchat(sd,1); - aFree(cd); - return 0; - } - pc_setchatid(sd,cd->bl.id); - - clif_createchat(sd,0); - clif_dispchat(cd,0); - - return 0; -} - -/*========================================== - * 既存チャットルームに参加 - *------------------------------------------ - */ -int chat_joinchat (struct map_session_data *sd, int chatid, char* pass) -{ - struct chat_data *cd; - - nullpo_retr(0, sd); - cd = (struct chat_data*)map_id2bl(chatid); - - //No need for a nullpo check. The chatid was sent by the client, if they lag or mess with the packet - //a wrong chat id can be received. [Skotlex] - if (cd == NULL) - return 1; - if (cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->vender_id || sd->chatID || cd->limit <= cd->users) { - clif_joinchatfail(sd,0); - return 0; - } - //Allows Gm access to protected room with any password they want by valaris - if ((cd->pub == 0 && strncmp(pass, (char *)cd->pass, 8) && (pc_isGM(sd) < battle_config.gm_join_chat || !battle_config.gm_join_chat)) || - chatid == (int)sd->chatID) //Double Chat fix by Alex14, thx CHaNGeTe - { - clif_joinchatfail(sd,1); - return 0; - } - - pc_stop_walking(sd,1); - cd->usersd[cd->users] = sd; - cd->users++; - - pc_setchatid(sd,cd->bl.id); - - clif_joinchatok(sd,cd); // 新たに参加した人には全員のリスト - clif_addchat(cd,sd); // 既に中に居た人には追加した人の報告 - clif_dispchat(cd,0); // 周囲の人には人数変化報告 - - chat_triggerevent(cd); // イベント - - return 0; -} - -/*========================================== - * チャットルームから抜ける - *------------------------------------------ - */ -int chat_leavechat(struct map_session_data *sd) -{ - struct chat_data *cd; - int i,leavechar; - - nullpo_retr(1, sd); - - cd=(struct chat_data*)map_id2bl(sd->chatID); - if(cd==NULL) { - sd->chatID = 0; - return 1; - } - - for(i = 0,leavechar=-1;i < cd->users;i++){ - if(cd->usersd[i] == sd){ - leavechar=i; - break; - } - } - if(leavechar<0) - { //Not found in the chatroom? - sd->chatID = 0; - return -1; - } - - if(leavechar==0 && cd->users>1 && (*cd->owner)->type==BL_PC){ - // 所有者だった&他に人が居る&PCのチャット - clif_changechatowner(cd,cd->usersd[1]); - clif_clearchat(cd,0); - } - - // 抜けるPCにも送るのでusersを減らす前に実行 - clif_leavechat(cd,sd); - - cd->users--; - pc_setchatid(sd,0); - - if(cd->users == 0 && (*cd->owner)->type==BL_PC){ - //Delete empty chatroom - clif_clearchat(cd,0); - map_delobject(cd->bl.id); - return 1; - } - for(i=leavechar;i < cd->users;i++) - cd->usersd[i] = cd->usersd[i+1]; - - if(leavechar==0 && (*cd->owner)->type==BL_PC){ - //Adjust Chat location after owner has been changed. - map_delblock( &cd->bl ); - cd->bl.x=cd->usersd[0]->bl.x; - cd->bl.y=cd->usersd[0]->bl.y; - map_addblock( &cd->bl ); - } - clif_dispchat(cd,0); - - return 0; -} - -/*========================================== - * チャットルームの持ち主を譲る - *------------------------------------------ - */ -int chat_changechatowner(struct map_session_data *sd,char *nextownername) -{ - struct chat_data *cd; - struct map_session_data *tmp_sd; - int i, nextowner; - - nullpo_retr(1, sd); - - cd = (struct chat_data*)map_id2bl(sd->chatID); - if (cd == NULL || (struct block_list *)sd != (*cd->owner)) - return 1; - - for(i = 1,nextowner=-1;i < cd->users;i++){ - if(strcmp(cd->usersd[i]->status.name,nextownername)==0){ - nextowner=i; - break; - } - } - if(nextowner<0) // そんな人は居ない - return -1; - - clif_changechatowner(cd,cd->usersd[nextowner]); - // 一旦消す - clif_clearchat(cd,0); - - // userlistの順番変更 (0が所有者なので) - if( (tmp_sd = cd->usersd[0]) == NULL ) //FIXME: How is this even possible!? Invoking character should be owner, hence, it SHOULD be on sc->usersd[0]! - return 1; //ありえるのかな? - cd->usersd[0] = cd->usersd[nextowner]; - cd->usersd[nextowner] = tmp_sd; - - map_delblock( &cd->bl ); - cd->bl.x=cd->usersd[0]->bl.x; - cd->bl.y=cd->usersd[0]->bl.y; - map_addblock( &cd->bl ); - - // 再度表示 - clif_dispchat(cd,0); - - return 0; -} - -/*========================================== - * チャットの状態(タイトル等)を変更 - *------------------------------------------ - */ -int chat_changechatstatus(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen) -{ - struct chat_data *cd; - - nullpo_retr(1, sd); - - cd=(struct chat_data*)map_id2bl(sd->chatID); - if(cd==NULL || (struct block_list *)sd != (*cd->owner)) - return 1; - - cd->limit = limit; - cd->pub = pub; - memcpy(cd->pass,pass,8); - cd->pass[7]= '\0'; //Overflow check... [Skotlex] - if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1; - memcpy(cd->title,title,titlelen); - cd->title[titlelen]=0; - - clif_changechatstatus(cd); - clif_dispchat(cd,0); - - return 0; -} - -/*========================================== - * チャットルームから蹴り出す - *------------------------------------------ - */ -int chat_kickchat(struct map_session_data *sd,char *kickusername) -{ - struct chat_data *cd; - int i; - - nullpo_retr(1, sd); - - cd = (struct chat_data *)map_id2bl(sd->chatID); - - if (!cd) return -1; - - for(i = 0; i < cd->users; i++) { - if (strcmp(cd->usersd[i]->status.name, kickusername) == 0) { - if (battle_config.gm_kick_chat && pc_isGM(cd->usersd[i]) >= battle_config.gm_kick_chat) - //gm kick protection by valaris - return 0; - - chat_leavechat(cd->usersd[i]); - return 0; - } - } - - return -1; -} - -/*========================================== - * npcチャットルーム作成 - *------------------------------------------ - */ -int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev) -{ - struct chat_data *cd; - - nullpo_retr(1, nd); - - cd = (struct chat_data *) aMalloc(sizeof(struct chat_data)); - - cd->limit = cd->trigger = limit; - if(trigger>0) - cd->trigger = trigger; - cd->pub = pub; - cd->users = 0; - memcpy(cd->pass,"",1); - if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1; - memcpy(cd->title,title,titlelen); - cd->title[titlelen]=0; - - cd->bl.m = nd->bl.m; - cd->bl.x = nd->bl.x; - cd->bl.y = nd->bl.y; - cd->bl.type = BL_CHAT; - cd->bl.prev= cd->bl.next = NULL; - cd->owner_ = (struct block_list *)nd; - cd->owner = &cd->owner_; - if (strlen(ev) > 49) - { //npc_event is a char[50] [Skotlex] - memcpy(cd->npc_event,ev,49); - cd->npc_event[49] = '\0'; - } else - memcpy(cd->npc_event,ev,strlen(ev)+1); //Include the \0 - - cd->bl.id = map_addobject(&cd->bl); - if(cd->bl.id==0){ - aFree(cd); - return 0; - } - nd->chat_id=cd->bl.id; - - clif_dispchat(cd,0); - - return 0; -} -/*========================================== - * npcチャットルーム削除 - *------------------------------------------ - */ -int chat_deletenpcchat(struct npc_data *nd) -{ - struct chat_data *cd; - - nullpo_retr(0, nd); - nullpo_retr(0, cd=(struct chat_data*)map_id2bl(nd->chat_id)); - - chat_npckickall(cd); - clif_clearchat(cd,0); - map_delobject(cd->bl.id); // freeまでしてくれる - nd->chat_id=0; - - return 0; -} - -/*========================================== - * 規定人数以上でイベントが定義されてるなら実行 - *------------------------------------------ - */ -int chat_triggerevent(struct chat_data *cd) -{ - nullpo_retr(0, cd); - - if(cd->users>=cd->trigger && cd->npc_event[0]) - npc_event_do(cd->npc_event); - return 0; -} - -/*========================================== - * イベントの有効化 - *------------------------------------------ - */ -int chat_enableevent(struct chat_data *cd) -{ - nullpo_retr(0, cd); - - cd->trigger&=0x7f; - chat_triggerevent(cd); - return 0; -} -/*========================================== - * イベントの無効化 - *------------------------------------------ - */ -int chat_disableevent(struct chat_data *cd) -{ - nullpo_retr(0, cd); - - cd->trigger|=0x80; - return 0; -} -/*========================================== - * チャットルームから全員蹴り出す - *------------------------------------------ - */ -int chat_npckickall(struct chat_data *cd) -{ - nullpo_retr(0, cd); - - while(cd->users>0){ - chat_leavechat(cd->usersd[cd->users-1]); - } - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include + +#include "../common/nullpo.h" +#include "../common/malloc.h" +#include "battle.h" +#include "chat.h" +#include "map.h" +#include "clif.h" +#include "pc.h" +#include "npc.h" +#include "atcommand.h" + +int chat_triggerevent(struct chat_data *cd); + +/*========================================== + * チャットルーム作成 + *------------------------------------------ + */ +int chat_createchat(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen) +{ + struct chat_data *cd; + + nullpo_retr(0, sd); + + if (sd->chatID) + return 0; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex] + + if (map[sd->bl.m].flag.nochat) { + clif_displaymessage (sd->fd, msg_txt(281)); + return 0; //Can't create chatrooms on this map. + } + pc_stop_walking(sd,1); + cd = (struct chat_data *) aMalloc(sizeof(struct chat_data)); + + cd->limit = limit; + cd->pub = pub; + cd->users = 1; + memcpy(cd->pass,pass,8); + cd->pass[7]= '\0'; //Overflow check... [Skotlex] + if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1; + memcpy(cd->title,title,titlelen); + cd->title[titlelen]=0; + + cd->owner = (struct block_list **)(&cd->usersd[0]); + cd->usersd[0] = sd; + cd->bl.m = sd->bl.m; + cd->bl.x = sd->bl.x; + cd->bl.y = sd->bl.y; + cd->bl.type = BL_CHAT; + cd->bl.next = cd->bl.prev = NULL; + cd->bl.id = map_addobject(&cd->bl); + if(cd->bl.id==0){ + clif_createchat(sd,1); + aFree(cd); + return 0; + } + pc_setchatid(sd,cd->bl.id); + + clif_createchat(sd,0); + clif_dispchat(cd,0); + + return 0; +} + +/*========================================== + * 既存チャットルームに参加 + *------------------------------------------ + */ +int chat_joinchat (struct map_session_data *sd, int chatid, char* pass) +{ + struct chat_data *cd; + + nullpo_retr(0, sd); + cd = (struct chat_data*)map_id2bl(chatid); + + //No need for a nullpo check. The chatid was sent by the client, if they lag or mess with the packet + //a wrong chat id can be received. [Skotlex] + if (cd == NULL) + return 1; + if (cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->vender_id || sd->chatID || cd->limit <= cd->users) { + clif_joinchatfail(sd,0); + return 0; + } + //Allows Gm access to protected room with any password they want by valaris + if ((cd->pub == 0 && strncmp(pass, (char *)cd->pass, 8) && (pc_isGM(sd) < battle_config.gm_join_chat || !battle_config.gm_join_chat)) || + chatid == (int)sd->chatID) //Double Chat fix by Alex14, thx CHaNGeTe + { + clif_joinchatfail(sd,1); + return 0; + } + + pc_stop_walking(sd,1); + cd->usersd[cd->users] = sd; + cd->users++; + + pc_setchatid(sd,cd->bl.id); + + clif_joinchatok(sd,cd); // 新たに参加した人には全員のリスト + clif_addchat(cd,sd); // 既に中に居た人には追加した人の報告 + clif_dispchat(cd,0); // 周囲の人には人数変化報告 + + chat_triggerevent(cd); // イベント + + return 0; +} + +/*========================================== + * チャットルームから抜ける + *------------------------------------------ + */ +int chat_leavechat(struct map_session_data *sd) +{ + struct chat_data *cd; + int i,leavechar; + + nullpo_retr(1, sd); + + cd=(struct chat_data*)map_id2bl(sd->chatID); + if(cd==NULL) { + sd->chatID = 0; + return 1; + } + + for(i = 0,leavechar=-1;i < cd->users;i++){ + if(cd->usersd[i] == sd){ + leavechar=i; + break; + } + } + if(leavechar<0) + { //Not found in the chatroom? + sd->chatID = 0; + return -1; + } + + if(leavechar==0 && cd->users>1 && (*cd->owner)->type==BL_PC){ + // 所有者だった&他に人が居る&PCのチャット + clif_changechatowner(cd,cd->usersd[1]); + clif_clearchat(cd,0); + } + + // 抜けるPCにも送るのでusersを減らす前に実行 + clif_leavechat(cd,sd); + + cd->users--; + pc_setchatid(sd,0); + + if(cd->users == 0 && (*cd->owner)->type==BL_PC){ + //Delete empty chatroom + clif_clearchat(cd,0); + map_delobject(cd->bl.id); + return 1; + } + for(i=leavechar;i < cd->users;i++) + cd->usersd[i] = cd->usersd[i+1]; + + if(leavechar==0 && (*cd->owner)->type==BL_PC){ + //Adjust Chat location after owner has been changed. + map_delblock( &cd->bl ); + cd->bl.x=cd->usersd[0]->bl.x; + cd->bl.y=cd->usersd[0]->bl.y; + map_addblock( &cd->bl ); + } + clif_dispchat(cd,0); + + return 0; +} + +/*========================================== + * チャットルームの持ち主を譲る + *------------------------------------------ + */ +int chat_changechatowner(struct map_session_data *sd,char *nextownername) +{ + struct chat_data *cd; + struct map_session_data *tmp_sd; + int i, nextowner; + + nullpo_retr(1, sd); + + cd = (struct chat_data*)map_id2bl(sd->chatID); + if (cd == NULL || (struct block_list *)sd != (*cd->owner)) + return 1; + + for(i = 1,nextowner=-1;i < cd->users;i++){ + if(strcmp(cd->usersd[i]->status.name,nextownername)==0){ + nextowner=i; + break; + } + } + if(nextowner<0) // そんな人は居ない + return -1; + + clif_changechatowner(cd,cd->usersd[nextowner]); + // 一旦消す + clif_clearchat(cd,0); + + // userlistの順番変更 (0が所有者なので) + if( (tmp_sd = cd->usersd[0]) == NULL ) //FIXME: How is this even possible!? Invoking character should be owner, hence, it SHOULD be on sc->usersd[0]! + return 1; //ありえるのかな? + cd->usersd[0] = cd->usersd[nextowner]; + cd->usersd[nextowner] = tmp_sd; + + map_delblock( &cd->bl ); + cd->bl.x=cd->usersd[0]->bl.x; + cd->bl.y=cd->usersd[0]->bl.y; + map_addblock( &cd->bl ); + + // 再度表示 + clif_dispchat(cd,0); + + return 0; +} + +/*========================================== + * チャットの状態(タイトル等)を変更 + *------------------------------------------ + */ +int chat_changechatstatus(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen) +{ + struct chat_data *cd; + + nullpo_retr(1, sd); + + cd=(struct chat_data*)map_id2bl(sd->chatID); + if(cd==NULL || (struct block_list *)sd != (*cd->owner)) + return 1; + + cd->limit = limit; + cd->pub = pub; + memcpy(cd->pass,pass,8); + cd->pass[7]= '\0'; //Overflow check... [Skotlex] + if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1; + memcpy(cd->title,title,titlelen); + cd->title[titlelen]=0; + + clif_changechatstatus(cd); + clif_dispchat(cd,0); + + return 0; +} + +/*========================================== + * チャットルームから蹴り出す + *------------------------------------------ + */ +int chat_kickchat(struct map_session_data *sd,char *kickusername) +{ + struct chat_data *cd; + int i; + + nullpo_retr(1, sd); + + cd = (struct chat_data *)map_id2bl(sd->chatID); + + if (!cd) return -1; + + for(i = 0; i < cd->users; i++) { + if (strcmp(cd->usersd[i]->status.name, kickusername) == 0) { + if (battle_config.gm_kick_chat && pc_isGM(cd->usersd[i]) >= battle_config.gm_kick_chat) + //gm kick protection by valaris + return 0; + + chat_leavechat(cd->usersd[i]); + return 0; + } + } + + return -1; +} + +/*========================================== + * npcチャットルーム作成 + *------------------------------------------ + */ +int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev) +{ + struct chat_data *cd; + + nullpo_retr(1, nd); + + cd = (struct chat_data *) aMalloc(sizeof(struct chat_data)); + + cd->limit = cd->trigger = limit; + if(trigger>0) + cd->trigger = trigger; + cd->pub = pub; + cd->users = 0; + memcpy(cd->pass,"",1); + if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1; + memcpy(cd->title,title,titlelen); + cd->title[titlelen]=0; + + cd->bl.m = nd->bl.m; + cd->bl.x = nd->bl.x; + cd->bl.y = nd->bl.y; + cd->bl.type = BL_CHAT; + cd->bl.prev= cd->bl.next = NULL; + cd->owner_ = (struct block_list *)nd; + cd->owner = &cd->owner_; + if (strlen(ev) > 49) + { //npc_event is a char[50] [Skotlex] + memcpy(cd->npc_event,ev,49); + cd->npc_event[49] = '\0'; + } else + memcpy(cd->npc_event,ev,strlen(ev)+1); //Include the \0 + + cd->bl.id = map_addobject(&cd->bl); + if(cd->bl.id==0){ + aFree(cd); + return 0; + } + nd->chat_id=cd->bl.id; + + clif_dispchat(cd,0); + + return 0; +} +/*========================================== + * npcチャットルーム削除 + *------------------------------------------ + */ +int chat_deletenpcchat(struct npc_data *nd) +{ + struct chat_data *cd; + + nullpo_retr(0, nd); + nullpo_retr(0, cd=(struct chat_data*)map_id2bl(nd->chat_id)); + + chat_npckickall(cd); + clif_clearchat(cd,0); + map_delobject(cd->bl.id); // freeまでしてくれる + nd->chat_id=0; + + return 0; +} + +/*========================================== + * 規定人数以上でイベントが定義されてるなら実行 + *------------------------------------------ + */ +int chat_triggerevent(struct chat_data *cd) +{ + nullpo_retr(0, cd); + + if(cd->users>=cd->trigger && cd->npc_event[0]) + npc_event_do(cd->npc_event); + return 0; +} + +/*========================================== + * イベントの有効化 + *------------------------------------------ + */ +int chat_enableevent(struct chat_data *cd) +{ + nullpo_retr(0, cd); + + cd->trigger&=0x7f; + chat_triggerevent(cd); + return 0; +} +/*========================================== + * イベントの無効化 + *------------------------------------------ + */ +int chat_disableevent(struct chat_data *cd) +{ + nullpo_retr(0, cd); + + cd->trigger|=0x80; + return 0; +} +/*========================================== + * チャットルームから全員蹴り出す + *------------------------------------------ + */ +int chat_npckickall(struct chat_data *cd) +{ + nullpo_retr(0, cd); + + while(cd->users>0){ + chat_leavechat(cd->usersd[cd->users-1]); + } + return 0; +} diff --git a/src/map/chat.h b/src/map/chat.h index 1251ad98c..702fd7dad 100644 --- a/src/map/chat.h +++ b/src/map/chat.h @@ -1,22 +1,22 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _CHAT_H_ -#define _CHAT_H_ - -#include "map.h" - -int chat_createchat(struct map_session_data *,int,int,char*,char*,int); -int chat_joinchat(struct map_session_data *,int,char*); -int chat_leavechat(struct map_session_data* ); -int chat_changechatowner(struct map_session_data *,char *); -int chat_changechatstatus(struct map_session_data *,int,int,char*,char*,int); -int chat_kickchat(struct map_session_data *,char *); - -int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev); -int chat_deletenpcchat(struct npc_data *nd); -int chat_enableevent(struct chat_data *cd); -int chat_disableevent(struct chat_data *cd); -int chat_npckickall(struct chat_data *cd); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _CHAT_H_ +#define _CHAT_H_ + +#include "map.h" + +int chat_createchat(struct map_session_data *,int,int,char*,char*,int); +int chat_joinchat(struct map_session_data *,int,char*); +int chat_leavechat(struct map_session_data* ); +int chat_changechatowner(struct map_session_data *,char *); +int chat_changechatstatus(struct map_session_data *,int,int,char*,char*,int); +int chat_kickchat(struct map_session_data *,char *); + +int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev); +int chat_deletenpcchat(struct npc_data *nd); +int chat_enableevent(struct chat_data *cd); +int chat_disableevent(struct chat_data *cd); +int chat_npckickall(struct chat_data *cd); + +#endif diff --git a/src/map/chrif.c b/src/map/chrif.c index df37aaf1d..1e8183641 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -1,1636 +1,1636 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include -#include -#include - -#include "../common/malloc.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/showmsg.h" - -#include "map.h" -#include "battle.h" -#include "chrif.h" -#include "clif.h" -#include "intif.h" -#include "npc.h" -#include "pc.h" -#include "status.h" -#include "mercenary.h" -#ifndef TXT_ONLY -#include "charsave.h" -#endif -//Updated table (only doc^^) [Sirius] -//Used Packets: U->2af8 -//Free Packets: F->2af8 - -struct dbt *auth_db; - -static const int packet_len_table[0x3d] = { - 60, 3,-1,27,10,-1, 6,-1, // 2af8-2aff: U->2af8, U->2af9, U->2afa, U->2afb, U->2afc, U->2afd, U->2afe, U->2aff - 6,-1,18, 7,-1,49,30,10, // 2b00-2b07: U->2b00, U->2b01, U->2b02, U->2b03, U->2b04, U->2b05, U->2b06, U->2b07 - 6,30,-1,10,86, 7,44,34, // 2b08-2b0f: U->2b08, U->2b09, U->2b0a, U->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f - 0,-1,10, 6,11,-1, 0, 0, // 2b10-2b17: U->2b10, U->2b11, U->2b12, U->2b13, U->2b14, U->2b15, U->2b16, U->2b17 - -1,-1,-1,-1,-1,-1, 2, 7, // 2b18-2b1f: U->2b18, U->2b19, U->2b1a, U->2b1b, U->2b1c, U->2b1d, U->2b1e, U->2b1f - -1,10,-1,-1,-1,-1,-1,-1, // 2b20-2b27: U->2b20, U->2b21, F->2b22, F->2b23, F->2b24, F->2b25, F->2b26, F->2b27 -}; - -//Used Packets: -//2af8: Outgoing, chrif_connect -> 'connect to charserver / auth @ charserver' -//2af9: Incomming, chrif_connectack -> 'answer of the 2af8 login(ok / fail)' -//2afa: Outgoing, chrif_sendmap -> 'sending our maps' -//2afb: Incomming, chrif_sendmapack -> 'Maps received successfully / or not ..' -//2afc: Outgoing, chrif_scdata_request -> request sc_data for pc_authok'ed char. <- new command reuses previous one. -//2afd: Incomming, chrif_authok -> 'character selected, add to auth db' -//2afe: Outgoing, send_usercount_tochar -> 'sends player count of this map server to charserver' -//2aff: Outgoing, send_users_tochar -> 'sends all actual connected character ids to charserver' -//2b00: Incomming, map_setusers -> 'set the actual usercount? PACKET.2B COUNT.L.. ?' (not sure) -//2b01: Outgoing, chrif_save -> 'charsave of char XY account XY (complete struct)' -//2b02: Outgoing, chrif_charselectreq -> 'player returns from ingame to charserver to select another char.., this packets includes sessid etc' ? (not 100% sure) -//2b03: Incomming, clif_charselectok -> '' (i think its the packet after enterworld?) (not sure) -//2b04: Incomming, chrif_recvmap -> 'getting maps from charserver of other mapserver's' -//2b05: Outgoing, chrif_changemapserver -> 'Tell the charserver the mapchange / quest for ok...' -//2b06: Incomming, chrif_changemapserverack -> 'awnser of 2b05, ok/fail, data: dunno^^' -//2b07: Incoming, clif_updatemaxid -> Received when updating the max account/char known -//2b08: Outgoing, chrif_searchcharid -> '...' -//2b09: Incomming, map_addchariddb -> 'Adds a name to the nick db' -//2b0a: Outgoing, chrif_changegm -> 'level change of acc/char XY' -//2b0b: Incomming, chrif_changedgm -> 'answer of 2b0a..' -//2b0c: Outgoing, chrif_changeemail -> 'change mail address ...' -//2b0d: Incomming, chrif_changedsex -> 'Change sex of acc XY' -//2b0e: Outgoing, chrif_char_ask_name -> 'Do some operations (change sex, ban / unban etc)' -//2b0f: Incomming, chrif_char_ask_name_answer -> 'answer of the 2b0e' -//2b10: Outgoing, chrif_updatefamelist -> 'Update the fame ranking lists and send them' -//2b11: Outgoing, chrif_changesex -> 'change sex of acc X' -//2b12: Incomming, chrif_divorce -> 'divorce a wedding of charid X and partner id X' -//2b13: Incomming, chrif_accountdeletion -> 'Delete acc XX, if the player is on, kick ....' -//2b14: Incomming, chrif_accountban -> 'not sure: kick the player with message XY' -//2b15: Incomming, chrif_recvgmaccounts -> 'recieve gm accs from charserver (seems to be incomplete !)' -//2b16: Outgoing, chrif_ragsrvinfo -> 'sends motd / rates ....' -//2b17: Outgoing, chrif_char_offline -> 'tell the charserver that the char is now offline' -//2b18: Outgoing, chrif_char_reset_offline -> 'set all players OFF!' -//2b19: Outgoing, chrif_char_online -> 'tell the charserver that the char .. is online' -//2b1a: Outgoing, chrif_buildfamelist -> 'Build the fame ranking lists and send them' -//2b1b: Incomming, chrif_recvfamelist -> 'Receive fame ranking lists' -//2b1c: Outgoing, chrif_save_scdata -> 'Send sc_data of player for saving.' -//2b1d: Incomming, chrif_load_scdata -> 'received sc_data of player for loading.' -//2b1e: Incoming, chrif_update_ip -> 'Reqest forwarded from char-server for interserver IP sync.' [Lance] -//2b1f: Incomming, chrif_disconnectplayer -> 'disconnects a player (aid X) with the message XY ... 0x81 ..' [Sirius] -//2b20: Incomming, chrif_removemap -> 'remove maps of a server (sample: its going offline)' [Sirius] -//2b21: Incomming, chrif_save_ack. Returned after a character has been "final saved" on the char-server. [Skotlex] -//2b22-2b27: FREE - -int chrif_connected = 0; -int char_fd = 0; //Using 0 instead of -1 is safer against crashes. [Skotlex] -int srvinfo; -static char char_ip_str[128]; -static in_addr_t char_ip= 0; -static int char_port = 6121; -static char userid[NAME_LENGTH], passwd[NAME_LENGTH]; -static int chrif_state = 0; -static int char_init_done = 0; -int other_mapserver_count=0; //Holds count of how many other map servers are online (apart of this instance) [Skotlex] - -//Interval at which map server updates online listing. [Valaris] -#define CHECK_INTERVAL 3600000 -//Interval at which map server sends number of connected users. [Skotlex] -#define UPDATE_INTERVAL 10000 -//This define should spare writing the check in every function. [Skotlex] -#define chrif_check(a) { if(!chrif_isconnect()) return a; } - -// 設定ファイル読み込み関係 -/*========================================== - * - *------------------------------------------ - */ -void chrif_setuserid(char *id) -{ - memcpy(userid, id, NAME_LENGTH); -} - -/*========================================== - * - *------------------------------------------ - */ -void chrif_setpasswd(char *pwd) -{ - memcpy(passwd, pwd, NAME_LENGTH); -} - -/*========================================== - * - *------------------------------------------ - */ -void chrif_checkdefaultlogin(void) -{ - if (strcmp(userid, "s1")==0 && strcmp(passwd, "p1")==0) { - ShowError("Using the default user/password s1/p1 is NOT RECOMMENDED.\n"); -#ifdef TXT_ONLY - ShowNotice("Please edit your save/account.txt file to create a proper inter-server user/password (gender 'S')\n"); -#else - ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n"); -#endif - ShowNotice("and then edit your user/password in conf/map_athena.conf (or conf/import/map_conf.txt)\n"); - } -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_setip(char *ip) -{ - char ip_str[16]; - char_ip = resolve_hostbyname(ip,NULL,ip_str); - - if (!char_ip) { - ShowWarning("Failed to Resolve Char Server Address! (%s)\n", ip); - return 0; - } - strncpy(char_ip_str, ip, sizeof(char_ip_str)); - ShowInfo("Char Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip_str); - return 1; -} - -/*========================================== - * - *------------------------------------------ - */ -void chrif_setport(int port) -{ - char_port = port; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_isconnect(void) -{ - return (char_fd > 0 && session[char_fd] != NULL && chrif_state == 2); -} - -/*========================================== - * Saves char. - * Flag = 1: Character is quitting. - * Flag = 2: Character is changing map-servers - *------------------------------------------ - */ -int chrif_save(struct map_session_data *sd, int flag) -{ - nullpo_retr(-1, sd); - - pc_makesavestatus(sd); - if(!chrif_isconnect()) - { - if (flag) sd->state.finalsave = 1; //Will save character on reconnect. - return -1; - } - - if (sd->state.finalsave) - return -1; //Refuse to save a char already tagged for final saving. [Skotlex] - //For data sync - if (sd->state.storage_flag == 1) - storage_storage_save(sd->status.account_id, flag); - else if (sd->state.storage_flag == 2) - storage_guild_storagesave(sd->status.account_id, sd->status.guild_id, flag); - if (flag) sd->state.storage_flag = 0; //Force close it. - - //Saving of registry values. - if (sd->state.reg_dirty&4) - intif_saveregistry(sd, 3); //Save char regs - if (sd->state.reg_dirty&2) - intif_saveregistry(sd, 2); //Save account regs - if (sd->state.reg_dirty&1) - intif_saveregistry(sd, 1); //Save account2 regs -#ifndef TXT_ONLY - if(charsave_method){ //New 'Local' save - charsave_savechar(sd->status.char_id, &sd->status); - if (flag) //Character final saved. - sd->state.finalsave = 1; - if (flag == 1) - chrif_char_offline(sd); //Tell char server that character went offline. - return 0; - } -#endif - WFIFOHEAD(char_fd, sizeof(sd->status) + 13); - WFIFOW(char_fd,0) = 0x2b01; - WFIFOW(char_fd,2) = sizeof(sd->status) + 13; - WFIFOL(char_fd,4) = sd->status.account_id; - WFIFOL(char_fd,8) = sd->status.char_id; - WFIFOB(char_fd,12) = (flag==1)?1:0; //Flag to tell char-server this character is quitting. - memcpy(WFIFOP(char_fd,13), &sd->status, sizeof(sd->status)); - WFIFOSET(char_fd, WFIFOW(char_fd,2)); - - if (sd->hd && merc_is_hom_active(sd->hd)) - merc_save(sd->hd); - - if (flag) - sd->state.finalsave = 1; //Mark the last save as done. - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_connect(int fd) -{ - ShowStatus("Logging in to char server...\n", char_fd); - WFIFOHEAD(fd, 60); - WFIFOW(fd,0) = 0x2af8; - memcpy(WFIFOP(fd,2), userid, NAME_LENGTH); - memcpy(WFIFOP(fd,26), passwd, NAME_LENGTH); - WFIFOL(fd,50) = 0; - WFIFOL(fd,54) = clif_getip_long(); - WFIFOW(fd,58) = clif_getport(); // [Valaris] thanks to fov - WFIFOSET(fd,60); - - return 0; -} - -/*========================================== - * マップ送信 - *------------------------------------------ - */ -int chrif_sendmap(int fd) -{ - int i; - ShowStatus("Sending maps to char server...\n"); - WFIFOHEAD(fd, 4 + map_num * 4); - WFIFOW(fd,0) = 0x2afa; - for(i = 0; i < map_num; i++) - WFIFOW(fd,4+i*4) = map[i].index; - WFIFOW(fd,2) = 4 + i * 4; - WFIFOSET(fd,WFIFOW(fd,2)); - - return 0; -} - -/*========================================== - * マップ受信 - *------------------------------------------ - */ -int chrif_recvmap(int fd) -{ - int i, j, ip, port; - unsigned char *p = (unsigned char *)&ip; - RFIFOHEAD(fd); - ip = RFIFOL(fd,4); - port = RFIFOW(fd,8); - for(i = 10, j = 0; i < RFIFOW(fd,2); i += 4, j++) { - map_setipport(RFIFOW(fd,i), ip, port); -// if (battle_config.etc_log) -// printf("recv map %d %s\n", j, RFIFOP(fd,i)); - } - if (battle_config.etc_log) - ShowStatus("recv map on %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j); - - other_mapserver_count++; - return 0; -} - -/*========================================== - * Delete maps of other servers, (if an other mapserver is going OFF) - *------------------------------------------ - */ -int chrif_removemap(int fd){ - int i, j, ip, port; - unsigned char *p = (unsigned char *)&ip; - RFIFOHEAD(fd); - - ip = RFIFOL(fd, 4); - port = RFIFOW(fd, 8); - - for(i = 10, j = 0; i < RFIFOW(fd, 2); i += 4, j++){ - map_eraseipport(RFIFOW(fd, i), ip, port); - } - - other_mapserver_count--; - if(battle_config.etc_log) - ShowStatus("remove map of server %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j); - return 0; -} - -int chrif_save_ack(int fd) { - struct map_session_data *sd; - RFIFOHEAD(fd); - sd = map_id2sd(RFIFOL(fd,2)); - - if (sd && sd->status.char_id == RFIFOL(fd,6)) - map_quit_ack(sd); - return 0; -} - -/*========================================== - * マップ鯖間移動のためのデータ準備要求 - *------------------------------------------ - */ -int chrif_changemapserver(struct map_session_data *sd, short map, int x, int y, int ip, short port) -{ - int s_ip; - - nullpo_retr(-1, sd); - - chrif_check(-1); - - if (other_mapserver_count < 1) - { //No other map servers are online! - clif_authfail_fd(sd->fd, 0); - return -1; - } - - if (sd->fd && sd->fd < fd_max && session[sd->fd]) - s_ip = session[sd->fd]->client_addr.sin_addr.s_addr; - else //Not connected? Can't retrieve IP - s_ip = 0; - - WFIFOHEAD(char_fd, 35); - WFIFOW(char_fd, 0) = 0x2b05; - WFIFOL(char_fd, 2) = sd->bl.id; - WFIFOL(char_fd, 6) = sd->login_id1; - WFIFOL(char_fd,10) = sd->login_id2; - WFIFOL(char_fd,14) = sd->status.char_id; - WFIFOW(char_fd,18) = map; - WFIFOW(char_fd,20) = x; - WFIFOW(char_fd,22) = y; - WFIFOL(char_fd,24) = ip; - WFIFOW(char_fd,28) = port; - WFIFOB(char_fd,30) = sd->status.sex; - WFIFOL(char_fd,31) = s_ip; - WFIFOSET(char_fd,35); - - return 0; -} - -/*========================================== - * マップ鯖間移動ack - *------------------------------------------ - */ -int chrif_changemapserverack(int fd) -{ - struct map_session_data *sd; - RFIFOHEAD(fd); - sd = map_id2sd(RFIFOL(fd,2)); - - if (sd == NULL || sd->status.char_id != RFIFOL(fd,14)) - return -1; - - if (RFIFOL(fd,6) == 1) { - if (battle_config.error_log) - ShowError("map server change failed.\n"); - clif_authfail_fd(sd->fd, 0); - return 0; - } - clif_changemapserver(sd, (char*)mapindex_id2name(RFIFOW(fd,18)), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28)); - - //Player has been saved already, remove him from memory. [Skotlex] - map_quit(sd); - map_quit_ack(sd); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_connectack(int fd) -{ - RFIFOHEAD(fd); - if (RFIFOB(fd,2)) { - ShowFatalError("Connection to char-server failed %d.\n", RFIFOB(fd,2)); - exit(1); - } - ShowStatus("Successfully logged on to Char Server (Connection: '"CL_WHITE"%d"CL_RESET"').\n",fd); - chrif_state = 1; - chrif_connected=1; - - chrif_sendmap(fd); - - ShowStatus("Event '"CL_WHITE"OnCharIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnCharIfInit")); - ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit")); - if(!char_init_done) { - char_init_done = 1; - ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInitOnce")); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_sendmapack(int fd) -{ - RFIFOHEAD(fd); - if (RFIFOB(fd,2)) { - ShowFatalError("chrif : send map list to char server failed %d\n", RFIFOB(fd,2)); - exit(1); - } - memcpy(wisp_server_name, RFIFOP(fd,3), NAME_LENGTH); - ShowStatus("Map sending complete. Map Server is now online.\n"); - chrif_state = 2; - - //If there are players online, send them to the char-server. [Skotlex] - send_users_tochar(-1, gettick(), 0, 0); - - //Re-save any storages that were modified in the disconnection time. [Skotlex] - do_reconnect_map(); - do_reconnect_storage(); - - return 0; -} - -/*========================================== - * Request sc_data from charserver [Skotlex] - *------------------------------------------ - */ -int chrif_scdata_request(int account_id, int char_id) -{ -#ifdef ENABLE_SC_SAVING -#ifndef TXT_ONLY - if (charsave_method) - return charsave_load_scdata(account_id, char_id); -#endif - chrif_check(-1); - - WFIFOHEAD(char_fd, 10); - WFIFOW(char_fd, 0) = 0x2afc; - WFIFOL(char_fd, 2) = account_id; - WFIFOL(char_fd, 6) = char_id; - WFIFOSET(char_fd,10); -#endif - return 0; -} - -/*========================================== - * new auth system [Kevin] - *------------------------------------------ - */ -void chrif_authreq(struct map_session_data *sd) -{ - struct auth_node *auth_data; - auth_data=idb_get(auth_db, sd->bl.id); - - if(auth_data) { - if(auth_data->char_dat && - auth_data->account_id== sd->bl.id && - auth_data->login_id1 == sd->login_id1) - { //auth ok - pc_authok(sd, auth_data->login_id2, auth_data->connect_until_time, auth_data->char_dat); - } else { //auth failed - pc_authfail(sd); - chrif_char_offline(sd); //Set him offline, the char server likely has it set as online already. - } - if (auth_data->char_dat) - aFree(auth_data->char_dat); - idb_remove(auth_db, sd->bl.id); - } else { //data from char server has not arrived yet. - auth_data = aCalloc(1,sizeof(struct auth_node)); - auth_data->sd = sd; - auth_data->fd = sd->fd; - auth_data->account_id = sd->bl.id; - auth_data->login_id1 = sd->login_id1; - auth_data->node_created = gettick(); - uidb_put(auth_db, sd->bl.id, auth_data); - } - return; -} - -//character selected, insert into auth db -void chrif_authok(int fd) { - struct auth_node *auth_data; - RFIFOHEAD(fd); - - if (map_id2sd(RFIFOL(fd, 4)) != NULL) - //Someone with this account is already in! Do not store the info to prevent possible sync exploits. [Skotlex] - return; - - if ((auth_data =uidb_get(auth_db, RFIFOL(fd, 4))) != NULL) - { //Is the character already awaiting authorization? - if (auth_data->sd) - { - //First, check to see if the session data still exists (avoid dangling pointers) - if(session[auth_data->fd] && session[auth_data->fd]->session_data == auth_data->sd) - { - if (auth_data->char_dat == NULL && - auth_data->account_id == RFIFOL(fd, 4) && - auth_data->login_id1 == RFIFOL(fd, 8)) - { //Auth Ok - pc_authok(auth_data->sd, RFIFOL(fd, 16), RFIFOL(fd, 12), (struct mmo_charstatus*)RFIFOP(fd, 20)); - } else { //Auth Failed - pc_authfail(auth_data->sd); - chrif_char_offline(auth_data->sd); //Set him offline, the char server likely has it set as online already. - } - } //else: Character no longer exists, just go through. - } - //Delete the data of this node... - if (auth_data->char_dat) - aFree (auth_data->char_dat); - uidb_remove(auth_db, RFIFOL(fd, 4)); - return; - } - // Awaiting for client to connect. - auth_data = (struct auth_node *)aCalloc(1,sizeof(struct auth_node)); - auth_data->char_dat = (struct mmo_charstatus *) aCalloc(1,sizeof(struct mmo_charstatus)); - - auth_data->account_id=RFIFOL(fd, 4); - auth_data->login_id1=RFIFOL(fd, 8); - auth_data->connect_until_time=RFIFOL(fd, 12); - auth_data->login_id2=RFIFOL(fd, 16); - memcpy(auth_data->char_dat,RFIFOP(fd, 20),sizeof(struct mmo_charstatus)); - auth_data->node_created=gettick(); - uidb_put(auth_db, RFIFOL(fd, 4), auth_data); -} - -int auth_db_cleanup_sub(DBKey key,void *data,va_list ap) -{ - struct auth_node *node=(struct auth_node*)data; - - if(DIFF_TICK(gettick(),node->node_created)>30000) { - ShowNotice("Character (aid: %d) not authed within 30 seconds of character select!\n", node->account_id); - if (node->char_dat) - aFree(node->char_dat); - db_remove(auth_db, key); - return 1; - } - return 0; -} - -int auth_db_cleanup(int tid, unsigned int tick, int id, int data) { - auth_db->foreach(auth_db, auth_db_cleanup_sub); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_charselectreq(struct map_session_data *sd, unsigned long s_ip) -{ - nullpo_retr(-1, sd); - - if( !sd || !sd->bl.id || !sd->login_id1 ) - return -1; - chrif_check(-1); - - WFIFOHEAD(char_fd, 18); - WFIFOW(char_fd, 0) = 0x2b02; - WFIFOL(char_fd, 2) = sd->bl.id; - WFIFOL(char_fd, 6) = sd->login_id1; - WFIFOL(char_fd,10) = sd->login_id2; - WFIFOL(char_fd,14) = s_ip; - WFIFOSET(char_fd,18); - - return 0; -} - -/*========================================== - * キャラ名問い合わせ - *------------------------------------------ - */ -int chrif_searchcharid(int char_id) -{ - if( !char_id ) - return -1; - chrif_check(-1); - - WFIFOHEAD(char_fd, 6); - WFIFOW(char_fd,0) = 0x2b08; - WFIFOL(char_fd,2) = char_id; - WFIFOSET(char_fd,6); - - return 0; -} - -/*========================================== - * GMに変化要求 - *------------------------------------------ - */ -int chrif_changegm(int id, const char *pass, int len) -{ - if (battle_config.etc_log) - ShowInfo("chrif_changegm: account: %d, password: '%s'.\n", id, pass); - - chrif_check(-1); - - WFIFOHEAD(char_fd, len + 8); - WFIFOW(char_fd,0) = 0x2b0a; - WFIFOW(char_fd,2) = len + 8; - WFIFOL(char_fd,4) = id; - memcpy(WFIFOP(char_fd,8), pass, len); - WFIFOSET(char_fd, len + 8); - - return 0; -} - -/*========================================== - * Change Email - *------------------------------------------ - */ -int chrif_changeemail(int id, const char *actual_email, const char *new_email) -{ - if (battle_config.etc_log) - ShowInfo("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n", id, actual_email, new_email); - - chrif_check(-1); - - WFIFOHEAD(char_fd, 86); - WFIFOW(char_fd,0) = 0x2b0c; - WFIFOL(char_fd,2) = id; - memcpy(WFIFOP(char_fd,6), actual_email, 40); - memcpy(WFIFOP(char_fd,46), new_email, 40); - WFIFOSET(char_fd,86); - - return 0; -} - -/*========================================== - * Send message to char-server with a character name to do some operations (by Yor) - * Used to ask Char-server about a character name to have the account number to modify account file in login-server. - * type of operation: - * 1: block - * 2: ban - * 3: unblock - * 4: unban - * 5: changesex - *------------------------------------------ - */ -int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second) -{ - chrif_check(-1); - - WFIFOHEAD(char_fd, 44); - WFIFOW(char_fd, 0) = 0x2b0e; - WFIFOL(char_fd, 2) = id; // account_id of who ask (for answer) -1 if nobody - memcpy(WFIFOP(char_fd,6), character_name, NAME_LENGTH); - WFIFOW(char_fd, 30) = operation_type; // type of operation - if (operation_type == 2) { - WFIFOW(char_fd, 32) = year; - WFIFOW(char_fd, 34) = month; - WFIFOW(char_fd, 36) = day; - WFIFOW(char_fd, 38) = hour; - WFIFOW(char_fd, 40) = minute; - WFIFOW(char_fd, 42) = second; - } -// ShowInfo("chrif : sent 0x2b0e\n"); - WFIFOSET(char_fd,44); - - return 0; -} - -/*========================================== - * 性別変化要求 - *------------------------------------------ - */ -int chrif_changesex(int id, int sex) { - chrif_check(-1); - - WFIFOHEAD(char_fd, 9); - WFIFOW(char_fd,0) = 0x2b11; - WFIFOW(char_fd,2) = 9; - WFIFOL(char_fd,4) = id; - WFIFOB(char_fd,8) = sex; -// ShowInfo("chrif : sent 0x3000(changesex)\n"); - WFIFOSET(char_fd,9); - return 0; -} - -/*========================================== - * Answer after a request about a character name to do some operations (by Yor) - * Used to answer of chrif_char_ask_name. - * type of operation: - * 1: block - * 2: ban - * 3: unblock - * 4: unban - * 5: changesex - * type of answer: - * 0: login-server resquest done - * 1: player not found - * 2: gm level too low - * 3: login-server offline - *------------------------------------------ - */ -int chrif_char_ask_name_answer(int fd) -{ - int acc; - struct map_session_data *sd; - char output[256]; - char player_name[NAME_LENGTH]; - RFIFOHEAD(fd); - - acc = RFIFOL(fd,2); // account_id of who has asked (-1 if nobody) - memcpy(player_name, RFIFOP(fd,6), NAME_LENGTH-1); - player_name[NAME_LENGTH-1] = '\0'; - - sd = map_id2sd(acc); - if (acc >= 0 && sd != NULL) { - if (RFIFOW(fd, 32) == 1) // player not found - sprintf(output, "The player '%s' doesn't exist.", player_name); - else { - switch(RFIFOW(fd, 30)) { - case 1: // block - switch(RFIFOW(fd, 32)) { - case 0: // login-server resquest done - sprintf(output, "Login-server has been asked to block the player '%s'.", player_name); - break; - //case 1: // player not found - case 2: // gm level too low - sprintf(output, "Your GM level don't authorise you to block the player '%s'.", player_name); - break; - case 3: // login-server offline - sprintf(output, "Login-server is offline. Impossible to block the the player '%s'.", player_name); - break; - } - break; - case 2: // ban - switch(RFIFOW(fd, 32)) { - case 0: // login-server resquest done - sprintf(output, "Login-server has been asked to ban the player '%s'.", player_name); - break; - //case 1: // player not found - case 2: // gm level too low - sprintf(output, "Your GM level don't authorise you to ban the player '%s'.", player_name); - break; - case 3: // login-server offline - sprintf(output, "Login-server is offline. Impossible to ban the the player '%s'.", player_name); - break; - } - break; - case 3: // unblock - switch(RFIFOW(fd, 32)) { - case 0: // login-server resquest done - sprintf(output, "Login-server has been asked to unblock the player '%s'.", player_name); - break; - //case 1: // player not found - case 2: // gm level too low - sprintf(output, "Your GM level don't authorise you to unblock the player '%s'.", player_name); - break; - case 3: // login-server offline - sprintf(output, "Login-server is offline. Impossible to unblock the the player '%s'.", player_name); - break; - } - break; - case 4: // unban - switch(RFIFOW(fd, 32)) { - case 0: // login-server resquest done - sprintf(output, "Login-server has been asked to unban the player '%s'.", player_name); - break; - //case 1: // player not found - case 2: // gm level too low - sprintf(output, "Your GM level don't authorise you to unban the player '%s'.", player_name); - break; - case 3: // login-server offline - sprintf(output, "Login-server is offline. Impossible to unban the the player '%s'.", player_name); - break; - } - break; - case 5: // changesex - switch(RFIFOW(fd, 32)) { - case 0: // login-server resquest done - sprintf(output, "Login-server has been asked to change the sex of the player '%s'.", player_name); - break; - //case 1: // player not found - case 2: // gm level too low - sprintf(output, "Your GM level don't authorise you to change the sex of the player '%s'.", player_name); - break; - case 3: // login-server offline - sprintf(output, "Login-server is offline. Impossible to change the sex of the the player '%s'.", player_name); - break; - } - break; - } - } - if (output[0] != '\0') { - output[sizeof(output)-1] = '\0'; - clif_displaymessage(sd->fd, output); - } - } else - ShowError("chrif_char_ask_name_answer failed - player not online.\n"); - - return 0; -} - -/*========================================== - * End of GM change (@GM) (modified by Yor) - *------------------------------------------ - */ -int chrif_changedgm(int fd) -{ - int acc, level; - struct map_session_data *sd = NULL; - RFIFOHEAD(fd); - - acc = RFIFOL(fd,2); - level = RFIFOL(fd,6); - - sd = map_id2sd(acc); - - if (battle_config.etc_log) - ShowNotice("chrif_changedgm: account: %d, GM level 0 -> %d.\n", acc, level); - if (sd != NULL) { - if (level > 0) - clif_displaymessage(sd->fd, "GM modification success."); - else - clif_displaymessage(sd->fd, "Failure of GM modification."); - } - - return 0; -} - -/*========================================== - * 性別変化終了 (modified by Yor) - *------------------------------------------ - */ -int chrif_changedsex(int fd) -{ - int acc, sex, i; - struct map_session_data *sd; - RFIFOHEAD(fd); - - acc = RFIFOL(fd,2); - sex = RFIFOL(fd,6); - if (battle_config.etc_log) - ShowNotice("chrif_changedsex %d.\n", acc); - sd = map_id2sd(acc); - if (acc > 0) { - if (sd != NULL && sd->status.sex != sex) { - sd->status.sex = !sd->status.sex; - - // to avoid any problem with equipment and invalid sex, equipment is unequiped. - for (i = 0; i < MAX_INVENTORY; i++) { - if (sd->status.inventory[i].nameid && sd->status.inventory[i].equip) - pc_unequipitem((struct map_session_data*)sd, i, 2); - } - // reset skill of some job - if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) { - // remove specifical skills of Bard classes - for(i = 315; i <= 322; i++) { - if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) { - if (sd->status.skill_point > USHRT_MAX - sd->status.skill[i].lv) - sd->status.skill_point = USHRT_MAX; - else - sd->status.skill_point += sd->status.skill[i].lv; - sd->status.skill[i].id = 0; - sd->status.skill[i].lv = 0; - } - } - // remove specifical skills of Dancer classes - for(i = 323; i <= 330; i++) { - if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) { - if (sd->status.skill_point > USHRT_MAX - sd->status.skill[i].lv) - sd->status.skill_point = USHRT_MAX; - else - sd->status.skill_point += sd->status.skill[i].lv; - sd->status.skill[i].id = 0; - sd->status.skill[i].lv = 0; - } - } - clif_updatestatus(sd, SP_SKILLPOINT); - // change job if necessary - if (sd->status.sex) //Changed from Dancer - sd->status.class_ -= 1; - else //Changed from Bard - sd->status.class_ += 1; - //sd->class_ needs not be updated as both Dancer/Bard are the same. - } - // save character - //chrif_save(sd,1); Character will be saved on session closed -> map_quit - sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters - // do same modify in login-server for the account, but no in char-server (it ask again login_id1 to login, and don't remember it) - clif_displaymessage(sd->fd, "Your sex has been changed (need disconnection by the server)..."); - clif_setwaitclose(sd->fd); // forced to disconnect for the change - } - } else { - if (sd != NULL) { - ShowError("chrif_changedsex failed.\n"); - } - } - - return 0; -} - -/*========================================== - * 離婚情報同期要求 - *------------------------------------------ - */ -int chrif_divorce(int char_id, int partner_id) -{ - struct map_session_data *sd = NULL; - - if (!char_id || !partner_id) - return 0; - - nullpo_retr(0, sd = map_nick2sd(map_charid2nick(partner_id))); - if (sd->status.partner_id == char_id) { - int i; - //離婚(相方は既にキャラが消えている筈なので) - sd->status.partner_id = 0; - - //相方の結婚指輪を剥奪 - for(i = 0; i < MAX_INVENTORY; i++) - if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F) - pc_delitem(sd, i, 1, 0); - } - - return 0; -} - -/*========================================== - * Disconnection of a player (account has been deleted in login-server) by [Yor] - *------------------------------------------ - */ -int chrif_accountdeletion(int fd) -{ - int acc; - struct map_session_data *sd; - RFIFOHEAD(fd); - - acc = RFIFOL(fd,2); - if (battle_config.etc_log) - ShowNotice("chrif_accountdeletion %d.\n", acc); - sd = map_id2sd(acc); - if (acc > 0) { - if (sd != NULL) { - sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters - clif_displaymessage(sd->fd, "Your account has been deleted (disconnection)..."); - clif_setwaitclose(sd->fd); // forced to disconnect for the change - } - } else { - if (sd != NULL) - ShowError("chrif_accountdeletion failed - player not online.\n"); - } - - return 0; -} - -/*========================================== - * Disconnection of a player (account has been banned of has a status, from login-server) by [Yor] - *------------------------------------------ - */ -int chrif_accountban(int fd) -{ - int acc; - struct map_session_data *sd; - RFIFOHEAD(fd); - - acc = RFIFOL(fd,2); - if (battle_config.etc_log) - ShowNotice("chrif_accountban %d.\n", acc); - sd = map_id2sd(acc); - if (acc > 0) { - if (sd != NULL) { - sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters - if (RFIFOB(fd,6) == 0) { // 0: change of statut, 1: ban - switch (RFIFOL(fd,7)) { // status or final date of a banishment - case 1: // 0 = Unregistered ID - clif_displaymessage(sd->fd, "Your account has 'Unregistered'."); - break; - case 2: // 1 = Incorrect Password - clif_displaymessage(sd->fd, "Your account has an 'Incorrect Password'..."); - break; - case 3: // 2 = This ID is expired - clif_displaymessage(sd->fd, "Your account has expired."); - break; - case 4: // 3 = Rejected from Server - clif_displaymessage(sd->fd, "Your account has been rejected from server."); - break; - case 5: // 4 = You have been blocked by the GM Team - clif_displaymessage(sd->fd, "Your account has been blocked by the GM Team."); - break; - case 6: // 5 = Your Game's EXE file is not the latest version - clif_displaymessage(sd->fd, "Your Game's EXE file is not the latest version."); - break; - case 7: // 6 = Your are Prohibited to log in until %s - clif_displaymessage(sd->fd, "Your account has been prohibited to log in."); - break; - case 8: // 7 = Server is jammed due to over populated - clif_displaymessage(sd->fd, "Server is jammed due to over populated."); - break; - case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this) - clif_displaymessage(sd->fd, "Your account has not more authorised."); - break; - case 100: // 99 = This ID has been totally erased - clif_displaymessage(sd->fd, "Your account has been totally erased."); - break; - default: - clif_displaymessage(sd->fd, "Your account has not more authorised."); - break; - } - } else if (RFIFOB(fd,6) == 1) { // 0: change of statut, 1: ban - time_t timestamp; - char tmpstr[2048]; - timestamp = (time_t)RFIFOL(fd,7); // status or final date of a banishment - strcpy(tmpstr, "Your account has been banished until "); - strftime(tmpstr + strlen(tmpstr), 24, "%d-%m-%Y %H:%M:%S", localtime(×tamp)); - clif_displaymessage(sd->fd, tmpstr); - } - clif_setwaitclose(sd->fd); // forced to disconnect for the change - } - } else { - if (sd != NULL) - ShowError("chrif_accountban failed - player not online.\n"); - } - - return 0; -} - -//Disconnect the player out of the game, simple packet -//packet.w AID.L WHY.B 2+4+1 = 7byte -int chrif_disconnectplayer(int fd){ - struct map_session_data *sd; - RFIFOHEAD(fd); - - sd = map_id2sd(RFIFOL(fd, 2)); - - if(sd == NULL){ - return -1; - } - - if (!sd->fd) - { //No connection - if (sd->state.autotrade) - map_quit(sd); //Remove it. - //Else we don't remove it because the char should have a timer to remove the player because it force-quit before, - //and we don't want them kicking their previous instance before the 10 secs penalty time passes. [Skotlex] - return 0; - } - - switch(RFIFOB(fd, 6)){ - //clif_authfail_fd - case 1: //server closed - clif_authfail_fd(sd->fd, 1); - break; - - case 2: //someone else logged in - clif_authfail_fd(sd->fd, 2); - break; - - case 3: //server overpopulated - clif_authfail_fd(sd->fd, 4); - - break; - - case 4: //out of time payd for .. (avail) - clif_authfail_fd(sd->fd, 10); - break; - - case 5: //forced to dc by gm - clif_authfail_fd(sd->fd, 15); - break; - } - -return 0; -} - -/*========================================== - * Request to reload GM accounts and their levels: send to char-server by [Yor] - *------------------------------------------ - */ -int chrif_reloadGMdb(void) -{ - chrif_check(-1); - - WFIFOHEAD(char_fd, 2); - WFIFOW(char_fd,0) = 0x2af7; - WFIFOSET(char_fd, 2); - - return 0; -} - -/*========================================== - * Receiving GM accounts and their levels from char-server by [Yor] - *------------------------------------------ - */ -int chrif_recvgmaccounts(int fd) -{ - ShowInfo("From login-server: receiving information of '"CL_WHITE"%d"CL_RESET"' GM accounts.\n", pc_read_gm_account(fd)); - return 0; -} - -/*========================================== - * Request/Receive top 10 Fame character list - *------------------------------------------ - */ - -int chrif_updatefamelist(struct map_session_data *sd) -{ - char type; - chrif_check(-1); - - switch(sd->class_&MAPID_UPPERMASK) { - case MAPID_BLACKSMITH: - type = 1; - break; - case MAPID_ALCHEMIST: - type = 2; - break; - case MAPID_TAEKWON: - type = 3; - break; - default: - return 0; - } - - WFIFOHEAD(char_fd, 12); - WFIFOW(char_fd, 0) = 0x2b10; - WFIFOL(char_fd, 2) = sd->status.char_id; - WFIFOL(char_fd, 6) = sd->status.fame; - WFIFOB(char_fd, 10) = type; - WFIFOB(char_fd, 11) = pc_famerank(sd->status.char_id, sd->class_&MAPID_UPPERMASK); - WFIFOSET(char_fd, 12); - - return 0; -} - -int chrif_buildfamelist(void) -{ - chrif_check(-1); - - WFIFOHEAD(char_fd, 2); - WFIFOW(char_fd, 0) = 0x2b1a; - WFIFOSET(char_fd, 2); - - return 0; -} - -int chrif_recvfamelist(int fd) -{ - int num, size; - int total = 0, len = 8; - RFIFOHEAD(fd); - - malloc_tsetdword (smith_fame_list, 0, sizeof(smith_fame_list)); - malloc_tsetdword (chemist_fame_list, 0, sizeof(chemist_fame_list)); - malloc_tsetdword (taekwon_fame_list, 0, sizeof(taekwon_fame_list)); - - size = RFIFOW(fd, 6); //Blacksmith block size - for (num = 0; len < size && num < MAX_FAME_LIST; num++) { - memcpy(&smith_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list)); - len += sizeof(struct fame_list); - } - total += num; - - size = RFIFOW(fd, 4); //Alchemist block size - for (num = 0; len < size && num < MAX_FAME_LIST; num++) { - memcpy(&chemist_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list)); - len += sizeof(struct fame_list); - } - total += num; - - size = RFIFOW(fd, 2); //Total packet length - for (num = 0; len < size && num < MAX_FAME_LIST; num++) { - memcpy(&taekwon_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list)); - len += sizeof(struct fame_list); - } - total += num; - - ShowInfo("Received Fame List of '"CL_WHITE"%d"CL_RESET"' characters.\n", total); - - return 0; -} - -int chrif_save_scdata(struct map_session_data *sd) -{ //parses the sc_data of the player and sends it to the char-server for saving. [Skotlex] -#ifdef ENABLE_SC_SAVING - int i, count=0; - unsigned int tick; - struct status_change_data data; - struct TimerData *timer; - - if (sd->state.finalsave) //Character was already saved? - return -1; -#ifndef TXT_ONLY - if(charsave_method) //New 'Local' save - { - charsave_save_scdata(sd->status.account_id, sd->status.char_id, &sd->sc, MAX_STATUSCHANGE); - return 0; - } -#endif - - chrif_check(-1); - tick = gettick(); - - WFIFOHEAD(char_fd, 14 + SC_MAX*sizeof(struct status_change_data)); - WFIFOW(char_fd,0) = 0x2b1c; - WFIFOL(char_fd,4) = sd->status.account_id; - WFIFOL(char_fd,8) = sd->status.char_id; - for (i = 0; i < SC_MAX; i++) - { - if (sd->sc.data[i].timer == -1) - continue; - timer = get_timer(sd->sc.data[i].timer); - if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0) - continue; - data.tick = DIFF_TICK(timer->tick,tick); //Duration that is left before ending. - data.type = i; - data.val1 = sd->sc.data[i].val1; - data.val2 = sd->sc.data[i].val2; - data.val3 = sd->sc.data[i].val3; - data.val4 = sd->sc.data[i].val4; - memcpy(WFIFOP(char_fd,14 +count*sizeof(struct status_change_data)), - &data, sizeof(struct status_change_data)); - count++; - } - if (count == 0) - return 0; //Nothing to save. - WFIFOW(char_fd,12) = count; - WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size - WFIFOSET(char_fd,WFIFOW(char_fd,2)); -#endif - return 0; -} - -int chrif_load_scdata(int fd) -{ //Retrieve and load sc_data for a player. [Skotlex] -#ifdef ENABLE_SC_SAVING - struct map_session_data *sd; - struct status_change_data *data; - int aid, cid, i, count; - RFIFOHEAD(fd); - - aid = RFIFOL(fd,4); //Player Account ID - cid = RFIFOL(fd,8); //Player Char ID - - sd = map_id2sd(aid); - if (!sd) - { - ShowError("chrif_load_scdata: Player of AID %d not found!\n", aid); - return -1; - } - if (sd->status.char_id != cid) - { - ShowError("chrif_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid); - return -1; - } - count = RFIFOW(fd,12); //sc_count - for (i = 0; i < count; i++) - { - data = (struct status_change_data*)RFIFOP(fd,14 + i*sizeof(struct status_change_data)); - if (data->tick < 1) - { //Protection against invalid tick values. [Skotlex] - ShowWarning("chrif_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", data->tick, data->type, sd->status.name); - continue; - } - status_change_start(&sd->bl, data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, 15); - } -#endif - return 0; -} - -/*========================================== - * Send rates and motd to char server [Wizputer] - *------------------------------------------ - */ - int chrif_ragsrvinfo(int base_rate, int job_rate, int drop_rate) -{ - char buf[256]; - FILE *fp; - int i; - - chrif_check(-1); - - WFIFOHEAD(char_fd, sizeof(buf) + 10); - WFIFOW(char_fd,0) = 0x2b16; - WFIFOW(char_fd,2) = base_rate; - WFIFOW(char_fd,4) = job_rate; - WFIFOW(char_fd,6) = drop_rate; - - if ((fp = fopen(motd_txt, "r")) != NULL) { - if (fgets(buf, 250, fp) != NULL) { - for(i = 0; buf[i]; i++) { - if (buf[i] == '\r' || buf[i] == '\n') { - buf[i] = 0; - break; - } - } - WFIFOW(char_fd,8) = sizeof(buf) + 10; - memcpy(WFIFOP(char_fd,10), buf, sizeof(buf)); - } - fclose(fp); - } else { - malloc_tsetdword(buf, 0, sizeof(buf)); //No data found, send empty packets? - WFIFOW(char_fd,8) = sizeof(buf) + 10; - memcpy(WFIFOP(char_fd,10), buf, sizeof(buf)); - } - WFIFOSET(char_fd,WFIFOW(char_fd,8)); - return 0; -} - - -/*========================================= - * Tell char-server charcter disconnected [Wizputer] - *----------------------------------------- - */ - -int chrif_char_offline(struct map_session_data *sd) -{ - chrif_check(-1); - - WFIFOHEAD(char_fd, 10); - WFIFOW(char_fd,0) = 0x2b17; - WFIFOL(char_fd,2) = sd->status.char_id; - WFIFOL(char_fd,6) = sd->status.account_id; - WFIFOSET(char_fd,10); - - return 0; -} - -/*========================================= - * Tell char-server to reset all chars offline [Wizputer] - *----------------------------------------- - */ -int chrif_flush_fifo(void) { - chrif_check(-1); - - set_nonblocking(char_fd, 0); - flush_fifos(); - set_nonblocking(char_fd, 1); - - return 0; -} - -/*========================================= - * Tell char-server to reset all chars offline [Wizputer] - *----------------------------------------- - */ -int chrif_char_reset_offline(void) { - chrif_check(-1); - - WFIFOHEAD(char_fd, 2); - WFIFOW(char_fd,0) = 0x2b18; - WFIFOSET(char_fd,2); - - return 0; -} - -/*========================================= - * Tell char-server charcter is online [Wizputer] - *----------------------------------------- - */ - -int chrif_char_online(struct map_session_data *sd) -{ - chrif_check(-1); - - WFIFOHEAD(char_fd, 10); - WFIFOW(char_fd,0) = 0x2b19; - WFIFOL(char_fd,2) = sd->status.char_id; - WFIFOL(char_fd,6) = sd->status.account_id; - WFIFOSET(char_fd,10); - - return 0; -} - -int chrif_disconnect(int fd) { - if(fd == char_fd) { - char_fd = 0; - ShowWarning("Map Server disconnected from Char Server.\n\n"); - chrif_connected = 0; - - other_mapserver_count=0; //Reset counter. We receive ALL maps from all map-servers on reconnect. - map_eraseallipport(); - - //Attempt to reconnect in a second. [Skotlex] - add_timer(gettick() + 1000, check_connect_char_server, 0, 0); - } - return 0; -} - -void chrif_update_ip(int fd){ - unsigned long new_ip; - WFIFOHEAD(fd, 6); - new_ip = resolve_hostbyname(char_ip_str, NULL, NULL); - if (new_ip && new_ip != char_ip) - char_ip = new_ip; //Update char_ip - - new_ip = clif_refresh_ip(); - if (!new_ip) return; //No change - WFIFOW(fd, 0) = 0x2736; - WFIFOL(fd, 2) = new_ip; - WFIFOSET(fd, 6); -} - -/*========================================== - * - *------------------------------------------ - */ -int chrif_parse(int fd) -{ - int packet_len, cmd; - // only char-server can have an access to here. - // so, if it isn't the char-server, we disconnect the session (fd != char_fd). - if (fd != char_fd || session[fd]->eof) { - if (fd == char_fd && chrif_connected == 1) { - chrif_disconnect (fd); - } - else if (fd != char_fd) - ShowDebug("chrif_parse: Disconnecting invalid session #%d (is not the char-server)\n", fd); - - do_close(fd); - return 0; - } - - while (RFIFOREST(fd) >= 2 && !session[fd]->eof) { //Infinite loop on broken pipe fix. [Skotlex] - RFIFOHEAD(fd); - cmd = RFIFOW(fd,0); - if (cmd < 0x2af8 || cmd >= 0x2af8 + (sizeof(packet_len_table) / sizeof(packet_len_table[0])) || - packet_len_table[cmd-0x2af8] == 0) { - - int r = intif_parse(fd); // intifに渡す - - if (r == 1) continue; // intifで処理した - if (r == 2) return 0; // intifで処理したが、データが足りない - - session[fd]->eof = 1; - ShowWarning("chrif_parse: session #%d, intif_parse failed -> disconnected.\n", fd); - return 0; - } - packet_len = packet_len_table[cmd-0x2af8]; - if (packet_len == -1) { - if (RFIFOREST(fd) < 4) - return 0; - packet_len = RFIFOW(fd,2); - } - if ((int)RFIFOREST(fd) < packet_len) - return 0; - - switch(cmd) { - case 0x2af9: chrif_connectack(fd); break; - case 0x2afb: chrif_sendmapack(fd); break; - case 0x2afd: chrif_authok(fd); break; - case 0x2b00: map_setusers(fd); break; - case 0x2b03: clif_charselectok(RFIFOL(fd,2)); break; - case 0x2b04: chrif_recvmap(fd); break; - case 0x2b06: chrif_changemapserverack(fd); break; - case 0x2b07: clif_updatemaxid(RFIFOL(fd,2), RFIFOL(fd,6)); break; - case 0x2b09: map_addchariddb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break; - case 0x2b0b: chrif_changedgm(fd); break; - case 0x2b0d: chrif_changedsex(fd); break; - case 0x2b0f: chrif_char_ask_name_answer(fd); break; - case 0x2b12: chrif_divorce(RFIFOL(fd,2), RFIFOL(fd,6)); break; - case 0x2b13: chrif_accountdeletion(fd); break; - case 0x2b14: chrif_accountban(fd); break; - case 0x2b15: chrif_recvgmaccounts(fd); break; - case 0x2b1b: chrif_recvfamelist(fd); break; - case 0x2b1d: chrif_load_scdata(fd); break; - case 0x2b1e: chrif_update_ip(fd); break; - case 0x2b1f: chrif_disconnectplayer(fd); break; - case 0x2b20: chrif_removemap(fd); break; - case 0x2b21: chrif_save_ack(fd); break; - - default: - if (battle_config.error_log) - ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd); - session[fd]->eof = 1; - return 0; - } - if (fd == char_fd) //There's the slight chance we lost the connection during parse, in which case this would segfault if not checked [Skotlex] - RFIFOSKIP(fd, packet_len); - } - - return 0; -} - -int send_usercount_tochar(int tid, unsigned int tick, int id, int data) { - int count; - static int last_count = 0; - - chrif_check(-1); - - map_getallusers(&count); - - if (count == last_count) //No need to waste packets. - return 0; - last_count = count; - - WFIFOHEAD(char_fd, 4); - WFIFOW(char_fd,0) = 0x2afe; - WFIFOW(char_fd,2) = count; - WFIFOSET(char_fd,4); - return 0; -} - -/*========================================== - * timer関数 - * 今このmap鯖に繋がっているクライアント人数をchar鯖へ送る - *------------------------------------------ - */ -int send_users_tochar(int tid, unsigned int tick, int id, int data) { - int count, users=0, i; - struct map_session_data **all_sd; - - chrif_check(-1); - - all_sd = map_getallusers(&count); - WFIFOHEAD(char_fd, 6+8*users); - WFIFOW(char_fd,0) = 0x2aff; - for (i = 0; i < count; i++) { - WFIFOL(char_fd,6+8*users) = all_sd[i]->status.account_id; - WFIFOL(char_fd,6+8*users+4) = all_sd[i]->status.char_id; - users++; - } - WFIFOW(char_fd,2) = 6 + 8 * users; - WFIFOW(char_fd,4) = users; - WFIFOSET(char_fd,6+8*users); - - return 0; -} - -/*========================================== - * timer関数 - * char鯖との接続を確認し、もし切れていたら再度接続する - *------------------------------------------ - */ -int check_connect_char_server(int tid, unsigned int tick, int id, int data) { - static int displayed = 0; - if (char_fd <= 0 || session[char_fd] == NULL) { - if (!displayed) { - ShowStatus("Attempting to connect to Char Server. Please wait.\n"); - displayed = 1; - } - chrif_state = 0; - char_fd = make_connection(char_ip, char_port); - if (char_fd == -1) - { //Attempt to connect later. [Skotlex] - char_fd = 0; - return 0; - } - session[char_fd]->func_parse = chrif_parse; - realloc_fifo(char_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); - - chrif_connect(char_fd); - chrif_connected = (chrif_state == 2); -#ifndef TXT_ONLY - srvinfo = 0; -#endif /* not TXT_ONLY */ - } else { -#ifndef TXT_ONLY - if (srvinfo == 0) { - chrif_ragsrvinfo(battle_config.base_exp_rate, battle_config.job_exp_rate, battle_config.item_rate_common); - srvinfo = 1; - } -#endif /* not TXT_ONLY */ -/* There is no need, the connection is TCP, so the packet is assured to arrive unless the connection dies [Skotlex] - //If for some reason the next iteration (10 secs) we are still not connected, - //assume the packets got lost, so we need to resend them. [Skotlex] - if (chrif_state == 0) - chrif_connect(char_fd); - else if (chrif_state == 1) - chrif_sendmap(char_fd); -*/ - } - if (chrif_isconnect()) displayed = 0; - return 0; -} - -int auth_db_final(DBKey k,void *d,va_list ap) { - struct auth_node *node=(struct auth_node*)d; - if (node->char_dat) - aFree(node->char_dat); - return 0; -} - -/*========================================== - * 終了 - *------------------------------------------ - */ -int do_final_chrif(void) -{ - delete_session(char_fd); - auth_db->destroy(auth_db, auth_db_final); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int do_init_chrif(void) -{ - add_timer_func_list(check_connect_char_server, "check_connect_char_server"); - add_timer_func_list(send_usercount_tochar, "send_usercount_tochar"); - add_timer_func_list(send_users_tochar, "send_users_tochar"); - add_timer_func_list(auth_db_cleanup, "auth_db_cleanup"); - add_timer_interval(gettick() + 1000, check_connect_char_server, 0, 0, 10 * 1000); -#ifdef TXT_ONLY - //Txt needs this more frequently because it is used for the online.html file. - add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, UPDATE_INTERVAL); -#else - add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, CHECK_INTERVAL); - add_timer_interval(gettick() + 1000, send_usercount_tochar, 0, 0, UPDATE_INTERVAL); -#endif - add_timer_interval(gettick() + 1000, auth_db_cleanup, 0, 0, 30 * 1000); - - auth_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include +#include +#include + +#include "../common/malloc.h" +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/nullpo.h" +#include "../common/showmsg.h" + +#include "map.h" +#include "battle.h" +#include "chrif.h" +#include "clif.h" +#include "intif.h" +#include "npc.h" +#include "pc.h" +#include "status.h" +#include "mercenary.h" +#ifndef TXT_ONLY +#include "charsave.h" +#endif +//Updated table (only doc^^) [Sirius] +//Used Packets: U->2af8 +//Free Packets: F->2af8 + +struct dbt *auth_db; + +static const int packet_len_table[0x3d] = { + 60, 3,-1,27,10,-1, 6,-1, // 2af8-2aff: U->2af8, U->2af9, U->2afa, U->2afb, U->2afc, U->2afd, U->2afe, U->2aff + 6,-1,18, 7,-1,49,30,10, // 2b00-2b07: U->2b00, U->2b01, U->2b02, U->2b03, U->2b04, U->2b05, U->2b06, U->2b07 + 6,30,-1,10,86, 7,44,34, // 2b08-2b0f: U->2b08, U->2b09, U->2b0a, U->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f + 0,-1,10, 6,11,-1, 0, 0, // 2b10-2b17: U->2b10, U->2b11, U->2b12, U->2b13, U->2b14, U->2b15, U->2b16, U->2b17 + -1,-1,-1,-1,-1,-1, 2, 7, // 2b18-2b1f: U->2b18, U->2b19, U->2b1a, U->2b1b, U->2b1c, U->2b1d, U->2b1e, U->2b1f + -1,10,-1,-1,-1,-1,-1,-1, // 2b20-2b27: U->2b20, U->2b21, F->2b22, F->2b23, F->2b24, F->2b25, F->2b26, F->2b27 +}; + +//Used Packets: +//2af8: Outgoing, chrif_connect -> 'connect to charserver / auth @ charserver' +//2af9: Incomming, chrif_connectack -> 'answer of the 2af8 login(ok / fail)' +//2afa: Outgoing, chrif_sendmap -> 'sending our maps' +//2afb: Incomming, chrif_sendmapack -> 'Maps received successfully / or not ..' +//2afc: Outgoing, chrif_scdata_request -> request sc_data for pc_authok'ed char. <- new command reuses previous one. +//2afd: Incomming, chrif_authok -> 'character selected, add to auth db' +//2afe: Outgoing, send_usercount_tochar -> 'sends player count of this map server to charserver' +//2aff: Outgoing, send_users_tochar -> 'sends all actual connected character ids to charserver' +//2b00: Incomming, map_setusers -> 'set the actual usercount? PACKET.2B COUNT.L.. ?' (not sure) +//2b01: Outgoing, chrif_save -> 'charsave of char XY account XY (complete struct)' +//2b02: Outgoing, chrif_charselectreq -> 'player returns from ingame to charserver to select another char.., this packets includes sessid etc' ? (not 100% sure) +//2b03: Incomming, clif_charselectok -> '' (i think its the packet after enterworld?) (not sure) +//2b04: Incomming, chrif_recvmap -> 'getting maps from charserver of other mapserver's' +//2b05: Outgoing, chrif_changemapserver -> 'Tell the charserver the mapchange / quest for ok...' +//2b06: Incomming, chrif_changemapserverack -> 'awnser of 2b05, ok/fail, data: dunno^^' +//2b07: Incoming, clif_updatemaxid -> Received when updating the max account/char known +//2b08: Outgoing, chrif_searchcharid -> '...' +//2b09: Incomming, map_addchariddb -> 'Adds a name to the nick db' +//2b0a: Outgoing, chrif_changegm -> 'level change of acc/char XY' +//2b0b: Incomming, chrif_changedgm -> 'answer of 2b0a..' +//2b0c: Outgoing, chrif_changeemail -> 'change mail address ...' +//2b0d: Incomming, chrif_changedsex -> 'Change sex of acc XY' +//2b0e: Outgoing, chrif_char_ask_name -> 'Do some operations (change sex, ban / unban etc)' +//2b0f: Incomming, chrif_char_ask_name_answer -> 'answer of the 2b0e' +//2b10: Outgoing, chrif_updatefamelist -> 'Update the fame ranking lists and send them' +//2b11: Outgoing, chrif_changesex -> 'change sex of acc X' +//2b12: Incomming, chrif_divorce -> 'divorce a wedding of charid X and partner id X' +//2b13: Incomming, chrif_accountdeletion -> 'Delete acc XX, if the player is on, kick ....' +//2b14: Incomming, chrif_accountban -> 'not sure: kick the player with message XY' +//2b15: Incomming, chrif_recvgmaccounts -> 'recieve gm accs from charserver (seems to be incomplete !)' +//2b16: Outgoing, chrif_ragsrvinfo -> 'sends motd / rates ....' +//2b17: Outgoing, chrif_char_offline -> 'tell the charserver that the char is now offline' +//2b18: Outgoing, chrif_char_reset_offline -> 'set all players OFF!' +//2b19: Outgoing, chrif_char_online -> 'tell the charserver that the char .. is online' +//2b1a: Outgoing, chrif_buildfamelist -> 'Build the fame ranking lists and send them' +//2b1b: Incomming, chrif_recvfamelist -> 'Receive fame ranking lists' +//2b1c: Outgoing, chrif_save_scdata -> 'Send sc_data of player for saving.' +//2b1d: Incomming, chrif_load_scdata -> 'received sc_data of player for loading.' +//2b1e: Incoming, chrif_update_ip -> 'Reqest forwarded from char-server for interserver IP sync.' [Lance] +//2b1f: Incomming, chrif_disconnectplayer -> 'disconnects a player (aid X) with the message XY ... 0x81 ..' [Sirius] +//2b20: Incomming, chrif_removemap -> 'remove maps of a server (sample: its going offline)' [Sirius] +//2b21: Incomming, chrif_save_ack. Returned after a character has been "final saved" on the char-server. [Skotlex] +//2b22-2b27: FREE + +int chrif_connected = 0; +int char_fd = 0; //Using 0 instead of -1 is safer against crashes. [Skotlex] +int srvinfo; +static char char_ip_str[128]; +static in_addr_t char_ip= 0; +static int char_port = 6121; +static char userid[NAME_LENGTH], passwd[NAME_LENGTH]; +static int chrif_state = 0; +static int char_init_done = 0; +int other_mapserver_count=0; //Holds count of how many other map servers are online (apart of this instance) [Skotlex] + +//Interval at which map server updates online listing. [Valaris] +#define CHECK_INTERVAL 3600000 +//Interval at which map server sends number of connected users. [Skotlex] +#define UPDATE_INTERVAL 10000 +//This define should spare writing the check in every function. [Skotlex] +#define chrif_check(a) { if(!chrif_isconnect()) return a; } + +// 設定ファイル読み込み関係 +/*========================================== + * + *------------------------------------------ + */ +void chrif_setuserid(char *id) +{ + memcpy(userid, id, NAME_LENGTH); +} + +/*========================================== + * + *------------------------------------------ + */ +void chrif_setpasswd(char *pwd) +{ + memcpy(passwd, pwd, NAME_LENGTH); +} + +/*========================================== + * + *------------------------------------------ + */ +void chrif_checkdefaultlogin(void) +{ + if (strcmp(userid, "s1")==0 && strcmp(passwd, "p1")==0) { + ShowError("Using the default user/password s1/p1 is NOT RECOMMENDED.\n"); +#ifdef TXT_ONLY + ShowNotice("Please edit your save/account.txt file to create a proper inter-server user/password (gender 'S')\n"); +#else + ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n"); +#endif + ShowNotice("and then edit your user/password in conf/map_athena.conf (or conf/import/map_conf.txt)\n"); + } +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_setip(char *ip) +{ + char ip_str[16]; + char_ip = resolve_hostbyname(ip,NULL,ip_str); + + if (!char_ip) { + ShowWarning("Failed to Resolve Char Server Address! (%s)\n", ip); + return 0; + } + strncpy(char_ip_str, ip, sizeof(char_ip_str)); + ShowInfo("Char Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip_str); + return 1; +} + +/*========================================== + * + *------------------------------------------ + */ +void chrif_setport(int port) +{ + char_port = port; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_isconnect(void) +{ + return (char_fd > 0 && session[char_fd] != NULL && chrif_state == 2); +} + +/*========================================== + * Saves char. + * Flag = 1: Character is quitting. + * Flag = 2: Character is changing map-servers + *------------------------------------------ + */ +int chrif_save(struct map_session_data *sd, int flag) +{ + nullpo_retr(-1, sd); + + pc_makesavestatus(sd); + if(!chrif_isconnect()) + { + if (flag) sd->state.finalsave = 1; //Will save character on reconnect. + return -1; + } + + if (sd->state.finalsave) + return -1; //Refuse to save a char already tagged for final saving. [Skotlex] + //For data sync + if (sd->state.storage_flag == 1) + storage_storage_save(sd->status.account_id, flag); + else if (sd->state.storage_flag == 2) + storage_guild_storagesave(sd->status.account_id, sd->status.guild_id, flag); + if (flag) sd->state.storage_flag = 0; //Force close it. + + //Saving of registry values. + if (sd->state.reg_dirty&4) + intif_saveregistry(sd, 3); //Save char regs + if (sd->state.reg_dirty&2) + intif_saveregistry(sd, 2); //Save account regs + if (sd->state.reg_dirty&1) + intif_saveregistry(sd, 1); //Save account2 regs +#ifndef TXT_ONLY + if(charsave_method){ //New 'Local' save + charsave_savechar(sd->status.char_id, &sd->status); + if (flag) //Character final saved. + sd->state.finalsave = 1; + if (flag == 1) + chrif_char_offline(sd); //Tell char server that character went offline. + return 0; + } +#endif + WFIFOHEAD(char_fd, sizeof(sd->status) + 13); + WFIFOW(char_fd,0) = 0x2b01; + WFIFOW(char_fd,2) = sizeof(sd->status) + 13; + WFIFOL(char_fd,4) = sd->status.account_id; + WFIFOL(char_fd,8) = sd->status.char_id; + WFIFOB(char_fd,12) = (flag==1)?1:0; //Flag to tell char-server this character is quitting. + memcpy(WFIFOP(char_fd,13), &sd->status, sizeof(sd->status)); + WFIFOSET(char_fd, WFIFOW(char_fd,2)); + + if (sd->hd && merc_is_hom_active(sd->hd)) + merc_save(sd->hd); + + if (flag) + sd->state.finalsave = 1; //Mark the last save as done. + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_connect(int fd) +{ + ShowStatus("Logging in to char server...\n", char_fd); + WFIFOHEAD(fd, 60); + WFIFOW(fd,0) = 0x2af8; + memcpy(WFIFOP(fd,2), userid, NAME_LENGTH); + memcpy(WFIFOP(fd,26), passwd, NAME_LENGTH); + WFIFOL(fd,50) = 0; + WFIFOL(fd,54) = clif_getip_long(); + WFIFOW(fd,58) = clif_getport(); // [Valaris] thanks to fov + WFIFOSET(fd,60); + + return 0; +} + +/*========================================== + * マップ送信 + *------------------------------------------ + */ +int chrif_sendmap(int fd) +{ + int i; + ShowStatus("Sending maps to char server...\n"); + WFIFOHEAD(fd, 4 + map_num * 4); + WFIFOW(fd,0) = 0x2afa; + for(i = 0; i < map_num; i++) + WFIFOW(fd,4+i*4) = map[i].index; + WFIFOW(fd,2) = 4 + i * 4; + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +/*========================================== + * マップ受信 + *------------------------------------------ + */ +int chrif_recvmap(int fd) +{ + int i, j, ip, port; + unsigned char *p = (unsigned char *)&ip; + RFIFOHEAD(fd); + ip = RFIFOL(fd,4); + port = RFIFOW(fd,8); + for(i = 10, j = 0; i < RFIFOW(fd,2); i += 4, j++) { + map_setipport(RFIFOW(fd,i), ip, port); +// if (battle_config.etc_log) +// printf("recv map %d %s\n", j, RFIFOP(fd,i)); + } + if (battle_config.etc_log) + ShowStatus("recv map on %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j); + + other_mapserver_count++; + return 0; +} + +/*========================================== + * Delete maps of other servers, (if an other mapserver is going OFF) + *------------------------------------------ + */ +int chrif_removemap(int fd){ + int i, j, ip, port; + unsigned char *p = (unsigned char *)&ip; + RFIFOHEAD(fd); + + ip = RFIFOL(fd, 4); + port = RFIFOW(fd, 8); + + for(i = 10, j = 0; i < RFIFOW(fd, 2); i += 4, j++){ + map_eraseipport(RFIFOW(fd, i), ip, port); + } + + other_mapserver_count--; + if(battle_config.etc_log) + ShowStatus("remove map of server %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j); + return 0; +} + +int chrif_save_ack(int fd) { + struct map_session_data *sd; + RFIFOHEAD(fd); + sd = map_id2sd(RFIFOL(fd,2)); + + if (sd && sd->status.char_id == RFIFOL(fd,6)) + map_quit_ack(sd); + return 0; +} + +/*========================================== + * マップ鯖間移動のためのデータ準備要求 + *------------------------------------------ + */ +int chrif_changemapserver(struct map_session_data *sd, short map, int x, int y, int ip, short port) +{ + int s_ip; + + nullpo_retr(-1, sd); + + chrif_check(-1); + + if (other_mapserver_count < 1) + { //No other map servers are online! + clif_authfail_fd(sd->fd, 0); + return -1; + } + + if (sd->fd && sd->fd < fd_max && session[sd->fd]) + s_ip = session[sd->fd]->client_addr.sin_addr.s_addr; + else //Not connected? Can't retrieve IP + s_ip = 0; + + WFIFOHEAD(char_fd, 35); + WFIFOW(char_fd, 0) = 0x2b05; + WFIFOL(char_fd, 2) = sd->bl.id; + WFIFOL(char_fd, 6) = sd->login_id1; + WFIFOL(char_fd,10) = sd->login_id2; + WFIFOL(char_fd,14) = sd->status.char_id; + WFIFOW(char_fd,18) = map; + WFIFOW(char_fd,20) = x; + WFIFOW(char_fd,22) = y; + WFIFOL(char_fd,24) = ip; + WFIFOW(char_fd,28) = port; + WFIFOB(char_fd,30) = sd->status.sex; + WFIFOL(char_fd,31) = s_ip; + WFIFOSET(char_fd,35); + + return 0; +} + +/*========================================== + * マップ鯖間移動ack + *------------------------------------------ + */ +int chrif_changemapserverack(int fd) +{ + struct map_session_data *sd; + RFIFOHEAD(fd); + sd = map_id2sd(RFIFOL(fd,2)); + + if (sd == NULL || sd->status.char_id != RFIFOL(fd,14)) + return -1; + + if (RFIFOL(fd,6) == 1) { + if (battle_config.error_log) + ShowError("map server change failed.\n"); + clif_authfail_fd(sd->fd, 0); + return 0; + } + clif_changemapserver(sd, (char*)mapindex_id2name(RFIFOW(fd,18)), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28)); + + //Player has been saved already, remove him from memory. [Skotlex] + map_quit(sd); + map_quit_ack(sd); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_connectack(int fd) +{ + RFIFOHEAD(fd); + if (RFIFOB(fd,2)) { + ShowFatalError("Connection to char-server failed %d.\n", RFIFOB(fd,2)); + exit(1); + } + ShowStatus("Successfully logged on to Char Server (Connection: '"CL_WHITE"%d"CL_RESET"').\n",fd); + chrif_state = 1; + chrif_connected=1; + + chrif_sendmap(fd); + + ShowStatus("Event '"CL_WHITE"OnCharIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnCharIfInit")); + ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit")); + if(!char_init_done) { + char_init_done = 1; + ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInitOnce")); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_sendmapack(int fd) +{ + RFIFOHEAD(fd); + if (RFIFOB(fd,2)) { + ShowFatalError("chrif : send map list to char server failed %d\n", RFIFOB(fd,2)); + exit(1); + } + memcpy(wisp_server_name, RFIFOP(fd,3), NAME_LENGTH); + ShowStatus("Map sending complete. Map Server is now online.\n"); + chrif_state = 2; + + //If there are players online, send them to the char-server. [Skotlex] + send_users_tochar(-1, gettick(), 0, 0); + + //Re-save any storages that were modified in the disconnection time. [Skotlex] + do_reconnect_map(); + do_reconnect_storage(); + + return 0; +} + +/*========================================== + * Request sc_data from charserver [Skotlex] + *------------------------------------------ + */ +int chrif_scdata_request(int account_id, int char_id) +{ +#ifdef ENABLE_SC_SAVING +#ifndef TXT_ONLY + if (charsave_method) + return charsave_load_scdata(account_id, char_id); +#endif + chrif_check(-1); + + WFIFOHEAD(char_fd, 10); + WFIFOW(char_fd, 0) = 0x2afc; + WFIFOL(char_fd, 2) = account_id; + WFIFOL(char_fd, 6) = char_id; + WFIFOSET(char_fd,10); +#endif + return 0; +} + +/*========================================== + * new auth system [Kevin] + *------------------------------------------ + */ +void chrif_authreq(struct map_session_data *sd) +{ + struct auth_node *auth_data; + auth_data=idb_get(auth_db, sd->bl.id); + + if(auth_data) { + if(auth_data->char_dat && + auth_data->account_id== sd->bl.id && + auth_data->login_id1 == sd->login_id1) + { //auth ok + pc_authok(sd, auth_data->login_id2, auth_data->connect_until_time, auth_data->char_dat); + } else { //auth failed + pc_authfail(sd); + chrif_char_offline(sd); //Set him offline, the char server likely has it set as online already. + } + if (auth_data->char_dat) + aFree(auth_data->char_dat); + idb_remove(auth_db, sd->bl.id); + } else { //data from char server has not arrived yet. + auth_data = aCalloc(1,sizeof(struct auth_node)); + auth_data->sd = sd; + auth_data->fd = sd->fd; + auth_data->account_id = sd->bl.id; + auth_data->login_id1 = sd->login_id1; + auth_data->node_created = gettick(); + uidb_put(auth_db, sd->bl.id, auth_data); + } + return; +} + +//character selected, insert into auth db +void chrif_authok(int fd) { + struct auth_node *auth_data; + RFIFOHEAD(fd); + + if (map_id2sd(RFIFOL(fd, 4)) != NULL) + //Someone with this account is already in! Do not store the info to prevent possible sync exploits. [Skotlex] + return; + + if ((auth_data =uidb_get(auth_db, RFIFOL(fd, 4))) != NULL) + { //Is the character already awaiting authorization? + if (auth_data->sd) + { + //First, check to see if the session data still exists (avoid dangling pointers) + if(session[auth_data->fd] && session[auth_data->fd]->session_data == auth_data->sd) + { + if (auth_data->char_dat == NULL && + auth_data->account_id == RFIFOL(fd, 4) && + auth_data->login_id1 == RFIFOL(fd, 8)) + { //Auth Ok + pc_authok(auth_data->sd, RFIFOL(fd, 16), RFIFOL(fd, 12), (struct mmo_charstatus*)RFIFOP(fd, 20)); + } else { //Auth Failed + pc_authfail(auth_data->sd); + chrif_char_offline(auth_data->sd); //Set him offline, the char server likely has it set as online already. + } + } //else: Character no longer exists, just go through. + } + //Delete the data of this node... + if (auth_data->char_dat) + aFree (auth_data->char_dat); + uidb_remove(auth_db, RFIFOL(fd, 4)); + return; + } + // Awaiting for client to connect. + auth_data = (struct auth_node *)aCalloc(1,sizeof(struct auth_node)); + auth_data->char_dat = (struct mmo_charstatus *) aCalloc(1,sizeof(struct mmo_charstatus)); + + auth_data->account_id=RFIFOL(fd, 4); + auth_data->login_id1=RFIFOL(fd, 8); + auth_data->connect_until_time=RFIFOL(fd, 12); + auth_data->login_id2=RFIFOL(fd, 16); + memcpy(auth_data->char_dat,RFIFOP(fd, 20),sizeof(struct mmo_charstatus)); + auth_data->node_created=gettick(); + uidb_put(auth_db, RFIFOL(fd, 4), auth_data); +} + +int auth_db_cleanup_sub(DBKey key,void *data,va_list ap) +{ + struct auth_node *node=(struct auth_node*)data; + + if(DIFF_TICK(gettick(),node->node_created)>30000) { + ShowNotice("Character (aid: %d) not authed within 30 seconds of character select!\n", node->account_id); + if (node->char_dat) + aFree(node->char_dat); + db_remove(auth_db, key); + return 1; + } + return 0; +} + +int auth_db_cleanup(int tid, unsigned int tick, int id, int data) { + auth_db->foreach(auth_db, auth_db_cleanup_sub); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_charselectreq(struct map_session_data *sd, unsigned long s_ip) +{ + nullpo_retr(-1, sd); + + if( !sd || !sd->bl.id || !sd->login_id1 ) + return -1; + chrif_check(-1); + + WFIFOHEAD(char_fd, 18); + WFIFOW(char_fd, 0) = 0x2b02; + WFIFOL(char_fd, 2) = sd->bl.id; + WFIFOL(char_fd, 6) = sd->login_id1; + WFIFOL(char_fd,10) = sd->login_id2; + WFIFOL(char_fd,14) = s_ip; + WFIFOSET(char_fd,18); + + return 0; +} + +/*========================================== + * キャラ名問い合わせ + *------------------------------------------ + */ +int chrif_searchcharid(int char_id) +{ + if( !char_id ) + return -1; + chrif_check(-1); + + WFIFOHEAD(char_fd, 6); + WFIFOW(char_fd,0) = 0x2b08; + WFIFOL(char_fd,2) = char_id; + WFIFOSET(char_fd,6); + + return 0; +} + +/*========================================== + * GMに変化要求 + *------------------------------------------ + */ +int chrif_changegm(int id, const char *pass, int len) +{ + if (battle_config.etc_log) + ShowInfo("chrif_changegm: account: %d, password: '%s'.\n", id, pass); + + chrif_check(-1); + + WFIFOHEAD(char_fd, len + 8); + WFIFOW(char_fd,0) = 0x2b0a; + WFIFOW(char_fd,2) = len + 8; + WFIFOL(char_fd,4) = id; + memcpy(WFIFOP(char_fd,8), pass, len); + WFIFOSET(char_fd, len + 8); + + return 0; +} + +/*========================================== + * Change Email + *------------------------------------------ + */ +int chrif_changeemail(int id, const char *actual_email, const char *new_email) +{ + if (battle_config.etc_log) + ShowInfo("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n", id, actual_email, new_email); + + chrif_check(-1); + + WFIFOHEAD(char_fd, 86); + WFIFOW(char_fd,0) = 0x2b0c; + WFIFOL(char_fd,2) = id; + memcpy(WFIFOP(char_fd,6), actual_email, 40); + memcpy(WFIFOP(char_fd,46), new_email, 40); + WFIFOSET(char_fd,86); + + return 0; +} + +/*========================================== + * Send message to char-server with a character name to do some operations (by Yor) + * Used to ask Char-server about a character name to have the account number to modify account file in login-server. + * type of operation: + * 1: block + * 2: ban + * 3: unblock + * 4: unban + * 5: changesex + *------------------------------------------ + */ +int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second) +{ + chrif_check(-1); + + WFIFOHEAD(char_fd, 44); + WFIFOW(char_fd, 0) = 0x2b0e; + WFIFOL(char_fd, 2) = id; // account_id of who ask (for answer) -1 if nobody + memcpy(WFIFOP(char_fd,6), character_name, NAME_LENGTH); + WFIFOW(char_fd, 30) = operation_type; // type of operation + if (operation_type == 2) { + WFIFOW(char_fd, 32) = year; + WFIFOW(char_fd, 34) = month; + WFIFOW(char_fd, 36) = day; + WFIFOW(char_fd, 38) = hour; + WFIFOW(char_fd, 40) = minute; + WFIFOW(char_fd, 42) = second; + } +// ShowInfo("chrif : sent 0x2b0e\n"); + WFIFOSET(char_fd,44); + + return 0; +} + +/*========================================== + * 性別変化要求 + *------------------------------------------ + */ +int chrif_changesex(int id, int sex) { + chrif_check(-1); + + WFIFOHEAD(char_fd, 9); + WFIFOW(char_fd,0) = 0x2b11; + WFIFOW(char_fd,2) = 9; + WFIFOL(char_fd,4) = id; + WFIFOB(char_fd,8) = sex; +// ShowInfo("chrif : sent 0x3000(changesex)\n"); + WFIFOSET(char_fd,9); + return 0; +} + +/*========================================== + * Answer after a request about a character name to do some operations (by Yor) + * Used to answer of chrif_char_ask_name. + * type of operation: + * 1: block + * 2: ban + * 3: unblock + * 4: unban + * 5: changesex + * type of answer: + * 0: login-server resquest done + * 1: player not found + * 2: gm level too low + * 3: login-server offline + *------------------------------------------ + */ +int chrif_char_ask_name_answer(int fd) +{ + int acc; + struct map_session_data *sd; + char output[256]; + char player_name[NAME_LENGTH]; + RFIFOHEAD(fd); + + acc = RFIFOL(fd,2); // account_id of who has asked (-1 if nobody) + memcpy(player_name, RFIFOP(fd,6), NAME_LENGTH-1); + player_name[NAME_LENGTH-1] = '\0'; + + sd = map_id2sd(acc); + if (acc >= 0 && sd != NULL) { + if (RFIFOW(fd, 32) == 1) // player not found + sprintf(output, "The player '%s' doesn't exist.", player_name); + else { + switch(RFIFOW(fd, 30)) { + case 1: // block + switch(RFIFOW(fd, 32)) { + case 0: // login-server resquest done + sprintf(output, "Login-server has been asked to block the player '%s'.", player_name); + break; + //case 1: // player not found + case 2: // gm level too low + sprintf(output, "Your GM level don't authorise you to block the player '%s'.", player_name); + break; + case 3: // login-server offline + sprintf(output, "Login-server is offline. Impossible to block the the player '%s'.", player_name); + break; + } + break; + case 2: // ban + switch(RFIFOW(fd, 32)) { + case 0: // login-server resquest done + sprintf(output, "Login-server has been asked to ban the player '%s'.", player_name); + break; + //case 1: // player not found + case 2: // gm level too low + sprintf(output, "Your GM level don't authorise you to ban the player '%s'.", player_name); + break; + case 3: // login-server offline + sprintf(output, "Login-server is offline. Impossible to ban the the player '%s'.", player_name); + break; + } + break; + case 3: // unblock + switch(RFIFOW(fd, 32)) { + case 0: // login-server resquest done + sprintf(output, "Login-server has been asked to unblock the player '%s'.", player_name); + break; + //case 1: // player not found + case 2: // gm level too low + sprintf(output, "Your GM level don't authorise you to unblock the player '%s'.", player_name); + break; + case 3: // login-server offline + sprintf(output, "Login-server is offline. Impossible to unblock the the player '%s'.", player_name); + break; + } + break; + case 4: // unban + switch(RFIFOW(fd, 32)) { + case 0: // login-server resquest done + sprintf(output, "Login-server has been asked to unban the player '%s'.", player_name); + break; + //case 1: // player not found + case 2: // gm level too low + sprintf(output, "Your GM level don't authorise you to unban the player '%s'.", player_name); + break; + case 3: // login-server offline + sprintf(output, "Login-server is offline. Impossible to unban the the player '%s'.", player_name); + break; + } + break; + case 5: // changesex + switch(RFIFOW(fd, 32)) { + case 0: // login-server resquest done + sprintf(output, "Login-server has been asked to change the sex of the player '%s'.", player_name); + break; + //case 1: // player not found + case 2: // gm level too low + sprintf(output, "Your GM level don't authorise you to change the sex of the player '%s'.", player_name); + break; + case 3: // login-server offline + sprintf(output, "Login-server is offline. Impossible to change the sex of the the player '%s'.", player_name); + break; + } + break; + } + } + if (output[0] != '\0') { + output[sizeof(output)-1] = '\0'; + clif_displaymessage(sd->fd, output); + } + } else + ShowError("chrif_char_ask_name_answer failed - player not online.\n"); + + return 0; +} + +/*========================================== + * End of GM change (@GM) (modified by Yor) + *------------------------------------------ + */ +int chrif_changedgm(int fd) +{ + int acc, level; + struct map_session_data *sd = NULL; + RFIFOHEAD(fd); + + acc = RFIFOL(fd,2); + level = RFIFOL(fd,6); + + sd = map_id2sd(acc); + + if (battle_config.etc_log) + ShowNotice("chrif_changedgm: account: %d, GM level 0 -> %d.\n", acc, level); + if (sd != NULL) { + if (level > 0) + clif_displaymessage(sd->fd, "GM modification success."); + else + clif_displaymessage(sd->fd, "Failure of GM modification."); + } + + return 0; +} + +/*========================================== + * 性別変化終了 (modified by Yor) + *------------------------------------------ + */ +int chrif_changedsex(int fd) +{ + int acc, sex, i; + struct map_session_data *sd; + RFIFOHEAD(fd); + + acc = RFIFOL(fd,2); + sex = RFIFOL(fd,6); + if (battle_config.etc_log) + ShowNotice("chrif_changedsex %d.\n", acc); + sd = map_id2sd(acc); + if (acc > 0) { + if (sd != NULL && sd->status.sex != sex) { + sd->status.sex = !sd->status.sex; + + // to avoid any problem with equipment and invalid sex, equipment is unequiped. + for (i = 0; i < MAX_INVENTORY; i++) { + if (sd->status.inventory[i].nameid && sd->status.inventory[i].equip) + pc_unequipitem((struct map_session_data*)sd, i, 2); + } + // reset skill of some job + if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) { + // remove specifical skills of Bard classes + for(i = 315; i <= 322; i++) { + if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) { + if (sd->status.skill_point > USHRT_MAX - sd->status.skill[i].lv) + sd->status.skill_point = USHRT_MAX; + else + sd->status.skill_point += sd->status.skill[i].lv; + sd->status.skill[i].id = 0; + sd->status.skill[i].lv = 0; + } + } + // remove specifical skills of Dancer classes + for(i = 323; i <= 330; i++) { + if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) { + if (sd->status.skill_point > USHRT_MAX - sd->status.skill[i].lv) + sd->status.skill_point = USHRT_MAX; + else + sd->status.skill_point += sd->status.skill[i].lv; + sd->status.skill[i].id = 0; + sd->status.skill[i].lv = 0; + } + } + clif_updatestatus(sd, SP_SKILLPOINT); + // change job if necessary + if (sd->status.sex) //Changed from Dancer + sd->status.class_ -= 1; + else //Changed from Bard + sd->status.class_ += 1; + //sd->class_ needs not be updated as both Dancer/Bard are the same. + } + // save character + //chrif_save(sd,1); Character will be saved on session closed -> map_quit + sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters + // do same modify in login-server for the account, but no in char-server (it ask again login_id1 to login, and don't remember it) + clif_displaymessage(sd->fd, "Your sex has been changed (need disconnection by the server)..."); + clif_setwaitclose(sd->fd); // forced to disconnect for the change + } + } else { + if (sd != NULL) { + ShowError("chrif_changedsex failed.\n"); + } + } + + return 0; +} + +/*========================================== + * 離婚情報同期要求 + *------------------------------------------ + */ +int chrif_divorce(int char_id, int partner_id) +{ + struct map_session_data *sd = NULL; + + if (!char_id || !partner_id) + return 0; + + nullpo_retr(0, sd = map_nick2sd(map_charid2nick(partner_id))); + if (sd->status.partner_id == char_id) { + int i; + //離婚(相方は既にキャラが消えている筈なので) + sd->status.partner_id = 0; + + //相方の結婚指輪を剥奪 + for(i = 0; i < MAX_INVENTORY; i++) + if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F) + pc_delitem(sd, i, 1, 0); + } + + return 0; +} + +/*========================================== + * Disconnection of a player (account has been deleted in login-server) by [Yor] + *------------------------------------------ + */ +int chrif_accountdeletion(int fd) +{ + int acc; + struct map_session_data *sd; + RFIFOHEAD(fd); + + acc = RFIFOL(fd,2); + if (battle_config.etc_log) + ShowNotice("chrif_accountdeletion %d.\n", acc); + sd = map_id2sd(acc); + if (acc > 0) { + if (sd != NULL) { + sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters + clif_displaymessage(sd->fd, "Your account has been deleted (disconnection)..."); + clif_setwaitclose(sd->fd); // forced to disconnect for the change + } + } else { + if (sd != NULL) + ShowError("chrif_accountdeletion failed - player not online.\n"); + } + + return 0; +} + +/*========================================== + * Disconnection of a player (account has been banned of has a status, from login-server) by [Yor] + *------------------------------------------ + */ +int chrif_accountban(int fd) +{ + int acc; + struct map_session_data *sd; + RFIFOHEAD(fd); + + acc = RFIFOL(fd,2); + if (battle_config.etc_log) + ShowNotice("chrif_accountban %d.\n", acc); + sd = map_id2sd(acc); + if (acc > 0) { + if (sd != NULL) { + sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters + if (RFIFOB(fd,6) == 0) { // 0: change of statut, 1: ban + switch (RFIFOL(fd,7)) { // status or final date of a banishment + case 1: // 0 = Unregistered ID + clif_displaymessage(sd->fd, "Your account has 'Unregistered'."); + break; + case 2: // 1 = Incorrect Password + clif_displaymessage(sd->fd, "Your account has an 'Incorrect Password'..."); + break; + case 3: // 2 = This ID is expired + clif_displaymessage(sd->fd, "Your account has expired."); + break; + case 4: // 3 = Rejected from Server + clif_displaymessage(sd->fd, "Your account has been rejected from server."); + break; + case 5: // 4 = You have been blocked by the GM Team + clif_displaymessage(sd->fd, "Your account has been blocked by the GM Team."); + break; + case 6: // 5 = Your Game's EXE file is not the latest version + clif_displaymessage(sd->fd, "Your Game's EXE file is not the latest version."); + break; + case 7: // 6 = Your are Prohibited to log in until %s + clif_displaymessage(sd->fd, "Your account has been prohibited to log in."); + break; + case 8: // 7 = Server is jammed due to over populated + clif_displaymessage(sd->fd, "Server is jammed due to over populated."); + break; + case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this) + clif_displaymessage(sd->fd, "Your account has not more authorised."); + break; + case 100: // 99 = This ID has been totally erased + clif_displaymessage(sd->fd, "Your account has been totally erased."); + break; + default: + clif_displaymessage(sd->fd, "Your account has not more authorised."); + break; + } + } else if (RFIFOB(fd,6) == 1) { // 0: change of statut, 1: ban + time_t timestamp; + char tmpstr[2048]; + timestamp = (time_t)RFIFOL(fd,7); // status or final date of a banishment + strcpy(tmpstr, "Your account has been banished until "); + strftime(tmpstr + strlen(tmpstr), 24, "%d-%m-%Y %H:%M:%S", localtime(×tamp)); + clif_displaymessage(sd->fd, tmpstr); + } + clif_setwaitclose(sd->fd); // forced to disconnect for the change + } + } else { + if (sd != NULL) + ShowError("chrif_accountban failed - player not online.\n"); + } + + return 0; +} + +//Disconnect the player out of the game, simple packet +//packet.w AID.L WHY.B 2+4+1 = 7byte +int chrif_disconnectplayer(int fd){ + struct map_session_data *sd; + RFIFOHEAD(fd); + + sd = map_id2sd(RFIFOL(fd, 2)); + + if(sd == NULL){ + return -1; + } + + if (!sd->fd) + { //No connection + if (sd->state.autotrade) + map_quit(sd); //Remove it. + //Else we don't remove it because the char should have a timer to remove the player because it force-quit before, + //and we don't want them kicking their previous instance before the 10 secs penalty time passes. [Skotlex] + return 0; + } + + switch(RFIFOB(fd, 6)){ + //clif_authfail_fd + case 1: //server closed + clif_authfail_fd(sd->fd, 1); + break; + + case 2: //someone else logged in + clif_authfail_fd(sd->fd, 2); + break; + + case 3: //server overpopulated + clif_authfail_fd(sd->fd, 4); + + break; + + case 4: //out of time payd for .. (avail) + clif_authfail_fd(sd->fd, 10); + break; + + case 5: //forced to dc by gm + clif_authfail_fd(sd->fd, 15); + break; + } + +return 0; +} + +/*========================================== + * Request to reload GM accounts and their levels: send to char-server by [Yor] + *------------------------------------------ + */ +int chrif_reloadGMdb(void) +{ + chrif_check(-1); + + WFIFOHEAD(char_fd, 2); + WFIFOW(char_fd,0) = 0x2af7; + WFIFOSET(char_fd, 2); + + return 0; +} + +/*========================================== + * Receiving GM accounts and their levels from char-server by [Yor] + *------------------------------------------ + */ +int chrif_recvgmaccounts(int fd) +{ + ShowInfo("From login-server: receiving information of '"CL_WHITE"%d"CL_RESET"' GM accounts.\n", pc_read_gm_account(fd)); + return 0; +} + +/*========================================== + * Request/Receive top 10 Fame character list + *------------------------------------------ + */ + +int chrif_updatefamelist(struct map_session_data *sd) +{ + char type; + chrif_check(-1); + + switch(sd->class_&MAPID_UPPERMASK) { + case MAPID_BLACKSMITH: + type = 1; + break; + case MAPID_ALCHEMIST: + type = 2; + break; + case MAPID_TAEKWON: + type = 3; + break; + default: + return 0; + } + + WFIFOHEAD(char_fd, 12); + WFIFOW(char_fd, 0) = 0x2b10; + WFIFOL(char_fd, 2) = sd->status.char_id; + WFIFOL(char_fd, 6) = sd->status.fame; + WFIFOB(char_fd, 10) = type; + WFIFOB(char_fd, 11) = pc_famerank(sd->status.char_id, sd->class_&MAPID_UPPERMASK); + WFIFOSET(char_fd, 12); + + return 0; +} + +int chrif_buildfamelist(void) +{ + chrif_check(-1); + + WFIFOHEAD(char_fd, 2); + WFIFOW(char_fd, 0) = 0x2b1a; + WFIFOSET(char_fd, 2); + + return 0; +} + +int chrif_recvfamelist(int fd) +{ + int num, size; + int total = 0, len = 8; + RFIFOHEAD(fd); + + malloc_tsetdword (smith_fame_list, 0, sizeof(smith_fame_list)); + malloc_tsetdword (chemist_fame_list, 0, sizeof(chemist_fame_list)); + malloc_tsetdword (taekwon_fame_list, 0, sizeof(taekwon_fame_list)); + + size = RFIFOW(fd, 6); //Blacksmith block size + for (num = 0; len < size && num < MAX_FAME_LIST; num++) { + memcpy(&smith_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list)); + len += sizeof(struct fame_list); + } + total += num; + + size = RFIFOW(fd, 4); //Alchemist block size + for (num = 0; len < size && num < MAX_FAME_LIST; num++) { + memcpy(&chemist_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list)); + len += sizeof(struct fame_list); + } + total += num; + + size = RFIFOW(fd, 2); //Total packet length + for (num = 0; len < size && num < MAX_FAME_LIST; num++) { + memcpy(&taekwon_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list)); + len += sizeof(struct fame_list); + } + total += num; + + ShowInfo("Received Fame List of '"CL_WHITE"%d"CL_RESET"' characters.\n", total); + + return 0; +} + +int chrif_save_scdata(struct map_session_data *sd) +{ //parses the sc_data of the player and sends it to the char-server for saving. [Skotlex] +#ifdef ENABLE_SC_SAVING + int i, count=0; + unsigned int tick; + struct status_change_data data; + struct TimerData *timer; + + if (sd->state.finalsave) //Character was already saved? + return -1; +#ifndef TXT_ONLY + if(charsave_method) //New 'Local' save + { + charsave_save_scdata(sd->status.account_id, sd->status.char_id, &sd->sc, MAX_STATUSCHANGE); + return 0; + } +#endif + + chrif_check(-1); + tick = gettick(); + + WFIFOHEAD(char_fd, 14 + SC_MAX*sizeof(struct status_change_data)); + WFIFOW(char_fd,0) = 0x2b1c; + WFIFOL(char_fd,4) = sd->status.account_id; + WFIFOL(char_fd,8) = sd->status.char_id; + for (i = 0; i < SC_MAX; i++) + { + if (sd->sc.data[i].timer == -1) + continue; + timer = get_timer(sd->sc.data[i].timer); + if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0) + continue; + data.tick = DIFF_TICK(timer->tick,tick); //Duration that is left before ending. + data.type = i; + data.val1 = sd->sc.data[i].val1; + data.val2 = sd->sc.data[i].val2; + data.val3 = sd->sc.data[i].val3; + data.val4 = sd->sc.data[i].val4; + memcpy(WFIFOP(char_fd,14 +count*sizeof(struct status_change_data)), + &data, sizeof(struct status_change_data)); + count++; + } + if (count == 0) + return 0; //Nothing to save. + WFIFOW(char_fd,12) = count; + WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size + WFIFOSET(char_fd,WFIFOW(char_fd,2)); +#endif + return 0; +} + +int chrif_load_scdata(int fd) +{ //Retrieve and load sc_data for a player. [Skotlex] +#ifdef ENABLE_SC_SAVING + struct map_session_data *sd; + struct status_change_data *data; + int aid, cid, i, count; + RFIFOHEAD(fd); + + aid = RFIFOL(fd,4); //Player Account ID + cid = RFIFOL(fd,8); //Player Char ID + + sd = map_id2sd(aid); + if (!sd) + { + ShowError("chrif_load_scdata: Player of AID %d not found!\n", aid); + return -1; + } + if (sd->status.char_id != cid) + { + ShowError("chrif_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid); + return -1; + } + count = RFIFOW(fd,12); //sc_count + for (i = 0; i < count; i++) + { + data = (struct status_change_data*)RFIFOP(fd,14 + i*sizeof(struct status_change_data)); + if (data->tick < 1) + { //Protection against invalid tick values. [Skotlex] + ShowWarning("chrif_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", data->tick, data->type, sd->status.name); + continue; + } + status_change_start(&sd->bl, data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, 15); + } +#endif + return 0; +} + +/*========================================== + * Send rates and motd to char server [Wizputer] + *------------------------------------------ + */ + int chrif_ragsrvinfo(int base_rate, int job_rate, int drop_rate) +{ + char buf[256]; + FILE *fp; + int i; + + chrif_check(-1); + + WFIFOHEAD(char_fd, sizeof(buf) + 10); + WFIFOW(char_fd,0) = 0x2b16; + WFIFOW(char_fd,2) = base_rate; + WFIFOW(char_fd,4) = job_rate; + WFIFOW(char_fd,6) = drop_rate; + + if ((fp = fopen(motd_txt, "r")) != NULL) { + if (fgets(buf, 250, fp) != NULL) { + for(i = 0; buf[i]; i++) { + if (buf[i] == '\r' || buf[i] == '\n') { + buf[i] = 0; + break; + } + } + WFIFOW(char_fd,8) = sizeof(buf) + 10; + memcpy(WFIFOP(char_fd,10), buf, sizeof(buf)); + } + fclose(fp); + } else { + malloc_tsetdword(buf, 0, sizeof(buf)); //No data found, send empty packets? + WFIFOW(char_fd,8) = sizeof(buf) + 10; + memcpy(WFIFOP(char_fd,10), buf, sizeof(buf)); + } + WFIFOSET(char_fd,WFIFOW(char_fd,8)); + return 0; +} + + +/*========================================= + * Tell char-server charcter disconnected [Wizputer] + *----------------------------------------- + */ + +int chrif_char_offline(struct map_session_data *sd) +{ + chrif_check(-1); + + WFIFOHEAD(char_fd, 10); + WFIFOW(char_fd,0) = 0x2b17; + WFIFOL(char_fd,2) = sd->status.char_id; + WFIFOL(char_fd,6) = sd->status.account_id; + WFIFOSET(char_fd,10); + + return 0; +} + +/*========================================= + * Tell char-server to reset all chars offline [Wizputer] + *----------------------------------------- + */ +int chrif_flush_fifo(void) { + chrif_check(-1); + + set_nonblocking(char_fd, 0); + flush_fifos(); + set_nonblocking(char_fd, 1); + + return 0; +} + +/*========================================= + * Tell char-server to reset all chars offline [Wizputer] + *----------------------------------------- + */ +int chrif_char_reset_offline(void) { + chrif_check(-1); + + WFIFOHEAD(char_fd, 2); + WFIFOW(char_fd,0) = 0x2b18; + WFIFOSET(char_fd,2); + + return 0; +} + +/*========================================= + * Tell char-server charcter is online [Wizputer] + *----------------------------------------- + */ + +int chrif_char_online(struct map_session_data *sd) +{ + chrif_check(-1); + + WFIFOHEAD(char_fd, 10); + WFIFOW(char_fd,0) = 0x2b19; + WFIFOL(char_fd,2) = sd->status.char_id; + WFIFOL(char_fd,6) = sd->status.account_id; + WFIFOSET(char_fd,10); + + return 0; +} + +int chrif_disconnect(int fd) { + if(fd == char_fd) { + char_fd = 0; + ShowWarning("Map Server disconnected from Char Server.\n\n"); + chrif_connected = 0; + + other_mapserver_count=0; //Reset counter. We receive ALL maps from all map-servers on reconnect. + map_eraseallipport(); + + //Attempt to reconnect in a second. [Skotlex] + add_timer(gettick() + 1000, check_connect_char_server, 0, 0); + } + return 0; +} + +void chrif_update_ip(int fd){ + unsigned long new_ip; + WFIFOHEAD(fd, 6); + new_ip = resolve_hostbyname(char_ip_str, NULL, NULL); + if (new_ip && new_ip != char_ip) + char_ip = new_ip; //Update char_ip + + new_ip = clif_refresh_ip(); + if (!new_ip) return; //No change + WFIFOW(fd, 0) = 0x2736; + WFIFOL(fd, 2) = new_ip; + WFIFOSET(fd, 6); +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_parse(int fd) +{ + int packet_len, cmd; + // only char-server can have an access to here. + // so, if it isn't the char-server, we disconnect the session (fd != char_fd). + if (fd != char_fd || session[fd]->eof) { + if (fd == char_fd && chrif_connected == 1) { + chrif_disconnect (fd); + } + else if (fd != char_fd) + ShowDebug("chrif_parse: Disconnecting invalid session #%d (is not the char-server)\n", fd); + + do_close(fd); + return 0; + } + + while (RFIFOREST(fd) >= 2 && !session[fd]->eof) { //Infinite loop on broken pipe fix. [Skotlex] + RFIFOHEAD(fd); + cmd = RFIFOW(fd,0); + if (cmd < 0x2af8 || cmd >= 0x2af8 + (sizeof(packet_len_table) / sizeof(packet_len_table[0])) || + packet_len_table[cmd-0x2af8] == 0) { + + int r = intif_parse(fd); // intifに渡す + + if (r == 1) continue; // intifで処理した + if (r == 2) return 0; // intifで処理したが、データが足りない + + session[fd]->eof = 1; + ShowWarning("chrif_parse: session #%d, intif_parse failed -> disconnected.\n", fd); + return 0; + } + packet_len = packet_len_table[cmd-0x2af8]; + if (packet_len == -1) { + if (RFIFOREST(fd) < 4) + return 0; + packet_len = RFIFOW(fd,2); + } + if ((int)RFIFOREST(fd) < packet_len) + return 0; + + switch(cmd) { + case 0x2af9: chrif_connectack(fd); break; + case 0x2afb: chrif_sendmapack(fd); break; + case 0x2afd: chrif_authok(fd); break; + case 0x2b00: map_setusers(fd); break; + case 0x2b03: clif_charselectok(RFIFOL(fd,2)); break; + case 0x2b04: chrif_recvmap(fd); break; + case 0x2b06: chrif_changemapserverack(fd); break; + case 0x2b07: clif_updatemaxid(RFIFOL(fd,2), RFIFOL(fd,6)); break; + case 0x2b09: map_addchariddb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break; + case 0x2b0b: chrif_changedgm(fd); break; + case 0x2b0d: chrif_changedsex(fd); break; + case 0x2b0f: chrif_char_ask_name_answer(fd); break; + case 0x2b12: chrif_divorce(RFIFOL(fd,2), RFIFOL(fd,6)); break; + case 0x2b13: chrif_accountdeletion(fd); break; + case 0x2b14: chrif_accountban(fd); break; + case 0x2b15: chrif_recvgmaccounts(fd); break; + case 0x2b1b: chrif_recvfamelist(fd); break; + case 0x2b1d: chrif_load_scdata(fd); break; + case 0x2b1e: chrif_update_ip(fd); break; + case 0x2b1f: chrif_disconnectplayer(fd); break; + case 0x2b20: chrif_removemap(fd); break; + case 0x2b21: chrif_save_ack(fd); break; + + default: + if (battle_config.error_log) + ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd); + session[fd]->eof = 1; + return 0; + } + if (fd == char_fd) //There's the slight chance we lost the connection during parse, in which case this would segfault if not checked [Skotlex] + RFIFOSKIP(fd, packet_len); + } + + return 0; +} + +int send_usercount_tochar(int tid, unsigned int tick, int id, int data) { + int count; + static int last_count = 0; + + chrif_check(-1); + + map_getallusers(&count); + + if (count == last_count) //No need to waste packets. + return 0; + last_count = count; + + WFIFOHEAD(char_fd, 4); + WFIFOW(char_fd,0) = 0x2afe; + WFIFOW(char_fd,2) = count; + WFIFOSET(char_fd,4); + return 0; +} + +/*========================================== + * timer関数 + * 今このmap鯖に繋がっているクライアント人数をchar鯖へ送る + *------------------------------------------ + */ +int send_users_tochar(int tid, unsigned int tick, int id, int data) { + int count, users=0, i; + struct map_session_data **all_sd; + + chrif_check(-1); + + all_sd = map_getallusers(&count); + WFIFOHEAD(char_fd, 6+8*users); + WFIFOW(char_fd,0) = 0x2aff; + for (i = 0; i < count; i++) { + WFIFOL(char_fd,6+8*users) = all_sd[i]->status.account_id; + WFIFOL(char_fd,6+8*users+4) = all_sd[i]->status.char_id; + users++; + } + WFIFOW(char_fd,2) = 6 + 8 * users; + WFIFOW(char_fd,4) = users; + WFIFOSET(char_fd,6+8*users); + + return 0; +} + +/*========================================== + * timer関数 + * char鯖との接続を確認し、もし切れていたら再度接続する + *------------------------------------------ + */ +int check_connect_char_server(int tid, unsigned int tick, int id, int data) { + static int displayed = 0; + if (char_fd <= 0 || session[char_fd] == NULL) { + if (!displayed) { + ShowStatus("Attempting to connect to Char Server. Please wait.\n"); + displayed = 1; + } + chrif_state = 0; + char_fd = make_connection(char_ip, char_port); + if (char_fd == -1) + { //Attempt to connect later. [Skotlex] + char_fd = 0; + return 0; + } + session[char_fd]->func_parse = chrif_parse; + realloc_fifo(char_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + + chrif_connect(char_fd); + chrif_connected = (chrif_state == 2); +#ifndef TXT_ONLY + srvinfo = 0; +#endif /* not TXT_ONLY */ + } else { +#ifndef TXT_ONLY + if (srvinfo == 0) { + chrif_ragsrvinfo(battle_config.base_exp_rate, battle_config.job_exp_rate, battle_config.item_rate_common); + srvinfo = 1; + } +#endif /* not TXT_ONLY */ +/* There is no need, the connection is TCP, so the packet is assured to arrive unless the connection dies [Skotlex] + //If for some reason the next iteration (10 secs) we are still not connected, + //assume the packets got lost, so we need to resend them. [Skotlex] + if (chrif_state == 0) + chrif_connect(char_fd); + else if (chrif_state == 1) + chrif_sendmap(char_fd); +*/ + } + if (chrif_isconnect()) displayed = 0; + return 0; +} + +int auth_db_final(DBKey k,void *d,va_list ap) { + struct auth_node *node=(struct auth_node*)d; + if (node->char_dat) + aFree(node->char_dat); + return 0; +} + +/*========================================== + * 終了 + *------------------------------------------ + */ +int do_final_chrif(void) +{ + delete_session(char_fd); + auth_db->destroy(auth_db, auth_db_final); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int do_init_chrif(void) +{ + add_timer_func_list(check_connect_char_server, "check_connect_char_server"); + add_timer_func_list(send_usercount_tochar, "send_usercount_tochar"); + add_timer_func_list(send_users_tochar, "send_users_tochar"); + add_timer_func_list(auth_db_cleanup, "auth_db_cleanup"); + add_timer_interval(gettick() + 1000, check_connect_char_server, 0, 0, 10 * 1000); +#ifdef TXT_ONLY + //Txt needs this more frequently because it is used for the online.html file. + add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, UPDATE_INTERVAL); +#else + add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, CHECK_INTERVAL); + add_timer_interval(gettick() + 1000, send_usercount_tochar, 0, 0, UPDATE_INTERVAL); +#endif + add_timer_interval(gettick() + 1000, auth_db_cleanup, 0, 0, 30 * 1000); + + auth_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + + return 0; +} diff --git a/src/map/chrif.h b/src/map/chrif.h index fc87f9b18..302b31f56 100644 --- a/src/map/chrif.h +++ b/src/map/chrif.h @@ -1,58 +1,58 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _CHRIF_H_ -#define _CHRIF_H_ - -struct auth_node{ - int account_id, login_id1, login_id2, sex, fd; - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - struct map_session_data *sd; //Data from logged on char. - struct mmo_charstatus *char_dat; //Data from char server. - unsigned int node_created; //For node auto-deleting -}; - -void chrif_setuserid(char*); -void chrif_setpasswd(char*); -void chrif_checkdefaultlogin(void); -int chrif_setip(char*); -void chrif_setport(int); - -int chrif_isconnect(void); - -extern int chrif_connected; -extern int other_mapserver_count; - -void chrif_authreq(struct map_session_data *); -void chrif_authok(int fd); -int chrif_scdata_request(int account_id, int char_id); -int chrif_save(struct map_session_data*, int flag); -int chrif_charselectreq(struct map_session_data *sd, unsigned long s_ip); -void check_fake_id(int fd, struct map_session_data *sd, int target_id); -int chrif_changemapserver(struct map_session_data *sd,short map,int x,int y,int ip,short port); - -int chrif_searchcharid(int char_id); -int chrif_changegm(int id,const char *pass,int len); -int chrif_changeemail(int id, const char *actual_email, const char *new_email); -int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second); -int chrif_reloadGMdb(void); -int chrif_updatefamelist(struct map_session_data *sd); -int chrif_buildfamelist(void); -int chrif_save_scdata(struct map_session_data *sd); -int chrif_ragsrvinfo(int base_rate,int job_rate, int drop_rate); -int chrif_char_offline(struct map_session_data *sd); -int chrif_char_reset_offline(void); -int send_users_tochar(int tid, unsigned int tick, int id, int data); -int chrif_char_online(struct map_session_data *sd); -int chrif_changesex(int id, int sex); -int chrif_chardisconnect(struct map_session_data *sd); -int check_connect_char_server(int tid, unsigned int tick, int id, int data); - -int chrif_pcauthok(int fd); - -int do_final_chrif(void); -int do_init_chrif(void); - -int chrif_flush_fifo(void); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _CHRIF_H_ +#define _CHRIF_H_ + +struct auth_node{ + int account_id, login_id1, login_id2, sex, fd; + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + struct map_session_data *sd; //Data from logged on char. + struct mmo_charstatus *char_dat; //Data from char server. + unsigned int node_created; //For node auto-deleting +}; + +void chrif_setuserid(char*); +void chrif_setpasswd(char*); +void chrif_checkdefaultlogin(void); +int chrif_setip(char*); +void chrif_setport(int); + +int chrif_isconnect(void); + +extern int chrif_connected; +extern int other_mapserver_count; + +void chrif_authreq(struct map_session_data *); +void chrif_authok(int fd); +int chrif_scdata_request(int account_id, int char_id); +int chrif_save(struct map_session_data*, int flag); +int chrif_charselectreq(struct map_session_data *sd, unsigned long s_ip); +void check_fake_id(int fd, struct map_session_data *sd, int target_id); +int chrif_changemapserver(struct map_session_data *sd,short map,int x,int y,int ip,short port); + +int chrif_searchcharid(int char_id); +int chrif_changegm(int id,const char *pass,int len); +int chrif_changeemail(int id, const char *actual_email, const char *new_email); +int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second); +int chrif_reloadGMdb(void); +int chrif_updatefamelist(struct map_session_data *sd); +int chrif_buildfamelist(void); +int chrif_save_scdata(struct map_session_data *sd); +int chrif_ragsrvinfo(int base_rate,int job_rate, int drop_rate); +int chrif_char_offline(struct map_session_data *sd); +int chrif_char_reset_offline(void); +int send_users_tochar(int tid, unsigned int tick, int id, int data); +int chrif_char_online(struct map_session_data *sd); +int chrif_changesex(int id, int sex); +int chrif_chardisconnect(struct map_session_data *sd); +int check_connect_char_server(int tid, unsigned int tick, int id, int data); + +int chrif_pcauthok(int fd); + +int do_final_chrif(void); +int do_init_chrif(void); + +int chrif_flush_fifo(void); + +#endif diff --git a/src/map/clif.h b/src/map/clif.h index 5ba025065..df8ac3441 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -1,368 +1,368 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _CLIF_H_ -#define _CLIF_H_ - -#include "map.h" - -// protocol version -#define PACKETVER 7 - -// packet DB -#define MAX_PACKET_DB 0x25f -#define MAX_PACKET_VER 25 - -struct packet_db { - short len; - void (*func)(int, struct map_session_data *); - short pos[20]; -}; - -// local define -enum { - ALL_CLIENT, - ALL_SAMEMAP, - AREA, - AREA_WOS, - AREA_WOC, - AREA_WOSC, - AREA_CHAT_WOC, - CHAT, - CHAT_WOS, - CHAT_MAINCHAT, - PARTY, - PARTY_WOS, - PARTY_SAMEMAP, - PARTY_SAMEMAP_WOS, - PARTY_AREA, - PARTY_AREA_WOS, - GUILD, - GUILD_WOS, - GUILD_SAMEMAP, // [Valaris] - GUILD_SAMEMAP_WOS, - GUILD_AREA, - GUILD_AREA_WOS, // end additions [Valaris] - SELF, - DUEL, - DUEL_WOS -}; - -extern struct packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB]; - -int clif_setip(char*); -void clif_setbindip(char*); -void clif_setport(int); - -unsigned long clif_getip_long(void); -unsigned long clif_refresh_ip(void); -int clif_getport(void); -int clif_countusers(void); -void clif_setwaitclose(int); - -int clif_authok(struct map_session_data *); -int clif_authfail_fd(int,int); -void clif_updatemaxid(int, int); -int clif_charselectok(int); -void check_fake_id(int fd, struct map_session_data *sd, int target_id); -int clif_dropflooritem(struct flooritem_data *); -int clif_clearflooritem(struct flooritem_data *,int); -int clif_clearchar(struct block_list*,int); // area or fd -int clif_clearchar_delay(unsigned int,struct block_list *,int); -#define clif_clearchar_area(bl,type) clif_clearchar(bl,type) -int clif_clearchar_id(int,int,int); -int clif_spawn(struct block_list*); //area -int clif_walkok(struct map_session_data*); // self -int clif_move(struct block_list*); // area -int clif_changemap(struct map_session_data*,short,int,int); //self -int clif_changemapserver(struct map_session_data*,char*,int,int,int,int); //self -int clif_blown(struct block_list *); // area -int clif_slide(struct block_list *,int,int); // area -int clif_fixpos(struct block_list *); // area -int clif_fixpos2(struct block_list *); // area -int clif_npcbuysell(struct map_session_data*,int); //self -int clif_buylist(struct map_session_data*,struct npc_data*); //self -int clif_selllist(struct map_session_data*); //self -int clif_scriptmes(struct map_session_data*,int,char*); //self -int clif_scriptnext(struct map_session_data*,int); //self -int clif_scriptclose(struct map_session_data*,int); //self -int clif_scriptmenu(struct map_session_data*,int,char*); //self -int clif_scriptinput(struct map_session_data*,int); //self -int clif_scriptinputstr(struct map_session_data *sd,int npcid); // self -int clif_cutin(struct map_session_data*,char*,int); //self -int clif_viewpoint(struct map_session_data*,int,int,int,int,int,int); //self -int clif_additem(struct map_session_data*,int,int,int); //self -int clif_delitem(struct map_session_data*,int,int); //self -int clif_updatestatus(struct map_session_data*,int); //self -int clif_changestatus(struct block_list*,int,int); //area -int clif_damage(struct block_list *,struct block_list *,unsigned int,int,int,int,int,int,int); // area -#define clif_takeitem(src,dst) clif_damage(src,dst,0,0,0,0,0,1,0) -int clif_changelook(struct block_list *,int,int); // area -void clif_changetraplook(struct block_list *bl,int val); // area -void clif_refreshlook(struct block_list *bl,int id,int type,int val,int area); //area specified in 'area' -int clif_arrowequip(struct map_session_data *sd,int val); //self -int clif_arrow_fail(struct map_session_data *sd,int type); //self -int clif_arrow_create_list(struct map_session_data *sd); //self -int clif_statusupack(struct map_session_data *,int,int,int); // self -int clif_equipitemack(struct map_session_data *,int,int,int); // self -int clif_unequipitemack(struct map_session_data *,int,int,int); // self -int clif_misceffect(struct block_list*,int); // area -int clif_misceffect2(struct block_list *bl,int type); -int clif_changeoption(struct block_list*); // area -int clif_useitemack(struct map_session_data*,int,int,int); // self -void clif_GlobalMessage(struct block_list *bl,char *message); -int clif_createchat(struct map_session_data*,int); // self -int clif_dispchat(struct chat_data*,int); // area or fd -int clif_joinchatfail(struct map_session_data*,int); // self -int clif_joinchatok(struct map_session_data*,struct chat_data*); // self -int clif_addchat(struct chat_data*,struct map_session_data*); // chat -int clif_changechatowner(struct chat_data*,struct map_session_data*); // chat -int clif_clearchat(struct chat_data*,int); // area or fd -int clif_leavechat(struct chat_data*,struct map_session_data*); // chat -int clif_changechatstatus(struct chat_data*); // chat -int clif_refresh(struct map_session_data*); // self - -int clif_fame_blacksmith(struct map_session_data *, int); -int clif_fame_alchemist(struct map_session_data *, int); -int clif_fame_taekwon(struct map_session_data *, int); - -void clif_emotion(struct block_list *bl,int type); -void clif_talkiebox(struct block_list *bl,char* talkie); -void clif_wedding_effect(struct block_list *bl); -void clif_divorced(struct map_session_data *sd, char *); -//void clif_sitting(int fd, struct map_session_data *sd); -//void clif_callpartner(struct map_session_data *sd); -void clif_adopt_process(struct map_session_data *sd); -void clif_sitting(struct map_session_data *sd); -void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,char *name,int type); -int clif_soundeffectall(struct block_list *bl, char *name, int type, int coverage); -void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, int target_id, unsigned int tick); -void clif_parse_LoadEndAck(int fd,struct map_session_data *sd); - -// trade -int clif_traderequest(struct map_session_data *sd,char *name); -int clif_tradestart(struct map_session_data *sd,int type); -int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount); -int clif_tradeitemok(struct map_session_data *sd,int index,int fail); -int clif_tradedeal_lock(struct map_session_data *sd,int fail); -int clif_tradecancelled(struct map_session_data *sd); -int clif_tradecompleted(struct map_session_data *sd,int fail); - -// storage -#include "storage.h" -void clif_storagelist(struct map_session_data *sd,struct storage *stor); -int clif_updatestorageamount(struct map_session_data *sd,struct storage *stor); -int clif_storageitemadded(struct map_session_data *sd,struct storage *stor,int index,int amount); -int clif_storageitemremoved(struct map_session_data *sd,int index,int amount); -int clif_storageclose(struct map_session_data *sd); -void clif_guildstoragelist(struct map_session_data *sd,struct guild_storage *stor); -int clif_updateguildstorageamount(struct map_session_data *sd,struct guild_storage *stor); -int clif_guildstorageitemadded(struct map_session_data *sd,struct guild_storage *stor,int index,int amount); - -int clif_insight(struct block_list *,va_list); // map_forallinmovearea callback -int clif_outsight(struct block_list *,va_list); // map_forallinmovearea callback - -int clif_class_change(struct block_list *bl,int class_,int type); -#define clif_mob_class_change(md, class_) clif_class_change(&md->bl, class_, 1) -int clif_mob_equip(struct mob_data *md,int nameid); // [Valaris] - -int clif_skillinfo(struct map_session_data *sd,int skillid,int type,int range); -int clif_skillinfoblock(struct map_session_data *sd); -int clif_skillup(struct map_session_data *sd,int skill_num); - -int clif_skillcasting(struct block_list* bl, - int src_id,int dst_id,int dst_x,int dst_y,int skill_num,int casttime); -int clif_skillcastcancel(struct block_list* bl); -int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype); -int clif_skill_damage(struct block_list *src,struct block_list *dst, - unsigned int tick,int sdelay,int ddelay,int damage,int div, - int skill_id,int skill_lv,int type); -int clif_skill_damage2(struct block_list *src,struct block_list *dst, - unsigned int tick,int sdelay,int ddelay,int damage,int div, - int skill_id,int skill_lv,int type); -int clif_skill_nodamage(struct block_list *src,struct block_list *dst, - int skill_id,int heal,int fail); -int clif_skill_poseffect(struct block_list *src,int skill_id, - int val,int x,int y,int tick); -int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst); -int clif_skill_warppoint(struct map_session_data *sd,int skill_num, int skill_lv, - const char *map1,const char *map2,const char *map3,const char *map4); -int clif_skill_memo(struct map_session_data *sd,int flag); -int clif_skill_teleportmessage(struct map_session_data *sd,int flag); -int clif_skill_produce_mix_list(struct map_session_data *sd, int trigger); - -int clif_produceeffect(struct map_session_data *sd,int flag,int nameid); - -int clif_skill_setunit(struct skill_unit *unit); -int clif_skill_delunit(struct skill_unit *unit); - -int clif_01ac(struct block_list *bl); - -int clif_autospell(struct map_session_data *sd,int skilllv); -int clif_devotion(struct map_session_data *sd); -int clif_marionette(struct block_list *src, struct block_list *target); -int clif_spiritball(struct map_session_data *sd); -int clif_combo_delay(struct block_list *src,int wait); -int clif_bladestop(struct block_list *src,struct block_list *dst,int bool_); -int clif_changemapcell(int m,int x,int y,int cell_type,int type); - -int clif_status_load(struct block_list *bl,int type, int flag); -int clif_status_change(struct block_list *bl,int type,int flag); - -int clif_wis_message(int fd,char *nick,char *mes,int mes_len); -int clif_wis_end(int fd,int flag); - -int clif_solved_charname(struct map_session_data *sd,int char_id); -int clif_charnameack(int fd, struct block_list *bl); -int clif_charnameupdate(struct map_session_data *ssd); - -int clif_use_card(struct map_session_data *sd,int idx); -int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag); - -void clif_inventorylist(struct map_session_data *sd); -void clif_equiplist(struct map_session_data *sd); - -int clif_cart_additem(struct map_session_data*,int,int,int); -int clif_cart_delitem(struct map_session_data*,int,int); -void clif_cartlist(struct map_session_data *sd); - -int clif_item_identify_list(struct map_session_data *sd); -int clif_item_identified(struct map_session_data *sd,int idx,int flag); -int clif_item_repair_list (struct map_session_data *sd, struct map_session_data *dstsd); -int clif_item_repaireffect(struct map_session_data *sd, int nameid, int flag); -int clif_item_refine_list(struct map_session_data *sd); - -int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv,const char *name); - -int clif_mvp_effect(struct map_session_data *sd); -int clif_mvp_item(struct map_session_data *sd,int nameid); -int clif_mvp_exp(struct map_session_data *sd,unsigned long exp); -void clif_changed_dir(struct block_list *bl, int area); - -// vending -int clif_openvendingreq(struct map_session_data *sd,int num); -int clif_showvendingboard(struct block_list* bl,char *message,int fd); -int clif_closevendingboard(struct block_list* bl,int fd); -int clif_vendinglist(struct map_session_data *sd,int id,struct vending *vending); -int clif_buyvending(struct map_session_data *sd,int index,int amount,int fail); -int clif_openvending(struct map_session_data *sd,int id,struct vending *vending); -int clif_vendingreport(struct map_session_data *sd,int index,int amount); - -int clif_movetoattack(struct map_session_data *sd,struct block_list *bl); - -// party -int clif_party_created(struct map_session_data *sd,int flag); -int clif_party_main_info(struct party_data *p, int fd); -int clif_party_join_info(struct party *p, struct map_session_data *sd); -int clif_party_info(struct party_data *p,int fd); -int clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd); -int clif_party_inviteack(struct map_session_data *sd,char *nick,int flag); -int clif_party_option(struct party_data *p,struct map_session_data *sd,int flag); -int clif_party_leaved(struct party_data *p,struct map_session_data *sd,int account_id,char *name,int flag); -int clif_party_message(struct party_data *p,int account_id,char *mes,int len); -int clif_party_move(struct party *p,struct map_session_data *sd,int online); -int clif_party_xy(struct map_session_data *sd); -int clif_party_xy_single(int fd, struct map_session_data *sd); -int clif_party_hp(struct map_session_data *sd); -int clif_hpmeter(struct map_session_data *sd); - -// guild -int clif_guild_created(struct map_session_data *sd,int flag); -int clif_guild_belonginfo(struct map_session_data *sd,struct guild *g); -int clif_guild_basicinfo(struct map_session_data *sd); -int clif_guild_allianceinfo(struct map_session_data *sd); -int clif_guild_memberlist(struct map_session_data *sd); -int clif_guild_skillinfo(struct map_session_data *sd); -int clif_guild_send_onlineinfo(struct map_session_data *sd); //[LuzZza] -int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag); -int clif_guild_invite(struct map_session_data *sd,struct guild *g); -int clif_guild_inviteack(struct map_session_data *sd,int flag); -int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes); -int clif_guild_expulsion(struct map_session_data *sd,const char *name,const char *mes,int account_id); -int clif_guild_positionchanged(struct guild *g,int idx); -int clif_guild_memberpositionchanged(struct guild *g,int idx); -int clif_guild_emblem(struct map_session_data *sd,struct guild *g); -int clif_guild_notice(struct map_session_data *sd,struct guild *g); -int clif_guild_message(struct guild *g,int account_id,const char *mes,int len); -int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv); -int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name); -int clif_guild_allianceack(struct map_session_data *sd,int flag); -int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag); -int clif_guild_oppositionack(struct map_session_data *sd,int flag); -int clif_guild_broken(struct map_session_data *sd,int flag); -int clif_guild_xy(struct map_session_data *sd); -int clif_guild_xy_single(int fd, struct map_session_data *sd); -int clif_guild_xy_remove(struct map_session_data *sd); - - -// atcommand -int clif_displaymessage(const int fd,char* mes); -int clif_disp_onlyself(struct map_session_data *sd,char *mes,int len); -void clif_disp_message(struct block_list *src, char *mes, int len, int type); -int clif_GMmessage(struct block_list *bl,char* mes,int len,int flag); -void clif_MainChatMessage(char* message); //luzza -int clif_announce(struct block_list *bl, char* mes, int len, unsigned long color, int flag); -int clif_heal(int fd,int type,int val); -int clif_resurrection(struct block_list *bl,int type); -int clif_set0199(int fd,int type); -int clif_pvpset(struct map_session_data *sd, int pvprank, int pvpnum,int type); -int clif_send0199(int map,int type); -int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val); - -//petsystem -int clif_catch_process(struct map_session_data *sd); -int clif_pet_rulet(struct map_session_data *sd,int data); -int clif_sendegg(struct map_session_data *sd); -int clif_send_petdata(struct map_session_data *sd,int type,int param); -int clif_send_petstatus(struct map_session_data *sd); -int clif_pet_emotion(struct pet_data *pd,int param); -int clif_pet_performance(struct block_list *bl,int param); -int clif_pet_equip(struct pet_data *pd); -int clif_pet_food(struct map_session_data *sd,int foodid,int fail); -int clif_send (unsigned char *buf, int len, struct block_list *bl, int type); -int clif_send_debug(struct map_session_data *sd, int cmd, int* args, int args_num); - -//friends list -int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap); -void clif_friendslist_send(struct map_session_data *sd); -void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type); - -// [Valaris] -int clif_mob_hp(struct mob_data *md); -int clif_weather(int m); // [Valaris] -int clif_specialeffect(struct block_list *bl,int type, int flag); // special effects [Valaris] -int clif_message(struct block_list *bl, char* msg); // messages (from mobs/npcs) [Valaris] - -int clif_GM_kickack(struct map_session_data *sd,int id); -int clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd,int type); -int clif_GM_silence(struct map_session_data *sd,struct map_session_data *tsd,int type); -int clif_timedout(struct map_session_data *sd); - -int clif_foreachclient(int (*)(struct map_session_data*,va_list),...); -int clif_disp_overhead(struct map_session_data *sd, char* mes); - -int do_final_clif(void); -int do_init_clif(void); - -void clif_get_weapon_view(TBL_PC* sd, unsigned short *rhand, unsigned short *lhand); - -int clif_party_xy_remove(struct map_session_data *sd); //Fix for minimap [Kevin] -void clif_gospel_info(struct map_session_data *sd, int type); -void clif_parse_ReqFeel(int fd, struct map_session_data *sd, int skilllv); -void clif_feel_info(struct map_session_data *sd, unsigned char feel_level, unsigned char type); -void clif_hate_info(struct map_session_data *sd, unsigned char hate_level,int class_, unsigned char type); -void clif_mission_info(struct map_session_data *sd, int mob_id, unsigned char progress); -void clif_feel_hate_reset(struct map_session_data *sd); - -// [blackhole89] -int clif_spawnhomun(struct homun_data *hd); -int clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int flag); -int clif_homskillinfoblock(struct map_session_data *sd); -void clif_homskillup(struct map_session_data *sd, int skill_num) ; //[orn] -int clif_hom_food(struct map_session_data *sd,int foodid,int fail); //[orn] -void clif_send_homdata(struct map_session_data *sd, int type, int param); //[orn] -int clif_hwalkok(struct homun_data *hd); //[orn] - -#endif - - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _CLIF_H_ +#define _CLIF_H_ + +#include "map.h" + +// protocol version +#define PACKETVER 7 + +// packet DB +#define MAX_PACKET_DB 0x25f +#define MAX_PACKET_VER 25 + +struct packet_db { + short len; + void (*func)(int, struct map_session_data *); + short pos[20]; +}; + +// local define +enum { + ALL_CLIENT, + ALL_SAMEMAP, + AREA, + AREA_WOS, + AREA_WOC, + AREA_WOSC, + AREA_CHAT_WOC, + CHAT, + CHAT_WOS, + CHAT_MAINCHAT, + PARTY, + PARTY_WOS, + PARTY_SAMEMAP, + PARTY_SAMEMAP_WOS, + PARTY_AREA, + PARTY_AREA_WOS, + GUILD, + GUILD_WOS, + GUILD_SAMEMAP, // [Valaris] + GUILD_SAMEMAP_WOS, + GUILD_AREA, + GUILD_AREA_WOS, // end additions [Valaris] + SELF, + DUEL, + DUEL_WOS +}; + +extern struct packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB]; + +int clif_setip(char*); +void clif_setbindip(char*); +void clif_setport(int); + +unsigned long clif_getip_long(void); +unsigned long clif_refresh_ip(void); +int clif_getport(void); +int clif_countusers(void); +void clif_setwaitclose(int); + +int clif_authok(struct map_session_data *); +int clif_authfail_fd(int,int); +void clif_updatemaxid(int, int); +int clif_charselectok(int); +void check_fake_id(int fd, struct map_session_data *sd, int target_id); +int clif_dropflooritem(struct flooritem_data *); +int clif_clearflooritem(struct flooritem_data *,int); +int clif_clearchar(struct block_list*,int); // area or fd +int clif_clearchar_delay(unsigned int,struct block_list *,int); +#define clif_clearchar_area(bl,type) clif_clearchar(bl,type) +int clif_clearchar_id(int,int,int); +int clif_spawn(struct block_list*); //area +int clif_walkok(struct map_session_data*); // self +int clif_move(struct block_list*); // area +int clif_changemap(struct map_session_data*,short,int,int); //self +int clif_changemapserver(struct map_session_data*,char*,int,int,int,int); //self +int clif_blown(struct block_list *); // area +int clif_slide(struct block_list *,int,int); // area +int clif_fixpos(struct block_list *); // area +int clif_fixpos2(struct block_list *); // area +int clif_npcbuysell(struct map_session_data*,int); //self +int clif_buylist(struct map_session_data*,struct npc_data*); //self +int clif_selllist(struct map_session_data*); //self +int clif_scriptmes(struct map_session_data*,int,char*); //self +int clif_scriptnext(struct map_session_data*,int); //self +int clif_scriptclose(struct map_session_data*,int); //self +int clif_scriptmenu(struct map_session_data*,int,char*); //self +int clif_scriptinput(struct map_session_data*,int); //self +int clif_scriptinputstr(struct map_session_data *sd,int npcid); // self +int clif_cutin(struct map_session_data*,char*,int); //self +int clif_viewpoint(struct map_session_data*,int,int,int,int,int,int); //self +int clif_additem(struct map_session_data*,int,int,int); //self +int clif_delitem(struct map_session_data*,int,int); //self +int clif_updatestatus(struct map_session_data*,int); //self +int clif_changestatus(struct block_list*,int,int); //area +int clif_damage(struct block_list *,struct block_list *,unsigned int,int,int,int,int,int,int); // area +#define clif_takeitem(src,dst) clif_damage(src,dst,0,0,0,0,0,1,0) +int clif_changelook(struct block_list *,int,int); // area +void clif_changetraplook(struct block_list *bl,int val); // area +void clif_refreshlook(struct block_list *bl,int id,int type,int val,int area); //area specified in 'area' +int clif_arrowequip(struct map_session_data *sd,int val); //self +int clif_arrow_fail(struct map_session_data *sd,int type); //self +int clif_arrow_create_list(struct map_session_data *sd); //self +int clif_statusupack(struct map_session_data *,int,int,int); // self +int clif_equipitemack(struct map_session_data *,int,int,int); // self +int clif_unequipitemack(struct map_session_data *,int,int,int); // self +int clif_misceffect(struct block_list*,int); // area +int clif_misceffect2(struct block_list *bl,int type); +int clif_changeoption(struct block_list*); // area +int clif_useitemack(struct map_session_data*,int,int,int); // self +void clif_GlobalMessage(struct block_list *bl,char *message); +int clif_createchat(struct map_session_data*,int); // self +int clif_dispchat(struct chat_data*,int); // area or fd +int clif_joinchatfail(struct map_session_data*,int); // self +int clif_joinchatok(struct map_session_data*,struct chat_data*); // self +int clif_addchat(struct chat_data*,struct map_session_data*); // chat +int clif_changechatowner(struct chat_data*,struct map_session_data*); // chat +int clif_clearchat(struct chat_data*,int); // area or fd +int clif_leavechat(struct chat_data*,struct map_session_data*); // chat +int clif_changechatstatus(struct chat_data*); // chat +int clif_refresh(struct map_session_data*); // self + +int clif_fame_blacksmith(struct map_session_data *, int); +int clif_fame_alchemist(struct map_session_data *, int); +int clif_fame_taekwon(struct map_session_data *, int); + +void clif_emotion(struct block_list *bl,int type); +void clif_talkiebox(struct block_list *bl,char* talkie); +void clif_wedding_effect(struct block_list *bl); +void clif_divorced(struct map_session_data *sd, char *); +//void clif_sitting(int fd, struct map_session_data *sd); +//void clif_callpartner(struct map_session_data *sd); +void clif_adopt_process(struct map_session_data *sd); +void clif_sitting(struct map_session_data *sd); +void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,char *name,int type); +int clif_soundeffectall(struct block_list *bl, char *name, int type, int coverage); +void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, int target_id, unsigned int tick); +void clif_parse_LoadEndAck(int fd,struct map_session_data *sd); + +// trade +int clif_traderequest(struct map_session_data *sd,char *name); +int clif_tradestart(struct map_session_data *sd,int type); +int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount); +int clif_tradeitemok(struct map_session_data *sd,int index,int fail); +int clif_tradedeal_lock(struct map_session_data *sd,int fail); +int clif_tradecancelled(struct map_session_data *sd); +int clif_tradecompleted(struct map_session_data *sd,int fail); + +// storage +#include "storage.h" +void clif_storagelist(struct map_session_data *sd,struct storage *stor); +int clif_updatestorageamount(struct map_session_data *sd,struct storage *stor); +int clif_storageitemadded(struct map_session_data *sd,struct storage *stor,int index,int amount); +int clif_storageitemremoved(struct map_session_data *sd,int index,int amount); +int clif_storageclose(struct map_session_data *sd); +void clif_guildstoragelist(struct map_session_data *sd,struct guild_storage *stor); +int clif_updateguildstorageamount(struct map_session_data *sd,struct guild_storage *stor); +int clif_guildstorageitemadded(struct map_session_data *sd,struct guild_storage *stor,int index,int amount); + +int clif_insight(struct block_list *,va_list); // map_forallinmovearea callback +int clif_outsight(struct block_list *,va_list); // map_forallinmovearea callback + +int clif_class_change(struct block_list *bl,int class_,int type); +#define clif_mob_class_change(md, class_) clif_class_change(&md->bl, class_, 1) +int clif_mob_equip(struct mob_data *md,int nameid); // [Valaris] + +int clif_skillinfo(struct map_session_data *sd,int skillid,int type,int range); +int clif_skillinfoblock(struct map_session_data *sd); +int clif_skillup(struct map_session_data *sd,int skill_num); + +int clif_skillcasting(struct block_list* bl, + int src_id,int dst_id,int dst_x,int dst_y,int skill_num,int casttime); +int clif_skillcastcancel(struct block_list* bl); +int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype); +int clif_skill_damage(struct block_list *src,struct block_list *dst, + unsigned int tick,int sdelay,int ddelay,int damage,int div, + int skill_id,int skill_lv,int type); +int clif_skill_damage2(struct block_list *src,struct block_list *dst, + unsigned int tick,int sdelay,int ddelay,int damage,int div, + int skill_id,int skill_lv,int type); +int clif_skill_nodamage(struct block_list *src,struct block_list *dst, + int skill_id,int heal,int fail); +int clif_skill_poseffect(struct block_list *src,int skill_id, + int val,int x,int y,int tick); +int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst); +int clif_skill_warppoint(struct map_session_data *sd,int skill_num, int skill_lv, + const char *map1,const char *map2,const char *map3,const char *map4); +int clif_skill_memo(struct map_session_data *sd,int flag); +int clif_skill_teleportmessage(struct map_session_data *sd,int flag); +int clif_skill_produce_mix_list(struct map_session_data *sd, int trigger); + +int clif_produceeffect(struct map_session_data *sd,int flag,int nameid); + +int clif_skill_setunit(struct skill_unit *unit); +int clif_skill_delunit(struct skill_unit *unit); + +int clif_01ac(struct block_list *bl); + +int clif_autospell(struct map_session_data *sd,int skilllv); +int clif_devotion(struct map_session_data *sd); +int clif_marionette(struct block_list *src, struct block_list *target); +int clif_spiritball(struct map_session_data *sd); +int clif_combo_delay(struct block_list *src,int wait); +int clif_bladestop(struct block_list *src,struct block_list *dst,int bool_); +int clif_changemapcell(int m,int x,int y,int cell_type,int type); + +int clif_status_load(struct block_list *bl,int type, int flag); +int clif_status_change(struct block_list *bl,int type,int flag); + +int clif_wis_message(int fd,char *nick,char *mes,int mes_len); +int clif_wis_end(int fd,int flag); + +int clif_solved_charname(struct map_session_data *sd,int char_id); +int clif_charnameack(int fd, struct block_list *bl); +int clif_charnameupdate(struct map_session_data *ssd); + +int clif_use_card(struct map_session_data *sd,int idx); +int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag); + +void clif_inventorylist(struct map_session_data *sd); +void clif_equiplist(struct map_session_data *sd); + +int clif_cart_additem(struct map_session_data*,int,int,int); +int clif_cart_delitem(struct map_session_data*,int,int); +void clif_cartlist(struct map_session_data *sd); + +int clif_item_identify_list(struct map_session_data *sd); +int clif_item_identified(struct map_session_data *sd,int idx,int flag); +int clif_item_repair_list (struct map_session_data *sd, struct map_session_data *dstsd); +int clif_item_repaireffect(struct map_session_data *sd, int nameid, int flag); +int clif_item_refine_list(struct map_session_data *sd); + +int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv,const char *name); + +int clif_mvp_effect(struct map_session_data *sd); +int clif_mvp_item(struct map_session_data *sd,int nameid); +int clif_mvp_exp(struct map_session_data *sd,unsigned long exp); +void clif_changed_dir(struct block_list *bl, int area); + +// vending +int clif_openvendingreq(struct map_session_data *sd,int num); +int clif_showvendingboard(struct block_list* bl,char *message,int fd); +int clif_closevendingboard(struct block_list* bl,int fd); +int clif_vendinglist(struct map_session_data *sd,int id,struct vending *vending); +int clif_buyvending(struct map_session_data *sd,int index,int amount,int fail); +int clif_openvending(struct map_session_data *sd,int id,struct vending *vending); +int clif_vendingreport(struct map_session_data *sd,int index,int amount); + +int clif_movetoattack(struct map_session_data *sd,struct block_list *bl); + +// party +int clif_party_created(struct map_session_data *sd,int flag); +int clif_party_main_info(struct party_data *p, int fd); +int clif_party_join_info(struct party *p, struct map_session_data *sd); +int clif_party_info(struct party_data *p,int fd); +int clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd); +int clif_party_inviteack(struct map_session_data *sd,char *nick,int flag); +int clif_party_option(struct party_data *p,struct map_session_data *sd,int flag); +int clif_party_leaved(struct party_data *p,struct map_session_data *sd,int account_id,char *name,int flag); +int clif_party_message(struct party_data *p,int account_id,char *mes,int len); +int clif_party_move(struct party *p,struct map_session_data *sd,int online); +int clif_party_xy(struct map_session_data *sd); +int clif_party_xy_single(int fd, struct map_session_data *sd); +int clif_party_hp(struct map_session_data *sd); +int clif_hpmeter(struct map_session_data *sd); + +// guild +int clif_guild_created(struct map_session_data *sd,int flag); +int clif_guild_belonginfo(struct map_session_data *sd,struct guild *g); +int clif_guild_basicinfo(struct map_session_data *sd); +int clif_guild_allianceinfo(struct map_session_data *sd); +int clif_guild_memberlist(struct map_session_data *sd); +int clif_guild_skillinfo(struct map_session_data *sd); +int clif_guild_send_onlineinfo(struct map_session_data *sd); //[LuzZza] +int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag); +int clif_guild_invite(struct map_session_data *sd,struct guild *g); +int clif_guild_inviteack(struct map_session_data *sd,int flag); +int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes); +int clif_guild_expulsion(struct map_session_data *sd,const char *name,const char *mes,int account_id); +int clif_guild_positionchanged(struct guild *g,int idx); +int clif_guild_memberpositionchanged(struct guild *g,int idx); +int clif_guild_emblem(struct map_session_data *sd,struct guild *g); +int clif_guild_notice(struct map_session_data *sd,struct guild *g); +int clif_guild_message(struct guild *g,int account_id,const char *mes,int len); +int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv); +int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name); +int clif_guild_allianceack(struct map_session_data *sd,int flag); +int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag); +int clif_guild_oppositionack(struct map_session_data *sd,int flag); +int clif_guild_broken(struct map_session_data *sd,int flag); +int clif_guild_xy(struct map_session_data *sd); +int clif_guild_xy_single(int fd, struct map_session_data *sd); +int clif_guild_xy_remove(struct map_session_data *sd); + + +// atcommand +int clif_displaymessage(const int fd,char* mes); +int clif_disp_onlyself(struct map_session_data *sd,char *mes,int len); +void clif_disp_message(struct block_list *src, char *mes, int len, int type); +int clif_GMmessage(struct block_list *bl,char* mes,int len,int flag); +void clif_MainChatMessage(char* message); //luzza +int clif_announce(struct block_list *bl, char* mes, int len, unsigned long color, int flag); +int clif_heal(int fd,int type,int val); +int clif_resurrection(struct block_list *bl,int type); +int clif_set0199(int fd,int type); +int clif_pvpset(struct map_session_data *sd, int pvprank, int pvpnum,int type); +int clif_send0199(int map,int type); +int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val); + +//petsystem +int clif_catch_process(struct map_session_data *sd); +int clif_pet_rulet(struct map_session_data *sd,int data); +int clif_sendegg(struct map_session_data *sd); +int clif_send_petdata(struct map_session_data *sd,int type,int param); +int clif_send_petstatus(struct map_session_data *sd); +int clif_pet_emotion(struct pet_data *pd,int param); +int clif_pet_performance(struct block_list *bl,int param); +int clif_pet_equip(struct pet_data *pd); +int clif_pet_food(struct map_session_data *sd,int foodid,int fail); +int clif_send (unsigned char *buf, int len, struct block_list *bl, int type); +int clif_send_debug(struct map_session_data *sd, int cmd, int* args, int args_num); + +//friends list +int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap); +void clif_friendslist_send(struct map_session_data *sd); +void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type); + +// [Valaris] +int clif_mob_hp(struct mob_data *md); +int clif_weather(int m); // [Valaris] +int clif_specialeffect(struct block_list *bl,int type, int flag); // special effects [Valaris] +int clif_message(struct block_list *bl, char* msg); // messages (from mobs/npcs) [Valaris] + +int clif_GM_kickack(struct map_session_data *sd,int id); +int clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd,int type); +int clif_GM_silence(struct map_session_data *sd,struct map_session_data *tsd,int type); +int clif_timedout(struct map_session_data *sd); + +int clif_foreachclient(int (*)(struct map_session_data*,va_list),...); +int clif_disp_overhead(struct map_session_data *sd, char* mes); + +int do_final_clif(void); +int do_init_clif(void); + +void clif_get_weapon_view(TBL_PC* sd, unsigned short *rhand, unsigned short *lhand); + +int clif_party_xy_remove(struct map_session_data *sd); //Fix for minimap [Kevin] +void clif_gospel_info(struct map_session_data *sd, int type); +void clif_parse_ReqFeel(int fd, struct map_session_data *sd, int skilllv); +void clif_feel_info(struct map_session_data *sd, unsigned char feel_level, unsigned char type); +void clif_hate_info(struct map_session_data *sd, unsigned char hate_level,int class_, unsigned char type); +void clif_mission_info(struct map_session_data *sd, int mob_id, unsigned char progress); +void clif_feel_hate_reset(struct map_session_data *sd); + +// [blackhole89] +int clif_spawnhomun(struct homun_data *hd); +int clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int flag); +int clif_homskillinfoblock(struct map_session_data *sd); +void clif_homskillup(struct map_session_data *sd, int skill_num) ; //[orn] +int clif_hom_food(struct map_session_data *sd,int foodid,int fail); //[orn] +void clif_send_homdata(struct map_session_data *sd, int type, int param); //[orn] +int clif_hwalkok(struct homun_data *hd); //[orn] + +#endif + + diff --git a/src/map/date.c b/src/map/date.c index 3bb7dca66..4643d6780 100644 --- a/src/map/date.c +++ b/src/map/date.c @@ -1,72 +1,72 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include "date.h" -#include - -int date_get_year(void) -{ - time_t t; - struct tm * lt; - t = time(NULL); - lt = localtime(&t); - return lt->tm_year+1900; -} -int date_get_month(void) -{ - time_t t; - struct tm * lt; - t = time(NULL); - lt = localtime(&t); - return lt->tm_mon+1; -} -int date_get_day(void) -{ - time_t t; - struct tm * lt; - t = time(NULL); - lt = localtime(&t); - return lt->tm_mday; -} -int date_get_hour(void) -{ - time_t t; - struct tm * lt; - t = time(NULL); - lt = localtime(&t); - return lt->tm_hour; -} - -int date_get_min(void) -{ - time_t t; - struct tm * lt; - t = time(NULL); - lt = localtime(&t); - return lt->tm_min; -} - -int date_get_sec(void) -{ - time_t t; - struct tm * lt; - t = time(NULL); - lt = localtime(&t); - return lt->tm_sec; -} - -int is_day_of_sun(void) -{ - return date_get_day()%2 == 0; -} - -int is_day_of_moon(void) -{ - return date_get_day()%2 == 1; -} - -int is_day_of_star(void) -{ - return date_get_day()%5 == 0; -} - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include "date.h" +#include + +int date_get_year(void) +{ + time_t t; + struct tm * lt; + t = time(NULL); + lt = localtime(&t); + return lt->tm_year+1900; +} +int date_get_month(void) +{ + time_t t; + struct tm * lt; + t = time(NULL); + lt = localtime(&t); + return lt->tm_mon+1; +} +int date_get_day(void) +{ + time_t t; + struct tm * lt; + t = time(NULL); + lt = localtime(&t); + return lt->tm_mday; +} +int date_get_hour(void) +{ + time_t t; + struct tm * lt; + t = time(NULL); + lt = localtime(&t); + return lt->tm_hour; +} + +int date_get_min(void) +{ + time_t t; + struct tm * lt; + t = time(NULL); + lt = localtime(&t); + return lt->tm_min; +} + +int date_get_sec(void) +{ + time_t t; + struct tm * lt; + t = time(NULL); + lt = localtime(&t); + return lt->tm_sec; +} + +int is_day_of_sun(void) +{ + return date_get_day()%2 == 0; +} + +int is_day_of_moon(void) +{ + return date_get_day()%2 == 1; +} + +int is_day_of_star(void) +{ + return date_get_day()%5 == 0; +} + diff --git a/src/map/date.h b/src/map/date.h index 2dfbf58dd..2b8ffe991 100644 --- a/src/map/date.h +++ b/src/map/date.h @@ -1,17 +1,17 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _DATE_H_ -#define _DATE_H_ -#endif - -int date_get_year(void); -int date_get_month(void); -int date_get_day(void); -int date_get_hour(void); -int date_get_min(void); -int date_get_sec(void); - -int is_day_of_sun(void); -int is_day_of_moon(void); -int is_day_of_star(void); +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _DATE_H_ +#define _DATE_H_ +#endif + +int date_get_year(void); +int date_get_month(void); +int date_get_day(void); +int date_get_hour(void); +int date_get_min(void); +int date_get_sec(void); + +int is_day_of_sun(void); +int is_day_of_moon(void); +int is_day_of_star(void); diff --git a/src/map/guild.c b/src/map/guild.c index 1fe0f526d..0b3be181b 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -1,2017 +1,2017 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include - -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/malloc.h" -#include "../common/showmsg.h" -#include "../common/ers.h" - -#include "map.h" -#include "guild.h" -#include "storage.h" -#include "battle.h" -#include "npc.h" -#include "pc.h" -#include "status.h" -#include "mob.h" -#include "intif.h" -#include "clif.h" -#include "skill.h" -#include "log.h" - -static struct guild* guild_cache; //For fast retrieval of the same guild over and over. [Skotlex] -static struct dbt *guild_db; -static struct dbt *castle_db; -static struct dbt *guild_expcache_db; -static struct dbt *guild_infoevent_db; -static struct dbt *guild_castleinfoevent_db; - -struct eventlist { - char name[50]; - struct eventlist *next; -}; - -// ギルドのEXPキャッシュのフラッシュに関連する定数 -#define GUILD_SEND_XY_INVERVAL 5000 // 座標やHP送信の間隔 -#define GUILD_PAYEXP_INVERVAL 10000 // 間隔(キャッシュの最大生存時間、ミリ秒) -#define GUILD_PAYEXP_LIST 8192 // キャッシュの最大数 - -// ギルドのEXPキャッシュ -struct guild_expcache { - int guild_id, account_id, char_id; - unsigned int exp; -}; -static struct eri *expcache_ers; //For handling of guild exp payment. - -struct{ - int id; - int max; - struct{ - short id; - short lv; - }need[6]; -} guild_skill_tree[MAX_GUILDSKILL]; - -// timer for auto saving guild data during WoE -#define GUILD_SAVE_INTERVAL 300000 -int guild_save_timer = -1; - -int guild_payexp_timer(int tid,unsigned int tick,int id,int data); -int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data); -int guild_save_sub(int tid,unsigned int tick,int id,int data); -static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data); - - // Modified [Komurka] -int guild_skill_get_max (int id) -{ - if (id < GD_SKILLBASE || id > GD_SKILLBASE+MAX_GUILDSKILL) - return 0; - return guild_skill_tree[id-GD_SKILLBASE].max; -} - -// ギルドスキルがあるか確認 -int guild_checkskill(struct guild *g,int id) -{ - int idx = id-GD_SKILLBASE; - if (idx < 0 || idx >= MAX_GUILDSKILL) - return 0; - return g->skill[idx].lv; -} - -/*========================================== - * guild_skill_tree.txt reading - from jA [Komurka] - *------------------------------------------ - */ -int guild_read_guildskill_tree_db(void) -{ - int i,k,id=0,ln=0; - FILE *fp; - char line[1024],*p; - - malloc_set(guild_skill_tree,0,sizeof(guild_skill_tree)); - sprintf(line, "%s/guild_skill_tree.txt", db_path); - if( (fp=fopen(line,"r"))==NULL){ - ShowError("can't read %s\n", line); - return -1; - } - while(fgets(line,1020,fp)){ - char *split[50]; - if(line[0]=='/' && line[1]=='/') - continue; - for(i=0,p=line;i<12 && p;i++){ - split[i]=p; - p=strchr(p,','); - if(p) *p++=0; - } - if(i<12) - continue; - id = atoi(split[0]) - GD_SKILLBASE; - if(id<0 || id>=MAX_GUILDSKILL) - continue; - guild_skill_tree[id].id=atoi(split[0]); - guild_skill_tree[id].max=atoi(split[1]); - if (guild_skill_tree[id].id==GD_GLORYGUILD && battle_config.require_glory_guild && guild_skill_tree[id].max==0) guild_skill_tree[id].max=1; - for(k=0;k<5;k++){ - guild_skill_tree[id].need[k].id=atoi(split[k*2+2]); - guild_skill_tree[id].need[k].lv=atoi(split[k*2+3]); - } - ln++; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"guild_skill_tree.txt"); - - return 0; -} - -/*========================================== - * Guild skill check - from jA [Komurka] - *------------------------------------------ - */ -int guild_check_skill_require(struct guild *g,int id) -{ - int i; - int idx = id-GD_SKILLBASE; - - if(g == NULL) - return 0; - - if (idx < 0 || idx >= MAX_GUILDSKILL) - return 0; - - for(i=0;i<5;i++) - { - if(guild_skill_tree[idx].need[i].id == 0) break; - if(guild_skill_tree[idx].need[i].lv > guild_checkskill(g,guild_skill_tree[idx].need[i].id)) - return 0; - } - return 1; -} - -static int guild_read_castledb(void) -{ - FILE *fp; - char line[1024]; - int j,ln=0; - char *str[32],*p; - struct guild_castle *gc; - - sprintf(line, "%s/castle_db.txt", db_path); - if( (fp=fopen(line,"r"))==NULL){ - ShowError("can't read %s\n", line); - return -1; - } - - while(fgets(line,1020,fp)){ - if(line[0]=='/' && line[1]=='/') - continue; - malloc_tsetdword(str,0,sizeof(str)); - for(j=0,p=line;j<6 && p;j++){ - str[j]=p; - p=strchr(p,','); - if(p) *p++=0; - } - if (j < 4) //Insufficient data for castle. [Skotlex] - { - ShowError("castle_db.txt: invalid line '%s'\n", line); - continue; - } - - gc=(struct guild_castle *)aCalloc(1,sizeof(struct guild_castle)); - gc->castle_id=atoi(str[0]); - memcpy(gc->map_name,str[1],MAP_NAME_LENGTH-1); - memcpy(gc->castle_name,str[2],NAME_LENGTH-1); - memcpy(gc->castle_event,str[3],NAME_LENGTH-1); - - idb_put(castle_db,gc->castle_id,gc); - - //intif_guild_castle_info(gc->castle_id); - - ln++; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"castle_db.txt"); - return 0; -} - -// 初期化 -void do_init_guild(void) -{ - guild_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - castle_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - guild_expcache_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); - guild_infoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); - expcache_ers = ers_new((uint32)sizeof(struct guild_expcache)); - guild_castleinfoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); - - guild_read_castledb(); - - guild_read_guildskill_tree_db(); //guild skill tree [Komurka] - - add_timer_func_list(guild_gvg_eliminate_timer,"guild_gvg_eliminate_timer"); - add_timer_func_list(guild_payexp_timer,"guild_payexp_timer"); - add_timer_func_list(guild_save_sub, "guild_save_sub"); - add_timer_func_list(guild_send_xy_timer, "guild_send_xy_timer"); - add_timer_interval(gettick()+GUILD_PAYEXP_INVERVAL,guild_payexp_timer,0,0,GUILD_PAYEXP_INVERVAL); - add_timer_interval(gettick()+GUILD_SEND_XY_INVERVAL,guild_send_xy_timer,0,0,GUILD_SEND_XY_INVERVAL); -} - - -// 検索 -struct guild *guild_search(int guild_id) -{ - if(guild_cache && guild_cache->guild_id == guild_id) - return guild_cache; - guild_cache = idb_get(guild_db,guild_id); - return guild_cache; -} -int guild_searchname_sub(DBKey key,void *data,va_list ap) -{ - struct guild *g=(struct guild *)data,**dst; - char *str; - str=va_arg(ap,char *); - dst=va_arg(ap,struct guild **); - if(strcmpi(g->name,str)==0) - *dst=g; - return 0; -} -// ギルド名検索 -struct guild* guild_searchname(char *str) -{ - struct guild *g=NULL; - guild_db->foreach(guild_db,guild_searchname_sub,str,&g); - return g; -} -struct guild_castle *guild_castle_search(int gcid) -{ - return idb_get(castle_db,gcid); -} - -// mapnameに対応したアジトのgcを返す -struct guild_castle *guild_mapname2gc(char *mapname) -{ - int i; - struct guild_castle *gc=NULL; - for(i=0;imap_name,mapname)==0) return gc; - } - return NULL; -} - -struct guild_castle *guild_mapindex2gc(short mapname) -{ - int i; - struct guild_castle *gc=NULL; - for(i=0;imap_name,mapindex_id2name(mapname))==0) return gc; - } - return NULL; -} - - - -// ログイン中のギルドメンバーの1人のsdを返す -struct map_session_data *guild_getavailablesd(struct guild *g) -{ - int i; - - nullpo_retr(NULL, g); - - for(i=0;imax_member;i++) - if(g->member[i].sd!=NULL) - return g->member[i].sd; - return NULL; -} - -// ギルドメンバーのインデックスを返す -int guild_getindex(struct guild *g,int account_id,int char_id) -{ - int i; - if(g==NULL) - return -1; - for(i=0;imax_member;i++) - if( g->member[i].account_id==account_id && - g->member[i].char_id==char_id ) - return i; - return -1; -} -// ギルドメンバーの役職を返す -int guild_getposition(struct map_session_data *sd,struct guild *g) -{ - int i; - - nullpo_retr(-1, sd); - - if(g==NULL && (g=guild_search(sd->status.guild_id))==NULL) - return -1; - for(i=0;imax_member;i++) - if( g->member[i].account_id==sd->status.account_id && - g->member[i].char_id==sd->status.char_id ) - return g->member[i].position; - return -1; -} - -// メンバー情報の作成 -void guild_makemember(struct guild_member *m,struct map_session_data *sd) -{ - nullpo_retv(sd); - - malloc_set(m,0,sizeof(struct guild_member)); - m->account_id =sd->status.account_id; - m->char_id =sd->status.char_id; - m->hair =sd->status.hair; - m->hair_color =sd->status.hair_color; - m->gender =sd->status.sex; - m->class_ =sd->status.class_; - m->lv =sd->status.base_level; -// m->exp =0; -// m->exp_payper =0; - m->online =1; - m->position =MAX_GUILDPOSITION-1; - memcpy(m->name,sd->status.name,NAME_LENGTH-1); - return; -} -// ギルド競合確認 -int guild_check_conflict(struct map_session_data *sd) -{ - nullpo_retr(0, sd); - - intif_guild_checkconflict(sd->status.guild_id, - sd->status.account_id,sd->status.char_id); - return 0; -} - -// ギルドのEXPキャッシュをinter鯖にフラッシュする -int guild_payexp_timer_sub(DBKey dataid, void *data, va_list ap) -{ - int i; - struct guild_expcache *c; - struct guild *g; - - c = (struct guild_expcache *)data; - - if ( - (g = guild_search(c->guild_id)) == NULL || - (i = guild_getindex(g, c->account_id, c->char_id)) < 0 - ) { - ers_free(expcache_ers, data); - return 0; - } - - if (g->member[i].exp > UINT_MAX - c->exp) - g->member[i].exp = UINT_MAX; - else - g->member[i].exp+= c->exp; - - intif_guild_change_memberinfo(g->guild_id,c->account_id,c->char_id, - GMI_EXP,&g->member[i].exp,sizeof(g->member[i].exp)); - c->exp=0; - - ers_free(expcache_ers, data); - return 0; -} - -int guild_payexp_timer(int tid, unsigned int tick, int id, int data) -{ - guild_expcache_db->clear(guild_expcache_db,guild_payexp_timer_sub); - return 0; -} - -//Taken from party_send_xy_timer_sub. [Skotlex] -int guild_send_xy_timer_sub(DBKey key,void *data,va_list ap) -{ - struct guild *g=(struct guild *)data; - int i; - - nullpo_retr(0, g); - - for(i=0;imax_member;i++){ - struct map_session_data *sd; - if((sd=g->member[i].sd)!=NULL){ - if(sd->guild_x!=sd->bl.x || sd->guild_y!=sd->bl.y){ - clif_guild_xy(sd); - sd->guild_x=sd->bl.x; - sd->guild_y=sd->bl.y; - } - } - } - return 0; -} - -//Code from party_send_xy_timer [Skotlex] -static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data) -{ - guild_db->foreach(guild_db,guild_send_xy_timer_sub,tick); - return 0; -} - -int guild_send_dot_remove(struct map_session_data *sd) -{ - if (sd->status.guild_id) - clif_guild_xy_remove(sd); - return 0; -} -//------------------------------------------------------------------------ - -// 作成要求 -int guild_create(struct map_session_data *sd,char *name) -{ - nullpo_retr(0, sd); - - if(sd->status.guild_id) - { - clif_guild_created(sd,1); // すでに所属している - return 0; - } - if(!battle_config.guild_emperium_check || pc_search_inventory(sd,714) >= 0) { - struct guild_member m; - guild_makemember(&m,sd); - m.position=0; - intif_guild_create(name,&m); - return 1; - } - clif_guild_created(sd,3); // エンペリウムがいない - return 0; -} - -// 作成可否 -int guild_created(int account_id,int guild_id) -{ - struct map_session_data *sd=map_id2sd(account_id); - - if(sd==NULL) - return 0; - if(!guild_id) { - clif_guild_created(sd,2); // 作成失敗(同名ギルド存在) - return 0; - } - //struct guild *g; - sd->status.guild_id=guild_id; - sd->state.guild_sent=0; - clif_guild_created(sd,0); - if(battle_config.guild_emperium_check) - pc_delitem(sd,pc_search_inventory(sd,714),1,0); // エンペリウム消耗 - return 0; -} - -// 情報要求 -int guild_request_info(int guild_id) -{ -// if(battle_config.etc_log) -// printf("guild_request_info\n"); - return intif_guild_request_info(guild_id); -} -// イベント付き情報要求 -int guild_npc_request_info(int guild_id,const char *event) -{ - struct eventlist *ev; - - if( guild_search(guild_id) ){ - if(event && *event) - npc_event_do(event); - return 0; - } - - if(event==NULL || *event==0) - return guild_request_info(guild_id); - - ev=(struct eventlist *)aCalloc(sizeof(struct eventlist),1); - memcpy(ev->name,event,strlen(event)); - //The one in the db becomes the next event from this. - ev->next=idb_put(guild_infoevent_db,guild_id,ev); - return guild_request_info(guild_id); -} - -// 所属キャラの確認 -int guild_check_member(struct guild *g) -{ - int i, j, users; - struct map_session_data *sd, **all_sd; - - nullpo_retr(0, g); - - all_sd = map_getallusers(&users); - - for(i=0;istatus.guild_id==g->guild_id){ - j=guild_getindex(g,sd->status.account_id,sd->status.char_id); - if (j < 0) { - sd->status.guild_id=0; - sd->state.guild_sent=0; - sd->guild_emblem_id=0; - if(battle_config.error_log) - ShowWarning("guild: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name); - } - } - } - return 0; -} -// 情報所得失敗(そのIDのキャラを全部未所属にする) -int guild_recv_noinfo(int guild_id) -{ - int i, users; - struct map_session_data *sd, **all_sd; - - all_sd = map_getallusers(&users); - - for(i=0;istatus.guild_id==guild_id) - sd->status.guild_id=0; - } - } - return 0; -} -// 情報所得 -int guild_recv_info(struct guild *sg) -{ - struct guild *g,before; - int i,bm,m; - struct eventlist *ev,*ev2; - - nullpo_retr(0, sg); - - if((g=idb_get(guild_db,sg->guild_id))==NULL){ - struct map_session_data *sd; - g=(struct guild *)aCalloc(1,sizeof(struct guild)); - idb_put(guild_db,sg->guild_id,g); - before=*sg; - - // 最初のロードなのでユーザーのチェックを行う - guild_check_member(sg); - //If the guild master is online the first time the guild_info is received, that means he was the first to join, - //and as such, his guild skills should be blocked to avoid login/logout abuse [Skotlex] - if ((sd = map_nick2sd(sg->master)) != NULL) - { - guild_block_skill(sd, 300000); - //Also set the guild master flag. - sd->state.gmaster_flag = g; - clif_charnameupdate(sd); // [LuzZza] - } - }else - before=*g; - memcpy(g,sg,sizeof(struct guild)); - - if(g->max_member > MAX_GUILD) - { - if (battle_config.error_log) - ShowError("guild_recv_info: Received guild with %d members, but MAX_GUILD is only %d. Extra guild-members have been lost!\n", g->max_member, MAX_GUILD); - g->max_member = MAX_GUILD; - } - - for(i=bm=m=0;imax_member;i++){ // sdの設定と人数の確認 - if(g->member[i].account_id>0){ - struct map_session_data *sd = map_id2sd(g->member[i].account_id); - if (sd && sd->status.char_id == g->member[i].char_id && - sd->status.guild_id == g->guild_id && - !sd->state.waitingdisconnect) { - g->member[i].sd = sd; - clif_charnameupdate(sd); // [LuzZza] - } else g->member[i].sd = NULL; - m++; - }else - g->member[i].sd=NULL; - if(before.member[i].account_id>0) - bm++; - } - - for(i=0;imax_member;i++){ // 情報の送信 - struct map_session_data *sd = g->member[i].sd; - if( sd==NULL ) - continue; - - if( before.guild_lv!=g->guild_lv || bm!=m || - before.max_member!=g->max_member ){ - clif_guild_basicinfo(sd); // 基本情報送信 - clif_guild_emblem(sd,g); // エンブレム送信 - } - - if(bm!=m){ // メンバー情報送信 - clif_guild_memberlist(g->member[i].sd); - } - - if( before.skill_point!=g->skill_point) - clif_guild_skillinfo(sd); // スキル情報送信 - - if( sd->state.guild_sent==0){ // 未送信なら所属情報も送る - clif_guild_belonginfo(sd,g); - clif_guild_notice(sd,g); - sd->guild_emblem_id=g->emblem_id; - sd->state.guild_sent=1; - } - } - - // イベントの発生 - if( (ev=idb_remove(guild_infoevent_db,sg->guild_id))!=NULL ){ - while(ev){ - npc_event_do(ev->name); - ev2=ev->next; - aFree(ev); - ev=ev2; - } - } - - return 0; -} - - -// ギルドへの勧誘 -int guild_invite(struct map_session_data *sd,struct map_session_data *tsd) -{ - struct guild *g; - int i; - - nullpo_retr(0, sd); - - g=guild_search(sd->status.guild_id); - - if(tsd==NULL || g==NULL) - return 0; - - if(!battle_config.invite_request_check) { - if (tsd->party_invite>0 || tsd->trade_partner) { // 相手が取引中かどうか - clif_guild_inviteack(sd,0); - return 0; - } - } - if(tsd->status.guild_id>0 || - tsd->guild_invite>0 || - map[tsd->bl.m].flag.gvg_castle) - { //Can't invite people inside castles. [Skotlex] - clif_guild_inviteack(sd,0); - return 0; - } - - // 定員確認 - for(i=0;imax_member;i++) - if(g->member[i].account_id==0) - break; - if(i==g->max_member){ - clif_guild_inviteack(sd,3); - return 0; - } - - tsd->guild_invite=sd->status.guild_id; - tsd->guild_invite_account=sd->status.account_id; - - clif_guild_invite(tsd,g); - return 0; -} -// ギルド勧誘への返答 -int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag) -{ - struct map_session_data *tsd; - - nullpo_retr(0, sd); - - //nullpo_retr(0, tsd= map_id2sd( sd->guild_invite_account )); - //I checked the code, and there's no "check" for the case where the guy - //that invites another to a guild quits the map-server before being replied. - //Hence that's a valid null pointer scenario. :) [Skotlex] - if ((tsd= map_id2sd( sd->guild_invite_account )) == NULL) - { //Do we send a "invitation failed" msg or something to the player? - //Or should we accept the invitation and add it to the guild anyway? - //afterall, guild_invite holds the guild id that the player was invited to. - sd->guild_invite=0; - sd->guild_invite_account=0; - return 0; - } - - if(sd->guild_invite!=guild_id) // 勧誘とギルドIDが違う - return 0; - - if(flag==1){ // 承諾 - struct guild_member m; - struct guild *g; - int i; - - // 定員確認 - if( (g=guild_search(tsd->status.guild_id))==NULL ){ - sd->guild_invite=0; - sd->guild_invite_account=0; - return 0; - } - for(i=0;imax_member;i++) - if(g->member[i].account_id==0) - break; - if(i==g->max_member){ - sd->guild_invite=0; - sd->guild_invite_account=0; - clif_guild_inviteack(tsd,3); - return 0; - } - - - //inter鯖へ追加要求 - guild_makemember(&m,sd); - intif_guild_addmember( sd->guild_invite, &m ); - return 0; - }else{ // 拒否 - sd->guild_invite=0; - sd->guild_invite_account=0; - clif_guild_inviteack(tsd,1); - } - return 0; -} -// ギルドメンバが追加された -int guild_member_added(int guild_id,int account_id,int char_id,int flag) -{ - struct map_session_data *sd= map_id2sd(account_id),*sd2; - struct guild *g; - - if( (g=guild_search(guild_id))==NULL ) - return 0; - - if(sd==NULL || sd->guild_invite==0){ - // キャラ側に登録できなかったため脱退要求を出す - if (flag == 0) { - if(battle_config.error_log) - ShowError("guild: member added error %d is not online\n",account_id); - intif_guild_leave(guild_id,account_id,char_id,0,"**登録失敗**"); - } - return 0; - } - sd2 = map_id2sd(sd->guild_invite_account); - sd->guild_invite = 0; - sd->guild_invite_account = 0; - - if(flag==1){ // 失敗 - if( sd2!=NULL ) - clif_guild_inviteack(sd2,3); - return 0; - } - - // 成功 - sd->state.guild_sent=0; - sd->status.guild_id=guild_id; - - if( sd2!=NULL ) - clif_guild_inviteack(sd2,2); - - // いちおう競合確認 - guild_check_conflict(sd); - //Next line commented because it do nothing, look at guild_recv_info [LuzZza] - //clif_charnameupdate(sd); //Update display name [Skotlex] - return 0; -} - -// ギルド脱退要求 -int guild_leave(struct map_session_data *sd,int guild_id, - int account_id,int char_id,const char *mes) -{ - struct guild *g; - int i; - - nullpo_retr(0, sd); - - g = guild_search(sd->status.guild_id); - - if(g==NULL) - return 0; - - if( sd->status.account_id!=account_id || - sd->status.char_id!=char_id || sd->status.guild_id!=guild_id || - map[sd->bl.m].flag.gvg_castle) //Can't leave inside guild castles. - return 0; - - for(i=0;imax_member;i++){ // 所属しているか - if( g->member[i].account_id==sd->status.account_id && - g->member[i].char_id==sd->status.char_id ){ - intif_guild_leave(g->guild_id,sd->status.account_id,sd->status.char_id,0,mes); - return 0; - } - } - return 0; -} -// ギルド追放要求 -int guild_expulsion(struct map_session_data *sd,int guild_id, - int account_id,int char_id,const char *mes) -{ - struct guild *g; - int i,ps; - - nullpo_retr(0, sd); - - g = guild_search(sd->status.guild_id); - - if(g==NULL) - return 0; - - if(sd->status.guild_id!=guild_id || map[sd->bl.m].flag.gvg_castle) //Can't leave inside guild castles. - return 0; - - if( (ps=guild_getposition(sd,g))<0 || !(g->position[ps].mode&0x0010) ) - return 0; // 処罰権限無し - - for(i=0;imax_member;i++){ // 所属しているか - if( g->member[i].account_id==account_id && - g->member[i].char_id==char_id ){ - intif_guild_leave(g->guild_id,account_id,char_id,1,mes); - //It's wrong way, member info will erased later - //see guild_member_leaved [LuzZza] - //malloc_set(&g->member[i],0,sizeof(struct guild_member)); - return 0; - } - } - return 0; -} - -int guild_member_leaved(int guild_id,int account_id,int char_id,int flag, - const char *name,const char *mes) // rewrote [LuzZza] -{ - int i; - struct guild *g = guild_search(guild_id); - struct map_session_data *sd = map_charid2sd(char_id); - struct map_session_data *online_member_sd; - - if(g == NULL) - return 0; - - for(i=0;imax_member;i++) { - if( g->member[i].account_id == account_id && - g->member[i].char_id == char_id ){ - - if((online_member_sd = guild_getavailablesd(g)) == NULL) - return 0; - - if(!flag) - clif_guild_leave(online_member_sd, name, mes); - else - clif_guild_expulsion(online_member_sd, name, mes, account_id); - - malloc_set(&g->member[i],0,sizeof(struct guild_member)); - clif_guild_memberlist(online_member_sd); - - if(sd != NULL && sd->status.guild_id == guild_id) { - if (sd->state.storage_flag == 2) //Close the guild storage. - storage_guild_storageclose(sd); - sd->status.guild_id=0; - sd->guild_emblem_id=0; - sd->state.guild_sent=0; - - guild_send_dot_remove(sd); - clif_charnameupdate(sd); //Update display name [Skotlex] - } - return 0; - } - - } - - return 0; -} - -int guild_send_memberinfoshort(struct map_session_data *sd,int online) -{ // cleaned up [LuzZza] - struct guild *g; - - nullpo_retr(0, sd); - - if(!(g = guild_search(sd->status.guild_id))) - return 0; - - //Moved to place before intif_guild_memberinfoshort because - //If it's not a member, needn't send it's info to intif. [LuzZza] - guild_check_member(g); - - if(sd->status.guild_id <= 0) - return 0; - - intif_guild_memberinfoshort(g->guild_id, - sd->status.account_id,sd->status.char_id,online,sd->status.base_level,sd->status.class_); - - if(!online){ - int i=guild_getindex(g,sd->status.account_id,sd->status.char_id); - if(i>=0) - g->member[i].sd=NULL; - return 0; - } - - if(sd->state.guild_sent) - return 0; - - clif_guild_belonginfo(sd,g); - clif_guild_notice(sd,g); - - sd->state.guild_sent = 1; - sd->guild_emblem_id = g->emblem_id; - - return 0; -} - -int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_) -{ // cleaned up [LuzZza] - - int i,alv,c,idx=-1,om=0,oldonline=-1; - struct guild *g = guild_search(guild_id); - - if(g == NULL) - return 0; - - for(i=0,alv=0,c=0,om=0;imax_member;i++){ - struct guild_member *m=&g->member[i]; - if(m->account_id==account_id && m->char_id==char_id ){ - oldonline=m->online; - m->online=online; - m->lv=lv; - m->class_=class_; - idx=i; - } - if(m->account_id>0){ - alv+=m->lv; - c++; - } - if(m->online) - om++; - } - - if(idx == -1 || c == 0) { - // ギルドのメンバー外なので追放扱いする - struct map_session_data *sd = map_id2sd(account_id); - if(sd && sd->status.char_id == char_id) { - sd->status.guild_id=0; - sd->guild_emblem_id=0; - sd->state.guild_sent=0; - } - if(battle_config.error_log) - ShowWarning("guild: not found member %d,%d on %d[%s]\n", account_id,char_id,guild_id,g->name); - return 0; - } - - g->average_lv=alv/c; - g->connect_member=om; - - for(i=0;imax_member;i++) { - struct map_session_data *sd= map_id2sd(g->member[i].account_id); - g->member[i].sd = (sd && sd->status.char_id == g->member[i].char_id && - sd->status.guild_id == g->guild_id && !sd->state.waitingdisconnect) ? sd : NULL; - } - - if(oldonline!=online) - clif_guild_memberlogin_notice(g, idx, online); - - - if(!g->member[idx].sd) - return 0; - - //Send XY dot updates. [Skotlex] - //Moved from guild_send_memberinfoshort [LuzZza] - for(i=0; i < g->max_member; i++) { - - if(!g->member[i].sd || i == idx || - g->member[i].sd->bl.m != g->member[idx].sd->bl.m) - continue; - - clif_guild_xy_single(g->member[idx].sd->fd, g->member[i].sd); - } - - return 0; -} -// ギルド会話送信 -int guild_send_message(struct map_session_data *sd,char *mes,int len) -{ - nullpo_retr(0, sd); - - if(sd->status.guild_id==0) - return 0; - intif_guild_message(sd->status.guild_id,sd->status.account_id,mes,len); - guild_recv_message(sd->status.guild_id,sd->status.account_id,mes,len); - - //Chatlogging type 'G' - if(log_config.chat&1 //we log everything then - || ( log_config.chat&8 //if Guild bit is on - && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF - log_chat("G", sd->status.guild_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes); - - return 0; -} -// ギルド会話受信 -int guild_recv_message(int guild_id,int account_id,char *mes,int len) -{ - struct guild *g; - if( (g=guild_search(guild_id))==NULL) - return 0; - clif_guild_message(g,account_id,mes,len); - return 0; -} -// ギルドメンバの役職変更 -int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx) -{ - return intif_guild_change_memberinfo( - guild_id,account_id,char_id,GMI_POSITION,&idx,sizeof(idx)); -} -// ギルドメンバの役職変更通知 -int guild_memberposition_changed(struct guild *g,int idx,int pos) -{ - nullpo_retr(0, g); - - g->member[idx].position=pos; - clif_guild_memberpositionchanged(g,idx); - - // Update char position in client [LuzZza] - if(g->member[idx].sd != NULL) - clif_charnameupdate(g->member[idx].sd); - return 0; -} -// ギルド役職変更 -int guild_change_position(struct map_session_data *sd,int idx, - int mode,int exp_mode,const char *name) -{ - struct guild_position p; - - nullpo_retr(0, sd); - - if(exp_mode>battle_config.guild_exp_limit) - exp_mode=battle_config.guild_exp_limit; - if(exp_mode<0)exp_mode=0; - p.mode=mode; - p.exp_mode=exp_mode; - memcpy(p.name,name,NAME_LENGTH-1); - p.name[NAME_LENGTH-1] = '\0'; //Security check... [Skotlex] - return intif_guild_position(sd->status.guild_id,idx,&p); -} -// ギルド役職変更通知 -int guild_position_changed(int guild_id,int idx,struct guild_position *p) -{ - struct guild *g=guild_search(guild_id); - int i; - if(g==NULL) - return 0; - memcpy(&g->position[idx],p,sizeof(struct guild_position)); - clif_guild_positionchanged(g,idx); - - // Update char name in client [LuzZza] - for(i=0;imax_member;i++) - if(g->member[i].position == idx && g->member[i].sd != NULL) - clif_charnameupdate(g->member[i].sd); - return 0; -} -// ギルド告知変更 -int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2) -{ - nullpo_retr(0, sd); - - if(guild_id!=sd->status.guild_id) - return 0; - return intif_guild_notice(guild_id,mes1,mes2); -} -// ギルド告知変更通知 -int guild_notice_changed(int guild_id,const char *mes1,const char *mes2) -{ - int i; - struct map_session_data *sd; - struct guild *g=guild_search(guild_id); - if(g==NULL) - return 0; - - memcpy(g->mes1,mes1,60); - memcpy(g->mes2,mes2,120); - - for(i=0;imax_member;i++){ - if((sd=g->member[i].sd)!=NULL) - clif_guild_notice(sd,g); - } - return 0; -} -// ギルドエンブレム変更 -int guild_change_emblem(struct map_session_data *sd,int len,const char *data) -{ - struct guild *g; - nullpo_retr(0, sd); - - if (battle_config.require_glory_guild && - !((g = guild_search(sd->status.guild_id)) && guild_checkskill(g, GD_GLORYGUILD)>0)) { - clif_skill_fail(sd,GD_GLORYGUILD,0,0); - return 0; - } - - return intif_guild_emblem(sd->status.guild_id,len,data); -} -// ギルドエンブレム変更通知 -int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data) -{ - int i; - struct map_session_data *sd; - struct guild *g=guild_search(guild_id); - if(g==NULL) - return 0; - - memcpy(g->emblem_data,data,len); - g->emblem_len=len; - g->emblem_id=emblem_id; - - for(i=0;imax_member;i++){ - if((sd=g->member[i].sd)!=NULL){ - sd->guild_emblem_id=emblem_id; - clif_guild_belonginfo(sd,g); - clif_guild_emblem(sd,g); - } - } - return 0; -} - -static void* create_expcache(DBKey key, va_list args) { - struct guild_expcache *c; - struct map_session_data *sd = va_arg(args, struct map_session_data*); - - c = ers_alloc(expcache_ers, struct guild_expcache); - c->guild_id = sd->status.guild_id; - c->account_id = sd->status.account_id; - c->char_id = sd->status.char_id; - c->exp = 0; - return c; -} - -// ギルドのEXP上納 -unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp) -{ - struct guild *g; - struct guild_expcache *c; - int per; - - nullpo_retr(0, sd); - - if (!exp) return 0; - - if (sd->status.guild_id == 0 || - (g = guild_search(sd->status.guild_id)) == NULL || - (per = guild_getposition(sd,g)) < 0 || - (per = g->position[per].exp_mode) < 1) - return 0; - - - if (per < 100) - exp = (unsigned int) exp * per / 100; - //Otherwise tax everything. - - c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd); - - if (c->exp > UINT_MAX - exp) - c->exp = UINT_MAX; - else - c->exp += exp; - - return exp; -} - -// Celest -int guild_getexp(struct map_session_data *sd,int exp) -{ - struct guild *g; - struct guild_expcache *c; - nullpo_retr(0, sd); - - if (sd->status.guild_id == 0 || (g = guild_search(sd->status.guild_id)) == NULL) - return 0; - - c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd); - if (c->exp > UINT_MAX - exp) - c->exp = UINT_MAX; - else - c->exp += exp; - return exp; -} - -// スキルポイント割り振り -int guild_skillup(struct map_session_data *sd,int skill_num,int flag) -{ - struct guild *g; - int idx = skill_num - GD_SKILLBASE; - - nullpo_retr(0, sd); - - if(idx < 0 || idx >= MAX_GUILDSKILL) - - return 0; - if(sd->status.guild_id==0 || (g=guild_search(sd->status.guild_id))==NULL) - return 0; - if(strcmp(sd->status.name,g->master)) - return 0; - - if( (g->skill_point>0 || flag&1) && - g->skill[idx].id!=0 && - g->skill[idx].lv < guild_skill_get_max(skill_num) ){ - intif_guild_skillup(g->guild_id,skill_num,sd->status.account_id,flag); - } - return 0; -} -// スキルポイント割り振り通知 -int guild_skillupack(int guild_id,int skill_num,int account_id) -{ - struct map_session_data *sd=map_id2sd(account_id); - struct guild *g=guild_search(guild_id); - int i; - if(g==NULL) - return 0; - if(sd!=NULL) - clif_guild_skillup(sd,skill_num,g->skill[skill_num-GD_SKILLBASE].lv); - // 全員に通知 - for(i=0;imax_member;i++) - if((sd=g->member[i].sd)!=NULL) - clif_guild_skillinfo(sd); - return 0; -} - -// ギルド同盟数所得 -int guild_get_alliance_count(struct guild *g,int flag) -{ - int i,c; - - nullpo_retr(0, g); - - for(i=c=0;ialliance[i].guild_id>0 && - g->alliance[i].opposition==flag ) - c++; - } - return c; -} - -// Blocks all guild skills which have a common delay time. -void guild_block_skill(struct map_session_data *sd, int time) { - int skill_num[] = { GD_BATTLEORDER, GD_REGENERATION, GD_RESTORE, GD_EMERGENCYCALL }; - int i; - for (i = 0; i < 4; i++) - skill_blockpc_start(sd, skill_num[i], time); -} - -// 同盟関係かどうかチェック -// 同盟なら1、それ以外は0 -int guild_check_alliance(int guild_id1, int guild_id2, int flag) -{ - struct guild *g; - int i; - - g = guild_search(guild_id1); - if (g == NULL) - return 0; - - for (i=0; ialliance[i].guild_id == guild_id2) && (g->alliance[i].opposition == flag)) - return 1; - - return 0; -} -// ギルド同盟要求 -int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd) -{ - struct guild *g[2]; - int i; - - if(agit_flag) { // Disable alliance creation during woe [Valaris] - clif_displaymessage(sd->fd,"Alliances cannot be made during Guild Wars!"); - return 0; - } // end addition [Valaris] - - - nullpo_retr(0, sd); - - if(tsd==NULL || tsd->status.guild_id<=0) - return 0; - - g[0]=guild_search(sd->status.guild_id); - g[1]=guild_search(tsd->status.guild_id); - - if(g[0]==NULL || g[1]==NULL) - return 0; - - // Prevent creation alliance with same guilds [LuzZza] - if(sd->status.guild_id == tsd->status.guild_id) - return 0; - - if( guild_get_alliance_count(g[0],0)>=3 ) { - clif_guild_allianceack(sd,4); - return 0; - } - if( guild_get_alliance_count(g[1],0)>=3 ) { - clif_guild_allianceack(sd,3); - return 0; - } - - if( tsd->guild_alliance>0 ){ - clif_guild_allianceack(sd,1); - return 0; - } - - for(i=0;ialliance[i].guild_id==tsd->status.guild_id && - g[0]->alliance[i].opposition==0){ - clif_guild_allianceack(sd,0); - return 0; - } - } - - tsd->guild_alliance=sd->status.guild_id; - tsd->guild_alliance_account=sd->status.account_id; - - clif_guild_reqalliance(tsd,sd->status.account_id,g[0]->name); - return 0; -} -// ギルド勧誘への返答 -int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag) -{ - struct map_session_data *tsd; - - nullpo_retr(0, sd); - tsd= map_id2sd( account_id ); - if (!tsd) { //Character left? Cancel alliance. - clif_guild_allianceack(sd,3); - return 0; - } - - if(sd->guild_alliance!=tsd->status.guild_id) // 勧誘とギルドIDが違う - return 0; - - if(flag==1){ // 承諾 - int i; - - struct guild *g,*tg; // 同盟数再確認 - g=guild_search(sd->status.guild_id); - tg=guild_search(tsd->status.guild_id); - - if(g==NULL || guild_get_alliance_count(g,0)>=3){ - clif_guild_allianceack(sd,4); - clif_guild_allianceack(tsd,3); - return 0; - } - if(tg==NULL || guild_get_alliance_count(tg,0)>=3){ - clif_guild_allianceack(sd,3); - clif_guild_allianceack(tsd,4); - return 0; - } - - for(i=0;ialliance[i].guild_id==tsd->status.guild_id && - g->alliance[i].opposition==1) - intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, - sd->status.account_id,tsd->status.account_id,9 ); - } - for(i=0;ialliance[i].guild_id==sd->status.guild_id && - tg->alliance[i].opposition==1) - intif_guild_alliance( tsd->status.guild_id,sd->status.guild_id, - tsd->status.account_id,sd->status.account_id,9 ); - } - - // inter鯖へ同盟要請 - intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, - sd->status.account_id,tsd->status.account_id,0 ); - return 0; - }else{ // 拒否 - sd->guild_alliance=0; - sd->guild_alliance_account=0; - if(tsd!=NULL) - clif_guild_allianceack(tsd,3); - } - return 0; -} -// ギルド関係解消 -int guild_delalliance(struct map_session_data *sd,int guild_id,int flag) -{ - nullpo_retr(0, sd); - - if(agit_flag) { // Disable alliance breaking during woe [Valaris] - clif_displaymessage(sd->fd,"Alliances cannot be broken during Guild Wars!"); - return 0; - } // end addition [Valaris] - - intif_guild_alliance( sd->status.guild_id,guild_id, - sd->status.account_id,0,flag|8 ); - return 0; -} -// ギルド敵対 -int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd) -{ - struct guild *g; - int i; - - nullpo_retr(0, sd); - - g=guild_search(sd->status.guild_id); - if(g==NULL || tsd==NULL) - return 0; - - // Prevent creation opposition with same guilds [LuzZza] - if(sd->status.guild_id == tsd->status.guild_id) - return 0; - - if( guild_get_alliance_count(g,1)>=3 ) { - clif_guild_oppositionack(sd,1); - return 0; - } - - if(agit_flag) { - clif_displaymessage(sd->fd,"You cannot make oppositions during Guild Wars!"); - return 0; - } - - for(i=0;ialliance[i].guild_id==tsd->status.guild_id){ - if(g->alliance[i].opposition==1){ // すでに敵対 - clif_guild_oppositionack(sd,2); - return 0; - } - //Change alliance to opposition. - intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, - sd->status.account_id,tsd->status.account_id,8 ); - } - } - - // inter鯖に敵対要請 - intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, - sd->status.account_id,tsd->status.account_id,1 ); - return 0; -} -// ギルド同盟/敵対通知 -int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2, - int flag,const char *name1,const char *name2) -{ - struct guild *g[2]; - int guild_id[2]; - const char *guild_name[2]; - struct map_session_data *sd[2]; - int j,i; - - guild_id[0] = guild_id1; - guild_id[1] = guild_id2; - guild_name[0] = name1; - guild_name[1] = name2; - sd[0] = map_id2sd(account_id1); - sd[1] = map_id2sd(account_id2); - - g[0]=guild_search(guild_id1); - g[1]=guild_search(guild_id2); - - if(sd[0]!=NULL && (flag&0x0f)==0){ - sd[0]->guild_alliance=0; - sd[0]->guild_alliance_account=0; - } - - if(flag&0x70){ // 失敗 - for(i=0;i<2-(flag&1);i++) - if( sd[i]!=NULL ) - clif_guild_allianceack(sd[i],((flag>>4)==i+1)?3:4); - return 0; - } -// if(battle_config.etc_log) -// printf("guild alliance_ack %d %d %d %d %d %s %s\n",guild_id1,guild_id2,account_id1,account_id2,flag,name1,name2); - - if(!(flag&0x08)){ // 関係追加 - for(i=0;i<2-(flag&1);i++) - if(g[i]!=NULL) - for(j=0;jalliance[j].guild_id==0){ - g[i]->alliance[j].guild_id=guild_id[1-i]; - memcpy(g[i]->alliance[j].name,guild_name[1-i],NAME_LENGTH-1); - g[i]->alliance[j].opposition=flag&1; - break; - } - }else{ // 関係解消 - for(i=0;i<2-(flag&1);i++){ - if(g[i]!=NULL) - for(j=0;jalliance[j].guild_id==guild_id[1-i] && - g[i]->alliance[j].opposition==(flag&1)){ - g[i]->alliance[j].guild_id=0; - break; - } - if( sd[i]!=NULL ) // 解消通知 - clif_guild_delalliance(sd[i],guild_id[1-i],(flag&1)); - } - } - - if((flag&0x0f)==0){ // 同盟通知 - if( sd[1]!=NULL ) - clif_guild_allianceack(sd[1],2); - }else if((flag&0x0f)==1){ // 敵対通知 - if( sd[0]!=NULL ) - clif_guild_oppositionack(sd[0],0); - } - - - for(i=0;i<2-(flag&1);i++){ // 同盟/敵対リストの再送信 - struct map_session_data *sd; - if(g[i]!=NULL) - for(j=0;jmax_member;j++) - if((sd=g[i]->member[j].sd)!=NULL) - clif_guild_allianceinfo(sd); - } - return 0; -} -// ギルド解散通知用 -int guild_broken_sub(DBKey key,void *data,va_list ap) -{ - struct guild *g=(struct guild *)data; - int guild_id=va_arg(ap,int); - int i,j; - struct map_session_data *sd=NULL; - - nullpo_retr(0, g); - - for(i=0;ialliance[i].guild_id==guild_id){ - for(j=0;jmax_member;j++) - if( (sd=g->member[j].sd)!=NULL ) - clif_guild_delalliance(sd,guild_id,g->alliance[i].opposition); - intif_guild_alliance(g->guild_id, guild_id,0,0,g->alliance[i].opposition|8); - g->alliance[i].guild_id=0; - } - } - return 0; -} - -//Invoked on Castles when a guild is broken. [Skotlex] -int castle_guild_broken_sub(DBKey key,void *data,va_list ap) -{ - struct guild_castle *gc=(struct guild_castle *)data; - int guild_id=va_arg(ap,int); - - nullpo_retr(0, gc); - - if (gc->guild_id == guild_id) - { //Save the new 'owner', this should invoke guardian clean up and other such things. - gc->guild_id = 0; - guild_castledatasave(gc->castle_id, 1, 0); - } - return 0; -} - -//Innvoked on /breakguild "Guild name" -int guild_broken(int guild_id,int flag) -{ - struct guild *g=guild_search(guild_id); -// struct guild_castle *gc=NULL; - struct map_session_data *sd; - int i; -// char *name;; - - if(flag!=0 || g==NULL) - return 0; - - //we call castle_event::OnGuildBreak of all castlesof the guild - //you can set all castle_events in the castle_db.txt -/* name=(char *)aCalloc(50,sizeof(char)); //24 char = event name, + space for "::OnGuildBreak" - for(i=0;iguild_id == guild_id){ - memcpy(name,gc->castle_event,50); - npc_event_do(strcat(name,"::OnGuildBreak")); - } - } - } - free(name); -*/ - for(i=0;imax_member;i++){ // ギルド解散を通知 - if((sd=g->member[i].sd)!=NULL){ - if(sd->state.storage_flag == 2) - storage_guild_storage_quit(sd,1); - sd->status.guild_id=0; - sd->state.guild_sent=0; - clif_guild_broken(g->member[i].sd,0); - clif_charnameupdate(sd); // [LuzZza] - } - } - - guild_db->foreach(guild_db,guild_broken_sub,guild_id); - castle_db->foreach(castle_db,castle_guild_broken_sub,guild_id); - if (guild_cache && guild_cache->guild_id == guild_id) - guild_cache = NULL; - guild_storage_delete(guild_id); - idb_remove(guild_db,guild_id); - return 0; -} - -//Changes the Guild Master to the specified player. [Skotlex] -int guild_gm_change(int guild_id, struct map_session_data *sd) -{ - struct guild *g; - nullpo_retr(0, sd); - - if (sd->status.guild_id != guild_id) - return 0; - - g=guild_search(guild_id); - - nullpo_retr(0, g); - - if (strcmp(g->master, sd->status.name) == 0) //Nothing to change. - return 0; - - //Notify servers that master has changed. - intif_guild_change_gm(guild_id, sd->status.name, strlen(sd->status.name)); - return 1; -} - -//Notification from Char server that a guild's master has changed. [Skotlex] -int guild_gm_changed(int guild_id, int pos) -{ - struct guild *g; - struct guild_member gm; - - g=guild_search(guild_id); - - if (!g || pos < 0 || pos > g->max_member) - return 0; - - memcpy(&gm, &g->member[pos], sizeof (struct guild_member)); - memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member)); - memcpy(&g->member[0], &gm, sizeof(struct guild_member)); - - g->member[pos].position = g->member[0].position; - g->member[0].position = 0; //Position 0: guild Master. - strcpy(g->master, g->member[0].name); - - if (g->member[pos].sd && g->member[pos].sd->fd) - { - clif_displaymessage(g->member[pos].sd->fd, "You no longer are the Guild Master."); - g->member[pos].sd->state.gmaster_flag = 0; - } - - if (g->member[0].sd && g->member[0].sd->fd) - { - clif_displaymessage(g->member[0].sd->fd, "You have become the Guild Master!"); - g->member[0].sd->state.gmaster_flag = g; - //Block his skills for 5 minutes to prevent abuse. - guild_block_skill(g->member[0].sd, 300000); - } - return 1; -} - -// ギルド解散 -int guild_break(struct map_session_data *sd,char *name) -{ - struct guild *g; - int i; - - nullpo_retr(0, sd); - - if( (g=guild_search(sd->status.guild_id))==NULL ) - return 0; - if(strcmp(g->name,name)!=0) - return 0; - if(strcmp(sd->status.name,g->master)!=0) - return 0; - for(i=0;imax_member;i++){ - if( g->member[i].account_id>0 && ( - g->member[i].account_id!=sd->status.account_id || - g->member[i].char_id!=sd->status.char_id )) - break; - } - if(imax_member){ - clif_guild_broken(sd,2); - return 0; - } - - intif_guild_break(g->guild_id); - return 0; -} - -// ギルド城データ要求 -int guild_castledataload(int castle_id,int index) -{ - return intif_guild_castle_dataload(castle_id,index); -} -// ギルド城情報所得時イベント追加 -int guild_addcastleinfoevent(int castle_id,int index,const char *name) -{ - struct eventlist *ev; - int code=castle_id|(index<<16); - - if( name==NULL || *name==0 ) - return 0; - - ev=(struct eventlist *)aMalloc(sizeof(struct eventlist)); - memcpy(ev->name,name,sizeof(ev->name)); - //The next event becomes whatever was currently stored. - ev->next= idb_put(guild_castleinfoevent_db,code,ev); - return 0; -} - -// ギルド城データ要求返信 -int guild_castledataloadack(int castle_id,int index,int value) -{ - struct guild_castle *gc=guild_castle_search(castle_id); - int code=castle_id|(index<<16); - struct eventlist *ev,*ev2; - - if(gc==NULL){ - return 0; - } - switch(index){ - case 1: - gc->guild_id = value; - if (value && guild_search(value)==NULL) //Request guild data which will be required for spawned guardians. [Skotlex] - guild_request_info(value); - break; - case 2: gc->economy = value; break; - case 3: gc->defense = value; break; - case 4: gc->triggerE = value; break; - case 5: gc->triggerD = value; break; - case 6: gc->nextTime = value; break; - case 7: gc->payTime = value; break; - case 8: gc->createTime = value; break; - case 9: gc->visibleC = value; break; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - gc->guardian[index-10].visible = value; break; - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - case 24: - case 25: - gc->guardian[index-18].hp = value; break; - default: - ShowError("guild_castledataloadack ERROR!! (Not found index=%d)\n", index); - return 0; - } - if( (ev=idb_remove(guild_castleinfoevent_db,code))!=NULL ){ - while(ev){ - npc_event_do(ev->name); - ev2=ev->next; - aFree(ev); - ev=ev2; - } - } - return 1; -} -// ギルド城データ変更要求 -int guild_castledatasave(int castle_id,int index,int value) -{ - if (index == 1) - { //The castle's owner has changed? Update Guardian ownership, too. [Skotlex] - struct guild_castle *gc = guild_castle_search(castle_id); - int m = -1; - if (gc) m = map_mapname2mapid(gc->map_name); - if (m != -1) - map_foreachinmap(mob_guardian_guildchange, m, BL_MOB); - } - return intif_guild_castle_datasave(castle_id,index,value); -} - -// ギルド城データ変更通知 -int guild_castledatasaveack(int castle_id,int index,int value) -{ - struct guild_castle *gc=guild_castle_search(castle_id); - if(gc==NULL){ - return 0; - } - switch(index){ - case 1: gc->guild_id = value; break; - case 2: gc->economy = value; break; - case 3: gc->defense = value; break; - case 4: gc->triggerE = value; break; - case 5: gc->triggerD = value; break; - case 6: gc->nextTime = value; break; - case 7: gc->payTime = value; break; - case 8: gc->createTime = value; break; - case 9: gc->visibleC = value; break; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - gc->guardian[index-10].visible = value; break; - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - case 24: - case 25: - gc->guardian[index-18].hp = value; break; - default: - ShowError("guild_castledatasaveack ERROR!! (Not found index=%d)\n", index); - return 0; - } - return 1; -} - -// ギルドデータ一括受信(初期化時) -int guild_castlealldataload(int len,struct guild_castle *gc) -{ - int i; - int n = (len-4) / sizeof(struct guild_castle), ev = -1; - - nullpo_retr(0, gc); - - //Last owned castle in the list invokes ::OnAgitinit - for(i = 0; i < n; i++) { - if ((gc + i)->guild_id) - ev = i; - } - - // 城データ格納とギルド情報要求 - for(i = 0; i < n; i++, gc++) { - struct guild_castle *c = guild_castle_search(gc->castle_id); - if (!c) { - ShowError("guild_castlealldataload Castle id=%d not found.\n", gc->castle_id); - continue; - } - memcpy(&c->guild_id,&gc->guild_id, - sizeof(struct guild_castle) - ((int)&c->guild_id - (int)c) ); - if( c->guild_id ){ - if(i!=ev) - guild_request_info(c->guild_id); - else - guild_npc_request_info(c->guild_id, "::OnAgitInit"); - } - } - if (ev == -1) //No castles owned, invoke OnAgitInit as it is. - npc_event_doall("OnAgitInit"); - return 0; -} - -int guild_agit_start(void) -{ // Run All NPC_Event[OnAgitStart] - int c = npc_event_doall("OnAgitStart"); - ShowStatus("NPC_Event:[OnAgitStart] Run (%d) Events by @AgitStart.\n",c); - // Start auto saving - guild_save_timer = add_timer_interval (gettick() + GUILD_SAVE_INTERVAL, guild_save_sub, 0, 0, GUILD_SAVE_INTERVAL); - return 0; -} - -int guild_agit_end(void) -{ // Run All NPC_Event[OnAgitEnd] - int c = npc_event_doall("OnAgitEnd"); - ShowStatus("NPC_Event:[OnAgitEnd] Run (%d) Events by @AgitEnd.\n",c); - // Stop auto saving - delete_timer (guild_save_timer, guild_save_sub); - return 0; -} - -int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data) -{ // Run One NPC_Event[OnAgitEliminate] - char *name = (char*)data; - size_t len = (name) ? strlen(name) : 0; - // the rest is dangerous, but let it crash, - // if this happens, it's ruined anyway - int c=0; - - if(agit_flag) // Agit not already End - { - char *evname=(char*)aMalloc( (len + 10) * sizeof(char)); - memcpy(evname,name,len - 5); - strcpy(evname + len - 5,"Eliminate"); - c = npc_event_do(evname); - ShowStatus("NPC_Event:[%s] Run (%d) Events.\n",evname,c); - aFree(evname); // [Lance] Should fix this - } - if(name) aFree(name); - return 0; -} - -static int Ghp[MAX_GUILDCASTLE][MAX_GUARDIANS]; // so save only if HP are changed // experimental code [Yor] -static int Gid[MAX_GUILDCASTLE]; -int guild_save_sub(int tid,unsigned int tick,int id,int data) -{ - struct guild_castle *gc; - int i,j; - - for(i = 0; i < MAX_GUILDCASTLE; i++) { // [Yor] - gc = guild_castle_search(i); - if (!gc) continue; - if (gc->guild_id != Gid[i]) { - // Re-save guild id if its owner guild has changed - guild_castledatasave(gc->castle_id, 1, gc->guild_id); - Gid[i] = gc->guild_id; - } - for (j = 0; j < MAX_GUARDIANS; j++) - { - if (gc->guardian[j].visible && Ghp[i][j] != gc->guardian[j].hp) - guild_castledatasave(gc->castle_id, 18+j, gc->guardian[j].hp); - } - } - - return 0; -} - -int guild_agit_break(struct mob_data *md) -{ // Run One NPC_Event[OnAgitBreak] - char *evname; - - nullpo_retr(0, md); - - evname=(char *)aMallocA((strlen(md->npc_event) + 1)*sizeof(char)); - - strcpy(evname,md->npc_event); -// Now By User to Run [OnAgitBreak] NPC Event... -// It's a little impossible to null point with player disconnect in this! -// But Script will be stop, so nothing... -// Maybe will be changed in the futher.. -// int c = npc_event_do(evname); - if(!agit_flag) return 0; // Agit already End - add_timer(gettick()+battle_config.gvg_eliminate_time,guild_gvg_eliminate_timer,md->bl.m,(int)evname); - return 0; -} - -// [MouseJstr] -// How many castles does this guild have? -int guild_checkcastles(struct guild *g) { - int i,nb_cas=0, id,cas_id=0; - struct guild_castle *gc; - id=g->guild_id; - for(i=0;iguild_id; - if(g->guild_id==cas_id) - nb_cas=nb_cas+1; - } //end for - return nb_cas; -} - -// [MouseJstr] -// is this guild allied with this castle? -int guild_isallied(struct guild *g, struct guild_castle *gc) -{ - int i; - - nullpo_retr(0, g); - - if(g->guild_id == gc->guild_id) - return 1; - - if (gc->guild_id == 0) - return 0; - - - for(i=0;ialliance[i].guild_id == gc->guild_id) { - if(g->alliance[i].opposition == 0) - return 1; - else - return 0; - } - - return 0; -} - -int guild_idisallied(int guild_id, int guild_id2) -{ - int i; - struct guild *g; - - if (guild_id <= 0 || guild_id2 <= 0) - return 0; - - if(guild_id == guild_id2) - return 1; - - g = guild_search(guild_id); - - nullpo_retr(0, g); - - - for(i=0;ialliance[i].guild_id == guild_id2) { - if(g->alliance[i].opposition == 0) - return 1; - else - return 0; - } - - return 0; -} - -static int guild_infoevent_db_final(DBKey key,void *data,va_list ap) -{ - aFree(data); - return 0; -} - -static int guild_expcache_db_final(DBKey key,void *data,va_list ap) -{ - ers_free(expcache_ers, data); - return 0; -} - -void do_final_guild(void) -{ - guild_db->destroy(guild_db,NULL); - castle_db->destroy(castle_db,NULL); - guild_expcache_db->destroy(guild_expcache_db,guild_expcache_db_final); - guild_infoevent_db->destroy(guild_infoevent_db,guild_infoevent_db_final); - guild_castleinfoevent_db->destroy(guild_castleinfoevent_db,guild_infoevent_db_final); - ers_destroy(expcache_ers); -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include + +#include "../common/timer.h" +#include "../common/nullpo.h" +#include "../common/malloc.h" +#include "../common/showmsg.h" +#include "../common/ers.h" + +#include "map.h" +#include "guild.h" +#include "storage.h" +#include "battle.h" +#include "npc.h" +#include "pc.h" +#include "status.h" +#include "mob.h" +#include "intif.h" +#include "clif.h" +#include "skill.h" +#include "log.h" + +static struct guild* guild_cache; //For fast retrieval of the same guild over and over. [Skotlex] +static struct dbt *guild_db; +static struct dbt *castle_db; +static struct dbt *guild_expcache_db; +static struct dbt *guild_infoevent_db; +static struct dbt *guild_castleinfoevent_db; + +struct eventlist { + char name[50]; + struct eventlist *next; +}; + +// ギルドのEXPキャッシュのフラッシュに関連する定数 +#define GUILD_SEND_XY_INVERVAL 5000 // 座標やHP送信の間隔 +#define GUILD_PAYEXP_INVERVAL 10000 // 間隔(キャッシュの最大生存時間、ミリ秒) +#define GUILD_PAYEXP_LIST 8192 // キャッシュの最大数 + +// ギルドのEXPキャッシュ +struct guild_expcache { + int guild_id, account_id, char_id; + unsigned int exp; +}; +static struct eri *expcache_ers; //For handling of guild exp payment. + +struct{ + int id; + int max; + struct{ + short id; + short lv; + }need[6]; +} guild_skill_tree[MAX_GUILDSKILL]; + +// timer for auto saving guild data during WoE +#define GUILD_SAVE_INTERVAL 300000 +int guild_save_timer = -1; + +int guild_payexp_timer(int tid,unsigned int tick,int id,int data); +int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data); +int guild_save_sub(int tid,unsigned int tick,int id,int data); +static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data); + + // Modified [Komurka] +int guild_skill_get_max (int id) +{ + if (id < GD_SKILLBASE || id > GD_SKILLBASE+MAX_GUILDSKILL) + return 0; + return guild_skill_tree[id-GD_SKILLBASE].max; +} + +// ギルドスキルがあるか確認 +int guild_checkskill(struct guild *g,int id) +{ + int idx = id-GD_SKILLBASE; + if (idx < 0 || idx >= MAX_GUILDSKILL) + return 0; + return g->skill[idx].lv; +} + +/*========================================== + * guild_skill_tree.txt reading - from jA [Komurka] + *------------------------------------------ + */ +int guild_read_guildskill_tree_db(void) +{ + int i,k,id=0,ln=0; + FILE *fp; + char line[1024],*p; + + malloc_set(guild_skill_tree,0,sizeof(guild_skill_tree)); + sprintf(line, "%s/guild_skill_tree.txt", db_path); + if( (fp=fopen(line,"r"))==NULL){ + ShowError("can't read %s\n", line); + return -1; + } + while(fgets(line,1020,fp)){ + char *split[50]; + if(line[0]=='/' && line[1]=='/') + continue; + for(i=0,p=line;i<12 && p;i++){ + split[i]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(i<12) + continue; + id = atoi(split[0]) - GD_SKILLBASE; + if(id<0 || id>=MAX_GUILDSKILL) + continue; + guild_skill_tree[id].id=atoi(split[0]); + guild_skill_tree[id].max=atoi(split[1]); + if (guild_skill_tree[id].id==GD_GLORYGUILD && battle_config.require_glory_guild && guild_skill_tree[id].max==0) guild_skill_tree[id].max=1; + for(k=0;k<5;k++){ + guild_skill_tree[id].need[k].id=atoi(split[k*2+2]); + guild_skill_tree[id].need[k].lv=atoi(split[k*2+3]); + } + ln++; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"guild_skill_tree.txt"); + + return 0; +} + +/*========================================== + * Guild skill check - from jA [Komurka] + *------------------------------------------ + */ +int guild_check_skill_require(struct guild *g,int id) +{ + int i; + int idx = id-GD_SKILLBASE; + + if(g == NULL) + return 0; + + if (idx < 0 || idx >= MAX_GUILDSKILL) + return 0; + + for(i=0;i<5;i++) + { + if(guild_skill_tree[idx].need[i].id == 0) break; + if(guild_skill_tree[idx].need[i].lv > guild_checkskill(g,guild_skill_tree[idx].need[i].id)) + return 0; + } + return 1; +} + +static int guild_read_castledb(void) +{ + FILE *fp; + char line[1024]; + int j,ln=0; + char *str[32],*p; + struct guild_castle *gc; + + sprintf(line, "%s/castle_db.txt", db_path); + if( (fp=fopen(line,"r"))==NULL){ + ShowError("can't read %s\n", line); + return -1; + } + + while(fgets(line,1020,fp)){ + if(line[0]=='/' && line[1]=='/') + continue; + malloc_tsetdword(str,0,sizeof(str)); + for(j=0,p=line;j<6 && p;j++){ + str[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if (j < 4) //Insufficient data for castle. [Skotlex] + { + ShowError("castle_db.txt: invalid line '%s'\n", line); + continue; + } + + gc=(struct guild_castle *)aCalloc(1,sizeof(struct guild_castle)); + gc->castle_id=atoi(str[0]); + memcpy(gc->map_name,str[1],MAP_NAME_LENGTH-1); + memcpy(gc->castle_name,str[2],NAME_LENGTH-1); + memcpy(gc->castle_event,str[3],NAME_LENGTH-1); + + idb_put(castle_db,gc->castle_id,gc); + + //intif_guild_castle_info(gc->castle_id); + + ln++; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"castle_db.txt"); + return 0; +} + +// 初期化 +void do_init_guild(void) +{ + guild_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + castle_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + guild_expcache_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); + guild_infoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); + expcache_ers = ers_new((uint32)sizeof(struct guild_expcache)); + guild_castleinfoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); + + guild_read_castledb(); + + guild_read_guildskill_tree_db(); //guild skill tree [Komurka] + + add_timer_func_list(guild_gvg_eliminate_timer,"guild_gvg_eliminate_timer"); + add_timer_func_list(guild_payexp_timer,"guild_payexp_timer"); + add_timer_func_list(guild_save_sub, "guild_save_sub"); + add_timer_func_list(guild_send_xy_timer, "guild_send_xy_timer"); + add_timer_interval(gettick()+GUILD_PAYEXP_INVERVAL,guild_payexp_timer,0,0,GUILD_PAYEXP_INVERVAL); + add_timer_interval(gettick()+GUILD_SEND_XY_INVERVAL,guild_send_xy_timer,0,0,GUILD_SEND_XY_INVERVAL); +} + + +// 検索 +struct guild *guild_search(int guild_id) +{ + if(guild_cache && guild_cache->guild_id == guild_id) + return guild_cache; + guild_cache = idb_get(guild_db,guild_id); + return guild_cache; +} +int guild_searchname_sub(DBKey key,void *data,va_list ap) +{ + struct guild *g=(struct guild *)data,**dst; + char *str; + str=va_arg(ap,char *); + dst=va_arg(ap,struct guild **); + if(strcmpi(g->name,str)==0) + *dst=g; + return 0; +} +// ギルド名検索 +struct guild* guild_searchname(char *str) +{ + struct guild *g=NULL; + guild_db->foreach(guild_db,guild_searchname_sub,str,&g); + return g; +} +struct guild_castle *guild_castle_search(int gcid) +{ + return idb_get(castle_db,gcid); +} + +// mapnameに対応したアジトのgcを返す +struct guild_castle *guild_mapname2gc(char *mapname) +{ + int i; + struct guild_castle *gc=NULL; + for(i=0;imap_name,mapname)==0) return gc; + } + return NULL; +} + +struct guild_castle *guild_mapindex2gc(short mapname) +{ + int i; + struct guild_castle *gc=NULL; + for(i=0;imap_name,mapindex_id2name(mapname))==0) return gc; + } + return NULL; +} + + + +// ログイン中のギルドメンバーの1人のsdを返す +struct map_session_data *guild_getavailablesd(struct guild *g) +{ + int i; + + nullpo_retr(NULL, g); + + for(i=0;imax_member;i++) + if(g->member[i].sd!=NULL) + return g->member[i].sd; + return NULL; +} + +// ギルドメンバーのインデックスを返す +int guild_getindex(struct guild *g,int account_id,int char_id) +{ + int i; + if(g==NULL) + return -1; + for(i=0;imax_member;i++) + if( g->member[i].account_id==account_id && + g->member[i].char_id==char_id ) + return i; + return -1; +} +// ギルドメンバーの役職を返す +int guild_getposition(struct map_session_data *sd,struct guild *g) +{ + int i; + + nullpo_retr(-1, sd); + + if(g==NULL && (g=guild_search(sd->status.guild_id))==NULL) + return -1; + for(i=0;imax_member;i++) + if( g->member[i].account_id==sd->status.account_id && + g->member[i].char_id==sd->status.char_id ) + return g->member[i].position; + return -1; +} + +// メンバー情報の作成 +void guild_makemember(struct guild_member *m,struct map_session_data *sd) +{ + nullpo_retv(sd); + + malloc_set(m,0,sizeof(struct guild_member)); + m->account_id =sd->status.account_id; + m->char_id =sd->status.char_id; + m->hair =sd->status.hair; + m->hair_color =sd->status.hair_color; + m->gender =sd->status.sex; + m->class_ =sd->status.class_; + m->lv =sd->status.base_level; +// m->exp =0; +// m->exp_payper =0; + m->online =1; + m->position =MAX_GUILDPOSITION-1; + memcpy(m->name,sd->status.name,NAME_LENGTH-1); + return; +} +// ギルド競合確認 +int guild_check_conflict(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + intif_guild_checkconflict(sd->status.guild_id, + sd->status.account_id,sd->status.char_id); + return 0; +} + +// ギルドのEXPキャッシュをinter鯖にフラッシュする +int guild_payexp_timer_sub(DBKey dataid, void *data, va_list ap) +{ + int i; + struct guild_expcache *c; + struct guild *g; + + c = (struct guild_expcache *)data; + + if ( + (g = guild_search(c->guild_id)) == NULL || + (i = guild_getindex(g, c->account_id, c->char_id)) < 0 + ) { + ers_free(expcache_ers, data); + return 0; + } + + if (g->member[i].exp > UINT_MAX - c->exp) + g->member[i].exp = UINT_MAX; + else + g->member[i].exp+= c->exp; + + intif_guild_change_memberinfo(g->guild_id,c->account_id,c->char_id, + GMI_EXP,&g->member[i].exp,sizeof(g->member[i].exp)); + c->exp=0; + + ers_free(expcache_ers, data); + return 0; +} + +int guild_payexp_timer(int tid, unsigned int tick, int id, int data) +{ + guild_expcache_db->clear(guild_expcache_db,guild_payexp_timer_sub); + return 0; +} + +//Taken from party_send_xy_timer_sub. [Skotlex] +int guild_send_xy_timer_sub(DBKey key,void *data,va_list ap) +{ + struct guild *g=(struct guild *)data; + int i; + + nullpo_retr(0, g); + + for(i=0;imax_member;i++){ + struct map_session_data *sd; + if((sd=g->member[i].sd)!=NULL){ + if(sd->guild_x!=sd->bl.x || sd->guild_y!=sd->bl.y){ + clif_guild_xy(sd); + sd->guild_x=sd->bl.x; + sd->guild_y=sd->bl.y; + } + } + } + return 0; +} + +//Code from party_send_xy_timer [Skotlex] +static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data) +{ + guild_db->foreach(guild_db,guild_send_xy_timer_sub,tick); + return 0; +} + +int guild_send_dot_remove(struct map_session_data *sd) +{ + if (sd->status.guild_id) + clif_guild_xy_remove(sd); + return 0; +} +//------------------------------------------------------------------------ + +// 作成要求 +int guild_create(struct map_session_data *sd,char *name) +{ + nullpo_retr(0, sd); + + if(sd->status.guild_id) + { + clif_guild_created(sd,1); // すでに所属している + return 0; + } + if(!battle_config.guild_emperium_check || pc_search_inventory(sd,714) >= 0) { + struct guild_member m; + guild_makemember(&m,sd); + m.position=0; + intif_guild_create(name,&m); + return 1; + } + clif_guild_created(sd,3); // エンペリウムがいない + return 0; +} + +// 作成可否 +int guild_created(int account_id,int guild_id) +{ + struct map_session_data *sd=map_id2sd(account_id); + + if(sd==NULL) + return 0; + if(!guild_id) { + clif_guild_created(sd,2); // 作成失敗(同名ギルド存在) + return 0; + } + //struct guild *g; + sd->status.guild_id=guild_id; + sd->state.guild_sent=0; + clif_guild_created(sd,0); + if(battle_config.guild_emperium_check) + pc_delitem(sd,pc_search_inventory(sd,714),1,0); // エンペリウム消耗 + return 0; +} + +// 情報要求 +int guild_request_info(int guild_id) +{ +// if(battle_config.etc_log) +// printf("guild_request_info\n"); + return intif_guild_request_info(guild_id); +} +// イベント付き情報要求 +int guild_npc_request_info(int guild_id,const char *event) +{ + struct eventlist *ev; + + if( guild_search(guild_id) ){ + if(event && *event) + npc_event_do(event); + return 0; + } + + if(event==NULL || *event==0) + return guild_request_info(guild_id); + + ev=(struct eventlist *)aCalloc(sizeof(struct eventlist),1); + memcpy(ev->name,event,strlen(event)); + //The one in the db becomes the next event from this. + ev->next=idb_put(guild_infoevent_db,guild_id,ev); + return guild_request_info(guild_id); +} + +// 所属キャラの確認 +int guild_check_member(struct guild *g) +{ + int i, j, users; + struct map_session_data *sd, **all_sd; + + nullpo_retr(0, g); + + all_sd = map_getallusers(&users); + + for(i=0;istatus.guild_id==g->guild_id){ + j=guild_getindex(g,sd->status.account_id,sd->status.char_id); + if (j < 0) { + sd->status.guild_id=0; + sd->state.guild_sent=0; + sd->guild_emblem_id=0; + if(battle_config.error_log) + ShowWarning("guild: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name); + } + } + } + return 0; +} +// 情報所得失敗(そのIDのキャラを全部未所属にする) +int guild_recv_noinfo(int guild_id) +{ + int i, users; + struct map_session_data *sd, **all_sd; + + all_sd = map_getallusers(&users); + + for(i=0;istatus.guild_id==guild_id) + sd->status.guild_id=0; + } + } + return 0; +} +// 情報所得 +int guild_recv_info(struct guild *sg) +{ + struct guild *g,before; + int i,bm,m; + struct eventlist *ev,*ev2; + + nullpo_retr(0, sg); + + if((g=idb_get(guild_db,sg->guild_id))==NULL){ + struct map_session_data *sd; + g=(struct guild *)aCalloc(1,sizeof(struct guild)); + idb_put(guild_db,sg->guild_id,g); + before=*sg; + + // 最初のロードなのでユーザーのチェックを行う + guild_check_member(sg); + //If the guild master is online the first time the guild_info is received, that means he was the first to join, + //and as such, his guild skills should be blocked to avoid login/logout abuse [Skotlex] + if ((sd = map_nick2sd(sg->master)) != NULL) + { + guild_block_skill(sd, 300000); + //Also set the guild master flag. + sd->state.gmaster_flag = g; + clif_charnameupdate(sd); // [LuzZza] + } + }else + before=*g; + memcpy(g,sg,sizeof(struct guild)); + + if(g->max_member > MAX_GUILD) + { + if (battle_config.error_log) + ShowError("guild_recv_info: Received guild with %d members, but MAX_GUILD is only %d. Extra guild-members have been lost!\n", g->max_member, MAX_GUILD); + g->max_member = MAX_GUILD; + } + + for(i=bm=m=0;imax_member;i++){ // sdの設定と人数の確認 + if(g->member[i].account_id>0){ + struct map_session_data *sd = map_id2sd(g->member[i].account_id); + if (sd && sd->status.char_id == g->member[i].char_id && + sd->status.guild_id == g->guild_id && + !sd->state.waitingdisconnect) { + g->member[i].sd = sd; + clif_charnameupdate(sd); // [LuzZza] + } else g->member[i].sd = NULL; + m++; + }else + g->member[i].sd=NULL; + if(before.member[i].account_id>0) + bm++; + } + + for(i=0;imax_member;i++){ // 情報の送信 + struct map_session_data *sd = g->member[i].sd; + if( sd==NULL ) + continue; + + if( before.guild_lv!=g->guild_lv || bm!=m || + before.max_member!=g->max_member ){ + clif_guild_basicinfo(sd); // 基本情報送信 + clif_guild_emblem(sd,g); // エンブレム送信 + } + + if(bm!=m){ // メンバー情報送信 + clif_guild_memberlist(g->member[i].sd); + } + + if( before.skill_point!=g->skill_point) + clif_guild_skillinfo(sd); // スキル情報送信 + + if( sd->state.guild_sent==0){ // 未送信なら所属情報も送る + clif_guild_belonginfo(sd,g); + clif_guild_notice(sd,g); + sd->guild_emblem_id=g->emblem_id; + sd->state.guild_sent=1; + } + } + + // イベントの発生 + if( (ev=idb_remove(guild_infoevent_db,sg->guild_id))!=NULL ){ + while(ev){ + npc_event_do(ev->name); + ev2=ev->next; + aFree(ev); + ev=ev2; + } + } + + return 0; +} + + +// ギルドへの勧誘 +int guild_invite(struct map_session_data *sd,struct map_session_data *tsd) +{ + struct guild *g; + int i; + + nullpo_retr(0, sd); + + g=guild_search(sd->status.guild_id); + + if(tsd==NULL || g==NULL) + return 0; + + if(!battle_config.invite_request_check) { + if (tsd->party_invite>0 || tsd->trade_partner) { // 相手が取引中かどうか + clif_guild_inviteack(sd,0); + return 0; + } + } + if(tsd->status.guild_id>0 || + tsd->guild_invite>0 || + map[tsd->bl.m].flag.gvg_castle) + { //Can't invite people inside castles. [Skotlex] + clif_guild_inviteack(sd,0); + return 0; + } + + // 定員確認 + for(i=0;imax_member;i++) + if(g->member[i].account_id==0) + break; + if(i==g->max_member){ + clif_guild_inviteack(sd,3); + return 0; + } + + tsd->guild_invite=sd->status.guild_id; + tsd->guild_invite_account=sd->status.account_id; + + clif_guild_invite(tsd,g); + return 0; +} +// ギルド勧誘への返答 +int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag) +{ + struct map_session_data *tsd; + + nullpo_retr(0, sd); + + //nullpo_retr(0, tsd= map_id2sd( sd->guild_invite_account )); + //I checked the code, and there's no "check" for the case where the guy + //that invites another to a guild quits the map-server before being replied. + //Hence that's a valid null pointer scenario. :) [Skotlex] + if ((tsd= map_id2sd( sd->guild_invite_account )) == NULL) + { //Do we send a "invitation failed" msg or something to the player? + //Or should we accept the invitation and add it to the guild anyway? + //afterall, guild_invite holds the guild id that the player was invited to. + sd->guild_invite=0; + sd->guild_invite_account=0; + return 0; + } + + if(sd->guild_invite!=guild_id) // 勧誘とギルドIDが違う + return 0; + + if(flag==1){ // 承諾 + struct guild_member m; + struct guild *g; + int i; + + // 定員確認 + if( (g=guild_search(tsd->status.guild_id))==NULL ){ + sd->guild_invite=0; + sd->guild_invite_account=0; + return 0; + } + for(i=0;imax_member;i++) + if(g->member[i].account_id==0) + break; + if(i==g->max_member){ + sd->guild_invite=0; + sd->guild_invite_account=0; + clif_guild_inviteack(tsd,3); + return 0; + } + + + //inter鯖へ追加要求 + guild_makemember(&m,sd); + intif_guild_addmember( sd->guild_invite, &m ); + return 0; + }else{ // 拒否 + sd->guild_invite=0; + sd->guild_invite_account=0; + clif_guild_inviteack(tsd,1); + } + return 0; +} +// ギルドメンバが追加された +int guild_member_added(int guild_id,int account_id,int char_id,int flag) +{ + struct map_session_data *sd= map_id2sd(account_id),*sd2; + struct guild *g; + + if( (g=guild_search(guild_id))==NULL ) + return 0; + + if(sd==NULL || sd->guild_invite==0){ + // キャラ側に登録できなかったため脱退要求を出す + if (flag == 0) { + if(battle_config.error_log) + ShowError("guild: member added error %d is not online\n",account_id); + intif_guild_leave(guild_id,account_id,char_id,0,"**登録失敗**"); + } + return 0; + } + sd2 = map_id2sd(sd->guild_invite_account); + sd->guild_invite = 0; + sd->guild_invite_account = 0; + + if(flag==1){ // 失敗 + if( sd2!=NULL ) + clif_guild_inviteack(sd2,3); + return 0; + } + + // 成功 + sd->state.guild_sent=0; + sd->status.guild_id=guild_id; + + if( sd2!=NULL ) + clif_guild_inviteack(sd2,2); + + // いちおう競合確認 + guild_check_conflict(sd); + //Next line commented because it do nothing, look at guild_recv_info [LuzZza] + //clif_charnameupdate(sd); //Update display name [Skotlex] + return 0; +} + +// ギルド脱退要求 +int guild_leave(struct map_session_data *sd,int guild_id, + int account_id,int char_id,const char *mes) +{ + struct guild *g; + int i; + + nullpo_retr(0, sd); + + g = guild_search(sd->status.guild_id); + + if(g==NULL) + return 0; + + if( sd->status.account_id!=account_id || + sd->status.char_id!=char_id || sd->status.guild_id!=guild_id || + map[sd->bl.m].flag.gvg_castle) //Can't leave inside guild castles. + return 0; + + for(i=0;imax_member;i++){ // 所属しているか + if( g->member[i].account_id==sd->status.account_id && + g->member[i].char_id==sd->status.char_id ){ + intif_guild_leave(g->guild_id,sd->status.account_id,sd->status.char_id,0,mes); + return 0; + } + } + return 0; +} +// ギルド追放要求 +int guild_expulsion(struct map_session_data *sd,int guild_id, + int account_id,int char_id,const char *mes) +{ + struct guild *g; + int i,ps; + + nullpo_retr(0, sd); + + g = guild_search(sd->status.guild_id); + + if(g==NULL) + return 0; + + if(sd->status.guild_id!=guild_id || map[sd->bl.m].flag.gvg_castle) //Can't leave inside guild castles. + return 0; + + if( (ps=guild_getposition(sd,g))<0 || !(g->position[ps].mode&0x0010) ) + return 0; // 処罰権限無し + + for(i=0;imax_member;i++){ // 所属しているか + if( g->member[i].account_id==account_id && + g->member[i].char_id==char_id ){ + intif_guild_leave(g->guild_id,account_id,char_id,1,mes); + //It's wrong way, member info will erased later + //see guild_member_leaved [LuzZza] + //malloc_set(&g->member[i],0,sizeof(struct guild_member)); + return 0; + } + } + return 0; +} + +int guild_member_leaved(int guild_id,int account_id,int char_id,int flag, + const char *name,const char *mes) // rewrote [LuzZza] +{ + int i; + struct guild *g = guild_search(guild_id); + struct map_session_data *sd = map_charid2sd(char_id); + struct map_session_data *online_member_sd; + + if(g == NULL) + return 0; + + for(i=0;imax_member;i++) { + if( g->member[i].account_id == account_id && + g->member[i].char_id == char_id ){ + + if((online_member_sd = guild_getavailablesd(g)) == NULL) + return 0; + + if(!flag) + clif_guild_leave(online_member_sd, name, mes); + else + clif_guild_expulsion(online_member_sd, name, mes, account_id); + + malloc_set(&g->member[i],0,sizeof(struct guild_member)); + clif_guild_memberlist(online_member_sd); + + if(sd != NULL && sd->status.guild_id == guild_id) { + if (sd->state.storage_flag == 2) //Close the guild storage. + storage_guild_storageclose(sd); + sd->status.guild_id=0; + sd->guild_emblem_id=0; + sd->state.guild_sent=0; + + guild_send_dot_remove(sd); + clif_charnameupdate(sd); //Update display name [Skotlex] + } + return 0; + } + + } + + return 0; +} + +int guild_send_memberinfoshort(struct map_session_data *sd,int online) +{ // cleaned up [LuzZza] + struct guild *g; + + nullpo_retr(0, sd); + + if(!(g = guild_search(sd->status.guild_id))) + return 0; + + //Moved to place before intif_guild_memberinfoshort because + //If it's not a member, needn't send it's info to intif. [LuzZza] + guild_check_member(g); + + if(sd->status.guild_id <= 0) + return 0; + + intif_guild_memberinfoshort(g->guild_id, + sd->status.account_id,sd->status.char_id,online,sd->status.base_level,sd->status.class_); + + if(!online){ + int i=guild_getindex(g,sd->status.account_id,sd->status.char_id); + if(i>=0) + g->member[i].sd=NULL; + return 0; + } + + if(sd->state.guild_sent) + return 0; + + clif_guild_belonginfo(sd,g); + clif_guild_notice(sd,g); + + sd->state.guild_sent = 1; + sd->guild_emblem_id = g->emblem_id; + + return 0; +} + +int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_) +{ // cleaned up [LuzZza] + + int i,alv,c,idx=-1,om=0,oldonline=-1; + struct guild *g = guild_search(guild_id); + + if(g == NULL) + return 0; + + for(i=0,alv=0,c=0,om=0;imax_member;i++){ + struct guild_member *m=&g->member[i]; + if(m->account_id==account_id && m->char_id==char_id ){ + oldonline=m->online; + m->online=online; + m->lv=lv; + m->class_=class_; + idx=i; + } + if(m->account_id>0){ + alv+=m->lv; + c++; + } + if(m->online) + om++; + } + + if(idx == -1 || c == 0) { + // ギルドのメンバー外なので追放扱いする + struct map_session_data *sd = map_id2sd(account_id); + if(sd && sd->status.char_id == char_id) { + sd->status.guild_id=0; + sd->guild_emblem_id=0; + sd->state.guild_sent=0; + } + if(battle_config.error_log) + ShowWarning("guild: not found member %d,%d on %d[%s]\n", account_id,char_id,guild_id,g->name); + return 0; + } + + g->average_lv=alv/c; + g->connect_member=om; + + for(i=0;imax_member;i++) { + struct map_session_data *sd= map_id2sd(g->member[i].account_id); + g->member[i].sd = (sd && sd->status.char_id == g->member[i].char_id && + sd->status.guild_id == g->guild_id && !sd->state.waitingdisconnect) ? sd : NULL; + } + + if(oldonline!=online) + clif_guild_memberlogin_notice(g, idx, online); + + + if(!g->member[idx].sd) + return 0; + + //Send XY dot updates. [Skotlex] + //Moved from guild_send_memberinfoshort [LuzZza] + for(i=0; i < g->max_member; i++) { + + if(!g->member[i].sd || i == idx || + g->member[i].sd->bl.m != g->member[idx].sd->bl.m) + continue; + + clif_guild_xy_single(g->member[idx].sd->fd, g->member[i].sd); + } + + return 0; +} +// ギルド会話送信 +int guild_send_message(struct map_session_data *sd,char *mes,int len) +{ + nullpo_retr(0, sd); + + if(sd->status.guild_id==0) + return 0; + intif_guild_message(sd->status.guild_id,sd->status.account_id,mes,len); + guild_recv_message(sd->status.guild_id,sd->status.account_id,mes,len); + + //Chatlogging type 'G' + if(log_config.chat&1 //we log everything then + || ( log_config.chat&8 //if Guild bit is on + && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF + log_chat("G", sd->status.guild_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes); + + return 0; +} +// ギルド会話受信 +int guild_recv_message(int guild_id,int account_id,char *mes,int len) +{ + struct guild *g; + if( (g=guild_search(guild_id))==NULL) + return 0; + clif_guild_message(g,account_id,mes,len); + return 0; +} +// ギルドメンバの役職変更 +int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx) +{ + return intif_guild_change_memberinfo( + guild_id,account_id,char_id,GMI_POSITION,&idx,sizeof(idx)); +} +// ギルドメンバの役職変更通知 +int guild_memberposition_changed(struct guild *g,int idx,int pos) +{ + nullpo_retr(0, g); + + g->member[idx].position=pos; + clif_guild_memberpositionchanged(g,idx); + + // Update char position in client [LuzZza] + if(g->member[idx].sd != NULL) + clif_charnameupdate(g->member[idx].sd); + return 0; +} +// ギルド役職変更 +int guild_change_position(struct map_session_data *sd,int idx, + int mode,int exp_mode,const char *name) +{ + struct guild_position p; + + nullpo_retr(0, sd); + + if(exp_mode>battle_config.guild_exp_limit) + exp_mode=battle_config.guild_exp_limit; + if(exp_mode<0)exp_mode=0; + p.mode=mode; + p.exp_mode=exp_mode; + memcpy(p.name,name,NAME_LENGTH-1); + p.name[NAME_LENGTH-1] = '\0'; //Security check... [Skotlex] + return intif_guild_position(sd->status.guild_id,idx,&p); +} +// ギルド役職変更通知 +int guild_position_changed(int guild_id,int idx,struct guild_position *p) +{ + struct guild *g=guild_search(guild_id); + int i; + if(g==NULL) + return 0; + memcpy(&g->position[idx],p,sizeof(struct guild_position)); + clif_guild_positionchanged(g,idx); + + // Update char name in client [LuzZza] + for(i=0;imax_member;i++) + if(g->member[i].position == idx && g->member[i].sd != NULL) + clif_charnameupdate(g->member[i].sd); + return 0; +} +// ギルド告知変更 +int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2) +{ + nullpo_retr(0, sd); + + if(guild_id!=sd->status.guild_id) + return 0; + return intif_guild_notice(guild_id,mes1,mes2); +} +// ギルド告知変更通知 +int guild_notice_changed(int guild_id,const char *mes1,const char *mes2) +{ + int i; + struct map_session_data *sd; + struct guild *g=guild_search(guild_id); + if(g==NULL) + return 0; + + memcpy(g->mes1,mes1,60); + memcpy(g->mes2,mes2,120); + + for(i=0;imax_member;i++){ + if((sd=g->member[i].sd)!=NULL) + clif_guild_notice(sd,g); + } + return 0; +} +// ギルドエンブレム変更 +int guild_change_emblem(struct map_session_data *sd,int len,const char *data) +{ + struct guild *g; + nullpo_retr(0, sd); + + if (battle_config.require_glory_guild && + !((g = guild_search(sd->status.guild_id)) && guild_checkskill(g, GD_GLORYGUILD)>0)) { + clif_skill_fail(sd,GD_GLORYGUILD,0,0); + return 0; + } + + return intif_guild_emblem(sd->status.guild_id,len,data); +} +// ギルドエンブレム変更通知 +int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data) +{ + int i; + struct map_session_data *sd; + struct guild *g=guild_search(guild_id); + if(g==NULL) + return 0; + + memcpy(g->emblem_data,data,len); + g->emblem_len=len; + g->emblem_id=emblem_id; + + for(i=0;imax_member;i++){ + if((sd=g->member[i].sd)!=NULL){ + sd->guild_emblem_id=emblem_id; + clif_guild_belonginfo(sd,g); + clif_guild_emblem(sd,g); + } + } + return 0; +} + +static void* create_expcache(DBKey key, va_list args) { + struct guild_expcache *c; + struct map_session_data *sd = va_arg(args, struct map_session_data*); + + c = ers_alloc(expcache_ers, struct guild_expcache); + c->guild_id = sd->status.guild_id; + c->account_id = sd->status.account_id; + c->char_id = sd->status.char_id; + c->exp = 0; + return c; +} + +// ギルドのEXP上納 +unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp) +{ + struct guild *g; + struct guild_expcache *c; + int per; + + nullpo_retr(0, sd); + + if (!exp) return 0; + + if (sd->status.guild_id == 0 || + (g = guild_search(sd->status.guild_id)) == NULL || + (per = guild_getposition(sd,g)) < 0 || + (per = g->position[per].exp_mode) < 1) + return 0; + + + if (per < 100) + exp = (unsigned int) exp * per / 100; + //Otherwise tax everything. + + c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd); + + if (c->exp > UINT_MAX - exp) + c->exp = UINT_MAX; + else + c->exp += exp; + + return exp; +} + +// Celest +int guild_getexp(struct map_session_data *sd,int exp) +{ + struct guild *g; + struct guild_expcache *c; + nullpo_retr(0, sd); + + if (sd->status.guild_id == 0 || (g = guild_search(sd->status.guild_id)) == NULL) + return 0; + + c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd); + if (c->exp > UINT_MAX - exp) + c->exp = UINT_MAX; + else + c->exp += exp; + return exp; +} + +// スキルポイント割り振り +int guild_skillup(struct map_session_data *sd,int skill_num,int flag) +{ + struct guild *g; + int idx = skill_num - GD_SKILLBASE; + + nullpo_retr(0, sd); + + if(idx < 0 || idx >= MAX_GUILDSKILL) + + return 0; + if(sd->status.guild_id==0 || (g=guild_search(sd->status.guild_id))==NULL) + return 0; + if(strcmp(sd->status.name,g->master)) + return 0; + + if( (g->skill_point>0 || flag&1) && + g->skill[idx].id!=0 && + g->skill[idx].lv < guild_skill_get_max(skill_num) ){ + intif_guild_skillup(g->guild_id,skill_num,sd->status.account_id,flag); + } + return 0; +} +// スキルポイント割り振り通知 +int guild_skillupack(int guild_id,int skill_num,int account_id) +{ + struct map_session_data *sd=map_id2sd(account_id); + struct guild *g=guild_search(guild_id); + int i; + if(g==NULL) + return 0; + if(sd!=NULL) + clif_guild_skillup(sd,skill_num,g->skill[skill_num-GD_SKILLBASE].lv); + // 全員に通知 + for(i=0;imax_member;i++) + if((sd=g->member[i].sd)!=NULL) + clif_guild_skillinfo(sd); + return 0; +} + +// ギルド同盟数所得 +int guild_get_alliance_count(struct guild *g,int flag) +{ + int i,c; + + nullpo_retr(0, g); + + for(i=c=0;ialliance[i].guild_id>0 && + g->alliance[i].opposition==flag ) + c++; + } + return c; +} + +// Blocks all guild skills which have a common delay time. +void guild_block_skill(struct map_session_data *sd, int time) { + int skill_num[] = { GD_BATTLEORDER, GD_REGENERATION, GD_RESTORE, GD_EMERGENCYCALL }; + int i; + for (i = 0; i < 4; i++) + skill_blockpc_start(sd, skill_num[i], time); +} + +// 同盟関係かどうかチェック +// 同盟なら1、それ以外は0 +int guild_check_alliance(int guild_id1, int guild_id2, int flag) +{ + struct guild *g; + int i; + + g = guild_search(guild_id1); + if (g == NULL) + return 0; + + for (i=0; ialliance[i].guild_id == guild_id2) && (g->alliance[i].opposition == flag)) + return 1; + + return 0; +} +// ギルド同盟要求 +int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd) +{ + struct guild *g[2]; + int i; + + if(agit_flag) { // Disable alliance creation during woe [Valaris] + clif_displaymessage(sd->fd,"Alliances cannot be made during Guild Wars!"); + return 0; + } // end addition [Valaris] + + + nullpo_retr(0, sd); + + if(tsd==NULL || tsd->status.guild_id<=0) + return 0; + + g[0]=guild_search(sd->status.guild_id); + g[1]=guild_search(tsd->status.guild_id); + + if(g[0]==NULL || g[1]==NULL) + return 0; + + // Prevent creation alliance with same guilds [LuzZza] + if(sd->status.guild_id == tsd->status.guild_id) + return 0; + + if( guild_get_alliance_count(g[0],0)>=3 ) { + clif_guild_allianceack(sd,4); + return 0; + } + if( guild_get_alliance_count(g[1],0)>=3 ) { + clif_guild_allianceack(sd,3); + return 0; + } + + if( tsd->guild_alliance>0 ){ + clif_guild_allianceack(sd,1); + return 0; + } + + for(i=0;ialliance[i].guild_id==tsd->status.guild_id && + g[0]->alliance[i].opposition==0){ + clif_guild_allianceack(sd,0); + return 0; + } + } + + tsd->guild_alliance=sd->status.guild_id; + tsd->guild_alliance_account=sd->status.account_id; + + clif_guild_reqalliance(tsd,sd->status.account_id,g[0]->name); + return 0; +} +// ギルド勧誘への返答 +int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag) +{ + struct map_session_data *tsd; + + nullpo_retr(0, sd); + tsd= map_id2sd( account_id ); + if (!tsd) { //Character left? Cancel alliance. + clif_guild_allianceack(sd,3); + return 0; + } + + if(sd->guild_alliance!=tsd->status.guild_id) // 勧誘とギルドIDが違う + return 0; + + if(flag==1){ // 承諾 + int i; + + struct guild *g,*tg; // 同盟数再確認 + g=guild_search(sd->status.guild_id); + tg=guild_search(tsd->status.guild_id); + + if(g==NULL || guild_get_alliance_count(g,0)>=3){ + clif_guild_allianceack(sd,4); + clif_guild_allianceack(tsd,3); + return 0; + } + if(tg==NULL || guild_get_alliance_count(tg,0)>=3){ + clif_guild_allianceack(sd,3); + clif_guild_allianceack(tsd,4); + return 0; + } + + for(i=0;ialliance[i].guild_id==tsd->status.guild_id && + g->alliance[i].opposition==1) + intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, + sd->status.account_id,tsd->status.account_id,9 ); + } + for(i=0;ialliance[i].guild_id==sd->status.guild_id && + tg->alliance[i].opposition==1) + intif_guild_alliance( tsd->status.guild_id,sd->status.guild_id, + tsd->status.account_id,sd->status.account_id,9 ); + } + + // inter鯖へ同盟要請 + intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, + sd->status.account_id,tsd->status.account_id,0 ); + return 0; + }else{ // 拒否 + sd->guild_alliance=0; + sd->guild_alliance_account=0; + if(tsd!=NULL) + clif_guild_allianceack(tsd,3); + } + return 0; +} +// ギルド関係解消 +int guild_delalliance(struct map_session_data *sd,int guild_id,int flag) +{ + nullpo_retr(0, sd); + + if(agit_flag) { // Disable alliance breaking during woe [Valaris] + clif_displaymessage(sd->fd,"Alliances cannot be broken during Guild Wars!"); + return 0; + } // end addition [Valaris] + + intif_guild_alliance( sd->status.guild_id,guild_id, + sd->status.account_id,0,flag|8 ); + return 0; +} +// ギルド敵対 +int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd) +{ + struct guild *g; + int i; + + nullpo_retr(0, sd); + + g=guild_search(sd->status.guild_id); + if(g==NULL || tsd==NULL) + return 0; + + // Prevent creation opposition with same guilds [LuzZza] + if(sd->status.guild_id == tsd->status.guild_id) + return 0; + + if( guild_get_alliance_count(g,1)>=3 ) { + clif_guild_oppositionack(sd,1); + return 0; + } + + if(agit_flag) { + clif_displaymessage(sd->fd,"You cannot make oppositions during Guild Wars!"); + return 0; + } + + for(i=0;ialliance[i].guild_id==tsd->status.guild_id){ + if(g->alliance[i].opposition==1){ // すでに敵対 + clif_guild_oppositionack(sd,2); + return 0; + } + //Change alliance to opposition. + intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, + sd->status.account_id,tsd->status.account_id,8 ); + } + } + + // inter鯖に敵対要請 + intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, + sd->status.account_id,tsd->status.account_id,1 ); + return 0; +} +// ギルド同盟/敵対通知 +int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2, + int flag,const char *name1,const char *name2) +{ + struct guild *g[2]; + int guild_id[2]; + const char *guild_name[2]; + struct map_session_data *sd[2]; + int j,i; + + guild_id[0] = guild_id1; + guild_id[1] = guild_id2; + guild_name[0] = name1; + guild_name[1] = name2; + sd[0] = map_id2sd(account_id1); + sd[1] = map_id2sd(account_id2); + + g[0]=guild_search(guild_id1); + g[1]=guild_search(guild_id2); + + if(sd[0]!=NULL && (flag&0x0f)==0){ + sd[0]->guild_alliance=0; + sd[0]->guild_alliance_account=0; + } + + if(flag&0x70){ // 失敗 + for(i=0;i<2-(flag&1);i++) + if( sd[i]!=NULL ) + clif_guild_allianceack(sd[i],((flag>>4)==i+1)?3:4); + return 0; + } +// if(battle_config.etc_log) +// printf("guild alliance_ack %d %d %d %d %d %s %s\n",guild_id1,guild_id2,account_id1,account_id2,flag,name1,name2); + + if(!(flag&0x08)){ // 関係追加 + for(i=0;i<2-(flag&1);i++) + if(g[i]!=NULL) + for(j=0;jalliance[j].guild_id==0){ + g[i]->alliance[j].guild_id=guild_id[1-i]; + memcpy(g[i]->alliance[j].name,guild_name[1-i],NAME_LENGTH-1); + g[i]->alliance[j].opposition=flag&1; + break; + } + }else{ // 関係解消 + for(i=0;i<2-(flag&1);i++){ + if(g[i]!=NULL) + for(j=0;jalliance[j].guild_id==guild_id[1-i] && + g[i]->alliance[j].opposition==(flag&1)){ + g[i]->alliance[j].guild_id=0; + break; + } + if( sd[i]!=NULL ) // 解消通知 + clif_guild_delalliance(sd[i],guild_id[1-i],(flag&1)); + } + } + + if((flag&0x0f)==0){ // 同盟通知 + if( sd[1]!=NULL ) + clif_guild_allianceack(sd[1],2); + }else if((flag&0x0f)==1){ // 敵対通知 + if( sd[0]!=NULL ) + clif_guild_oppositionack(sd[0],0); + } + + + for(i=0;i<2-(flag&1);i++){ // 同盟/敵対リストの再送信 + struct map_session_data *sd; + if(g[i]!=NULL) + for(j=0;jmax_member;j++) + if((sd=g[i]->member[j].sd)!=NULL) + clif_guild_allianceinfo(sd); + } + return 0; +} +// ギルド解散通知用 +int guild_broken_sub(DBKey key,void *data,va_list ap) +{ + struct guild *g=(struct guild *)data; + int guild_id=va_arg(ap,int); + int i,j; + struct map_session_data *sd=NULL; + + nullpo_retr(0, g); + + for(i=0;ialliance[i].guild_id==guild_id){ + for(j=0;jmax_member;j++) + if( (sd=g->member[j].sd)!=NULL ) + clif_guild_delalliance(sd,guild_id,g->alliance[i].opposition); + intif_guild_alliance(g->guild_id, guild_id,0,0,g->alliance[i].opposition|8); + g->alliance[i].guild_id=0; + } + } + return 0; +} + +//Invoked on Castles when a guild is broken. [Skotlex] +int castle_guild_broken_sub(DBKey key,void *data,va_list ap) +{ + struct guild_castle *gc=(struct guild_castle *)data; + int guild_id=va_arg(ap,int); + + nullpo_retr(0, gc); + + if (gc->guild_id == guild_id) + { //Save the new 'owner', this should invoke guardian clean up and other such things. + gc->guild_id = 0; + guild_castledatasave(gc->castle_id, 1, 0); + } + return 0; +} + +//Innvoked on /breakguild "Guild name" +int guild_broken(int guild_id,int flag) +{ + struct guild *g=guild_search(guild_id); +// struct guild_castle *gc=NULL; + struct map_session_data *sd; + int i; +// char *name;; + + if(flag!=0 || g==NULL) + return 0; + + //we call castle_event::OnGuildBreak of all castlesof the guild + //you can set all castle_events in the castle_db.txt +/* name=(char *)aCalloc(50,sizeof(char)); //24 char = event name, + space for "::OnGuildBreak" + for(i=0;iguild_id == guild_id){ + memcpy(name,gc->castle_event,50); + npc_event_do(strcat(name,"::OnGuildBreak")); + } + } + } + free(name); +*/ + for(i=0;imax_member;i++){ // ギルド解散を通知 + if((sd=g->member[i].sd)!=NULL){ + if(sd->state.storage_flag == 2) + storage_guild_storage_quit(sd,1); + sd->status.guild_id=0; + sd->state.guild_sent=0; + clif_guild_broken(g->member[i].sd,0); + clif_charnameupdate(sd); // [LuzZza] + } + } + + guild_db->foreach(guild_db,guild_broken_sub,guild_id); + castle_db->foreach(castle_db,castle_guild_broken_sub,guild_id); + if (guild_cache && guild_cache->guild_id == guild_id) + guild_cache = NULL; + guild_storage_delete(guild_id); + idb_remove(guild_db,guild_id); + return 0; +} + +//Changes the Guild Master to the specified player. [Skotlex] +int guild_gm_change(int guild_id, struct map_session_data *sd) +{ + struct guild *g; + nullpo_retr(0, sd); + + if (sd->status.guild_id != guild_id) + return 0; + + g=guild_search(guild_id); + + nullpo_retr(0, g); + + if (strcmp(g->master, sd->status.name) == 0) //Nothing to change. + return 0; + + //Notify servers that master has changed. + intif_guild_change_gm(guild_id, sd->status.name, strlen(sd->status.name)); + return 1; +} + +//Notification from Char server that a guild's master has changed. [Skotlex] +int guild_gm_changed(int guild_id, int pos) +{ + struct guild *g; + struct guild_member gm; + + g=guild_search(guild_id); + + if (!g || pos < 0 || pos > g->max_member) + return 0; + + memcpy(&gm, &g->member[pos], sizeof (struct guild_member)); + memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member)); + memcpy(&g->member[0], &gm, sizeof(struct guild_member)); + + g->member[pos].position = g->member[0].position; + g->member[0].position = 0; //Position 0: guild Master. + strcpy(g->master, g->member[0].name); + + if (g->member[pos].sd && g->member[pos].sd->fd) + { + clif_displaymessage(g->member[pos].sd->fd, "You no longer are the Guild Master."); + g->member[pos].sd->state.gmaster_flag = 0; + } + + if (g->member[0].sd && g->member[0].sd->fd) + { + clif_displaymessage(g->member[0].sd->fd, "You have become the Guild Master!"); + g->member[0].sd->state.gmaster_flag = g; + //Block his skills for 5 minutes to prevent abuse. + guild_block_skill(g->member[0].sd, 300000); + } + return 1; +} + +// ギルド解散 +int guild_break(struct map_session_data *sd,char *name) +{ + struct guild *g; + int i; + + nullpo_retr(0, sd); + + if( (g=guild_search(sd->status.guild_id))==NULL ) + return 0; + if(strcmp(g->name,name)!=0) + return 0; + if(strcmp(sd->status.name,g->master)!=0) + return 0; + for(i=0;imax_member;i++){ + if( g->member[i].account_id>0 && ( + g->member[i].account_id!=sd->status.account_id || + g->member[i].char_id!=sd->status.char_id )) + break; + } + if(imax_member){ + clif_guild_broken(sd,2); + return 0; + } + + intif_guild_break(g->guild_id); + return 0; +} + +// ギルド城データ要求 +int guild_castledataload(int castle_id,int index) +{ + return intif_guild_castle_dataload(castle_id,index); +} +// ギルド城情報所得時イベント追加 +int guild_addcastleinfoevent(int castle_id,int index,const char *name) +{ + struct eventlist *ev; + int code=castle_id|(index<<16); + + if( name==NULL || *name==0 ) + return 0; + + ev=(struct eventlist *)aMalloc(sizeof(struct eventlist)); + memcpy(ev->name,name,sizeof(ev->name)); + //The next event becomes whatever was currently stored. + ev->next= idb_put(guild_castleinfoevent_db,code,ev); + return 0; +} + +// ギルド城データ要求返信 +int guild_castledataloadack(int castle_id,int index,int value) +{ + struct guild_castle *gc=guild_castle_search(castle_id); + int code=castle_id|(index<<16); + struct eventlist *ev,*ev2; + + if(gc==NULL){ + return 0; + } + switch(index){ + case 1: + gc->guild_id = value; + if (value && guild_search(value)==NULL) //Request guild data which will be required for spawned guardians. [Skotlex] + guild_request_info(value); + break; + case 2: gc->economy = value; break; + case 3: gc->defense = value; break; + case 4: gc->triggerE = value; break; + case 5: gc->triggerD = value; break; + case 6: gc->nextTime = value; break; + case 7: gc->payTime = value; break; + case 8: gc->createTime = value; break; + case 9: gc->visibleC = value; break; + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + gc->guardian[index-10].visible = value; break; + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + gc->guardian[index-18].hp = value; break; + default: + ShowError("guild_castledataloadack ERROR!! (Not found index=%d)\n", index); + return 0; + } + if( (ev=idb_remove(guild_castleinfoevent_db,code))!=NULL ){ + while(ev){ + npc_event_do(ev->name); + ev2=ev->next; + aFree(ev); + ev=ev2; + } + } + return 1; +} +// ギルド城データ変更要求 +int guild_castledatasave(int castle_id,int index,int value) +{ + if (index == 1) + { //The castle's owner has changed? Update Guardian ownership, too. [Skotlex] + struct guild_castle *gc = guild_castle_search(castle_id); + int m = -1; + if (gc) m = map_mapname2mapid(gc->map_name); + if (m != -1) + map_foreachinmap(mob_guardian_guildchange, m, BL_MOB); + } + return intif_guild_castle_datasave(castle_id,index,value); +} + +// ギルド城データ変更通知 +int guild_castledatasaveack(int castle_id,int index,int value) +{ + struct guild_castle *gc=guild_castle_search(castle_id); + if(gc==NULL){ + return 0; + } + switch(index){ + case 1: gc->guild_id = value; break; + case 2: gc->economy = value; break; + case 3: gc->defense = value; break; + case 4: gc->triggerE = value; break; + case 5: gc->triggerD = value; break; + case 6: gc->nextTime = value; break; + case 7: gc->payTime = value; break; + case 8: gc->createTime = value; break; + case 9: gc->visibleC = value; break; + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + gc->guardian[index-10].visible = value; break; + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + gc->guardian[index-18].hp = value; break; + default: + ShowError("guild_castledatasaveack ERROR!! (Not found index=%d)\n", index); + return 0; + } + return 1; +} + +// ギルドデータ一括受信(初期化時) +int guild_castlealldataload(int len,struct guild_castle *gc) +{ + int i; + int n = (len-4) / sizeof(struct guild_castle), ev = -1; + + nullpo_retr(0, gc); + + //Last owned castle in the list invokes ::OnAgitinit + for(i = 0; i < n; i++) { + if ((gc + i)->guild_id) + ev = i; + } + + // 城データ格納とギルド情報要求 + for(i = 0; i < n; i++, gc++) { + struct guild_castle *c = guild_castle_search(gc->castle_id); + if (!c) { + ShowError("guild_castlealldataload Castle id=%d not found.\n", gc->castle_id); + continue; + } + memcpy(&c->guild_id,&gc->guild_id, + sizeof(struct guild_castle) - ((int)&c->guild_id - (int)c) ); + if( c->guild_id ){ + if(i!=ev) + guild_request_info(c->guild_id); + else + guild_npc_request_info(c->guild_id, "::OnAgitInit"); + } + } + if (ev == -1) //No castles owned, invoke OnAgitInit as it is. + npc_event_doall("OnAgitInit"); + return 0; +} + +int guild_agit_start(void) +{ // Run All NPC_Event[OnAgitStart] + int c = npc_event_doall("OnAgitStart"); + ShowStatus("NPC_Event:[OnAgitStart] Run (%d) Events by @AgitStart.\n",c); + // Start auto saving + guild_save_timer = add_timer_interval (gettick() + GUILD_SAVE_INTERVAL, guild_save_sub, 0, 0, GUILD_SAVE_INTERVAL); + return 0; +} + +int guild_agit_end(void) +{ // Run All NPC_Event[OnAgitEnd] + int c = npc_event_doall("OnAgitEnd"); + ShowStatus("NPC_Event:[OnAgitEnd] Run (%d) Events by @AgitEnd.\n",c); + // Stop auto saving + delete_timer (guild_save_timer, guild_save_sub); + return 0; +} + +int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data) +{ // Run One NPC_Event[OnAgitEliminate] + char *name = (char*)data; + size_t len = (name) ? strlen(name) : 0; + // the rest is dangerous, but let it crash, + // if this happens, it's ruined anyway + int c=0; + + if(agit_flag) // Agit not already End + { + char *evname=(char*)aMalloc( (len + 10) * sizeof(char)); + memcpy(evname,name,len - 5); + strcpy(evname + len - 5,"Eliminate"); + c = npc_event_do(evname); + ShowStatus("NPC_Event:[%s] Run (%d) Events.\n",evname,c); + aFree(evname); // [Lance] Should fix this + } + if(name) aFree(name); + return 0; +} + +static int Ghp[MAX_GUILDCASTLE][MAX_GUARDIANS]; // so save only if HP are changed // experimental code [Yor] +static int Gid[MAX_GUILDCASTLE]; +int guild_save_sub(int tid,unsigned int tick,int id,int data) +{ + struct guild_castle *gc; + int i,j; + + for(i = 0; i < MAX_GUILDCASTLE; i++) { // [Yor] + gc = guild_castle_search(i); + if (!gc) continue; + if (gc->guild_id != Gid[i]) { + // Re-save guild id if its owner guild has changed + guild_castledatasave(gc->castle_id, 1, gc->guild_id); + Gid[i] = gc->guild_id; + } + for (j = 0; j < MAX_GUARDIANS; j++) + { + if (gc->guardian[j].visible && Ghp[i][j] != gc->guardian[j].hp) + guild_castledatasave(gc->castle_id, 18+j, gc->guardian[j].hp); + } + } + + return 0; +} + +int guild_agit_break(struct mob_data *md) +{ // Run One NPC_Event[OnAgitBreak] + char *evname; + + nullpo_retr(0, md); + + evname=(char *)aMallocA((strlen(md->npc_event) + 1)*sizeof(char)); + + strcpy(evname,md->npc_event); +// Now By User to Run [OnAgitBreak] NPC Event... +// It's a little impossible to null point with player disconnect in this! +// But Script will be stop, so nothing... +// Maybe will be changed in the futher.. +// int c = npc_event_do(evname); + if(!agit_flag) return 0; // Agit already End + add_timer(gettick()+battle_config.gvg_eliminate_time,guild_gvg_eliminate_timer,md->bl.m,(int)evname); + return 0; +} + +// [MouseJstr] +// How many castles does this guild have? +int guild_checkcastles(struct guild *g) { + int i,nb_cas=0, id,cas_id=0; + struct guild_castle *gc; + id=g->guild_id; + for(i=0;iguild_id; + if(g->guild_id==cas_id) + nb_cas=nb_cas+1; + } //end for + return nb_cas; +} + +// [MouseJstr] +// is this guild allied with this castle? +int guild_isallied(struct guild *g, struct guild_castle *gc) +{ + int i; + + nullpo_retr(0, g); + + if(g->guild_id == gc->guild_id) + return 1; + + if (gc->guild_id == 0) + return 0; + + + for(i=0;ialliance[i].guild_id == gc->guild_id) { + if(g->alliance[i].opposition == 0) + return 1; + else + return 0; + } + + return 0; +} + +int guild_idisallied(int guild_id, int guild_id2) +{ + int i; + struct guild *g; + + if (guild_id <= 0 || guild_id2 <= 0) + return 0; + + if(guild_id == guild_id2) + return 1; + + g = guild_search(guild_id); + + nullpo_retr(0, g); + + + for(i=0;ialliance[i].guild_id == guild_id2) { + if(g->alliance[i].opposition == 0) + return 1; + else + return 0; + } + + return 0; +} + +static int guild_infoevent_db_final(DBKey key,void *data,va_list ap) +{ + aFree(data); + return 0; +} + +static int guild_expcache_db_final(DBKey key,void *data,va_list ap) +{ + ers_free(expcache_ers, data); + return 0; +} + +void do_final_guild(void) +{ + guild_db->destroy(guild_db,NULL); + castle_db->destroy(castle_db,NULL); + guild_expcache_db->destroy(guild_expcache_db,guild_expcache_db_final); + guild_infoevent_db->destroy(guild_infoevent_db,guild_infoevent_db_final); + guild_castleinfoevent_db->destroy(guild_castleinfoevent_db,guild_infoevent_db_final); + ers_destroy(expcache_ers); +} diff --git a/src/map/guild.h b/src/map/guild.h index 5a71c4525..69174e021 100644 --- a/src/map/guild.h +++ b/src/map/guild.h @@ -1,95 +1,95 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _GUILD_H_ -#define _GUILD_H_ - -struct map_session_data; -struct mob_data; -struct guild; -struct guild_member; -struct guild_position; -struct guild_castle; - -int guild_skill_get_max(int id); - -int guild_checkskill(struct guild *g,int id); -int guild_check_skill_require(struct guild *g,int id); // [Komurka] -int guild_checkcastles(struct guild *g); // [MouseJstr] -int guild_isallied(struct guild *g, struct guild_castle *gc); -int guild_idisallied(int guild_id, int guild_id2); //Checks alliance based on guild Ids. [Skotlex] - -void do_init_guild(void); -struct guild *guild_search(int guild_id); -struct guild *guild_searchname(char *str); -struct guild_castle *guild_castle_search(int gcid); - -struct guild_castle *guild_mapname2gc(char *mapname); -struct guild_castle *guild_mapindex2gc(short mapname); - -struct map_session_data *guild_getavailablesd(struct guild *g); -int guild_getindex(struct guild *g,int account_id,int char_id); -int guild_getposition(struct map_session_data *sd,struct guild *g); -unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp); -int guild_getexp(struct map_session_data *sd,int exp); // [Celest] - -int guild_create(struct map_session_data *sd,char *name); -int guild_created(int account_id,int guild_id); -int guild_request_info(int guild_id); -int guild_recv_noinfo(int guild_id); -int guild_recv_info(struct guild *sg); -int guild_npc_request_info(int guild_id,const char *ev); -int guild_invite(struct map_session_data *sd,struct map_session_data *tsd); -int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag); -int guild_member_added(int guild_id,int account_id,int char_id,int flag); -int guild_leave(struct map_session_data *sd,int guild_id, - int account_id,int char_id,const char *mes); -int guild_member_leaved(int guild_id,int account_id,int char_id,int flag, - const char *name,const char *mes); -int guild_expulsion(struct map_session_data *sd,int guild_id, - int account_id,int char_id,const char *mes); -int guild_skillup(struct map_session_data *sd,int skill_num,int flag); -void guild_block_skill(struct map_session_data *sd, int time); -int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd); -int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag); -int guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2); -int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2, - int flag,const char *name1,const char *name2); -int guild_delalliance(struct map_session_data *sd,int guild_id,int flag); -int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd); -int guild_check_alliance(int guild_id1, int guild_id2, int flag); - -int guild_send_memberinfoshort(struct map_session_data *sd,int online); -int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_); -int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx); -int guild_memberposition_changed(struct guild *g,int idx,int pos); -int guild_change_position(struct map_session_data *sd,int idx, - int mode,int exp_mode,const char *name); -int guild_position_changed(int guild_id,int idx,struct guild_position *p); -int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2); -int guild_notice_changed(int guild_id,const char *mes1,const char *mes2); -int guild_change_emblem(struct map_session_data *sd,int len,const char *data); -int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data); -int guild_send_message(struct map_session_data *sd,char *mes,int len); -int guild_recv_message(int guild_id,int account_id,char *mes,int len); -int guild_send_dot_remove(struct map_session_data *sd); -int guild_skillupack(int guild_id,int skill_num,int account_id); -int guild_break(struct map_session_data *sd,char *name); -int guild_broken(int guild_id,int flag); -int guild_gm_change(int guild_id, struct map_session_data *sd); -int guild_gm_changed(int guild_id, int pos); - -int guild_addcastleinfoevent(int castle_id,int index,const char *name); -int guild_castledataload(int castle_id,int index); -int guild_castledataloadack(int castle_id,int index,int value); -int guild_castledatasave(int castle_id,int index,int value); -int guild_castledatasaveack(int castle_id,int index,int value); -int guild_castlealldataload(int len,struct guild_castle *gc); - -int guild_agit_start(void); -int guild_agit_end(void); -int guild_agit_break(struct mob_data *md); - -void do_final_guild(void); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _GUILD_H_ +#define _GUILD_H_ + +struct map_session_data; +struct mob_data; +struct guild; +struct guild_member; +struct guild_position; +struct guild_castle; + +int guild_skill_get_max(int id); + +int guild_checkskill(struct guild *g,int id); +int guild_check_skill_require(struct guild *g,int id); // [Komurka] +int guild_checkcastles(struct guild *g); // [MouseJstr] +int guild_isallied(struct guild *g, struct guild_castle *gc); +int guild_idisallied(int guild_id, int guild_id2); //Checks alliance based on guild Ids. [Skotlex] + +void do_init_guild(void); +struct guild *guild_search(int guild_id); +struct guild *guild_searchname(char *str); +struct guild_castle *guild_castle_search(int gcid); + +struct guild_castle *guild_mapname2gc(char *mapname); +struct guild_castle *guild_mapindex2gc(short mapname); + +struct map_session_data *guild_getavailablesd(struct guild *g); +int guild_getindex(struct guild *g,int account_id,int char_id); +int guild_getposition(struct map_session_data *sd,struct guild *g); +unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp); +int guild_getexp(struct map_session_data *sd,int exp); // [Celest] + +int guild_create(struct map_session_data *sd,char *name); +int guild_created(int account_id,int guild_id); +int guild_request_info(int guild_id); +int guild_recv_noinfo(int guild_id); +int guild_recv_info(struct guild *sg); +int guild_npc_request_info(int guild_id,const char *ev); +int guild_invite(struct map_session_data *sd,struct map_session_data *tsd); +int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag); +int guild_member_added(int guild_id,int account_id,int char_id,int flag); +int guild_leave(struct map_session_data *sd,int guild_id, + int account_id,int char_id,const char *mes); +int guild_member_leaved(int guild_id,int account_id,int char_id,int flag, + const char *name,const char *mes); +int guild_expulsion(struct map_session_data *sd,int guild_id, + int account_id,int char_id,const char *mes); +int guild_skillup(struct map_session_data *sd,int skill_num,int flag); +void guild_block_skill(struct map_session_data *sd, int time); +int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd); +int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag); +int guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2); +int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2, + int flag,const char *name1,const char *name2); +int guild_delalliance(struct map_session_data *sd,int guild_id,int flag); +int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd); +int guild_check_alliance(int guild_id1, int guild_id2, int flag); + +int guild_send_memberinfoshort(struct map_session_data *sd,int online); +int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_); +int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx); +int guild_memberposition_changed(struct guild *g,int idx,int pos); +int guild_change_position(struct map_session_data *sd,int idx, + int mode,int exp_mode,const char *name); +int guild_position_changed(int guild_id,int idx,struct guild_position *p); +int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2); +int guild_notice_changed(int guild_id,const char *mes1,const char *mes2); +int guild_change_emblem(struct map_session_data *sd,int len,const char *data); +int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data); +int guild_send_message(struct map_session_data *sd,char *mes,int len); +int guild_recv_message(int guild_id,int account_id,char *mes,int len); +int guild_send_dot_remove(struct map_session_data *sd); +int guild_skillupack(int guild_id,int skill_num,int account_id); +int guild_break(struct map_session_data *sd,char *name); +int guild_broken(int guild_id,int flag); +int guild_gm_change(int guild_id, struct map_session_data *sd); +int guild_gm_changed(int guild_id, int pos); + +int guild_addcastleinfoevent(int castle_id,int index,const char *name); +int guild_castledataload(int castle_id,int index); +int guild_castledataloadack(int castle_id,int index,int value); +int guild_castledatasave(int castle_id,int index,int value); +int guild_castledatasaveack(int castle_id,int index,int value); +int guild_castlealldataload(int len,struct guild_castle *gc); + +int guild_agit_start(void); +int guild_agit_end(void); +int guild_agit_break(struct mob_data *md); + +void do_final_guild(void); + +#endif diff --git a/src/map/intif.h b/src/map/intif.h index 0351642b1..8fc005508 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -1,72 +1,72 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _INTIF_H_ -#define _INFIF_H_ - -int intif_parse(int fd); - -int intif_GMmessage(char* mes,int len,int flag); -int intif_announce(char* mes,int len, unsigned long color, int flag); - -int intif_wis_message(struct map_session_data *sd,char *nick,char *mes,int mes_len); -int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes); - -int intif_saveregistry(struct map_session_data *sd, int type); -int intif_request_registry(struct map_session_data *sd, int flag); - -int intif_request_storage(int account_id); -int intif_send_storage(struct storage *stor); -int intif_request_guild_storage(int account_id, int guild_id); -int intif_send_guild_storage(int account_id, struct guild_storage *gstor); - - -int intif_create_party(struct party_member *member,char *name,int item,int item2); -int intif_request_partyinfo(int party_id); - -int intif_party_addmember(int party_id,struct party_member *member); -int intif_party_changeoption(int party_id, int account_id, int exp, int item); -int intif_party_leave(int party_id,int account_id, int char_id); -int intif_party_changemap(struct map_session_data *sd, int online); -int intif_break_party(int party_id); -int intif_party_message(int party_id, int account_id, char *mes,int len); -int intif_party_checkconflict(int party_id,int account_id,int char_id); -int intif_party_leaderchange(int party_id,int account_id,int char_id); - - -int intif_guild_create(const char *name, const struct guild_member *master); -int intif_guild_request_info(int guild_id); -int intif_guild_addmember(int guild_id, struct guild_member *m); -int intif_guild_leave(int guild_id, int account_id, int char_id, int flag, const char *mes); -int intif_guild_memberinfoshort(int guild_id, int account_id, int char_id, int online, int lv, int class_); -int intif_guild_break(int guild_id); -int intif_guild_message(int guild_id, int account_id, char *mes, int len); -int intif_guild_checkconflict(int guild_id, int account_id, int char_id); -int intif_guild_change_gm(int guild_id, const char* name, int len); -int intif_guild_change_basicinfo(int guild_id, int type, const void *data, int len); -int intif_guild_change_memberinfo(int guild_id, int account_id, int char_id, int type, const void *data, int len); -int intif_guild_position(int guild_id, int idx, struct guild_position *p); -int intif_guild_skillup(int guild_id, int skill_num, int account_id, int flag); -int intif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag); -int intif_guild_notice(int guild_id, const char *mes1, const char *mes2); -int intif_guild_emblem(int guild_id, int len, const char *data); -int intif_guild_castle_dataload(int castle_id, int index); -int intif_guild_castle_datasave(int castle_id, int index, int value); - -int intif_create_pet(int account_id, int char_id, short pet_type, short pet_lv, short pet_egg_id, - short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name); -int intif_request_petdata(int account_id, int char_id, int pet_id); -int intif_save_petdata(int account_id, struct s_pet *p); -int intif_delete_petdata(int pet_id); -int intif_rename_pet(struct map_session_data *sd, char *name); - - -int intif_homunculus_create(int account_id, struct s_homunculus *sh); -int intif_homunculus_requestload(int account_id, int homun_id); -int intif_homunculus_requestsave(int account_id, struct s_homunculus* sh); -int intif_homunculus_requestdelete(int homun_id); - - -int CheckForCharServer(void); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _INTIF_H_ +#define _INFIF_H_ + +int intif_parse(int fd); + +int intif_GMmessage(char* mes,int len,int flag); +int intif_announce(char* mes,int len, unsigned long color, int flag); + +int intif_wis_message(struct map_session_data *sd,char *nick,char *mes,int mes_len); +int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes); + +int intif_saveregistry(struct map_session_data *sd, int type); +int intif_request_registry(struct map_session_data *sd, int flag); + +int intif_request_storage(int account_id); +int intif_send_storage(struct storage *stor); +int intif_request_guild_storage(int account_id, int guild_id); +int intif_send_guild_storage(int account_id, struct guild_storage *gstor); + + +int intif_create_party(struct party_member *member,char *name,int item,int item2); +int intif_request_partyinfo(int party_id); + +int intif_party_addmember(int party_id,struct party_member *member); +int intif_party_changeoption(int party_id, int account_id, int exp, int item); +int intif_party_leave(int party_id,int account_id, int char_id); +int intif_party_changemap(struct map_session_data *sd, int online); +int intif_break_party(int party_id); +int intif_party_message(int party_id, int account_id, char *mes,int len); +int intif_party_checkconflict(int party_id,int account_id,int char_id); +int intif_party_leaderchange(int party_id,int account_id,int char_id); + + +int intif_guild_create(const char *name, const struct guild_member *master); +int intif_guild_request_info(int guild_id); +int intif_guild_addmember(int guild_id, struct guild_member *m); +int intif_guild_leave(int guild_id, int account_id, int char_id, int flag, const char *mes); +int intif_guild_memberinfoshort(int guild_id, int account_id, int char_id, int online, int lv, int class_); +int intif_guild_break(int guild_id); +int intif_guild_message(int guild_id, int account_id, char *mes, int len); +int intif_guild_checkconflict(int guild_id, int account_id, int char_id); +int intif_guild_change_gm(int guild_id, const char* name, int len); +int intif_guild_change_basicinfo(int guild_id, int type, const void *data, int len); +int intif_guild_change_memberinfo(int guild_id, int account_id, int char_id, int type, const void *data, int len); +int intif_guild_position(int guild_id, int idx, struct guild_position *p); +int intif_guild_skillup(int guild_id, int skill_num, int account_id, int flag); +int intif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag); +int intif_guild_notice(int guild_id, const char *mes1, const char *mes2); +int intif_guild_emblem(int guild_id, int len, const char *data); +int intif_guild_castle_dataload(int castle_id, int index); +int intif_guild_castle_datasave(int castle_id, int index, int value); + +int intif_create_pet(int account_id, int char_id, short pet_type, short pet_lv, short pet_egg_id, + short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name); +int intif_request_petdata(int account_id, int char_id, int pet_id); +int intif_save_petdata(int account_id, struct s_pet *p); +int intif_delete_petdata(int pet_id); +int intif_rename_pet(struct map_session_data *sd, char *name); + + +int intif_homunculus_create(int account_id, struct s_homunculus *sh); +int intif_homunculus_requestload(int account_id, int homun_id); +int intif_homunculus_requestsave(int account_id, struct s_homunculus* sh); +int intif_homunculus_requestdelete(int homun_id); + + +int CheckForCharServer(void); + +#endif diff --git a/src/map/irc.c b/src/map/irc.c index 4b9187107..4722e9c2e 100644 --- a/src/map/irc.c +++ b/src/map/irc.c @@ -1,543 +1,543 @@ -#include -#include -#include - -#include "../common/core.h" -#include "../common/socket.h" -#include "../common/malloc.h" -#include "../common/db.h" -#include "../common/timer.h" -#include "../common/strlib.h" -#include "../common/mmo.h" -#include "../common/showmsg.h" -#include "../common/version.h" -#include "../common/nullpo.h" - -#include "map.h" -#include "pc.h" -#include "irc.h" -#include "intif.h" //For GM Broadcast [Zido] - -short use_irc=0; - -short irc_autojoin=0; - -short irc_announce_flag=1; -short irc_announce_mvp_flag=1; -short irc_announce_jobchange_flag=1; -short irc_announce_shop_flag=1; - -IRC_SI *irc_si=NULL; - -char irc_nick[30]=""; -char irc_password[32]=""; - -char irc_channel[32]=""; -char irc_channel_pass[32]=""; -char irc_trade_channel[32]=""; - -unsigned char irc_ip_str[128]=""; -unsigned long irc_ip=0; -unsigned short irc_port = 6667; -int irc_fd=0; - -struct channel_data cd; -int last_cd_user=0; - -int irc_connect_timer(int tid, unsigned int tick, int id, int data) -{ - if(irc_si && session[irc_si->fd]) - return 0; - //Ok, this ShowInfo and printf are a little ugly, but they are meant to - //debug just how long the code freezes here. [Skotlex] - ShowInfo("(IRC) Connecting to %s... ", irc_ip_str); - irc_fd = make_connection(irc_ip,irc_port); - if(irc_fd > 0){ - printf("ok\n"); - session[irc_fd]->func_parse = irc_parse; - } else - printf("failed\n"); - return 0; -} - -void irc_announce(char *buf) -{ - char send_string[256]; - // malloc_tsetdword(send_string,'\0',256); // NOT REQUIRED - - sprintf(send_string,"PRIVMSG %s :",irc_channel); - strcat(send_string, buf); - irc_send(send_string); -} - -void irc_announce_jobchange(struct map_session_data *sd) -{ - char send_string[256]; - - nullpo_retv(sd); - malloc_tsetdword(send_string,'\0',256); - - sprintf(send_string,"PRIVMSG %s :%s has changed into a %s.",irc_channel,sd->status.name,job_name(sd->status.class_)); - irc_send(send_string); -} - -void irc_announce_shop(struct map_session_data *sd, int flag) -{ - char send_string[256]; - char mapname[16]; - int maplen = 0; - nullpo_retv(sd); - - malloc_tsetdword(send_string,'\0',256); - malloc_tsetdword(mapname,'\0',16); - - if(flag){ - strcpy(mapname, map[sd->bl.m].name); - maplen = strcspn(mapname,"."); - mapname[maplen] = '\0'; - mapname[0]=toupper(mapname[0]); - - sprintf(send_string,"PRIVMSG %s :%s has opened a shop, %s, at <%d,%d> in %s.",irc_trade_channel,sd->status.name,sd->message,sd->bl.x,sd->bl.y,mapname); - } else - sprintf(send_string,"PRIVMSG %s :%s has closed their shop.",irc_trade_channel,sd->status.name); - - irc_send(send_string); -} - -void irc_announce_mvp(struct map_session_data *sd, struct mob_data *md) -{ - char send_string[256]; - char mapname[16]; - int maplen = 0; - - nullpo_retv(sd); - nullpo_retv(md); - - malloc_tsetdword(send_string,'\0',256); - malloc_tsetdword(mapname,'\0',16); - mapname[15]='\0'; // 15 is the final index, not 16 [Lance] - strcpy(mapname, map[md->bl.m].name); - maplen = strcspn(mapname,"."); - mapname[maplen] = '\0'; - mapname[0]=toupper(mapname[0]); - - sprintf(send_string,"PRIVMSG %s :%s the %s has MVP'd %s in %s.",irc_channel,sd->status.name,job_name(sd->status.class_),md->name, mapname); - irc_send(send_string); -} - -int irc_parse(int fd) -{ - if (session[fd]->eof){ - do_close(fd); - irc_si = NULL; - add_timer(gettick() + 15000, irc_connect_timer, 0, 0); - return 0; - } - if (session[fd]->session_data == NULL){ - irc_si = (struct IRC_Session_Info*)aMalloc(sizeof(struct IRC_Session_Info)); - irc_si->fd = fd; - irc_si->state = 0; - session[fd]->session_data = irc_si; - } else if (!irc_si) { - irc_si = (struct IRC_Session_Info*)session[fd]->session_data; - irc_si->fd = fd; - } - if(RFIFOREST(fd) > 0){ - char *incoming_string=aMalloc(RFIFOREST(fd)*sizeof(char)); - RFIFOHEAD(fd); - memcpy(incoming_string,RFIFOP(fd,0),RFIFOREST(fd)); - send_to_parser(fd,incoming_string,"\n"); - RFIFOSKIP(fd,RFIFOREST(fd)); - aFree(incoming_string); - } - return 0; -} - -int irc_keepalive_timer(int tid, unsigned int tick, int id, int data) -{ - char send_string[128]; - malloc_tsetdword(send_string,'\0',128); - - sprintf(send_string,"PRIVMSG %s : ", irc_nick); - irc_send(send_string); - add_timer(gettick() + 30000, irc_keepalive_timer, 0, 0); - return 0; -} - -void irc_send_sub(int fd, char transmit[4096]) -{ - sprintf(transmit,"%s%c",transmit,10); - WFIFOHEAD(fd,strlen(transmit)); - memcpy(WFIFOP(fd,0),transmit, strlen(transmit)); - WFIFOSET(fd,strlen(transmit)); -} - -void irc_send(char *buf) -{ - char transmit[4096]; - - if(!irc_si || !session[irc_si->fd]) - return; - - malloc_tsetdword(transmit,'\0',4096); - - sprintf(transmit,buf); - irc_send_sub(irc_si->fd,transmit); -} - -void irc_parse_sub(int fd, char *incoming_string) -{ - char source[256]; - char command[256]; - char target[256]; - char message[8192]; - char send_string[8192]; - char *source_nick=NULL; - char *source_ident=NULL; - char *source_host=NULL; - char *state_mgr=NULL; - - char cmd1[256]; - char cmd2[256]; - char cmdname[256]; - char cmdargs[256]; - - int users=0; - int i=0; - - struct map_session_data **allsd; - - malloc_tsetdword(source,'\0',256); - malloc_tsetdword(command,'\0',256); - malloc_tsetdword(target,'\0',256); - malloc_tsetdword(message,'\0',8192); - malloc_tsetdword(send_string,'\0',8192); - - malloc_tsetdword(cmd1,'\0',256); - malloc_tsetdword(cmd2,'\0',256); - malloc_tsetdword(cmdname,'\0',256); - malloc_tsetdword(cmdargs,'\0',256); - - sscanf(incoming_string, ":%255s %255s %255s :%4095[^\r\n]", source, command, target, message); - if (source != NULL) { - if (strstr(source,"!") != NULL) { - source_nick = strtok_r(source,"!",&state_mgr); - source_ident = strtok_r(NULL,"@",&state_mgr); - source_host = strtok_r(NULL,"%%",&state_mgr); - } - } - if (irc_si->state == 0){ - sprintf(send_string, "NICK %s", irc_nick); - irc_send(send_string); - sprintf(send_string, "USER eABot 8 * : eABot"); - irc_send(send_string); - irc_si->state = 1; - } - else if (irc_si->state == 1){ - if(!strcmp(command,"001")){ - ShowStatus("IRC: Connected to IRC.\n"); - sprintf(send_string, "PRIVMSG nickserv :identify %s", irc_password); - irc_send(send_string); - sprintf(send_string, "JOIN %s %s", irc_channel, irc_channel_pass); - irc_send(send_string); - sprintf(send_string,"NAMES %s",irc_channel); - irc_send(send_string); - irc_si->state = 2; - } - else if(!strcmp(command,"433")){ - ShowError("IRC: Nickname %s is already taken, IRC Client unable to connect.\n", irc_nick); - sprintf(send_string, "QUIT"); - irc_send(send_string); - if(session[fd]) - session[fd]->eof=1; - } - } - else if (irc_si->state == 2){ - if(!strcmp(source, "PING")){ - sprintf(send_string, "PONG %s", command); - irc_send(send_string); - } - - else if((strcmpi(target,irc_channel)==0)||(strcmpi(target,irc_channel+1)==0)) { - - // Broadcast [Zido] (Work in Progress) - if((strcmpi(command,"privmsg")==0)&&(sscanf(message,"@%255s %255[^\r\n]",cmdname,cmdargs)>0)&&(target[0]=='#')) { - if(strcmpi(cmdname,"kami")==0) { - if(get_access(source_nick)0) { - sprintf(send_string,"NOTICE %s :%d Users Online",source_nick,users); - irc_send(send_string); - for(i=0;istatus.name); - irc_send(send_string); - } - } else { - sprintf(send_string,"NOTICE %s :No Users Online",source_nick); - irc_send(send_string); - } - } - } - - // Refresh Names [Zido] - else if((strcmpi(command,"join")==0)||(strcmpi(command,"part")==0)||(strcmpi(command,"mode")==0)||(strcmpi(command,"nick")==0)) { - ShowInfo("IRC: Refreshing User List"); - irc_rmnames(); - printf("..."); - sprintf(send_string,"NAMES %s",irc_channel); - printf("..."); - irc_send(send_string); - printf("Done\n"); - } - - // Autojoin on kick [Zido] - else if((strcmpi(command,"kick")==0)&&(irc_autojoin==1)) { - sprintf(send_string, "JOIN %s %s", target, irc_channel_pass); - irc_send(send_string); - } - } - - // Names Reply [Zido] - else if((strcmpi(command,"353")==0)) { - ShowInfo("IRC: NAMES recieved\n"); - parse_names_packet(incoming_string); - } - } - - return; -} - -int send_to_parser(int fd, char *input,char key[2]) -{ - char *temp_string=NULL; - char *state_mgr=NULL; - int total_loops=0; - - temp_string = strtok_r(input,key,&state_mgr); - while (temp_string != NULL){ - total_loops = total_loops+1; - irc_parse_sub(fd,temp_string); - temp_string = strtok_r(NULL,key,&state_mgr); - } - return total_loops; -} - -void do_final_irc(void) -{ - -} - -void do_init_irc(void) -{ - if(!use_irc) - return; - if (irc_ip_str[strlen(irc_ip_str)-1] == '\n') - irc_ip_str[strlen(irc_ip_str)-1] = '\0'; - irc_ip = resolve_hostbyname(irc_ip_str, NULL, irc_ip_str); - if (!irc_ip) - { - ShowError("Unable to resolve %s! Cannot connect to IRC server, disabling irc_bot.\n", irc_ip_str); - use_irc = 0; - return; - } - - irc_connect_timer(0, 0, 0, 0); - - add_timer_func_list(irc_connect_timer, "irc_connect_timer"); - add_timer_func_list(irc_keepalive_timer, "irc_keepalive_timer"); - add_timer(gettick() + 30000, irc_keepalive_timer, 0, 0); -} - -//NAMES Packet(353) parser [Zido] -int parse_names_packet(char *str) { - char *tok; - char source[256]; - char numeric[10]; - char target[256]; - char channel[256]; - char names[1024]; - - malloc_tsetdword(source,'\0',256); - malloc_tsetword(numeric,'\0',10); - malloc_tsetdword(target,'\0',256); - malloc_tsetdword(channel,'\0',256); - malloc_tsetdword(names,'\0',1024); - - tok=strtok(str,"\r\n"); - sscanf(tok,":%255s %10s %255s %*1[=@] %255s :%1023[^\r\n]",source,numeric,target,channel,names); - if(strcmpi(numeric,"353")==0) - parse_names(names); - - while((tok=strtok(NULL,"\r\n"))!=NULL) { - sscanf(tok,":%255s %10s %255s %*1[=@] %255s :%1023[^\r\n]",source,numeric,target,channel,names); - if(strcmpi(numeric,"353")==0) - parse_names(names); - } - - return 0; -} - -//User access level prefix parser [Zido] -int parse_names(char *str) { - char *tok; - if (str == NULL) return 0; //Nothing to parse! - tok=strtok(str," "); - switch(tok[0]) { - case '~': - set_access(tok+1,ACCESS_OWNER); - break; - case '&': - set_access(tok+1,ACCESS_SOP); - break; - case '@': - set_access(tok+1,ACCESS_OP); - break; - case '%': - set_access(tok+1,ACCESS_HOP); - break; - case '+': - set_access(tok+1,ACCESS_VOICE); - break; - default: - set_access(tok,ACCESS_NORM); - break; - } - - while((tok=strtok(NULL," "))!=NULL) { - switch(tok[0]) { - case '~': - set_access(tok+1,ACCESS_OWNER); - break; - case '&': - set_access(tok+1,ACCESS_SOP); - break; - case '@': - set_access(tok+1,ACCESS_OP); - break; - case '%': - set_access(tok+1,ACCESS_HOP); - break; - case '+': - set_access(tok+1,ACCESS_VOICE); - break; - default: - set_access(tok,ACCESS_NORM); - break; - } - } - - return 1; -} - -//Store user's access level [Zido] -int set_access(char *nick,int newlevel) { - int i=0; - - for(i=0;i<=MAX_CHANNEL_USERS;i++) { - if(strcmpi(cd.user[i].name,nick)==0) { - cd.user[i].level=newlevel; - return 1; - } - } - - strcpy(cd.user[last_cd_user].name,nick); - cd.user[last_cd_user].level=newlevel; - last_cd_user++; - - return 0; -} - -//Returns users access level [Zido] -int get_access(char *nick) { - int i=0; - - for(i=0;i<=MAX_CHANNEL_USERS;i++) { - if(strcmpi(cd.user[i].name,nick)==0) { - return (cd.user[i].level); - } - } - - return -1; -} - -int irc_rmnames() { - int i=0; - - for(i=0;i<=MAX_CHANNEL_USERS;i++) { - //malloc_tsetdword(cd.user[i].name,'\0',256); - cd.user[i].level=0; - } - - last_cd_user=0; - - - return 0; -} - -int irc_read_conf(char *file) { - FILE *fp=NULL; - char w1[256]; - char w2[256]; - char path[256]; - char row[1024]; - - malloc_tsetdword(w1,'\0',256); - malloc_tsetdword(w2,'\0',256); - malloc_tsetdword(path,'\0',256); - malloc_tsetdword(row,'\0',256); - - sprintf(path,"conf/%s",file); - - if(!(fp=fopen(path,"r"))) { - ShowError("Cannot find file: %s\n",path); - return 0; - } - - while(fgets(row,1023,fp)!=NULL) { - if(row[0]=='/'&&row[1]=='/') - continue; - sscanf(row,"%[^:]: %255[^\r\n]",w1,w2); - if(strcmpi(w1,"use_irc")==0) { - if(strcmpi(w2,"on")==0) - use_irc=1; - else - use_irc=0; - } - else if(strcmpi(w1,"irc_server")==0) - strcpy(irc_ip_str,w2); - else if(strcmpi(w1,"irc_port")==0) - irc_port=atoi(w2); - else if(strcmpi(w1,"irc_autojoin")==0) - irc_autojoin=atoi(w2); - else if(strcmpi(w1,"irc_channel")==0) - strcpy(irc_channel,w2); - else if(strcmpi(w1,"irc_channel_pass")==0) - strcpy(irc_channel_pass,w2); - else if(strcmpi(w1,"irc_trade_channel")==0) - strcpy(irc_trade_channel,w2); - else if(strcmpi(w1,"irc_nick")==0) - strcpy(irc_nick,w2); - else if(strcmpi(w1,"irc_pass")==0) { - if(strcmpi(w2,"0")!=0) - strcpy(irc_password,w2); - } - } - - ShowInfo("IRC Config read successfully\n"); - - return 1; -} +#include +#include +#include + +#include "../common/core.h" +#include "../common/socket.h" +#include "../common/malloc.h" +#include "../common/db.h" +#include "../common/timer.h" +#include "../common/strlib.h" +#include "../common/mmo.h" +#include "../common/showmsg.h" +#include "../common/version.h" +#include "../common/nullpo.h" + +#include "map.h" +#include "pc.h" +#include "irc.h" +#include "intif.h" //For GM Broadcast [Zido] + +short use_irc=0; + +short irc_autojoin=0; + +short irc_announce_flag=1; +short irc_announce_mvp_flag=1; +short irc_announce_jobchange_flag=1; +short irc_announce_shop_flag=1; + +IRC_SI *irc_si=NULL; + +char irc_nick[30]=""; +char irc_password[32]=""; + +char irc_channel[32]=""; +char irc_channel_pass[32]=""; +char irc_trade_channel[32]=""; + +unsigned char irc_ip_str[128]=""; +unsigned long irc_ip=0; +unsigned short irc_port = 6667; +int irc_fd=0; + +struct channel_data cd; +int last_cd_user=0; + +int irc_connect_timer(int tid, unsigned int tick, int id, int data) +{ + if(irc_si && session[irc_si->fd]) + return 0; + //Ok, this ShowInfo and printf are a little ugly, but they are meant to + //debug just how long the code freezes here. [Skotlex] + ShowInfo("(IRC) Connecting to %s... ", irc_ip_str); + irc_fd = make_connection(irc_ip,irc_port); + if(irc_fd > 0){ + printf("ok\n"); + session[irc_fd]->func_parse = irc_parse; + } else + printf("failed\n"); + return 0; +} + +void irc_announce(char *buf) +{ + char send_string[256]; + // malloc_tsetdword(send_string,'\0',256); // NOT REQUIRED + + sprintf(send_string,"PRIVMSG %s :",irc_channel); + strcat(send_string, buf); + irc_send(send_string); +} + +void irc_announce_jobchange(struct map_session_data *sd) +{ + char send_string[256]; + + nullpo_retv(sd); + malloc_tsetdword(send_string,'\0',256); + + sprintf(send_string,"PRIVMSG %s :%s has changed into a %s.",irc_channel,sd->status.name,job_name(sd->status.class_)); + irc_send(send_string); +} + +void irc_announce_shop(struct map_session_data *sd, int flag) +{ + char send_string[256]; + char mapname[16]; + int maplen = 0; + nullpo_retv(sd); + + malloc_tsetdword(send_string,'\0',256); + malloc_tsetdword(mapname,'\0',16); + + if(flag){ + strcpy(mapname, map[sd->bl.m].name); + maplen = strcspn(mapname,"."); + mapname[maplen] = '\0'; + mapname[0]=toupper(mapname[0]); + + sprintf(send_string,"PRIVMSG %s :%s has opened a shop, %s, at <%d,%d> in %s.",irc_trade_channel,sd->status.name,sd->message,sd->bl.x,sd->bl.y,mapname); + } else + sprintf(send_string,"PRIVMSG %s :%s has closed their shop.",irc_trade_channel,sd->status.name); + + irc_send(send_string); +} + +void irc_announce_mvp(struct map_session_data *sd, struct mob_data *md) +{ + char send_string[256]; + char mapname[16]; + int maplen = 0; + + nullpo_retv(sd); + nullpo_retv(md); + + malloc_tsetdword(send_string,'\0',256); + malloc_tsetdword(mapname,'\0',16); + mapname[15]='\0'; // 15 is the final index, not 16 [Lance] + strcpy(mapname, map[md->bl.m].name); + maplen = strcspn(mapname,"."); + mapname[maplen] = '\0'; + mapname[0]=toupper(mapname[0]); + + sprintf(send_string,"PRIVMSG %s :%s the %s has MVP'd %s in %s.",irc_channel,sd->status.name,job_name(sd->status.class_),md->name, mapname); + irc_send(send_string); +} + +int irc_parse(int fd) +{ + if (session[fd]->eof){ + do_close(fd); + irc_si = NULL; + add_timer(gettick() + 15000, irc_connect_timer, 0, 0); + return 0; + } + if (session[fd]->session_data == NULL){ + irc_si = (struct IRC_Session_Info*)aMalloc(sizeof(struct IRC_Session_Info)); + irc_si->fd = fd; + irc_si->state = 0; + session[fd]->session_data = irc_si; + } else if (!irc_si) { + irc_si = (struct IRC_Session_Info*)session[fd]->session_data; + irc_si->fd = fd; + } + if(RFIFOREST(fd) > 0){ + char *incoming_string=aMalloc(RFIFOREST(fd)*sizeof(char)); + RFIFOHEAD(fd); + memcpy(incoming_string,RFIFOP(fd,0),RFIFOREST(fd)); + send_to_parser(fd,incoming_string,"\n"); + RFIFOSKIP(fd,RFIFOREST(fd)); + aFree(incoming_string); + } + return 0; +} + +int irc_keepalive_timer(int tid, unsigned int tick, int id, int data) +{ + char send_string[128]; + malloc_tsetdword(send_string,'\0',128); + + sprintf(send_string,"PRIVMSG %s : ", irc_nick); + irc_send(send_string); + add_timer(gettick() + 30000, irc_keepalive_timer, 0, 0); + return 0; +} + +void irc_send_sub(int fd, char transmit[4096]) +{ + sprintf(transmit,"%s%c",transmit,10); + WFIFOHEAD(fd,strlen(transmit)); + memcpy(WFIFOP(fd,0),transmit, strlen(transmit)); + WFIFOSET(fd,strlen(transmit)); +} + +void irc_send(char *buf) +{ + char transmit[4096]; + + if(!irc_si || !session[irc_si->fd]) + return; + + malloc_tsetdword(transmit,'\0',4096); + + sprintf(transmit,buf); + irc_send_sub(irc_si->fd,transmit); +} + +void irc_parse_sub(int fd, char *incoming_string) +{ + char source[256]; + char command[256]; + char target[256]; + char message[8192]; + char send_string[8192]; + char *source_nick=NULL; + char *source_ident=NULL; + char *source_host=NULL; + char *state_mgr=NULL; + + char cmd1[256]; + char cmd2[256]; + char cmdname[256]; + char cmdargs[256]; + + int users=0; + int i=0; + + struct map_session_data **allsd; + + malloc_tsetdword(source,'\0',256); + malloc_tsetdword(command,'\0',256); + malloc_tsetdword(target,'\0',256); + malloc_tsetdword(message,'\0',8192); + malloc_tsetdword(send_string,'\0',8192); + + malloc_tsetdword(cmd1,'\0',256); + malloc_tsetdword(cmd2,'\0',256); + malloc_tsetdword(cmdname,'\0',256); + malloc_tsetdword(cmdargs,'\0',256); + + sscanf(incoming_string, ":%255s %255s %255s :%4095[^\r\n]", source, command, target, message); + if (source != NULL) { + if (strstr(source,"!") != NULL) { + source_nick = strtok_r(source,"!",&state_mgr); + source_ident = strtok_r(NULL,"@",&state_mgr); + source_host = strtok_r(NULL,"%%",&state_mgr); + } + } + if (irc_si->state == 0){ + sprintf(send_string, "NICK %s", irc_nick); + irc_send(send_string); + sprintf(send_string, "USER eABot 8 * : eABot"); + irc_send(send_string); + irc_si->state = 1; + } + else if (irc_si->state == 1){ + if(!strcmp(command,"001")){ + ShowStatus("IRC: Connected to IRC.\n"); + sprintf(send_string, "PRIVMSG nickserv :identify %s", irc_password); + irc_send(send_string); + sprintf(send_string, "JOIN %s %s", irc_channel, irc_channel_pass); + irc_send(send_string); + sprintf(send_string,"NAMES %s",irc_channel); + irc_send(send_string); + irc_si->state = 2; + } + else if(!strcmp(command,"433")){ + ShowError("IRC: Nickname %s is already taken, IRC Client unable to connect.\n", irc_nick); + sprintf(send_string, "QUIT"); + irc_send(send_string); + if(session[fd]) + session[fd]->eof=1; + } + } + else if (irc_si->state == 2){ + if(!strcmp(source, "PING")){ + sprintf(send_string, "PONG %s", command); + irc_send(send_string); + } + + else if((strcmpi(target,irc_channel)==0)||(strcmpi(target,irc_channel+1)==0)) { + + // Broadcast [Zido] (Work in Progress) + if((strcmpi(command,"privmsg")==0)&&(sscanf(message,"@%255s %255[^\r\n]",cmdname,cmdargs)>0)&&(target[0]=='#')) { + if(strcmpi(cmdname,"kami")==0) { + if(get_access(source_nick)0) { + sprintf(send_string,"NOTICE %s :%d Users Online",source_nick,users); + irc_send(send_string); + for(i=0;istatus.name); + irc_send(send_string); + } + } else { + sprintf(send_string,"NOTICE %s :No Users Online",source_nick); + irc_send(send_string); + } + } + } + + // Refresh Names [Zido] + else if((strcmpi(command,"join")==0)||(strcmpi(command,"part")==0)||(strcmpi(command,"mode")==0)||(strcmpi(command,"nick")==0)) { + ShowInfo("IRC: Refreshing User List"); + irc_rmnames(); + printf("..."); + sprintf(send_string,"NAMES %s",irc_channel); + printf("..."); + irc_send(send_string); + printf("Done\n"); + } + + // Autojoin on kick [Zido] + else if((strcmpi(command,"kick")==0)&&(irc_autojoin==1)) { + sprintf(send_string, "JOIN %s %s", target, irc_channel_pass); + irc_send(send_string); + } + } + + // Names Reply [Zido] + else if((strcmpi(command,"353")==0)) { + ShowInfo("IRC: NAMES recieved\n"); + parse_names_packet(incoming_string); + } + } + + return; +} + +int send_to_parser(int fd, char *input,char key[2]) +{ + char *temp_string=NULL; + char *state_mgr=NULL; + int total_loops=0; + + temp_string = strtok_r(input,key,&state_mgr); + while (temp_string != NULL){ + total_loops = total_loops+1; + irc_parse_sub(fd,temp_string); + temp_string = strtok_r(NULL,key,&state_mgr); + } + return total_loops; +} + +void do_final_irc(void) +{ + +} + +void do_init_irc(void) +{ + if(!use_irc) + return; + if (irc_ip_str[strlen(irc_ip_str)-1] == '\n') + irc_ip_str[strlen(irc_ip_str)-1] = '\0'; + irc_ip = resolve_hostbyname(irc_ip_str, NULL, irc_ip_str); + if (!irc_ip) + { + ShowError("Unable to resolve %s! Cannot connect to IRC server, disabling irc_bot.\n", irc_ip_str); + use_irc = 0; + return; + } + + irc_connect_timer(0, 0, 0, 0); + + add_timer_func_list(irc_connect_timer, "irc_connect_timer"); + add_timer_func_list(irc_keepalive_timer, "irc_keepalive_timer"); + add_timer(gettick() + 30000, irc_keepalive_timer, 0, 0); +} + +//NAMES Packet(353) parser [Zido] +int parse_names_packet(char *str) { + char *tok; + char source[256]; + char numeric[10]; + char target[256]; + char channel[256]; + char names[1024]; + + malloc_tsetdword(source,'\0',256); + malloc_tsetword(numeric,'\0',10); + malloc_tsetdword(target,'\0',256); + malloc_tsetdword(channel,'\0',256); + malloc_tsetdword(names,'\0',1024); + + tok=strtok(str,"\r\n"); + sscanf(tok,":%255s %10s %255s %*1[=@] %255s :%1023[^\r\n]",source,numeric,target,channel,names); + if(strcmpi(numeric,"353")==0) + parse_names(names); + + while((tok=strtok(NULL,"\r\n"))!=NULL) { + sscanf(tok,":%255s %10s %255s %*1[=@] %255s :%1023[^\r\n]",source,numeric,target,channel,names); + if(strcmpi(numeric,"353")==0) + parse_names(names); + } + + return 0; +} + +//User access level prefix parser [Zido] +int parse_names(char *str) { + char *tok; + if (str == NULL) return 0; //Nothing to parse! + tok=strtok(str," "); + switch(tok[0]) { + case '~': + set_access(tok+1,ACCESS_OWNER); + break; + case '&': + set_access(tok+1,ACCESS_SOP); + break; + case '@': + set_access(tok+1,ACCESS_OP); + break; + case '%': + set_access(tok+1,ACCESS_HOP); + break; + case '+': + set_access(tok+1,ACCESS_VOICE); + break; + default: + set_access(tok,ACCESS_NORM); + break; + } + + while((tok=strtok(NULL," "))!=NULL) { + switch(tok[0]) { + case '~': + set_access(tok+1,ACCESS_OWNER); + break; + case '&': + set_access(tok+1,ACCESS_SOP); + break; + case '@': + set_access(tok+1,ACCESS_OP); + break; + case '%': + set_access(tok+1,ACCESS_HOP); + break; + case '+': + set_access(tok+1,ACCESS_VOICE); + break; + default: + set_access(tok,ACCESS_NORM); + break; + } + } + + return 1; +} + +//Store user's access level [Zido] +int set_access(char *nick,int newlevel) { + int i=0; + + for(i=0;i<=MAX_CHANNEL_USERS;i++) { + if(strcmpi(cd.user[i].name,nick)==0) { + cd.user[i].level=newlevel; + return 1; + } + } + + strcpy(cd.user[last_cd_user].name,nick); + cd.user[last_cd_user].level=newlevel; + last_cd_user++; + + return 0; +} + +//Returns users access level [Zido] +int get_access(char *nick) { + int i=0; + + for(i=0;i<=MAX_CHANNEL_USERS;i++) { + if(strcmpi(cd.user[i].name,nick)==0) { + return (cd.user[i].level); + } + } + + return -1; +} + +int irc_rmnames() { + int i=0; + + for(i=0;i<=MAX_CHANNEL_USERS;i++) { + //malloc_tsetdword(cd.user[i].name,'\0',256); + cd.user[i].level=0; + } + + last_cd_user=0; + + + return 0; +} + +int irc_read_conf(char *file) { + FILE *fp=NULL; + char w1[256]; + char w2[256]; + char path[256]; + char row[1024]; + + malloc_tsetdword(w1,'\0',256); + malloc_tsetdword(w2,'\0',256); + malloc_tsetdword(path,'\0',256); + malloc_tsetdword(row,'\0',256); + + sprintf(path,"conf/%s",file); + + if(!(fp=fopen(path,"r"))) { + ShowError("Cannot find file: %s\n",path); + return 0; + } + + while(fgets(row,1023,fp)!=NULL) { + if(row[0]=='/'&&row[1]=='/') + continue; + sscanf(row,"%[^:]: %255[^\r\n]",w1,w2); + if(strcmpi(w1,"use_irc")==0) { + if(strcmpi(w2,"on")==0) + use_irc=1; + else + use_irc=0; + } + else if(strcmpi(w1,"irc_server")==0) + strcpy(irc_ip_str,w2); + else if(strcmpi(w1,"irc_port")==0) + irc_port=atoi(w2); + else if(strcmpi(w1,"irc_autojoin")==0) + irc_autojoin=atoi(w2); + else if(strcmpi(w1,"irc_channel")==0) + strcpy(irc_channel,w2); + else if(strcmpi(w1,"irc_channel_pass")==0) + strcpy(irc_channel_pass,w2); + else if(strcmpi(w1,"irc_trade_channel")==0) + strcpy(irc_trade_channel,w2); + else if(strcmpi(w1,"irc_nick")==0) + strcpy(irc_nick,w2); + else if(strcmpi(w1,"irc_pass")==0) { + if(strcmpi(w2,"0")!=0) + strcpy(irc_password,w2); + } + } + + ShowInfo("IRC Config read successfully\n"); + + return 1; +} diff --git a/src/map/irc.h b/src/map/irc.h index 5c321605b..2297f013b 100644 --- a/src/map/irc.h +++ b/src/map/irc.h @@ -1,55 +1,55 @@ -#include "map.h" - -// IRC .conf file [Zido] -#define IRC_CONF "irc_athena.conf" - -// IRC Access levels [Zido] -#define ACCESS_OWNER 5 -#define ACCESS_SOP 4 -#define ACCESS_OP 3 -#define ACCESS_HOP 2 -#define ACCESS_VOICE 1 -#define ACCESS_NORM 0 - -#define MAX_CHANNEL_USERS 500 - -extern short use_irc; - -extern short irc_announce_flag; -extern short irc_announce_mvp_flag; -extern short irc_announce_shop_flag; -extern short irc_announce_jobchange_flag; - -void irc_announce(char *buf); -void irc_announce_jobchange(struct map_session_data *sd); -void irc_announce_shop(struct map_session_data *sd,int flag); -void irc_announce_mvp(struct map_session_data *sd, struct mob_data *md); - -int irc_parse(int fd); -void do_final_irc(void); -void do_init_irc(void); -void irc_send(char *buf); -void irc_parse_sub(int fd, char *incoming_string); -int send_to_parser(int fd, char *input,char key[2]); -struct IRC_Session_Info { - int state; - int fd; - char username[30]; - char password[33]; -}; - -typedef struct IRC_Session_Info IRC_SI; - -struct channel_data { - struct { - char name[256]; - int level; - }user[MAX_CHANNEL_USERS]; -}; - -int parse_names_packet(char *str); // [Zido] -int parse_names(char *str); // [Zido] -int set_access(char *nick,int level); // [Zido] -int get_access(char *nick); // [Zido] -int irc_rmnames(void); // [Zido] -int irc_read_conf(char *file); // [Zido] +#include "map.h" + +// IRC .conf file [Zido] +#define IRC_CONF "irc_athena.conf" + +// IRC Access levels [Zido] +#define ACCESS_OWNER 5 +#define ACCESS_SOP 4 +#define ACCESS_OP 3 +#define ACCESS_HOP 2 +#define ACCESS_VOICE 1 +#define ACCESS_NORM 0 + +#define MAX_CHANNEL_USERS 500 + +extern short use_irc; + +extern short irc_announce_flag; +extern short irc_announce_mvp_flag; +extern short irc_announce_shop_flag; +extern short irc_announce_jobchange_flag; + +void irc_announce(char *buf); +void irc_announce_jobchange(struct map_session_data *sd); +void irc_announce_shop(struct map_session_data *sd,int flag); +void irc_announce_mvp(struct map_session_data *sd, struct mob_data *md); + +int irc_parse(int fd); +void do_final_irc(void); +void do_init_irc(void); +void irc_send(char *buf); +void irc_parse_sub(int fd, char *incoming_string); +int send_to_parser(int fd, char *input,char key[2]); +struct IRC_Session_Info { + int state; + int fd; + char username[30]; + char password[33]; +}; + +typedef struct IRC_Session_Info IRC_SI; + +struct channel_data { + struct { + char name[256]; + int level; + }user[MAX_CHANNEL_USERS]; +}; + +int parse_names_packet(char *str); // [Zido] +int parse_names(char *str); // [Zido] +int set_access(char *nick,int level); // [Zido] +int get_access(char *nick); // [Zido] +int irc_rmnames(void); // [Zido] +int irc_read_conf(char *file); // [Zido] diff --git a/src/map/itemdb.c b/src/map/itemdb.c index 62d40fe51..b6626fa41 100644 --- a/src/map/itemdb.c +++ b/src/map/itemdb.c @@ -1,1277 +1,1277 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include - -#include "../common/nullpo.h" -#include "../common/malloc.h" -#include "../common/showmsg.h" -#include "../common/grfio.h" -#include "../common/strlib.h" -#include "map.h" -#include "battle.h" -#include "itemdb.h" -#include "script.h" -#include "pc.h" - -// ** ITEMDB_OVERRIDE_NAME_VERBOSE ** -// 定義すると、itemdb.txtとgrfで名前が異なる場合、表示します. -//#define ITEMDB_OVERRIDE_NAME_VERBOSE 1 - -static struct dbt* item_db; - -static struct item_group itemgroup_db[MAX_ITEMGROUP]; - -struct item_data dummy_item; //This is the default dummy item used for non-existant items. [Skotlex] - -/*========================================== - * 名前で検索用 - *------------------------------------------ - */ -// name = item alias, so we should find items aliases first. if not found then look for "jname" (full name) -int itemdb_searchname_sub(DBKey key,void *data,va_list ap) -{ - struct item_data *item=(struct item_data *)data,**dst; - char *str; - str=va_arg(ap,char *); - dst=va_arg(ap,struct item_data **); - if(item == &dummy_item) return 0; - if( strcmpi(item->name,str)==0 ) //by lupus - *dst=item; - return 0; -} - -/*========================================== - * 名前で検索用 - *------------------------------------------ - */ -int itemdb_searchjname_sub(int key,void *data,va_list ap) -{ - struct item_data *item=(struct item_data *)data,**dst; - char *str; - str=va_arg(ap,char *); - dst=va_arg(ap,struct item_data **); - if(item == &dummy_item) return 0; - if( strcmpi(item->jname,str)==0 ) - *dst=item; - return 0; -} - -/*========================================== - * 名前で検索 - *------------------------------------------ - */ -struct item_data* itemdb_searchname(const char *str) -{ - struct item_data *item=NULL; - item_db->foreach(item_db,itemdb_searchname_sub,str,&item); - return item; -} - -static int itemdb_searchname_array_sub(DBKey key,void * data,va_list ap) -{ - struct item_data *item=(struct item_data *)data; - char *str; - str=va_arg(ap,char *); - if (item == &dummy_item) - return 1; //Invalid item. - if(stristr(item->jname,str)) - return 0; - if(stristr(item->name,str)) - return 0; - return strcmpi(item->jname,str); -} - -/*========================================== - * Founds up to N matches. Returns number of matches [Skotlex] - *------------------------------------------ - */ -int itemdb_searchname_array(struct item_data** data, int size, const char *str) -{ - return item_db->getall(item_db,(void**)data,size,itemdb_searchname_array_sub,str); -} - - -/*========================================== - * 箱系アイテム検索 - *------------------------------------------ - */ -int itemdb_searchrandomid(int group) -{ - if(group<1 || group>=MAX_ITEMGROUP) { - if (battle_config.error_log) - ShowError("itemdb_searchrandomid: Invalid group id %d\n", group); - return UNKNOWN_ITEM_ID; - } - if (itemgroup_db[group].qty) - return itemgroup_db[group].nameid[rand()%itemgroup_db[group].qty]; - - if (battle_config.error_log) - ShowError("itemdb_searchrandomid: No item entries for group id %d\n", group); - return UNKNOWN_ITEM_ID; -} - -/*========================================== - * Calculates total item-group related bonuses for the given item. [Skotlex] - *------------------------------------------ - */ -int itemdb_group_bonus(struct map_session_data *sd, int itemid) -{ - int bonus = 0, i, j; - for (i=0; i < MAX_ITEMGROUP; i++) { - if (!sd->itemgrouphealrate[i]) - continue; - for (j=0; j < itemgroup_db[i].qty; j++) { - if (itemgroup_db[i].nameid[j] == itemid) - { - bonus += sd->itemgrouphealrate[i]; - continue; - } - } - } - return bonus; -} - -/*========================================== - * DBの存在確認 - *------------------------------------------ - */ -struct item_data* itemdb_exists(int nameid) -{ - struct item_data* id; - if (!nameid) return NULL; - id = idb_get(item_db,nameid); - //Adjust nameid in case it's used outside. [Skotlex] - if (id == &dummy_item) - dummy_item.nameid = nameid; - return id; -} - -/*========================================== - * Converts the jobid from the format in itemdb - * to the format used by the map server. [Skotlex] - *------------------------------------------ - */ -static void itemdb_jobid2mapid(unsigned int *bclass, unsigned int jobmask) -{ - int i; - bclass[0]= bclass[1]= bclass[2]= 0; - //Base classes - if (jobmask & 1<nameid = key.i; - id->weight=1; - id->type=IT_ETC; - return id; -} - -/*========================================== - * Loads (and creates if not found) an item from the db. - *------------------------------------------ - */ -struct item_data* itemdb_load(int nameid) -{ - struct item_data *id = idb_ensure(item_db,nameid,create_item_data); - if (id == &dummy_item) - { //Remove dummy_item, replace by real data. - DBKey key; - key.i = nameid; - idb_remove(item_db,nameid); - id = create_item_data(key, NULL); - idb_put(item_db,nameid,id); - } - return id; -} - -static void* return_dummy_data(DBKey key, va_list args) { - if (battle_config.error_log) - ShowWarning("itemdb_search: Item ID %d does not exists in the item_db. Using dummy data.\n", key.i); - dummy_item.nameid = key.i; - return &dummy_item; -} - -/*========================================== - * Loads an item from the db. If not found, it will return the dummy item. - *------------------------------------------ - */ -struct item_data* itemdb_search(int nameid) -{ - return idb_ensure(item_db,nameid,return_dummy_data); -} - -/*========================================== - * Returns if given item is a player-equippable piece. - *------------------------------------------ - */ -int itemdb_isequip(int nameid) -{ - int type=itemdb_type(nameid); - switch (type) { - case IT_WEAPON: - case IT_ARMOR: - case IT_AMMO: - return 1; - default: - return 0; - } -} - -/*========================================== - * Alternate version of itemdb_isequip - *------------------------------------------ - */ -int itemdb_isequip2(struct item_data *data) -{ - nullpo_retr(0, data); - switch(data->type) { - case IT_WEAPON: - case IT_ARMOR: - case IT_AMMO: - return 1; - default: - return 0; - } -} - -/*========================================== -* Returns if given item's type is stackable. -*------------------------------------------ -*/ -int itemdb_isstackable(int nameid) -{ - int type=itemdb_type(nameid); - switch(type) { - case IT_WEAPON: - case IT_ARMOR: - case IT_PETEGG: - case IT_PETARMOR: - return 0; - default: - return 1; - } -} - -/*========================================== -* Alternate version of itemdb_isstackable -*------------------------------------------ -*/ -int itemdb_isstackable2(struct item_data *data) -{ - nullpo_retr(0, data); - switch(data->type) { - case IT_WEAPON: - case IT_ARMOR: - case IT_PETEGG: - case IT_PETARMOR: - return 0; - default: - return 1; - } -} - - -/*========================================== - * Trade Restriction functions [Skotlex] - *------------------------------------------ - */ -int itemdb_isdropable_sub(struct item_data *item, int gmlv, int unused) -{ - return (item && (!(item->flag.trade_restriction&1) || gmlv >= item->gm_lv_trade_override)); -} - -int itemdb_cantrade_sub(struct item_data* item, int gmlv, int gmlv2) -{ - return (item && (!(item->flag.trade_restriction&2) || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override)); -} - -int itemdb_canpartnertrade_sub(struct item_data* item, int gmlv, int gmlv2) -{ - return (item && (item->flag.trade_restriction&4 || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override)); -} - -int itemdb_cansell_sub(struct item_data* item, int gmlv, int unused) -{ - return (item && (!(item->flag.trade_restriction&8) || gmlv >= item->gm_lv_trade_override)); -} - -int itemdb_cancartstore_sub(struct item_data* item, int gmlv, int unused) -{ - return (item && (!(item->flag.trade_restriction&16) || gmlv >= item->gm_lv_trade_override)); -} - -int itemdb_canstore_sub(struct item_data* item, int gmlv, int unused) -{ - return (item && (!(item->flag.trade_restriction&32) || gmlv >= item->gm_lv_trade_override)); -} - -int itemdb_canguildstore_sub(struct item_data* item, int gmlv, int unused) -{ - return (item && (!(item->flag.trade_restriction&64) || gmlv >= item->gm_lv_trade_override)); -} - -int itemdb_isrestricted(struct item* item, int gmlv, int gmlv2, int (*func)(struct item_data*, int, int)) -{ - struct item_data* item_data = itemdb_search(item->nameid); - int i; - - if (!func(item_data, gmlv, gmlv2)) - return 0; - - if(item_data->slot == 0 || itemdb_isspecial(item->card[0])) - return 1; - - for(i = 0; i < item_data->slot; i++) { - if (!item->card[i]) continue; - if (!func(itemdb_search(item->card[i]), gmlv, gmlv2)) - return 0; - } - return 1; -} - -/*========================================== - * Specifies if item-type should drop unidentified. - *------------------------------------------ - */ -int itemdb_isidentified(int nameid) -{ - int type=itemdb_type(nameid); - switch (type) { - case IT_WEAPON: - case IT_ARMOR: - case IT_PETARMOR: - return 0; - default: - return 1; - } -} - -/*========================================== - * アイテム使用可能フラグのオーバーライド - *------------------------------------------ - */ -static int itemdb_read_itemavail (void) -{ - FILE *fp; - int nameid, j, k, ln = 0; - char line[1024], *str[10], *p; - struct item_data *id; - - sprintf(line, "%s/item_avail.txt", db_path); - if ((fp = fopen(line,"r")) == NULL) { - ShowError("can't read %s\n", line); - return -1; - } - - while (fgets(line, sizeof(line) - 1, fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - malloc_tsetdword(str, 0, sizeof(str)); - for (j = 0, p = line; j < 2 && p; j++) { - str[j] = p; - p = strchr(p, ','); - if(p) *p++ = 0; - } - - if (j < 2 || str[0] == NULL || - (nameid = atoi(str[0])) < 0 || !(id = itemdb_exists(nameid))) - continue; - - k = atoi(str[1]); - if (k > 0) { - id->flag.available = 1; - id->view_id = k; - } else - id->flag.available = 0; - ln++; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_avail.txt"); - - return 0; -} - -/*========================================== - * read item group data - *------------------------------------------ - */ -static void itemdb_read_itemgroup_sub(const char* filename) -{ - FILE *fp; - char line[1024]; - int ln=0; - int groupid,j,k,nameid; - char *str[3],*p; - char w1[1024], w2[1024]; - - if( (fp=fopen(filename,"r"))==NULL ){ - ShowError("can't read %s\n", line); - return; - } - - while(fgets(line,1020,fp)){ - ln++; - if(line[0]=='/' && line[1]=='/') - continue; - if(strstr(line,"import")) { - if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2 && - strcmpi(w1, "import") == 0) { - itemdb_read_itemgroup_sub(w2); - continue; - } - } - malloc_tsetdword(str,0,sizeof(str)); - for(j=0,p=line;j<3 && p;j++){ - str[j]=p; - p=strchr(p,','); - if(p) *p++=0; - } - if(str[0]==NULL) - continue; - if (j<3) { - if (j>1) //Or else it barks on blank lines... - ShowWarning("itemdb_read_itemgroup: Insufficient fields for entry at %s:%d\n", filename, ln); - continue; - } - groupid = atoi(str[0]); - if (groupid < 0 || groupid >= MAX_ITEMGROUP) { - ShowWarning("itemdb_read_itemgroup: Invalid group %d in %s:%d\n", groupid, filename, ln); - continue; - } - nameid = atoi(str[1]); - if (!itemdb_exists(nameid)) { - ShowWarning("itemdb_read_itemgroup: Non-existant item %d in %s:%d\n", nameid, filename, ln); - continue; - } - k = atoi(str[2]); - if (itemgroup_db[groupid].qty+k > MAX_RANDITEM) { - ShowWarning("itemdb_read_itemgroup: Group %d is full (%d entries) in %s:%d\n", groupid, MAX_RANDITEM, filename, ln); - continue; - } - for(j=0;jjname,buf2,ITEM_NAME_LENGTH)!=0 ){ - ShowNotice("[override] %d %s => %s\n",nameid - ,itemdb_search(nameid)->jname,buf2); - } -#endif - - strncpy(itemdb_search(nameid)->jname,buf2,ITEM_NAME_LENGTH-1); - } - - p=strchr(p,10); - if(!p) break; - p++; - } - aFree(buf); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\idnum2itemdisplaynametable.txt"); - - return 0; -} - -/*========================================== - * カードイラストのリソース名前テーブルを読み込む - *------------------------------------------ - */ -static int itemdb_read_cardillustnametable(void) -{ - char *buf,*p; - int s; - - buf=(char *) grfio_reads("data\\num2cardillustnametable.txt",&s); - - if(buf==NULL) - return -1; - - buf[s]=0; - for(p=buf;p-bufcardillustname,buf2,64); - } - - p=strchr(p,10); - if(!p) break; - p++; - } - aFree(buf); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\num2cardillustnametable.txt"); - - return 0; -} - -// -// 初期化 -// -/*========================================== - * - *------------------------------------------ - */ -static int itemdb_read_itemslottable(void) -{ - char *buf, *p; - int s; - - buf = (char *)grfio_reads("data\\itemslottable.txt", &s); - if (buf == NULL) - return -1; - buf[s] = 0; - for (p = buf; p - buf < s; ) { - int nameid, equip; - struct item_data* item; - sscanf(p, "%d#%d#", &nameid, &equip); - item = itemdb_search(nameid); - if (equip && item && itemdb_isequip2(item)) - item->equip = equip; - p = strchr(p, 10); - if(!p) break; - p++; - p=strchr(p, 10); - if(!p) break; - p++; - } - aFree(buf); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\itemslottable.txt"); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -static int itemdb_read_itemslotcounttable(void) -{ - char *buf, *p; - int s; - - buf = (char *)grfio_reads("data\\itemslotcounttable.txt", &s); - if (buf == NULL) - return -1; - buf[s] = 0; - for (p = buf; p - buf < s;){ - int nameid, slot; - sscanf(p, "%d#%d#", &nameid, &slot); - if (slot > MAX_SLOTS) - { - ShowWarning("itemdb_read_itemslotcounttable: Item %d specifies %d slots, but the server only supports up to %d\n", nameid, slot, MAX_SLOTS); - slot = MAX_SLOTS; - } - itemdb_slot(nameid) = slot; - p = strchr(p,10); - if(!p) break; - p++; - p = strchr(p,10); - if(!p) break; - p++; - } - aFree(buf); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", "data\\itemslotcounttable.txt"); - - return 0; -} - -/*========================================== - * 装備制限ファイル読み出し - *------------------------------------------ - */ -static int itemdb_read_noequip(void) -{ - FILE *fp; - char line[1024]; - int ln=0; - int nameid,j; - char *str[32],*p; - struct item_data *id; - - sprintf(line, "%s/item_noequip.txt", db_path); - if( (fp=fopen(line,"r"))==NULL ){ - ShowError("can't read %s\n", line); - return -1; - } - while(fgets(line,1020,fp)){ - if(line[0]=='/' && line[1]=='/') - continue; - malloc_tsetdword(str,0,sizeof(str)); - for(j=0,p=line;j<2 && p;j++){ - str[j]=p; - p=strchr(p,','); - if(p) *p++=0; - } - if(str[0]==NULL) - continue; - - nameid=atoi(str[0]); - if(nameid<=0 || !(id=itemdb_exists(nameid))) - continue; - - id->flag.no_equip=atoi(str[1]); - - ln++; - - } - fclose(fp); - if (ln > 0) { - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"item_noequip.txt"); - } - return 0; -} - -/*========================================== - * Reads item trade restrictions [Skotlex] - *------------------------------------------ - */ -static int itemdb_read_itemtrade(void) -{ - FILE *fp; - int nameid, j, flag, gmlv, ln = 0; - char line[1024], *str[10], *p; - struct item_data *id; - - sprintf(line, "%s/item_trade.txt", db_path); - if ((fp = fopen(line,"r")) == NULL) { - ShowError("can't read %s\n", line); - return -1; - } - - while (fgets(line, sizeof(line) - 1, fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - malloc_tsetdword(str, 0, sizeof(str)); - for (j = 0, p = line; j < 3 && p; j++) { - str[j] = p; - p = strchr(p, ','); - if(p) *p++ = 0; - } - - if (j < 3 || str[0] == NULL || - (nameid = atoi(str[0])) < 0 || !(id = itemdb_exists(nameid))) - continue; - - flag = atoi(str[1]); - gmlv = atoi(str[2]); - - if (flag > 0 && flag < 128 && gmlv > 0) { //Check range - id->flag.trade_restriction = flag; - id->gm_lv_trade_override = gmlv; - ln++; - } - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_trade.txt"); - - return 0; -} - -/*====================================== - * Applies gender restrictions according to settings. [Skotlex] - *====================================== - */ -static int itemdb_gendercheck(struct item_data *id) -{ - if (id->nameid == WEDDING_RING_M) //Grom Ring - return 1; - if (id->nameid == WEDDING_RING_F) //Bride Ring - return 0; - if (id->look == W_MUSICAL && id->type == IT_WEAPON) //Musical instruments are always male-only - return 1; - if (id->look == W_WHIP && id->type == IT_WEAPON) //Whips are always female-only - return 0; - - return (battle_config.ignore_items_gender?2:id->sex); -} -#ifndef TXT_ONLY - -/*====================================== -* SQL -*=================================== -*/ -static int itemdb_read_sqldb(void) -{ - unsigned short nameid; - struct item_data *id; - char script[65535 + 2 + 1]; // Maximum length of MySQL TEXT type (65535) + 2 bytes for curly brackets + 1 byte for terminator - char *item_db_name[] = { item_db_db, item_db2_db }; - long unsigned int ln = 0; - int i; - - // ---------- - - for (i = 0; i < 2; i++) { - sprintf(tmp_sql, "SELECT * FROM `%s`", item_db_name[i]); - - // Execute the query; if the query execution succeeded... - if (mysql_query(&mmysql_handle, tmp_sql) == 0) { - sql_res = mysql_store_result(&mmysql_handle); - - // If the storage of the query result succeeded... - if (sql_res) { - // Parse each row in the query result into sql_row - while ((sql_row = mysql_fetch_row(sql_res))) - { /*Table structure is: - 00 id - 01 name_english - 02 name_japanese - 03 type - 04 price_buy - 05 price_sell - 06 weight - 07 attack - 08 defence - 09 range - 10 slots - 11 equip_jobs - 12 equip_upper - 13 equip_genders - 14 equip_locations - 15 weapon_level - 16 equip_level - 17 refineable - 18 view - 19 script - 20 equip_script - 21 unequip_script - */ - nameid = atoi(sql_row[0]); - - // If the identifier is not within the valid range, process the next row - if (nameid == 0) - continue; - - ln++; - - // ---------- - id = itemdb_load(nameid); - - strncpy(id->name, sql_row[1], ITEM_NAME_LENGTH-1); - strncpy(id->jname, sql_row[2], ITEM_NAME_LENGTH-1); - - id->type = atoi(sql_row[3]); - if (id->type == IT_DELAYCONSUME) - { //Items that are consumed upon target confirmation - //(yggdrasil leaf, spells & pet lures) [Skotlex] - id->type = IT_USABLE; - id->flag.delay_consume=1; - } - - // If price_buy is not NULL and price_sell is not NULL... - if ((sql_row[4] != NULL) && (sql_row[5] != NULL)) { - id->value_buy = atoi(sql_row[4]); - id->value_sell = atoi(sql_row[5]); - } - // If price_buy is not NULL and price_sell is NULL... - else if ((sql_row[4] != NULL) && (sql_row[5] == NULL)) { - id->value_buy = atoi(sql_row[4]); - id->value_sell = atoi(sql_row[4]) / 2; - } - // If price_buy is NULL and price_sell is not NULL... - else if ((sql_row[4] == NULL) && (sql_row[5] != NULL)) { - id->value_buy = atoi(sql_row[5]) * 2; - id->value_sell = atoi(sql_row[5]); - } - // If price_buy is NULL and price_sell is NULL... - if ((sql_row[4] == NULL) && (sql_row[5] == NULL)) { - id->value_buy = 0; - id->value_sell = 0; - } - - id->weight = atoi(sql_row[6]); - id->atk = (sql_row[7] != NULL) ? atoi(sql_row[7]) : 0; - id->def = (sql_row[8] != NULL) ? atoi(sql_row[8]) : 0; - id->range = (sql_row[9] != NULL) ? atoi(sql_row[9]) : 0; - id->slot = (sql_row[10] != NULL) ? atoi(sql_row[10]) : 0; - if (id->slot > MAX_SLOTS) - { - ShowWarning("itemdb_read_sqldb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS); - id->slot = MAX_SLOTS; - } - itemdb_jobid2mapid(id->class_base, (sql_row[11] != NULL) ? (unsigned int)strtoul(sql_row[11], NULL, 0) : 0); - id->class_upper= (sql_row[12] != NULL) ? atoi(sql_row[12]) : 0; - id->sex = (sql_row[13] != NULL) ? atoi(sql_row[13]) : 0; - id->equip = (sql_row[14] != NULL) ? atoi(sql_row[14]) : 0; - if (!id->equip && itemdb_isequip2(id)) - { - ShowWarning("Item %d (%s) is an equipment with no equip-field! Making it an etc item.\n", nameid, id->jname); - id->type = 3; - } - id->wlv = (sql_row[15] != NULL) ? atoi(sql_row[15]) : 0; - id->elv = (sql_row[16] != NULL) ? atoi(sql_row[16]) : 0; - id->flag.no_refine = (sql_row[17] == NULL || atoi(sql_row[17]) == 1)?0:1; - id->look = (sql_row[18] != NULL) ? atoi(sql_row[18]) : 0; - id->view_id = 0; - id->sex = itemdb_gendercheck(id); //Apply gender filtering. - - // ---------- - - if (id->script) - script_free_code(id->script); - if (sql_row[19] != NULL) { - if (sql_row[19][0] == '{') - id->script = parse_script((unsigned char *) sql_row[19],item_db_name[i], 0); - else { - sprintf(script, "{%s}", sql_row[19]); - id->script = parse_script((unsigned char *) script, item_db_name[i], 0); - } - } else id->script = NULL; - - if (id->equip_script) - script_free_code(id->equip_script); - if (sql_row[20] != NULL) { - if (sql_row[20][0] == '{') - id->equip_script = parse_script((unsigned char *) sql_row[20], item_db_name[i], 0); - else { - sprintf(script, "{%s}", sql_row[20]); - id->equip_script = parse_script((unsigned char *) script, item_db_name[i], 0); - } - } else id->equip_script = NULL; - - if (id->unequip_script) - script_free_code(id->unequip_script); - if (sql_row[21] != NULL) { - if (sql_row[21][0] == '{') - id->unequip_script = parse_script((unsigned char *) sql_row[21],item_db_name[i], 0); - else { - sprintf(script, "{%s}", sql_row[21]); - id->unequip_script = parse_script((unsigned char *) script, item_db_name[i], 0); - } - } else id->unequip_script = NULL; - - // ---------- - - id->flag.available = 1; - id->flag.value_notdc = 0; - id->flag.value_notoc = 0; - } - - ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, item_db_name[i]); - ln = 0; - } else { - ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - // Free the query result - mysql_free_result(sql_res); - } else { - ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - return 0; -} -#endif /* not TXT_ONLY */ - -/*========================================== - * アイテムデータベースの読み込み - *------------------------------------------ - */ -static int itemdb_readdb(void) -{ - FILE *fp; - char line[1024]; - int ln=0,lines=0; - int nameid,j; - char *str[32],*p,*np; - struct item_data *id; - int i=0; - char *filename[]={ "item_db.txt","item_db2.txt" }; - - for(i=0;i<2;i++){ - sprintf(line, "%s/%s", db_path, filename[i]); - fp=fopen(line,"r"); - if(fp==NULL){ - if(i>0) - continue; - ShowFatalError("can't read %s\n",line); - exit(1); - } - - lines=0; - while(fgets(line,1020,fp)){ - lines++; - if(line[0]=='/' && line[1]=='/') - continue; - malloc_tsetdword(str,0,sizeof(str)); - for(j=0,np=p=line;j<19 && p;j++){ - str[j]=p; - p=strchr(p,','); - if(p){ *p++=0; np=p; } - } - if(str[0]==NULL) - continue; - - nameid=atoi(str[0]); - if(nameid<=0) - continue; - if (j < 19) - { //Crash-fix on broken item lines. [Skotlex] - ShowWarning("Reading %s: Insufficient fields for item with id %d, skipping.\n", filename[i], nameid); - continue; - } - ln++; - - //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Job Upper,Gender,Loc,wLV,eLV,refineable,View - id=itemdb_load(nameid); - strncpy(id->name, str[1], ITEM_NAME_LENGTH-1); - strncpy(id->jname, str[2], ITEM_NAME_LENGTH-1); - id->type=atoi(str[3]); - if (id->type == IT_DELAYCONSUME) - { //Items that are consumed upon target confirmation - //(yggdrasil leaf, spells & pet lures) [Skotlex] - id->type = IT_USABLE; - id->flag.delay_consume=1; - } - - { - int buy = atoi(str[4]), sell = atoi(str[5]); - // if buying price > selling price * 2 consider it valid and don't change it [celest] - if (buy && sell && buy > sell*2){ - id->value_buy = buy; - id->value_sell = sell; - } else { - // buy≠sell*2 は item_value_db.txt で指定してください。 - if (sell) { // sell値を優先とする - id->value_buy = sell*2; - id->value_sell = sell; - } else { - id->value_buy = buy; - id->value_sell = buy/2; - } - } - // check for bad prices that can possibly cause exploits - if (id->value_buy*75/100 < id->value_sell*124/100) { - ShowWarning ("Item %s [%d] buying:%d < selling:%d\n", - id->name, id->nameid, id->value_buy*75/100, id->value_sell*124/100); - } - } - id->weight=atoi(str[6]); - id->atk=atoi(str[7]); - id->def=atoi(str[8]); - id->range=atoi(str[9]); - id->slot=atoi(str[10]); - if (id->slot > MAX_SLOTS) - { - ShowWarning("itemdb_readdb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS); - id->slot = MAX_SLOTS; - } - itemdb_jobid2mapid(id->class_base, (unsigned int)strtoul(str[11],NULL,0)); - id->class_upper = atoi(str[12]); - id->sex = atoi(str[13]); - if(id->equip != atoi(str[14])){ - id->equip=atoi(str[14]); - } - if (!id->equip && itemdb_isequip2(id)) - { - ShowWarning("Item %d (%s) is an equipment with no equip-field! Making it an etc item.\n", nameid, id->jname); - id->type = 3; - } - id->wlv=atoi(str[15]); - id->elv=atoi(str[16]); - id->flag.no_refine = atoi(str[17])?0:1; //If the refine column is 1, no_refine is 0 - id->look=atoi(str[18]); - id->flag.available=1; - id->flag.value_notdc=0; - id->flag.value_notoc=0; - id->view_id=0; - id->sex = itemdb_gendercheck(id); //Apply gender filtering. - - if (id->script) { - script_free_code(id->script); - id->script=NULL; - } - if (id->equip_script) { - script_free_code(id->equip_script); - id->equip_script=NULL; - } - if (id->unequip_script) { - script_free_code(id->unequip_script); - id->unequip_script=NULL; - } - - if((p=strchr(np,'{'))==NULL) - continue; - - str[19] = p; //Script - np = strchr(p,'}'); - - while (np && np[1] && np[1] != ',') - np = strchr(np+1,'}'); //Jump close brackets until the next field is found. - if (!np || !np[1]) { - //Couldn't find the end of the script field. - id->script = parse_script((unsigned char *) str[19],filename[i],lines); - continue; - } - np[1] = '\0'; //Set end of script - id->script = parse_script((unsigned char *) str[19],filename[i],lines); - np+=2; //Skip to next field - - if(!np || (p=strchr(np,'{'))==NULL) - continue; - - str[20] = p; //Equip Script - np = strchr(p,'}'); - - while (np && np[1] && np[1] != ',') - np = strchr(np+1,'}'); //Jump close brackets until the next field is found. - if (!np || !np[1]) { - //Couldn't find the end of the script field. - id->equip_script = parse_script((unsigned char *) str[20],filename[i],lines); - continue; - } - - np[1] = '\0'; //Set end of script - id->equip_script = parse_script((unsigned char *) str[20],filename[i],lines); - np+=2; //Skip comma, to next field - - if(!np || (p=strchr(np,'{'))==NULL) - continue; - //Unequip script, last column. - id->unequip_script = parse_script((unsigned char *) p,filename[i],lines); - } - fclose(fp); - if (ln > 0) { - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,filename[i]); - } - ln=0; // reset to 0 - } - return 0; -} - -/*==================================== - * Removed item_value_db, don't re-add - *------------------------------------ - */ -static void itemdb_read(void) -{ -#ifndef TXT_ONLY - if (db_use_sqldbs) - itemdb_read_sqldb(); - else -#endif - itemdb_readdb(); - - itemdb_read_itemgroup(); - itemdb_read_itemavail(); - itemdb_read_noequip(); - itemdb_read_itemtrade(); - if (battle_config.cardillust_read_grffile) - itemdb_read_cardillustnametable(); - if (battle_config.item_equip_override_grffile) - itemdb_read_itemslottable(); - if (battle_config.item_slots_override_grffile) - itemdb_read_itemslotcounttable(); - if (battle_config.item_name_override_grffile) - itemdb_read_itemnametable(); -} - -/*========================================== - * Initialize / Finalize - *------------------------------------------ - */ -static int itemdb_final_sub (DBKey key,void *data,va_list ap) -{ - int flag; - struct item_data *id = (struct item_data *)data; - - flag = va_arg(ap, int); - if (id->script) - { - script_free_code(id->script); - id->script = NULL; - } - if (id->equip_script) - { - script_free_code(id->equip_script); - id->equip_script = NULL; - } - if (id->unequip_script) - { - script_free_code(id->unequip_script); - id->unequip_script = NULL; - } - // Whether to clear the item data (exception: do not clear the dummy item data - if (flag && id != &dummy_item) - aFree(id); - - return 0; -} - -void itemdb_reload(void) -{ - //Just read, the function takes care of freeing scripts. - itemdb_read(); -} - -void do_final_itemdb(void) -{ - item_db->destroy(item_db, itemdb_final_sub, 1); - if (dummy_item.script) { - script_free_code(dummy_item.script); - dummy_item.script = NULL; - } - if (dummy_item.equip_script) { - script_free_code(dummy_item.equip_script); - dummy_item.equip_script = NULL; - } - if (dummy_item.unequip_script) { - script_free_code(dummy_item.unequip_script); - dummy_item.unequip_script = NULL; - } -} - -int do_init_itemdb(void) -{ - item_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); - create_dummy_data(); //Dummy data item. - itemdb_read(); - - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include + +#include "../common/nullpo.h" +#include "../common/malloc.h" +#include "../common/showmsg.h" +#include "../common/grfio.h" +#include "../common/strlib.h" +#include "map.h" +#include "battle.h" +#include "itemdb.h" +#include "script.h" +#include "pc.h" + +// ** ITEMDB_OVERRIDE_NAME_VERBOSE ** +// 定義すると、itemdb.txtとgrfで名前が異なる場合、表示します. +//#define ITEMDB_OVERRIDE_NAME_VERBOSE 1 + +static struct dbt* item_db; + +static struct item_group itemgroup_db[MAX_ITEMGROUP]; + +struct item_data dummy_item; //This is the default dummy item used for non-existant items. [Skotlex] + +/*========================================== + * 名前で検索用 + *------------------------------------------ + */ +// name = item alias, so we should find items aliases first. if not found then look for "jname" (full name) +int itemdb_searchname_sub(DBKey key,void *data,va_list ap) +{ + struct item_data *item=(struct item_data *)data,**dst; + char *str; + str=va_arg(ap,char *); + dst=va_arg(ap,struct item_data **); + if(item == &dummy_item) return 0; + if( strcmpi(item->name,str)==0 ) //by lupus + *dst=item; + return 0; +} + +/*========================================== + * 名前で検索用 + *------------------------------------------ + */ +int itemdb_searchjname_sub(int key,void *data,va_list ap) +{ + struct item_data *item=(struct item_data *)data,**dst; + char *str; + str=va_arg(ap,char *); + dst=va_arg(ap,struct item_data **); + if(item == &dummy_item) return 0; + if( strcmpi(item->jname,str)==0 ) + *dst=item; + return 0; +} + +/*========================================== + * 名前で検索 + *------------------------------------------ + */ +struct item_data* itemdb_searchname(const char *str) +{ + struct item_data *item=NULL; + item_db->foreach(item_db,itemdb_searchname_sub,str,&item); + return item; +} + +static int itemdb_searchname_array_sub(DBKey key,void * data,va_list ap) +{ + struct item_data *item=(struct item_data *)data; + char *str; + str=va_arg(ap,char *); + if (item == &dummy_item) + return 1; //Invalid item. + if(stristr(item->jname,str)) + return 0; + if(stristr(item->name,str)) + return 0; + return strcmpi(item->jname,str); +} + +/*========================================== + * Founds up to N matches. Returns number of matches [Skotlex] + *------------------------------------------ + */ +int itemdb_searchname_array(struct item_data** data, int size, const char *str) +{ + return item_db->getall(item_db,(void**)data,size,itemdb_searchname_array_sub,str); +} + + +/*========================================== + * 箱系アイテム検索 + *------------------------------------------ + */ +int itemdb_searchrandomid(int group) +{ + if(group<1 || group>=MAX_ITEMGROUP) { + if (battle_config.error_log) + ShowError("itemdb_searchrandomid: Invalid group id %d\n", group); + return UNKNOWN_ITEM_ID; + } + if (itemgroup_db[group].qty) + return itemgroup_db[group].nameid[rand()%itemgroup_db[group].qty]; + + if (battle_config.error_log) + ShowError("itemdb_searchrandomid: No item entries for group id %d\n", group); + return UNKNOWN_ITEM_ID; +} + +/*========================================== + * Calculates total item-group related bonuses for the given item. [Skotlex] + *------------------------------------------ + */ +int itemdb_group_bonus(struct map_session_data *sd, int itemid) +{ + int bonus = 0, i, j; + for (i=0; i < MAX_ITEMGROUP; i++) { + if (!sd->itemgrouphealrate[i]) + continue; + for (j=0; j < itemgroup_db[i].qty; j++) { + if (itemgroup_db[i].nameid[j] == itemid) + { + bonus += sd->itemgrouphealrate[i]; + continue; + } + } + } + return bonus; +} + +/*========================================== + * DBの存在確認 + *------------------------------------------ + */ +struct item_data* itemdb_exists(int nameid) +{ + struct item_data* id; + if (!nameid) return NULL; + id = idb_get(item_db,nameid); + //Adjust nameid in case it's used outside. [Skotlex] + if (id == &dummy_item) + dummy_item.nameid = nameid; + return id; +} + +/*========================================== + * Converts the jobid from the format in itemdb + * to the format used by the map server. [Skotlex] + *------------------------------------------ + */ +static void itemdb_jobid2mapid(unsigned int *bclass, unsigned int jobmask) +{ + int i; + bclass[0]= bclass[1]= bclass[2]= 0; + //Base classes + if (jobmask & 1<nameid = key.i; + id->weight=1; + id->type=IT_ETC; + return id; +} + +/*========================================== + * Loads (and creates if not found) an item from the db. + *------------------------------------------ + */ +struct item_data* itemdb_load(int nameid) +{ + struct item_data *id = idb_ensure(item_db,nameid,create_item_data); + if (id == &dummy_item) + { //Remove dummy_item, replace by real data. + DBKey key; + key.i = nameid; + idb_remove(item_db,nameid); + id = create_item_data(key, NULL); + idb_put(item_db,nameid,id); + } + return id; +} + +static void* return_dummy_data(DBKey key, va_list args) { + if (battle_config.error_log) + ShowWarning("itemdb_search: Item ID %d does not exists in the item_db. Using dummy data.\n", key.i); + dummy_item.nameid = key.i; + return &dummy_item; +} + +/*========================================== + * Loads an item from the db. If not found, it will return the dummy item. + *------------------------------------------ + */ +struct item_data* itemdb_search(int nameid) +{ + return idb_ensure(item_db,nameid,return_dummy_data); +} + +/*========================================== + * Returns if given item is a player-equippable piece. + *------------------------------------------ + */ +int itemdb_isequip(int nameid) +{ + int type=itemdb_type(nameid); + switch (type) { + case IT_WEAPON: + case IT_ARMOR: + case IT_AMMO: + return 1; + default: + return 0; + } +} + +/*========================================== + * Alternate version of itemdb_isequip + *------------------------------------------ + */ +int itemdb_isequip2(struct item_data *data) +{ + nullpo_retr(0, data); + switch(data->type) { + case IT_WEAPON: + case IT_ARMOR: + case IT_AMMO: + return 1; + default: + return 0; + } +} + +/*========================================== +* Returns if given item's type is stackable. +*------------------------------------------ +*/ +int itemdb_isstackable(int nameid) +{ + int type=itemdb_type(nameid); + switch(type) { + case IT_WEAPON: + case IT_ARMOR: + case IT_PETEGG: + case IT_PETARMOR: + return 0; + default: + return 1; + } +} + +/*========================================== +* Alternate version of itemdb_isstackable +*------------------------------------------ +*/ +int itemdb_isstackable2(struct item_data *data) +{ + nullpo_retr(0, data); + switch(data->type) { + case IT_WEAPON: + case IT_ARMOR: + case IT_PETEGG: + case IT_PETARMOR: + return 0; + default: + return 1; + } +} + + +/*========================================== + * Trade Restriction functions [Skotlex] + *------------------------------------------ + */ +int itemdb_isdropable_sub(struct item_data *item, int gmlv, int unused) +{ + return (item && (!(item->flag.trade_restriction&1) || gmlv >= item->gm_lv_trade_override)); +} + +int itemdb_cantrade_sub(struct item_data* item, int gmlv, int gmlv2) +{ + return (item && (!(item->flag.trade_restriction&2) || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override)); +} + +int itemdb_canpartnertrade_sub(struct item_data* item, int gmlv, int gmlv2) +{ + return (item && (item->flag.trade_restriction&4 || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override)); +} + +int itemdb_cansell_sub(struct item_data* item, int gmlv, int unused) +{ + return (item && (!(item->flag.trade_restriction&8) || gmlv >= item->gm_lv_trade_override)); +} + +int itemdb_cancartstore_sub(struct item_data* item, int gmlv, int unused) +{ + return (item && (!(item->flag.trade_restriction&16) || gmlv >= item->gm_lv_trade_override)); +} + +int itemdb_canstore_sub(struct item_data* item, int gmlv, int unused) +{ + return (item && (!(item->flag.trade_restriction&32) || gmlv >= item->gm_lv_trade_override)); +} + +int itemdb_canguildstore_sub(struct item_data* item, int gmlv, int unused) +{ + return (item && (!(item->flag.trade_restriction&64) || gmlv >= item->gm_lv_trade_override)); +} + +int itemdb_isrestricted(struct item* item, int gmlv, int gmlv2, int (*func)(struct item_data*, int, int)) +{ + struct item_data* item_data = itemdb_search(item->nameid); + int i; + + if (!func(item_data, gmlv, gmlv2)) + return 0; + + if(item_data->slot == 0 || itemdb_isspecial(item->card[0])) + return 1; + + for(i = 0; i < item_data->slot; i++) { + if (!item->card[i]) continue; + if (!func(itemdb_search(item->card[i]), gmlv, gmlv2)) + return 0; + } + return 1; +} + +/*========================================== + * Specifies if item-type should drop unidentified. + *------------------------------------------ + */ +int itemdb_isidentified(int nameid) +{ + int type=itemdb_type(nameid); + switch (type) { + case IT_WEAPON: + case IT_ARMOR: + case IT_PETARMOR: + return 0; + default: + return 1; + } +} + +/*========================================== + * アイテム使用可能フラグのオーバーライド + *------------------------------------------ + */ +static int itemdb_read_itemavail (void) +{ + FILE *fp; + int nameid, j, k, ln = 0; + char line[1024], *str[10], *p; + struct item_data *id; + + sprintf(line, "%s/item_avail.txt", db_path); + if ((fp = fopen(line,"r")) == NULL) { + ShowError("can't read %s\n", line); + return -1; + } + + while (fgets(line, sizeof(line) - 1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + malloc_tsetdword(str, 0, sizeof(str)); + for (j = 0, p = line; j < 2 && p; j++) { + str[j] = p; + p = strchr(p, ','); + if(p) *p++ = 0; + } + + if (j < 2 || str[0] == NULL || + (nameid = atoi(str[0])) < 0 || !(id = itemdb_exists(nameid))) + continue; + + k = atoi(str[1]); + if (k > 0) { + id->flag.available = 1; + id->view_id = k; + } else + id->flag.available = 0; + ln++; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_avail.txt"); + + return 0; +} + +/*========================================== + * read item group data + *------------------------------------------ + */ +static void itemdb_read_itemgroup_sub(const char* filename) +{ + FILE *fp; + char line[1024]; + int ln=0; + int groupid,j,k,nameid; + char *str[3],*p; + char w1[1024], w2[1024]; + + if( (fp=fopen(filename,"r"))==NULL ){ + ShowError("can't read %s\n", line); + return; + } + + while(fgets(line,1020,fp)){ + ln++; + if(line[0]=='/' && line[1]=='/') + continue; + if(strstr(line,"import")) { + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2 && + strcmpi(w1, "import") == 0) { + itemdb_read_itemgroup_sub(w2); + continue; + } + } + malloc_tsetdword(str,0,sizeof(str)); + for(j=0,p=line;j<3 && p;j++){ + str[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(str[0]==NULL) + continue; + if (j<3) { + if (j>1) //Or else it barks on blank lines... + ShowWarning("itemdb_read_itemgroup: Insufficient fields for entry at %s:%d\n", filename, ln); + continue; + } + groupid = atoi(str[0]); + if (groupid < 0 || groupid >= MAX_ITEMGROUP) { + ShowWarning("itemdb_read_itemgroup: Invalid group %d in %s:%d\n", groupid, filename, ln); + continue; + } + nameid = atoi(str[1]); + if (!itemdb_exists(nameid)) { + ShowWarning("itemdb_read_itemgroup: Non-existant item %d in %s:%d\n", nameid, filename, ln); + continue; + } + k = atoi(str[2]); + if (itemgroup_db[groupid].qty+k > MAX_RANDITEM) { + ShowWarning("itemdb_read_itemgroup: Group %d is full (%d entries) in %s:%d\n", groupid, MAX_RANDITEM, filename, ln); + continue; + } + for(j=0;jjname,buf2,ITEM_NAME_LENGTH)!=0 ){ + ShowNotice("[override] %d %s => %s\n",nameid + ,itemdb_search(nameid)->jname,buf2); + } +#endif + + strncpy(itemdb_search(nameid)->jname,buf2,ITEM_NAME_LENGTH-1); + } + + p=strchr(p,10); + if(!p) break; + p++; + } + aFree(buf); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\idnum2itemdisplaynametable.txt"); + + return 0; +} + +/*========================================== + * カードイラストのリソース名前テーブルを読み込む + *------------------------------------------ + */ +static int itemdb_read_cardillustnametable(void) +{ + char *buf,*p; + int s; + + buf=(char *) grfio_reads("data\\num2cardillustnametable.txt",&s); + + if(buf==NULL) + return -1; + + buf[s]=0; + for(p=buf;p-bufcardillustname,buf2,64); + } + + p=strchr(p,10); + if(!p) break; + p++; + } + aFree(buf); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\num2cardillustnametable.txt"); + + return 0; +} + +// +// 初期化 +// +/*========================================== + * + *------------------------------------------ + */ +static int itemdb_read_itemslottable(void) +{ + char *buf, *p; + int s; + + buf = (char *)grfio_reads("data\\itemslottable.txt", &s); + if (buf == NULL) + return -1; + buf[s] = 0; + for (p = buf; p - buf < s; ) { + int nameid, equip; + struct item_data* item; + sscanf(p, "%d#%d#", &nameid, &equip); + item = itemdb_search(nameid); + if (equip && item && itemdb_isequip2(item)) + item->equip = equip; + p = strchr(p, 10); + if(!p) break; + p++; + p=strchr(p, 10); + if(!p) break; + p++; + } + aFree(buf); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\itemslottable.txt"); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static int itemdb_read_itemslotcounttable(void) +{ + char *buf, *p; + int s; + + buf = (char *)grfio_reads("data\\itemslotcounttable.txt", &s); + if (buf == NULL) + return -1; + buf[s] = 0; + for (p = buf; p - buf < s;){ + int nameid, slot; + sscanf(p, "%d#%d#", &nameid, &slot); + if (slot > MAX_SLOTS) + { + ShowWarning("itemdb_read_itemslotcounttable: Item %d specifies %d slots, but the server only supports up to %d\n", nameid, slot, MAX_SLOTS); + slot = MAX_SLOTS; + } + itemdb_slot(nameid) = slot; + p = strchr(p,10); + if(!p) break; + p++; + p = strchr(p,10); + if(!p) break; + p++; + } + aFree(buf); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", "data\\itemslotcounttable.txt"); + + return 0; +} + +/*========================================== + * 装備制限ファイル読み出し + *------------------------------------------ + */ +static int itemdb_read_noequip(void) +{ + FILE *fp; + char line[1024]; + int ln=0; + int nameid,j; + char *str[32],*p; + struct item_data *id; + + sprintf(line, "%s/item_noequip.txt", db_path); + if( (fp=fopen(line,"r"))==NULL ){ + ShowError("can't read %s\n", line); + return -1; + } + while(fgets(line,1020,fp)){ + if(line[0]=='/' && line[1]=='/') + continue; + malloc_tsetdword(str,0,sizeof(str)); + for(j=0,p=line;j<2 && p;j++){ + str[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(str[0]==NULL) + continue; + + nameid=atoi(str[0]); + if(nameid<=0 || !(id=itemdb_exists(nameid))) + continue; + + id->flag.no_equip=atoi(str[1]); + + ln++; + + } + fclose(fp); + if (ln > 0) { + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"item_noequip.txt"); + } + return 0; +} + +/*========================================== + * Reads item trade restrictions [Skotlex] + *------------------------------------------ + */ +static int itemdb_read_itemtrade(void) +{ + FILE *fp; + int nameid, j, flag, gmlv, ln = 0; + char line[1024], *str[10], *p; + struct item_data *id; + + sprintf(line, "%s/item_trade.txt", db_path); + if ((fp = fopen(line,"r")) == NULL) { + ShowError("can't read %s\n", line); + return -1; + } + + while (fgets(line, sizeof(line) - 1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + malloc_tsetdword(str, 0, sizeof(str)); + for (j = 0, p = line; j < 3 && p; j++) { + str[j] = p; + p = strchr(p, ','); + if(p) *p++ = 0; + } + + if (j < 3 || str[0] == NULL || + (nameid = atoi(str[0])) < 0 || !(id = itemdb_exists(nameid))) + continue; + + flag = atoi(str[1]); + gmlv = atoi(str[2]); + + if (flag > 0 && flag < 128 && gmlv > 0) { //Check range + id->flag.trade_restriction = flag; + id->gm_lv_trade_override = gmlv; + ln++; + } + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_trade.txt"); + + return 0; +} + +/*====================================== + * Applies gender restrictions according to settings. [Skotlex] + *====================================== + */ +static int itemdb_gendercheck(struct item_data *id) +{ + if (id->nameid == WEDDING_RING_M) //Grom Ring + return 1; + if (id->nameid == WEDDING_RING_F) //Bride Ring + return 0; + if (id->look == W_MUSICAL && id->type == IT_WEAPON) //Musical instruments are always male-only + return 1; + if (id->look == W_WHIP && id->type == IT_WEAPON) //Whips are always female-only + return 0; + + return (battle_config.ignore_items_gender?2:id->sex); +} +#ifndef TXT_ONLY + +/*====================================== +* SQL +*=================================== +*/ +static int itemdb_read_sqldb(void) +{ + unsigned short nameid; + struct item_data *id; + char script[65535 + 2 + 1]; // Maximum length of MySQL TEXT type (65535) + 2 bytes for curly brackets + 1 byte for terminator + char *item_db_name[] = { item_db_db, item_db2_db }; + long unsigned int ln = 0; + int i; + + // ---------- + + for (i = 0; i < 2; i++) { + sprintf(tmp_sql, "SELECT * FROM `%s`", item_db_name[i]); + + // Execute the query; if the query execution succeeded... + if (mysql_query(&mmysql_handle, tmp_sql) == 0) { + sql_res = mysql_store_result(&mmysql_handle); + + // If the storage of the query result succeeded... + if (sql_res) { + // Parse each row in the query result into sql_row + while ((sql_row = mysql_fetch_row(sql_res))) + { /*Table structure is: + 00 id + 01 name_english + 02 name_japanese + 03 type + 04 price_buy + 05 price_sell + 06 weight + 07 attack + 08 defence + 09 range + 10 slots + 11 equip_jobs + 12 equip_upper + 13 equip_genders + 14 equip_locations + 15 weapon_level + 16 equip_level + 17 refineable + 18 view + 19 script + 20 equip_script + 21 unequip_script + */ + nameid = atoi(sql_row[0]); + + // If the identifier is not within the valid range, process the next row + if (nameid == 0) + continue; + + ln++; + + // ---------- + id = itemdb_load(nameid); + + strncpy(id->name, sql_row[1], ITEM_NAME_LENGTH-1); + strncpy(id->jname, sql_row[2], ITEM_NAME_LENGTH-1); + + id->type = atoi(sql_row[3]); + if (id->type == IT_DELAYCONSUME) + { //Items that are consumed upon target confirmation + //(yggdrasil leaf, spells & pet lures) [Skotlex] + id->type = IT_USABLE; + id->flag.delay_consume=1; + } + + // If price_buy is not NULL and price_sell is not NULL... + if ((sql_row[4] != NULL) && (sql_row[5] != NULL)) { + id->value_buy = atoi(sql_row[4]); + id->value_sell = atoi(sql_row[5]); + } + // If price_buy is not NULL and price_sell is NULL... + else if ((sql_row[4] != NULL) && (sql_row[5] == NULL)) { + id->value_buy = atoi(sql_row[4]); + id->value_sell = atoi(sql_row[4]) / 2; + } + // If price_buy is NULL and price_sell is not NULL... + else if ((sql_row[4] == NULL) && (sql_row[5] != NULL)) { + id->value_buy = atoi(sql_row[5]) * 2; + id->value_sell = atoi(sql_row[5]); + } + // If price_buy is NULL and price_sell is NULL... + if ((sql_row[4] == NULL) && (sql_row[5] == NULL)) { + id->value_buy = 0; + id->value_sell = 0; + } + + id->weight = atoi(sql_row[6]); + id->atk = (sql_row[7] != NULL) ? atoi(sql_row[7]) : 0; + id->def = (sql_row[8] != NULL) ? atoi(sql_row[8]) : 0; + id->range = (sql_row[9] != NULL) ? atoi(sql_row[9]) : 0; + id->slot = (sql_row[10] != NULL) ? atoi(sql_row[10]) : 0; + if (id->slot > MAX_SLOTS) + { + ShowWarning("itemdb_read_sqldb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS); + id->slot = MAX_SLOTS; + } + itemdb_jobid2mapid(id->class_base, (sql_row[11] != NULL) ? (unsigned int)strtoul(sql_row[11], NULL, 0) : 0); + id->class_upper= (sql_row[12] != NULL) ? atoi(sql_row[12]) : 0; + id->sex = (sql_row[13] != NULL) ? atoi(sql_row[13]) : 0; + id->equip = (sql_row[14] != NULL) ? atoi(sql_row[14]) : 0; + if (!id->equip && itemdb_isequip2(id)) + { + ShowWarning("Item %d (%s) is an equipment with no equip-field! Making it an etc item.\n", nameid, id->jname); + id->type = 3; + } + id->wlv = (sql_row[15] != NULL) ? atoi(sql_row[15]) : 0; + id->elv = (sql_row[16] != NULL) ? atoi(sql_row[16]) : 0; + id->flag.no_refine = (sql_row[17] == NULL || atoi(sql_row[17]) == 1)?0:1; + id->look = (sql_row[18] != NULL) ? atoi(sql_row[18]) : 0; + id->view_id = 0; + id->sex = itemdb_gendercheck(id); //Apply gender filtering. + + // ---------- + + if (id->script) + script_free_code(id->script); + if (sql_row[19] != NULL) { + if (sql_row[19][0] == '{') + id->script = parse_script((unsigned char *) sql_row[19],item_db_name[i], 0); + else { + sprintf(script, "{%s}", sql_row[19]); + id->script = parse_script((unsigned char *) script, item_db_name[i], 0); + } + } else id->script = NULL; + + if (id->equip_script) + script_free_code(id->equip_script); + if (sql_row[20] != NULL) { + if (sql_row[20][0] == '{') + id->equip_script = parse_script((unsigned char *) sql_row[20], item_db_name[i], 0); + else { + sprintf(script, "{%s}", sql_row[20]); + id->equip_script = parse_script((unsigned char *) script, item_db_name[i], 0); + } + } else id->equip_script = NULL; + + if (id->unequip_script) + script_free_code(id->unequip_script); + if (sql_row[21] != NULL) { + if (sql_row[21][0] == '{') + id->unequip_script = parse_script((unsigned char *) sql_row[21],item_db_name[i], 0); + else { + sprintf(script, "{%s}", sql_row[21]); + id->unequip_script = parse_script((unsigned char *) script, item_db_name[i], 0); + } + } else id->unequip_script = NULL; + + // ---------- + + id->flag.available = 1; + id->flag.value_notdc = 0; + id->flag.value_notoc = 0; + } + + ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, item_db_name[i]); + ln = 0; + } else { + ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + // Free the query result + mysql_free_result(sql_res); + } else { + ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + return 0; +} +#endif /* not TXT_ONLY */ + +/*========================================== + * アイテムデータベースの読み込み + *------------------------------------------ + */ +static int itemdb_readdb(void) +{ + FILE *fp; + char line[1024]; + int ln=0,lines=0; + int nameid,j; + char *str[32],*p,*np; + struct item_data *id; + int i=0; + char *filename[]={ "item_db.txt","item_db2.txt" }; + + for(i=0;i<2;i++){ + sprintf(line, "%s/%s", db_path, filename[i]); + fp=fopen(line,"r"); + if(fp==NULL){ + if(i>0) + continue; + ShowFatalError("can't read %s\n",line); + exit(1); + } + + lines=0; + while(fgets(line,1020,fp)){ + lines++; + if(line[0]=='/' && line[1]=='/') + continue; + malloc_tsetdword(str,0,sizeof(str)); + for(j=0,np=p=line;j<19 && p;j++){ + str[j]=p; + p=strchr(p,','); + if(p){ *p++=0; np=p; } + } + if(str[0]==NULL) + continue; + + nameid=atoi(str[0]); + if(nameid<=0) + continue; + if (j < 19) + { //Crash-fix on broken item lines. [Skotlex] + ShowWarning("Reading %s: Insufficient fields for item with id %d, skipping.\n", filename[i], nameid); + continue; + } + ln++; + + //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Job Upper,Gender,Loc,wLV,eLV,refineable,View + id=itemdb_load(nameid); + strncpy(id->name, str[1], ITEM_NAME_LENGTH-1); + strncpy(id->jname, str[2], ITEM_NAME_LENGTH-1); + id->type=atoi(str[3]); + if (id->type == IT_DELAYCONSUME) + { //Items that are consumed upon target confirmation + //(yggdrasil leaf, spells & pet lures) [Skotlex] + id->type = IT_USABLE; + id->flag.delay_consume=1; + } + + { + int buy = atoi(str[4]), sell = atoi(str[5]); + // if buying price > selling price * 2 consider it valid and don't change it [celest] + if (buy && sell && buy > sell*2){ + id->value_buy = buy; + id->value_sell = sell; + } else { + // buy≠sell*2 は item_value_db.txt で指定してください。 + if (sell) { // sell値を優先とする + id->value_buy = sell*2; + id->value_sell = sell; + } else { + id->value_buy = buy; + id->value_sell = buy/2; + } + } + // check for bad prices that can possibly cause exploits + if (id->value_buy*75/100 < id->value_sell*124/100) { + ShowWarning ("Item %s [%d] buying:%d < selling:%d\n", + id->name, id->nameid, id->value_buy*75/100, id->value_sell*124/100); + } + } + id->weight=atoi(str[6]); + id->atk=atoi(str[7]); + id->def=atoi(str[8]); + id->range=atoi(str[9]); + id->slot=atoi(str[10]); + if (id->slot > MAX_SLOTS) + { + ShowWarning("itemdb_readdb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS); + id->slot = MAX_SLOTS; + } + itemdb_jobid2mapid(id->class_base, (unsigned int)strtoul(str[11],NULL,0)); + id->class_upper = atoi(str[12]); + id->sex = atoi(str[13]); + if(id->equip != atoi(str[14])){ + id->equip=atoi(str[14]); + } + if (!id->equip && itemdb_isequip2(id)) + { + ShowWarning("Item %d (%s) is an equipment with no equip-field! Making it an etc item.\n", nameid, id->jname); + id->type = 3; + } + id->wlv=atoi(str[15]); + id->elv=atoi(str[16]); + id->flag.no_refine = atoi(str[17])?0:1; //If the refine column is 1, no_refine is 0 + id->look=atoi(str[18]); + id->flag.available=1; + id->flag.value_notdc=0; + id->flag.value_notoc=0; + id->view_id=0; + id->sex = itemdb_gendercheck(id); //Apply gender filtering. + + if (id->script) { + script_free_code(id->script); + id->script=NULL; + } + if (id->equip_script) { + script_free_code(id->equip_script); + id->equip_script=NULL; + } + if (id->unequip_script) { + script_free_code(id->unequip_script); + id->unequip_script=NULL; + } + + if((p=strchr(np,'{'))==NULL) + continue; + + str[19] = p; //Script + np = strchr(p,'}'); + + while (np && np[1] && np[1] != ',') + np = strchr(np+1,'}'); //Jump close brackets until the next field is found. + if (!np || !np[1]) { + //Couldn't find the end of the script field. + id->script = parse_script((unsigned char *) str[19],filename[i],lines); + continue; + } + np[1] = '\0'; //Set end of script + id->script = parse_script((unsigned char *) str[19],filename[i],lines); + np+=2; //Skip to next field + + if(!np || (p=strchr(np,'{'))==NULL) + continue; + + str[20] = p; //Equip Script + np = strchr(p,'}'); + + while (np && np[1] && np[1] != ',') + np = strchr(np+1,'}'); //Jump close brackets until the next field is found. + if (!np || !np[1]) { + //Couldn't find the end of the script field. + id->equip_script = parse_script((unsigned char *) str[20],filename[i],lines); + continue; + } + + np[1] = '\0'; //Set end of script + id->equip_script = parse_script((unsigned char *) str[20],filename[i],lines); + np+=2; //Skip comma, to next field + + if(!np || (p=strchr(np,'{'))==NULL) + continue; + //Unequip script, last column. + id->unequip_script = parse_script((unsigned char *) p,filename[i],lines); + } + fclose(fp); + if (ln > 0) { + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,filename[i]); + } + ln=0; // reset to 0 + } + return 0; +} + +/*==================================== + * Removed item_value_db, don't re-add + *------------------------------------ + */ +static void itemdb_read(void) +{ +#ifndef TXT_ONLY + if (db_use_sqldbs) + itemdb_read_sqldb(); + else +#endif + itemdb_readdb(); + + itemdb_read_itemgroup(); + itemdb_read_itemavail(); + itemdb_read_noequip(); + itemdb_read_itemtrade(); + if (battle_config.cardillust_read_grffile) + itemdb_read_cardillustnametable(); + if (battle_config.item_equip_override_grffile) + itemdb_read_itemslottable(); + if (battle_config.item_slots_override_grffile) + itemdb_read_itemslotcounttable(); + if (battle_config.item_name_override_grffile) + itemdb_read_itemnametable(); +} + +/*========================================== + * Initialize / Finalize + *------------------------------------------ + */ +static int itemdb_final_sub (DBKey key,void *data,va_list ap) +{ + int flag; + struct item_data *id = (struct item_data *)data; + + flag = va_arg(ap, int); + if (id->script) + { + script_free_code(id->script); + id->script = NULL; + } + if (id->equip_script) + { + script_free_code(id->equip_script); + id->equip_script = NULL; + } + if (id->unequip_script) + { + script_free_code(id->unequip_script); + id->unequip_script = NULL; + } + // Whether to clear the item data (exception: do not clear the dummy item data + if (flag && id != &dummy_item) + aFree(id); + + return 0; +} + +void itemdb_reload(void) +{ + //Just read, the function takes care of freeing scripts. + itemdb_read(); +} + +void do_final_itemdb(void) +{ + item_db->destroy(item_db, itemdb_final_sub, 1); + if (dummy_item.script) { + script_free_code(dummy_item.script); + dummy_item.script = NULL; + } + if (dummy_item.equip_script) { + script_free_code(dummy_item.equip_script); + dummy_item.equip_script = NULL; + } + if (dummy_item.unequip_script) { + script_free_code(dummy_item.unequip_script); + dummy_item.unequip_script = NULL; + } +} + +int do_init_itemdb(void) +{ + item_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); + create_dummy_data(); //Dummy data item. + itemdb_read(); + + return 0; +} diff --git a/src/map/itemdb.h b/src/map/itemdb.h index b141abbfc..f1e97227c 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -1,148 +1,148 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _ITEMDB_H_ -#define _ITEMDB_H_ - -#include "map.h" -#define MAX_RANDITEM 10000 - -enum { - IT_HEALING = 0, - IT_UNKNOWN, //1 - IT_USABLE, //2 - IT_ETC, //3 - IT_WEAPON, //4 - IT_ARMOR, //5 - IT_CARD, //6 - IT_PETEGG, //7 - IT_PETARMOR,//8 - IT_UNKNOWN2,//9 - IT_AMMO, //10 - IT_DELAYCONSUME,//11 - IT_MAX -} item_types; - -#define CARD0_FORGE 0x00FF -#define CARD0_CREATE 0x00FE -#define CARD0_PET ((short)0xFF00) - -//Marks if the card0 given is "special" (non-item id used to mark pets/created items. [Skotlex] -#define itemdb_isspecial(i) (i == CARD0_FORGE || i == CARD0_CREATE || i == CARD0_PET) - -//Use apple for unknown items. -#define UNKNOWN_ITEM_ID 512 - -struct item_data { - int nameid; - char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH]; - char prefix[NAME_LENGTH],suffix[NAME_LENGTH]; - char cardillustname[64]; - //Do not add stuff between value_buy and wlv (see how getiteminfo works) - int value_buy; - int value_sell; - int type; - int maxchance; //For logs, for external game info, for scripts: Max drop chance of this item (e.g. 0.01% , etc.. if it = 0, then monsters don't drop it) [Lupus] - int sex; - int equip; - int weight; - int atk; - int def; - int range; - int slot; - int look; - int elv; - int wlv; -//Lupus: I rearranged order of these fields due to compatibility with ITEMINFO script command -// some script commands should be revised as well... - unsigned int class_base[3]; //Specifies if the base can wear this item (split in 3 indexes per type: 1-1, 2-1, 2-2) - unsigned class_upper : 3; //Specifies if the upper-type can equip it (1: normal, 2: upper, 3: baby) - struct { - unsigned short chance; - int id; - } mob[MAX_SEARCH]; //Holds the mobs that have the highest drop rate for this item. [Skotlex] - struct script_code *script; //Default script for everything. - struct script_code *equip_script; //Script executed once when equipping. - struct script_code *unequip_script;//Script executed once when unequipping. - struct { - unsigned available : 1; - unsigned value_notdc : 1; - unsigned value_notoc : 1; - short no_equip; - unsigned no_use : 1; - unsigned no_refine : 1; // [celest] - unsigned delay_consume : 1; // Signifies items that are not consumed inmediately upon double-click [Skotlex] - unsigned trade_restriction : 7; //Item restrictions mask [Skotlex] - unsigned autoequip: 1; - } flag; - short gm_lv_trade_override; //GM-level to override trade_restriction - int view_id; -}; - -struct item_group { - int nameid[MAX_RANDITEM]; - int qty; //Counts amount of items in the group. -}; - -struct item_data* itemdb_searchname(const char *name); -int itemdb_searchname_array(struct item_data** data, int size, const char *str); -struct item_data* itemdb_load(int nameid); -struct item_data* itemdb_search(int nameid); -struct item_data* itemdb_exists(int nameid); -#define itemdb_type(n) itemdb_search(n)->type -#define itemdb_atk(n) itemdb_search(n)->atk -#define itemdb_def(n) itemdb_search(n)->def -#define itemdb_look(n) itemdb_search(n)->look -#define itemdb_weight(n) itemdb_search(n)->weight -#define itemdb_equip(n) itemdb_search(n)->equip -#define itemdb_usescript(n) itemdb_search(n)->script -#define itemdb_equipscript(n) itemdb_search(n)->script -#define itemdb_wlv(n) itemdb_search(n)->wlv -#define itemdb_range(n) itemdb_search(n)->range -#define itemdb_slot(n) itemdb_search(n)->slot -#define itemdb_available(n) (itemdb_exists(n) && itemdb_search(n)->flag.available) -#define itemdb_viewid(n) (itemdb_search(n)->view_id) -#define itemdb_autoequip(n) (itemdb_search(n)->flag.autoequip) -int itemdb_group_bonus(struct map_session_data *sd, int itemid); - -int itemdb_searchrandomid(int flags); - -#define itemdb_value_buy(n) itemdb_search(n)->value_buy -#define itemdb_value_sell(n) itemdb_search(n)->value_sell -#define itemdb_value_notdc(n) itemdb_search(n)->flag.value_notdc -#define itemdb_value_notoc(n) itemdb_search(n)->flag.value_notoc -#define itemdb_canrefine(n) itemdb_search(n)->flag.no_refine -//Item trade restrictions [Skotlex] -int itemdb_isdropable_sub(struct item_data *, int, int); -int itemdb_cantrade_sub(struct item_data*, int, int); -int itemdb_canpartnertrade_sub(struct item_data*, int, int); -int itemdb_cansell_sub(struct item_data*,int, int); -int itemdb_cancartstore_sub(struct item_data*, int, int); -int itemdb_canstore_sub(struct item_data*, int, int); -int itemdb_canguildstore_sub(struct item_data*, int, int); -int itemdb_isrestricted(struct item* item, int gmlv, int gmlv2, int (*func)(struct item_data*, int, int)); -#define itemdb_isdropable(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_isdropable_sub) -#define itemdb_cantrade(item, gmlv, gmlv2) itemdb_isrestricted(item, gmlv, gmlv2, itemdb_cantrade_sub) -#define itemdb_canpartnertrade(item, gmlv, gmlv2) itemdb_isrestricted(item, gmlv, gmlv2, itemdb_canpartnertrade_sub) -#define itemdb_cansell(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_cansell_sub) -#define itemdb_cancartstore(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_cancartstore_sub) -#define itemdb_canstore(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_canstore_sub) -#define itemdb_canguildstore(item, gmlv) itemdb_isrestricted(item , gmlv, 0, itemdb_canguildstore_sub) - -int itemdb_isequip(int); -int itemdb_isequip2(struct item_data *); -int itemdb_isidentified(int); -int itemdb_isstackable(int); -int itemdb_isstackable2(struct item_data *); - -// itemdb_equipマクロとitemdb_equippointとの違いは -// 前者が鯖側dbで定義された値そのものを返すのに対し -// 後者はsessiondataを考慮した鞍側での装備可能場所 -// すべての組み合わせを返す - -void itemdb_reload(void); - -void do_final_itemdb(void); -int do_init_itemdb(void); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _ITEMDB_H_ +#define _ITEMDB_H_ + +#include "map.h" +#define MAX_RANDITEM 10000 + +enum { + IT_HEALING = 0, + IT_UNKNOWN, //1 + IT_USABLE, //2 + IT_ETC, //3 + IT_WEAPON, //4 + IT_ARMOR, //5 + IT_CARD, //6 + IT_PETEGG, //7 + IT_PETARMOR,//8 + IT_UNKNOWN2,//9 + IT_AMMO, //10 + IT_DELAYCONSUME,//11 + IT_MAX +} item_types; + +#define CARD0_FORGE 0x00FF +#define CARD0_CREATE 0x00FE +#define CARD0_PET ((short)0xFF00) + +//Marks if the card0 given is "special" (non-item id used to mark pets/created items. [Skotlex] +#define itemdb_isspecial(i) (i == CARD0_FORGE || i == CARD0_CREATE || i == CARD0_PET) + +//Use apple for unknown items. +#define UNKNOWN_ITEM_ID 512 + +struct item_data { + int nameid; + char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH]; + char prefix[NAME_LENGTH],suffix[NAME_LENGTH]; + char cardillustname[64]; + //Do not add stuff between value_buy and wlv (see how getiteminfo works) + int value_buy; + int value_sell; + int type; + int maxchance; //For logs, for external game info, for scripts: Max drop chance of this item (e.g. 0.01% , etc.. if it = 0, then monsters don't drop it) [Lupus] + int sex; + int equip; + int weight; + int atk; + int def; + int range; + int slot; + int look; + int elv; + int wlv; +//Lupus: I rearranged order of these fields due to compatibility with ITEMINFO script command +// some script commands should be revised as well... + unsigned int class_base[3]; //Specifies if the base can wear this item (split in 3 indexes per type: 1-1, 2-1, 2-2) + unsigned class_upper : 3; //Specifies if the upper-type can equip it (1: normal, 2: upper, 3: baby) + struct { + unsigned short chance; + int id; + } mob[MAX_SEARCH]; //Holds the mobs that have the highest drop rate for this item. [Skotlex] + struct script_code *script; //Default script for everything. + struct script_code *equip_script; //Script executed once when equipping. + struct script_code *unequip_script;//Script executed once when unequipping. + struct { + unsigned available : 1; + unsigned value_notdc : 1; + unsigned value_notoc : 1; + short no_equip; + unsigned no_use : 1; + unsigned no_refine : 1; // [celest] + unsigned delay_consume : 1; // Signifies items that are not consumed inmediately upon double-click [Skotlex] + unsigned trade_restriction : 7; //Item restrictions mask [Skotlex] + unsigned autoequip: 1; + } flag; + short gm_lv_trade_override; //GM-level to override trade_restriction + int view_id; +}; + +struct item_group { + int nameid[MAX_RANDITEM]; + int qty; //Counts amount of items in the group. +}; + +struct item_data* itemdb_searchname(const char *name); +int itemdb_searchname_array(struct item_data** data, int size, const char *str); +struct item_data* itemdb_load(int nameid); +struct item_data* itemdb_search(int nameid); +struct item_data* itemdb_exists(int nameid); +#define itemdb_type(n) itemdb_search(n)->type +#define itemdb_atk(n) itemdb_search(n)->atk +#define itemdb_def(n) itemdb_search(n)->def +#define itemdb_look(n) itemdb_search(n)->look +#define itemdb_weight(n) itemdb_search(n)->weight +#define itemdb_equip(n) itemdb_search(n)->equip +#define itemdb_usescript(n) itemdb_search(n)->script +#define itemdb_equipscript(n) itemdb_search(n)->script +#define itemdb_wlv(n) itemdb_search(n)->wlv +#define itemdb_range(n) itemdb_search(n)->range +#define itemdb_slot(n) itemdb_search(n)->slot +#define itemdb_available(n) (itemdb_exists(n) && itemdb_search(n)->flag.available) +#define itemdb_viewid(n) (itemdb_search(n)->view_id) +#define itemdb_autoequip(n) (itemdb_search(n)->flag.autoequip) +int itemdb_group_bonus(struct map_session_data *sd, int itemid); + +int itemdb_searchrandomid(int flags); + +#define itemdb_value_buy(n) itemdb_search(n)->value_buy +#define itemdb_value_sell(n) itemdb_search(n)->value_sell +#define itemdb_value_notdc(n) itemdb_search(n)->flag.value_notdc +#define itemdb_value_notoc(n) itemdb_search(n)->flag.value_notoc +#define itemdb_canrefine(n) itemdb_search(n)->flag.no_refine +//Item trade restrictions [Skotlex] +int itemdb_isdropable_sub(struct item_data *, int, int); +int itemdb_cantrade_sub(struct item_data*, int, int); +int itemdb_canpartnertrade_sub(struct item_data*, int, int); +int itemdb_cansell_sub(struct item_data*,int, int); +int itemdb_cancartstore_sub(struct item_data*, int, int); +int itemdb_canstore_sub(struct item_data*, int, int); +int itemdb_canguildstore_sub(struct item_data*, int, int); +int itemdb_isrestricted(struct item* item, int gmlv, int gmlv2, int (*func)(struct item_data*, int, int)); +#define itemdb_isdropable(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_isdropable_sub) +#define itemdb_cantrade(item, gmlv, gmlv2) itemdb_isrestricted(item, gmlv, gmlv2, itemdb_cantrade_sub) +#define itemdb_canpartnertrade(item, gmlv, gmlv2) itemdb_isrestricted(item, gmlv, gmlv2, itemdb_canpartnertrade_sub) +#define itemdb_cansell(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_cansell_sub) +#define itemdb_cancartstore(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_cancartstore_sub) +#define itemdb_canstore(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_canstore_sub) +#define itemdb_canguildstore(item, gmlv) itemdb_isrestricted(item , gmlv, 0, itemdb_canguildstore_sub) + +int itemdb_isequip(int); +int itemdb_isequip2(struct item_data *); +int itemdb_isidentified(int); +int itemdb_isstackable(int); +int itemdb_isstackable2(struct item_data *); + +// itemdb_equipマクロとitemdb_equippointとの違いは +// 前者が鯖側dbで定義された値そのものを返すのに対し +// 後者はsessiondataを考慮した鞍側での装備可能場所 +// すべての組み合わせを返す + +void itemdb_reload(void); + +void do_final_itemdb(void); +int do_init_itemdb(void); + +#endif diff --git a/src/map/log.c b/src/map/log.c index 0d6f010f2..209b81d32 100644 --- a/src/map/log.c +++ b/src/map/log.c @@ -1,532 +1,532 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -// Logging functions by Azndragon & Codemaster -#include -#include -#include - -#include "../common/strlib.h" -#include "../common/nullpo.h" -#include "../common/showmsg.h" -#include "../common/malloc.h" -#include "itemdb.h" -#include "map.h" -#include "log.h" - -#ifndef SQL_DEBUG - -#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] - -#else - -#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) - -#endif - -struct Log_Config log_config; - -char timestring[255]; -time_t curtime; - -//FILTER OPTIONS -//0 = Don't log -//1 = Log any item -//Bits: || -//2 - Healing items (0) -//3 - Etc Items(3) + Arrows (10) -//4 - Usable Items(2) + Scrolls,Lures(11) -//5 - Weapon(4) -//6 - Shields,Armor,Headgears,Accessories,etc(5) -//7 - Cards(6) -//8 - Pet Accessories(8) + Eggs(7) (well, monsters don't drop 'em but we'll use the same system for ALL logs) -//9 - Log expensive items ( >= price_log) -//10 - Log big amount of items ( >= amount_log) -//11 - Log refined items (if their refine >= refine_log ) -//12 - Log rare items (if their drop chance <= rare_log ) - -//check if this item should be logged according the settings -int should_log_item(int filter, int nameid, int amount) { - struct item_data *item_data; - if ((item_data= itemdb_exists(nameid)) == NULL) return 0; - if ((filter&1) || // Filter = 1, we log any item - (filter&2 && item_data->type == IT_HEALING ) || - (filter&4 && (item_data->type == IT_ETC || item_data->type == IT_AMMO) ) || - (filter&8 && item_data->type == IT_USABLE ) || - (filter&16 && item_data->type == IT_WEAPON ) || - (filter&32 && item_data->type == IT_ARMOR ) || - (filter&64 && item_data->type == IT_CARD ) || - (filter&128 && (item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) ) || - (filter&256 && item_data->value_buy >= log_config.price_items_log ) || //expensive items - (filter&512 && abs(amount) >= log_config.amount_items_log ) || //big amount of items - (filter&2048 && ((item_data->maxchance <= log_config.rare_items_log) || item_data->nameid == 714) ) //Rare items or Emperium - ) return item_data->nameid; - - return 0; -} - -int log_branch(struct map_session_data *sd) -{ -#ifndef TXT_ONLY - char t_name[NAME_LENGTH*2]; -#endif - FILE *logfp; - - if(!log_config.enable_logs) - return 0; - nullpo_retr(0, sd); -#ifndef TXT_ONLY - if(log_config.sql_logs > 0) - { - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`branch_date`, `account_id`, `char_id`, `char_name`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%s')", - log_config.log_branch_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex)); - if(mysql_query(&logmysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - return 1; - } -#endif - if((logfp=fopen(log_config.log_branch,"a+")) == NULL) - return 0; - time(&curtime); - strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); - fprintf(logfp,"%s - %s[%d:%d]\t%s%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex), RETCODE); - fclose(logfp); - return 1; -} - - -int log_pick_pc(struct map_session_data *sd, const char *type, int nameid, int amount, struct item *itm) -{ - FILE *logfp; - char *mapname; - - nullpo_retr(0, sd); - //Should we log this item? [Lupus] - if (!should_log_item(log_config.filter,nameid, amount)) - return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus] - - mapname = (char*)mapindex_id2name(sd->mapindex); - - if(mapname==NULL) - mapname=""; - -#ifndef TXT_ONLY - if(log_config.sql_logs > 0) - { - if (itm==NULL) { - //We log common item - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')", - log_config.log_pick_db, sd->status.char_id, type, nameid, amount, mapname); - } else { - //We log Extended item - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')", - log_config.log_pick_db, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname); - } - - if(mysql_query(&logmysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - return 1; - } -#endif - if((logfp=fopen(log_config.log_pick,"a+")) == NULL) - return 0; - time(&curtime); - strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); - - if (itm==NULL) { - //We log common item - fprintf(logfp,"%s - %d\t%s\t%d,%d,%s%s", - timestring, sd->status.char_id, type, nameid, amount, mapname, RETCODE); - - } else { - //We log Extended item - fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s%s", - timestring, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname, RETCODE); - } - fclose(logfp); - return 1; //Logged -} - -//Mob picked item -int log_pick_mob(struct mob_data *md, const char *type, int nameid, int amount, struct item *itm) -{ - FILE *logfp; - char *mapname; - - nullpo_retr(0, md); - //Should we log this item? [Lupus] - if (!should_log_item(log_config.filter,nameid, amount)) - return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus] - - //either PLAYER or MOB (here we get map name and objects ID) - mapname = map[md->bl.m].name; - if(mapname==NULL) - mapname=""; - -#ifndef TXT_ONLY - if(log_config.sql_logs > 0) - { - if (itm==NULL) { - //We log common item - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')", - log_config.log_pick_db, md->class_, type, nameid, amount, mapname); - } else { - //We log Extended item - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')", - log_config.log_pick_db, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname); - } - - if(mysql_query(&logmysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - return 1; - } -#endif - if((logfp=fopen(log_config.log_pick,"a+")) == NULL) - return 0; - time(&curtime); - strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); - - if (itm==NULL) { - //We log common item - fprintf(logfp,"%s - %d\t%s\t%d,%d,%s%s", - timestring, md->class_, type, nameid, amount, mapname, RETCODE); - - } else { - //We log Extended item - fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s%s", - timestring, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname, RETCODE); - } - fclose(logfp); - return 1; //Logged -} - -int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount) -{ -// FILE *logfp; - if(!log_config.enable_logs || (log_config.zeny!=1 && abs(amount) 0) - { - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `src_id`, `type`, `amount`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%s')", - log_config.log_zeny_db, sd->status.char_id, src_sd->status.char_id, type, amount, mapindex_id2name(sd->mapindex)); - if(mysql_query(&logmysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - return 1; - } -#endif -// if((logfp=fopen(log_config.log_zeny,"a+")) == NULL) -// return 0; -// time(&curtime); -// strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); -// fprintf(logfp,"%s - %s[%d]\t%s[%d]\t%d\t%s", timestring, sd->status.name, sd->status.account_id, target_sd->status.name, target_sd->status.account_id, sd->deal.zeny, RETCODE); -// fclose(logfp); -// return 1; - return 0; -} - -int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp) -{ - FILE *logfp; - - if(!log_config.enable_logs) - return 0; - nullpo_retr(0, sd); -#ifndef TXT_ONLY - if(log_config.sql_logs > 0) - { - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`mvp_date`, `kill_char_id`, `monster_id`, `prize`, `mvpexp`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%s') ", log_config.log_mvpdrop_db, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], mapindex_id2name(sd->mapindex)); - if(mysql_query(&logmysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - return 1; - } -#endif - if((logfp=fopen(log_config.log_mvpdrop,"a+")) == NULL) - return 0; - time(&curtime); - strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); - fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d,%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], RETCODE); - fclose(logfp); - return 0; -} - - -int log_atcommand(struct map_session_data *sd, const char *message) -{ - FILE *logfp; -#ifndef TXT_ONLY - char t_name[NAME_LENGTH*2]; - char t_msg[MESSAGE_SIZE*2+1]; //These are the contents of an @ call, so there shouldn't be overflow danger here? -#endif - - if(!log_config.enable_logs) - return 0; - nullpo_retr(0, sd); -#ifndef TXT_ONLY - if(log_config.sql_logs > 0) - { - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`atcommand_date`, `account_id`, `char_id`, `char_name`, `map`, `command`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ", - log_config.log_gm_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message)); - if(mysql_query(&logmysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - return 1; - } -#endif - if((logfp=fopen(log_config.log_gm,"a+")) == NULL) - return 0; - time(&curtime); - strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); - fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE); - fclose(logfp); - return 1; -} - -int log_npc(struct map_session_data *sd, const char *message) -{ //[Lupus] - FILE *logfp; -#ifndef TXT_ONLY - char t_name[NAME_LENGTH*2]; - char t_msg[255+1]; //it's 255 chars MAX. -#endif - - if(!log_config.enable_logs) - return 0; - nullpo_retr(0, sd); -#ifndef TXT_ONLY - if(log_config.sql_logs > 0) - { - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`npc_date`, `account_id`, `char_id`, `char_name`, `map`, `mes`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ", - log_config.log_npc_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message)); - if(mysql_query(&logmysql_handle, tmp_sql)) - { - ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - return 1; - } -#endif - if((logfp=fopen(log_config.log_npc,"a+")) == NULL) - return 0; - time(&curtime); - strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); - fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE); - fclose(logfp); - return 1; -} - -//ChatLogging -// Log CHAT (currently only: Party, Guild, Whisper) -// LOGGING FILTERS [Lupus] -//============================================================= -//0 = Don't log at all -//1 = Log any chat messages -//Advanced Filter Bits: || -//2 - Log Whisper messages -//3 - Log Party messages -//4 - Log Guild messages -//5 - Log Common messages (not implemented) -//6 - Don't log when WOE is on -//Example: -//log_chat: 1 = logs ANY messages -//log_chat: 6 = logs both Whisper & Party messages -//log_chat: 8 = logs only Guild messages -//log_chat: 18 = logs only Whisper, when WOE is off - -int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message){ - FILE *logfp; -#ifndef TXT_ONLY - char t_charname[NAME_LENGTH*2]; - char t_msg[MESSAGE_SIZE*2+1]; //Chat line fully escaped, with an extra space just in case. -#endif - - //Check ON/OFF - if(log_config.chat <= 0) - return 0; //Deactivated - -#ifndef TXT_ONLY - if(log_config.sql_logs > 0){ - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `type`, `type_id`, `src_charid`, `src_accountid`, `src_map`, `src_map_x`, `src_map_y`, `dst_charname`, `message`) VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', '%s', '%s')", - log_config.log_chat_db, type, type_id, src_charid, src_accid, map, x, y, jstrescapecpy(t_charname, dst_charname), jstrescapecpy(t_msg, message)); - - if(mysql_query(&logmysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - return 1; - } -#endif - if((logfp = fopen(log_config.log_chat, "a+")) == NULL) - return 0; - time(&curtime); - strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); - //DATE - type,type_id,src_charid,src_accountid,src_map,src_x,src_y,dst_charname,message - fprintf(logfp, "%s - %s,%d,%d,%d,%s,%d,%d,%s,%s%s", - timestring, type, type_id, src_charid, src_accid, map, x, y, dst_charname, message, RETCODE); - fclose(logfp); - return 1; -} - - -void log_set_defaults(void) -{ - malloc_set(&log_config, 0, sizeof(log_config)); - - //LOG FILTER Default values - log_config.refine_items_log = 5; //log refined items, with refine >= +7 - log_config.rare_items_log = 100; //log rare items. drop chance <= 1% - log_config.price_items_log = 1000; //1000z - log_config.amount_items_log = 100; -} - -int log_config_read(char *cfgName) -{ - static int count = 0; - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - if ((count++) == 0) - log_set_defaults(); - - if((fp = fopen(cfgName, "r")) == NULL) - { - ShowError("Log configuration file not found at: %s\n", cfgName); - return 1; - } - - while(fgets(line, sizeof(line) -1, fp)) - { - if(line[0] == '/' && line[1] == '/') - continue; - - if(sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) - { - if(strcmpi(w1,"enable_logs") == 0) { - log_config.enable_logs = (atoi(w2)); - if (log_config.enable_logs&1) //Log everything. - log_config.enable_logs=0xFFFFFFFF; - } else if(strcmpi(w1,"sql_logs") == 0) { - log_config.sql_logs = (atoi(w2)); -//start of common filter settings - } else if(strcmpi(w1,"rare_items_log") == 0) { - log_config.rare_items_log = (atoi(w2)); - } else if(strcmpi(w1,"refine_items_log") == 0) { - log_config.refine_items_log = (atoi(w2)); - } else if(strcmpi(w1,"price_items_log") == 0) { - log_config.price_items_log = (atoi(w2)); - } else if(strcmpi(w1,"amount_items_log") == 0) { - log_config.amount_items_log = (atoi(w2)); -//end of common filter settings - } else if(strcmpi(w1,"log_branch") == 0) { - log_config.branch = (atoi(w2)); - } else if(strcmpi(w1,"log_filter") == 0) { - log_config.filter = (atoi(w2)); - } else if(strcmpi(w1,"log_zeny") == 0) { - log_config.zeny = (atoi(w2)); - } else if(strcmpi(w1,"log_gm") == 0) { - log_config.gm = (atoi(w2)); - } else if(strcmpi(w1,"log_npc") == 0) { - log_config.npc = (atoi(w2)); - } else if(strcmpi(w1, "log_chat") == 0) { - log_config.chat = (atoi(w2)); - } else if(strcmpi(w1,"log_mvpdrop") == 0) { - log_config.mvpdrop = (atoi(w2)); - } - -#ifndef TXT_ONLY - else if(strcmpi(w1, "log_branch_db") == 0) { - strcpy(log_config.log_branch_db, w2); - if(log_config.branch == 1) - ShowNotice("Logging Dead Branch Usage to table `%s`\n", w2); - } else if(strcmpi(w1, "log_pick_db") == 0) { - strcpy(log_config.log_pick_db, w2); - if(log_config.filter) - ShowNotice("Logging Item Picks to table `%s`\n", w2); - } else if(strcmpi(w1, "log_zeny_db") == 0) { - strcpy(log_config.log_zeny_db, w2); - if(log_config.zeny == 1) - ShowNotice("Logging Zeny to table `%s`\n", w2); - } else if(strcmpi(w1, "log_mvpdrop_db") == 0) { - strcpy(log_config.log_mvpdrop_db, w2); - if(log_config.mvpdrop == 1) - ShowNotice("Logging MVP Drops to table `%s`\n", w2); - } else if(strcmpi(w1, "log_gm_db") == 0) { - strcpy(log_config.log_gm_db, w2); - if(log_config.gm > 0) - ShowNotice("Logging GM Level %d Commands to table `%s`\n", log_config.gm, w2); - } else if(strcmpi(w1, "log_npc_db") == 0) { - strcpy(log_config.log_npc_db, w2); - if(log_config.npc > 0) - ShowNotice("Logging NPC 'logmes' to table `%s`\n", w2); - } else if(strcmpi(w1, "log_chat_db") == 0) { - strcpy(log_config.log_chat_db, w2); - if(log_config.chat > 0) - ShowNotice("Logging CHAT to table `%s`\n", w2); - } -#endif - - else if(strcmpi(w1, "log_branch_file") == 0) { - strcpy(log_config.log_branch, w2); - if(log_config.branch > 0 && log_config.sql_logs < 1) - ShowNotice("Logging Dead Branch Usage to file `%s`.txt\n", w2); - } else if(strcmpi(w1, "log_pick_file") == 0) { - strcpy(log_config.log_pick, w2); - if(log_config.filter > 0 && log_config.sql_logs < 1) - ShowNotice("Logging Item Picks to file `%s`.txt\n", w2); - } else if(strcmpi(w1, "log_zeny_file") == 0) { - strcpy(log_config.log_zeny, w2); - if(log_config.zeny > 0 && log_config.sql_logs < 1) - ShowNotice("Logging Zeny to file `%s`.txt\n", w2); - } else if(strcmpi(w1, "log_mvpdrop_file") == 0) { - strcpy(log_config.log_mvpdrop, w2); - if(log_config.mvpdrop > 0 && log_config.sql_logs < 1) - ShowNotice("Logging MVP Drops to file `%s`.txt\n", w2); - } else if(strcmpi(w1, "log_gm_file") == 0) { - strcpy(log_config.log_gm, w2); - if(log_config.gm > 0 && log_config.sql_logs < 1) - ShowNotice("Logging GM Level %d Commands to file `%s`.txt\n", log_config.gm, w2); - } else if(strcmpi(w1, "log_npc_file") == 0) { - strcpy(log_config.log_npc, w2); - if(log_config.npc > 0 && log_config.sql_logs < 1) - ShowNotice("Logging NPC 'logmes' to file `%s`.txt\n", w2); - } else if(strcmpi(w1, "log_chat_file") == 0) { - strcpy(log_config.log_chat, w2); - if(log_config.chat > 0 && log_config.sql_logs < 1) - ShowNotice("Logging CHAT to file `%s`.txt\n", w2); - //support the import command, just like any other config - } else if(strcmpi(w1,"import") == 0) { - log_config_read(w2); - } - } - } - - fclose(fp); - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +// Logging functions by Azndragon & Codemaster +#include +#include +#include + +#include "../common/strlib.h" +#include "../common/nullpo.h" +#include "../common/showmsg.h" +#include "../common/malloc.h" +#include "itemdb.h" +#include "map.h" +#include "log.h" + +#ifndef SQL_DEBUG + +#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin] + +#else + +#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) + +#endif + +struct Log_Config log_config; + +char timestring[255]; +time_t curtime; + +//FILTER OPTIONS +//0 = Don't log +//1 = Log any item +//Bits: || +//2 - Healing items (0) +//3 - Etc Items(3) + Arrows (10) +//4 - Usable Items(2) + Scrolls,Lures(11) +//5 - Weapon(4) +//6 - Shields,Armor,Headgears,Accessories,etc(5) +//7 - Cards(6) +//8 - Pet Accessories(8) + Eggs(7) (well, monsters don't drop 'em but we'll use the same system for ALL logs) +//9 - Log expensive items ( >= price_log) +//10 - Log big amount of items ( >= amount_log) +//11 - Log refined items (if their refine >= refine_log ) +//12 - Log rare items (if their drop chance <= rare_log ) + +//check if this item should be logged according the settings +int should_log_item(int filter, int nameid, int amount) { + struct item_data *item_data; + if ((item_data= itemdb_exists(nameid)) == NULL) return 0; + if ((filter&1) || // Filter = 1, we log any item + (filter&2 && item_data->type == IT_HEALING ) || + (filter&4 && (item_data->type == IT_ETC || item_data->type == IT_AMMO) ) || + (filter&8 && item_data->type == IT_USABLE ) || + (filter&16 && item_data->type == IT_WEAPON ) || + (filter&32 && item_data->type == IT_ARMOR ) || + (filter&64 && item_data->type == IT_CARD ) || + (filter&128 && (item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) ) || + (filter&256 && item_data->value_buy >= log_config.price_items_log ) || //expensive items + (filter&512 && abs(amount) >= log_config.amount_items_log ) || //big amount of items + (filter&2048 && ((item_data->maxchance <= log_config.rare_items_log) || item_data->nameid == 714) ) //Rare items or Emperium + ) return item_data->nameid; + + return 0; +} + +int log_branch(struct map_session_data *sd) +{ +#ifndef TXT_ONLY + char t_name[NAME_LENGTH*2]; +#endif + FILE *logfp; + + if(!log_config.enable_logs) + return 0; + nullpo_retr(0, sd); +#ifndef TXT_ONLY + if(log_config.sql_logs > 0) + { + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`branch_date`, `account_id`, `char_id`, `char_name`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%s')", + log_config.log_branch_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex)); + if(mysql_query(&logmysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + return 1; + } +#endif + if((logfp=fopen(log_config.log_branch,"a+")) == NULL) + return 0; + time(&curtime); + strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); + fprintf(logfp,"%s - %s[%d:%d]\t%s%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex), RETCODE); + fclose(logfp); + return 1; +} + + +int log_pick_pc(struct map_session_data *sd, const char *type, int nameid, int amount, struct item *itm) +{ + FILE *logfp; + char *mapname; + + nullpo_retr(0, sd); + //Should we log this item? [Lupus] + if (!should_log_item(log_config.filter,nameid, amount)) + return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus] + + mapname = (char*)mapindex_id2name(sd->mapindex); + + if(mapname==NULL) + mapname=""; + +#ifndef TXT_ONLY + if(log_config.sql_logs > 0) + { + if (itm==NULL) { + //We log common item + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')", + log_config.log_pick_db, sd->status.char_id, type, nameid, amount, mapname); + } else { + //We log Extended item + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')", + log_config.log_pick_db, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname); + } + + if(mysql_query(&logmysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + return 1; + } +#endif + if((logfp=fopen(log_config.log_pick,"a+")) == NULL) + return 0; + time(&curtime); + strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); + + if (itm==NULL) { + //We log common item + fprintf(logfp,"%s - %d\t%s\t%d,%d,%s%s", + timestring, sd->status.char_id, type, nameid, amount, mapname, RETCODE); + + } else { + //We log Extended item + fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s%s", + timestring, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname, RETCODE); + } + fclose(logfp); + return 1; //Logged +} + +//Mob picked item +int log_pick_mob(struct mob_data *md, const char *type, int nameid, int amount, struct item *itm) +{ + FILE *logfp; + char *mapname; + + nullpo_retr(0, md); + //Should we log this item? [Lupus] + if (!should_log_item(log_config.filter,nameid, amount)) + return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus] + + //either PLAYER or MOB (here we get map name and objects ID) + mapname = map[md->bl.m].name; + if(mapname==NULL) + mapname=""; + +#ifndef TXT_ONLY + if(log_config.sql_logs > 0) + { + if (itm==NULL) { + //We log common item + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')", + log_config.log_pick_db, md->class_, type, nameid, amount, mapname); + } else { + //We log Extended item + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')", + log_config.log_pick_db, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname); + } + + if(mysql_query(&logmysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + return 1; + } +#endif + if((logfp=fopen(log_config.log_pick,"a+")) == NULL) + return 0; + time(&curtime); + strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); + + if (itm==NULL) { + //We log common item + fprintf(logfp,"%s - %d\t%s\t%d,%d,%s%s", + timestring, md->class_, type, nameid, amount, mapname, RETCODE); + + } else { + //We log Extended item + fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s%s", + timestring, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname, RETCODE); + } + fclose(logfp); + return 1; //Logged +} + +int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount) +{ +// FILE *logfp; + if(!log_config.enable_logs || (log_config.zeny!=1 && abs(amount) 0) + { + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `src_id`, `type`, `amount`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%s')", + log_config.log_zeny_db, sd->status.char_id, src_sd->status.char_id, type, amount, mapindex_id2name(sd->mapindex)); + if(mysql_query(&logmysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + return 1; + } +#endif +// if((logfp=fopen(log_config.log_zeny,"a+")) == NULL) +// return 0; +// time(&curtime); +// strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); +// fprintf(logfp,"%s - %s[%d]\t%s[%d]\t%d\t%s", timestring, sd->status.name, sd->status.account_id, target_sd->status.name, target_sd->status.account_id, sd->deal.zeny, RETCODE); +// fclose(logfp); +// return 1; + return 0; +} + +int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp) +{ + FILE *logfp; + + if(!log_config.enable_logs) + return 0; + nullpo_retr(0, sd); +#ifndef TXT_ONLY + if(log_config.sql_logs > 0) + { + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`mvp_date`, `kill_char_id`, `monster_id`, `prize`, `mvpexp`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%s') ", log_config.log_mvpdrop_db, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], mapindex_id2name(sd->mapindex)); + if(mysql_query(&logmysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + return 1; + } +#endif + if((logfp=fopen(log_config.log_mvpdrop,"a+")) == NULL) + return 0; + time(&curtime); + strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); + fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d,%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], RETCODE); + fclose(logfp); + return 0; +} + + +int log_atcommand(struct map_session_data *sd, const char *message) +{ + FILE *logfp; +#ifndef TXT_ONLY + char t_name[NAME_LENGTH*2]; + char t_msg[MESSAGE_SIZE*2+1]; //These are the contents of an @ call, so there shouldn't be overflow danger here? +#endif + + if(!log_config.enable_logs) + return 0; + nullpo_retr(0, sd); +#ifndef TXT_ONLY + if(log_config.sql_logs > 0) + { + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`atcommand_date`, `account_id`, `char_id`, `char_name`, `map`, `command`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ", + log_config.log_gm_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message)); + if(mysql_query(&logmysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + return 1; + } +#endif + if((logfp=fopen(log_config.log_gm,"a+")) == NULL) + return 0; + time(&curtime); + strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); + fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE); + fclose(logfp); + return 1; +} + +int log_npc(struct map_session_data *sd, const char *message) +{ //[Lupus] + FILE *logfp; +#ifndef TXT_ONLY + char t_name[NAME_LENGTH*2]; + char t_msg[255+1]; //it's 255 chars MAX. +#endif + + if(!log_config.enable_logs) + return 0; + nullpo_retr(0, sd); +#ifndef TXT_ONLY + if(log_config.sql_logs > 0) + { + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`npc_date`, `account_id`, `char_id`, `char_name`, `map`, `mes`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ", + log_config.log_npc_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message)); + if(mysql_query(&logmysql_handle, tmp_sql)) + { + ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + return 1; + } +#endif + if((logfp=fopen(log_config.log_npc,"a+")) == NULL) + return 0; + time(&curtime); + strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); + fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE); + fclose(logfp); + return 1; +} + +//ChatLogging +// Log CHAT (currently only: Party, Guild, Whisper) +// LOGGING FILTERS [Lupus] +//============================================================= +//0 = Don't log at all +//1 = Log any chat messages +//Advanced Filter Bits: || +//2 - Log Whisper messages +//3 - Log Party messages +//4 - Log Guild messages +//5 - Log Common messages (not implemented) +//6 - Don't log when WOE is on +//Example: +//log_chat: 1 = logs ANY messages +//log_chat: 6 = logs both Whisper & Party messages +//log_chat: 8 = logs only Guild messages +//log_chat: 18 = logs only Whisper, when WOE is off + +int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message){ + FILE *logfp; +#ifndef TXT_ONLY + char t_charname[NAME_LENGTH*2]; + char t_msg[MESSAGE_SIZE*2+1]; //Chat line fully escaped, with an extra space just in case. +#endif + + //Check ON/OFF + if(log_config.chat <= 0) + return 0; //Deactivated + +#ifndef TXT_ONLY + if(log_config.sql_logs > 0){ + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `type`, `type_id`, `src_charid`, `src_accountid`, `src_map`, `src_map_x`, `src_map_y`, `dst_charname`, `message`) VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', '%s', '%s')", + log_config.log_chat_db, type, type_id, src_charid, src_accid, map, x, y, jstrescapecpy(t_charname, dst_charname), jstrescapecpy(t_msg, message)); + + if(mysql_query(&logmysql_handle, tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + return 1; + } +#endif + if((logfp = fopen(log_config.log_chat, "a+")) == NULL) + return 0; + time(&curtime); + strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); + //DATE - type,type_id,src_charid,src_accountid,src_map,src_x,src_y,dst_charname,message + fprintf(logfp, "%s - %s,%d,%d,%d,%s,%d,%d,%s,%s%s", + timestring, type, type_id, src_charid, src_accid, map, x, y, dst_charname, message, RETCODE); + fclose(logfp); + return 1; +} + + +void log_set_defaults(void) +{ + malloc_set(&log_config, 0, sizeof(log_config)); + + //LOG FILTER Default values + log_config.refine_items_log = 5; //log refined items, with refine >= +7 + log_config.rare_items_log = 100; //log rare items. drop chance <= 1% + log_config.price_items_log = 1000; //1000z + log_config.amount_items_log = 100; +} + +int log_config_read(char *cfgName) +{ + static int count = 0; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + if ((count++) == 0) + log_set_defaults(); + + if((fp = fopen(cfgName, "r")) == NULL) + { + ShowError("Log configuration file not found at: %s\n", cfgName); + return 1; + } + + while(fgets(line, sizeof(line) -1, fp)) + { + if(line[0] == '/' && line[1] == '/') + continue; + + if(sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) + { + if(strcmpi(w1,"enable_logs") == 0) { + log_config.enable_logs = (atoi(w2)); + if (log_config.enable_logs&1) //Log everything. + log_config.enable_logs=0xFFFFFFFF; + } else if(strcmpi(w1,"sql_logs") == 0) { + log_config.sql_logs = (atoi(w2)); +//start of common filter settings + } else if(strcmpi(w1,"rare_items_log") == 0) { + log_config.rare_items_log = (atoi(w2)); + } else if(strcmpi(w1,"refine_items_log") == 0) { + log_config.refine_items_log = (atoi(w2)); + } else if(strcmpi(w1,"price_items_log") == 0) { + log_config.price_items_log = (atoi(w2)); + } else if(strcmpi(w1,"amount_items_log") == 0) { + log_config.amount_items_log = (atoi(w2)); +//end of common filter settings + } else if(strcmpi(w1,"log_branch") == 0) { + log_config.branch = (atoi(w2)); + } else if(strcmpi(w1,"log_filter") == 0) { + log_config.filter = (atoi(w2)); + } else if(strcmpi(w1,"log_zeny") == 0) { + log_config.zeny = (atoi(w2)); + } else if(strcmpi(w1,"log_gm") == 0) { + log_config.gm = (atoi(w2)); + } else if(strcmpi(w1,"log_npc") == 0) { + log_config.npc = (atoi(w2)); + } else if(strcmpi(w1, "log_chat") == 0) { + log_config.chat = (atoi(w2)); + } else if(strcmpi(w1,"log_mvpdrop") == 0) { + log_config.mvpdrop = (atoi(w2)); + } + +#ifndef TXT_ONLY + else if(strcmpi(w1, "log_branch_db") == 0) { + strcpy(log_config.log_branch_db, w2); + if(log_config.branch == 1) + ShowNotice("Logging Dead Branch Usage to table `%s`\n", w2); + } else if(strcmpi(w1, "log_pick_db") == 0) { + strcpy(log_config.log_pick_db, w2); + if(log_config.filter) + ShowNotice("Logging Item Picks to table `%s`\n", w2); + } else if(strcmpi(w1, "log_zeny_db") == 0) { + strcpy(log_config.log_zeny_db, w2); + if(log_config.zeny == 1) + ShowNotice("Logging Zeny to table `%s`\n", w2); + } else if(strcmpi(w1, "log_mvpdrop_db") == 0) { + strcpy(log_config.log_mvpdrop_db, w2); + if(log_config.mvpdrop == 1) + ShowNotice("Logging MVP Drops to table `%s`\n", w2); + } else if(strcmpi(w1, "log_gm_db") == 0) { + strcpy(log_config.log_gm_db, w2); + if(log_config.gm > 0) + ShowNotice("Logging GM Level %d Commands to table `%s`\n", log_config.gm, w2); + } else if(strcmpi(w1, "log_npc_db") == 0) { + strcpy(log_config.log_npc_db, w2); + if(log_config.npc > 0) + ShowNotice("Logging NPC 'logmes' to table `%s`\n", w2); + } else if(strcmpi(w1, "log_chat_db") == 0) { + strcpy(log_config.log_chat_db, w2); + if(log_config.chat > 0) + ShowNotice("Logging CHAT to table `%s`\n", w2); + } +#endif + + else if(strcmpi(w1, "log_branch_file") == 0) { + strcpy(log_config.log_branch, w2); + if(log_config.branch > 0 && log_config.sql_logs < 1) + ShowNotice("Logging Dead Branch Usage to file `%s`.txt\n", w2); + } else if(strcmpi(w1, "log_pick_file") == 0) { + strcpy(log_config.log_pick, w2); + if(log_config.filter > 0 && log_config.sql_logs < 1) + ShowNotice("Logging Item Picks to file `%s`.txt\n", w2); + } else if(strcmpi(w1, "log_zeny_file") == 0) { + strcpy(log_config.log_zeny, w2); + if(log_config.zeny > 0 && log_config.sql_logs < 1) + ShowNotice("Logging Zeny to file `%s`.txt\n", w2); + } else if(strcmpi(w1, "log_mvpdrop_file") == 0) { + strcpy(log_config.log_mvpdrop, w2); + if(log_config.mvpdrop > 0 && log_config.sql_logs < 1) + ShowNotice("Logging MVP Drops to file `%s`.txt\n", w2); + } else if(strcmpi(w1, "log_gm_file") == 0) { + strcpy(log_config.log_gm, w2); + if(log_config.gm > 0 && log_config.sql_logs < 1) + ShowNotice("Logging GM Level %d Commands to file `%s`.txt\n", log_config.gm, w2); + } else if(strcmpi(w1, "log_npc_file") == 0) { + strcpy(log_config.log_npc, w2); + if(log_config.npc > 0 && log_config.sql_logs < 1) + ShowNotice("Logging NPC 'logmes' to file `%s`.txt\n", w2); + } else if(strcmpi(w1, "log_chat_file") == 0) { + strcpy(log_config.log_chat, w2); + if(log_config.chat > 0 && log_config.sql_logs < 1) + ShowNotice("Logging CHAT to file `%s`.txt\n", w2); + //support the import command, just like any other config + } else if(strcmpi(w1,"import") == 0) { + log_config_read(w2); + } + } + } + + fclose(fp); + return 0; +} diff --git a/src/map/log.h b/src/map/log.h index 32f3853ef..1cc2247db 100644 --- a/src/map/log.h +++ b/src/map/log.h @@ -1,43 +1,43 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _LOG_H_ -#define _LOG_H_ - -#include "map.h" - -#ifndef TXT_ONLY - -extern char db_server_logdb[32]; - -#endif //NOT TXT_ONLY - -//New logs -int log_pick_pc(struct map_session_data *sd, const char *type, int nameid, int amount, struct item *itm); -int log_pick_mob(struct mob_data *md, const char *type, int nameid, int amount, struct item *itm); -int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount); - -int log_npc(struct map_session_data *sd, const char *message); -int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message); -int log_atcommand(struct map_session_data *sd, const char *message); - -//Old, but useful logs -int log_branch(struct map_session_data *sd); -int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp); - -int log_config_read(char *cfgName); - -int should_log_item(int filter, int nameid, int amount); //log filter check - -extern struct Log_Config { - int enable_logs, filter; - int sql_logs; - int rare_items_log,refine_items_log,price_items_log,amount_items_log; //for filter - int branch, drop, mvpdrop, zeny, gm, npc, chat; - char log_branch[32], log_pick[32], log_zeny[32], log_mvpdrop[32], log_gm[32], log_npc[32], log_chat[32]; - char log_branch_db[32], log_pick_db[32], log_zeny_db[32], log_mvpdrop_db[32], log_gm_db[32], log_npc_db[32], log_chat_db[32]; - int uptime; - char log_uptime[32]; -} log_config; - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _LOG_H_ +#define _LOG_H_ + +#include "map.h" + +#ifndef TXT_ONLY + +extern char db_server_logdb[32]; + +#endif //NOT TXT_ONLY + +//New logs +int log_pick_pc(struct map_session_data *sd, const char *type, int nameid, int amount, struct item *itm); +int log_pick_mob(struct mob_data *md, const char *type, int nameid, int amount, struct item *itm); +int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount); + +int log_npc(struct map_session_data *sd, const char *message); +int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message); +int log_atcommand(struct map_session_data *sd, const char *message); + +//Old, but useful logs +int log_branch(struct map_session_data *sd); +int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp); + +int log_config_read(char *cfgName); + +int should_log_item(int filter, int nameid, int amount); //log filter check + +extern struct Log_Config { + int enable_logs, filter; + int sql_logs; + int rare_items_log,refine_items_log,price_items_log,amount_items_log; //for filter + int branch, drop, mvpdrop, zeny, gm, npc, chat; + char log_branch[32], log_pick[32], log_zeny[32], log_mvpdrop[32], log_gm[32], log_npc[32], log_chat[32]; + char log_branch_db[32], log_pick_db[32], log_zeny_db[32], log_mvpdrop_db[32], log_gm_db[32], log_npc_db[32], log_chat_db[32]; + int uptime; + char log_uptime[32]; +} log_config; + +#endif diff --git a/src/map/mail.c b/src/map/mail.c index 316bc9c18..cd1f63b1e 100644 --- a/src/map/mail.c +++ b/src/map/mail.c @@ -1,356 +1,356 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef TXT_ONLY -// Mail System for eAthena SQL -// Created by Valaris -// moved all strings to msg_athena.conf [Lupus] - -#include -#include -#include - -#include "../common/strlib.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/showmsg.h" - -#include "map.h" -#include "clif.h" -#include "chrif.h" -#include "intif.h" -#include "atcommand.h" -#include "pc.h" -#include "mail.h" - -#ifndef TXT_ONLY - #ifndef SQL_DEBUG - - #define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) - - #else - - #define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) - - #endif -#endif - -int MAIL_CHECK_TIME = 120000; -int mail_timer; -//extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others) - -int mail_check(struct map_session_data *sd,int type) -{ - int i = 0, new_ = 0, priority = 0; - char message[80]; - - nullpo_retr (0, sd); - - sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`read_flag`,`priority`,`check_flag` " - "FROM `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id`", mail_db, sd->status.account_id); - - if (mysql_query(&mail_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - - mail_res = mysql_store_result(&mail_handle); - if(mail_res) { - if (mysql_num_rows(mail_res) == 0) { - //clif_displaymessage(sd->fd,"You have no messages."); - clif_displaymessage(sd->fd, msg_txt(516)); - - mysql_free_result(mail_res); - return 0; - } - - while ((mail_row = mysql_fetch_row(mail_res))) { - i++; - if(!atoi(mail_row[5])) { - sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0])); - if(mysql_query(&mail_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - if(!atoi(mail_row[3])) { - new_++; - if(atoi(mail_row[4])) - priority++; - if(type==2 || type==3) { - if(atoi(mail_row[4])) { - snprintf(message, 80, msg_txt(511), i, mail_row[2]); - clif_displaymessage(sd->fd, message); - } else { - //sprintf(message, "%d - From : %s (New)", i, mail_row[2]); - snprintf(message, 80, msg_txt(512), i, mail_row[2]); - clif_displaymessage(sd->fd, message); - } - } - } else if(type==2){ - snprintf(message, 80, msg_txt(513), i, mail_row[2]); - clif_displaymessage(sd->fd, message); - } - } - - mysql_free_result(mail_res); - } else { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - - if(i>0 && new_>0 && type==1) { - //sprintf(message, "You have %d new messages.", new_); - sprintf(message, msg_txt(514), new_); - - clif_displaymessage(sd->fd, jstrescape(message)); - } - if(i>0 && new_>0 && priority>0 && type==1) { - //sprintf(message, "You have %d unread priority messages.", priority); - sprintf(message, msg_txt(515), priority); - clif_displaymessage(sd->fd, jstrescape(message)); - } - if(!new_) { - //clif_displaymessage(sd->fd, "You have no new messages."); - clif_displaymessage(sd->fd, msg_txt(516)); - } - - return 0; -} - -int mail_read(struct map_session_data *sd, int message_id) -{ - - char message[80]; - - nullpo_retr (0, sd); - - sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`message`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1); - - if (mysql_query(&mail_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - - mail_res = mysql_store_result(&mail_handle); - if(mail_res) { - if (mysql_num_rows(mail_res) == 0) { - mysql_free_result(mail_res); - //clif_displaymessage(sd->fd, "Message not found."); - clif_displaymessage(sd->fd, msg_txt(517)); - return 0; - } - - if ((mail_row = mysql_fetch_row(mail_res))) { - if(!atoi(mail_row[6])) { - sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0])); - if(mysql_query(&mail_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - //sprintf(message, "Reading message from %s", mail_row[2]); - sprintf(message, msg_txt(518), mail_row[2]); - clif_displaymessage(sd->fd, jstrescape(message)); - - sprintf(message, "%s", mail_row[3]); - clif_displaymessage(sd->fd, jstrescape(message)); - - sprintf(tmp_sql,"UPDATE `%s` SET `read_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0])); - if(mysql_query(&mail_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - - mysql_free_result(mail_res); - - } else { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - return 0; -} - -int mail_delete(struct map_session_data *sd, int message_id) -{ - nullpo_retr (0, sd); - - sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1); - - if (mysql_query(&mail_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - - mail_res = mysql_store_result(&mail_handle); - if(mail_res) { - if (mysql_num_rows(mail_res) == 0) { - mysql_free_result(mail_res); - //clif_displaymessage(sd->fd, "Message not found."); - clif_displaymessage(sd->fd, msg_txt(517)); - return 0; - } - - if ((mail_row = mysql_fetch_row(mail_res))) { - if(!atoi(mail_row[2]) && atoi(mail_row[3])) { - mysql_free_result(mail_res); - //clif_displaymessage(sd->fd,"Cannot delete unread priority mail."); - clif_displaymessage(sd->fd,msg_txt(519)); - - return 0; - } - if(!atoi(mail_row[4])) { - mysql_free_result(mail_res); - //clif_displaymessage(sd->fd,"You have recieved new mail, use @listmail before deleting."); - clif_displaymessage(sd->fd,msg_txt(520)); - return 0; - } - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `message_id` = \"%d\"", mail_db, atoi(mail_row[0])); - if(mysql_query(&mail_handle, tmp_sql) ) { - mysql_free_result(mail_res); - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - //else clif_displaymessage(sd->fd,"Message deleted."); - else clif_displaymessage(sd->fd,msg_txt(521)); - } - - mysql_free_result(mail_res); - - } else { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - return 0; -} - -int mail_send(struct map_session_data *sd, char *name, char *message, int flag) -{ - nullpo_retr (0, sd); - - if(pc_isGM(sd) < 80 && sd->mail_counter > 0) { - //clif_displaymessage(sd->fd,"You must wait 10 minutes before sending another message"); - clif_displaymessage(sd->fd,msg_txt(522)); - return 0; - } - - if(strcmp(name,"*")==0) { - if(pc_isGM(sd) < 80) { - //clif_displaymessage(sd->fd, "Access Denied."); - clif_displaymessage(sd->fd, msg_txt(523)); - return 0; - } - else - sprintf(tmp_sql,"SELECT DISTINCT `account_id` FROM `%s` WHERE `account_id` <> '%d' ORDER BY `account_id`", char_db, sd->status.account_id); - } - else - sprintf(tmp_sql,"SELECT `account_id`,`name` FROM `%s` WHERE `name` = \"%s\"", char_db, jstrescape(name)); - - if (mysql_query(&mail_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - - mail_res = mysql_store_result(&mail_handle); - if(mail_res) { - if (mysql_num_rows(mail_res) == 0) { - mysql_free_result(mail_res); - //clif_displaymessage(sd->fd,"Character does not exist."); - clif_displaymessage(sd->fd,msg_txt(524)); - return 0; - } - - while ((mail_row = mysql_fetch_row(mail_res))) { - if(strcmp(name,"*")==0) { - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`from_account_id`,`from_char_name`,`message`,`priority`)" - " VALUES ('%d', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), sd->status.account_id, sd->status.name, jstrescape(message), flag); - } - else { - sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`to_char_name`,`from_account_id`,`from_char_name`,`message`,`priority`)" - " VALUES ('%d', '%s', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), mail_row[1], sd->status.account_id, sd->status.name, jstrescape(message), flag); - if(pc_isGM(sd) < 80) - sd->mail_counter=5; - } - - if(mysql_query(&mail_handle, tmp_sql) ) { - mysql_free_result(mail_res); - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 0; - } - } - } - - //clif_displaymessage(sd->fd,"Mail has been sent."); - clif_displaymessage(sd->fd,msg_txt(525)); - - return 0; -} - -static int mail_check_timer_sub(struct map_session_data *sd, va_list va) -{ - int id = va_arg(va, int); - if(pc_isGM(sd) < 80 && sd->mail_counter > 0) - sd->mail_counter--; - if(sd->status.account_id==id) - clif_displaymessage(sd->fd, msg_txt(526)); //you got new email. - return 0; -} - -int mail_check_timer(int tid,unsigned int tick,int id,int data) -{ - if(mail_timer != tid) - return 0; - - sprintf(tmp_sql,"SELECT DISTINCT `to_account_id` FROM `%s` WHERE `read_flag` = '0' AND `check_flag` = '0'", mail_db); - - if (mysql_query(&mail_handle, tmp_sql)) { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0); - return 0; - } - - mail_res = mysql_store_result(&mail_handle); - - if (mail_res) { - if (mysql_num_rows(mail_res) == 0) { - mysql_free_result(mail_res); - mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0); - return 0; - } - - while ((mail_row = mysql_fetch_row(mail_res))) - clif_foreachclient(mail_check_timer_sub, atoi(mail_row[0])); - } - - sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `check_flag`= '0' ", mail_db); - if(mysql_query(&mail_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - - mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0); - return 0; -} - -int do_init_mail(void) -{ - add_timer_func_list(mail_check_timer,"mail_check_timer"); - mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0); - return 0; -} - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef TXT_ONLY +// Mail System for eAthena SQL +// Created by Valaris +// moved all strings to msg_athena.conf [Lupus] + +#include +#include +#include + +#include "../common/strlib.h" +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/nullpo.h" +#include "../common/showmsg.h" + +#include "map.h" +#include "clif.h" +#include "chrif.h" +#include "intif.h" +#include "atcommand.h" +#include "pc.h" +#include "mail.h" + +#ifndef TXT_ONLY + #ifndef SQL_DEBUG + + #define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) + + #else + + #define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y) + + #endif +#endif + +int MAIL_CHECK_TIME = 120000; +int mail_timer; +//extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others) + +int mail_check(struct map_session_data *sd,int type) +{ + int i = 0, new_ = 0, priority = 0; + char message[80]; + + nullpo_retr (0, sd); + + sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`read_flag`,`priority`,`check_flag` " + "FROM `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id`", mail_db, sd->status.account_id); + + if (mysql_query(&mail_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + + mail_res = mysql_store_result(&mail_handle); + if(mail_res) { + if (mysql_num_rows(mail_res) == 0) { + //clif_displaymessage(sd->fd,"You have no messages."); + clif_displaymessage(sd->fd, msg_txt(516)); + + mysql_free_result(mail_res); + return 0; + } + + while ((mail_row = mysql_fetch_row(mail_res))) { + i++; + if(!atoi(mail_row[5])) { + sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0])); + if(mysql_query(&mail_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + if(!atoi(mail_row[3])) { + new_++; + if(atoi(mail_row[4])) + priority++; + if(type==2 || type==3) { + if(atoi(mail_row[4])) { + snprintf(message, 80, msg_txt(511), i, mail_row[2]); + clif_displaymessage(sd->fd, message); + } else { + //sprintf(message, "%d - From : %s (New)", i, mail_row[2]); + snprintf(message, 80, msg_txt(512), i, mail_row[2]); + clif_displaymessage(sd->fd, message); + } + } + } else if(type==2){ + snprintf(message, 80, msg_txt(513), i, mail_row[2]); + clif_displaymessage(sd->fd, message); + } + } + + mysql_free_result(mail_res); + } else { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + + if(i>0 && new_>0 && type==1) { + //sprintf(message, "You have %d new messages.", new_); + sprintf(message, msg_txt(514), new_); + + clif_displaymessage(sd->fd, jstrescape(message)); + } + if(i>0 && new_>0 && priority>0 && type==1) { + //sprintf(message, "You have %d unread priority messages.", priority); + sprintf(message, msg_txt(515), priority); + clif_displaymessage(sd->fd, jstrescape(message)); + } + if(!new_) { + //clif_displaymessage(sd->fd, "You have no new messages."); + clif_displaymessage(sd->fd, msg_txt(516)); + } + + return 0; +} + +int mail_read(struct map_session_data *sd, int message_id) +{ + + char message[80]; + + nullpo_retr (0, sd); + + sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`message`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1); + + if (mysql_query(&mail_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + + mail_res = mysql_store_result(&mail_handle); + if(mail_res) { + if (mysql_num_rows(mail_res) == 0) { + mysql_free_result(mail_res); + //clif_displaymessage(sd->fd, "Message not found."); + clif_displaymessage(sd->fd, msg_txt(517)); + return 0; + } + + if ((mail_row = mysql_fetch_row(mail_res))) { + if(!atoi(mail_row[6])) { + sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0])); + if(mysql_query(&mail_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + //sprintf(message, "Reading message from %s", mail_row[2]); + sprintf(message, msg_txt(518), mail_row[2]); + clif_displaymessage(sd->fd, jstrescape(message)); + + sprintf(message, "%s", mail_row[3]); + clif_displaymessage(sd->fd, jstrescape(message)); + + sprintf(tmp_sql,"UPDATE `%s` SET `read_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0])); + if(mysql_query(&mail_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + + mysql_free_result(mail_res); + + } else { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + return 0; +} + +int mail_delete(struct map_session_data *sd, int message_id) +{ + nullpo_retr (0, sd); + + sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1); + + if (mysql_query(&mail_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + + mail_res = mysql_store_result(&mail_handle); + if(mail_res) { + if (mysql_num_rows(mail_res) == 0) { + mysql_free_result(mail_res); + //clif_displaymessage(sd->fd, "Message not found."); + clif_displaymessage(sd->fd, msg_txt(517)); + return 0; + } + + if ((mail_row = mysql_fetch_row(mail_res))) { + if(!atoi(mail_row[2]) && atoi(mail_row[3])) { + mysql_free_result(mail_res); + //clif_displaymessage(sd->fd,"Cannot delete unread priority mail."); + clif_displaymessage(sd->fd,msg_txt(519)); + + return 0; + } + if(!atoi(mail_row[4])) { + mysql_free_result(mail_res); + //clif_displaymessage(sd->fd,"You have recieved new mail, use @listmail before deleting."); + clif_displaymessage(sd->fd,msg_txt(520)); + return 0; + } + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `message_id` = \"%d\"", mail_db, atoi(mail_row[0])); + if(mysql_query(&mail_handle, tmp_sql) ) { + mysql_free_result(mail_res); + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + //else clif_displaymessage(sd->fd,"Message deleted."); + else clif_displaymessage(sd->fd,msg_txt(521)); + } + + mysql_free_result(mail_res); + + } else { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + return 0; +} + +int mail_send(struct map_session_data *sd, char *name, char *message, int flag) +{ + nullpo_retr (0, sd); + + if(pc_isGM(sd) < 80 && sd->mail_counter > 0) { + //clif_displaymessage(sd->fd,"You must wait 10 minutes before sending another message"); + clif_displaymessage(sd->fd,msg_txt(522)); + return 0; + } + + if(strcmp(name,"*")==0) { + if(pc_isGM(sd) < 80) { + //clif_displaymessage(sd->fd, "Access Denied."); + clif_displaymessage(sd->fd, msg_txt(523)); + return 0; + } + else + sprintf(tmp_sql,"SELECT DISTINCT `account_id` FROM `%s` WHERE `account_id` <> '%d' ORDER BY `account_id`", char_db, sd->status.account_id); + } + else + sprintf(tmp_sql,"SELECT `account_id`,`name` FROM `%s` WHERE `name` = \"%s\"", char_db, jstrescape(name)); + + if (mysql_query(&mail_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + + mail_res = mysql_store_result(&mail_handle); + if(mail_res) { + if (mysql_num_rows(mail_res) == 0) { + mysql_free_result(mail_res); + //clif_displaymessage(sd->fd,"Character does not exist."); + clif_displaymessage(sd->fd,msg_txt(524)); + return 0; + } + + while ((mail_row = mysql_fetch_row(mail_res))) { + if(strcmp(name,"*")==0) { + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`from_account_id`,`from_char_name`,`message`,`priority`)" + " VALUES ('%d', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), sd->status.account_id, sd->status.name, jstrescape(message), flag); + } + else { + sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`to_char_name`,`from_account_id`,`from_char_name`,`message`,`priority`)" + " VALUES ('%d', '%s', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), mail_row[1], sd->status.account_id, sd->status.name, jstrescape(message), flag); + if(pc_isGM(sd) < 80) + sd->mail_counter=5; + } + + if(mysql_query(&mail_handle, tmp_sql) ) { + mysql_free_result(mail_res); + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 0; + } + } + } + + //clif_displaymessage(sd->fd,"Mail has been sent."); + clif_displaymessage(sd->fd,msg_txt(525)); + + return 0; +} + +static int mail_check_timer_sub(struct map_session_data *sd, va_list va) +{ + int id = va_arg(va, int); + if(pc_isGM(sd) < 80 && sd->mail_counter > 0) + sd->mail_counter--; + if(sd->status.account_id==id) + clif_displaymessage(sd->fd, msg_txt(526)); //you got new email. + return 0; +} + +int mail_check_timer(int tid,unsigned int tick,int id,int data) +{ + if(mail_timer != tid) + return 0; + + sprintf(tmp_sql,"SELECT DISTINCT `to_account_id` FROM `%s` WHERE `read_flag` = '0' AND `check_flag` = '0'", mail_db); + + if (mysql_query(&mail_handle, tmp_sql)) { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0); + return 0; + } + + mail_res = mysql_store_result(&mail_handle); + + if (mail_res) { + if (mysql_num_rows(mail_res) == 0) { + mysql_free_result(mail_res); + mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0); + return 0; + } + + while ((mail_row = mysql_fetch_row(mail_res))) + clif_foreachclient(mail_check_timer_sub, atoi(mail_row[0])); + } + + sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `check_flag`= '0' ", mail_db); + if(mysql_query(&mail_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mail_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + + mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0); + return 0; +} + +int do_init_mail(void) +{ + add_timer_func_list(mail_check_timer,"mail_check_timer"); + mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0); + return 0; +} + +#endif diff --git a/src/map/mail.h b/src/map/mail.h index 2460de238..d6f865f78 100644 --- a/src/map/mail.h +++ b/src/map/mail.h @@ -1,12 +1,12 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -// Mail System for eAthena -// Created by Valaris - -int mail_check(struct map_session_data *sd, int type); -int mail_read(struct map_session_data *sd, int message_id); -int mail_delete(struct map_session_data *sd, int message_id); -int mail_send(struct map_session_data *sd, char *name, char *message, int flag); - -int do_init_mail(void); +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +// Mail System for eAthena +// Created by Valaris + +int mail_check(struct map_session_data *sd, int type); +int mail_read(struct map_session_data *sd, int message_id); +int mail_delete(struct map_session_data *sd, int message_id); +int mail_send(struct map_session_data *sd, char *name, char *message, int flag); + +int do_init_mail(void); diff --git a/src/map/map.h b/src/map/map.h index 0cb147e83..2f61a07d5 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -1,1491 +1,1491 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _MAP_H_ -#define _MAP_H_ - -#include -#include "../common/mmo.h" -#include "../common/mapindex.h" -#include "../common/db.h" - -//Uncomment to enable the Cell Stack Limit mod. -//It's only config is the battle_config cell_stack_limit. -//Only chars affected are those defined in BL_CHAR (mobs and players currently) -//#define CELL_NOSTACK - -//Uncomment to enable circular area checks. -//By default, all range checks in Aegis are of Square shapes, so a weapon range -// of 10 allows you to attack from anywhere within a 21x21 area. -//Enabling this changes such checks to circular checks, which is more realistic, -// but is not the official behaviour. -//#define CIRCULAR_AREA - -#define MAX_PC_CLASS 4050 -#define PC_CLASS_BASE 0 -#define PC_CLASS_BASE2 (PC_CLASS_BASE + 4001) -#define PC_CLASS_BASE3 (PC_CLASS_BASE2 + 22) -#define MAX_NPC_PER_MAP 512 -#define BLOCK_SIZE 8 -#define AREA_SIZE battle_config.area_size -#define LIFETIME_FLOORITEM 60 -#define DAMAGELOG_SIZE 30 -#define LOOTITEM_SIZE 10 -#define MAX_STATUSCHANGE 250 -//Quick defines to know which are the min-max common ailments. [Skotlex] -//Because of the way the headers are included.. these must be replaced for actual values. -//Remember to update as needed! Min is SC_STONE and max is SC_DPOISON currently. -#define SC_COMMON_MIN 0 -#define SC_COMMON_MAX 10 - -#define MAX_SKILL_LEVEL 100 -#define MAX_SKILLUNITGROUP 25 -#define MAX_SKILLUNITGROUPTICKSET 25 -#define MAX_SKILLTIMERSKILL 15 -#define MAX_MOBSKILL 50 -#define MAX_MOB_LIST_PER_MAP 128 -#define MAX_EVENTQUEUE 2 -#define MAX_EVENTTIMER 32 -#define NATURAL_HEAL_INTERVAL 500 -#define MAX_FLOORITEM 500000 -#define MAX_LEVEL 1000 -#define MAX_WALKPATH 32 -#define MAX_DROP_PER_MAP 48 -#define MAX_IGNORE_LIST 80 -#define MAX_VENDING 12 -#define MOBID_EMPERIUM 1288 - -#define MAX_PC_BONUS 10 -//Designed for search functions, species max number of matches to display. -#define MAX_SEARCH 5 -#define MAX_DUEL 1024 - -#define map_id2index(id) map[(id)].index - -//The following system marks a different job ID system used by the map server, -//which makes a lot more sense than the normal one. [Skotlex] -// -//These marks the "level" of the job. -#define JOBL_2_1 0x100 //256 -#define JOBL_2_2 0x200 //512 -#define JOBL_2 0x300 - -#define JOBL_UPPER 0x1000 //4096 -#define JOBL_BABY 0x2000 //8192 - -//for filtering and quick checking. -#define MAPID_UPPERMASK 0x0fff -#define MAPID_BASEMASK 0x00ff -//First Jobs -//Note the oddity of the novice: -//Super Novices are considered the 2-1 version of the novice! Novices are considered a first class type, too... -enum { - MAPID_NOVICE = 0x0, - MAPID_SWORDMAN, - MAPID_MAGE, - MAPID_ARCHER, - MAPID_ACOLYTE, - MAPID_MERCHANT, - MAPID_THIEF, - MAPID_TAEKWON, - MAPID_WEDDING, - MAPID_GUNSLINGER, - MAPID_NINJA, - MAPID_XMAS, // [Valaris] -//2_1 classes - MAPID_SUPER_NOVICE = JOBL_2_1|0x0, - MAPID_KNIGHT, - MAPID_WIZARD, - MAPID_HUNTER, - MAPID_PRIEST, - MAPID_BLACKSMITH, - MAPID_ASSASSIN, - MAPID_STAR_GLADIATOR, -//2_2 classes - MAPID_CRUSADER = JOBL_2_2|0x1, - MAPID_SAGE, - MAPID_BARDDANCER, - MAPID_MONK, - MAPID_ALCHEMIST, - MAPID_ROGUE, - MAPID_SOUL_LINKER, -//1-1, advanced - MAPID_NOVICE_HIGH = JOBL_UPPER|0x0, - MAPID_SWORDMAN_HIGH, - MAPID_MAGE_HIGH, - MAPID_ARCHER_HIGH, - MAPID_ACOLYTE_HIGH, - MAPID_MERCHANT_HIGH, - MAPID_THIEF_HIGH, -//2_1 advanced - MAPID_LORD_KNIGHT = JOBL_UPPER|JOBL_2_1|0x1, - MAPID_HIGH_WIZARD, - MAPID_SNIPER, - MAPID_HIGH_PRIEST, - MAPID_WHITESMITH, - MAPID_ASSASSIN_CROSS, -//2_2 advanced - MAPID_PALADIN = JOBL_UPPER|JOBL_2_2|0x1, - MAPID_PROFESSOR, - MAPID_CLOWNGYPSY, - MAPID_CHAMPION, - MAPID_CREATOR, - MAPID_STALKER, -//1-1 baby - MAPID_BABY = JOBL_BABY|0x0, - MAPID_BABY_SWORDMAN, - MAPID_BABY_MAGE, - MAPID_BABY_ARCHER, - MAPID_BABY_ACOLYTE, - MAPID_BABY_MERCHANT, - MAPID_BABY_THIEF, - MAPID_BABY_TAEKWON, -//2_1 baby - MAPID_SUPER_BABY = JOBL_BABY|JOBL_2_1|0x0, - MAPID_BABY_KNIGHT, - MAPID_BABY_WIZARD, - MAPID_BABY_HUNTER, - MAPID_BABY_PRIEST, - MAPID_BABY_BLACKSMITH, - MAPID_BABY_ASSASSIN, - MAPID_BABY_STAR_GLADIATOR, -//2_2 baby - MAPID_BABY_CRUSADER = JOBL_BABY|JOBL_2_2|0x1, - MAPID_BABY_SAGE, - MAPID_BABY_BARDDANCER, - MAPID_BABY_MONK, - MAPID_BABY_ALCHEMIST, - MAPID_BABY_ROGUE, - MAPID_BABY_SOUL_LINKER, -}; - -//Don't change this, as the client seems to always send/receive 80 characters as it currently is. [Skotlex] -#define MESSAGE_SIZE 80 - -#define DEFAULT_AUTOSAVE_INTERVAL 5*60*1000 - -//Specifies maps where players may hit each other -#define map_flag_vs(m) (map[m].flag.pvp || map[m].flag.gvg_dungeon || map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle)) -//Specifies maps that have special GvG/WoE restrictions -#define map_flag_gvg(m) (map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle)) -//Specifies if the map is tagged as GvG/WoE (regardless of agit_flag status) -#define map_flag_gvg2(m) (map[m].flag.gvg || map[m].flag.gvg_castle) -//Caps values to min/max -#define cap_value(a, min, max) (a>=max?max:a<=min?min:a) - -//This stackable implementation does not means a BL can be more than one type at a time, but it's -//meant to make it easier to check for multiple types at a time on invocations such as -// map_foreach* calls [Skotlex] -enum { - BL_NUL = 0x000, - BL_PC = 0x001, - BL_MOB = 0x002, - BL_PET = 0x004, - BL_HOM = 0x008, //[blackhole89] - BL_ITEM = 0x010, - BL_SKILL = 0x020, - BL_NPC = 0x040, - BL_CHAT = 0x080, -}; - -//For common mapforeach calls. Since pets cannot be affected, they aren't included here yet. -#define BL_CHAR (BL_PC|BL_MOB|BL_HOM) -#define BL_ALL 0xfff - -enum { WARP, SHOP, SCRIPT, MONS }; - -enum { - RC_FORMLESS=0, - RC_UNDEAD, - RC_BRUTE, - RC_PLANT, - RC_INSECT, - RC_FISH, - RC_DEMON, - RC_DEMIHUMAN, - RC_ANGEL, - RC_DRAGON, - RC_BOSS, - RC_NONBOSS, - RC_MAX -}; - -enum { - ELE_NEUTRAL=0, - ELE_WATER, - ELE_EARTH, - ELE_FIRE, - ELE_WIND, - ELE_POISON, - ELE_HOLY, - ELE_DARK, - ELE_GHOST, - ELE_UNDEAD, - ELE_MAX -}; - -enum { - IG_BLUEBOX=1, - IG_VIOLETBOX, //2 - IG_CARDALBUM, //3 - IG_GIFTBOX, //4 - IG_SCROLLBOX, //5 - IG_FINDINGORE, //6 - IG_COOKIEBAG, //7 - IG_POTION, //8 - IG_HERBS, //9 - IG_FRUITS, //10 - IG_MEAT, //11 - IG_CANDY, //12 - IG_JUICE, //13 - IG_FISH, //14 - IG_BOXES, //15 - IG_GEMSTONE, //16 - IG_JELLOPY, //17 - IG_ORE, //18 - IG_FOOD, //19 - IG_RECOVERY, //20 - IG_MINERALS, //21 - IG_TAMING, //22 - IG_SCROLLS, //23 - IG_QUIVERS, //24 - IG_MASKS, //25 - IG_ACCESORY, //26 - IG_JEWELS, //27 - IG_GIFTBOX_1, //28 - IG_GIFTBOX_2, //29 - IG_GIFTBOX_3, //30 - IG_GIFTBOX_4, //31 - IG_EGGBOY, //32 - IG_EGGGIRL, //33 - IG_GIFTBOXCHINA, //34 - IG_LOTTOBOX, //35 - MAX_ITEMGROUP, -} item_group_list; - -enum { - ATF_SELF=0x01, - ATF_TARGET=0x02, - ATF_SHORT=0x04, - ATF_LONG=0x08 -} auto_trigger_flag; - -struct block_list { - struct block_list *next,*prev; - int id; - short m,x,y; - unsigned char type; - unsigned char subtype; -}; - -struct walkpath_data { - unsigned char path_len,path_pos,path_half; - unsigned char path[MAX_WALKPATH]; -}; -struct shootpath_data { - int rx,ry,len; - int x[MAX_WALKPATH]; - int y[MAX_WALKPATH]; -}; - -struct skill_timerskill { - int timer; - int src_id; - int target_id; - int map; - short x,y; - short skill_id,skill_lv; - int type; - int flag; -}; - -struct skill_unit_group; -struct skill_unit { - struct block_list bl; - - struct skill_unit_group *group; - - int limit; - int val1,val2; - short alive,range; -}; - -struct skill_unit_group { - int src_id; - int party_id; - int guild_id; - int map; - int target_flag; //Holds BCT_* flag for battle_check_target - int bl_flag; //Holds BL_* flag for map_foreachin* functions - unsigned int tick; - int limit,interval; - - int skill_id,skill_lv; - int val1,val2,val3; - char *valstr; - int unit_id; - int group_id; - int unit_count,alive_count; - struct skill_unit *unit; - struct { - unsigned ammo_consume : 1; - unsigned magic_power : 1; - unsigned into_abyss : 1; - unsigned song_dance : 2; //0x1 Song/Dance, 0x2 Ensemble - } state; -}; -struct skill_unit_group_tickset { - unsigned int tick; - int id; -}; - -struct unit_data { - struct block_list *bl; - struct walkpath_data walkpath; - struct skill_timerskill *skilltimerskill[MAX_SKILLTIMERSKILL]; - struct skill_unit_group *skillunit[MAX_SKILLUNITGROUP]; - struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET]; - short attacktarget_lv; - short to_x,to_y; - short skillx,skilly; - short skillid,skilllv; - int skilltarget; - int skilltimer; - int target; - int attacktimer; - int walktimer; - int chaserange; - unsigned int attackabletime; - unsigned int canact_tick; - unsigned int canmove_tick; - unsigned char dir; - unsigned char walk_count; - struct { - unsigned change_walk_target : 1 ; - unsigned skillcastcancel : 1 ; - unsigned attack_continue : 1 ; - unsigned walk_easy : 1 ; - unsigned running : 1; - } state; -}; - -//Basic damage info of a weapon -//Required because players have two of these, one in status_data and another -//for their left hand weapon. -struct weapon_atk { - unsigned short atk, atk2; - unsigned short range; - unsigned char ele; -}; - -//For holding basic status (which can be modified by status changes) -struct status_data { - unsigned int - hp, sp, - max_hp, max_sp; - unsigned short - str, agi, vit, int_, dex, luk, - batk, - matk_min, matk_max, - speed, - amotion, adelay, dmotion, - mode; - short - hit, flee, cri, flee2, - def2, mdef2, - aspd_rate; - unsigned char - def_ele, ele_lv, - size, race; - signed char - def, mdef; - struct weapon_atk rhw, *lhw; //Right Hand/Left Hand Weapon. Only players have a lhw (hence it's a pointer) -}; - -struct script_reg { - int index; - int data; -}; -struct script_regstr { - int index; - char data[256]; -}; - -struct status_change_entry { - int timer; - int val1,val2,val3,val4; -}; - -struct status_change { - struct status_change_entry data[MAX_STATUSCHANGE]; - short count; - unsigned short opt1,opt2; - unsigned int opt3, option; //Note that older packet versions use short here. -}; - -struct vending { - short index; - unsigned short amount; - unsigned int value; -}; - -struct weapon_data { - int atkmods[3]; - // all the variables except atkmods get zero'ed in each call of status_calc_pc - // NOTE: if you want to add a non-zeroed variable, you need to update the malloc_set call - // in status_calc_pc as well! All the following are automatically zero'ed. [Skotlex] - int overrefine; - int star; - int ignore_def_ele; - int ignore_def_race; - int def_ratio_atk_ele; - int def_ratio_atk_race; - int addele[ELE_MAX]; - int addrace[RC_MAX]; - int addrace2[RC_MAX]; - int addsize[3]; - - short ignore_def_mob; - struct drain_data { - short rate; - short per; - short value; - unsigned type:1; - } hp_drain[RC_MAX], sp_drain[RC_MAX]; - - short add_damage_classid[MAX_PC_BONUS]; - int add_damage_classrate[MAX_PC_BONUS]; - int add_damage_class_count; -}; - -struct view_data { - unsigned short - class_, - weapon, - shield, //Or left-hand weapon. - head_top, - head_mid, - head_bottom, - hair_style, - hair_color, - cloth_color; - char sex; - unsigned dead_sit : 2; -}; - -//Additional regen data that only players have. -struct regen_data_sub { - unsigned short - hp,sp; - - //tick accumulation before healing. - struct { - unsigned int hp,sp; - } tick; - - //Regen rates (where every 1 means +100% regen) - struct { - unsigned char hp,sp; - } rate; -}; - -struct regen_data { - - unsigned short flag; //Marks what stuff you may heal or not. - unsigned short - hp,sp,shp,ssp; - - //tick accumulation before healing. - struct { - unsigned int hp,sp,shp,ssp; - } tick; - - //Regen rates (where every 1 means +100% regen) - struct { - unsigned char - hp,sp,shp,ssp; - } rate; - - struct { - unsigned walk:1; //Can you regen even when walking? - unsigned gc:1; //Tags when you should have double regen due to GVG castle - unsigned overweight :2; //overweight state (1: 50%, 2: 90%) - unsigned block :2; //Block regen flag (1: Hp, 2: Sp) - } state; - - //skill-regen, sitting-skill-regen (since not all chars with regen need it) - struct regen_data_sub *sregen, *ssregen; -}; - -struct party_member_data { - struct map_session_data *sd; - unsigned int hp; //For HP,x,y refreshing. - unsigned short x, y; -}; - -struct party_data { - struct party party; - struct party_member_data data[MAX_PARTY]; - unsigned char itemc; //For item distribution. - struct { - unsigned monk : 1; //There's at least one monk in party? - unsigned sg : 1; //There's at least one Star Gladiator in party? - unsigned snovice :1; //There's a Super Novice - unsigned tk : 1; //There's a taekwon - } state; -}; - -struct npc_data; -struct pet_db; -struct homunculus_db; //[orn] -struct item_data; -struct square; - -struct map_session_data { - struct block_list bl; - struct unit_data ud; - struct view_data vd; - struct status_data base_status, battle_status; - struct weapon_atk base_lhw, battle_lhw; //Left-hand weapon atk data. - struct status_change sc; - struct regen_data regen; - struct regen_data_sub sregen, ssregen; - //NOTE: When deciding to add a flag to state or special_state, take into consideration that state is preserved in - //status_calc_pc, while special_state is recalculated in each call. [Skotlex] - struct { - unsigned auth : 1; - unsigned menu_or_input : 1; - unsigned dead_sit : 2; - unsigned waitingdisconnect : 1; - unsigned lr_flag : 2; - unsigned connect_new : 1; - unsigned arrow_atk : 1; - unsigned skill_flag : 1; - unsigned gangsterparadise : 1; - unsigned rest : 1; - unsigned storage_flag : 2; //0: closed, 1: Normal Storage open, 2: guild storage open [Skotlex] - unsigned snovice_flag : 4; - // originally by Qamera, adapted by celest - unsigned event_death : 1; - unsigned event_kill_pc : 1; - unsigned event_disconnect : 1; - unsigned event_kill_mob : 1; - unsigned event_baselvup : 1; - unsigned event_joblvup : 1; - unsigned event_loadmap : 1; - // Abracadabra bugfix by Aru - unsigned abra_flag : 1; - unsigned autotrade : 1; //By Fantik - unsigned reg_dirty : 3; //By Skotlex (marks whether registry variables have been saved or not yet) - unsigned showdelay :1; - unsigned showexp :1; - unsigned showzeny :1; - unsigned mainchat :1; //[LuzZza] - unsigned noask :1; // [LuzZza] - unsigned trading :1; //[Skotlex] is 1 only after a trade has started. - unsigned deal_locked :2; //1: Clicked on OK. 2: Clicked on TRADE - unsigned party_sent :1; - unsigned guild_sent :1; - unsigned monster_ignore :1; // for monsters to ignore a character [Valaris] [zzo] - unsigned size :2; // for tiny/large types - unsigned night :1; //Holds whether or not the player currently has the SI_NIGHT effect on. [Skotlex] - unsigned finalsave :1; //Signals whether the final save for the char was done or not yet. Meant to prevent exploits and the like. [Skotlex] - unsigned blockedmove :1; - unsigned using_fake_npc :1; - unsigned rewarp :1; //Signals that a player should warp as soon as he is done loading a map. [Skotlex] - unsigned killer : 1; - unsigned killable : 1; - unsigned doridori : 1; - unsigned ignoreAll : 1; - unsigned short autoloot; - struct guild *gmaster_flag; - } state; - struct { - unsigned char no_weapon_damage, no_magic_damage, no_misc_damage; - unsigned restart_full_recover : 1; - unsigned no_castcancel : 1; - unsigned no_castcancel2 : 1; - unsigned no_sizefix : 1; - unsigned no_gemstone : 1; - unsigned intravision : 1; // Maya Purple Card effect allowing to see Hiding/Cloaking people [DracoRPG] - unsigned perfect_hiding : 1; // [Valaris] - } special_state; - int login_id1, login_id2; - unsigned short class_; //This is the internal job ID used by the map server to simplify comparisons/queries/etc. [Skotlex] - - int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18 - struct mmo_charstatus status; - struct registry save_reg; - - struct item_data *inventory_data[MAX_INVENTORY]; - short equip_index[11]; - unsigned int weight,max_weight; - int cart_weight,cart_max_weight,cart_num,cart_max_num; - int fd; - unsigned short mapindex; - unsigned short prev_speed,prev_adelay; - unsigned char head_dir; //0: Look forward. 1: Look right, 2: Look left. - unsigned int client_tick; - int npc_id,areanpc_id,npc_shopid; - int npc_item_flag; //Marks the npc_id with which you can use items during interactions with said npc (see script command enable_itemuse) - int npc_menu, max_menu; - int npc_amount; - struct script_state *st; - char npc_str[256]; - int npc_timer_id; //For player attached npc timers. [Skotlex] - unsigned int chatID; - time_t idletime; - - struct{ - char name[NAME_LENGTH]; - } ignore[MAX_IGNORE_LIST]; - - int followtimer; // [MouseJstr] - int followtarget; - - time_t emotionlasttime; // to limit flood with emotion packets - - short skillitem,skillitemlv; - short skillid_old,skilllv_old; - short skillid_dance,skilllv_dance; - char blockskill[MAX_SKILL]; // [celest] - int cloneskill_id; - int menuskill_id, menuskill_lv; - - int invincible_timer; - unsigned int canlog_tick; - unsigned int canuseitem_tick; // [Skotlex] - unsigned int cantalk_tick; - - short weapontype1,weapontype2; - short disguise; // [Valaris] - - struct weapon_data right_weapon, left_weapon; - - // here start arrays to be globally zeroed at the beginning of status_calc_pc() - int param_bonus[6],param_equip[6]; //Stores card/equipment bonuses. - int subele[ELE_MAX]; - int subrace[RC_MAX]; - int subrace2[RC_MAX]; - int subsize[3]; - int reseff[SC_COMMON_MAX-SC_COMMON_MIN+1]; - int weapon_coma_ele[ELE_MAX]; - int weapon_coma_race[RC_MAX]; - int weapon_atk[16]; - int weapon_atk_rate[16]; - int arrow_addele[ELE_MAX]; - int arrow_addrace[RC_MAX]; - int arrow_addsize[3]; - int magic_addele[ELE_MAX]; - int magic_addrace[RC_MAX]; - int magic_addsize[3]; - int critaddrace[RC_MAX]; - int expaddrace[RC_MAX]; - int itemgrouphealrate[MAX_ITEMGROUP]; - short sp_gain_race[RC_MAX]; - // zeroed arrays end here. - // zeroed structures start here - struct s_autospell{ - short id, lv, rate, card_id; - } autospell[MAX_PC_BONUS], autospell2[MAX_PC_BONUS]; - struct s_addeffect{ - short id, rate, arrow_rate; - unsigned char flag; - } addeff[MAX_PC_BONUS], addeff2[MAX_PC_BONUS]; - struct { //skillatk raises bonus dmg% of skills, skillblown increases bonus blewcount for some skills. - short id, val; - } skillatk[MAX_PC_BONUS], skillblown[MAX_PC_BONUS]; - struct { - short class_, rate; - } add_def[MAX_PC_BONUS], add_mdef[MAX_PC_BONUS], - add_dmg[MAX_PC_BONUS], add_mdmg[MAX_PC_BONUS]; - struct s_add_drop { - short id, group; - int race, rate; - } add_drop[MAX_PC_BONUS]; - struct { - int nameid; - int rate; - } itemhealrate[MAX_PC_BONUS]; - // zeroed structures end here - // zeroed vars start here. - int arrow_atk,arrow_ele,arrow_cri,arrow_hit; - int nsshealhp,nsshealsp; - int critical_def,double_rate; - int long_attack_atk_rate; //Long range atk rate, not weapon based. [Skotlex] - int near_attack_def_rate,long_attack_def_rate,magic_def_rate,misc_def_rate; - int ignore_mdef_ele; - int ignore_mdef_race; - int perfect_hit; - int perfect_hit_add; - int get_zeny_rate; - int get_zeny_num; //Added Get Zeny Rate [Skotlex] - int double_add_rate; - int short_weapon_damage_return,long_weapon_damage_return; - int magic_damage_return; // AppleGirl Was Here - int random_attack_increase_add,random_attack_increase_per; // [Valaris] - int break_weapon_rate,break_armor_rate; - int crit_atk_rate; - int hp_loss_rate; - int sp_loss_rate; - int classchange; // [Valaris] - int speed_add_rate, aspd_add_rate; - unsigned int setitem_hash, setitem_hash2; //Split in 2 because shift operations only work on int ranges. [Skotlex] - - short splash_range, splash_add_range; - short add_steal_rate; - short hp_loss_value; - short sp_loss_value; - short hp_loss_type; - short sp_gain_value, hp_gain_value; - short sp_vanish_rate; - short sp_vanish_per; - short add_drop_count; - unsigned short unbreakable; // chance to prevent ANY equipment breaking [celest] - unsigned short unbreakable_equip; //100% break resistance on certain equipment - unsigned short unstripable_equip; - short add_def_count,add_mdef_count; - short add_dmg_count,add_mdmg_count; - - // zeroed vars end here. - - int castrate,delayrate,hprate,sprate,dsprate; - int atk_rate; - int speed_rate,hprecov_rate,sprecov_rate; - int matk_rate; - int critical_rate,hit_rate,flee_rate,flee2_rate,def_rate,def2_rate,mdef_rate,mdef2_rate; - - int hp_loss_tick; - int sp_loss_tick; - - int itemid; - short itemindex; //Used item's index in sd->inventory [Skotlex] - - short catch_target_class; // pet catching, stores a pet class to catch (short now) [zzo] - - short spiritball, spiritball_old; - int spirit_timer[MAX_SKILL_LEVEL]; - - unsigned char potion_success_counter; //Potion successes in row counter - unsigned char mission_count; //Stores the bounty kill count for TK_MISSION - short mission_mobid; //Stores the target mob_id for TK_MISSION - int die_counter; //Total number of times you've died - int devotion[5]; //Stores the char IDs of chars devoted to. - int reg_num; //Number of registries (type numeric) - int regstr_num; //Number of registries (type string) - - struct script_reg *reg; - struct script_regstr *regstr; - - int trade_partner; - struct { - struct { - int index, amount; - } item[10]; - int zeny, weight; - } deal; - - int party_invite,party_invite_account; - - int guild_invite,guild_invite_account; - int guild_emblem_id,guild_alliance,guild_alliance_account; - short guild_x,guild_y; // For guildmate position display. [Skotlex] should be short [zzo] - int guildspy; // [Syrus22] - int partyspy; // [Syrus22] - - int vender_id; - int vend_num; - char message[MESSAGE_SIZE]; - struct vending vending[MAX_VENDING]; - - struct pet_data *pd; - struct homun_data *hd; // [blackhole89] - - struct{ - int m; //-1 - none, other: map index corresponding to map name. - unsigned short index; //map index - }feel_map[3];// 0 - Sun; 1 - Moon; 2 - Stars - short hate_mob[3]; - - unsigned int pvp_timer; - short pvp_point; - unsigned short pvp_rank, pvp_lastusers; - unsigned short pvp_won, pvp_lost; - - char eventqueue[MAX_EVENTQUEUE][50]; - int eventtimer[MAX_EVENTTIMER]; - unsigned short eventcount; // [celest] - - unsigned char change_level; // [celest] - - char fakename[NAME_LENGTH]; // fake names [Valaris] - -#ifndef TXT_ONLY - int mail_counter; // mail counter for mail system [Valaris] -#endif - - int duel_group; // duel vars [LuzZza] - int duel_invite; - - char away_message[128]; // [LuzZza] - -}; - -struct { - int members_count; - int invites_count; - int max_players_limit; -} duel_list[MAX_DUEL]; - -int duel_count; - -struct npc_timerevent_list { - int timer,pos; -}; -struct npc_label_list { - char name[NAME_LENGTH]; - int pos; -}; -struct npc_item_list { - unsigned int nameid,value; -}; -struct npc_data { - struct block_list bl; - struct unit_data ud; //Because they need to be able to move.... - struct view_data *vd; - struct status_change sc; //They can't have status changes, but.. they want the visual opt values. - short n; - short class_; - short speed; - unsigned char name[NAME_LENGTH]; - unsigned char exname[NAME_LENGTH]; - int chat_id; - unsigned int next_walktime; - - char eventqueue[MAX_EVENTQUEUE][50]; - int eventtimer[MAX_EVENTTIMER]; - short arenaflag; - - void *chatdb; - struct npc_data *master_nd; - - union { - struct { - struct script_code *script; - short xs,ys; - int guild_id; - int timer,timerid,timeramount,rid; - unsigned int timertick; - struct npc_timerevent_list *timer_event; - int label_list_num; - struct npc_label_list *label_list; - int src_id; - } scr; - struct npc_item_list shop_item[1]; - struct { - short xs,ys; - short x,y; - unsigned short mapindex; - } warp; - } u; - //Do NOT place anything afterwards... shop data NPC will override any variables from here and on! [Skotlex] -}; - -//For quick linking to a guardian's info. [Skotlex] -struct guardian_data { - int number; //0-MAX_GUARDIANS-1 = Guardians. MAX_GUARDIANS = Emperium. - int guild_id; - int emblem_id; - int guardup_lv; //Level of GD_GUARDUP skill. - char guild_name[NAME_LENGTH]; - struct guild_castle* castle; -}; - -// Mob List Held in memory for Dynamic Mobs [Wizputer] -// Expanded to specify all mob-related spawn data by [Skotlex] -struct spawn_data { - short class_; //Class, used because a mob can change it's class - unsigned short m,x,y; //Spawn information (map, point, spawn-area around point) - signed short xs,ys; - unsigned short num; //Number of mobs using this structure. - unsigned int level; //Custom level. - unsigned int delay1,delay2; //Min delay before respawning after spawn/death - struct { - unsigned size :2; //Holds if mob has to be tiny/large - unsigned ai :2; //Holds if mob is special ai. - } state; - char name[NAME_LENGTH],eventname[50]; //Name/event -}; - - -struct mob_data { - struct block_list bl; - struct unit_data ud; - struct view_data *vd; - struct status_data status, *base_status; //Second one is in case of leveling up mobs, or tiny/large mobs. - struct status_change sc; - struct mob_db *db; //For quick data access (saves doing mob_db(md->class_) all the time) [Skotlex] - char name[NAME_LENGTH]; - struct { - unsigned size : 2; //Small/Big monsters. - unsigned cached : 1; //Cached mobs for dynamic mob unloading [Skotlex] - unsigned ai : 2; //Special ai for summoned monsters. - //0: Normal mob. - //1: Standard summon, attacks mobs. - //2: Alchemist Marine Sphere - //3: Alchemist Summon Flora - } special_state; //Special mob information that does not needs to be zero'ed on mob respawn. - struct { - unsigned skillstate : 8; - unsigned aggressive : 1; //Signals whether the mob AI is in aggressive mode or reactive mode. [Skotlex] - unsigned char steal_flag; //number of steal tries (to prevent steal exploit on mobs with few items) [Lupus] - unsigned steal_coin_flag : 1; - unsigned soul_change_flag : 1; // Celest - unsigned alchemist: 1; - unsigned no_random_walk: 1; - unsigned killer: 1; - int provoke_flag; // Celest - } state; - struct guardian_data* guardian_data; - struct { - int id; - int dmg; - unsigned flag : 1; //0: Normal. 1: Homunc exp - } dmglog[DAMAGELOG_SIZE]; - struct spawn_data *spawn; //Spawn data. - struct item *lootitem; - short spawn_n; //Spawn data index on the map server. - short class_; - short attacked_count; - unsigned char attacked_players; - unsigned int tdmg; //Stores total damage given to the mob, for exp calculations. [Skotlex] - int level; - int target_id,attacked_id; - unsigned int next_walktime; - unsigned int last_deadtime,last_spawntime,last_thinktime,last_linktime; - short move_fail_count; - short lootitem_count; - short min_chase; - - int deletetimer; - int master_id,master_dist; - - struct npc_data *nd; - unsigned short callback_flag; - - short skillidx; - unsigned int skilldelay[MAX_MOBSKILL]; - char npc_event[50]; -}; - -/* [blackhole89] */ -struct homun_data { - struct block_list bl; - struct unit_data ud; - struct view_data *vd; - struct status_data base_status, battle_status; - struct status_change sc; - struct regen_data regen; - struct homunculus_db *homunculusDB; //[orn] - struct s_homunculus homunculus ; //[orn] - - struct map_session_data *master; //pointer back to its master - int hungry_timer; //[orn] - unsigned int exp_next; - char blockskill[MAX_SKILL]; // [orn] -}; - -struct pet_data { - struct block_list bl; - struct unit_data ud; - struct view_data vd; - struct s_pet pet; - struct status_data status; - struct mob_db *db; - struct pet_db *petDB; - int pet_hungry_timer; - int target_id; - short n; - struct { - unsigned skillbonus : 1; - } state; - int move_fail_count; - unsigned int next_walktime,last_thinktime; - short rate_fix; //Support rate as modified by intimacy (1000 = 100%) [Skotlex] - - struct pet_recovery { //Stat recovery - unsigned short type; //Status Change id - unsigned short delay; //How long before curing (secs). - int timer; - } *recovery; //[Valaris] / Reimplemented by [Skotlex] - - struct pet_bonus { - unsigned short type; //bStr, bVit? - unsigned short val; //Qty - unsigned short duration; //in secs - unsigned short delay; //Time before recasting (secs) - int timer; - } *bonus; //[Valaris] / Reimplemented by [Skotlex] - - struct pet_skill_attack { //Attack Skill - unsigned short id; - unsigned short lv; - unsigned short div_; //0 = Normal skill. >0 = Fixed damage (lv), fixed div_. - unsigned short rate; //Base chance of skill ocurrance (10 = 10% of attacks) - unsigned short bonusrate; //How being 100% loyal affects cast rate (10 = At 1000 intimacy->rate+10% - } *a_skill; //[Skotlex] - - struct pet_skill_support { //Support Skill - unsigned short id; - unsigned short lv; - unsigned short hp; //Max HP% for skill to trigger (50 -> 50% for Magnificat) - unsigned short sp; //Max SP% for skill to trigger (100 = no check) - unsigned short delay; //Time (secs) between being able to recast. - int timer; - } *s_skill; //[Skotlex] - - struct pet_loot { - struct item *item; - unsigned short count; - unsigned short weight; - unsigned short max; - } *loot; //[Valaris] / Rewritten by [Skotlex] - - struct map_session_data *msd; -}; - -enum { ATK_LUCKY=1,ATK_FLEE,ATK_DEF}; // 囲まれペナルティ計算用 - -struct map_data { - char name[MAP_NAME_LENGTH]; - unsigned short index; //Index is the map index used by the mapindex* functions. - unsigned char *gat; // NULLなら下のmap_data_other_serverとして扱う - unsigned char *cell; //Contains temporary cell data that is set/unset on tiles. -#ifdef CELL_NOSTACK - unsigned char *cell_bl; //Holds amount of bls in any given cell. -#endif - char *alias; // [MouseJstr] - struct block_list **block; - struct block_list **block_mob; - int *block_count,*block_mob_count; - int m; - short xs,ys; - short bxs,bys; - int water_height; - int npc_num; - int users; - struct { - unsigned alias : 1; - unsigned nomemo : 1; - unsigned noteleport : 1; - unsigned noreturn : 1; - unsigned monster_noteleport : 1; - unsigned nosave : 1; - unsigned nobranch : 1; - unsigned noexppenalty : 1; - unsigned pvp : 1; - unsigned pvp_noparty : 1; - unsigned pvp_noguild : 1; - unsigned pvp_nightmaredrop :1; - unsigned pvp_nocalcrank : 1; - unsigned gvg_castle : 1; - unsigned gvg : 1; // Now it identifies gvg versus maps that are active 24/7 - unsigned gvg_dungeon : 1; // Celest - unsigned gvg_noparty : 1; - unsigned nozenypenalty : 1; - unsigned notrade : 1; - unsigned noskill : 1; - unsigned nowarp : 1; - unsigned nowarpto : 1; - unsigned noicewall : 1; // [Valaris] - unsigned snow : 1; // [Valaris] - unsigned clouds : 1; - unsigned clouds2 : 1; // [Valaris] - unsigned fog : 1; // [Valaris] - unsigned fireworks : 1; - unsigned sakura : 1; // [Valaris] - unsigned leaves : 1; // [Valaris] - unsigned rain : 1; // [Valaris] - unsigned indoors : 1; // celest - unsigned nogo : 1; // [Valaris] - unsigned nobaseexp : 1; // [Lorky] added by Lupus - unsigned nojobexp : 1; // [Lorky] - unsigned nomobloot : 1; // [Lorky] - unsigned nomvploot : 1; // [Lorky] - unsigned nightenabled :1; //For night display. [Skotlex] - unsigned restricted : 1; // [Komurka] - unsigned nodrop : 1; - unsigned novending : 1; - unsigned loadevent : 1; - unsigned nochat :1; - unsigned partylock :1; - unsigned guildlock :1; - } flag; - struct point save; - struct npc_data *npc[MAX_NPC_PER_MAP]; - struct { - int drop_id; - int drop_type; - int drop_per; - } drop_list[MAX_DROP_PER_MAP]; - - struct spawn_data *moblist[MAX_MOB_LIST_PER_MAP]; // [Wizputer] - int mob_delete_timer; // [Skotlex] - int zone; // [Komurka] - int jexp; // map experience multiplicator - int bexp; // map experience multiplicator - int nocommand; //Blocks @/# commands for non-gms. [Skotlex] -}; - -struct map_data_other_server { - char name[MAP_NAME_LENGTH]; - unsigned short index; //Index is the map index used by the mapindex* functions. - unsigned char *gat; // NULL固定にして判断 - unsigned long ip; - unsigned int port; -}; - -struct flooritem_data { - struct block_list bl; - unsigned char subx,suby; - int cleartimer; - int first_get_id,second_get_id,third_get_id; - unsigned int first_get_tick,second_get_tick,third_get_tick; - struct item item_data; -}; - -enum { - SP_SPEED,SP_BASEEXP,SP_JOBEXP,SP_KARMA,SP_MANNER,SP_HP,SP_MAXHP,SP_SP, // 0-7 - SP_MAXSP,SP_STATUSPOINT,SP_0a,SP_BASELEVEL,SP_SKILLPOINT,SP_STR,SP_AGI,SP_VIT, // 8-15 - SP_INT,SP_DEX,SP_LUK,SP_CLASS,SP_ZENY,SP_SEX,SP_NEXTBASEEXP,SP_NEXTJOBEXP, // 16-23 - SP_WEIGHT,SP_MAXWEIGHT,SP_1a,SP_1b,SP_1c,SP_1d,SP_1e,SP_1f, // 24-31 - SP_USTR,SP_UAGI,SP_UVIT,SP_UINT,SP_UDEX,SP_ULUK,SP_26,SP_27, // 32-39 - SP_28,SP_ATK1,SP_ATK2,SP_MATK1,SP_MATK2,SP_DEF1,SP_DEF2,SP_MDEF1, // 40-47 - SP_MDEF2,SP_HIT,SP_FLEE1,SP_FLEE2,SP_CRITICAL,SP_ASPD,SP_36,SP_JOBLEVEL, // 48-55 - SP_UPPER,SP_PARTNER,SP_CART,SP_FAME,SP_UNBREAKABLE, //56-60 - SP_CARTINFO=99, // 99 - - SP_BASEJOB=119, // 100+19 - celest - SP_BASECLASS=120, //Hmm.. why 100+19? I just use the next one... [Skotlex] - - // original 1000- - SP_ATTACKRANGE=1000, SP_ATKELE,SP_DEFELE, // 1000-1002 - SP_CASTRATE, SP_MAXHPRATE, SP_MAXSPRATE, SP_SPRATE, // 1003-1006 - SP_ADDELE, SP_ADDRACE, SP_ADDSIZE, SP_SUBELE, SP_SUBRACE, // 1007-1011 - SP_ADDEFF, SP_RESEFF, // 1012-1013 - SP_BASE_ATK,SP_ASPD_RATE,SP_HP_RECOV_RATE,SP_SP_RECOV_RATE,SP_SPEED_RATE, // 1014-1018 - SP_CRITICAL_DEF,SP_NEAR_ATK_DEF,SP_LONG_ATK_DEF, // 1019-1021 - SP_DOUBLE_RATE, SP_DOUBLE_ADD_RATE, SP_MATK, SP_MATK_RATE, // 1022-1025 - SP_IGNORE_DEF_ELE,SP_IGNORE_DEF_RACE, // 1026-1027 - SP_ATK_RATE,SP_SPEED_ADDRATE,SP_ASPD_ADDRATE, // 1028-1030 - SP_MAGIC_ATK_DEF,SP_MISC_ATK_DEF, // 1031-1032 - SP_IGNORE_MDEF_ELE,SP_IGNORE_MDEF_RACE, // 1033-1034 - SP_MAGIC_ADDELE,SP_MAGIC_ADDRACE,SP_MAGIC_ADDSIZE, // 1035-1037 - SP_PERFECT_HIT_RATE,SP_PERFECT_HIT_ADD_RATE,SP_CRITICAL_RATE,SP_GET_ZENY_NUM,SP_ADD_GET_ZENY_NUM, // 1038-1042 - SP_ADD_DAMAGE_CLASS,SP_ADD_MAGIC_DAMAGE_CLASS,SP_ADD_DEF_CLASS,SP_ADD_MDEF_CLASS, // 1043-1046 - SP_ADD_MONSTER_DROP_ITEM,SP_DEF_RATIO_ATK_ELE,SP_DEF_RATIO_ATK_RACE,SP_ADD_SPEED, // 1047-1050 - SP_HIT_RATE,SP_FLEE_RATE,SP_FLEE2_RATE,SP_DEF_RATE,SP_DEF2_RATE,SP_MDEF_RATE,SP_MDEF2_RATE, // 1051-1057 - SP_SPLASH_RANGE,SP_SPLASH_ADD_RANGE,SP_AUTOSPELL,SP_HP_DRAIN_RATE,SP_SP_DRAIN_RATE, // 1058-1062 - SP_SHORT_WEAPON_DAMAGE_RETURN,SP_LONG_WEAPON_DAMAGE_RETURN,SP_WEAPON_COMA_ELE,SP_WEAPON_COMA_RACE, // 1063-1066 - SP_ADDEFF2,SP_BREAK_WEAPON_RATE,SP_BREAK_ARMOR_RATE,SP_ADD_STEAL_RATE, // 1067-1070 - SP_MAGIC_DAMAGE_RETURN,SP_RANDOM_ATTACK_INCREASE,SP_ALL_STATS,SP_AGI_VIT,SP_AGI_DEX_STR,SP_PERFECT_HIDE, // 1071-1076 - SP_FREE,SP_CLASSCHANGE, // 1077-1078 - SP_HP_DRAIN_VALUE,SP_SP_DRAIN_VALUE, // 1079-1080 - SP_WEAPON_ATK,SP_WEAPON_ATK_RATE, // 1081-1082 - SP_DELAYRATE,SP_HP_DRAIN_RATE_RACE,SP_SP_DRAIN_RATE_RACE, // 1083-1085 - - SP_RESTART_FULL_RECOVER=2000,SP_NO_CASTCANCEL,SP_NO_SIZEFIX,SP_NO_MAGIC_DAMAGE,SP_NO_WEAPON_DAMAGE,SP_NO_GEMSTONE, // 2000-2005 - SP_NO_CASTCANCEL2,SP_NO_MISC_DAMAGE,SP_UNBREAKABLE_WEAPON,SP_UNBREAKABLE_ARMOR, SP_UNBREAKABLE_HELM, // 2006-2010 - SP_UNBREAKABLE_SHIELD, SP_LONG_ATK_RATE, // 2011-2012 - - SP_CRIT_ATK_RATE, SP_CRITICAL_ADDRACE, SP_NO_REGEN, SP_ADDEFF_WHENHIT, SP_AUTOSPELL_WHENHIT, // 2013-2017 - SP_SKILL_ATK, SP_UNSTRIPABLE, SP_ADD_DAMAGE_BY_CLASS, // 2018-2020 - SP_SP_GAIN_VALUE, SP_IGNORE_DEF_MOB, SP_HP_LOSS_RATE, SP_ADDRACE2, SP_HP_GAIN_VALUE, // 2021-2025 - SP_SUBSIZE, SP_HP_DRAIN_VALUE_RACE, SP_ADD_ITEM_HEAL_RATE, SP_SP_DRAIN_VALUE_RACE, SP_EXP_ADDRACE, // 2026-2030 - SP_SP_GAIN_RACE, SP_SUBRACE2, SP_ADDEFF_WHENHIT_SHORT, // 2031-2033 - SP_UNSTRIPABLE_WEAPON,SP_UNSTRIPABLE_ARMOR,SP_UNSTRIPABLE_HELM,SP_UNSTRIPABLE_SHIELD, // 2034-2037 - SP_INTRAVISION, SP_ADD_MONSTER_DROP_ITEMGROUP, SP_SP_LOSS_RATE, // 2038-2040 - SP_ADD_SKILL_BLOW, SP_SP_VANISH_RATE //2041 - //Before adding another, note that - //1077 (SP_FREE, previously disguise), - //are available! -}; - -enum { - LOOK_BASE,LOOK_HAIR,LOOK_WEAPON,LOOK_HEAD_BOTTOM,LOOK_HEAD_TOP,LOOK_HEAD_MID,LOOK_HAIR_COLOR,LOOK_CLOTHES_COLOR,LOOK_SHIELD,LOOK_SHOES -}; - -// CELLs for non-permanent cell-based effects (Pneuma, Basilica, Npcs, etc) -#define CELL_NPC 0x1 -#define CELL_REGEN 0x2 -#define CELL_PNEUMA 0x4 -#define CELL_SAFETYWALL 0x8 -#define CELL_LANDPROTECTOR 0x10 -#define CELL_BASILICA 0x20 -#define CELL_ICEWALL 0x80 -/* - * map_getcell()で使用されるフラグ - */ -typedef enum { - CELL_CHKWALL=0, // 壁(セルタイプ1) - CELL_CHKWATER, // 水場(セルタイプ3) - CELL_CHKGROUND, // 地面障害物(セルタイプ5) - CELL_CHKPASS, // 通過可能(セルタイプ1,5以外) - CELL_CHKREACH, // Same as PASS, but ignores the cell-stacking mod. - CELL_CHKNOPASS, // 通過不可(セルタイプ1,5) - CELL_CHKNOREACH, // Same as NOPASS, but ignores the cell-stacking mod. - CELL_GETTYPE, // セルタイプを返す - CELL_GETCELLTYPE, - CELL_CHKNPC=0x10, // タッチタイプのNPC(セルタイプ0x80フラグ) - CELL_CHKREGEN, // cells that improve regeneration - CELL_CHKPNEUMA, - CELL_CHKSAFETYWALL, - CELL_CHKBASILICA, // バジリカ(セルタイプ0x40フラグ) - CELL_CHKLANDPROTECTOR, - CELL_CHKICEWALL, - CELL_CHKSTACK, -} cell_t; -// map_setcell()で使用されるフラグ -enum { - CELL_SETNPC=0x10, // タッチタイプのNPCをセット - CELL_CLRNPC, - CELL_SETBASILICA, // バジリカをセット - CELL_CLRBASILICA, // バジリカをクリア - CELL_SETREGEN, // set regen cell - CELL_SETLANDPROTECTOR, //Set/Clear Magnetic Earth - CELL_CLRLANDPROTECTOR, - CELL_SETPNEUMA, - CELL_CLRPNEUMA, - CELL_SETSAFETYWALL, - CELL_CLRSAFETYWALL, - CELL_SETICEWALL, - CELL_CLRICEWALL, -}; - -struct chat_data { - struct block_list bl; - - unsigned char pass[8]; /* password */ - unsigned char title[61]; /* room title MAX 60 */ - unsigned char limit; /* join limit */ - unsigned char trigger; - unsigned char users; /* current users */ - unsigned char pub; /* room attribute */ - struct map_session_data *usersd[20]; - struct block_list *owner_; - struct block_list **owner; - char npc_event[50]; -}; - -extern struct map_data map[]; -extern int map_num; -extern int autosave_interval; -extern int minsave_interval; -extern int save_settings; -extern int agit_flag; -extern int night_flag; // 0=day, 1=night [Yor] -extern int enable_spy; //Determines if @spy commands are active. -extern char db_path[256]; - -// gat?ヨァ -int map_getcell(int,int,int,cell_t); -int map_getcellp(struct map_data*,int,int,cell_t); -void map_setcell(int,int,int,int); -extern int map_read_flag; // 0: grfォユォ。ォ、ォ・1: ォュォ罩テォキォ・2: ォュォ罩テォキォ・?) -enum { - READ_FROM_GAT, READ_FROM_AFM, - READ_FROM_BITMAP, CREATE_BITMAP, - READ_FROM_BITMAP_COMPRESSED, CREATE_BITMAP_COMPRESSED -}; - -extern char motd_txt[]; -extern char help_txt[]; -extern char help2_txt[]; -extern char charhelp_txt[]; - -extern char talkie_mes[]; - -extern char wisp_server_name[]; - -// 鯖全体情報 -void map_setusers(int); -int map_getusers(void); -// block削除関連 -int map_freeblock(struct block_list *bl); -int map_freeblock_lock(void); -//int map_freeblock_unlock(void); -int map_freeblock_unlock_sub (char *file, int lineno); -#define map_freeblock_unlock() map_freeblock_unlock_sub (__FILE__, __LINE__) -// block関連 -int map_addblock_sub(struct block_list *, int); -int map_delblock_sub(struct block_list *, int); -#define map_addblock(bl) map_addblock_sub(bl,1) -#define map_delblock(bl) map_delblock_sub(bl,1) -int map_moveblock(struct block_list *, int, int, unsigned int); -int map_foreachinrange(int (*)(struct block_list*,va_list),struct block_list *,int,int,...); -int map_foreachinshootrange(int (*)(struct block_list*,va_list),struct block_list *,int,int,...); -int map_foreachinarea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,...); -// -- moonsoul (added map_foreachincell) -int map_foreachincell(int (*)(struct block_list*,va_list),int,int,int,int,...); -int map_foreachinmovearea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,int,int,...); -int map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int type,...); // Celest -int map_foreachinmap(int (*)(struct block_list*,va_list),int,int,...); -int map_countnearpc(int,int,int); -//block関連に追加 -int map_count_oncell(int m,int x,int y,int type); -struct skill_unit *map_find_skill_unit_oncell(struct block_list *,int x,int y,int skill_id,struct skill_unit *); -// 一時的object関連 -int map_addobject(struct block_list *); -int map_delobject(int); -int map_delobjectnofree(int id); -void map_foreachobject(int (*)(struct block_list*,va_list),int,...); -int map_search_freecell(struct block_list *src, int m, short *x, short *y, int rx, int ry, int flag); -// -int map_quit(struct map_session_data *); -void map_quit_ack(struct map_session_data *); -// npc -int map_addnpc(int,struct npc_data *); - -// 床アイテム関連 -int map_clearflooritem_timer(int,unsigned int,int,int); -int map_removemobs_timer(int,unsigned int,int,int); -#define map_clearflooritem(id) map_clearflooritem_timer(0,0,id,1) -int map_addflooritem(struct item *,int,int,int,int,struct map_session_data *,struct map_session_data *,struct map_session_data *,int); - -// キャラid=>キャラ名 変換関連 -void map_addchariddb(int charid,char *name); -void map_delchariddb(int charid); -int map_reqchariddb(struct map_session_data * sd,int charid); -char * map_charid2nick(int); -struct map_session_data * map_charid2sd(int); - -struct map_session_data * map_id2sd(int); -struct block_list * map_id2bl(int); -int map_mapindex2mapid(unsigned short mapindex); -int map_mapname2mapid(char*); -int map_mapname2ipport(unsigned short,int*,int*); -int map_setipport(unsigned short map,unsigned long ip,int port); -int map_eraseipport(unsigned short map,unsigned long ip,int port); -int map_eraseallipport(void); -void map_addiddb(struct block_list *); -void map_deliddb(struct block_list *bl); -struct map_session_data** map_getallusers(int *users); -void map_foreachpc(int (*func)(DBKey,void*,va_list),...); -int map_foreachiddb(int (*)(DBKey,void*,va_list),...); -void map_addnickdb(struct map_session_data *); -struct map_session_data * map_nick2sd(char*); -int compare_item(struct item *a, struct item *b); - -// その他 -int map_check_dir(int s_dir,int t_dir); -int map_calc_dir( struct block_list *src,int x,int y); -int map_random_dir(struct block_list *bl, short *x, short *y); // [Skotlex] - -// Water functions... -// -int map_setwaterheight(int m, char *mapname, int height); -int map_waterheight(char *mapname); - -// path.cより -int path_search_real(struct walkpath_data *wpd,int m,int x0,int y0,int x1,int y1,int flag,cell_t flag2); -#define path_search(wpd,m,x0,y0,x1,y1,flag) path_search_real(wpd,m,x0,y0,x1,y1,flag,CELL_CHKNOPASS) -#define path_search2(wpd,m,x0,y0,x1,y1,flag) path_search_real(wpd,m,x0,y0,x1,y1,flag,CELL_CHKWALL) - -int path_search_long_real(struct shootpath_data *spd,int m,int x0,int y0,int x1,int y1,cell_t flag); -#define path_search_long(spd,m,x0,y0,x1,y1) path_search_long_real(spd,m,x0,y0,x1,y1,CELL_CHKWALL) - -int path_blownpos(int m,int x0,int y0,int dx,int dy,int count); - -// distance related functions [Skotlex] -#define check_distance_bl(bl1, bl2, distance) check_distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y, distance) -#define check_distance_blxy(bl, x1, y1, distance) check_distance((bl)->x-(x1), (bl)->y-(y1), distance) -#define check_distance_xy(x0, y0, x1, y1, distance) check_distance((x0)-(x1), (y0)-(y1), distance) -int check_distance(int dx, int dy, int distance); - -#define distance_bl(bl1, bl2) distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y) -#define distance_blxy(bl, x1, y1) distance((bl)->x-(x1), (bl)->y-(y1)) -#define distance_xy(x0, y0, x1, y1) distance((x0)-(x1), (y0)-(y1)) -unsigned int distance(int dx, int dy); - -int cleanup_sub(struct block_list *bl, va_list ap); - -void map_helpscreen(int flag); // [Valaris] -int map_delmap(char *mapname); - -int map_addmobtolist(unsigned short m, struct spawn_data *spawn); // [Wizputer] -void map_spawnmobs(int); // [Wizputer] -void map_removemobs(int); // [Wizputer] -void do_reconnect_map(void); //Invoked on map-char reconnection [Skotlex] - -//Added for own save method -int charsql_db_init(int method); - -extern char *INTER_CONF_NAME; -extern char *LOG_CONF_NAME; -extern char *MAP_CONF_NAME; -extern char *BATTLE_CONF_FILENAME; -extern char *ATCOMMAND_CONF_FILENAME; -extern char *CHARCOMMAND_CONF_FILENAME; -extern char *SCRIPT_CONF_NAME; -extern char *MSG_CONF_NAME; -extern char *GRF_PATH_FILENAME; - - -extern int charsave_method; //needed .. -extern char *map_server_dns; - -#ifndef TXT_ONLY - -// MySQL -#ifdef __WIN32 -#include -#include -#endif -#include - -extern char tmp_sql[65535]; - -extern int db_use_sqldbs; -extern MYSQL mmysql_handle; -extern MYSQL_RES* sql_res ; -extern MYSQL_ROW sql_row ; - -extern MYSQL charsql_handle; -extern MYSQL_RES* charsql_res; -extern MYSQL_ROW charsql_row; - -extern MYSQL logmysql_handle; -extern MYSQL_RES* logsql_res ; -extern MYSQL_ROW logsql_row ; - -extern int mail_server_enable; -extern MYSQL mail_handle; -extern MYSQL_RES* mail_res ; -extern MYSQL_ROW mail_row ; - -extern char item_db_db[32]; -extern char item_db2_db[32]; -extern char mob_db_db[32]; -extern char mob_db2_db[32]; -extern char char_db[32]; -extern char mail_db[32]; - -#endif /* not TXT_ONLY */ -//Useful typedefs from jA [Skotlex] -typedef struct map_session_data TBL_PC; -typedef struct npc_data TBL_NPC; -typedef struct mob_data TBL_MOB; -typedef struct flooritem_data TBL_ITEM; -typedef struct chat_data TBL_CHAT; -typedef struct skill_unit TBL_SKILL; -typedef struct pet_data TBL_PET; -typedef struct homun_data TBL_HOM; - -#define BL_CAST(type_, bl , dest) \ - (((bl) == NULL || (bl)->type != type_) ? ((dest) = NULL, 0) : ((dest) = (T ## type_ *)(bl), 1)) - - -extern int lowest_gm_level; -extern char main_chat_nick[16]; - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _MAP_H_ +#define _MAP_H_ + +#include +#include "../common/mmo.h" +#include "../common/mapindex.h" +#include "../common/db.h" + +//Uncomment to enable the Cell Stack Limit mod. +//It's only config is the battle_config cell_stack_limit. +//Only chars affected are those defined in BL_CHAR (mobs and players currently) +//#define CELL_NOSTACK + +//Uncomment to enable circular area checks. +//By default, all range checks in Aegis are of Square shapes, so a weapon range +// of 10 allows you to attack from anywhere within a 21x21 area. +//Enabling this changes such checks to circular checks, which is more realistic, +// but is not the official behaviour. +//#define CIRCULAR_AREA + +#define MAX_PC_CLASS 4050 +#define PC_CLASS_BASE 0 +#define PC_CLASS_BASE2 (PC_CLASS_BASE + 4001) +#define PC_CLASS_BASE3 (PC_CLASS_BASE2 + 22) +#define MAX_NPC_PER_MAP 512 +#define BLOCK_SIZE 8 +#define AREA_SIZE battle_config.area_size +#define LIFETIME_FLOORITEM 60 +#define DAMAGELOG_SIZE 30 +#define LOOTITEM_SIZE 10 +#define MAX_STATUSCHANGE 250 +//Quick defines to know which are the min-max common ailments. [Skotlex] +//Because of the way the headers are included.. these must be replaced for actual values. +//Remember to update as needed! Min is SC_STONE and max is SC_DPOISON currently. +#define SC_COMMON_MIN 0 +#define SC_COMMON_MAX 10 + +#define MAX_SKILL_LEVEL 100 +#define MAX_SKILLUNITGROUP 25 +#define MAX_SKILLUNITGROUPTICKSET 25 +#define MAX_SKILLTIMERSKILL 15 +#define MAX_MOBSKILL 50 +#define MAX_MOB_LIST_PER_MAP 128 +#define MAX_EVENTQUEUE 2 +#define MAX_EVENTTIMER 32 +#define NATURAL_HEAL_INTERVAL 500 +#define MAX_FLOORITEM 500000 +#define MAX_LEVEL 1000 +#define MAX_WALKPATH 32 +#define MAX_DROP_PER_MAP 48 +#define MAX_IGNORE_LIST 80 +#define MAX_VENDING 12 +#define MOBID_EMPERIUM 1288 + +#define MAX_PC_BONUS 10 +//Designed for search functions, species max number of matches to display. +#define MAX_SEARCH 5 +#define MAX_DUEL 1024 + +#define map_id2index(id) map[(id)].index + +//The following system marks a different job ID system used by the map server, +//which makes a lot more sense than the normal one. [Skotlex] +// +//These marks the "level" of the job. +#define JOBL_2_1 0x100 //256 +#define JOBL_2_2 0x200 //512 +#define JOBL_2 0x300 + +#define JOBL_UPPER 0x1000 //4096 +#define JOBL_BABY 0x2000 //8192 + +//for filtering and quick checking. +#define MAPID_UPPERMASK 0x0fff +#define MAPID_BASEMASK 0x00ff +//First Jobs +//Note the oddity of the novice: +//Super Novices are considered the 2-1 version of the novice! Novices are considered a first class type, too... +enum { + MAPID_NOVICE = 0x0, + MAPID_SWORDMAN, + MAPID_MAGE, + MAPID_ARCHER, + MAPID_ACOLYTE, + MAPID_MERCHANT, + MAPID_THIEF, + MAPID_TAEKWON, + MAPID_WEDDING, + MAPID_GUNSLINGER, + MAPID_NINJA, + MAPID_XMAS, // [Valaris] +//2_1 classes + MAPID_SUPER_NOVICE = JOBL_2_1|0x0, + MAPID_KNIGHT, + MAPID_WIZARD, + MAPID_HUNTER, + MAPID_PRIEST, + MAPID_BLACKSMITH, + MAPID_ASSASSIN, + MAPID_STAR_GLADIATOR, +//2_2 classes + MAPID_CRUSADER = JOBL_2_2|0x1, + MAPID_SAGE, + MAPID_BARDDANCER, + MAPID_MONK, + MAPID_ALCHEMIST, + MAPID_ROGUE, + MAPID_SOUL_LINKER, +//1-1, advanced + MAPID_NOVICE_HIGH = JOBL_UPPER|0x0, + MAPID_SWORDMAN_HIGH, + MAPID_MAGE_HIGH, + MAPID_ARCHER_HIGH, + MAPID_ACOLYTE_HIGH, + MAPID_MERCHANT_HIGH, + MAPID_THIEF_HIGH, +//2_1 advanced + MAPID_LORD_KNIGHT = JOBL_UPPER|JOBL_2_1|0x1, + MAPID_HIGH_WIZARD, + MAPID_SNIPER, + MAPID_HIGH_PRIEST, + MAPID_WHITESMITH, + MAPID_ASSASSIN_CROSS, +//2_2 advanced + MAPID_PALADIN = JOBL_UPPER|JOBL_2_2|0x1, + MAPID_PROFESSOR, + MAPID_CLOWNGYPSY, + MAPID_CHAMPION, + MAPID_CREATOR, + MAPID_STALKER, +//1-1 baby + MAPID_BABY = JOBL_BABY|0x0, + MAPID_BABY_SWORDMAN, + MAPID_BABY_MAGE, + MAPID_BABY_ARCHER, + MAPID_BABY_ACOLYTE, + MAPID_BABY_MERCHANT, + MAPID_BABY_THIEF, + MAPID_BABY_TAEKWON, +//2_1 baby + MAPID_SUPER_BABY = JOBL_BABY|JOBL_2_1|0x0, + MAPID_BABY_KNIGHT, + MAPID_BABY_WIZARD, + MAPID_BABY_HUNTER, + MAPID_BABY_PRIEST, + MAPID_BABY_BLACKSMITH, + MAPID_BABY_ASSASSIN, + MAPID_BABY_STAR_GLADIATOR, +//2_2 baby + MAPID_BABY_CRUSADER = JOBL_BABY|JOBL_2_2|0x1, + MAPID_BABY_SAGE, + MAPID_BABY_BARDDANCER, + MAPID_BABY_MONK, + MAPID_BABY_ALCHEMIST, + MAPID_BABY_ROGUE, + MAPID_BABY_SOUL_LINKER, +}; + +//Don't change this, as the client seems to always send/receive 80 characters as it currently is. [Skotlex] +#define MESSAGE_SIZE 80 + +#define DEFAULT_AUTOSAVE_INTERVAL 5*60*1000 + +//Specifies maps where players may hit each other +#define map_flag_vs(m) (map[m].flag.pvp || map[m].flag.gvg_dungeon || map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle)) +//Specifies maps that have special GvG/WoE restrictions +#define map_flag_gvg(m) (map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle)) +//Specifies if the map is tagged as GvG/WoE (regardless of agit_flag status) +#define map_flag_gvg2(m) (map[m].flag.gvg || map[m].flag.gvg_castle) +//Caps values to min/max +#define cap_value(a, min, max) (a>=max?max:a<=min?min:a) + +//This stackable implementation does not means a BL can be more than one type at a time, but it's +//meant to make it easier to check for multiple types at a time on invocations such as +// map_foreach* calls [Skotlex] +enum { + BL_NUL = 0x000, + BL_PC = 0x001, + BL_MOB = 0x002, + BL_PET = 0x004, + BL_HOM = 0x008, //[blackhole89] + BL_ITEM = 0x010, + BL_SKILL = 0x020, + BL_NPC = 0x040, + BL_CHAT = 0x080, +}; + +//For common mapforeach calls. Since pets cannot be affected, they aren't included here yet. +#define BL_CHAR (BL_PC|BL_MOB|BL_HOM) +#define BL_ALL 0xfff + +enum { WARP, SHOP, SCRIPT, MONS }; + +enum { + RC_FORMLESS=0, + RC_UNDEAD, + RC_BRUTE, + RC_PLANT, + RC_INSECT, + RC_FISH, + RC_DEMON, + RC_DEMIHUMAN, + RC_ANGEL, + RC_DRAGON, + RC_BOSS, + RC_NONBOSS, + RC_MAX +}; + +enum { + ELE_NEUTRAL=0, + ELE_WATER, + ELE_EARTH, + ELE_FIRE, + ELE_WIND, + ELE_POISON, + ELE_HOLY, + ELE_DARK, + ELE_GHOST, + ELE_UNDEAD, + ELE_MAX +}; + +enum { + IG_BLUEBOX=1, + IG_VIOLETBOX, //2 + IG_CARDALBUM, //3 + IG_GIFTBOX, //4 + IG_SCROLLBOX, //5 + IG_FINDINGORE, //6 + IG_COOKIEBAG, //7 + IG_POTION, //8 + IG_HERBS, //9 + IG_FRUITS, //10 + IG_MEAT, //11 + IG_CANDY, //12 + IG_JUICE, //13 + IG_FISH, //14 + IG_BOXES, //15 + IG_GEMSTONE, //16 + IG_JELLOPY, //17 + IG_ORE, //18 + IG_FOOD, //19 + IG_RECOVERY, //20 + IG_MINERALS, //21 + IG_TAMING, //22 + IG_SCROLLS, //23 + IG_QUIVERS, //24 + IG_MASKS, //25 + IG_ACCESORY, //26 + IG_JEWELS, //27 + IG_GIFTBOX_1, //28 + IG_GIFTBOX_2, //29 + IG_GIFTBOX_3, //30 + IG_GIFTBOX_4, //31 + IG_EGGBOY, //32 + IG_EGGGIRL, //33 + IG_GIFTBOXCHINA, //34 + IG_LOTTOBOX, //35 + MAX_ITEMGROUP, +} item_group_list; + +enum { + ATF_SELF=0x01, + ATF_TARGET=0x02, + ATF_SHORT=0x04, + ATF_LONG=0x08 +} auto_trigger_flag; + +struct block_list { + struct block_list *next,*prev; + int id; + short m,x,y; + unsigned char type; + unsigned char subtype; +}; + +struct walkpath_data { + unsigned char path_len,path_pos,path_half; + unsigned char path[MAX_WALKPATH]; +}; +struct shootpath_data { + int rx,ry,len; + int x[MAX_WALKPATH]; + int y[MAX_WALKPATH]; +}; + +struct skill_timerskill { + int timer; + int src_id; + int target_id; + int map; + short x,y; + short skill_id,skill_lv; + int type; + int flag; +}; + +struct skill_unit_group; +struct skill_unit { + struct block_list bl; + + struct skill_unit_group *group; + + int limit; + int val1,val2; + short alive,range; +}; + +struct skill_unit_group { + int src_id; + int party_id; + int guild_id; + int map; + int target_flag; //Holds BCT_* flag for battle_check_target + int bl_flag; //Holds BL_* flag for map_foreachin* functions + unsigned int tick; + int limit,interval; + + int skill_id,skill_lv; + int val1,val2,val3; + char *valstr; + int unit_id; + int group_id; + int unit_count,alive_count; + struct skill_unit *unit; + struct { + unsigned ammo_consume : 1; + unsigned magic_power : 1; + unsigned into_abyss : 1; + unsigned song_dance : 2; //0x1 Song/Dance, 0x2 Ensemble + } state; +}; +struct skill_unit_group_tickset { + unsigned int tick; + int id; +}; + +struct unit_data { + struct block_list *bl; + struct walkpath_data walkpath; + struct skill_timerskill *skilltimerskill[MAX_SKILLTIMERSKILL]; + struct skill_unit_group *skillunit[MAX_SKILLUNITGROUP]; + struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET]; + short attacktarget_lv; + short to_x,to_y; + short skillx,skilly; + short skillid,skilllv; + int skilltarget; + int skilltimer; + int target; + int attacktimer; + int walktimer; + int chaserange; + unsigned int attackabletime; + unsigned int canact_tick; + unsigned int canmove_tick; + unsigned char dir; + unsigned char walk_count; + struct { + unsigned change_walk_target : 1 ; + unsigned skillcastcancel : 1 ; + unsigned attack_continue : 1 ; + unsigned walk_easy : 1 ; + unsigned running : 1; + } state; +}; + +//Basic damage info of a weapon +//Required because players have two of these, one in status_data and another +//for their left hand weapon. +struct weapon_atk { + unsigned short atk, atk2; + unsigned short range; + unsigned char ele; +}; + +//For holding basic status (which can be modified by status changes) +struct status_data { + unsigned int + hp, sp, + max_hp, max_sp; + unsigned short + str, agi, vit, int_, dex, luk, + batk, + matk_min, matk_max, + speed, + amotion, adelay, dmotion, + mode; + short + hit, flee, cri, flee2, + def2, mdef2, + aspd_rate; + unsigned char + def_ele, ele_lv, + size, race; + signed char + def, mdef; + struct weapon_atk rhw, *lhw; //Right Hand/Left Hand Weapon. Only players have a lhw (hence it's a pointer) +}; + +struct script_reg { + int index; + int data; +}; +struct script_regstr { + int index; + char data[256]; +}; + +struct status_change_entry { + int timer; + int val1,val2,val3,val4; +}; + +struct status_change { + struct status_change_entry data[MAX_STATUSCHANGE]; + short count; + unsigned short opt1,opt2; + unsigned int opt3, option; //Note that older packet versions use short here. +}; + +struct vending { + short index; + unsigned short amount; + unsigned int value; +}; + +struct weapon_data { + int atkmods[3]; + // all the variables except atkmods get zero'ed in each call of status_calc_pc + // NOTE: if you want to add a non-zeroed variable, you need to update the malloc_set call + // in status_calc_pc as well! All the following are automatically zero'ed. [Skotlex] + int overrefine; + int star; + int ignore_def_ele; + int ignore_def_race; + int def_ratio_atk_ele; + int def_ratio_atk_race; + int addele[ELE_MAX]; + int addrace[RC_MAX]; + int addrace2[RC_MAX]; + int addsize[3]; + + short ignore_def_mob; + struct drain_data { + short rate; + short per; + short value; + unsigned type:1; + } hp_drain[RC_MAX], sp_drain[RC_MAX]; + + short add_damage_classid[MAX_PC_BONUS]; + int add_damage_classrate[MAX_PC_BONUS]; + int add_damage_class_count; +}; + +struct view_data { + unsigned short + class_, + weapon, + shield, //Or left-hand weapon. + head_top, + head_mid, + head_bottom, + hair_style, + hair_color, + cloth_color; + char sex; + unsigned dead_sit : 2; +}; + +//Additional regen data that only players have. +struct regen_data_sub { + unsigned short + hp,sp; + + //tick accumulation before healing. + struct { + unsigned int hp,sp; + } tick; + + //Regen rates (where every 1 means +100% regen) + struct { + unsigned char hp,sp; + } rate; +}; + +struct regen_data { + + unsigned short flag; //Marks what stuff you may heal or not. + unsigned short + hp,sp,shp,ssp; + + //tick accumulation before healing. + struct { + unsigned int hp,sp,shp,ssp; + } tick; + + //Regen rates (where every 1 means +100% regen) + struct { + unsigned char + hp,sp,shp,ssp; + } rate; + + struct { + unsigned walk:1; //Can you regen even when walking? + unsigned gc:1; //Tags when you should have double regen due to GVG castle + unsigned overweight :2; //overweight state (1: 50%, 2: 90%) + unsigned block :2; //Block regen flag (1: Hp, 2: Sp) + } state; + + //skill-regen, sitting-skill-regen (since not all chars with regen need it) + struct regen_data_sub *sregen, *ssregen; +}; + +struct party_member_data { + struct map_session_data *sd; + unsigned int hp; //For HP,x,y refreshing. + unsigned short x, y; +}; + +struct party_data { + struct party party; + struct party_member_data data[MAX_PARTY]; + unsigned char itemc; //For item distribution. + struct { + unsigned monk : 1; //There's at least one monk in party? + unsigned sg : 1; //There's at least one Star Gladiator in party? + unsigned snovice :1; //There's a Super Novice + unsigned tk : 1; //There's a taekwon + } state; +}; + +struct npc_data; +struct pet_db; +struct homunculus_db; //[orn] +struct item_data; +struct square; + +struct map_session_data { + struct block_list bl; + struct unit_data ud; + struct view_data vd; + struct status_data base_status, battle_status; + struct weapon_atk base_lhw, battle_lhw; //Left-hand weapon atk data. + struct status_change sc; + struct regen_data regen; + struct regen_data_sub sregen, ssregen; + //NOTE: When deciding to add a flag to state or special_state, take into consideration that state is preserved in + //status_calc_pc, while special_state is recalculated in each call. [Skotlex] + struct { + unsigned auth : 1; + unsigned menu_or_input : 1; + unsigned dead_sit : 2; + unsigned waitingdisconnect : 1; + unsigned lr_flag : 2; + unsigned connect_new : 1; + unsigned arrow_atk : 1; + unsigned skill_flag : 1; + unsigned gangsterparadise : 1; + unsigned rest : 1; + unsigned storage_flag : 2; //0: closed, 1: Normal Storage open, 2: guild storage open [Skotlex] + unsigned snovice_flag : 4; + // originally by Qamera, adapted by celest + unsigned event_death : 1; + unsigned event_kill_pc : 1; + unsigned event_disconnect : 1; + unsigned event_kill_mob : 1; + unsigned event_baselvup : 1; + unsigned event_joblvup : 1; + unsigned event_loadmap : 1; + // Abracadabra bugfix by Aru + unsigned abra_flag : 1; + unsigned autotrade : 1; //By Fantik + unsigned reg_dirty : 3; //By Skotlex (marks whether registry variables have been saved or not yet) + unsigned showdelay :1; + unsigned showexp :1; + unsigned showzeny :1; + unsigned mainchat :1; //[LuzZza] + unsigned noask :1; // [LuzZza] + unsigned trading :1; //[Skotlex] is 1 only after a trade has started. + unsigned deal_locked :2; //1: Clicked on OK. 2: Clicked on TRADE + unsigned party_sent :1; + unsigned guild_sent :1; + unsigned monster_ignore :1; // for monsters to ignore a character [Valaris] [zzo] + unsigned size :2; // for tiny/large types + unsigned night :1; //Holds whether or not the player currently has the SI_NIGHT effect on. [Skotlex] + unsigned finalsave :1; //Signals whether the final save for the char was done or not yet. Meant to prevent exploits and the like. [Skotlex] + unsigned blockedmove :1; + unsigned using_fake_npc :1; + unsigned rewarp :1; //Signals that a player should warp as soon as he is done loading a map. [Skotlex] + unsigned killer : 1; + unsigned killable : 1; + unsigned doridori : 1; + unsigned ignoreAll : 1; + unsigned short autoloot; + struct guild *gmaster_flag; + } state; + struct { + unsigned char no_weapon_damage, no_magic_damage, no_misc_damage; + unsigned restart_full_recover : 1; + unsigned no_castcancel : 1; + unsigned no_castcancel2 : 1; + unsigned no_sizefix : 1; + unsigned no_gemstone : 1; + unsigned intravision : 1; // Maya Purple Card effect allowing to see Hiding/Cloaking people [DracoRPG] + unsigned perfect_hiding : 1; // [Valaris] + } special_state; + int login_id1, login_id2; + unsigned short class_; //This is the internal job ID used by the map server to simplify comparisons/queries/etc. [Skotlex] + + int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18 + struct mmo_charstatus status; + struct registry save_reg; + + struct item_data *inventory_data[MAX_INVENTORY]; + short equip_index[11]; + unsigned int weight,max_weight; + int cart_weight,cart_max_weight,cart_num,cart_max_num; + int fd; + unsigned short mapindex; + unsigned short prev_speed,prev_adelay; + unsigned char head_dir; //0: Look forward. 1: Look right, 2: Look left. + unsigned int client_tick; + int npc_id,areanpc_id,npc_shopid; + int npc_item_flag; //Marks the npc_id with which you can use items during interactions with said npc (see script command enable_itemuse) + int npc_menu, max_menu; + int npc_amount; + struct script_state *st; + char npc_str[256]; + int npc_timer_id; //For player attached npc timers. [Skotlex] + unsigned int chatID; + time_t idletime; + + struct{ + char name[NAME_LENGTH]; + } ignore[MAX_IGNORE_LIST]; + + int followtimer; // [MouseJstr] + int followtarget; + + time_t emotionlasttime; // to limit flood with emotion packets + + short skillitem,skillitemlv; + short skillid_old,skilllv_old; + short skillid_dance,skilllv_dance; + char blockskill[MAX_SKILL]; // [celest] + int cloneskill_id; + int menuskill_id, menuskill_lv; + + int invincible_timer; + unsigned int canlog_tick; + unsigned int canuseitem_tick; // [Skotlex] + unsigned int cantalk_tick; + + short weapontype1,weapontype2; + short disguise; // [Valaris] + + struct weapon_data right_weapon, left_weapon; + + // here start arrays to be globally zeroed at the beginning of status_calc_pc() + int param_bonus[6],param_equip[6]; //Stores card/equipment bonuses. + int subele[ELE_MAX]; + int subrace[RC_MAX]; + int subrace2[RC_MAX]; + int subsize[3]; + int reseff[SC_COMMON_MAX-SC_COMMON_MIN+1]; + int weapon_coma_ele[ELE_MAX]; + int weapon_coma_race[RC_MAX]; + int weapon_atk[16]; + int weapon_atk_rate[16]; + int arrow_addele[ELE_MAX]; + int arrow_addrace[RC_MAX]; + int arrow_addsize[3]; + int magic_addele[ELE_MAX]; + int magic_addrace[RC_MAX]; + int magic_addsize[3]; + int critaddrace[RC_MAX]; + int expaddrace[RC_MAX]; + int itemgrouphealrate[MAX_ITEMGROUP]; + short sp_gain_race[RC_MAX]; + // zeroed arrays end here. + // zeroed structures start here + struct s_autospell{ + short id, lv, rate, card_id; + } autospell[MAX_PC_BONUS], autospell2[MAX_PC_BONUS]; + struct s_addeffect{ + short id, rate, arrow_rate; + unsigned char flag; + } addeff[MAX_PC_BONUS], addeff2[MAX_PC_BONUS]; + struct { //skillatk raises bonus dmg% of skills, skillblown increases bonus blewcount for some skills. + short id, val; + } skillatk[MAX_PC_BONUS], skillblown[MAX_PC_BONUS]; + struct { + short class_, rate; + } add_def[MAX_PC_BONUS], add_mdef[MAX_PC_BONUS], + add_dmg[MAX_PC_BONUS], add_mdmg[MAX_PC_BONUS]; + struct s_add_drop { + short id, group; + int race, rate; + } add_drop[MAX_PC_BONUS]; + struct { + int nameid; + int rate; + } itemhealrate[MAX_PC_BONUS]; + // zeroed structures end here + // zeroed vars start here. + int arrow_atk,arrow_ele,arrow_cri,arrow_hit; + int nsshealhp,nsshealsp; + int critical_def,double_rate; + int long_attack_atk_rate; //Long range atk rate, not weapon based. [Skotlex] + int near_attack_def_rate,long_attack_def_rate,magic_def_rate,misc_def_rate; + int ignore_mdef_ele; + int ignore_mdef_race; + int perfect_hit; + int perfect_hit_add; + int get_zeny_rate; + int get_zeny_num; //Added Get Zeny Rate [Skotlex] + int double_add_rate; + int short_weapon_damage_return,long_weapon_damage_return; + int magic_damage_return; // AppleGirl Was Here + int random_attack_increase_add,random_attack_increase_per; // [Valaris] + int break_weapon_rate,break_armor_rate; + int crit_atk_rate; + int hp_loss_rate; + int sp_loss_rate; + int classchange; // [Valaris] + int speed_add_rate, aspd_add_rate; + unsigned int setitem_hash, setitem_hash2; //Split in 2 because shift operations only work on int ranges. [Skotlex] + + short splash_range, splash_add_range; + short add_steal_rate; + short hp_loss_value; + short sp_loss_value; + short hp_loss_type; + short sp_gain_value, hp_gain_value; + short sp_vanish_rate; + short sp_vanish_per; + short add_drop_count; + unsigned short unbreakable; // chance to prevent ANY equipment breaking [celest] + unsigned short unbreakable_equip; //100% break resistance on certain equipment + unsigned short unstripable_equip; + short add_def_count,add_mdef_count; + short add_dmg_count,add_mdmg_count; + + // zeroed vars end here. + + int castrate,delayrate,hprate,sprate,dsprate; + int atk_rate; + int speed_rate,hprecov_rate,sprecov_rate; + int matk_rate; + int critical_rate,hit_rate,flee_rate,flee2_rate,def_rate,def2_rate,mdef_rate,mdef2_rate; + + int hp_loss_tick; + int sp_loss_tick; + + int itemid; + short itemindex; //Used item's index in sd->inventory [Skotlex] + + short catch_target_class; // pet catching, stores a pet class to catch (short now) [zzo] + + short spiritball, spiritball_old; + int spirit_timer[MAX_SKILL_LEVEL]; + + unsigned char potion_success_counter; //Potion successes in row counter + unsigned char mission_count; //Stores the bounty kill count for TK_MISSION + short mission_mobid; //Stores the target mob_id for TK_MISSION + int die_counter; //Total number of times you've died + int devotion[5]; //Stores the char IDs of chars devoted to. + int reg_num; //Number of registries (type numeric) + int regstr_num; //Number of registries (type string) + + struct script_reg *reg; + struct script_regstr *regstr; + + int trade_partner; + struct { + struct { + int index, amount; + } item[10]; + int zeny, weight; + } deal; + + int party_invite,party_invite_account; + + int guild_invite,guild_invite_account; + int guild_emblem_id,guild_alliance,guild_alliance_account; + short guild_x,guild_y; // For guildmate position display. [Skotlex] should be short [zzo] + int guildspy; // [Syrus22] + int partyspy; // [Syrus22] + + int vender_id; + int vend_num; + char message[MESSAGE_SIZE]; + struct vending vending[MAX_VENDING]; + + struct pet_data *pd; + struct homun_data *hd; // [blackhole89] + + struct{ + int m; //-1 - none, other: map index corresponding to map name. + unsigned short index; //map index + }feel_map[3];// 0 - Sun; 1 - Moon; 2 - Stars + short hate_mob[3]; + + unsigned int pvp_timer; + short pvp_point; + unsigned short pvp_rank, pvp_lastusers; + unsigned short pvp_won, pvp_lost; + + char eventqueue[MAX_EVENTQUEUE][50]; + int eventtimer[MAX_EVENTTIMER]; + unsigned short eventcount; // [celest] + + unsigned char change_level; // [celest] + + char fakename[NAME_LENGTH]; // fake names [Valaris] + +#ifndef TXT_ONLY + int mail_counter; // mail counter for mail system [Valaris] +#endif + + int duel_group; // duel vars [LuzZza] + int duel_invite; + + char away_message[128]; // [LuzZza] + +}; + +struct { + int members_count; + int invites_count; + int max_players_limit; +} duel_list[MAX_DUEL]; + +int duel_count; + +struct npc_timerevent_list { + int timer,pos; +}; +struct npc_label_list { + char name[NAME_LENGTH]; + int pos; +}; +struct npc_item_list { + unsigned int nameid,value; +}; +struct npc_data { + struct block_list bl; + struct unit_data ud; //Because they need to be able to move.... + struct view_data *vd; + struct status_change sc; //They can't have status changes, but.. they want the visual opt values. + short n; + short class_; + short speed; + unsigned char name[NAME_LENGTH]; + unsigned char exname[NAME_LENGTH]; + int chat_id; + unsigned int next_walktime; + + char eventqueue[MAX_EVENTQUEUE][50]; + int eventtimer[MAX_EVENTTIMER]; + short arenaflag; + + void *chatdb; + struct npc_data *master_nd; + + union { + struct { + struct script_code *script; + short xs,ys; + int guild_id; + int timer,timerid,timeramount,rid; + unsigned int timertick; + struct npc_timerevent_list *timer_event; + int label_list_num; + struct npc_label_list *label_list; + int src_id; + } scr; + struct npc_item_list shop_item[1]; + struct { + short xs,ys; + short x,y; + unsigned short mapindex; + } warp; + } u; + //Do NOT place anything afterwards... shop data NPC will override any variables from here and on! [Skotlex] +}; + +//For quick linking to a guardian's info. [Skotlex] +struct guardian_data { + int number; //0-MAX_GUARDIANS-1 = Guardians. MAX_GUARDIANS = Emperium. + int guild_id; + int emblem_id; + int guardup_lv; //Level of GD_GUARDUP skill. + char guild_name[NAME_LENGTH]; + struct guild_castle* castle; +}; + +// Mob List Held in memory for Dynamic Mobs [Wizputer] +// Expanded to specify all mob-related spawn data by [Skotlex] +struct spawn_data { + short class_; //Class, used because a mob can change it's class + unsigned short m,x,y; //Spawn information (map, point, spawn-area around point) + signed short xs,ys; + unsigned short num; //Number of mobs using this structure. + unsigned int level; //Custom level. + unsigned int delay1,delay2; //Min delay before respawning after spawn/death + struct { + unsigned size :2; //Holds if mob has to be tiny/large + unsigned ai :2; //Holds if mob is special ai. + } state; + char name[NAME_LENGTH],eventname[50]; //Name/event +}; + + +struct mob_data { + struct block_list bl; + struct unit_data ud; + struct view_data *vd; + struct status_data status, *base_status; //Second one is in case of leveling up mobs, or tiny/large mobs. + struct status_change sc; + struct mob_db *db; //For quick data access (saves doing mob_db(md->class_) all the time) [Skotlex] + char name[NAME_LENGTH]; + struct { + unsigned size : 2; //Small/Big monsters. + unsigned cached : 1; //Cached mobs for dynamic mob unloading [Skotlex] + unsigned ai : 2; //Special ai for summoned monsters. + //0: Normal mob. + //1: Standard summon, attacks mobs. + //2: Alchemist Marine Sphere + //3: Alchemist Summon Flora + } special_state; //Special mob information that does not needs to be zero'ed on mob respawn. + struct { + unsigned skillstate : 8; + unsigned aggressive : 1; //Signals whether the mob AI is in aggressive mode or reactive mode. [Skotlex] + unsigned char steal_flag; //number of steal tries (to prevent steal exploit on mobs with few items) [Lupus] + unsigned steal_coin_flag : 1; + unsigned soul_change_flag : 1; // Celest + unsigned alchemist: 1; + unsigned no_random_walk: 1; + unsigned killer: 1; + int provoke_flag; // Celest + } state; + struct guardian_data* guardian_data; + struct { + int id; + int dmg; + unsigned flag : 1; //0: Normal. 1: Homunc exp + } dmglog[DAMAGELOG_SIZE]; + struct spawn_data *spawn; //Spawn data. + struct item *lootitem; + short spawn_n; //Spawn data index on the map server. + short class_; + short attacked_count; + unsigned char attacked_players; + unsigned int tdmg; //Stores total damage given to the mob, for exp calculations. [Skotlex] + int level; + int target_id,attacked_id; + unsigned int next_walktime; + unsigned int last_deadtime,last_spawntime,last_thinktime,last_linktime; + short move_fail_count; + short lootitem_count; + short min_chase; + + int deletetimer; + int master_id,master_dist; + + struct npc_data *nd; + unsigned short callback_flag; + + short skillidx; + unsigned int skilldelay[MAX_MOBSKILL]; + char npc_event[50]; +}; + +/* [blackhole89] */ +struct homun_data { + struct block_list bl; + struct unit_data ud; + struct view_data *vd; + struct status_data base_status, battle_status; + struct status_change sc; + struct regen_data regen; + struct homunculus_db *homunculusDB; //[orn] + struct s_homunculus homunculus ; //[orn] + + struct map_session_data *master; //pointer back to its master + int hungry_timer; //[orn] + unsigned int exp_next; + char blockskill[MAX_SKILL]; // [orn] +}; + +struct pet_data { + struct block_list bl; + struct unit_data ud; + struct view_data vd; + struct s_pet pet; + struct status_data status; + struct mob_db *db; + struct pet_db *petDB; + int pet_hungry_timer; + int target_id; + short n; + struct { + unsigned skillbonus : 1; + } state; + int move_fail_count; + unsigned int next_walktime,last_thinktime; + short rate_fix; //Support rate as modified by intimacy (1000 = 100%) [Skotlex] + + struct pet_recovery { //Stat recovery + unsigned short type; //Status Change id + unsigned short delay; //How long before curing (secs). + int timer; + } *recovery; //[Valaris] / Reimplemented by [Skotlex] + + struct pet_bonus { + unsigned short type; //bStr, bVit? + unsigned short val; //Qty + unsigned short duration; //in secs + unsigned short delay; //Time before recasting (secs) + int timer; + } *bonus; //[Valaris] / Reimplemented by [Skotlex] + + struct pet_skill_attack { //Attack Skill + unsigned short id; + unsigned short lv; + unsigned short div_; //0 = Normal skill. >0 = Fixed damage (lv), fixed div_. + unsigned short rate; //Base chance of skill ocurrance (10 = 10% of attacks) + unsigned short bonusrate; //How being 100% loyal affects cast rate (10 = At 1000 intimacy->rate+10% + } *a_skill; //[Skotlex] + + struct pet_skill_support { //Support Skill + unsigned short id; + unsigned short lv; + unsigned short hp; //Max HP% for skill to trigger (50 -> 50% for Magnificat) + unsigned short sp; //Max SP% for skill to trigger (100 = no check) + unsigned short delay; //Time (secs) between being able to recast. + int timer; + } *s_skill; //[Skotlex] + + struct pet_loot { + struct item *item; + unsigned short count; + unsigned short weight; + unsigned short max; + } *loot; //[Valaris] / Rewritten by [Skotlex] + + struct map_session_data *msd; +}; + +enum { ATK_LUCKY=1,ATK_FLEE,ATK_DEF}; // 囲まれペナルティ計算用 + +struct map_data { + char name[MAP_NAME_LENGTH]; + unsigned short index; //Index is the map index used by the mapindex* functions. + unsigned char *gat; // NULLなら下のmap_data_other_serverとして扱う + unsigned char *cell; //Contains temporary cell data that is set/unset on tiles. +#ifdef CELL_NOSTACK + unsigned char *cell_bl; //Holds amount of bls in any given cell. +#endif + char *alias; // [MouseJstr] + struct block_list **block; + struct block_list **block_mob; + int *block_count,*block_mob_count; + int m; + short xs,ys; + short bxs,bys; + int water_height; + int npc_num; + int users; + struct { + unsigned alias : 1; + unsigned nomemo : 1; + unsigned noteleport : 1; + unsigned noreturn : 1; + unsigned monster_noteleport : 1; + unsigned nosave : 1; + unsigned nobranch : 1; + unsigned noexppenalty : 1; + unsigned pvp : 1; + unsigned pvp_noparty : 1; + unsigned pvp_noguild : 1; + unsigned pvp_nightmaredrop :1; + unsigned pvp_nocalcrank : 1; + unsigned gvg_castle : 1; + unsigned gvg : 1; // Now it identifies gvg versus maps that are active 24/7 + unsigned gvg_dungeon : 1; // Celest + unsigned gvg_noparty : 1; + unsigned nozenypenalty : 1; + unsigned notrade : 1; + unsigned noskill : 1; + unsigned nowarp : 1; + unsigned nowarpto : 1; + unsigned noicewall : 1; // [Valaris] + unsigned snow : 1; // [Valaris] + unsigned clouds : 1; + unsigned clouds2 : 1; // [Valaris] + unsigned fog : 1; // [Valaris] + unsigned fireworks : 1; + unsigned sakura : 1; // [Valaris] + unsigned leaves : 1; // [Valaris] + unsigned rain : 1; // [Valaris] + unsigned indoors : 1; // celest + unsigned nogo : 1; // [Valaris] + unsigned nobaseexp : 1; // [Lorky] added by Lupus + unsigned nojobexp : 1; // [Lorky] + unsigned nomobloot : 1; // [Lorky] + unsigned nomvploot : 1; // [Lorky] + unsigned nightenabled :1; //For night display. [Skotlex] + unsigned restricted : 1; // [Komurka] + unsigned nodrop : 1; + unsigned novending : 1; + unsigned loadevent : 1; + unsigned nochat :1; + unsigned partylock :1; + unsigned guildlock :1; + } flag; + struct point save; + struct npc_data *npc[MAX_NPC_PER_MAP]; + struct { + int drop_id; + int drop_type; + int drop_per; + } drop_list[MAX_DROP_PER_MAP]; + + struct spawn_data *moblist[MAX_MOB_LIST_PER_MAP]; // [Wizputer] + int mob_delete_timer; // [Skotlex] + int zone; // [Komurka] + int jexp; // map experience multiplicator + int bexp; // map experience multiplicator + int nocommand; //Blocks @/# commands for non-gms. [Skotlex] +}; + +struct map_data_other_server { + char name[MAP_NAME_LENGTH]; + unsigned short index; //Index is the map index used by the mapindex* functions. + unsigned char *gat; // NULL固定にして判断 + unsigned long ip; + unsigned int port; +}; + +struct flooritem_data { + struct block_list bl; + unsigned char subx,suby; + int cleartimer; + int first_get_id,second_get_id,third_get_id; + unsigned int first_get_tick,second_get_tick,third_get_tick; + struct item item_data; +}; + +enum { + SP_SPEED,SP_BASEEXP,SP_JOBEXP,SP_KARMA,SP_MANNER,SP_HP,SP_MAXHP,SP_SP, // 0-7 + SP_MAXSP,SP_STATUSPOINT,SP_0a,SP_BASELEVEL,SP_SKILLPOINT,SP_STR,SP_AGI,SP_VIT, // 8-15 + SP_INT,SP_DEX,SP_LUK,SP_CLASS,SP_ZENY,SP_SEX,SP_NEXTBASEEXP,SP_NEXTJOBEXP, // 16-23 + SP_WEIGHT,SP_MAXWEIGHT,SP_1a,SP_1b,SP_1c,SP_1d,SP_1e,SP_1f, // 24-31 + SP_USTR,SP_UAGI,SP_UVIT,SP_UINT,SP_UDEX,SP_ULUK,SP_26,SP_27, // 32-39 + SP_28,SP_ATK1,SP_ATK2,SP_MATK1,SP_MATK2,SP_DEF1,SP_DEF2,SP_MDEF1, // 40-47 + SP_MDEF2,SP_HIT,SP_FLEE1,SP_FLEE2,SP_CRITICAL,SP_ASPD,SP_36,SP_JOBLEVEL, // 48-55 + SP_UPPER,SP_PARTNER,SP_CART,SP_FAME,SP_UNBREAKABLE, //56-60 + SP_CARTINFO=99, // 99 + + SP_BASEJOB=119, // 100+19 - celest + SP_BASECLASS=120, //Hmm.. why 100+19? I just use the next one... [Skotlex] + + // original 1000- + SP_ATTACKRANGE=1000, SP_ATKELE,SP_DEFELE, // 1000-1002 + SP_CASTRATE, SP_MAXHPRATE, SP_MAXSPRATE, SP_SPRATE, // 1003-1006 + SP_ADDELE, SP_ADDRACE, SP_ADDSIZE, SP_SUBELE, SP_SUBRACE, // 1007-1011 + SP_ADDEFF, SP_RESEFF, // 1012-1013 + SP_BASE_ATK,SP_ASPD_RATE,SP_HP_RECOV_RATE,SP_SP_RECOV_RATE,SP_SPEED_RATE, // 1014-1018 + SP_CRITICAL_DEF,SP_NEAR_ATK_DEF,SP_LONG_ATK_DEF, // 1019-1021 + SP_DOUBLE_RATE, SP_DOUBLE_ADD_RATE, SP_MATK, SP_MATK_RATE, // 1022-1025 + SP_IGNORE_DEF_ELE,SP_IGNORE_DEF_RACE, // 1026-1027 + SP_ATK_RATE,SP_SPEED_ADDRATE,SP_ASPD_ADDRATE, // 1028-1030 + SP_MAGIC_ATK_DEF,SP_MISC_ATK_DEF, // 1031-1032 + SP_IGNORE_MDEF_ELE,SP_IGNORE_MDEF_RACE, // 1033-1034 + SP_MAGIC_ADDELE,SP_MAGIC_ADDRACE,SP_MAGIC_ADDSIZE, // 1035-1037 + SP_PERFECT_HIT_RATE,SP_PERFECT_HIT_ADD_RATE,SP_CRITICAL_RATE,SP_GET_ZENY_NUM,SP_ADD_GET_ZENY_NUM, // 1038-1042 + SP_ADD_DAMAGE_CLASS,SP_ADD_MAGIC_DAMAGE_CLASS,SP_ADD_DEF_CLASS,SP_ADD_MDEF_CLASS, // 1043-1046 + SP_ADD_MONSTER_DROP_ITEM,SP_DEF_RATIO_ATK_ELE,SP_DEF_RATIO_ATK_RACE,SP_ADD_SPEED, // 1047-1050 + SP_HIT_RATE,SP_FLEE_RATE,SP_FLEE2_RATE,SP_DEF_RATE,SP_DEF2_RATE,SP_MDEF_RATE,SP_MDEF2_RATE, // 1051-1057 + SP_SPLASH_RANGE,SP_SPLASH_ADD_RANGE,SP_AUTOSPELL,SP_HP_DRAIN_RATE,SP_SP_DRAIN_RATE, // 1058-1062 + SP_SHORT_WEAPON_DAMAGE_RETURN,SP_LONG_WEAPON_DAMAGE_RETURN,SP_WEAPON_COMA_ELE,SP_WEAPON_COMA_RACE, // 1063-1066 + SP_ADDEFF2,SP_BREAK_WEAPON_RATE,SP_BREAK_ARMOR_RATE,SP_ADD_STEAL_RATE, // 1067-1070 + SP_MAGIC_DAMAGE_RETURN,SP_RANDOM_ATTACK_INCREASE,SP_ALL_STATS,SP_AGI_VIT,SP_AGI_DEX_STR,SP_PERFECT_HIDE, // 1071-1076 + SP_FREE,SP_CLASSCHANGE, // 1077-1078 + SP_HP_DRAIN_VALUE,SP_SP_DRAIN_VALUE, // 1079-1080 + SP_WEAPON_ATK,SP_WEAPON_ATK_RATE, // 1081-1082 + SP_DELAYRATE,SP_HP_DRAIN_RATE_RACE,SP_SP_DRAIN_RATE_RACE, // 1083-1085 + + SP_RESTART_FULL_RECOVER=2000,SP_NO_CASTCANCEL,SP_NO_SIZEFIX,SP_NO_MAGIC_DAMAGE,SP_NO_WEAPON_DAMAGE,SP_NO_GEMSTONE, // 2000-2005 + SP_NO_CASTCANCEL2,SP_NO_MISC_DAMAGE,SP_UNBREAKABLE_WEAPON,SP_UNBREAKABLE_ARMOR, SP_UNBREAKABLE_HELM, // 2006-2010 + SP_UNBREAKABLE_SHIELD, SP_LONG_ATK_RATE, // 2011-2012 + + SP_CRIT_ATK_RATE, SP_CRITICAL_ADDRACE, SP_NO_REGEN, SP_ADDEFF_WHENHIT, SP_AUTOSPELL_WHENHIT, // 2013-2017 + SP_SKILL_ATK, SP_UNSTRIPABLE, SP_ADD_DAMAGE_BY_CLASS, // 2018-2020 + SP_SP_GAIN_VALUE, SP_IGNORE_DEF_MOB, SP_HP_LOSS_RATE, SP_ADDRACE2, SP_HP_GAIN_VALUE, // 2021-2025 + SP_SUBSIZE, SP_HP_DRAIN_VALUE_RACE, SP_ADD_ITEM_HEAL_RATE, SP_SP_DRAIN_VALUE_RACE, SP_EXP_ADDRACE, // 2026-2030 + SP_SP_GAIN_RACE, SP_SUBRACE2, SP_ADDEFF_WHENHIT_SHORT, // 2031-2033 + SP_UNSTRIPABLE_WEAPON,SP_UNSTRIPABLE_ARMOR,SP_UNSTRIPABLE_HELM,SP_UNSTRIPABLE_SHIELD, // 2034-2037 + SP_INTRAVISION, SP_ADD_MONSTER_DROP_ITEMGROUP, SP_SP_LOSS_RATE, // 2038-2040 + SP_ADD_SKILL_BLOW, SP_SP_VANISH_RATE //2041 + //Before adding another, note that + //1077 (SP_FREE, previously disguise), + //are available! +}; + +enum { + LOOK_BASE,LOOK_HAIR,LOOK_WEAPON,LOOK_HEAD_BOTTOM,LOOK_HEAD_TOP,LOOK_HEAD_MID,LOOK_HAIR_COLOR,LOOK_CLOTHES_COLOR,LOOK_SHIELD,LOOK_SHOES +}; + +// CELLs for non-permanent cell-based effects (Pneuma, Basilica, Npcs, etc) +#define CELL_NPC 0x1 +#define CELL_REGEN 0x2 +#define CELL_PNEUMA 0x4 +#define CELL_SAFETYWALL 0x8 +#define CELL_LANDPROTECTOR 0x10 +#define CELL_BASILICA 0x20 +#define CELL_ICEWALL 0x80 +/* + * map_getcell()で使用されるフラグ + */ +typedef enum { + CELL_CHKWALL=0, // 壁(セルタイプ1) + CELL_CHKWATER, // 水場(セルタイプ3) + CELL_CHKGROUND, // 地面障害物(セルタイプ5) + CELL_CHKPASS, // 通過可能(セルタイプ1,5以外) + CELL_CHKREACH, // Same as PASS, but ignores the cell-stacking mod. + CELL_CHKNOPASS, // 通過不可(セルタイプ1,5) + CELL_CHKNOREACH, // Same as NOPASS, but ignores the cell-stacking mod. + CELL_GETTYPE, // セルタイプを返す + CELL_GETCELLTYPE, + CELL_CHKNPC=0x10, // タッチタイプのNPC(セルタイプ0x80フラグ) + CELL_CHKREGEN, // cells that improve regeneration + CELL_CHKPNEUMA, + CELL_CHKSAFETYWALL, + CELL_CHKBASILICA, // バジリカ(セルタイプ0x40フラグ) + CELL_CHKLANDPROTECTOR, + CELL_CHKICEWALL, + CELL_CHKSTACK, +} cell_t; +// map_setcell()で使用されるフラグ +enum { + CELL_SETNPC=0x10, // タッチタイプのNPCをセット + CELL_CLRNPC, + CELL_SETBASILICA, // バジリカをセット + CELL_CLRBASILICA, // バジリカをクリア + CELL_SETREGEN, // set regen cell + CELL_SETLANDPROTECTOR, //Set/Clear Magnetic Earth + CELL_CLRLANDPROTECTOR, + CELL_SETPNEUMA, + CELL_CLRPNEUMA, + CELL_SETSAFETYWALL, + CELL_CLRSAFETYWALL, + CELL_SETICEWALL, + CELL_CLRICEWALL, +}; + +struct chat_data { + struct block_list bl; + + unsigned char pass[8]; /* password */ + unsigned char title[61]; /* room title MAX 60 */ + unsigned char limit; /* join limit */ + unsigned char trigger; + unsigned char users; /* current users */ + unsigned char pub; /* room attribute */ + struct map_session_data *usersd[20]; + struct block_list *owner_; + struct block_list **owner; + char npc_event[50]; +}; + +extern struct map_data map[]; +extern int map_num; +extern int autosave_interval; +extern int minsave_interval; +extern int save_settings; +extern int agit_flag; +extern int night_flag; // 0=day, 1=night [Yor] +extern int enable_spy; //Determines if @spy commands are active. +extern char db_path[256]; + +// gat?ヨァ +int map_getcell(int,int,int,cell_t); +int map_getcellp(struct map_data*,int,int,cell_t); +void map_setcell(int,int,int,int); +extern int map_read_flag; // 0: grfォユォ。ォ、ォ・1: ォュォ罩テォキォ・2: ォュォ罩テォキォ・?) +enum { + READ_FROM_GAT, READ_FROM_AFM, + READ_FROM_BITMAP, CREATE_BITMAP, + READ_FROM_BITMAP_COMPRESSED, CREATE_BITMAP_COMPRESSED +}; + +extern char motd_txt[]; +extern char help_txt[]; +extern char help2_txt[]; +extern char charhelp_txt[]; + +extern char talkie_mes[]; + +extern char wisp_server_name[]; + +// 鯖全体情報 +void map_setusers(int); +int map_getusers(void); +// block削除関連 +int map_freeblock(struct block_list *bl); +int map_freeblock_lock(void); +//int map_freeblock_unlock(void); +int map_freeblock_unlock_sub (char *file, int lineno); +#define map_freeblock_unlock() map_freeblock_unlock_sub (__FILE__, __LINE__) +// block関連 +int map_addblock_sub(struct block_list *, int); +int map_delblock_sub(struct block_list *, int); +#define map_addblock(bl) map_addblock_sub(bl,1) +#define map_delblock(bl) map_delblock_sub(bl,1) +int map_moveblock(struct block_list *, int, int, unsigned int); +int map_foreachinrange(int (*)(struct block_list*,va_list),struct block_list *,int,int,...); +int map_foreachinshootrange(int (*)(struct block_list*,va_list),struct block_list *,int,int,...); +int map_foreachinarea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,...); +// -- moonsoul (added map_foreachincell) +int map_foreachincell(int (*)(struct block_list*,va_list),int,int,int,int,...); +int map_foreachinmovearea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,int,int,...); +int map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int type,...); // Celest +int map_foreachinmap(int (*)(struct block_list*,va_list),int,int,...); +int map_countnearpc(int,int,int); +//block関連に追加 +int map_count_oncell(int m,int x,int y,int type); +struct skill_unit *map_find_skill_unit_oncell(struct block_list *,int x,int y,int skill_id,struct skill_unit *); +// 一時的object関連 +int map_addobject(struct block_list *); +int map_delobject(int); +int map_delobjectnofree(int id); +void map_foreachobject(int (*)(struct block_list*,va_list),int,...); +int map_search_freecell(struct block_list *src, int m, short *x, short *y, int rx, int ry, int flag); +// +int map_quit(struct map_session_data *); +void map_quit_ack(struct map_session_data *); +// npc +int map_addnpc(int,struct npc_data *); + +// 床アイテム関連 +int map_clearflooritem_timer(int,unsigned int,int,int); +int map_removemobs_timer(int,unsigned int,int,int); +#define map_clearflooritem(id) map_clearflooritem_timer(0,0,id,1) +int map_addflooritem(struct item *,int,int,int,int,struct map_session_data *,struct map_session_data *,struct map_session_data *,int); + +// キャラid=>キャラ名 変換関連 +void map_addchariddb(int charid,char *name); +void map_delchariddb(int charid); +int map_reqchariddb(struct map_session_data * sd,int charid); +char * map_charid2nick(int); +struct map_session_data * map_charid2sd(int); + +struct map_session_data * map_id2sd(int); +struct block_list * map_id2bl(int); +int map_mapindex2mapid(unsigned short mapindex); +int map_mapname2mapid(char*); +int map_mapname2ipport(unsigned short,int*,int*); +int map_setipport(unsigned short map,unsigned long ip,int port); +int map_eraseipport(unsigned short map,unsigned long ip,int port); +int map_eraseallipport(void); +void map_addiddb(struct block_list *); +void map_deliddb(struct block_list *bl); +struct map_session_data** map_getallusers(int *users); +void map_foreachpc(int (*func)(DBKey,void*,va_list),...); +int map_foreachiddb(int (*)(DBKey,void*,va_list),...); +void map_addnickdb(struct map_session_data *); +struct map_session_data * map_nick2sd(char*); +int compare_item(struct item *a, struct item *b); + +// その他 +int map_check_dir(int s_dir,int t_dir); +int map_calc_dir( struct block_list *src,int x,int y); +int map_random_dir(struct block_list *bl, short *x, short *y); // [Skotlex] + +// Water functions... +// +int map_setwaterheight(int m, char *mapname, int height); +int map_waterheight(char *mapname); + +// path.cより +int path_search_real(struct walkpath_data *wpd,int m,int x0,int y0,int x1,int y1,int flag,cell_t flag2); +#define path_search(wpd,m,x0,y0,x1,y1,flag) path_search_real(wpd,m,x0,y0,x1,y1,flag,CELL_CHKNOPASS) +#define path_search2(wpd,m,x0,y0,x1,y1,flag) path_search_real(wpd,m,x0,y0,x1,y1,flag,CELL_CHKWALL) + +int path_search_long_real(struct shootpath_data *spd,int m,int x0,int y0,int x1,int y1,cell_t flag); +#define path_search_long(spd,m,x0,y0,x1,y1) path_search_long_real(spd,m,x0,y0,x1,y1,CELL_CHKWALL) + +int path_blownpos(int m,int x0,int y0,int dx,int dy,int count); + +// distance related functions [Skotlex] +#define check_distance_bl(bl1, bl2, distance) check_distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y, distance) +#define check_distance_blxy(bl, x1, y1, distance) check_distance((bl)->x-(x1), (bl)->y-(y1), distance) +#define check_distance_xy(x0, y0, x1, y1, distance) check_distance((x0)-(x1), (y0)-(y1), distance) +int check_distance(int dx, int dy, int distance); + +#define distance_bl(bl1, bl2) distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y) +#define distance_blxy(bl, x1, y1) distance((bl)->x-(x1), (bl)->y-(y1)) +#define distance_xy(x0, y0, x1, y1) distance((x0)-(x1), (y0)-(y1)) +unsigned int distance(int dx, int dy); + +int cleanup_sub(struct block_list *bl, va_list ap); + +void map_helpscreen(int flag); // [Valaris] +int map_delmap(char *mapname); + +int map_addmobtolist(unsigned short m, struct spawn_data *spawn); // [Wizputer] +void map_spawnmobs(int); // [Wizputer] +void map_removemobs(int); // [Wizputer] +void do_reconnect_map(void); //Invoked on map-char reconnection [Skotlex] + +//Added for own save method +int charsql_db_init(int method); + +extern char *INTER_CONF_NAME; +extern char *LOG_CONF_NAME; +extern char *MAP_CONF_NAME; +extern char *BATTLE_CONF_FILENAME; +extern char *ATCOMMAND_CONF_FILENAME; +extern char *CHARCOMMAND_CONF_FILENAME; +extern char *SCRIPT_CONF_NAME; +extern char *MSG_CONF_NAME; +extern char *GRF_PATH_FILENAME; + + +extern int charsave_method; //needed .. +extern char *map_server_dns; + +#ifndef TXT_ONLY + +// MySQL +#ifdef __WIN32 +#include +#include +#endif +#include + +extern char tmp_sql[65535]; + +extern int db_use_sqldbs; +extern MYSQL mmysql_handle; +extern MYSQL_RES* sql_res ; +extern MYSQL_ROW sql_row ; + +extern MYSQL charsql_handle; +extern MYSQL_RES* charsql_res; +extern MYSQL_ROW charsql_row; + +extern MYSQL logmysql_handle; +extern MYSQL_RES* logsql_res ; +extern MYSQL_ROW logsql_row ; + +extern int mail_server_enable; +extern MYSQL mail_handle; +extern MYSQL_RES* mail_res ; +extern MYSQL_ROW mail_row ; + +extern char item_db_db[32]; +extern char item_db2_db[32]; +extern char mob_db_db[32]; +extern char mob_db2_db[32]; +extern char char_db[32]; +extern char mail_db[32]; + +#endif /* not TXT_ONLY */ +//Useful typedefs from jA [Skotlex] +typedef struct map_session_data TBL_PC; +typedef struct npc_data TBL_NPC; +typedef struct mob_data TBL_MOB; +typedef struct flooritem_data TBL_ITEM; +typedef struct chat_data TBL_CHAT; +typedef struct skill_unit TBL_SKILL; +typedef struct pet_data TBL_PET; +typedef struct homun_data TBL_HOM; + +#define BL_CAST(type_, bl , dest) \ + (((bl) == NULL || (bl)->type != type_) ? ((dest) = NULL, 0) : ((dest) = (T ## type_ *)(bl), 1)) + + +extern int lowest_gm_level; +extern char main_chat_nick[16]; + +#endif diff --git a/src/map/mercenary.c b/src/map/mercenary.c index f83d3673c..3ae9f8ffb 100644 --- a/src/map/mercenary.c +++ b/src/map/mercenary.c @@ -1,957 +1,957 @@ -#include -#include -#include -#include -#include -#include - -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/mmo.h" -#include "../common/showmsg.h" - -#include "log.h" -#include "clif.h" -#include "chrif.h" -#include "intif.h" -#include "itemdb.h" -#include "map.h" -#include "pc.h" -#include "status.h" -#include "skill.h" -#include "mob.h" -#include "pet.h" -#include "battle.h" -#include "party.h" -#include "guild.h" -#include "atcommand.h" -#include "script.h" -#include "npc.h" -#include "trade.h" -#include "unit.h" - -#include "mercenary.h" -#include "charsave.h" - -//Better equiprobability than rand()% [orn] -#define rand(a, b) a+(int) ((float)(b-a+1)*rand()/(RAND_MAX+1.0)) - -struct homunculus_db homunculus_db[MAX_HOMUNCULUS_CLASS]; //[orn] -struct skill_tree_entry hskill_tree[MAX_HOMUNCULUS_CLASS][MAX_SKILL_TREE]; - -static int merc_hom_hungry(int tid,unsigned int tick,int id,int data); - -static unsigned int hexptbl[MAX_LEVEL]; - -void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp) -{ - clif_hominfo(hd->master,hd,0); -} - -int merc_hom_dead(struct homun_data *hd, struct block_list *src) -{ - //There's no intimacy penalties on death (from Tharis) - struct map_session_data *sd = hd->master; - - clif_emotion(&hd->bl, 16) ; //wah - - //Delete timers when dead. - merc_hom_hungry_timer_delete(hd); - hd->homunculus.hp = 0; - - if (!sd) //unit remove map will invoke unit free - return 3; - - clif_hominfo(sd,hd,0); // Send dead flag - clif_emotion(&sd->bl, 28) ; //sob - //Remove from map (if it has no intimacy, it is auto-removed from memory) - return 3; -} - -//Vaporize a character's homun. If flag, HP needs to be 80% or above. -int merc_hom_vaporize(struct map_session_data *sd, int flag) -{ - struct homun_data *hd; - - nullpo_retr(0, sd); - - hd = sd->hd; - if (!hd || hd->homunculus.vaporize) - return 0; - - if (status_isdead(&hd->bl)) - return 0; //Can't vaporize a dead homun. - - if (flag && hd->battle_status.hp < (hd->battle_status.max_hp*80/100)) - return 0; - - hd->regen.state.block = 3; //Block regen while vaporized. - //Delete timers when vaporized. - merc_hom_hungry_timer_delete(hd); - hd->homunculus.vaporize = 1; - clif_hominfo(sd, sd->hd, 0); - merc_save(hd); - return unit_remove_map(&hd->bl, 0); -} - -//delete a homunculus, completely "killing it". -//Emote is the emotion the master should use, send negative to disable. -int merc_hom_delete(struct homun_data *hd, int emote) -{ - struct map_session_data *sd; - nullpo_retr(0, hd); - sd = hd->master; - - if (!sd) - return unit_free(&hd->bl,1); - - if (emote >= 0) - clif_emotion(&sd->bl, emote); - - //This makes it be deleted right away. - hd->homunculus.intimacy = 0; - // Send homunculus_dead to client - hd->homunculus.hp = 0; - clif_hominfo(sd, hd, 0); - return unit_remove_map(&hd->bl,0); -} - -int merc_hom_calc_skilltree(struct homun_data *hd) -{ - int i,id=0 ; - int j,f=1; - int c=0; - - nullpo_retr(0, hd); - c = hd->homunculus.class_ - HM_CLASS_BASE; - - for(i=0;i < MAX_SKILL_TREE && (id = hskill_tree[c][i].id) > 0;i++) - { - if(hd->homunculus.hskill[id-HM_SKILLBASE-1].id) - continue; //Skill already known. - if(!battle_config.skillfree) - { - for(j=0;j<5;j++) - { - if( hskill_tree[c][i].need[j].id && - merc_hom_checkskill(hd,hskill_tree[c][i].need[j].id) < hskill_tree[c][i].need[j].lv) - { - f=0; - break; - } - } - } - if (f) - hd->homunculus.hskill[id-HM_SKILLBASE-1].id = id ; - } - return 0; -} - -int merc_hom_checkskill(struct homun_data *hd,int skill_id) -{ - int i = skill_id - HM_SKILLBASE - 1; - if(!hd) - return 0; - - if(hd->homunculus.hskill[i].id == skill_id) - return (hd->homunculus.hskill[i].lv); - - return 0; -} - -int merc_skill_tree_get_max(int id, int b_class){ - int i, skillid; - b_class -= HM_CLASS_BASE; - for(i=0;(skillid=hskill_tree[b_class][i].id)>0;i++) - if (id == skillid) - return hskill_tree[b_class][i].max; - return skill_get_max(id); -} - -void merc_hom_skillup(struct homun_data *hd,int skillnum) -{ - int i = 0 ; - nullpo_retv(hd); - - if(!hd->homunculus.vaporize) - { - i = skillnum - HM_SKILLBASE - 1 ; - if( hd->homunculus.skillpts > 0 && - hd->homunculus.hskill[i].id && - hd->homunculus.hskill[i].flag == 0 && //Don't allow raising while you have granted skills. [Skotlex] - hd->homunculus.hskill[i].lv < merc_skill_tree_get_max(skillnum, hd->homunculus.class_) - ) - { - hd->homunculus.hskill[i].lv++ ; - hd->homunculus.skillpts-- ; - status_calc_homunculus(hd,0) ; - if (hd->master) { - clif_homskillup(hd->master, skillnum); - clif_hominfo(hd->master,hd,0); - clif_homskillinfoblock(hd->master); - } - } - } -} - -int merc_hom_levelup(struct homun_data *hd) -{ - int growth_str, growth_agi, growth_vit, growth_int, growth_dex, growth_luk ; - int growth_max_hp, growth_max_sp ; - char output[256] ; - - if (hd->homunculus.level == MAX_LEVEL || !hd->exp_next || hd->homunculus.exp < hd->exp_next) - return 0 ; - - hd->homunculus.level++ ; - if (!(hd->homunculus.level % 3)) - hd->homunculus.skillpts++ ; //1 skillpoint each 3 base level - - hd->homunculus.exp -= hd->exp_next ; - hd->exp_next = hexptbl[hd->homunculus.level - 1] ; - - if ( hd->homunculusDB->gmaxHP <= hd->homunculusDB->gminHP ) - growth_max_hp = hd->homunculusDB->gminHP ; - else - growth_max_hp = rand(hd->homunculusDB->gminHP, hd->homunculusDB->gmaxHP) ; - if ( hd->homunculusDB->gmaxSP <= hd->homunculusDB->gminSP ) - growth_max_sp = hd->homunculusDB->gminSP ; - else - growth_max_sp = rand(hd->homunculusDB->gminSP, hd->homunculusDB->gmaxSP) ; - if ( hd->homunculusDB->gmaxSTR <= hd->homunculusDB->gminSTR ) - growth_str = hd->homunculusDB->gminSTR ; - else - growth_str = rand(hd->homunculusDB->gminSTR, hd->homunculusDB->gmaxSTR) ; - if ( hd->homunculusDB->gmaxAGI <= hd->homunculusDB->gminAGI ) - growth_agi = hd->homunculusDB->gminAGI ; - else - growth_agi = rand(hd->homunculusDB->gminAGI, hd->homunculusDB->gmaxAGI) ; - if ( hd->homunculusDB->gmaxVIT <= hd->homunculusDB->gminVIT ) - growth_vit = hd->homunculusDB->gminVIT ; - else - growth_vit = rand(hd->homunculusDB->gminVIT, hd->homunculusDB->gmaxVIT) ; - if ( hd->homunculusDB->gmaxDEX <= hd->homunculusDB->gminDEX ) - growth_dex = hd->homunculusDB->gminDEX ; - else - growth_dex = rand(hd->homunculusDB->gminDEX, hd->homunculusDB->gmaxDEX) ; - if ( hd->homunculusDB->gmaxINT <= hd->homunculusDB->gminINT ) - growth_int = hd->homunculusDB->gminINT ; - else - growth_int = rand(hd->homunculusDB->gminINT, hd->homunculusDB->gmaxINT) ; - if ( hd->homunculusDB->gmaxLUK <= hd->homunculusDB->gminLUK ) - growth_luk = hd->homunculusDB->gminLUK ; - else - growth_luk = rand(hd->homunculusDB->gminLUK, hd->homunculusDB->gmaxLUK) ; - - hd->homunculus.max_hp += growth_max_hp; - hd->homunculus.max_sp += growth_max_sp; - hd->homunculus.str += growth_str ; - hd->homunculus.agi += growth_agi ; - hd->homunculus.vit += growth_vit ; - hd->homunculus.dex += growth_dex ; - hd->homunculus.int_ += growth_int ; - hd->homunculus.luk += growth_luk ; - - if ( battle_config.homunculus_show_growth ) { - sprintf(output, - "Growth : hp:%d sp:%d str(%.2f) agi(%.2f) vit(%.2f) int(%.2f) dex(%.2f) luk(%.2f) ", growth_max_hp, growth_max_sp, growth_str/(float)10, growth_agi/(float)10, growth_vit/(float)10, growth_int/(float)10, growth_dex/(float)10, growth_luk/(float)10 ) ; - clif_disp_onlyself(hd->master,output,strlen(output)); - } - return 1 ; -} - -int merc_hom_change_class(struct homun_data *hd, short class_) -{ - int i; - i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS); - if(i < 0) - return 0; - hd->homunculusDB = &homunculus_db[i]; - hd->homunculus.class_ = class_; - status_set_viewdata(&hd->bl, class_); - merc_hom_calc_skilltree(hd); - return 1; -} - -int merc_hom_evolution(struct homun_data *hd) -{ - struct map_session_data *sd; - nullpo_retr(0, hd); - - if(!hd->homunculusDB->evo_class) - { - clif_emotion(&hd->bl, 4) ; //swt - return 0 ; - } - sd = hd->master; - if (!sd) - return 0; - - merc_hom_vaporize(sd, 0); - - if (!merc_hom_change_class(hd, hd->homunculusDB->evo_class)) { - ShowError("merc_hom_evolution: Can't evoluate homunc from %d to %d", hd->homunculus.class_, hd->homunculusDB->evo_class); - return 0; - } - hd->homunculus.intimacy = 500; - merc_call_homunculus(sd); - clif_emotion(&sd->bl, 21); //no1 - clif_misceffect2(&hd->bl,568); - return 1 ; -} - -int merc_hom_gainexp(struct homun_data *hd,int exp) -{ - if(hd->homunculus.vaporize) - return 1; - - if( hd->exp_next == 0 ) { - hd->homunculus.exp = 0 ; - return 0; - } - - hd->homunculus.exp += exp; - - if(hd->homunculus.exp < hd->exp_next) { - clif_hominfo(hd->master,hd,0); - return 0; - } - - //levelup - do - { - merc_hom_levelup(hd) ; - } - while(hd->homunculus.exp > hd->exp_next && hd->exp_next != 0 ); - - if( hd->exp_next == 0 ) - hd->homunculus.exp = 0 ; - - clif_misceffect2(&hd->bl,568); - status_calc_homunculus(hd,0); - status_percent_heal(&hd->bl, 100, 100); - return 0; -} - -// Return the new value -int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value) -{ - if (battle_config.homunculus_friendly_rate != 100) - value = (value * battle_config.homunculus_friendly_rate) / 100; - - if (hd->homunculus.intimacy + value <= 100000) - hd->homunculus.intimacy += value; - else - hd->homunculus.intimacy = 100000; - return hd->homunculus.intimacy; -} - -// Return 0 if decrease fails or intimacy became 0 else the new value -int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value) -{ - if (hd->homunculus.intimacy >= value) - hd->homunculus.intimacy -= value; - else - hd->homunculus.intimacy = 0; - - return hd->homunculus.intimacy; -} - -void merc_hom_heal(struct homun_data *hd,int hp,int sp) -{ - clif_hominfo(hd->master,hd,0); -} - -void merc_save(struct homun_data *hd) -{ - // copy data that must be saved in homunculus struct ( hp / sp ) - TBL_PC * sd = hd->master; - //Do not check for max_hp/max_sp caps as current could be higher to max due - //to status changes/skills (they will be capped as needed upon stat - //calculation on login) - hd->homunculus.hp = hd->battle_status.hp; - hd->homunculus.sp = hd->battle_status.sp; - intif_homunculus_requestsave(sd->status.account_id, &hd->homunculus) ; -} - -int merc_menu(struct map_session_data *sd,int menunum) -{ - nullpo_retr(0, sd); - if (sd->hd == NULL) - return 1; - - switch(menunum) { - case 0: - break; - case 1: - merc_hom_food(sd, sd->hd); - break; - case 2: - merc_hom_delete(sd->hd, -1); - break; - default: - ShowError("merc_menu : unknown menu choice : %d\n", menunum) ; - break; - } - return 0; -} - -int merc_hom_food(struct map_session_data *sd, struct homun_data *hd) -{ - int i, foodID, emotion; - - if(hd->homunculus.vaporize) - return 1 ; - - foodID = hd->homunculusDB->foodID; - i = pc_search_inventory(sd,foodID); - if(i < 0) { - clif_hom_food(sd,foodID,0); - return 1; - } - pc_delitem(sd,i,1,0); - - if ( hd->homunculus.hunger >= 91 ) { - merc_hom_decrease_intimacy(hd, 50); - emotion = 16; - } else if ( hd->homunculus.hunger >= 76 ) { - merc_hom_decrease_intimacy(hd, 5); - emotion = 19; - } else if ( hd->homunculus.hunger >= 26 ) { - merc_hom_increase_intimacy(hd, 75); - emotion = 2; - } else if ( hd->homunculus.hunger >= 11 ) { - merc_hom_increase_intimacy(hd, 100); - emotion = 2; - } else { - merc_hom_increase_intimacy(hd, 50); - emotion = 2; - } - - hd->homunculus.hunger += 10; //dunno increase value for each food - if(hd->homunculus.hunger > 100) - hd->homunculus.hunger = 100; - - clif_emotion(&hd->bl,emotion) ; - clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger); - clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100); - clif_hom_food(sd,foodID,1); - - // Too much food :/ - if(hd->homunculus.intimacy == 0) - return merc_hom_delete(sd->hd, 23); //omg - - return 0; -} - -static int merc_hom_hungry(int tid,unsigned int tick,int id,int data) -{ - struct map_session_data *sd; - struct homun_data *hd; - - sd=map_id2sd(id); - if(!sd) - return 1; - - if(!sd->status.hom_id || !(hd=sd->hd)) - return 1; - - if(hd->hungry_timer != tid){ - if(battle_config.error_log) - ShowError("merc_hom_hungry_timer %d != %d\n",hd->hungry_timer,tid); - return 0; - } - - hd->hungry_timer = -1; - - hd->homunculus.hunger-- ; - if(hd->homunculus.hunger <= 10) { - clif_emotion(&hd->bl, 6) ; //an - } else if(hd->homunculus.hunger == 25) { - clif_emotion(&hd->bl, 20) ; //hmm - } else if(hd->homunculus.hunger == 75) { - clif_emotion(&hd->bl, 33) ; //ok - } - - if(hd->homunculus.hunger < 0) { - hd->homunculus.hunger = 0; - // Delete the homunculus if intimacy <= 100 - if ( !merc_hom_decrease_intimacy(hd, 100) ) - return merc_hom_delete(hd, 23); //omg - clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100); - } - - clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger); - hd->hungry_timer = add_timer(tick+hd->homunculusDB->hungryDelay,merc_hom_hungry,sd->bl.id,0); //simple Fix albator - return 0; -} - -int merc_hom_hungry_timer_delete(struct homun_data *hd) -{ - nullpo_retr(0, hd); - if(hd->hungry_timer != -1) { - delete_timer(hd->hungry_timer,merc_hom_hungry); - hd->hungry_timer = -1; - } - return 1; -} - -int search_homunculusDB_index(int key,int type) -{ - int i; - - for(i=0;istatus.hom_id == 0 || sd->hd == 0) || sd->hd->master == sd); - - i = search_homunculusDB_index(hom->class_,HOMUNCULUS_CLASS); - if(i < 0) { - ShowError("merc_hom_alloc: unknown homunculus class [%d]", hom->class_); - sd->status.hom_id = 0; - intif_homunculus_requestdelete(hom->hom_id); - return 1; - } - sd->hd = hd = aCalloc(1,sizeof(struct homun_data)); - hd->bl.subtype = MONS; - hd->bl.type = BL_HOM; - hd->bl.id = npc_get_new_npc_id(); - - hd->master = sd; - hd->homunculusDB = &homunculus_db[i]; - memcpy(&hd->homunculus, hom, sizeof(struct s_homunculus)); - hd->exp_next = hexptbl[hd->homunculus.level - 1]; - - status_set_viewdata(&hd->bl, hd->homunculus.class_); - status_change_init(&hd->bl); - unit_dataset(&hd->bl); - hd->ud.dir = sd->ud.dir; - - // Find a random valid pos around the player - hd->bl.m = sd->bl.m; - hd->bl.x = sd->bl.x; - hd->bl.y = sd->bl.y; - x = sd->bl.x + 1; - y = sd->bl.y + 1; - map_random_dir(&hd->bl, &x, &y); - hd->bl.x = x; - hd->bl.y = y; - - map_addiddb(&hd->bl); - status_calc_homunculus(hd,1); - - hd->hungry_timer = -1; - return 0; -} - -void merc_hom_init_timers(struct homun_data * hd) -{ - if (hd->hungry_timer == -1) - hd->hungry_timer = add_timer(gettick()+hd->homunculusDB->hungryDelay,merc_hom_hungry,hd->master->bl.id,0); - hd->regen.state.block = 0; //Restore HP/SP block. -} - -int merc_call_homunculus(struct map_session_data *sd) -{ - struct homun_data *hd; - - if (!sd->status.hom_id) //Create a new homun. - return merc_create_homunculus_request(sd, HM_CLASS_BASE + rand(0, 7)) ; - - // If homunc not yet loaded, load it - if (!sd->hd) - return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id); - - hd = sd->hd; - - if (!hd->homunculus.vaporize) - return 0; //Can't use this if homun wasn't vaporized. - - merc_hom_init_timers(hd); - hd->homunculus.vaporize = 0; - if (hd->bl.prev == NULL) - { //Spawn him - hd->bl.x = sd->bl.x; - hd->bl.y = sd->bl.y; - hd->bl.m = sd->bl.m; - map_addblock(&hd->bl); - clif_spawn(&hd->bl); - clif_send_homdata(sd,SP_ACK,0); - clif_hominfo(sd,hd,1); - clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89] - clif_homskillinfoblock(sd); - merc_save(hd); - } else - //Warp him to master. - unit_warp(&hd->bl,sd->bl.m, sd->bl.x, sd->bl.y,0); - return 1; -} - -// Recv homunculus data from char server -int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag) -{ - struct map_session_data *sd; - struct homun_data *hd; - - sd = map_id2sd(account_id); - if(!sd) - return 0; - if (sd->status.char_id != sh->char_id) - { - if (sd->status.hom_id == sh->hom_id) - sh->char_id = sd->status.char_id; //Correct char id. - else - return 0; - } - if(!flag) { // Failed to load - sd->status.hom_id = 0; - return 0; - } - - if (!sd->status.hom_id) //Hom just created. - sd->status.hom_id = sh->hom_id; - if (sd->hd) //uh? Overwrite the data. - memcpy(&sd->hd->homunculus, sh, sizeof(struct s_homunculus)); - else - merc_hom_alloc(sd, sh); - - hd = sd->hd; - if(hd->homunculus.hp && !hd->homunculus.vaporize && - hd->bl.prev == NULL && sd->bl.prev != NULL) - { - map_addblock(&hd->bl); - clif_spawn(&hd->bl); - clif_hominfo(sd,hd,1); - clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89] - clif_homskillinfoblock(sd); - clif_hominfo(sd,hd,0); - clif_send_homdata(sd,SP_ACK,0); - merc_hom_init_timers(hd); - } - return 1; -} - -// Ask homunculus creation to char server -int merc_create_homunculus_request(struct map_session_data *sd, int class_) -{ - struct s_homunculus homun; - int i; - - nullpo_retr(1, sd); - - i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS); - if(i < 0) return 0; - - memset(&homun, 0, sizeof(struct s_homunculus)); - //Initial data - strncpy(homun.name, homunculus_db[i].name, NAME_LENGTH-1); - homun.class_ = class_; - homun.level = 1; -// FIXME: Commented value is what the map-server had as initial value, -// Uncommented value is what the char-server was overwriting it with -// So which one is correct? -// homun.hunger = 50; - homun.hunger = 32; -// homun.intimacy = 500; - homun.intimacy = 21; - homun.char_id = sd->status.char_id; - - homun.hp = 10 ; - homun.max_hp = homunculus_db[i].basemaxHP; - homun.max_sp = homunculus_db[i].basemaxSP; - homun.str = homunculus_db[i].baseSTR * 10; - homun.agi = homunculus_db[i].baseAGI * 10; - homun.vit = homunculus_db[i].baseVIT * 10; - homun.int_ = homunculus_db[i].baseINT * 10; - homun.dex = homunculus_db[i].baseDEX * 10; - homun.luk = homunculus_db[i].baseLUK * 10; - - // Request homunculus creation - intif_homunculus_create(sd->status.account_id, &homun); - return 1; -} - -int merc_resurrect_homunculus(struct map_session_data *sd, unsigned char per, short x, short y) -{ - struct homun_data *hd; - nullpo_retr(0, sd); - if (!sd->status.hom_id) - return 0; - - if (!sd->hd) //Load homun data; - return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id); - - hd = sd->hd; - - if (hd->homunculus.vaporize) - return 0; - - if (!status_isdead(&hd->bl)) - return 0; - - merc_hom_init_timers(hd); - - if (!hd->bl.prev) - { //Add it back to the map. - hd->bl.m = sd->bl.m; - hd->bl.x = x; - hd->bl.y = y; - map_addblock(&hd->bl); - clif_spawn(&hd->bl); - } - status_revive(&hd->bl, per, 0); - return 1; -} - -void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp) -{ - struct map_session_data *sd = hd->master; - hd->homunculus.hp = hd->battle_status.hp; - if (!sd) - return; - clif_send_homdata(sd,SP_ACK,0); - clif_hominfo(sd,hd,1); - clif_hominfo(sd,hd,0); - clif_homskillinfoblock(sd); -} - -int read_homunculusdb(void) -{ - FILE *fp; - char line[1024], *p; - int i, k, classid; - int j = 0; - char *filename[]={"homunculus_db.txt","homunculus_db2.txt"}; - char *str[36]; - - malloc_set(homunculus_db,0,sizeof(homunculus_db)); - for(i = 0; i<2; i++) - { - sprintf(line, "%s/%s", db_path, filename[i]); - fp = fopen(line,"r"); - if(!fp){ - if(i != 0) - continue; - ShowError("read_homunculusdb : can't read %s\n", line); - return -1; - } - - while(fgets(line,sizeof(line)-1,fp) && j < MAX_HOMUNCULUS_CLASS) - { - if(line[0] == '/' && line[1] == '/') - continue; - - k = 0; - p = strtok (line,","); - while (p != NULL && k < 36) - { - str[k++] = p; - p = strtok (NULL, ","); - } - - classid = atoi(str[0]); - if (k != 36 || classid < HM_CLASS_BASE || classid > HM_CLASS_MAX) - { - ShowError("read_homunculusdb : Error reading %s", filename[i]); - continue; - } - - //Class,Homunculus,HP,SP,ATK,MATK,HIT,CRI,DEF,MDEF,FLEE,ASPD,STR,AGI,VIT,INT,DEX,LUK - homunculus_db[j].class_ = classid; - strncpy(homunculus_db[j].name,str[1],NAME_LENGTH-1); - homunculus_db[j].basemaxHP = atoi(str[2]); - homunculus_db[j].basemaxSP = atoi(str[3]); - homunculus_db[j].baseSTR = atoi(str[4]); - homunculus_db[j].baseAGI = atoi(str[5]); - homunculus_db[j].baseVIT = atoi(str[6]); - homunculus_db[j].baseINT = atoi(str[7]); - homunculus_db[j].baseDEX = atoi(str[8]); - homunculus_db[j].baseLUK = atoi(str[9]); - homunculus_db[j].baseIntimacy = atoi(str[10]); - homunculus_db[j].baseHungry = atoi(str[11]); - homunculus_db[j].hungryDelay = atoi(str[12]); - homunculus_db[j].foodID = atoi(str[13]); - homunculus_db[j].gminHP = atoi(str[14]); - homunculus_db[j].gmaxHP = atoi(str[15]); - homunculus_db[j].gminSP = atoi(str[16]); - homunculus_db[j].gmaxSP = atoi(str[17]); - homunculus_db[j].gminSTR = atoi(str[18]); - homunculus_db[j].gmaxSTR = atoi(str[19]); - homunculus_db[j].gminAGI = atoi(str[20]); - homunculus_db[j].gmaxAGI = atoi(str[21]); - homunculus_db[j].gminVIT = atoi(str[22]); - homunculus_db[j].gmaxVIT = atoi(str[23]); - homunculus_db[j].gminINT = atoi(str[24]); - homunculus_db[j].gmaxINT = atoi(str[25]); - homunculus_db[j].gminDEX = atoi(str[26]); - homunculus_db[j].gmaxDEX = atoi(str[27]); - homunculus_db[j].gminLUK = atoi(str[28]); - homunculus_db[j].gmaxLUK = atoi(str[29]); - homunculus_db[j].evo_class = atoi(str[30]); - homunculus_db[j].baseASPD = atoi(str[31]); - homunculus_db[j].size = atoi(str[32]); - homunculus_db[j].race = atoi(str[33]); - homunculus_db[j].element = atoi(str[34]); - homunculus_db[j].accessID = atoi(str[35]); - j++; - } - if (j > MAX_HOMUNCULUS_CLASS) - ShowWarning("read_homunculusdb: Reached max number of homunculus [%d]. Remaining homunculus were not read.\n ", MAX_HOMUNCULUS_CLASS); - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' homunculus in '"CL_WHITE"db/%s"CL_RESET"'.\n",j,filename[i]); - } - return 0; -} - -int read_homunculus_skilldb(void) -{ - FILE *fp; - char line[1024], *p; - int k, classid; - int j = 0; - char *split[15]; - - malloc_tsetdword(hskill_tree,0,sizeof(hskill_tree)); - sprintf(line, "%s/homun_skill_tree.txt", db_path); - fp=fopen(line,"r"); - if(fp==NULL){ - ShowError("can't read %s\n", line); - return 1; - } - - while(fgets(line, sizeof(line)-1, fp)) - { - int minJobLevelPresent = 0; - - if(line[0]=='/' && line[1]=='/') - continue; - - k = 0; - p = strtok(line,","); - while (p != NULL && k < 15) - { - split[k++] = p; - p = strtok(NULL, ","); - } - - if(k < 13) - continue; - - if (k == 14) - minJobLevelPresent = 1; // MinJobLvl has been added - - // check for bounds [celest] - classid = atoi(split[0]) - HM_CLASS_BASE; - if ( classid >= MAX_HOMUNCULUS_CLASS ) - continue; - - k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex] - // Search an empty line or a line with the same skill_id (stored in j) - for(j = 0; j < MAX_SKILL_TREE && hskill_tree[classid][j].id && hskill_tree[classid][j].id != k; j++); - - if (j == MAX_SKILL_TREE) - { - ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", k, classid); - continue; - } - - hskill_tree[classid][j].id=k; - hskill_tree[classid][j].max=atoi(split[2]); - if (minJobLevelPresent) - hskill_tree[classid][j].joblv=atoi(split[3]); - - for(k=0;k<5;k++){ - hskill_tree[classid][j].need[k].id=atoi(split[3+k*2+minJobLevelPresent]); - hskill_tree[classid][j].need[k].lv=atoi(split[3+k*2+minJobLevelPresent+1]); - } - } - - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","homun_skill_tree.txt"); - return 0; -} - -void read_homunculus_expdb(void) -{ - FILE *fp; - char line[1024]; - int i, j=0; - char *filename[]={"exp_homun.txt","exp_homun2.txt"}; - - malloc_tsetdword(hexptbl,0,sizeof(hexptbl)); - for(i=0; i<2; i++){ - sprintf(line, "%s/%s", db_path, filename[i]); - fp=fopen(line,"r"); - if(fp == NULL){ - if(i != 0) - continue; - ShowError("can't read %s\n",line); - return; - } - while(fgets(line,sizeof(line)-1,fp) && j < MAX_LEVEL) - { - if(line[0] == '/' && line[1] == '/') - continue; - - hexptbl[j] = strtoul(line, NULL, 10); - if (!hexptbl[j++]) - break; - } - if (hexptbl[MAX_LEVEL - 1]) // Last permitted level have to be 0! - { - ShowWarning("read_hexptbl: Reached max level in exp_homun [%d]. Remaining lines were not read.\n ", MAX_LEVEL); - hexptbl[MAX_LEVEL - 1] = 0; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' levels in '"CL_WHITE"%s"CL_RESET"'.\n", j, filename[i]); - } -} - -void merc_reload(void) -{ - read_homunculusdb(); - read_homunculus_expdb(); -} - -void merc_skill_reload(void) -{ - read_homunculus_skilldb(); -} - -int do_init_merc(void) -{ - read_homunculusdb(); - read_homunculus_expdb(); - read_homunculus_skilldb(); - // Add homunc timer function to timer func list [Toms] - add_timer_func_list(merc_hom_hungry, "merc_hom_hungry"); - return 0; -} - -int do_final_merc(void); +#include +#include +#include +#include +#include +#include + +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/nullpo.h" +#include "../common/mmo.h" +#include "../common/showmsg.h" + +#include "log.h" +#include "clif.h" +#include "chrif.h" +#include "intif.h" +#include "itemdb.h" +#include "map.h" +#include "pc.h" +#include "status.h" +#include "skill.h" +#include "mob.h" +#include "pet.h" +#include "battle.h" +#include "party.h" +#include "guild.h" +#include "atcommand.h" +#include "script.h" +#include "npc.h" +#include "trade.h" +#include "unit.h" + +#include "mercenary.h" +#include "charsave.h" + +//Better equiprobability than rand()% [orn] +#define rand(a, b) a+(int) ((float)(b-a+1)*rand()/(RAND_MAX+1.0)) + +struct homunculus_db homunculus_db[MAX_HOMUNCULUS_CLASS]; //[orn] +struct skill_tree_entry hskill_tree[MAX_HOMUNCULUS_CLASS][MAX_SKILL_TREE]; + +static int merc_hom_hungry(int tid,unsigned int tick,int id,int data); + +static unsigned int hexptbl[MAX_LEVEL]; + +void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp) +{ + clif_hominfo(hd->master,hd,0); +} + +int merc_hom_dead(struct homun_data *hd, struct block_list *src) +{ + //There's no intimacy penalties on death (from Tharis) + struct map_session_data *sd = hd->master; + + clif_emotion(&hd->bl, 16) ; //wah + + //Delete timers when dead. + merc_hom_hungry_timer_delete(hd); + hd->homunculus.hp = 0; + + if (!sd) //unit remove map will invoke unit free + return 3; + + clif_hominfo(sd,hd,0); // Send dead flag + clif_emotion(&sd->bl, 28) ; //sob + //Remove from map (if it has no intimacy, it is auto-removed from memory) + return 3; +} + +//Vaporize a character's homun. If flag, HP needs to be 80% or above. +int merc_hom_vaporize(struct map_session_data *sd, int flag) +{ + struct homun_data *hd; + + nullpo_retr(0, sd); + + hd = sd->hd; + if (!hd || hd->homunculus.vaporize) + return 0; + + if (status_isdead(&hd->bl)) + return 0; //Can't vaporize a dead homun. + + if (flag && hd->battle_status.hp < (hd->battle_status.max_hp*80/100)) + return 0; + + hd->regen.state.block = 3; //Block regen while vaporized. + //Delete timers when vaporized. + merc_hom_hungry_timer_delete(hd); + hd->homunculus.vaporize = 1; + clif_hominfo(sd, sd->hd, 0); + merc_save(hd); + return unit_remove_map(&hd->bl, 0); +} + +//delete a homunculus, completely "killing it". +//Emote is the emotion the master should use, send negative to disable. +int merc_hom_delete(struct homun_data *hd, int emote) +{ + struct map_session_data *sd; + nullpo_retr(0, hd); + sd = hd->master; + + if (!sd) + return unit_free(&hd->bl,1); + + if (emote >= 0) + clif_emotion(&sd->bl, emote); + + //This makes it be deleted right away. + hd->homunculus.intimacy = 0; + // Send homunculus_dead to client + hd->homunculus.hp = 0; + clif_hominfo(sd, hd, 0); + return unit_remove_map(&hd->bl,0); +} + +int merc_hom_calc_skilltree(struct homun_data *hd) +{ + int i,id=0 ; + int j,f=1; + int c=0; + + nullpo_retr(0, hd); + c = hd->homunculus.class_ - HM_CLASS_BASE; + + for(i=0;i < MAX_SKILL_TREE && (id = hskill_tree[c][i].id) > 0;i++) + { + if(hd->homunculus.hskill[id-HM_SKILLBASE-1].id) + continue; //Skill already known. + if(!battle_config.skillfree) + { + for(j=0;j<5;j++) + { + if( hskill_tree[c][i].need[j].id && + merc_hom_checkskill(hd,hskill_tree[c][i].need[j].id) < hskill_tree[c][i].need[j].lv) + { + f=0; + break; + } + } + } + if (f) + hd->homunculus.hskill[id-HM_SKILLBASE-1].id = id ; + } + return 0; +} + +int merc_hom_checkskill(struct homun_data *hd,int skill_id) +{ + int i = skill_id - HM_SKILLBASE - 1; + if(!hd) + return 0; + + if(hd->homunculus.hskill[i].id == skill_id) + return (hd->homunculus.hskill[i].lv); + + return 0; +} + +int merc_skill_tree_get_max(int id, int b_class){ + int i, skillid; + b_class -= HM_CLASS_BASE; + for(i=0;(skillid=hskill_tree[b_class][i].id)>0;i++) + if (id == skillid) + return hskill_tree[b_class][i].max; + return skill_get_max(id); +} + +void merc_hom_skillup(struct homun_data *hd,int skillnum) +{ + int i = 0 ; + nullpo_retv(hd); + + if(!hd->homunculus.vaporize) + { + i = skillnum - HM_SKILLBASE - 1 ; + if( hd->homunculus.skillpts > 0 && + hd->homunculus.hskill[i].id && + hd->homunculus.hskill[i].flag == 0 && //Don't allow raising while you have granted skills. [Skotlex] + hd->homunculus.hskill[i].lv < merc_skill_tree_get_max(skillnum, hd->homunculus.class_) + ) + { + hd->homunculus.hskill[i].lv++ ; + hd->homunculus.skillpts-- ; + status_calc_homunculus(hd,0) ; + if (hd->master) { + clif_homskillup(hd->master, skillnum); + clif_hominfo(hd->master,hd,0); + clif_homskillinfoblock(hd->master); + } + } + } +} + +int merc_hom_levelup(struct homun_data *hd) +{ + int growth_str, growth_agi, growth_vit, growth_int, growth_dex, growth_luk ; + int growth_max_hp, growth_max_sp ; + char output[256] ; + + if (hd->homunculus.level == MAX_LEVEL || !hd->exp_next || hd->homunculus.exp < hd->exp_next) + return 0 ; + + hd->homunculus.level++ ; + if (!(hd->homunculus.level % 3)) + hd->homunculus.skillpts++ ; //1 skillpoint each 3 base level + + hd->homunculus.exp -= hd->exp_next ; + hd->exp_next = hexptbl[hd->homunculus.level - 1] ; + + if ( hd->homunculusDB->gmaxHP <= hd->homunculusDB->gminHP ) + growth_max_hp = hd->homunculusDB->gminHP ; + else + growth_max_hp = rand(hd->homunculusDB->gminHP, hd->homunculusDB->gmaxHP) ; + if ( hd->homunculusDB->gmaxSP <= hd->homunculusDB->gminSP ) + growth_max_sp = hd->homunculusDB->gminSP ; + else + growth_max_sp = rand(hd->homunculusDB->gminSP, hd->homunculusDB->gmaxSP) ; + if ( hd->homunculusDB->gmaxSTR <= hd->homunculusDB->gminSTR ) + growth_str = hd->homunculusDB->gminSTR ; + else + growth_str = rand(hd->homunculusDB->gminSTR, hd->homunculusDB->gmaxSTR) ; + if ( hd->homunculusDB->gmaxAGI <= hd->homunculusDB->gminAGI ) + growth_agi = hd->homunculusDB->gminAGI ; + else + growth_agi = rand(hd->homunculusDB->gminAGI, hd->homunculusDB->gmaxAGI) ; + if ( hd->homunculusDB->gmaxVIT <= hd->homunculusDB->gminVIT ) + growth_vit = hd->homunculusDB->gminVIT ; + else + growth_vit = rand(hd->homunculusDB->gminVIT, hd->homunculusDB->gmaxVIT) ; + if ( hd->homunculusDB->gmaxDEX <= hd->homunculusDB->gminDEX ) + growth_dex = hd->homunculusDB->gminDEX ; + else + growth_dex = rand(hd->homunculusDB->gminDEX, hd->homunculusDB->gmaxDEX) ; + if ( hd->homunculusDB->gmaxINT <= hd->homunculusDB->gminINT ) + growth_int = hd->homunculusDB->gminINT ; + else + growth_int = rand(hd->homunculusDB->gminINT, hd->homunculusDB->gmaxINT) ; + if ( hd->homunculusDB->gmaxLUK <= hd->homunculusDB->gminLUK ) + growth_luk = hd->homunculusDB->gminLUK ; + else + growth_luk = rand(hd->homunculusDB->gminLUK, hd->homunculusDB->gmaxLUK) ; + + hd->homunculus.max_hp += growth_max_hp; + hd->homunculus.max_sp += growth_max_sp; + hd->homunculus.str += growth_str ; + hd->homunculus.agi += growth_agi ; + hd->homunculus.vit += growth_vit ; + hd->homunculus.dex += growth_dex ; + hd->homunculus.int_ += growth_int ; + hd->homunculus.luk += growth_luk ; + + if ( battle_config.homunculus_show_growth ) { + sprintf(output, + "Growth : hp:%d sp:%d str(%.2f) agi(%.2f) vit(%.2f) int(%.2f) dex(%.2f) luk(%.2f) ", growth_max_hp, growth_max_sp, growth_str/(float)10, growth_agi/(float)10, growth_vit/(float)10, growth_int/(float)10, growth_dex/(float)10, growth_luk/(float)10 ) ; + clif_disp_onlyself(hd->master,output,strlen(output)); + } + return 1 ; +} + +int merc_hom_change_class(struct homun_data *hd, short class_) +{ + int i; + i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS); + if(i < 0) + return 0; + hd->homunculusDB = &homunculus_db[i]; + hd->homunculus.class_ = class_; + status_set_viewdata(&hd->bl, class_); + merc_hom_calc_skilltree(hd); + return 1; +} + +int merc_hom_evolution(struct homun_data *hd) +{ + struct map_session_data *sd; + nullpo_retr(0, hd); + + if(!hd->homunculusDB->evo_class) + { + clif_emotion(&hd->bl, 4) ; //swt + return 0 ; + } + sd = hd->master; + if (!sd) + return 0; + + merc_hom_vaporize(sd, 0); + + if (!merc_hom_change_class(hd, hd->homunculusDB->evo_class)) { + ShowError("merc_hom_evolution: Can't evoluate homunc from %d to %d", hd->homunculus.class_, hd->homunculusDB->evo_class); + return 0; + } + hd->homunculus.intimacy = 500; + merc_call_homunculus(sd); + clif_emotion(&sd->bl, 21); //no1 + clif_misceffect2(&hd->bl,568); + return 1 ; +} + +int merc_hom_gainexp(struct homun_data *hd,int exp) +{ + if(hd->homunculus.vaporize) + return 1; + + if( hd->exp_next == 0 ) { + hd->homunculus.exp = 0 ; + return 0; + } + + hd->homunculus.exp += exp; + + if(hd->homunculus.exp < hd->exp_next) { + clif_hominfo(hd->master,hd,0); + return 0; + } + + //levelup + do + { + merc_hom_levelup(hd) ; + } + while(hd->homunculus.exp > hd->exp_next && hd->exp_next != 0 ); + + if( hd->exp_next == 0 ) + hd->homunculus.exp = 0 ; + + clif_misceffect2(&hd->bl,568); + status_calc_homunculus(hd,0); + status_percent_heal(&hd->bl, 100, 100); + return 0; +} + +// Return the new value +int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value) +{ + if (battle_config.homunculus_friendly_rate != 100) + value = (value * battle_config.homunculus_friendly_rate) / 100; + + if (hd->homunculus.intimacy + value <= 100000) + hd->homunculus.intimacy += value; + else + hd->homunculus.intimacy = 100000; + return hd->homunculus.intimacy; +} + +// Return 0 if decrease fails or intimacy became 0 else the new value +int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value) +{ + if (hd->homunculus.intimacy >= value) + hd->homunculus.intimacy -= value; + else + hd->homunculus.intimacy = 0; + + return hd->homunculus.intimacy; +} + +void merc_hom_heal(struct homun_data *hd,int hp,int sp) +{ + clif_hominfo(hd->master,hd,0); +} + +void merc_save(struct homun_data *hd) +{ + // copy data that must be saved in homunculus struct ( hp / sp ) + TBL_PC * sd = hd->master; + //Do not check for max_hp/max_sp caps as current could be higher to max due + //to status changes/skills (they will be capped as needed upon stat + //calculation on login) + hd->homunculus.hp = hd->battle_status.hp; + hd->homunculus.sp = hd->battle_status.sp; + intif_homunculus_requestsave(sd->status.account_id, &hd->homunculus) ; +} + +int merc_menu(struct map_session_data *sd,int menunum) +{ + nullpo_retr(0, sd); + if (sd->hd == NULL) + return 1; + + switch(menunum) { + case 0: + break; + case 1: + merc_hom_food(sd, sd->hd); + break; + case 2: + merc_hom_delete(sd->hd, -1); + break; + default: + ShowError("merc_menu : unknown menu choice : %d\n", menunum) ; + break; + } + return 0; +} + +int merc_hom_food(struct map_session_data *sd, struct homun_data *hd) +{ + int i, foodID, emotion; + + if(hd->homunculus.vaporize) + return 1 ; + + foodID = hd->homunculusDB->foodID; + i = pc_search_inventory(sd,foodID); + if(i < 0) { + clif_hom_food(sd,foodID,0); + return 1; + } + pc_delitem(sd,i,1,0); + + if ( hd->homunculus.hunger >= 91 ) { + merc_hom_decrease_intimacy(hd, 50); + emotion = 16; + } else if ( hd->homunculus.hunger >= 76 ) { + merc_hom_decrease_intimacy(hd, 5); + emotion = 19; + } else if ( hd->homunculus.hunger >= 26 ) { + merc_hom_increase_intimacy(hd, 75); + emotion = 2; + } else if ( hd->homunculus.hunger >= 11 ) { + merc_hom_increase_intimacy(hd, 100); + emotion = 2; + } else { + merc_hom_increase_intimacy(hd, 50); + emotion = 2; + } + + hd->homunculus.hunger += 10; //dunno increase value for each food + if(hd->homunculus.hunger > 100) + hd->homunculus.hunger = 100; + + clif_emotion(&hd->bl,emotion) ; + clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger); + clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100); + clif_hom_food(sd,foodID,1); + + // Too much food :/ + if(hd->homunculus.intimacy == 0) + return merc_hom_delete(sd->hd, 23); //omg + + return 0; +} + +static int merc_hom_hungry(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd; + struct homun_data *hd; + + sd=map_id2sd(id); + if(!sd) + return 1; + + if(!sd->status.hom_id || !(hd=sd->hd)) + return 1; + + if(hd->hungry_timer != tid){ + if(battle_config.error_log) + ShowError("merc_hom_hungry_timer %d != %d\n",hd->hungry_timer,tid); + return 0; + } + + hd->hungry_timer = -1; + + hd->homunculus.hunger-- ; + if(hd->homunculus.hunger <= 10) { + clif_emotion(&hd->bl, 6) ; //an + } else if(hd->homunculus.hunger == 25) { + clif_emotion(&hd->bl, 20) ; //hmm + } else if(hd->homunculus.hunger == 75) { + clif_emotion(&hd->bl, 33) ; //ok + } + + if(hd->homunculus.hunger < 0) { + hd->homunculus.hunger = 0; + // Delete the homunculus if intimacy <= 100 + if ( !merc_hom_decrease_intimacy(hd, 100) ) + return merc_hom_delete(hd, 23); //omg + clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100); + } + + clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger); + hd->hungry_timer = add_timer(tick+hd->homunculusDB->hungryDelay,merc_hom_hungry,sd->bl.id,0); //simple Fix albator + return 0; +} + +int merc_hom_hungry_timer_delete(struct homun_data *hd) +{ + nullpo_retr(0, hd); + if(hd->hungry_timer != -1) { + delete_timer(hd->hungry_timer,merc_hom_hungry); + hd->hungry_timer = -1; + } + return 1; +} + +int search_homunculusDB_index(int key,int type) +{ + int i; + + for(i=0;istatus.hom_id == 0 || sd->hd == 0) || sd->hd->master == sd); + + i = search_homunculusDB_index(hom->class_,HOMUNCULUS_CLASS); + if(i < 0) { + ShowError("merc_hom_alloc: unknown homunculus class [%d]", hom->class_); + sd->status.hom_id = 0; + intif_homunculus_requestdelete(hom->hom_id); + return 1; + } + sd->hd = hd = aCalloc(1,sizeof(struct homun_data)); + hd->bl.subtype = MONS; + hd->bl.type = BL_HOM; + hd->bl.id = npc_get_new_npc_id(); + + hd->master = sd; + hd->homunculusDB = &homunculus_db[i]; + memcpy(&hd->homunculus, hom, sizeof(struct s_homunculus)); + hd->exp_next = hexptbl[hd->homunculus.level - 1]; + + status_set_viewdata(&hd->bl, hd->homunculus.class_); + status_change_init(&hd->bl); + unit_dataset(&hd->bl); + hd->ud.dir = sd->ud.dir; + + // Find a random valid pos around the player + hd->bl.m = sd->bl.m; + hd->bl.x = sd->bl.x; + hd->bl.y = sd->bl.y; + x = sd->bl.x + 1; + y = sd->bl.y + 1; + map_random_dir(&hd->bl, &x, &y); + hd->bl.x = x; + hd->bl.y = y; + + map_addiddb(&hd->bl); + status_calc_homunculus(hd,1); + + hd->hungry_timer = -1; + return 0; +} + +void merc_hom_init_timers(struct homun_data * hd) +{ + if (hd->hungry_timer == -1) + hd->hungry_timer = add_timer(gettick()+hd->homunculusDB->hungryDelay,merc_hom_hungry,hd->master->bl.id,0); + hd->regen.state.block = 0; //Restore HP/SP block. +} + +int merc_call_homunculus(struct map_session_data *sd) +{ + struct homun_data *hd; + + if (!sd->status.hom_id) //Create a new homun. + return merc_create_homunculus_request(sd, HM_CLASS_BASE + rand(0, 7)) ; + + // If homunc not yet loaded, load it + if (!sd->hd) + return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id); + + hd = sd->hd; + + if (!hd->homunculus.vaporize) + return 0; //Can't use this if homun wasn't vaporized. + + merc_hom_init_timers(hd); + hd->homunculus.vaporize = 0; + if (hd->bl.prev == NULL) + { //Spawn him + hd->bl.x = sd->bl.x; + hd->bl.y = sd->bl.y; + hd->bl.m = sd->bl.m; + map_addblock(&hd->bl); + clif_spawn(&hd->bl); + clif_send_homdata(sd,SP_ACK,0); + clif_hominfo(sd,hd,1); + clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89] + clif_homskillinfoblock(sd); + merc_save(hd); + } else + //Warp him to master. + unit_warp(&hd->bl,sd->bl.m, sd->bl.x, sd->bl.y,0); + return 1; +} + +// Recv homunculus data from char server +int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag) +{ + struct map_session_data *sd; + struct homun_data *hd; + + sd = map_id2sd(account_id); + if(!sd) + return 0; + if (sd->status.char_id != sh->char_id) + { + if (sd->status.hom_id == sh->hom_id) + sh->char_id = sd->status.char_id; //Correct char id. + else + return 0; + } + if(!flag) { // Failed to load + sd->status.hom_id = 0; + return 0; + } + + if (!sd->status.hom_id) //Hom just created. + sd->status.hom_id = sh->hom_id; + if (sd->hd) //uh? Overwrite the data. + memcpy(&sd->hd->homunculus, sh, sizeof(struct s_homunculus)); + else + merc_hom_alloc(sd, sh); + + hd = sd->hd; + if(hd->homunculus.hp && !hd->homunculus.vaporize && + hd->bl.prev == NULL && sd->bl.prev != NULL) + { + map_addblock(&hd->bl); + clif_spawn(&hd->bl); + clif_hominfo(sd,hd,1); + clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89] + clif_homskillinfoblock(sd); + clif_hominfo(sd,hd,0); + clif_send_homdata(sd,SP_ACK,0); + merc_hom_init_timers(hd); + } + return 1; +} + +// Ask homunculus creation to char server +int merc_create_homunculus_request(struct map_session_data *sd, int class_) +{ + struct s_homunculus homun; + int i; + + nullpo_retr(1, sd); + + i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS); + if(i < 0) return 0; + + memset(&homun, 0, sizeof(struct s_homunculus)); + //Initial data + strncpy(homun.name, homunculus_db[i].name, NAME_LENGTH-1); + homun.class_ = class_; + homun.level = 1; +// FIXME: Commented value is what the map-server had as initial value, +// Uncommented value is what the char-server was overwriting it with +// So which one is correct? +// homun.hunger = 50; + homun.hunger = 32; +// homun.intimacy = 500; + homun.intimacy = 21; + homun.char_id = sd->status.char_id; + + homun.hp = 10 ; + homun.max_hp = homunculus_db[i].basemaxHP; + homun.max_sp = homunculus_db[i].basemaxSP; + homun.str = homunculus_db[i].baseSTR * 10; + homun.agi = homunculus_db[i].baseAGI * 10; + homun.vit = homunculus_db[i].baseVIT * 10; + homun.int_ = homunculus_db[i].baseINT * 10; + homun.dex = homunculus_db[i].baseDEX * 10; + homun.luk = homunculus_db[i].baseLUK * 10; + + // Request homunculus creation + intif_homunculus_create(sd->status.account_id, &homun); + return 1; +} + +int merc_resurrect_homunculus(struct map_session_data *sd, unsigned char per, short x, short y) +{ + struct homun_data *hd; + nullpo_retr(0, sd); + if (!sd->status.hom_id) + return 0; + + if (!sd->hd) //Load homun data; + return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id); + + hd = sd->hd; + + if (hd->homunculus.vaporize) + return 0; + + if (!status_isdead(&hd->bl)) + return 0; + + merc_hom_init_timers(hd); + + if (!hd->bl.prev) + { //Add it back to the map. + hd->bl.m = sd->bl.m; + hd->bl.x = x; + hd->bl.y = y; + map_addblock(&hd->bl); + clif_spawn(&hd->bl); + } + status_revive(&hd->bl, per, 0); + return 1; +} + +void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp) +{ + struct map_session_data *sd = hd->master; + hd->homunculus.hp = hd->battle_status.hp; + if (!sd) + return; + clif_send_homdata(sd,SP_ACK,0); + clif_hominfo(sd,hd,1); + clif_hominfo(sd,hd,0); + clif_homskillinfoblock(sd); +} + +int read_homunculusdb(void) +{ + FILE *fp; + char line[1024], *p; + int i, k, classid; + int j = 0; + char *filename[]={"homunculus_db.txt","homunculus_db2.txt"}; + char *str[36]; + + malloc_set(homunculus_db,0,sizeof(homunculus_db)); + for(i = 0; i<2; i++) + { + sprintf(line, "%s/%s", db_path, filename[i]); + fp = fopen(line,"r"); + if(!fp){ + if(i != 0) + continue; + ShowError("read_homunculusdb : can't read %s\n", line); + return -1; + } + + while(fgets(line,sizeof(line)-1,fp) && j < MAX_HOMUNCULUS_CLASS) + { + if(line[0] == '/' && line[1] == '/') + continue; + + k = 0; + p = strtok (line,","); + while (p != NULL && k < 36) + { + str[k++] = p; + p = strtok (NULL, ","); + } + + classid = atoi(str[0]); + if (k != 36 || classid < HM_CLASS_BASE || classid > HM_CLASS_MAX) + { + ShowError("read_homunculusdb : Error reading %s", filename[i]); + continue; + } + + //Class,Homunculus,HP,SP,ATK,MATK,HIT,CRI,DEF,MDEF,FLEE,ASPD,STR,AGI,VIT,INT,DEX,LUK + homunculus_db[j].class_ = classid; + strncpy(homunculus_db[j].name,str[1],NAME_LENGTH-1); + homunculus_db[j].basemaxHP = atoi(str[2]); + homunculus_db[j].basemaxSP = atoi(str[3]); + homunculus_db[j].baseSTR = atoi(str[4]); + homunculus_db[j].baseAGI = atoi(str[5]); + homunculus_db[j].baseVIT = atoi(str[6]); + homunculus_db[j].baseINT = atoi(str[7]); + homunculus_db[j].baseDEX = atoi(str[8]); + homunculus_db[j].baseLUK = atoi(str[9]); + homunculus_db[j].baseIntimacy = atoi(str[10]); + homunculus_db[j].baseHungry = atoi(str[11]); + homunculus_db[j].hungryDelay = atoi(str[12]); + homunculus_db[j].foodID = atoi(str[13]); + homunculus_db[j].gminHP = atoi(str[14]); + homunculus_db[j].gmaxHP = atoi(str[15]); + homunculus_db[j].gminSP = atoi(str[16]); + homunculus_db[j].gmaxSP = atoi(str[17]); + homunculus_db[j].gminSTR = atoi(str[18]); + homunculus_db[j].gmaxSTR = atoi(str[19]); + homunculus_db[j].gminAGI = atoi(str[20]); + homunculus_db[j].gmaxAGI = atoi(str[21]); + homunculus_db[j].gminVIT = atoi(str[22]); + homunculus_db[j].gmaxVIT = atoi(str[23]); + homunculus_db[j].gminINT = atoi(str[24]); + homunculus_db[j].gmaxINT = atoi(str[25]); + homunculus_db[j].gminDEX = atoi(str[26]); + homunculus_db[j].gmaxDEX = atoi(str[27]); + homunculus_db[j].gminLUK = atoi(str[28]); + homunculus_db[j].gmaxLUK = atoi(str[29]); + homunculus_db[j].evo_class = atoi(str[30]); + homunculus_db[j].baseASPD = atoi(str[31]); + homunculus_db[j].size = atoi(str[32]); + homunculus_db[j].race = atoi(str[33]); + homunculus_db[j].element = atoi(str[34]); + homunculus_db[j].accessID = atoi(str[35]); + j++; + } + if (j > MAX_HOMUNCULUS_CLASS) + ShowWarning("read_homunculusdb: Reached max number of homunculus [%d]. Remaining homunculus were not read.\n ", MAX_HOMUNCULUS_CLASS); + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' homunculus in '"CL_WHITE"db/%s"CL_RESET"'.\n",j,filename[i]); + } + return 0; +} + +int read_homunculus_skilldb(void) +{ + FILE *fp; + char line[1024], *p; + int k, classid; + int j = 0; + char *split[15]; + + malloc_tsetdword(hskill_tree,0,sizeof(hskill_tree)); + sprintf(line, "%s/homun_skill_tree.txt", db_path); + fp=fopen(line,"r"); + if(fp==NULL){ + ShowError("can't read %s\n", line); + return 1; + } + + while(fgets(line, sizeof(line)-1, fp)) + { + int minJobLevelPresent = 0; + + if(line[0]=='/' && line[1]=='/') + continue; + + k = 0; + p = strtok(line,","); + while (p != NULL && k < 15) + { + split[k++] = p; + p = strtok(NULL, ","); + } + + if(k < 13) + continue; + + if (k == 14) + minJobLevelPresent = 1; // MinJobLvl has been added + + // check for bounds [celest] + classid = atoi(split[0]) - HM_CLASS_BASE; + if ( classid >= MAX_HOMUNCULUS_CLASS ) + continue; + + k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex] + // Search an empty line or a line with the same skill_id (stored in j) + for(j = 0; j < MAX_SKILL_TREE && hskill_tree[classid][j].id && hskill_tree[classid][j].id != k; j++); + + if (j == MAX_SKILL_TREE) + { + ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", k, classid); + continue; + } + + hskill_tree[classid][j].id=k; + hskill_tree[classid][j].max=atoi(split[2]); + if (minJobLevelPresent) + hskill_tree[classid][j].joblv=atoi(split[3]); + + for(k=0;k<5;k++){ + hskill_tree[classid][j].need[k].id=atoi(split[3+k*2+minJobLevelPresent]); + hskill_tree[classid][j].need[k].lv=atoi(split[3+k*2+minJobLevelPresent+1]); + } + } + + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","homun_skill_tree.txt"); + return 0; +} + +void read_homunculus_expdb(void) +{ + FILE *fp; + char line[1024]; + int i, j=0; + char *filename[]={"exp_homun.txt","exp_homun2.txt"}; + + malloc_tsetdword(hexptbl,0,sizeof(hexptbl)); + for(i=0; i<2; i++){ + sprintf(line, "%s/%s", db_path, filename[i]); + fp=fopen(line,"r"); + if(fp == NULL){ + if(i != 0) + continue; + ShowError("can't read %s\n",line); + return; + } + while(fgets(line,sizeof(line)-1,fp) && j < MAX_LEVEL) + { + if(line[0] == '/' && line[1] == '/') + continue; + + hexptbl[j] = strtoul(line, NULL, 10); + if (!hexptbl[j++]) + break; + } + if (hexptbl[MAX_LEVEL - 1]) // Last permitted level have to be 0! + { + ShowWarning("read_hexptbl: Reached max level in exp_homun [%d]. Remaining lines were not read.\n ", MAX_LEVEL); + hexptbl[MAX_LEVEL - 1] = 0; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' levels in '"CL_WHITE"%s"CL_RESET"'.\n", j, filename[i]); + } +} + +void merc_reload(void) +{ + read_homunculusdb(); + read_homunculus_expdb(); +} + +void merc_skill_reload(void) +{ + read_homunculus_skilldb(); +} + +int do_init_merc(void) +{ + read_homunculusdb(); + read_homunculus_expdb(); + read_homunculus_skilldb(); + // Add homunc timer function to timer func list [Toms] + add_timer_func_list(merc_hom_hungry, "merc_hom_hungry"); + return 0; +} + +int do_final_merc(void); diff --git a/src/map/mercenary.h b/src/map/mercenary.h index 73f791c68..e5ba86cfe 100644 --- a/src/map/mercenary.h +++ b/src/map/mercenary.h @@ -1,79 +1,79 @@ -// Homunculus and future Mercenary system code go here [Celest] -// implemented by [orn] -struct homunculus_db { - int class_ ; - char name[NAME_LENGTH]; - int basemaxHP ; - int basemaxSP ; - int baseSTR ; - int baseAGI ; - int baseVIT ; - int baseINT ; - int baseDEX ; - int baseLUK ; - int foodID ; - int baseIntimacy ; - short baseHungry ; - long hungryDelay ; - int gminHP ; - int gmaxHP ; - int gminSP ; - int gmaxSP ; - int gminSTR ; - int gmaxSTR ; - int gminAGI ; - int gmaxAGI ; - int gminVIT ; - int gmaxVIT ; - int gminINT ; - int gmaxINT ; - int gminDEX ; - int gmaxDEX ; - int gminLUK ; - int gmaxLUK ; - int evo_class ; - int baseASPD ; - unsigned char element, race, size; - int accessID ; -}; -extern struct homunculus_db homuncumlus_db[MAX_HOMUNCULUS_CLASS]; -enum { HOMUNCULUS_CLASS, HOMUNCULUS_FOOD }; -enum { - SP_ACK = 0x00, - SP_INTIMATE = 0x100, - SP_HUNGRY = 0x200 -}; -// merc_is_hom_alive(struct homun_data *) -#define merc_is_hom_active(x) (x && x->homunculus.vaporize != 1 && x->battle_status.hp > 0) -int do_init_merc(void); -int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag); //albator -void merc_load_sub(struct homun_data *hd, struct map_session_data *sd); -void merc_load_exptables(void); -char *merc_hom_skill_get_name(int id); -void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp); -int merc_hom_dead(struct homun_data *hd, struct block_list *src); -void merc_hom_skillup(struct homun_data *hd,int skillnum); -int merc_hom_calc_skilltree(struct homun_data *hd) ; -int merc_hom_checkskill(struct homun_data *hd,int skill_id) ; -int merc_hom_gainexp(struct homun_data *hd,int exp) ; -int merc_hom_levelup(struct homun_data *hd) ; -int merc_hom_evolution(struct homun_data *hd) ; -void merc_hom_heal(struct homun_data *hd,int hp,int sp); -int merc_hom_vaporize(struct map_session_data *sd, int flag); -int merc_resurrect_homunculus(struct map_session_data *sd, unsigned char per, short x, short y); -void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp); -void merc_save(struct homun_data *hd); -int merc_call_homunculus(struct map_session_data *sd); -int merc_create_homunculus_request(struct map_session_data *sd, int class_); -int search_homunculusDB_index(int key,int type); -int merc_menu(struct map_session_data *sd,int menunum); -int merc_hom_food(struct map_session_data *sd, struct homun_data *hd); -int merc_hom_hungry_timer_delete(struct homun_data *hd); -#define merc_stop_walking(hd, type) { if((hd)->ud.walktimer != -1) unit_stop_walking(&(hd)->bl, type); } -#define merc_stop_attack(hd) { if((hd)->ud.attacktimer != -1) unit_stop_attack(&(hd)->bl); hd->ud.target = 0; } -int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value); -int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value); -int merc_skill_tree_get_max(int id, int b_class); -void merc_hom_init_timers(struct homun_data * hd); -void merc_skill_reload(void); -void merc_reload(void); +// Homunculus and future Mercenary system code go here [Celest] +// implemented by [orn] +struct homunculus_db { + int class_ ; + char name[NAME_LENGTH]; + int basemaxHP ; + int basemaxSP ; + int baseSTR ; + int baseAGI ; + int baseVIT ; + int baseINT ; + int baseDEX ; + int baseLUK ; + int foodID ; + int baseIntimacy ; + short baseHungry ; + long hungryDelay ; + int gminHP ; + int gmaxHP ; + int gminSP ; + int gmaxSP ; + int gminSTR ; + int gmaxSTR ; + int gminAGI ; + int gmaxAGI ; + int gminVIT ; + int gmaxVIT ; + int gminINT ; + int gmaxINT ; + int gminDEX ; + int gmaxDEX ; + int gminLUK ; + int gmaxLUK ; + int evo_class ; + int baseASPD ; + unsigned char element, race, size; + int accessID ; +}; +extern struct homunculus_db homuncumlus_db[MAX_HOMUNCULUS_CLASS]; +enum { HOMUNCULUS_CLASS, HOMUNCULUS_FOOD }; +enum { + SP_ACK = 0x00, + SP_INTIMATE = 0x100, + SP_HUNGRY = 0x200 +}; +// merc_is_hom_alive(struct homun_data *) +#define merc_is_hom_active(x) (x && x->homunculus.vaporize != 1 && x->battle_status.hp > 0) +int do_init_merc(void); +int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag); //albator +void merc_load_sub(struct homun_data *hd, struct map_session_data *sd); +void merc_load_exptables(void); +char *merc_hom_skill_get_name(int id); +void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp); +int merc_hom_dead(struct homun_data *hd, struct block_list *src); +void merc_hom_skillup(struct homun_data *hd,int skillnum); +int merc_hom_calc_skilltree(struct homun_data *hd) ; +int merc_hom_checkskill(struct homun_data *hd,int skill_id) ; +int merc_hom_gainexp(struct homun_data *hd,int exp) ; +int merc_hom_levelup(struct homun_data *hd) ; +int merc_hom_evolution(struct homun_data *hd) ; +void merc_hom_heal(struct homun_data *hd,int hp,int sp); +int merc_hom_vaporize(struct map_session_data *sd, int flag); +int merc_resurrect_homunculus(struct map_session_data *sd, unsigned char per, short x, short y); +void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp); +void merc_save(struct homun_data *hd); +int merc_call_homunculus(struct map_session_data *sd); +int merc_create_homunculus_request(struct map_session_data *sd, int class_); +int search_homunculusDB_index(int key,int type); +int merc_menu(struct map_session_data *sd,int menunum); +int merc_hom_food(struct map_session_data *sd, struct homun_data *hd); +int merc_hom_hungry_timer_delete(struct homun_data *hd); +#define merc_stop_walking(hd, type) { if((hd)->ud.walktimer != -1) unit_stop_walking(&(hd)->bl, type); } +#define merc_stop_attack(hd) { if((hd)->ud.attacktimer != -1) unit_stop_attack(&(hd)->bl); hd->ud.target = 0; } +int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value); +int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value); +int merc_skill_tree_get_max(int id, int b_class); +void merc_hom_init_timers(struct homun_data * hd); +void merc_skill_reload(void); +void merc_reload(void); diff --git a/src/map/mob.h b/src/map/mob.h index 7dedf948b..03d35b076 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -1,207 +1,207 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _MOB_H_ -#define _MOB_H_ - -#include "unit.h" -#include "map.h" - -#define MAX_RANDOMMONSTER 3 -#define MAX_MOB_RACE_DB 6 - /* Change this to increase the table size in your mob_db to accomodate - a larger mob database. Be sure to note that IDs 4001 to 4048 are reserved for advanced/baby/expanded classes. - */ -#define MAX_MOB_DB 10000 - -//The number of drops all mobs have and the max drop-slot that the steal skill -//will attempt to steal from. -#define MAX_MOB_DROP 10 -#define MAX_STEAL_DROP 7 - -//Min time before mobs do a check to call nearby friends for help (or for slaves to support their master) -#define MIN_MOBLINKTIME 1000 - -//Distance that slaves should keep from their master. -#define MOB_SLAVEDISTANCE 2 - -// These define the range of available IDs for clones. [Valaris] -#define MOB_CLONE_START 9001 -#define MOB_CLONE_END 10000 - -// Scripted Mob AI Constants -#define CALLBACK_NPCCLICK 0x100 -#define CALLBACK_ATTACK 0x80 -#define CALLBACK_DETECT 0x40 -#define CALLBACK_DEAD 0x20 -#define CALLBACK_ASSIST 0x10 -#define CALLBACK_KILL 0x08 -#define CALLBACK_UNLOCK 0x04 -#define CALLBACK_WALKACK 0x02 -#define CALLBACK_WARPACK 0x01 - -int mob_script_callback(struct mob_data *md, struct block_list *target, short action_type); - -struct mob_skill { - short state; - short skill_id,skill_lv; - short permillage; - int casttime,delay; - short cancel; - short cond1,cond2; - short target; - int val[5]; - short emotion; -}; - -struct mob_db { - char sprite[NAME_LENGTH],name[NAME_LENGTH],jname[NAME_LENGTH]; - unsigned int base_exp,job_exp; - unsigned int mexp,mexpper; - unsigned int min_thinktime; //Min think time, Recharge Time as aegis calls it. - int range2,range3; - short race2; // celest - unsigned short lv; - struct { int nameid,p; } dropitem[MAX_MOB_DROP]; - struct { int nameid,p; } mvpitem[3]; - struct status_data status; - struct view_data vd; - short option; - int summonper[MAX_RANDOMMONSTER]; - int maxskill; - struct mob_skill skill[MAX_MOBSKILL]; -}; - -enum { - MST_TARGET = 0, - MST_SELF, - MST_FRIEND, - MST_MASTER, - MST_AROUND5, - MST_AROUND6, - MST_AROUND7, - MST_AROUND8, - MST_AROUND1, - MST_AROUND2, - MST_AROUND3, - MST_AROUND4, - MST_AROUND = MST_AROUND4, - - MSC_ALWAYS = 0x0000, - MSC_MYHPLTMAXRATE, - MSC_MYHPINRATE, - MSC_FRIENDHPLTMAXRATE, - MSC_FRIENDHPINRATE, - MSC_MYSTATUSON, - MSC_MYSTATUSOFF, - MSC_FRIENDSTATUSON, - MSC_FRIENDSTATUSOFF, - MSC_ATTACKPCGT, - MSC_ATTACKPCGE, - MSC_SLAVELT, - MSC_SLAVELE, - MSC_CLOSEDATTACKED, - MSC_LONGRANGEATTACKED, - MSC_AFTERSKILL, - MSC_SKILLUSED , - MSC_CASTTARGETED, - MSC_RUDEATTACKED, - MSC_MASTERHPLTMAXRATE, - MSC_MASTERATTACKED, - MSC_ALCHEMIST, - MSC_SPAWN, -}; - -//Mob skill states. -enum { - MSS_ANY = -1, - MSS_IDLE, - MSS_WALK, - MSS_LOOT, - MSS_DEAD, - MSS_BERSERK, //Aggressive mob attacking - MSS_ANGRY, //Mob retaliating from being attacked. - MSS_RUSH, //Mob following a player after being attacked. - MSS_FOLLOW, //Mob following a player without being attacked. - MSS_ANYTARGET, -}; - - -/*========================================== - * The structure object for item drop with delay - * Since it is only two being able to pass [ int ] a timer function - * Data is put in and passed to this structure object. - *------------------------------------------ - */ -struct item_drop { - struct item item_data; - struct item_drop *next; -}; - -struct item_drop_list { - int m,x,y; - struct map_session_data *first_sd,*second_sd,*third_sd; - struct item_drop *item; -}; - -struct mob_db* mob_db(int class_); -int mobdb_searchname(const char *str); -int mobdb_searchname_array(struct mob_db** data, int size, const char *str); -int mobdb_checkid(const int id); -struct view_data* mob_get_viewdata(int class_); -struct mob_data *mob_once_spawn_sub(struct block_list *bl, int m, - short x, short y, const char *mobname, int class_, const char *event); -int mob_once_spawn(struct map_session_data *sd,char *mapname, - short x,short y,const char *mobname,int class_,int amount,const char *event); -int mob_once_spawn_area(struct map_session_data *sd,char *mapname, - int x0,int y0,int x1,int y1, - const char *mobname,int class_,int amount,const char *event); - -int mob_spawn_guardian(struct map_session_data *sd,char *mapname, // Spawning Guardians [Valaris] - int x,int y,const char *mobname,int class_,int amount,const char *event,int guardian); // Spawning Guardians [Valaris] -int mob_guardian_guildchange(struct block_list *bl,va_list ap); //Change Guardian's ownership. [Skotlex] - -int mob_randomwalk(struct mob_data *md,int tick); - -int mob_target(struct mob_data *md,struct block_list *bl,int dist); -int mob_unlocktarget(struct mob_data *md,int tick); -struct mob_data* mob_spawn_dataset(struct spawn_data *data); -int mob_spawn(struct mob_data *md); -int mob_setdelayspawn(struct mob_data *md); -int mob_parse_dataset(struct spawn_data *data); -void mob_damage(struct mob_data *md, struct block_list *src, int damage); -int mob_dead(struct mob_data *md, struct block_list *src, int type); -void mob_revive(struct mob_data *md, unsigned int hp); -void mob_heal(struct mob_data *md,unsigned int heal); - -#define mob_stop_walking(md, type) { if (md->ud.walktimer != -1) unit_stop_walking(&md->bl, type); } -#define mob_stop_attack(md) { if (md->ud.attacktimer != -1) unit_stop_attack(&md->bl); } - -int do_init_mob(void); -int do_final_mob(void); - -int mob_timer_delete(int tid, unsigned int tick, int id, int data); -int mob_deleteslave(struct mob_data *md); - -int mob_random_class (int *value, size_t count); -int mob_get_random_id(int type, int flag, int lv); -int mob_class_change(struct mob_data *md,int class_); -int mob_warpslave(struct block_list *bl, int range); -int mob_linksearch(struct block_list *bl,va_list ap); - -int mobskill_use(struct mob_data *md,unsigned int tick,int event); -int mobskill_event(struct mob_data *md,struct block_list *src,unsigned int tick, int flag); -int mobskill_castend_id( int tid, unsigned int tick, int id,int data ); -int mobskill_castend_pos( int tid, unsigned int tick, int id,int data ); -int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id); -int mob_countslave(struct block_list *bl); -int mob_convertslave(struct mob_data *md); - -int mob_is_clone(int class_); - -int mob_clone_spawn(struct map_session_data *sd, int m, int x, int y, const char *event, int master_id, int mode, int flag, unsigned int duration); -int mob_clone_delete(int class_); - -void mob_reload(void); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _MOB_H_ +#define _MOB_H_ + +#include "unit.h" +#include "map.h" + +#define MAX_RANDOMMONSTER 3 +#define MAX_MOB_RACE_DB 6 + /* Change this to increase the table size in your mob_db to accomodate + a larger mob database. Be sure to note that IDs 4001 to 4048 are reserved for advanced/baby/expanded classes. + */ +#define MAX_MOB_DB 10000 + +//The number of drops all mobs have and the max drop-slot that the steal skill +//will attempt to steal from. +#define MAX_MOB_DROP 10 +#define MAX_STEAL_DROP 7 + +//Min time before mobs do a check to call nearby friends for help (or for slaves to support their master) +#define MIN_MOBLINKTIME 1000 + +//Distance that slaves should keep from their master. +#define MOB_SLAVEDISTANCE 2 + +// These define the range of available IDs for clones. [Valaris] +#define MOB_CLONE_START 9001 +#define MOB_CLONE_END 10000 + +// Scripted Mob AI Constants +#define CALLBACK_NPCCLICK 0x100 +#define CALLBACK_ATTACK 0x80 +#define CALLBACK_DETECT 0x40 +#define CALLBACK_DEAD 0x20 +#define CALLBACK_ASSIST 0x10 +#define CALLBACK_KILL 0x08 +#define CALLBACK_UNLOCK 0x04 +#define CALLBACK_WALKACK 0x02 +#define CALLBACK_WARPACK 0x01 + +int mob_script_callback(struct mob_data *md, struct block_list *target, short action_type); + +struct mob_skill { + short state; + short skill_id,skill_lv; + short permillage; + int casttime,delay; + short cancel; + short cond1,cond2; + short target; + int val[5]; + short emotion; +}; + +struct mob_db { + char sprite[NAME_LENGTH],name[NAME_LENGTH],jname[NAME_LENGTH]; + unsigned int base_exp,job_exp; + unsigned int mexp,mexpper; + unsigned int min_thinktime; //Min think time, Recharge Time as aegis calls it. + int range2,range3; + short race2; // celest + unsigned short lv; + struct { int nameid,p; } dropitem[MAX_MOB_DROP]; + struct { int nameid,p; } mvpitem[3]; + struct status_data status; + struct view_data vd; + short option; + int summonper[MAX_RANDOMMONSTER]; + int maxskill; + struct mob_skill skill[MAX_MOBSKILL]; +}; + +enum { + MST_TARGET = 0, + MST_SELF, + MST_FRIEND, + MST_MASTER, + MST_AROUND5, + MST_AROUND6, + MST_AROUND7, + MST_AROUND8, + MST_AROUND1, + MST_AROUND2, + MST_AROUND3, + MST_AROUND4, + MST_AROUND = MST_AROUND4, + + MSC_ALWAYS = 0x0000, + MSC_MYHPLTMAXRATE, + MSC_MYHPINRATE, + MSC_FRIENDHPLTMAXRATE, + MSC_FRIENDHPINRATE, + MSC_MYSTATUSON, + MSC_MYSTATUSOFF, + MSC_FRIENDSTATUSON, + MSC_FRIENDSTATUSOFF, + MSC_ATTACKPCGT, + MSC_ATTACKPCGE, + MSC_SLAVELT, + MSC_SLAVELE, + MSC_CLOSEDATTACKED, + MSC_LONGRANGEATTACKED, + MSC_AFTERSKILL, + MSC_SKILLUSED , + MSC_CASTTARGETED, + MSC_RUDEATTACKED, + MSC_MASTERHPLTMAXRATE, + MSC_MASTERATTACKED, + MSC_ALCHEMIST, + MSC_SPAWN, +}; + +//Mob skill states. +enum { + MSS_ANY = -1, + MSS_IDLE, + MSS_WALK, + MSS_LOOT, + MSS_DEAD, + MSS_BERSERK, //Aggressive mob attacking + MSS_ANGRY, //Mob retaliating from being attacked. + MSS_RUSH, //Mob following a player after being attacked. + MSS_FOLLOW, //Mob following a player without being attacked. + MSS_ANYTARGET, +}; + + +/*========================================== + * The structure object for item drop with delay + * Since it is only two being able to pass [ int ] a timer function + * Data is put in and passed to this structure object. + *------------------------------------------ + */ +struct item_drop { + struct item item_data; + struct item_drop *next; +}; + +struct item_drop_list { + int m,x,y; + struct map_session_data *first_sd,*second_sd,*third_sd; + struct item_drop *item; +}; + +struct mob_db* mob_db(int class_); +int mobdb_searchname(const char *str); +int mobdb_searchname_array(struct mob_db** data, int size, const char *str); +int mobdb_checkid(const int id); +struct view_data* mob_get_viewdata(int class_); +struct mob_data *mob_once_spawn_sub(struct block_list *bl, int m, + short x, short y, const char *mobname, int class_, const char *event); +int mob_once_spawn(struct map_session_data *sd,char *mapname, + short x,short y,const char *mobname,int class_,int amount,const char *event); +int mob_once_spawn_area(struct map_session_data *sd,char *mapname, + int x0,int y0,int x1,int y1, + const char *mobname,int class_,int amount,const char *event); + +int mob_spawn_guardian(struct map_session_data *sd,char *mapname, // Spawning Guardians [Valaris] + int x,int y,const char *mobname,int class_,int amount,const char *event,int guardian); // Spawning Guardians [Valaris] +int mob_guardian_guildchange(struct block_list *bl,va_list ap); //Change Guardian's ownership. [Skotlex] + +int mob_randomwalk(struct mob_data *md,int tick); + +int mob_target(struct mob_data *md,struct block_list *bl,int dist); +int mob_unlocktarget(struct mob_data *md,int tick); +struct mob_data* mob_spawn_dataset(struct spawn_data *data); +int mob_spawn(struct mob_data *md); +int mob_setdelayspawn(struct mob_data *md); +int mob_parse_dataset(struct spawn_data *data); +void mob_damage(struct mob_data *md, struct block_list *src, int damage); +int mob_dead(struct mob_data *md, struct block_list *src, int type); +void mob_revive(struct mob_data *md, unsigned int hp); +void mob_heal(struct mob_data *md,unsigned int heal); + +#define mob_stop_walking(md, type) { if (md->ud.walktimer != -1) unit_stop_walking(&md->bl, type); } +#define mob_stop_attack(md) { if (md->ud.attacktimer != -1) unit_stop_attack(&md->bl); } + +int do_init_mob(void); +int do_final_mob(void); + +int mob_timer_delete(int tid, unsigned int tick, int id, int data); +int mob_deleteslave(struct mob_data *md); + +int mob_random_class (int *value, size_t count); +int mob_get_random_id(int type, int flag, int lv); +int mob_class_change(struct mob_data *md,int class_); +int mob_warpslave(struct block_list *bl, int range); +int mob_linksearch(struct block_list *bl,va_list ap); + +int mobskill_use(struct mob_data *md,unsigned int tick,int event); +int mobskill_event(struct mob_data *md,struct block_list *src,unsigned int tick, int flag); +int mobskill_castend_id( int tid, unsigned int tick, int id,int data ); +int mobskill_castend_pos( int tid, unsigned int tick, int id,int data ); +int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id); +int mob_countslave(struct block_list *bl); +int mob_convertslave(struct mob_data *md); + +int mob_is_clone(int class_); + +int mob_clone_spawn(struct map_session_data *sd, int m, int x, int y, const char *event, int master_id, int mode, int flag, unsigned int duration); +int mob_clone_delete(int class_); + +void mob_reload(void); + +#endif diff --git a/src/map/npc.c b/src/map/npc.c index fbedef5da..46fd4c4ab 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -1,3084 +1,3084 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include -#include -#include -#include - -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/malloc.h" -#include "../common/grfio.h" -#include "../common/showmsg.h" -#include "../common/ers.h" -#include "../common/db.h" -#include "map.h" -#include "log.h" -#include "npc.h" -#include "clif.h" -#include "intif.h" -#include "pc.h" -#include "status.h" -#include "itemdb.h" -#include "script.h" -#include "mob.h" -#include "pet.h" -#include "battle.h" -#include "skill.h" -#include "unit.h" - -#ifdef _WIN32 -#undef isspace -#define isspace(x) (x == ' ' || x == '\t') -#endif - -struct npc_src_list { - struct npc_src_list * next; -// struct npc_src_list * prev; //[Shinomori] - char name[4]; -}; - -static struct npc_src_list *npc_src_first=NULL; -static struct npc_src_list *npc_src_last=NULL; -static int npc_id=START_NPC_NUM; -static int npc_warp=0; -static int npc_shop=0; -static int npc_script=0; -static int npc_mob=0; -static int npc_delay_mob=0; -static int npc_cache_mob=0; -char *current_file = NULL; -int npc_get_new_npc_id(void){ return npc_id++; } - -static struct dbt *ev_db; -static struct dbt *npcname_db; - -struct event_data { - struct npc_data *nd; - int pos; -}; -static struct tm ev_tm_b; // 時計イベント用 - -static struct eri *timer_event_ers; //For the npc timer data. [Skotlex] - -//For holding the view data of npc classes. [Skotlex] -static struct view_data npc_viewdb[MAX_NPC_CLASS]; - -static struct -{ //Holds pointers to the commonly executed scripts for speedup. [Skotlex] - struct npc_data *nd; - struct event_data *event[UCHAR_MAX]; - unsigned char *event_name[UCHAR_MAX]; - unsigned char event_count; -} script_event[NPCE_MAX]; - -struct view_data* npc_get_viewdata(int class_) -{ //Returns the viewdata for normal npc classes. - if (class_ == INVISIBLE_CLASS) - return &npc_viewdb[0]; - if (npcdb_checkid(class_) || class_ == WARP_CLASS) - return &npc_viewdb[class_]; - return NULL; -} -/*========================================== - * NPCの無効化/有効化 - * npc_enable - * npc_enable_sub 有効時にOnTouchイベントを実行 - *------------------------------------------ - */ -int npc_enable_sub( struct block_list *bl, va_list ap ) -{ - struct map_session_data *sd; - struct npc_data *nd; - //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori] - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - nullpo_retr(0, nd=va_arg(ap,struct npc_data *)); - if(bl->type == BL_PC && (sd=(struct map_session_data *)bl)){ - char name[50]; // need 24 + 9 for the "::OnTouch" - - if (nd->sc.option&OPTION_INVISIBLE) // 無効化されている - return 1; - - if(sd->areanpc_id==nd->bl.id) - return 1; - sd->areanpc_id=nd->bl.id; - - snprintf(name, 50, "%s::OnTouch", nd->exname); // exname to be specific. exname is the unique identifier for script events. [Lance] - npc_event(sd,name,0); - } - //aFree(name); - return 0; -} -int npc_enable(const char *name,int flag) -{ - struct npc_data *nd= strdb_get(npcname_db,(unsigned char*)name); - if (nd==NULL) - return 0; - - if (flag&1) - nd->sc.option&=~OPTION_INVISIBLE; - else if (flag&2) - nd->sc.option&=~OPTION_HIDE; - else if (flag&4) - nd->sc.option|= OPTION_HIDE; - else //Can't change the view_data to invisible class because the view_data for all npcs is shared! [Skotlex] - nd->sc.option|= OPTION_INVISIBLE; - - if (nd->class_ == WARP_CLASS) - { //Client won't display option changes for warp portals [Toms] - if (nd->sc.option&(OPTION_HIDE|OPTION_INVISIBLE)) - clif_clearchar(&nd->bl, 0); - else - clif_spawn(&nd->bl); - } else - clif_changeoption(&nd->bl); - - if(flag&3 && (nd->u.scr.xs > 0 || nd->u.scr.ys >0)) - map_foreachinarea( npc_enable_sub,nd->bl.m,nd->bl.x-nd->u.scr.xs,nd->bl.y-nd->u.scr.ys,nd->bl.x+nd->u.scr.xs,nd->bl.y+nd->u.scr.ys,BL_PC,nd); - - return 0; -} - -/*========================================== - * NPCを名前で探す - *------------------------------------------ - */ -struct npc_data* npc_name2id(const char *name) -{ - return (struct npc_data *) strdb_get(npcname_db,(unsigned char*)name); -} - -/*========================================== - * イベントキューのイベント処理 - *------------------------------------------ - */ -int npc_event_dequeue(struct map_session_data *sd) -{ - nullpo_retr(0, sd); - - if (!sd->eventqueue[0][0]) - return 0; //Nothing to dequeue - - if (!pc_addeventtimer(sd,100,sd->eventqueue[0])) - { //Failed to dequeue, couldn't set a timer. - ShowWarning("npc_event_dequeue: event timer is full !\n"); - return 0; - } - //Event dequeued successfully, shift other elements. - sd->npc_id=0; //FIXME: Shouldn't dequeueing fail when you have an npc_id set? - memmove(sd->eventqueue[0], sd->eventqueue[1], (MAX_EVENTQUEUE-1)*sizeof(sd->eventqueue[0])); - sd->eventqueue[MAX_EVENTQUEUE-1][0]=0; - return 1; -} - -/*========================================== - * イベントの遅延実行 - *------------------------------------------ - */ -int npc_event_timer(int tid,unsigned int tick,int id,int data) -{ - unsigned char *eventname = (unsigned char *)data; - struct event_data *ev = strdb_get(ev_db,eventname); - struct npc_data *nd; - struct map_session_data *sd=map_id2sd(id); - size_t i; - - if((ev==NULL || (nd=ev->nd)==NULL)) - { - if(battle_config.error_log) - ShowWarning("npc_event: event not found [%s]\n",eventname); - } - else - { - for(i=0;ieventtimer[i]==tid ) { - nd->eventtimer[i]=-1; - npc_event(sd,eventname,0); // sd NULL check is within - break; - } - } - if(i==MAX_EVENTTIMER && battle_config.error_log) - ShowWarning("npc_event_timer: event timer not found [%s]!\n",eventname); - } - - aFree(eventname); - return 0; -} - -int npc_timer_event(const unsigned char *eventname) // Added by RoVeRT -{ - struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname); - struct npc_data *nd; -// int xs,ys; - - if((ev==NULL || (nd=ev->nd)==NULL)){ - ShowWarning("npc_timer_event: event not found [%s]\n",eventname); - return 0; - } - - run_script(nd->u.scr.script,ev->pos,nd->bl.id,nd->bl.id); - - return 0; -} -/* -int npc_timer_sub_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT -{ - char *p=(char *)key; - struct event_data *ev=(struct event_data *)data; - int *c=va_arg(ap,int *); - int tick=0,ctick=gettick(); - char temp[10]; - char event[100]; - - if(ev->nd->bl.id==(int)*c && (p=strchr(p,':')) && p && strncasecmp("::OnTimer",p,8)==0 ){ - sscanf(&p[9],"%s",temp); - tick=atoi(temp); - - strcpy( event, ev->nd->name); - strcat( event, p); - - if (ctick >= ev->nd->lastaction && ctick - ev->nd->timer >= tick) { - npc_timer_event(event); - ev->nd->lastaction = ctick; - } - } - return 0; -} - -int npc_timer_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT -{ - struct npc_data *nd=(struct npc_data*)data; - - if(nd->timer == -1) - return 0; - - sv_db->foreach(ev_db,npc_timer_sub_sub,&nd->bl.id); - - return 0; -} - -int npc_timer(int tid,unsigned int tick,int id,int data) // Added by RoVeRT -{ - npcname_db->foreach(npcname_db,npc_timer_sub); - - aFree((void*)data); - return 0; -}*/ -/*========================================== - * イベント用ラベルのエクスポート - * npc_parse_script->strdb_foreachから呼ばれる - *------------------------------------------ - */ -int npc_event_export(char *lname,void *data,va_list ap) -{ - int pos=(int)data; - struct npc_data *nd=va_arg(ap,struct npc_data *); - - if ((lname[0]=='O' || lname[0]=='o')&&(lname[1]=='N' || lname[1]=='n')) { - struct event_data *ev; - unsigned char buf[51]; - char *p=strchr(lname,':'); - // エクスポートされる - ev=(struct event_data *) aMalloc(sizeof(struct event_data)); - if (ev==NULL) { - ShowFatalError("npc_event_export: out of memory !\n"); - exit(1); - }else if (p==NULL || (p-lname)>NAME_LENGTH) { - ShowFatalError("npc_event_export: label name error !\n"); - exit(1); - }else{ - ev->nd=nd; - ev->pos=pos; - *p='\0'; - sprintf(buf,"%s::%s",nd->exname,lname); - *p=':'; - strdb_put(ev_db,buf,ev); - } - } - return 0; -} - -int npc_event_sub(struct map_session_data *, struct event_data *, const unsigned char *); //[Lance] -/*========================================== - * 全てのNPCのOn*イベント実行 - *------------------------------------------ - */ -int npc_event_doall_sub(DBKey key,void *data,va_list ap) -{ - unsigned char*p = key.str; - struct event_data *ev; - int *c; - int rid; - unsigned char *name; - - ev=(struct event_data *)data; - c=va_arg(ap,int *); - name=va_arg(ap,unsigned char *); - rid=va_arg(ap, int); - - if( (p=strchr(p,':')) && p && strcmpi(name,p)==0 ){ - if(rid) - npc_event_sub(((struct map_session_data *)map_id2bl(rid)),ev,key.str); - else - run_script(ev->nd->u.scr.script,ev->pos,rid,ev->nd->bl.id); - (*c)++; - } - - return 0; -} -int npc_event_doall(const unsigned char *name) -{ - int c=0; - unsigned char buf[64]="::"; - - strncpy(buf+2,name,62); - ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,0); - return c; -} -int npc_event_doall_id(const unsigned char *name, int rid) -{ - int c=0; - unsigned char buf[64]="::"; - - strncpy(buf+2,name,62); - ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,rid); - return c; -} - -int npc_event_do_sub(DBKey key,void *data,va_list ap) -{ - unsigned char *p = key.str; - struct event_data *ev; - int *c; - const unsigned char *name; - - nullpo_retr(0, ev=(struct event_data *)data); - nullpo_retr(0, ap); - nullpo_retr(0, c=va_arg(ap,int *)); - - name=va_arg(ap,const unsigned char *); - - if (p && strcmpi(name,p)==0 ) { - run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id); - (*c)++; - } - - return 0; -} -int npc_event_do(const unsigned char *name) -{ - int c=0; - - if (*name==':' && name[1]==':') { - return npc_event_doall(name+2); - } - - ev_db->foreach(ev_db,npc_event_do_sub,&c,name); - return c; -} - -/*========================================== - * 時計イベント実行 - *------------------------------------------ - */ -int npc_event_do_clock(int tid,unsigned int tick,int id,int data) -{ - time_t timer; - struct tm *t; - char buf[64]; - char *day=""; - int c=0; - - time(&timer); - t=localtime(&timer); - - switch (t->tm_wday) { - case 0: day = "Sun"; break; - case 1: day = "Mon"; break; - case 2: day = "Tue"; break; - case 3: day = "Wed"; break; - case 4: day = "Thu"; break; - case 5: day = "Fri"; break; - case 6: day = "Sat"; break; - } - - if (t->tm_min != ev_tm_b.tm_min ) { - sprintf(buf,"OnMinute%02d",t->tm_min); - c+=npc_event_doall(buf); - sprintf(buf,"OnClock%02d%02d",t->tm_hour,t->tm_min); - c+=npc_event_doall(buf); - sprintf(buf,"On%s%02d%02d",day,t->tm_hour,t->tm_min); - c+=npc_event_doall(buf); - } - if (t->tm_hour!= ev_tm_b.tm_hour) { - sprintf(buf,"OnHour%02d",t->tm_hour); - c+=npc_event_doall(buf); - } - if (t->tm_mday!= ev_tm_b.tm_mday) { - sprintf(buf,"OnDay%02d%02d",t->tm_mon+1,t->tm_mday); - c+=npc_event_doall(buf); - } - memcpy(&ev_tm_b,t,sizeof(ev_tm_b)); - return c; -} -/*========================================== - * OnInitイベント実行(&時計イベント開始) - *------------------------------------------ - */ -int npc_event_do_oninit(void) -{ -// int c = npc_event_doall("OnInit"); - ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '" - CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit")); - - add_timer_interval(gettick()+100, - npc_event_do_clock,0,0,1000); - - return 0; -} -/*========================================== - * OnTimer NPC event - by RoVeRT - *------------------------------------------ - */ -int npc_addeventtimer(struct npc_data *nd,int tick,const char *name) -{ - int i; - unsigned char *evname; - - for(i=0;ieventtimer[i]==-1 ) - break; - if(ieventtimer[i]=add_timer(gettick()+tick, - npc_event_timer,nd->bl.id,(int)evname); - }else - ShowWarning("npc_addtimer: event timer is full !\n"); - - return 0; -} - -int npc_deleventtimer(struct npc_data *nd,const unsigned char *name) -{ - int i; - for(i=0;ieventtimer[i]!=-1 && strcmp( - (unsigned char *)(get_timer(nd->eventtimer[i])->data), name)==0 ){ - delete_timer(nd->eventtimer[i],npc_event_timer); - nd->eventtimer[i]=-1; - break; - } - - return 0; -} - -int npc_cleareventtimer(struct npc_data *nd) -{ - int i; - for(i=0;ieventtimer[i]!=-1 ){ - delete_timer(nd->eventtimer[i],npc_event_timer); - nd->eventtimer[i]=-1; - } - - return 0; -} - -int npc_do_ontimer_sub(DBKey key,void *data,va_list ap) -{ - unsigned char *p = key.str; - struct event_data *ev = (struct event_data *)data; - int *c = va_arg(ap,int *); -// struct map_session_data *sd=va_arg(ap,struct map_session_data *); - int option = va_arg(ap,int); - int tick = 0; - char temp[10]; - char event[50]; - - if(ev->nd->bl.id == (int)*c && (p = strchr(p,':')) && strnicmp("::OnTimer",p,8) == 0){ - sscanf(&p[9], "%s", temp); - tick = atoi(temp); - - strcpy(event, ev->nd->name); - strcat(event, p); - - if (option!=0) { - npc_addeventtimer(ev->nd, tick, event); - } else { - npc_deleventtimer(ev->nd, event); - } - } - return 0; -} -int npc_do_ontimer(int npc_id, int option) -{ - ev_db->foreach(ev_db, npc_do_ontimer_sub, &npc_id, option); - return 0; -} -/*========================================== - * タイマーイベント用ラベルの取り込み - * npc_parse_script->strdb_foreachから呼ばれる - *------------------------------------------ - */ -int npc_timerevent_import(char *lname,void *data,va_list ap) -{ - int pos=(int)data; - struct npc_data *nd=va_arg(ap,struct npc_data *); - int t=0,i=0; - - if(sscanf(lname,"OnTimer%d%n",&t,&i)==1 && lname[i]==':') { - // タイマーイベント - struct npc_timerevent_list *te=nd->u.scr.timer_event; - int j,i=nd->u.scr.timeramount; - if(te==NULL) te=(struct npc_timerevent_list*)aMallocA(sizeof(struct npc_timerevent_list)); - else te= (struct npc_timerevent_list*)aRealloc( te, sizeof(struct npc_timerevent_list) * (i+1) ); - if(te==NULL){ - ShowFatalError("npc_timerevent_import: out of memory !\n"); - exit(1); - } - for(j=0;jt){ - memmove(te+j+1,te+j,sizeof(struct npc_timerevent_list)*(i-j)); - break; - } - } - te[j].timer=t; - te[j].pos=pos; - nd->u.scr.timer_event=te; - nd->u.scr.timeramount++; - } - return 0; -} -struct timer_event_data { - int rid; //Attached player for this timer. - int next; //timer index (starts with 0, then goes up to nd->u.scr.timeramount - int time; //holds total time elapsed for the script since time 0 (whenthe timers started) - unsigned int otick; //Holds tick value at which timer sequence was started (that is, it stores the tick value for which T= 0 -}; - -/*========================================== - * タイマーイベント実行 - *------------------------------------------ - */ -int npc_timerevent(int tid,unsigned int tick,int id,int data) -{ - int next,t,old_rid,old_timer; - unsigned int old_tick; - struct npc_data* nd=(struct npc_data *)map_id2bl(id); - struct npc_timerevent_list *te; - struct timer_event_data *ted = (struct timer_event_data*)data; - struct map_session_data *sd=NULL; - - if( nd==NULL ){ - ShowError("npc_timerevent: NPC not found??\n"); - return 0; - } - if (ted->rid) { - sd = map_id2sd(ted->rid); - if (!sd) { - if(battle_config.error_log) - ShowError("npc_timerevent: Attached player not found.\n"); - ers_free(timer_event_ers, ted); - return 0; - } - } - old_rid = nd->u.scr.rid; //To restore it later. - nd->u.scr.rid = sd?sd->bl.id:0; - - old_tick = nd->u.scr.timertick; - nd->u.scr.timertick=ted->otick; - te=nd->u.scr.timer_event+ ted->next; - - old_timer = nd->u.scr.timer; - t = nd->u.scr.timer=ted->time; - ted->next++; - - if( nd->u.scr.timeramount> ted->next){ - next= nd->u.scr.timer_event[ ted->next ].timer - - nd->u.scr.timer_event[ ted->next-1 ].timer; - ted->time+=next; - if (sd) - sd->npc_timer_id = add_timer(tick+next,npc_timerevent,id,(int)ted); - else - nd->u.scr.timerid = add_timer(tick+next,npc_timerevent,id,(int)ted); - } else { - if (sd) - sd->npc_timer_id = -1; - else - nd->u.scr.timerid = -1; - ers_free(timer_event_ers, ted); - } - run_script(nd->u.scr.script,te->pos,nd->u.scr.rid,nd->bl.id); - //Restore previous data, only if this timer is a player-attached one. - if (sd) { - nd->u.scr.rid = old_rid; - nd->u.scr.timer = old_timer; - nd->u.scr.timertick = old_tick; - } - return 0; -} -/*========================================== - * タイマーイベント開始 - *------------------------------------------ - */ -int npc_timerevent_start(struct npc_data *nd, int rid) -{ - int j,n, next; - struct map_session_data *sd=NULL; //Player to whom script is attached. - struct timer_event_data *ted; - - nullpo_retr(0, nd); - - n=nd->u.scr.timeramount; - if( n==0 ) - return 0; - - for(j=0;ju.scr.timer_event[j].timer > nd->u.scr.timer ) - break; - } - if(j>=n) // check if there is a timer to use !!BEFORE!! you write stuff to the structures [Shinomori] - return 0; - if (nd->u.scr.rid > 0) { - //Try to attach timer to this player. - sd = map_id2sd(nd->u.scr.rid); - if (!sd) { - if(battle_config.error_log) - ShowError("npc_timerevent_start: Attached player not found!\n"); - return 1; - } - } - //Check if timer is already started. - if (sd) { - if (sd->npc_timer_id != -1) - return 0; - } else if (nd->u.scr.timerid != -1) - return 0; - - ted = ers_alloc(timer_event_ers, struct timer_event_data); - ted->next = j; - nd->u.scr.timertick=ted->otick=gettick(); - - //Attach only the player if attachplayerrid was used. - ted->rid = sd?sd->bl.id:0; - -// Do not store it to make way to two types of timers: globals and personals. -// if (rid >= 0) nd->u.scr.rid=rid; // changed to: attaching to given rid by default [Shinomori] - // if rid is less than 0 leave it unchanged [celest] - - next = nd->u.scr.timer_event[j].timer - nd->u.scr.timer; - ted->time = nd->u.scr.timer_event[j].timer; - if (sd) - sd->npc_timer_id = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(int)ted); - else - nd->u.scr.timerid = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(int)ted); - return 0; -} -/*========================================== - * タイマーイベント終了 - *------------------------------------------ - */ -int npc_timerevent_stop(struct npc_data *nd) -{ - struct map_session_data *sd =NULL; - struct TimerData *td = NULL; - int *tid; - nullpo_retr(0, nd); - if (nd->u.scr.rid) { - sd = map_id2sd(nd->u.scr.rid); - if (!sd) { - if(battle_config.error_log) - ShowError("npc_timerevent_stop: Attached player not found!\n"); - return 1; - } - } - - tid = sd?&sd->npc_timer_id:&nd->u.scr.timerid; - - if (*tid == -1) //Nothing to stop - return 0; - td = get_timer(*tid); - if (td && td->data) - ers_free(timer_event_ers, (struct event_timer_data*)td->data); - delete_timer(*tid,npc_timerevent); - *tid = -1; - //Set the timer tick to the time that has passed since the beginning of the timers and now. - nd->u.scr.timer = DIFF_TICK(gettick(),nd->u.scr.timertick); -// nd->u.scr.rid = 0; //Eh? why detach? - return 0; -} -/*========================================== - * Aborts a running npc timer that is attached to a player. - *------------------------------------------ - */ -void npc_timerevent_quit(struct map_session_data *sd) { - struct TimerData *td; - if (sd->npc_timer_id == -1) - return; - td = get_timer(sd->npc_timer_id); - if (!td) { - sd->npc_timer_id = -1; - return; //?? - } - delete_timer(sd->npc_timer_id,npc_timerevent); - sd->npc_timer_id = -1; - ers_free(timer_event_ers, (struct event_timer_data*)td->data); -} - -/*========================================== - * タイマー値の所得 - *------------------------------------------ - */ -int npc_gettimerevent_tick(struct npc_data *nd) -{ - int tick; - nullpo_retr(0, nd); - - tick=nd->u.scr.timer; - if (nd->u.scr.timertick) - tick+=DIFF_TICK(gettick(), nd->u.scr.timertick); - return tick; -} -/*========================================== - * タイマー値の設定 - *------------------------------------------ - */ -int npc_settimerevent_tick(struct npc_data *nd,int newtimer) -{ - int flag; - struct map_session_data *sd=NULL; - - nullpo_retr(0, nd); - - if (nd->u.scr.rid) { - sd = map_id2sd(nd->u.scr.rid); - if (!sd) { - if(battle_config.error_log) - ShowError("npc_settimerevent_tick: Attached player not found!\n"); - return 1; - } - flag= sd->npc_timer_id != -1 ; - } else - flag= nd->u.scr.timerid != -1 ; - - if(flag) - npc_timerevent_stop(nd); - nd->u.scr.timer=newtimer; - if(flag) - npc_timerevent_start(nd, -1); - return 0; -} - -int npc_event_sub(struct map_session_data *sd, struct event_data *ev, const unsigned char *eventname){ - - if ( sd->npc_id!=0) { - //Enqueue the event trigger. - int i; - for(i=0;ieventqueue[i][0];i++); - - if (i==MAX_EVENTQUEUE) { - if (battle_config.error_log) - ShowWarning("npc_event: event queue is full !\n"); - }else //Event enqueued. - memcpy(sd->eventqueue[i],eventname,50); - return 1; - } - if (ev->nd->sc.option&OPTION_INVISIBLE) { - //Disabled npc, shouldn't trigger event. - npc_event_dequeue(sd); - return 2; - } - run_script(ev->nd->u.scr.script,ev->pos,sd->bl.id,ev->nd->bl.id); - return 0; -} - -/*========================================== - * イベント型のNPC処理 - *------------------------------------------ - */ -int npc_event (struct map_session_data *sd, const unsigned char *eventname, int mob_kill) -{ - struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname); - struct npc_data *nd; - int xs,ys; - unsigned char mobevent[100]; - - if (sd == NULL) { - nullpo_info(NLP_MARK); - return 0; - } - - if (ev == NULL && eventname && strcmp(((eventname)+strlen(eventname)-9),"::OnTouch") == 0) - return 1; - - if (ev == NULL || (nd = ev->nd) == NULL) { - if (mob_kill) { - strcpy( mobevent, eventname); - strcat( mobevent, "::OnMyMobDead"); - ev = strdb_get(ev_db, mobevent); - if (ev == NULL || (nd = ev->nd) == NULL) { - if (strnicmp(eventname, "GM_MONSTER",10) != 0) - ShowError("npc_event: (mob_kill) event not found [%s]\n", mobevent); - return 0; - } - } else { - if (battle_config.error_log) - ShowError("npc_event: event not found [%s]\n", eventname); - return 0; - } - } - - xs=nd->u.scr.xs; - ys=nd->u.scr.ys; - if (xs>=0 && ys>=0 && (strcmp(((eventname)+strlen(eventname)-6),"Global") != 0) ) - { - if (nd->bl.m >= 0) { //Non-invisible npc - if (nd->bl.m != sd->bl.m ) - return 1; - if ( xs>0 && (sd->bl.xbl.x-xs/2 || nd->bl.x+xs/2bl.x) ) - return 1; - if ( ys>0 && (sd->bl.ybl.y-ys/2 || nd->bl.y+ys/2bl.y) ) - return 1; - } - } - - return npc_event_sub(sd,ev,eventname); -} - - -int npc_command_sub(DBKey key,void *data,va_list ap) -{ - unsigned char *p = key.str; - struct event_data *ev=(struct event_data *)data; - unsigned char *npcname=va_arg(ap,char *); - char *command=va_arg(ap,char *); - unsigned char temp[100]; - - if(strcmp(ev->nd->name,npcname)==0 && (p=strchr(p,':')) && p && strnicmp("::OnCommand",p,10)==0 ){ - sscanf(&p[11],"%s",temp); - - if (strcmp(command,temp)==0) - run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id); - } - - return 0; -} - -int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command) -{ - ev_db->foreach(ev_db,npc_command_sub,npcname,command); - - return 0; -} -/*========================================== - * 接触型のNPC処理 - *------------------------------------------ - */ -int npc_touch_areanpc(struct map_session_data *sd,int m,int x,int y) -{ - int i,f=1; - int xs,ys; - - nullpo_retr(1, sd); - - if(sd->npc_id) - return 1; - - for(i=0;isc.option&OPTION_INVISIBLE) { // 無効化されている - f=0; - continue; - } - - switch(map[m].npc[i]->bl.subtype) { - case WARP: - xs=map[m].npc[i]->u.warp.xs; - ys=map[m].npc[i]->u.warp.ys; - break; - case SCRIPT: - xs=map[m].npc[i]->u.scr.xs; - ys=map[m].npc[i]->u.scr.ys; - break; - default: - continue; - } - if (x >= map[m].npc[i]->bl.x-xs/2 && x < map[m].npc[i]->bl.x-xs/2+xs && - y >= map[m].npc[i]->bl.y-ys/2 && y < map[m].npc[i]->bl.y-ys/2+ys) - break; - } - if (i==map[m].npc_num) { - if (f) { - if (battle_config.error_log) - ShowError("npc_touch_areanpc : some bug \n"); - } - return 1; - } - switch(map[m].npc[i]->bl.subtype) { - case WARP: - // hidden chars cannot use warps -- is it the same for scripts too? - if (sd->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || - (!battle_config.duel_allow_teleport && sd->duel_group)) // duel rstrct [LuzZza] - break; - pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0); - break; - case SCRIPT: - { - //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori] - char name[50]; // need 24 max + 9 for "::OnTouch" - - if(sd->areanpc_id == map[m].npc[i]->bl.id) - return 1; - sd->areanpc_id = map[m].npc[i]->bl.id; - - sprintf(name,"%s::OnTouch", map[m].npc[i]->exname); // It goes here too. exname being the unique identifier. [Lance] - - if( npc_event(sd,name,0)>0 ) { - pc_stop_walking(sd,1); //Make it stop walking! - npc_click(sd,map[m].npc[i]); - } - //aFree(name); - break; - } - } - return 0; -} - -int npc_touch_areanpc2(struct block_list *bl) -{ - int i,m=bl->m; - int xs,ys; - - for(i=0;isc.option&OPTION_INVISIBLE) - continue; - - if (map[m].npc[i]->bl.subtype!=WARP) - continue; - - xs=map[m].npc[i]->u.warp.xs; - ys=map[m].npc[i]->u.warp.ys; - - if (bl->x >= map[m].npc[i]->bl.x-xs/2 && bl->x < map[m].npc[i]->bl.x-xs/2+xs && - bl->y >= map[m].npc[i]->bl.y-ys/2 && bl->y < map[m].npc[i]->bl.y-ys/2+ys) - break; - } - if (i==map[m].npc_num) - return 0; - - xs = map_mapindex2mapid(map[m].npc[i]->u.warp.mapindex); - if (xs < 0) // Can't warp object between map servers... - return 0; - - if (unit_warp(bl, xs, map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0)) - return 0; //Failed to warp. - - return 1; -} - -/*========================================== - * 近くかどうかの判定 - *------------------------------------------ - */ -int npc_checknear2(struct map_session_data *sd,struct block_list *bl) -{ - nullpo_retr(1, sd); - if(bl == NULL) return 1; - - if(sd->state.using_fake_npc && sd->npc_id == bl->id) - return 0; - - if (status_get_class(bl)<0) //Class-less npc, enable click from anywhere. - return 0; - - if (bl->m!=sd->bl.m || - bl->xbl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 || - bl->ybl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1) - return 1; - - return 0; -} - -TBL_NPC *npc_checknear(struct map_session_data *sd,struct block_list *bl) -{ - struct npc_data *nd; - - nullpo_retr(NULL, sd); - if(bl == NULL) return NULL; - if(bl->type != BL_NPC) return NULL; - nd = (TBL_NPC*)bl; - - if(sd->state.using_fake_npc && sd->npc_id == bl->id) - return nd; - - if (nd->class_<0) //Class-less npc, enable click from anywhere. - return nd; - - if (bl->m!=sd->bl.m || - bl->xbl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 || - bl->ybl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1) - return NULL; - - return nd; -} - -/*========================================== - * NPCのオープンチャット発言 - *------------------------------------------ - */ -int npc_globalmessage(const char *name,char *mes) -{ - struct npc_data *nd=(struct npc_data *) strdb_get(npcname_db,(unsigned char*)name); - char temp[100]; - - if (!nd) - return 0; - - snprintf(temp, sizeof temp ,"%s : %s",name,mes); - clif_GlobalMessage(&nd->bl,temp); - - return 0; -} - -/*========================================== - * クリック時のNPC処理 - *------------------------------------------ - */ -int npc_click(struct map_session_data *sd,struct npc_data *nd) -{ - nullpo_retr(1, sd); - - if (sd->npc_id != 0) { - if (battle_config.error_log) - ShowError("npc_click: npc_id != 0\n"); - return 1; - } - - if(!nd) return 1; - if ((nd = npc_checknear(sd,&nd->bl)) == NULL) - return 1; - //Hidden/Disabled npc. - if (nd->class_ < 0 || nd->sc.option&OPTION_INVISIBLE) - return 1; - - switch(nd->bl.subtype) { - case SHOP: - clif_npcbuysell(sd,nd->bl.id); - npc_event_dequeue(sd); - break; - case SCRIPT: - run_script(nd->u.scr.script,0,sd->bl.id,nd->bl.id); - break; - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int npc_scriptcont(struct map_session_data *sd,int id) -{ - nullpo_retr(1, sd); - - if (id!=sd->npc_id){ - ShowWarning("npc_scriptcont: sd->npc_id (%d) is not id (%d).\n", sd->npc_id, id); - return 1; - } - - if(id != fake_nd->bl.id) { // Not item script - if ((npc_checknear(sd,map_id2bl(id))) == NULL){ - ShowWarning("npc_scriptcont: failed npc_checknear test.\n"); - return 1; - } - } - run_script_main(sd->st); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int npc_buysellsel(struct map_session_data *sd,int id,int type) -{ - struct npc_data *nd; - - nullpo_retr(1, sd); - - if ((nd = npc_checknear(sd,map_id2bl(id))) == NULL) - return 1; - - if (nd->bl.subtype!=SHOP) { - if (battle_config.error_log) - ShowError("no such shop npc : %d\n",id); - if (sd->npc_id == id) - sd->npc_id=0; - return 1; - } - if (nd->sc.option&OPTION_INVISIBLE) // 無効化されている - return 1; - - sd->npc_shopid=id; - if (type==0) { - clif_buylist(sd,nd); - } else { - clif_selllist(sd); - } - return 0; -} - -//npc_buylist for script-controlled shops. -static int npc_buylist_sub( - struct map_session_data *sd,int n, - unsigned short *item_list, struct npc_data *nd) -{ - unsigned char npc_ev[51]; - int i; - int regkey = add_str("@bought_nameid"); - int regkey2 = add_str("@bought_quantity"); - sprintf(npc_ev, "%s::OnBuyItem", nd->exname); - for(i=0;inpc_shopid))) == NULL) - return 3; - - if (nd->master_nd) //Script-based shops. - return npc_buylist_sub(sd,n,item_list,nd->master_nd); - - if (nd->bl.subtype!=SHOP) - return 3; - - for(i=0,w=0,z=0;iu.shop_item[j].nameid;j++) { - if (nd->u.shop_item[j].nameid==item_list[i*2+1] || //Normal items - itemdb_viewid(nd->u.shop_item[j].nameid)==item_list[i*2+1]) //item_avail replacement - break; - } - if (nd->u.shop_item[j].nameid==0) - return 3; - - if (!itemdb_isstackable(nd->u.shop_item[j].nameid) && item_list[i*2] > 1) - { //Exploit? You can't buy more than 1 of equipment types o.O - ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %d!\n", - sd->status.name, sd->status.account_id, sd->status.char_id, item_list[i*2], nd->u.shop_item[j].nameid); - item_list[i*2] = 1; - } - if (itemdb_value_notdc(nd->u.shop_item[j].nameid)) - z+=(double)nd->u.shop_item[j].value * item_list[i*2]; - else - z+=(double)pc_modifybuyvalue(sd,nd->u.shop_item[j].value) * item_list[i*2]; - itemamount+=item_list[i*2]; - - switch(pc_checkadditem(sd,item_list[i*2+1],item_list[i*2])) { - case ADDITEM_EXIST: - break; - case ADDITEM_NEW: - new_++; - break; - case ADDITEM_OVERAMOUNT: - return 2; - } - - w+=itemdb_weight(item_list[i*2+1]) * item_list[i*2]; - } - if (z > (double)sd->status.zeny) - return 1; // zeny不足 - if (w+sd->weight > sd->max_weight) - return 2; // 重量超過 - if (pc_inventoryblank(sd) 0 ) - log_zeny(sd, "S", sd, -(int)z); - //Logs - - pc_payzeny(sd,(int)z); - for(i=0;i 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0) { - if (sd->status.skill[MC_DISCOUNT].flag != 0) - skill = sd->status.skill[MC_DISCOUNT].flag - 2; - if (skill > 0) { - z = z * (double)skill * (double)battle_config.shop_exp/10000.; - if (z < 1) - z = 1; - pc_gainexp(sd,NULL,0,(int)z); - } - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int npc_selllist(struct map_session_data *sd,int n,unsigned short *item_list) -{ - double z; - int i,skill,itemamount=0; - struct npc_data *nd; - - nullpo_retr(1, sd); - nullpo_retr(1, item_list); - - if ((nd = npc_checknear(sd,map_id2bl(sd->npc_shopid))) == NULL) - return 1; - nd = nd->master_nd; //For OnSell triggers. - - for(i=0,z=0;i=MAX_INVENTORY || qty < 0) - break; - - nameid=sd->status.inventory[idx].nameid; - if (nameid == 0 || !sd->inventory_data[idx] || - sd->status.inventory[idx].amount < qty) - break; - - if (sd->inventory_data[idx]->flag.value_notoc) - z+=(double)qty*sd->inventory_data[idx]->value_sell; - else - z+=(double)qty*pc_modifysellvalue(sd,sd->inventory_data[idx]->value_sell); - - if(sd->inventory_data[idx]->type==7 && sd->status.inventory[idx].card[0] == (short)0xff00) - { - if(search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0) - intif_delete_petdata(MakeDWord(sd->status.inventory[idx].card[1],sd->status.inventory[idx].card[2])); - } - - if(log_config.enable_logs&0x20) //Logs items, Sold to NPC (S)hop [Lupus] - log_pick_pc(sd, "S", nameid, -qty, &sd->status.inventory[idx]); - - if(nd) { - pc_setreg(sd,add_str("@sold_nameid")+(i<<24),(int)sd->status.inventory[idx].nameid); - pc_setreg(sd,add_str("@sold_quantity")+(i<<24),qty); - } - itemamount+=qty; - pc_delitem(sd,idx,qty,0); - } - - if (z > MAX_ZENY) z = MAX_ZENY; - - if(log_config.zeny) //Logs (S)hopping Zeny [Lupus] - log_zeny(sd, "S", sd, (int)z); - - pc_getzeny(sd,(int)z); - - if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_OVERCHARGE)) > 0) { - if (sd->status.skill[MC_OVERCHARGE].flag != 0) - skill = sd->status.skill[MC_OVERCHARGE].flag - 2; - if (skill > 0) { - z = z * (double)skill * (double)battle_config.shop_exp/10000.; - if (z < 1) - z = 1; - pc_gainexp(sd,NULL,0,(int)z); - } - } - - if(nd) { - unsigned char npc_ev[51]; - sprintf(npc_ev, "%s::OnSellItem", nd->exname); - npc_event(sd, npc_ev, 0); - } - - if (ifd); - return 1; - } - return 0; -} - -int npc_remove_map (struct npc_data *nd) -{ - int m,i; - nullpo_retr(1, nd); - - if(nd->bl.prev == NULL || nd->bl.m < 0) - return 1; //Not assigned to a map. - m = nd->bl.m; -#ifdef PCRE_SUPPORT - npc_chat_finalize(nd); -#endif - clif_clearchar_area(&nd->bl,2); - strdb_remove(npcname_db, (nd->bl.subtype < SCRIPT) ? nd->name : nd->exname); - //Remove corresponding NPC CELLs - if (nd->bl.subtype == WARP) { - int j, xs, ys, x, y; - x = nd->bl.x; - y = nd->bl.y; - xs = nd->u.warp.xs; - ys = nd->u.warp.ys; - - for (i = 0; i < ys; i++) { - for (j = 0; j < xs; j++) { - if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNPC)) - map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_CLRNPC); - } - } - } - map_delblock(&nd->bl); - map_deliddb(&nd->bl); - //Remove npc from map[].npc list. [Skotlex] - for(i=0;i= map[m].npc_num) return 2; //failed to find it? - - map[m].npc_num--; - for(; ind->exname,npcname)==0){ - db_remove(ev_db, key); - return 1; - } - return 0; -} - -static int npc_unload_dup_sub(DBKey key,void * data,va_list ap) -{ - struct npc_data *nd = (struct npc_data *)data; - int src_id; - - if(nd->bl.type!=BL_NPC || nd->bl.subtype != SCRIPT) - return 0; - - src_id=va_arg(ap,int); - if (nd->u.scr.src_id == src_id) - npc_unload(nd); - return 0; -} -//Removes all npcs that are duplicates of the passed one. [Skotlex] -void npc_unload_duplicates (struct npc_data *nd) -{ - map_foreachiddb(npc_unload_dup_sub,nd->bl.id); -} - -int npc_unload (struct npc_data *nd) -{ - nullpo_ret(nd); - - npc_remove_map (nd); - map_deliddb(&nd->bl); - - if (nd->chat_id) { - struct chat_data *cd = (struct chat_data*)map_id2bl(nd->chat_id); - if (cd) aFree (cd); - cd = NULL; - } - if (nd->bl.subtype == SCRIPT) { - ev_db->foreach(ev_db,npc_unload_ev,nd->exname); //Clean up all events related. - if (nd->u.scr.timerid != -1) { - struct TimerData *td = NULL; - td = get_timer(nd->u.scr.timerid); - if (td && td->data) - ers_free(timer_event_ers, (struct event_timer_data*)td->data); - delete_timer(nd->u.scr.timerid, npc_timerevent); - } - npc_cleareventtimer (nd); - if (nd->u.scr.timer_event) - aFree(nd->u.scr.timer_event); - if (nd->u.scr.src_id == 0) { - if(nd->u.scr.script) { - script_free_code(nd->u.scr.script); - nd->u.scr.script = NULL; - } - if (nd->u.scr.label_list) { - aFree(nd->u.scr.label_list); - nd->u.scr.label_list = NULL; - } - } - } - aFree(nd); - - return 0; -} - -// -// 初期化関係 -// - -/*========================================== - * 読み込むnpcファイルのクリア - *------------------------------------------ - */ -void npc_clearsrcfile (void) -{ - struct npc_src_list *p = npc_src_first, *p2; - - while (p) { - p2 = p; - p = p->next; - aFree(p2); - } - npc_src_first = NULL; - npc_src_last = NULL; -} -/*========================================== - * 読み込むnpcファイルの追加 - *------------------------------------------ - */ -void npc_addsrcfile (char *name) -{ - struct npc_src_list *nsl; - - if (strcmpi(name, "clear") == 0) { - npc_clearsrcfile(); - return; - } - - // prevent multiple insert of source files - nsl = npc_src_first; - while (nsl) - { // found the file, no need to insert it again - if (0 == strcmp(name, nsl->name)) - return; - nsl = nsl->next; - } - - nsl = (struct npc_src_list *) aMalloc (sizeof(*nsl) + strlen(name)); - nsl->next = NULL; - strncpy(nsl->name, name, strlen(name) + 1); - if (npc_src_first == NULL) - npc_src_first = nsl; - if (npc_src_last) - npc_src_last->next = nsl; - npc_src_last = nsl; -} -/*========================================== - * 読み込むnpcファイルの削除 - *------------------------------------------ - */ -void npc_delsrcfile (char *name) -{ - struct npc_src_list *p = npc_src_first, *pp = NULL, **lp = &npc_src_first; - - if (strcmpi(name, "all") == 0) { - npc_clearsrcfile(); - return; - } - - while (p) { - if (strcmp(p->name, name) == 0) { - *lp = p->next; - if (npc_src_last == p) - npc_src_last = pp; - aFree(p); - break; - } - lp = &p->next; - pp = p; - p = p->next; - } -} - -/*========================================== - * warp行解析 - *------------------------------------------ - */ -int npc_parse_warp (char *w1,char *w2,char *w3,char *w4) -{ - int x, y, xs, ys, to_x, to_y, m; - int i, j; - char mapname[MAP_NAME_LENGTH], to_mapname[MAP_NAME_LENGTH]; - struct npc_data *nd; - - // 引数の個数チェック - if (sscanf(w1, "%15[^,],%d,%d", mapname, &x, &y) != 3 || - sscanf(w4, "%d,%d,%15[^,],%d,%d", &xs, &ys, to_mapname, &to_x, &to_y) != 5) { - ShowError("bad warp line : %s\n", w3); - return 1; - } - - m = map_mapname2mapid(mapname); - i = mapindex_name2id(to_mapname); - if (!i) { - ShowError("bad warp line (destination map not found): %s\n", w3); - return 1; - } - - nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data)); - - nd->bl.id = npc_get_new_npc_id(); - nd->n = map_addnpc(m, nd); - nd->bl.prev = nd->bl.next = NULL; - nd->bl.m = m; - nd->bl.x = x; - nd->bl.y = y; - memcpy(nd->name, w3, NAME_LENGTH-1); - memcpy(nd->exname, w3, NAME_LENGTH-1); - - if (!battle_config.warp_point_debug) - nd->class_ = WARP_CLASS; - else - nd->class_ = WARP_DEBUG_CLASS; - nd->speed = 200; - - nd->u.warp.mapindex = (short)i; - xs += 2; - ys += 2; - nd->u.warp.x = to_x; - nd->u.warp.y = to_y; - nd->u.warp.xs = xs; - nd->u.warp.ys = ys; - - for (i = 0; i < ys; i++) { - for (j = 0; j < xs; j++) { - if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNOPASS)) - continue; - map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_SETNPC); - } - } - - npc_warp++; - nd->bl.type = BL_NPC; - nd->bl.subtype = WARP; - map_addblock(&nd->bl); - status_set_viewdata(&nd->bl, nd->class_); - status_change_init(&nd->bl); - unit_dataset(&nd->bl); - clif_spawn(&nd->bl); - strdb_put(npcname_db, nd->name, nd); - - return 0; -} - -/*========================================== - * shop行解析 - *------------------------------------------ - */ -static int npc_parse_shop (char *w1, char *w2, char *w3, char *w4) -{ - #define MAX_SHOPITEM 100 - char *p; - int x, y, dir, m, pos = 0; - char mapname[MAP_NAME_LENGTH]; - struct npc_data *nd; - - if (strcmp(w1, "-") == 0) { - x = 0; y = 0; dir = 0; m = -1; - } else { - // 引数の個数チェック - if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 || - strchr(w4, ',') == NULL) { - ShowError("bad shop line : %s\n", w3); - return 1; - } - m = map_mapname2mapid(mapname); - } - - nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data) + - sizeof(nd->u.shop_item[0]) * (MAX_SHOPITEM + 1)); - p = strchr(w4, ','); - - while (p && pos < MAX_SHOPITEM) { - int nameid, value; - struct item_data *id; - p++; - if (sscanf(p, "%d:%d", &nameid, &value) != 2) - break; - nd->u.shop_item[pos].nameid = nameid; - id = itemdb_search(nameid); - if (value < 0) - value = id->value_buy; - nd->u.shop_item[pos].value = value; - // check for bad prices that can possibly cause exploits - if (value/124. < id->value_sell/75.) { //Clened up formula to prevent overflows. - printf("\r"); //Carriage return to clear the 'loading..' line. [Skotlex] - if (value < id->value_sell) - ShowWarning ("Item %s [%d] buying price (%d) is less than selling price (%d) at %s\n", - id->name, id->nameid, value, id->value_sell, current_file); - else - ShowWarning ("Item %s [%d] discounted buying price (%d) is less than overcharged selling price (%d) at %s\n", - id->name, id->nameid, value/100*75, id->value_sell/100*124, current_file); - } - //for logs filters, atcommands and iteminfo script command - if (id->maxchance<=0) - id->maxchance = 10000; //10000 (100% drop chance)would show that the item's sold in NPC Shop - - pos++; - p = strchr(p, ','); - } - if (pos == 0) { - aFree(nd); - return 1; - } - nd->u.shop_item[pos++].nameid = 0; - - nd->bl.prev = nd->bl.next = NULL; - nd->bl.m = m; - nd->bl.x = x; - nd->bl.y = y; - nd->bl.id = npc_get_new_npc_id(); - memcpy(nd->name, w3, NAME_LENGTH-1); - nd->name[NAME_LENGTH-1] = '\0'; - nd->class_ = m==-1?-1:atoi(w4); - nd->speed = 200; - - nd = (struct npc_data *)aRealloc(nd, - sizeof(struct npc_data) + sizeof(nd->u.shop_item[0]) * pos); - - npc_shop++; - nd->bl.type = BL_NPC; - nd->bl.subtype = SHOP; - if (m >= 0) { - nd->n = map_addnpc(m,nd); - map_addblock(&nd->bl); - status_set_viewdata(&nd->bl, nd->class_); - status_change_init(&nd->bl); - unit_dataset(&nd->bl); - nd->ud.dir = dir; - clif_spawn(&nd->bl); - } else - // we skip map_addnpc, but still add it to the list of ID's - map_addiddb(&nd->bl); - strdb_put(npcname_db, nd->name,nd); - - return 0; -} - -/*========================================== - * NPCのラベルデータコンバート - *------------------------------------------ - */ -int npc_convertlabel_db (DBKey key, void *data, va_list ap) -{ - unsigned char *lname = key.str; - int pos = (int)data; - struct npc_data *nd; - struct npc_label_list *lst; - int num; - char *p; - char c; - - nullpo_retr(0, ap); - nullpo_retr(0, nd = va_arg(ap,struct npc_data *)); - - lst = nd->u.scr.label_list; - num = nd->u.scr.label_list_num; - if (!lst) { - lst = (struct npc_label_list *) aCallocA (1, sizeof(struct npc_label_list)); - num = 0; - } else - lst = (struct npc_label_list *) aRealloc (lst, sizeof(struct npc_label_list)*(num+1)); - - // In case of labels not terminated with ':', for user defined function support - p = lname; - while(isalnum(*(unsigned char*)p) || *p == '_') { p++; } - c = *p; - *p='\0'; - - // here we check if the label fit into the buffer - if (strlen(lname) > 23) { - ShowError("npc_parse_script: label name longer than 23 chars! '%s'\n (%s)", lname, current_file); - exit(1); - } - memcpy(lst[num].name, lname, strlen(lname)+1); //including EOS - - *p = c; - lst[num].pos = pos; - nd->u.scr.label_list = lst; - nd->u.scr.label_list_num = num+1; - - return 0; -} - -/*========================================== - * script行解析 - *------------------------------------------ - */ -static void npc_parse_script_line(unsigned char *p,int *curly_count,int line) { - int i = strlen((char *)p),j; - int string_flag = 0; - static int comment_flag = 0; - for(j = 0; j < i ; j++) { - if(comment_flag) { - if(p[j] == '*' && p[j+1] == '/') { - // マルチラインコメント終了 - j++; - (*curly_count)--; - comment_flag = 0; - } - } else if(string_flag) { - if(p[j] == '"') { - string_flag = 0; - } else if(p[j] == '\\' && p[j-1]<=0x7e) { - // エスケープ - j++; - } - } else { - if(p[j] == '"') { - string_flag = 1; - } else if(p[j] == '}') { - if(*curly_count == 0) { - break; - } else { - (*curly_count)--; - } - } else if(p[j] == '{') { - (*curly_count)++; - } else if(p[j] == '/' && p[j+1] == '/') { - // コメント - break; - } else if(p[j] == '/' && p[j+1] == '*') { - // マルチラインコメント - j++; - (*curly_count)++; - comment_flag = 1; - } - } - } - if(string_flag) { - printf("Missing '\"' at file %s line %d\n",current_file,line); - exit(1); - } -} - -// Like npc_parse_script, except it's sole use is to skip the contents of a script. [Skotlex] -static int npc_skip_script (char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines) -{ - unsigned char *srcbuf = NULL; - int srcsize = 65536; - int startline = 0; - unsigned char line[1024]; - int curly_count = 0; - - srcbuf = (unsigned char *)aMallocA(srcsize*sizeof(char)); - if (strchr(first_line, '{')) { - strcpy((char *)srcbuf, strchr(first_line, '{')); - startline = *lines; - } else - srcbuf[0] = 0; - npc_parse_script_line(srcbuf,&curly_count,*lines); - while (curly_count > 0) { - fgets ((char *)line, 1020, fp); - (*lines)++; - npc_parse_script_line(line,&curly_count,*lines); - if (feof(fp)) - break; - if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) { - srcsize += 65536; - srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize); - malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536); - } - if (srcbuf[0] != '{') { - if (strchr((char *) line,'{')) { - strcpy((char *) srcbuf, strchr((const char *) line, '{')); - startline = *lines; - } - } else - strcat((char *) srcbuf, (const char *) line); - } - if(curly_count > 0) - ShowError("Missing right curly at file %s, line %d\n",current_file, *lines); - aFree(srcbuf); - return 0; -} - -static int npc_parse_script(char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines,const char* file) -{ - int x, y, dir = 0, m, xs = 0, ys = 0, class_ = 0; // [Valaris] thanks to fov - char mapname[MAP_NAME_LENGTH]; - unsigned char *srcbuf = NULL; - struct script_code *script; - int srcsize = 65536; - int startline = 0; - unsigned char line[1024]; - int i; - struct npc_data *nd, *dnd; - struct dbt *label_db; - char *p; - struct npc_label_list *label_dup = NULL; - int label_dupnum = 0; - int src_id = 0; - - if (strcmp(w1, "-") == 0) { - x = 0; y = 0; m = -1; - } else { - // 引数の個数チェック - if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 || - (strcmp(w2, "script") == 0 && strchr(w4,',') == NULL)) { - ShowError("bad script line (in file %s): %s\n", file, w3); - return 1; - } - m = map_mapname2mapid(mapname); - } - - if (strcmp(w2, "script") == 0){ - // parsing script with curly - int curly_count = 0; - srcbuf = (unsigned char *)aMallocA(srcsize*sizeof(char)); - if (strchr(first_line, '{')) { - strcpy((char *)srcbuf, strchr(first_line, '{')); - startline = *lines; - } else - srcbuf[0] = 0; - npc_parse_script_line(srcbuf,&curly_count,*lines); - while (curly_count > 0) { - fgets ((char *)line, 1020, fp); - (*lines)++; - npc_parse_script_line(line,&curly_count,*lines); - if (feof(fp)) - break; - if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) { - srcsize += 65536; - srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize); - malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536); - } - if (srcbuf[0] != '{') { - if (strchr((char *) line,'{')) { - strcpy((char *) srcbuf, strchr((const char *) line, '{')); - startline = *lines; - } - } else - strcat((char *) srcbuf, (const char *) line); - } - if(curly_count > 0) { - ShowError("Missing right curly at file %s, line %d\n",file, *lines); - script = NULL; - } else { - // printf("Ok line %d\n",*lines); - script = parse_script((unsigned char *) srcbuf, file, startline); - } - if (script == NULL) { - // script parse error? - aFree(srcbuf); - return 1; - } - } else { - // duplicateする - char srcname[128]; - struct npc_data *dnd; - if (sscanf(w2, "duplicate(%[^)])", srcname) != 1) { - ShowError("bad duplicate name (in %s)! : %s", file, w2); - return 0; - } - if ((dnd = npc_name2id(srcname)) == NULL) { - ShowError("bad duplicate name (in %s)! (not exist) : %s\n", file, srcname); - return 0; - } - script = dnd->u.scr.script; - label_dup = dnd->u.scr.label_list; - label_dupnum = dnd->u.scr.label_list_num; - src_id = dnd->bl.id; - - }// end of スクリプト解析 - - nd = (struct npc_data *)aCalloc(1, sizeof(struct npc_data)); - - if (sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3) { - // 接触型NPC - int i, j; - - if (xs >= 0) xs = xs * 2 + 1; - if (ys >= 0) ys = ys * 2 + 1; - - if (m >= 0) { - for (i = 0; i < ys; i++) { - for (j = 0; j < xs; j++) { - if (map_getcell(m, x - xs/2 + j, y - ys/2 + i, CELL_CHKNOPASS)) - continue; - map_setcell(m, x - xs/2 + j, y - ys/2 + i, CELL_SETNPC); - } - } - } - nd->u.scr.xs = xs; - nd->u.scr.ys = ys; - } else { - // クリック型NPC - class_ = atoi(w4); - nd->u.scr.xs = 0; - nd->u.scr.ys = 0; - } - - while ((p = strchr(w3,':'))) { - if (p[1] == ':') break; - } - if (p) { - *p = 0; - memcpy(nd->name, w3, NAME_LENGTH-1); - memcpy(nd->exname, p+2, NAME_LENGTH-1); - } else { - memcpy(nd->name, w3, NAME_LENGTH-1); - memcpy(nd->exname, w3, NAME_LENGTH-1); - } - - if((dnd = npc_name2id(nd->exname))){ - if(battle_config.etc_log) - ShowInfo("npc_parse_script: Overriding NPC '%s::%s' to '%s::%d'.. in file '%s' (Duplicated System Name - Lazy scripters >_>) \n",nd->name,nd->exname,nd->name,npc_script,file); - sprintf(nd->exname, "%d", npc_script); - } - - nd->bl.prev = nd->bl.next = NULL; - nd->bl.m = m; - nd->bl.x = x; - nd->bl.y = y; - nd->bl.id = npc_get_new_npc_id(); - nd->class_ = class_; - nd->speed = 200; - nd->u.scr.script = script; - nd->u.scr.src_id = src_id; - - npc_script++; - nd->bl.type = BL_NPC; - nd->bl.subtype = SCRIPT; - - for (i = 0; i < MAX_EVENTTIMER; i++) - nd->eventtimer[i] = -1; - if (m >= 0) { - nd->n = map_addnpc(m, nd); - status_change_init(&nd->bl); - unit_dataset(&nd->bl); - nd->ud.dir = dir; - map_addblock(&nd->bl); - // Unused. You can always use xxx::OnXXXX events. Have this removed to improve perfomance. - /*if (evflag) { // イベント型 - struct event_data *ev = (struct event_data *)aCalloc(1, sizeof(struct event_data)); - ev->nd = nd; - ev->pos = 0; - strdb_put(ev_db, nd->exname, ev); - } else { - clif_spawn(&nd->bl); - }*/ - if (class_ >= 0){ - status_set_viewdata(&nd->bl, nd->class_); - clif_spawn(&nd->bl); - } - } else { - // we skip map_addnpc, but still add it to the list of ID's - map_addiddb(&nd->bl); - } - strdb_put(npcname_db, nd->exname, nd); - - //----------------------------------------- - // ラベルデータの準備 - if (srcbuf){ - // script本体がある場合の処理 - // ラベルデータのコンバート - label_db = script_get_label_db(); - label_db->foreach(label_db, npc_convertlabel_db, nd); - - // もう使わないのでバッファ解放 - aFree(srcbuf); - } else { - // duplicate - nd->u.scr.label_list = label_dup; // ラベルデータ共有 - nd->u.scr.label_list_num = label_dupnum; - } - - //----------------------------------------- - // イベント用ラベルデータのエクスポート - for (i = 0; i < nd->u.scr.label_list_num; i++){ - char *lname = nd->u.scr.label_list[i].name; - int pos = nd->u.scr.label_list[i].pos; - - if ((lname[0] == 'O' || lname[0] == 'o') && (lname[1] == 'N' || lname[1] == 'n')) { - // this check is useless here because the buffer is only 24 chars - // and already overwritten if this is here is reached - // I leave the check anyway but place it correctly to npc_convertlabel_db - if (strlen(lname)>NAME_LENGTH-1) { - ShowError("npc_parse_script: label name longer than %d chars! '%s' (%s)\n", NAME_LENGTH-1, lname, file); - exit(1); - } else { - struct event_data *ev; - unsigned char buf[51]; - // 51 comes from: 24 for npc name + 24 for label + 2 for a "::" and 1 for EOS - sprintf(buf,"%s::%s",nd->exname,lname); - - // remember the label is max 50 chars + eos; see the strdb_init below - // generate the data and insert it - ev=(struct event_data *)aMalloc(sizeof(struct event_data)); - ev->nd=nd; - ev->pos=pos; - if (strdb_put(ev_db,buf,ev) != NULL) //There was already another event of the same name? - ShowWarning("npc_parse_script : duplicate event %s (%s)\n",buf, file); - } - } - } - - //----------------------------------------- - // ラベルデータからタイマーイベント取り込み - for (i = 0; i < nd->u.scr.label_list_num; i++){ - int t = 0, k = 0; - char *lname = nd->u.scr.label_list[i].name; - int pos = nd->u.scr.label_list[i].pos; - if (sscanf(lname, "OnTimer%d%n", &t, &k) == 1 && lname[k] == '\0') { - // タイマーイベント - struct npc_timerevent_list *te = nd->u.scr.timer_event; - int j, k = nd->u.scr.timeramount; - if (te == NULL) - te = (struct npc_timerevent_list *)aMallocA(sizeof(struct npc_timerevent_list)); - else - te = (struct npc_timerevent_list *)aRealloc( te, sizeof(struct npc_timerevent_list) * (k+1) ); - for (j = 0; j < k; j++){ - if (te[j].timer > t){ - memmove(te+j+1, te+j, sizeof(struct npc_timerevent_list)*(k-j)); - break; - } - } - te[j].timer = t; - te[j].pos = pos; - nd->u.scr.timer_event = te; - nd->u.scr.timeramount++; - } - } - nd->u.scr.timerid = -1; - - return 0; -} - -/*========================================== - * function行解析 - *------------------------------------------ - */ -static int npc_parse_function (char *w1, char *w2, char *w3, char *w4, char *first_line, FILE *fp, int *lines,const char* file) -{ - unsigned char *srcbuf, *p; - struct script_code *script; - struct script_code *oldscript; - int srcsize = 65536; - int startline = 0; - char line[1024]; - int curly_count = 0; - struct dbt *user_db; - - // スクリプトの解析 - srcbuf = (unsigned char *) aMallocA (srcsize*sizeof(char)); - if (strchr(first_line,'{')) { - strcpy(srcbuf, strchr(first_line,'{')); - startline = *lines; - } else - srcbuf[0] = 0; - npc_parse_script_line(srcbuf,&curly_count,*lines); - - while (curly_count > 0) { - fgets(line, sizeof(line) - 1, fp); - (*lines)++; - npc_parse_script_line(line,&curly_count,*lines); - if (feof(fp)) - break; - if (strlen(srcbuf)+strlen(line)+1 >= (unsigned int)srcsize) { - srcsize += 65536; - srcbuf = (char *)aRealloc(srcbuf, srcsize); - malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536); - } - if (srcbuf[0]!='{') { - if (strchr(line,'{')) { - strcpy(srcbuf, strchr(line,'{')); - startline = *lines; - } - } else - strcat(srcbuf,line); - } - if(curly_count > 0) { - ShowError("Missing right curly at file %s, line %d\n",file, *lines); - script = NULL; - } else { - script = parse_script(srcbuf, file, startline); - } - if (script == NULL) { - // script parse error? - aFree(srcbuf); - return 1; - } - - p = (char *) aMallocA (50*sizeof(char)); - strncpy(p, w3, 50); - - user_db = script_get_userfunc_db(); - oldscript = (struct script_code *)strdb_get(user_db, p); - if(oldscript != NULL) { - printf("\r"); //Carriage return to clear the 'loading..' line. [Skotlex] - ShowInfo("parse_function: Overwriting user function [%s] (%s:%d)\n", p, file, *lines); - script_free_code(oldscript); - user_db->remove(user_db,str2key(p)); - strdb_put(user_db, p, script); - } else - strdb_put(user_db, p, script); - - // もう使わないのでバッファ解放 - aFree(srcbuf); - -// printf("function %s => %p\n",p,script); - - return 0; -} - - -/*========================================== - * Parse Mob 1 - Parse mob list into each map - * Parse Mob 2 - Actually Spawns Mob - * [Wizputer] - * If cached =1, it is a dynamic cached mob - * index points to the index in the mob_list of the map_data cache. - * -1 indicates that it is not stored on the map. - *------------------------------------------ - */ -int npc_parse_mob2 (struct spawn_data *mob, int index) -{ - int i; - struct mob_data *md; - - for (i = 0; i < mob->num; i++) { - md = mob_spawn_dataset(mob); - md->spawn = mob; - md->spawn_n = index; - md->special_state.cached = (index>=0); //If mob is cached on map, it is dynamically removed - mob_spawn(md); - } - - return 1; -} - -int npc_parse_mob (char *w1, char *w2, char *w3, char *w4) -{ - int level, num, class_, mode, x,y,xs,ys; - char mapname[MAP_NAME_LENGTH]; - char mobname[NAME_LENGTH]; - struct spawn_data mob, *data; - - malloc_set(&mob, 0, sizeof(struct spawn_data)); - - // 引数の個数チェック - if (sscanf(w1, "%15[^,],%d,%d,%d,%d", mapname, &x, &y, &xs, &ys) < 3 || - sscanf(w4, "%d,%d,%u,%u,%49[^\r\n]", &class_, &num, &mob.delay1, &mob.delay2, mob.eventname) < 2 ) { - ShowError("bad monster line : %s %s %s (file %s)\n", w1, w3, w4, current_file); - return 1; - } - if (!mapindex_name2id(mapname)) { - ShowError("wrong map name : %s %s (file %s)\n", w1,w3, current_file); - return 1; - } - mode = map_mapname2mapid(mapname); - if (mode < 0) //Not loaded on this map-server instance. - return 1; - mob.m = (unsigned short)mode; - - if (x < 0 || map[mob.m].xs <= x || y < 0 || map[mob.m].ys <= y) { - ShowError("Out of range spawn coordinates: %s (%d,%d), map size is (%d,%d) - %s %s (file %s)\n", map[mob.m].name, x, y, map[mob.m].xs-1, map[mob.m].ys-1, w1,w3, current_file); - return 1; - } - - // check monster ID if exists! - if (mobdb_checkid(class_)==0) { - ShowError("bad monster ID : %s %s (file %s)\n", w3, w4, current_file); - return 1; - } - - if (num < 1 || num>1000 ) { - ShowError("wrong number of monsters : %s %s (file %s)\n", w3, w4, current_file); - return 1; - } - - mob.num = (unsigned short)num; - mob.class_ = (short) class_; - mob.x = (unsigned short)x; - mob.y = (unsigned short)y; - mob.xs = (signed short)xs; - mob.ys = (signed short)ys; - - if (mob.num > 1 && battle_config.mob_count_rate != 100) { - if ((mob.num = mob.num * battle_config.mob_count_rate / 100) < 1) - mob.num = 1; - } - - if (battle_config.force_random_spawn || (mob.x == 0 && mob.y == 0)) - { //Force a random spawn anywhere on the map. - mob.x = mob.y = 0; - mob.xs = mob.ys = -1; - } - - //Apply the spawn delay fix [Skotlex] - mode = mob_db(class_)->status.mode; - if (mode & MD_BOSS) { //Bosses - if (battle_config.boss_spawn_delay != 100) - { - mob.delay1 = mob.delay1*battle_config.boss_spawn_delay/100; - mob.delay2 = mob.delay2*battle_config.boss_spawn_delay/100; - } - } else if (mode&MD_PLANT) { //Plants - if (battle_config.plant_spawn_delay != 100) - { - mob.delay1 = mob.delay1*battle_config.plant_spawn_delay/100; - mob.delay2 = mob.delay2*battle_config.plant_spawn_delay/100; - } - } else if (battle_config.mob_spawn_delay != 100) - { //Normal mobs - mob.delay1 = mob.delay1*battle_config.mob_spawn_delay/100; - mob.delay2 = mob.delay2*battle_config.mob_spawn_delay/100; - } - - // parse MOB_NAME,[MOB LEVEL] - if (sscanf(w3, "%23[^,],%d", mobname, &level) > 1) - mob.level = level; - - if(mob.delay1>0xfffffff || mob.delay2>0xfffffff) { - ShowError("wrong monsters spawn delays : %s %s (file %s)\n", w3, w4, current_file); - return 1; - } - - //Use db names instead of the spawn file ones. - if(battle_config.override_mob_names==1) - strcpy(mob.name,"--en--"); - else if (battle_config.override_mob_names==2) - strcpy(mob.name,"--ja--"); - else - strncpy(mob.name, mobname, NAME_LENGTH-1); - - if (!mob_parse_dataset(&mob)) //Verify dataset. - return 1; - - //Now that all has been validated. We allocate the actual memory - //that the re-spawn data will use. - data = aMalloc(sizeof(struct spawn_data)); - memcpy(data, &mob, sizeof(struct spawn_data)); - - if( !battle_config.dynamic_mobs || mob.delay1 || mob.delay2 ) { - npc_parse_mob2(data,-1); - npc_delay_mob += mob.num; - } else { - int index = map_addmobtolist(data->m, data); - if( index >= 0 ) { - // check if target map has players - // (usually shouldn't occur when map server is just starting, - // but not the case when we do @reloadscript - if (map[mob.m].users > 0) - npc_parse_mob2(data,index); - npc_cache_mob += mob.num; - } else { - // mobcache is full - // create them as delayed with one second - mob.delay1 = 1000; - mob.delay2 = 1000; - npc_parse_mob2(data,-1); - npc_delay_mob += mob.num; - } - } - - npc_mob++; - - return 0; -} - -/*========================================== - * マップフラグ行の解析 - *------------------------------------------ - */ -static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4) -{ - int m; - char mapname[MAP_NAME_LENGTH]; - int state = 1; - - // 引数の個数チェック - if (sscanf(w1, "%15[^,]",mapname) != 1) - return 1; - - m = map_mapname2mapid(mapname); - if (m < 0) - return 1; - if (w4 && strcmpi(w4, "off") == 0) - state = 0; //Disable mapflag rather than enable it. [Skotlex] - -//マップフラグ - if (strcmpi(w3, "nosave") == 0) { - char savemap[MAP_NAME_LENGTH]; - int savex, savey; - if (state == 0) - ; //Map flag disabled. - else if (strcmp(w4, "SavePoint") == 0) { - map[m].save.map = 0; - map[m].save.x = -1; - map[m].save.y = -1; - } else if (sscanf(w4, "%15[^,],%d,%d", savemap, &savex, &savey) == 3) { - map[m].save.map = mapindex_name2id(savemap); - map[m].save.x = savex; - map[m].save.y = savey; - if (!map[m].save.map) { - ShowWarning("Specified save point map '%s' for mapflag 'nosave' not found (file %s), using 'SavePoint'.\n",savemap,current_file); - map[m].save.x = -1; - map[m].save.y = -1; - } - } - map[m].flag.nosave = state; - } - else if (strcmpi(w3,"nomemo")==0) { - map[m].flag.nomemo=state; - } - else if (strcmpi(w3,"noteleport")==0) { - map[m].flag.noteleport=state; - } - else if (strcmpi(w3,"nowarp")==0) { - map[m].flag.nowarp=state; - } - else if (strcmpi(w3,"nowarpto")==0) { - map[m].flag.nowarpto=state; - } - else if (strcmpi(w3,"noreturn")==0) { - map[m].flag.noreturn=state; - } - else if (strcmpi(w3,"monster_noteleport")==0) { - map[m].flag.monster_noteleport=state; - } - else if (strcmpi(w3,"nobranch")==0) { - map[m].flag.nobranch=state; - } - else if (strcmpi(w3,"nopenalty")==0) { - map[m].flag.noexppenalty=state; - map[m].flag.nozenypenalty=state; - } - else if (strcmpi(w3,"pvp")==0) { - map[m].flag.pvp=state; - if (state) { - map[m].flag.gvg=0; - map[m].flag.gvg=0; - map[m].flag.gvg_dungeon=0; - map[m].flag.gvg_castle=0; - } - } - else if (strcmpi(w3,"pvp_noparty")==0) { - map[m].flag.pvp_noparty=state; - } - else if (strcmpi(w3,"pvp_noguild")==0) { - map[m].flag.pvp_noguild=state; - } - else if (strcmpi(w3, "pvp_nightmaredrop") == 0) { - char drop_arg1[16], drop_arg2[16]; - int drop_id = 0, drop_type = 0, drop_per = 0; - if (sscanf(w4, "%[^,],%[^,],%d", drop_arg1, drop_arg2, &drop_per) == 3) { - int i; - if (strcmp(drop_arg1, "random") == 0) - drop_id = -1; - else if (itemdb_exists((drop_id = atoi(drop_arg1))) == NULL) - drop_id = 0; - if (strcmp(drop_arg2, "inventory") == 0) - drop_type = 1; - else if (strcmp(drop_arg2,"equip") == 0) - drop_type = 2; - else if (strcmp(drop_arg2,"all") == 0) - drop_type = 3; - - if (drop_id != 0){ - for (i = 0; i < MAX_DROP_PER_MAP; i++) { - if (map[m].drop_list[i].drop_id == 0){ - map[m].drop_list[i].drop_id = drop_id; - map[m].drop_list[i].drop_type = drop_type; - map[m].drop_list[i].drop_per = drop_per; - break; - } - } - map[m].flag.pvp_nightmaredrop = 1; - } - } else if (!state) //Disable - map[m].flag.pvp_nightmaredrop = 0; - } - else if (strcmpi(w3,"pvp_nocalcrank")==0) { - map[m].flag.pvp_nocalcrank=state; - } - else if (strcmpi(w3,"gvg")==0) { - map[m].flag.gvg=state; - if (state) map[m].flag.pvp=0; - } - else if (strcmpi(w3,"gvg_noparty")==0) { - map[m].flag.gvg_noparty=state; - } - else if (strcmpi(w3,"gvg_dungeon")==0) { - map[m].flag.gvg_dungeon=state; - if (state) map[m].flag.pvp=0; - } - else if (strcmpi(w3,"gvg_castle")==0) { - map[m].flag.gvg_castle=state; - if (state) map[m].flag.pvp=0; - } - else if (strcmpi(w3,"noexppenalty")==0) { - map[m].flag.noexppenalty=state; - } - else if (strcmpi(w3,"nozenypenalty")==0) { - map[m].flag.nozenypenalty=state; - } - else if (strcmpi(w3,"notrade")==0) { - map[m].flag.notrade=state; - } - else if (strcmpi(w3,"novending")==0) { - map[m].flag.novending=state; - } - else if (strcmpi(w3,"nodrop")==0) { - map[m].flag.nodrop=state; - } - else if (strcmpi(w3,"noskill")==0) { - map[m].flag.noskill=state; - } - else if (strcmpi(w3,"noicewall")==0) { // noicewall [Valaris] - map[m].flag.noicewall=state; - } - else if (strcmpi(w3,"snow")==0) { // snow [Valaris] - map[m].flag.snow=state; - } - else if (strcmpi(w3,"clouds")==0) { - map[m].flag.clouds=state; - } - else if (strcmpi(w3,"clouds2")==0) { // clouds2 [Valaris] - map[m].flag.clouds2=state; - } - else if (strcmpi(w3,"fog")==0) { // fog [Valaris] - map[m].flag.fog=state; - } - else if (strcmpi(w3,"fireworks")==0) { - map[m].flag.fireworks=state; - } - else if (strcmpi(w3,"sakura")==0) { // sakura [Valaris] - map[m].flag.sakura=state; - } - else if (strcmpi(w3,"leaves")==0) { // leaves [Valaris] - map[m].flag.leaves=state; - } - else if (strcmpi(w3,"rain")==0) { // rain [Valaris] - map[m].flag.rain=state; - } - else if (strcmpi(w3,"indoors")==0) { // celest - map[m].flag.indoors=state; - } - else if (strcmpi(w3,"nightenabled")==0) { // Skotlex - map[m].flag.nightenabled=state; - } - else if (strcmpi(w3,"nogo")==0) { // celest - map[m].flag.nogo=state; - } - else if (strcmpi(w3,"noexp")==0) { // Lorky - map[m].flag.nobaseexp=state; - map[m].flag.nojobexp=state; - } - else if (strcmpi(w3,"nobaseexp")==0) { // Lorky - map[m].flag.nobaseexp=state; - } - else if (strcmpi(w3,"nojobexp")==0) { // Lorky - map[m].flag.nojobexp=state; - } - else if (strcmpi(w3,"noloot")==0) { // Lorky - map[m].flag.nomobloot=state; - map[m].flag.nomvploot=state; - } - else if (strcmpi(w3,"nomobloot")==0) { // Lorky - map[m].flag.nomobloot=state; - } - else if (strcmpi(w3,"nomvploot")==0) { // Lorky - map[m].flag.nomvploot=state; - } - else if (strcmpi(w3,"nocommand")==0) { // Skotlex - if (state) { - if (sscanf(w4, "%d", &state) == 1) - map[m].nocommand =state; - else //No level specified, block everyone. - map[m].nocommand =100; - } else - map[m].nocommand=0; - } - else if (strcmpi(w3,"restricted")==0) { // Komurka - if (state) { - map[m].flag.restricted=1; - sscanf(w4, "%d", &state); - map[m].zone |= 1<<(state+1); - } else { - map[m].flag.restricted=0; - map[m].zone = 0; - } - } - else if (strcmpi(w3,"jexp")==0) { - map[m].jexp = (state) ? atoi(w4) : 100; - if( map[m].jexp < 0 ) map[m].jexp = 100; - map[m].flag.nojobexp = (map[m].jexp==0)?1:0; - } - else if (strcmpi(w3,"bexp")==0) { - map[m].bexp = (state) ? atoi(w4) : 100; - if( map[m].bexp < 0 ) map[m].bexp = 100; - map[m].flag.nobaseexp = (map[m].bexp==0)?1:0; - } - else if (strcmpi(w3,"loadevent")==0) { // Skotlex - map[m].flag.loadevent=state; - } - else if (strcmpi(w3,"nochat")==0) { // Skotlex - map[m].flag.nochat=state; - } - else if (strcmpi(w3,"partylock")==0) { // Skotlex - map[m].flag.partylock=state; - } - else if (strcmpi(w3,"guildlock")==0) { // Skotlex - map[m].flag.guildlock=state; - } - - return 0; -} - -/*========================================== - * Setting up map cells - *------------------------------------------ - */ -static int npc_parse_mapcell (char *w1, char *w2, char *w3, char *w4) -{ - int m, cell, x, y, x0, y0, x1, y1; - char type[24], mapname[MAP_NAME_LENGTH]; - - if (sscanf(w1, "%15[^,]", mapname) != 1) - return 1; - - m = map_mapname2mapid(mapname); - if (m < 0) - return 1; - - if (sscanf(w3, "%23[^,],%d,%d,%d,%d", type, &x0, &y0, &x1, &y1) < 4) { - ShowError("Bad setcell line : %s\n",w3); - return 1; - } - cell = strtol(type, (char **)NULL, 0); - //printf ("0x%x %d %d %d %d\n", cell, x0, y0, x1, y1); - - if (x0 > x1) { int t = x0; x0 = x1; x1 = t; } - if (y0 > y1) { int t = y0; y0 = y1; y1 = t; } - - for (x = x0; x <= x1; x++) { - for (y = y0; y <= y1; y++) { - map_setcell(m, x, y, cell); - //printf ("setcell 0x%x %d %d %d\n", cell, m, x, y); - } - } - - return 0; -} - -void npc_parsesrcfile (char *name) -{ - int m, lines = 0; - char line[2048]; - - FILE *fp = fopen (name,"r"); - if (fp == NULL) { - ShowError ("File not found : %s\n", name); - return; - } - current_file = name; - - while (fgets(line, sizeof(line) - 1, fp)) { - char w1[2048], w2[2048], w3[2048], w4[2048], mapname[2048]; - int i, w4pos, count; - lines++; - - if (line[0] == '/' && line[1] == '/') - continue; - - if (!sscanf(line, " %n", &i) && i == strlen(line)) // just whitespace - continue; - - // 最初はタブ区切りでチェックしてみて、ダメならスペース区切りで確認 - w1[0] = w2[0] = w3[0] = w4[0] = '\0'; //It's best to initialize values - //to prevent passing previously parsed values to the parsers when not all - //fields are specified. [Skotlex] - if ((count = sscanf(line, "%[^\t\r\n]\t%[^\t\r\n]\t%[^\t\r\n]\t%n%[^\r\n]", w1, w2, w3, &w4pos, w4)) < 3) - { - if ((count = sscanf(line, "%s %s %[^\t]\t %n%[^\n]", w1, w2, w3, &w4pos, w4)) == 4 || - (count = sscanf(line, "%s %s %s %n%[^\n]\n", w1, w2, w3, &w4pos, w4)) >= 3) - { - ShowWarning("\r"); - ShowWarning("Incorrect separator syntax in file '%s', line '%i'. Use tabs instead of spaces!\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4); - } else { - ShowError("\r"); //Erase the npc spinner. - ShowError("Could not parse file '%s', line '%i'.\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4); - continue; - } - } - - // マップの存在確認 - if (strcmp(w1,"-") !=0 && strcmpi(w1,"function") != 0 ){ - sscanf(w1,"%[^,]",mapname); - if (!mapindex_name2id(mapname)) { //Incorrect map - ShowError("Invalid map '%s' in line %d, file %s\n", mapname, lines, current_file); - if (strcmpi(w2,"script") == 0 && count > 3) //we must skip the script info... - npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines); - continue; - } - if ((m = map_mapname2mapid(mapname)) < 0) { - // "mapname" is not assigned to this server - // we must skip the script info... - if (strcmpi(w2,"script") == 0 && count > 3) - npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines); - continue; - } - } - if (strcmpi(w2,"warp") == 0 && count > 3) { - npc_parse_warp(w1,w2,w3,w4); - } else if (strcmpi(w2,"shop") == 0 && count > 3) { - npc_parse_shop(w1,w2,w3,w4); - } else if (strcmpi(w2,"script") == 0 && count > 3) { - if (strcmpi(w1,"function") == 0) { - npc_parse_function(w1,w2,w3,w4,line+w4pos,fp,&lines,name); - } else { - npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines,name); - } - } else if ((i = 0, sscanf(w2,"duplicate%n",&i), (i > 0 && w2[i] == '(')) && count > 3) { - npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines,name); - } else if (strcmpi(w2,"monster") == 0 && count > 3) { - npc_parse_mob(w1,w2,w3,w4); - } else if (strcmpi(w2,"mapflag") == 0 && count >= 3) { - npc_parse_mapflag(w1,w2,w3,w4); - } else if (strcmpi(w2,"setcell") == 0 && count >= 3) { - npc_parse_mapcell(w1,w2,w3,w4); - } else { - ShowError("Probably TAB is missing in file '%s', line '%i':\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4); - } - } - fclose(fp); - - return; -} - -int npc_script_event(TBL_PC* sd, int type) { - int i; - if (type < 0 || type >= NPCE_MAX) - return 0; - if (!sd) { - if (battle_config.error_log) - ShowError("npc_script_event: NULL sd. Event Type %d\n", type); - return 0; - } - if (script_event[type].nd) { - TBL_NPC *nd = script_event[type].nd; - run_script(nd->u.scr.script,0,sd->bl.id,nd->bl.id); - return 1; - } else if (script_event[type].event_count) { - for (i = 0; i= UCHAR_MAX) return 0; - - if((p=strchr(p,':')) && p && strcmpi(name,p)==0 ) - { - event_buf[*count] = (struct event_data *)data; - event_name[*count] = key.str; - (*count)++; - return 1; - } - return 0; -} - -static void npc_read_event_script(void) -{ - int i; - unsigned char buf[64]="::"; - struct { - char *name; - char *event_name; - } config[] = { - {"Login Event",script_config.login_event_name}, - {"Logout Event",script_config.logout_event_name}, - {"Load Map Event",script_config.loadmap_event_name}, - {"Base LV Up Event",script_config.baselvup_event_name}, - {"Job LV Up Event",script_config.joblvup_event_name}, - {"Die Event",script_config.die_event_name}, - {"Kill PC Event",script_config.kill_pc_event_name}, - {"Kill NPC Event",script_config.kill_mob_event_name}, - }; - - for (i = 0; i < NPCE_MAX; i++) { - if (script_event[i].nd) - script_event[i].nd = NULL; - if (script_event[i].event_count) - script_event[i].event_count = 0; - if (!script_config.event_script_type) { - //Use a single NPC as event source. - script_event[i].nd = npc_name2id(config[i].event_name); - } else { - //Use an array of Events - strncpy(buf+2,config[i].event_name,62); - ev_db->foreach(ev_db,npc_read_event_script_sub,buf, - &script_event[i].event, - &script_event[i].event_name, - &script_event[i].event_count); - } - } - if (battle_config.etc_log) { - //Print summary. - for (i = 0; i < NPCE_MAX; i++) { - if(!script_config.event_script_type) { - if (script_event[i].nd) - ShowInfo("%s: Using NPC named '%s'.\n", config[i].name, config[i].event_name); - else - ShowInfo("%s: No NPC found with name '%s'.\n", config[i].name, config[i].event_name); - } else - ShowInfo("%s: %d '%s' events.\n", config[i].name, script_event[i].event_count, config[i].event_name); - } - } -} -static int npc_read_indoors (void) -{ - char *buf, *p; - int s, m; - - buf = (char *)grfio_reads("data\\indoorrswtable.txt",&s); - if (buf == NULL) - return -1; - buf[s] = 0; - - for (p = buf; p - buf < s; ) { - char map_name[64]; - if (sscanf(p, "%15[^#]#", map_name) == 1) { - size_t pos = strlen(map_name) - 4; // replace '.xxx' extension - memcpy(map_name+pos,".gat",4); // with '.gat' - if ((m = map_mapname2mapid(map_name)) >= 0) - map[m].flag.indoors = 1; - } - - p = strchr(p, 10); - if (!p) break; - p++; - } - aFree(buf); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\indoorrswtable.txt"); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ - -static int npc_cleanup_sub (struct block_list *bl, va_list ap) { - nullpo_retr(0, bl); - - switch(bl->type) { - case BL_NPC: - npc_unload((struct npc_data *)bl); - break; - case BL_MOB: - //This is used only on reloading npcs, so let's not free spawn-once mobs. [Skotlex] - if (((TBL_MOB*)bl)->spawn) - unit_free(bl,0); - break; - } - - return 0; -} - -static int npc_cleanup_dbsub(DBKey key,void * data,va_list ap) { - return npc_cleanup_sub((struct block_list*)data, 0); -} - -int npc_reload (void) -{ - struct npc_src_list *nsl; - int m, i; - time_t last_time = time(0); - int busy = 0, npc_new_min = npc_id; - char c = '-'; - - //Remove all npcs/mobs. [Skotlex] - map_foreachiddb(npc_cleanup_dbsub); - for (m = 0; m < map_num; m++) { - if(battle_config.dynamic_mobs) { //dynamic check by [random] - for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++) - if (map[m].moblist[i]) aFree(map[m].moblist[i]); - malloc_set (map[m].moblist, 0, sizeof(map[m].moblist)); - } - if (map[m].npc_num > 0 && battle_config.error_log) - ShowWarning("npc_reload: %d npcs weren't removed at map %s!\n", map[m].npc_num, map[m].name); - } - - // anything else we should cleanup? - // Reloading npc's now - ev_db->clear(ev_db,NULL); - npcname_db->clear(npcname_db,NULL); - npc_warp = npc_shop = npc_script = 0; - npc_mob = npc_cache_mob = npc_delay_mob = 0; - - for (nsl = npc_src_first; nsl; nsl = nsl->next) { - npc_parsesrcfile(nsl->name); - if (script_config.verbose_mode) { - printf("\r"); - ShowStatus("Loading NPCs... %-53s", nsl->name); - } else { - if (last_time != time(0)) { - printf("\r"); - ShowStatus("Loading NPCs... Working: "); - last_time = time(0); - switch(busy) { - case 0: c='\\'; busy++; break; - case 1: c='|'; busy++; break; - case 2: c='/'; busy++; break; - case 3: c='-'; busy=0; - } - printf("[%c]",c); - } - } - fflush(stdout); - } - printf("\r"); - ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'" - CL_WHITE"%d"CL_RESET"' Warps\n\t-'" - CL_WHITE"%d"CL_RESET"' Shops\n\t-'" - CL_WHITE"%d"CL_RESET"' Scripts\n\t-'" - CL_WHITE"%d"CL_RESET"' Mobs\n\t-'" - CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'" - CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n", - npc_id - npc_new_min, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob); - - //Re-read the NPC Script Events cache. - npc_read_event_script(); - - //Execute the OnInit event for freshly loaded npcs. [Skotlex] - ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '" - CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit")); - // Execute rest of the startup events if connected to char-server. [Lance] - if(!CheckForCharServer()){ - ShowStatus("Event '"CL_WHITE"OnCharIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnCharIfInit")); - ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit")); - ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInitOnce")); - } - return 0; -} - -/*========================================== - * 終了 - *------------------------------------------ - */ -int do_final_npc(void) -{ - int i; - struct block_list *bl; - - for (i = START_NPC_NUM; i < npc_id; i++){ - if ((bl = map_id2bl(i))){ - if (bl->type == BL_NPC) - npc_unload((struct npc_data *)bl); - else if (bl->type&(BL_MOB|BL_PET)) - unit_free(bl, 0); - } - } - - ev_db->destroy(ev_db, NULL); - //There is no free function for npcname_db because at this point there shouldn't be any npcs left! - //So if there is anything remaining, let the memory manager catch it and report it. - npcname_db->destroy(npcname_db, NULL); - ers_destroy(timer_event_ers); - npc_clearsrcfile(); - - return 0; -} - -static void npc_debug_warps_sub(struct npc_data *nd) -{ - int m; - if (nd->bl.type != BL_NPC || nd->bl.subtype != WARP || nd->bl.m < 0) - return; - - m = map_mapindex2mapid(nd->u.warp.mapindex); - if (m < 0) return; //Warps to another map, nothing to do about it. - - if (map_getcell(m, nd->u.warp.x, nd->u.warp.y, CELL_CHKNPC)) { - ShowWarning("Warp %s at %s(%d,%d) warps directly on top of an area npc at %s(%d,%d)\n", - nd->name, - map[nd->bl.m].name, nd->bl.x, nd->bl.y, - map[m].name, nd->u.warp.x, nd->u.warp.y - ); - } - if (map_getcell(m, nd->u.warp.x, nd->u.warp.y, CELL_CHKNOPASS)) { - ShowWarning("Warp %s at %s(%d,%d) warps to a non-walkable tile at %s(%d,%d)\n", - nd->name, - map[nd->bl.m].name, nd->bl.x, nd->bl.y, - map[m].name, nd->u.warp.x, nd->u.warp.y - ); - } -} - -static void npc_debug_warps(void) -{ - int m, i; - for (m = 0; m < map_num; m++) - for (i = 0; i < map[m].npc_num; i++) - npc_debug_warps_sub(map[m].npc[i]); -} - -/*========================================== - * npc初期化 - *------------------------------------------ - */ -int do_init_npc(void) -{ - struct npc_src_list *nsl; - time_t last_time = time(0); - int busy, i; - char c = '-'; - - //Stock view data for normal npcs. - malloc_set(&npc_viewdb, 0, sizeof(npc_viewdb)); - npc_viewdb[0].class_ = INVISIBLE_CLASS; //Invisible class is stored here. - for (busy = 1; busy < MAX_NPC_CLASS; busy++) - npc_viewdb[busy].class_ = busy; - busy = 0; - // indoorrswtable.txt and etcinfo.txt [Celest] - if (battle_config.indoors_override_grffile) - npc_read_indoors(); - - // comparing only the first 24 chars of labels that are 50 chars long isn't that nice - // will cause "duplicated" labels where actually no dup is... - ev_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA,51); - npcname_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_BASE,NAME_LENGTH); - - malloc_set(&ev_tm_b, -1, sizeof(ev_tm_b)); - timer_event_ers = ers_new((uint32)sizeof(struct timer_event_data)); - - for (nsl = npc_src_first; nsl; nsl = nsl->next) { - npc_parsesrcfile(nsl->name); - current_file = NULL; - printf("\r"); - if (script_config.verbose_mode) - ShowStatus ("Loading NPCs... %-53s", nsl->name); - else { - ShowStatus("Loading NPCs... Working: "); - if (last_time != time(0)) { - last_time = time(0); - switch(busy) { - case 0: c='\\'; busy++; break; - case 1: c='|'; busy++; break; - case 2: c='/'; busy++; break; - case 3: c='-'; busy=0; - } - } - printf("[%c]",c); - } - fflush(stdout); - } - printf("\r"); - ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'" - CL_WHITE"%d"CL_RESET"' Warps\n\t-'" - CL_WHITE"%d"CL_RESET"' Shops\n\t-'" - CL_WHITE"%d"CL_RESET"' Scripts\n\t-'" - CL_WHITE"%d"CL_RESET"' Mobs\n\t-'" - CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'" - CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n", - npc_id - START_NPC_NUM, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob); - - malloc_set(script_event, 0, sizeof(script_event)); - npc_read_event_script(); - //Debug function to locate all endless loop warps. - if (battle_config.warp_point_debug) - npc_debug_warps(); - - add_timer_func_list(npc_event_timer,"npc_event_timer"); - add_timer_func_list(npc_event_do_clock,"npc_event_do_clock"); - add_timer_func_list(npc_timerevent,"npc_timerevent"); - - // Init dummy NPC - fake_nd = (struct npc_data *)aCalloc(sizeof(struct npc_data),1); - fake_nd->bl.prev = fake_nd->bl.next = NULL; - fake_nd->bl.m = -1; - fake_nd->bl.x = 0; - fake_nd->bl.y = 0; - fake_nd->bl.id = npc_get_new_npc_id(); - fake_nd->class_ = -1; - fake_nd->speed = 200; - fake_nd->u.scr.script = NULL; - fake_nd->u.scr.src_id = 0; - fake_nd->chatdb = NULL; - for (i = 0; i < MAX_EVENTTIMER; i++) - fake_nd->eventtimer[i] = -1; - strcpy(fake_nd->name,"FAKE_NPC"); - memcpy(fake_nd->exname, fake_nd->name, 9); - - npc_script++; - fake_nd->bl.type = BL_NPC; - fake_nd->bl.subtype = SCRIPT; - - strdb_put(npcname_db, fake_nd->exname, fake_nd); - fake_nd->u.scr.timerid = -1; - map_addiddb(&fake_nd->bl); - // End of initialization - - return 0; -} -// [Lance] -int npc_changename(const char *name, const char *newname, short look){ - struct npc_data *nd= (struct npc_data *) strdb_remove(npcname_db,(unsigned char*)name); - if (nd==NULL) - return 0; - npc_enable(name,0); - strcpy(nd->name,newname); - nd->class_ = look; - npc_enable(newname,1); - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include +#include +#include +#include + +#include "../common/timer.h" +#include "../common/nullpo.h" +#include "../common/malloc.h" +#include "../common/grfio.h" +#include "../common/showmsg.h" +#include "../common/ers.h" +#include "../common/db.h" +#include "map.h" +#include "log.h" +#include "npc.h" +#include "clif.h" +#include "intif.h" +#include "pc.h" +#include "status.h" +#include "itemdb.h" +#include "script.h" +#include "mob.h" +#include "pet.h" +#include "battle.h" +#include "skill.h" +#include "unit.h" + +#ifdef _WIN32 +#undef isspace +#define isspace(x) (x == ' ' || x == '\t') +#endif + +struct npc_src_list { + struct npc_src_list * next; +// struct npc_src_list * prev; //[Shinomori] + char name[4]; +}; + +static struct npc_src_list *npc_src_first=NULL; +static struct npc_src_list *npc_src_last=NULL; +static int npc_id=START_NPC_NUM; +static int npc_warp=0; +static int npc_shop=0; +static int npc_script=0; +static int npc_mob=0; +static int npc_delay_mob=0; +static int npc_cache_mob=0; +char *current_file = NULL; +int npc_get_new_npc_id(void){ return npc_id++; } + +static struct dbt *ev_db; +static struct dbt *npcname_db; + +struct event_data { + struct npc_data *nd; + int pos; +}; +static struct tm ev_tm_b; // 時計イベント用 + +static struct eri *timer_event_ers; //For the npc timer data. [Skotlex] + +//For holding the view data of npc classes. [Skotlex] +static struct view_data npc_viewdb[MAX_NPC_CLASS]; + +static struct +{ //Holds pointers to the commonly executed scripts for speedup. [Skotlex] + struct npc_data *nd; + struct event_data *event[UCHAR_MAX]; + unsigned char *event_name[UCHAR_MAX]; + unsigned char event_count; +} script_event[NPCE_MAX]; + +struct view_data* npc_get_viewdata(int class_) +{ //Returns the viewdata for normal npc classes. + if (class_ == INVISIBLE_CLASS) + return &npc_viewdb[0]; + if (npcdb_checkid(class_) || class_ == WARP_CLASS) + return &npc_viewdb[class_]; + return NULL; +} +/*========================================== + * NPCの無効化/有効化 + * npc_enable + * npc_enable_sub 有効時にOnTouchイベントを実行 + *------------------------------------------ + */ +int npc_enable_sub( struct block_list *bl, va_list ap ) +{ + struct map_session_data *sd; + struct npc_data *nd; + //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori] + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, nd=va_arg(ap,struct npc_data *)); + if(bl->type == BL_PC && (sd=(struct map_session_data *)bl)){ + char name[50]; // need 24 + 9 for the "::OnTouch" + + if (nd->sc.option&OPTION_INVISIBLE) // 無効化されている + return 1; + + if(sd->areanpc_id==nd->bl.id) + return 1; + sd->areanpc_id=nd->bl.id; + + snprintf(name, 50, "%s::OnTouch", nd->exname); // exname to be specific. exname is the unique identifier for script events. [Lance] + npc_event(sd,name,0); + } + //aFree(name); + return 0; +} +int npc_enable(const char *name,int flag) +{ + struct npc_data *nd= strdb_get(npcname_db,(unsigned char*)name); + if (nd==NULL) + return 0; + + if (flag&1) + nd->sc.option&=~OPTION_INVISIBLE; + else if (flag&2) + nd->sc.option&=~OPTION_HIDE; + else if (flag&4) + nd->sc.option|= OPTION_HIDE; + else //Can't change the view_data to invisible class because the view_data for all npcs is shared! [Skotlex] + nd->sc.option|= OPTION_INVISIBLE; + + if (nd->class_ == WARP_CLASS) + { //Client won't display option changes for warp portals [Toms] + if (nd->sc.option&(OPTION_HIDE|OPTION_INVISIBLE)) + clif_clearchar(&nd->bl, 0); + else + clif_spawn(&nd->bl); + } else + clif_changeoption(&nd->bl); + + if(flag&3 && (nd->u.scr.xs > 0 || nd->u.scr.ys >0)) + map_foreachinarea( npc_enable_sub,nd->bl.m,nd->bl.x-nd->u.scr.xs,nd->bl.y-nd->u.scr.ys,nd->bl.x+nd->u.scr.xs,nd->bl.y+nd->u.scr.ys,BL_PC,nd); + + return 0; +} + +/*========================================== + * NPCを名前で探す + *------------------------------------------ + */ +struct npc_data* npc_name2id(const char *name) +{ + return (struct npc_data *) strdb_get(npcname_db,(unsigned char*)name); +} + +/*========================================== + * イベントキューのイベント処理 + *------------------------------------------ + */ +int npc_event_dequeue(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + if (!sd->eventqueue[0][0]) + return 0; //Nothing to dequeue + + if (!pc_addeventtimer(sd,100,sd->eventqueue[0])) + { //Failed to dequeue, couldn't set a timer. + ShowWarning("npc_event_dequeue: event timer is full !\n"); + return 0; + } + //Event dequeued successfully, shift other elements. + sd->npc_id=0; //FIXME: Shouldn't dequeueing fail when you have an npc_id set? + memmove(sd->eventqueue[0], sd->eventqueue[1], (MAX_EVENTQUEUE-1)*sizeof(sd->eventqueue[0])); + sd->eventqueue[MAX_EVENTQUEUE-1][0]=0; + return 1; +} + +/*========================================== + * イベントの遅延実行 + *------------------------------------------ + */ +int npc_event_timer(int tid,unsigned int tick,int id,int data) +{ + unsigned char *eventname = (unsigned char *)data; + struct event_data *ev = strdb_get(ev_db,eventname); + struct npc_data *nd; + struct map_session_data *sd=map_id2sd(id); + size_t i; + + if((ev==NULL || (nd=ev->nd)==NULL)) + { + if(battle_config.error_log) + ShowWarning("npc_event: event not found [%s]\n",eventname); + } + else + { + for(i=0;ieventtimer[i]==tid ) { + nd->eventtimer[i]=-1; + npc_event(sd,eventname,0); // sd NULL check is within + break; + } + } + if(i==MAX_EVENTTIMER && battle_config.error_log) + ShowWarning("npc_event_timer: event timer not found [%s]!\n",eventname); + } + + aFree(eventname); + return 0; +} + +int npc_timer_event(const unsigned char *eventname) // Added by RoVeRT +{ + struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname); + struct npc_data *nd; +// int xs,ys; + + if((ev==NULL || (nd=ev->nd)==NULL)){ + ShowWarning("npc_timer_event: event not found [%s]\n",eventname); + return 0; + } + + run_script(nd->u.scr.script,ev->pos,nd->bl.id,nd->bl.id); + + return 0; +} +/* +int npc_timer_sub_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT +{ + char *p=(char *)key; + struct event_data *ev=(struct event_data *)data; + int *c=va_arg(ap,int *); + int tick=0,ctick=gettick(); + char temp[10]; + char event[100]; + + if(ev->nd->bl.id==(int)*c && (p=strchr(p,':')) && p && strncasecmp("::OnTimer",p,8)==0 ){ + sscanf(&p[9],"%s",temp); + tick=atoi(temp); + + strcpy( event, ev->nd->name); + strcat( event, p); + + if (ctick >= ev->nd->lastaction && ctick - ev->nd->timer >= tick) { + npc_timer_event(event); + ev->nd->lastaction = ctick; + } + } + return 0; +} + +int npc_timer_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT +{ + struct npc_data *nd=(struct npc_data*)data; + + if(nd->timer == -1) + return 0; + + sv_db->foreach(ev_db,npc_timer_sub_sub,&nd->bl.id); + + return 0; +} + +int npc_timer(int tid,unsigned int tick,int id,int data) // Added by RoVeRT +{ + npcname_db->foreach(npcname_db,npc_timer_sub); + + aFree((void*)data); + return 0; +}*/ +/*========================================== + * イベント用ラベルのエクスポート + * npc_parse_script->strdb_foreachから呼ばれる + *------------------------------------------ + */ +int npc_event_export(char *lname,void *data,va_list ap) +{ + int pos=(int)data; + struct npc_data *nd=va_arg(ap,struct npc_data *); + + if ((lname[0]=='O' || lname[0]=='o')&&(lname[1]=='N' || lname[1]=='n')) { + struct event_data *ev; + unsigned char buf[51]; + char *p=strchr(lname,':'); + // エクスポートされる + ev=(struct event_data *) aMalloc(sizeof(struct event_data)); + if (ev==NULL) { + ShowFatalError("npc_event_export: out of memory !\n"); + exit(1); + }else if (p==NULL || (p-lname)>NAME_LENGTH) { + ShowFatalError("npc_event_export: label name error !\n"); + exit(1); + }else{ + ev->nd=nd; + ev->pos=pos; + *p='\0'; + sprintf(buf,"%s::%s",nd->exname,lname); + *p=':'; + strdb_put(ev_db,buf,ev); + } + } + return 0; +} + +int npc_event_sub(struct map_session_data *, struct event_data *, const unsigned char *); //[Lance] +/*========================================== + * 全てのNPCのOn*イベント実行 + *------------------------------------------ + */ +int npc_event_doall_sub(DBKey key,void *data,va_list ap) +{ + unsigned char*p = key.str; + struct event_data *ev; + int *c; + int rid; + unsigned char *name; + + ev=(struct event_data *)data; + c=va_arg(ap,int *); + name=va_arg(ap,unsigned char *); + rid=va_arg(ap, int); + + if( (p=strchr(p,':')) && p && strcmpi(name,p)==0 ){ + if(rid) + npc_event_sub(((struct map_session_data *)map_id2bl(rid)),ev,key.str); + else + run_script(ev->nd->u.scr.script,ev->pos,rid,ev->nd->bl.id); + (*c)++; + } + + return 0; +} +int npc_event_doall(const unsigned char *name) +{ + int c=0; + unsigned char buf[64]="::"; + + strncpy(buf+2,name,62); + ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,0); + return c; +} +int npc_event_doall_id(const unsigned char *name, int rid) +{ + int c=0; + unsigned char buf[64]="::"; + + strncpy(buf+2,name,62); + ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,rid); + return c; +} + +int npc_event_do_sub(DBKey key,void *data,va_list ap) +{ + unsigned char *p = key.str; + struct event_data *ev; + int *c; + const unsigned char *name; + + nullpo_retr(0, ev=(struct event_data *)data); + nullpo_retr(0, ap); + nullpo_retr(0, c=va_arg(ap,int *)); + + name=va_arg(ap,const unsigned char *); + + if (p && strcmpi(name,p)==0 ) { + run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id); + (*c)++; + } + + return 0; +} +int npc_event_do(const unsigned char *name) +{ + int c=0; + + if (*name==':' && name[1]==':') { + return npc_event_doall(name+2); + } + + ev_db->foreach(ev_db,npc_event_do_sub,&c,name); + return c; +} + +/*========================================== + * 時計イベント実行 + *------------------------------------------ + */ +int npc_event_do_clock(int tid,unsigned int tick,int id,int data) +{ + time_t timer; + struct tm *t; + char buf[64]; + char *day=""; + int c=0; + + time(&timer); + t=localtime(&timer); + + switch (t->tm_wday) { + case 0: day = "Sun"; break; + case 1: day = "Mon"; break; + case 2: day = "Tue"; break; + case 3: day = "Wed"; break; + case 4: day = "Thu"; break; + case 5: day = "Fri"; break; + case 6: day = "Sat"; break; + } + + if (t->tm_min != ev_tm_b.tm_min ) { + sprintf(buf,"OnMinute%02d",t->tm_min); + c+=npc_event_doall(buf); + sprintf(buf,"OnClock%02d%02d",t->tm_hour,t->tm_min); + c+=npc_event_doall(buf); + sprintf(buf,"On%s%02d%02d",day,t->tm_hour,t->tm_min); + c+=npc_event_doall(buf); + } + if (t->tm_hour!= ev_tm_b.tm_hour) { + sprintf(buf,"OnHour%02d",t->tm_hour); + c+=npc_event_doall(buf); + } + if (t->tm_mday!= ev_tm_b.tm_mday) { + sprintf(buf,"OnDay%02d%02d",t->tm_mon+1,t->tm_mday); + c+=npc_event_doall(buf); + } + memcpy(&ev_tm_b,t,sizeof(ev_tm_b)); + return c; +} +/*========================================== + * OnInitイベント実行(&時計イベント開始) + *------------------------------------------ + */ +int npc_event_do_oninit(void) +{ +// int c = npc_event_doall("OnInit"); + ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '" + CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit")); + + add_timer_interval(gettick()+100, + npc_event_do_clock,0,0,1000); + + return 0; +} +/*========================================== + * OnTimer NPC event - by RoVeRT + *------------------------------------------ + */ +int npc_addeventtimer(struct npc_data *nd,int tick,const char *name) +{ + int i; + unsigned char *evname; + + for(i=0;ieventtimer[i]==-1 ) + break; + if(ieventtimer[i]=add_timer(gettick()+tick, + npc_event_timer,nd->bl.id,(int)evname); + }else + ShowWarning("npc_addtimer: event timer is full !\n"); + + return 0; +} + +int npc_deleventtimer(struct npc_data *nd,const unsigned char *name) +{ + int i; + for(i=0;ieventtimer[i]!=-1 && strcmp( + (unsigned char *)(get_timer(nd->eventtimer[i])->data), name)==0 ){ + delete_timer(nd->eventtimer[i],npc_event_timer); + nd->eventtimer[i]=-1; + break; + } + + return 0; +} + +int npc_cleareventtimer(struct npc_data *nd) +{ + int i; + for(i=0;ieventtimer[i]!=-1 ){ + delete_timer(nd->eventtimer[i],npc_event_timer); + nd->eventtimer[i]=-1; + } + + return 0; +} + +int npc_do_ontimer_sub(DBKey key,void *data,va_list ap) +{ + unsigned char *p = key.str; + struct event_data *ev = (struct event_data *)data; + int *c = va_arg(ap,int *); +// struct map_session_data *sd=va_arg(ap,struct map_session_data *); + int option = va_arg(ap,int); + int tick = 0; + char temp[10]; + char event[50]; + + if(ev->nd->bl.id == (int)*c && (p = strchr(p,':')) && strnicmp("::OnTimer",p,8) == 0){ + sscanf(&p[9], "%s", temp); + tick = atoi(temp); + + strcpy(event, ev->nd->name); + strcat(event, p); + + if (option!=0) { + npc_addeventtimer(ev->nd, tick, event); + } else { + npc_deleventtimer(ev->nd, event); + } + } + return 0; +} +int npc_do_ontimer(int npc_id, int option) +{ + ev_db->foreach(ev_db, npc_do_ontimer_sub, &npc_id, option); + return 0; +} +/*========================================== + * タイマーイベント用ラベルの取り込み + * npc_parse_script->strdb_foreachから呼ばれる + *------------------------------------------ + */ +int npc_timerevent_import(char *lname,void *data,va_list ap) +{ + int pos=(int)data; + struct npc_data *nd=va_arg(ap,struct npc_data *); + int t=0,i=0; + + if(sscanf(lname,"OnTimer%d%n",&t,&i)==1 && lname[i]==':') { + // タイマーイベント + struct npc_timerevent_list *te=nd->u.scr.timer_event; + int j,i=nd->u.scr.timeramount; + if(te==NULL) te=(struct npc_timerevent_list*)aMallocA(sizeof(struct npc_timerevent_list)); + else te= (struct npc_timerevent_list*)aRealloc( te, sizeof(struct npc_timerevent_list) * (i+1) ); + if(te==NULL){ + ShowFatalError("npc_timerevent_import: out of memory !\n"); + exit(1); + } + for(j=0;jt){ + memmove(te+j+1,te+j,sizeof(struct npc_timerevent_list)*(i-j)); + break; + } + } + te[j].timer=t; + te[j].pos=pos; + nd->u.scr.timer_event=te; + nd->u.scr.timeramount++; + } + return 0; +} +struct timer_event_data { + int rid; //Attached player for this timer. + int next; //timer index (starts with 0, then goes up to nd->u.scr.timeramount + int time; //holds total time elapsed for the script since time 0 (whenthe timers started) + unsigned int otick; //Holds tick value at which timer sequence was started (that is, it stores the tick value for which T= 0 +}; + +/*========================================== + * タイマーイベント実行 + *------------------------------------------ + */ +int npc_timerevent(int tid,unsigned int tick,int id,int data) +{ + int next,t,old_rid,old_timer; + unsigned int old_tick; + struct npc_data* nd=(struct npc_data *)map_id2bl(id); + struct npc_timerevent_list *te; + struct timer_event_data *ted = (struct timer_event_data*)data; + struct map_session_data *sd=NULL; + + if( nd==NULL ){ + ShowError("npc_timerevent: NPC not found??\n"); + return 0; + } + if (ted->rid) { + sd = map_id2sd(ted->rid); + if (!sd) { + if(battle_config.error_log) + ShowError("npc_timerevent: Attached player not found.\n"); + ers_free(timer_event_ers, ted); + return 0; + } + } + old_rid = nd->u.scr.rid; //To restore it later. + nd->u.scr.rid = sd?sd->bl.id:0; + + old_tick = nd->u.scr.timertick; + nd->u.scr.timertick=ted->otick; + te=nd->u.scr.timer_event+ ted->next; + + old_timer = nd->u.scr.timer; + t = nd->u.scr.timer=ted->time; + ted->next++; + + if( nd->u.scr.timeramount> ted->next){ + next= nd->u.scr.timer_event[ ted->next ].timer + - nd->u.scr.timer_event[ ted->next-1 ].timer; + ted->time+=next; + if (sd) + sd->npc_timer_id = add_timer(tick+next,npc_timerevent,id,(int)ted); + else + nd->u.scr.timerid = add_timer(tick+next,npc_timerevent,id,(int)ted); + } else { + if (sd) + sd->npc_timer_id = -1; + else + nd->u.scr.timerid = -1; + ers_free(timer_event_ers, ted); + } + run_script(nd->u.scr.script,te->pos,nd->u.scr.rid,nd->bl.id); + //Restore previous data, only if this timer is a player-attached one. + if (sd) { + nd->u.scr.rid = old_rid; + nd->u.scr.timer = old_timer; + nd->u.scr.timertick = old_tick; + } + return 0; +} +/*========================================== + * タイマーイベント開始 + *------------------------------------------ + */ +int npc_timerevent_start(struct npc_data *nd, int rid) +{ + int j,n, next; + struct map_session_data *sd=NULL; //Player to whom script is attached. + struct timer_event_data *ted; + + nullpo_retr(0, nd); + + n=nd->u.scr.timeramount; + if( n==0 ) + return 0; + + for(j=0;ju.scr.timer_event[j].timer > nd->u.scr.timer ) + break; + } + if(j>=n) // check if there is a timer to use !!BEFORE!! you write stuff to the structures [Shinomori] + return 0; + if (nd->u.scr.rid > 0) { + //Try to attach timer to this player. + sd = map_id2sd(nd->u.scr.rid); + if (!sd) { + if(battle_config.error_log) + ShowError("npc_timerevent_start: Attached player not found!\n"); + return 1; + } + } + //Check if timer is already started. + if (sd) { + if (sd->npc_timer_id != -1) + return 0; + } else if (nd->u.scr.timerid != -1) + return 0; + + ted = ers_alloc(timer_event_ers, struct timer_event_data); + ted->next = j; + nd->u.scr.timertick=ted->otick=gettick(); + + //Attach only the player if attachplayerrid was used. + ted->rid = sd?sd->bl.id:0; + +// Do not store it to make way to two types of timers: globals and personals. +// if (rid >= 0) nd->u.scr.rid=rid; // changed to: attaching to given rid by default [Shinomori] + // if rid is less than 0 leave it unchanged [celest] + + next = nd->u.scr.timer_event[j].timer - nd->u.scr.timer; + ted->time = nd->u.scr.timer_event[j].timer; + if (sd) + sd->npc_timer_id = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(int)ted); + else + nd->u.scr.timerid = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(int)ted); + return 0; +} +/*========================================== + * タイマーイベント終了 + *------------------------------------------ + */ +int npc_timerevent_stop(struct npc_data *nd) +{ + struct map_session_data *sd =NULL; + struct TimerData *td = NULL; + int *tid; + nullpo_retr(0, nd); + if (nd->u.scr.rid) { + sd = map_id2sd(nd->u.scr.rid); + if (!sd) { + if(battle_config.error_log) + ShowError("npc_timerevent_stop: Attached player not found!\n"); + return 1; + } + } + + tid = sd?&sd->npc_timer_id:&nd->u.scr.timerid; + + if (*tid == -1) //Nothing to stop + return 0; + td = get_timer(*tid); + if (td && td->data) + ers_free(timer_event_ers, (struct event_timer_data*)td->data); + delete_timer(*tid,npc_timerevent); + *tid = -1; + //Set the timer tick to the time that has passed since the beginning of the timers and now. + nd->u.scr.timer = DIFF_TICK(gettick(),nd->u.scr.timertick); +// nd->u.scr.rid = 0; //Eh? why detach? + return 0; +} +/*========================================== + * Aborts a running npc timer that is attached to a player. + *------------------------------------------ + */ +void npc_timerevent_quit(struct map_session_data *sd) { + struct TimerData *td; + if (sd->npc_timer_id == -1) + return; + td = get_timer(sd->npc_timer_id); + if (!td) { + sd->npc_timer_id = -1; + return; //?? + } + delete_timer(sd->npc_timer_id,npc_timerevent); + sd->npc_timer_id = -1; + ers_free(timer_event_ers, (struct event_timer_data*)td->data); +} + +/*========================================== + * タイマー値の所得 + *------------------------------------------ + */ +int npc_gettimerevent_tick(struct npc_data *nd) +{ + int tick; + nullpo_retr(0, nd); + + tick=nd->u.scr.timer; + if (nd->u.scr.timertick) + tick+=DIFF_TICK(gettick(), nd->u.scr.timertick); + return tick; +} +/*========================================== + * タイマー値の設定 + *------------------------------------------ + */ +int npc_settimerevent_tick(struct npc_data *nd,int newtimer) +{ + int flag; + struct map_session_data *sd=NULL; + + nullpo_retr(0, nd); + + if (nd->u.scr.rid) { + sd = map_id2sd(nd->u.scr.rid); + if (!sd) { + if(battle_config.error_log) + ShowError("npc_settimerevent_tick: Attached player not found!\n"); + return 1; + } + flag= sd->npc_timer_id != -1 ; + } else + flag= nd->u.scr.timerid != -1 ; + + if(flag) + npc_timerevent_stop(nd); + nd->u.scr.timer=newtimer; + if(flag) + npc_timerevent_start(nd, -1); + return 0; +} + +int npc_event_sub(struct map_session_data *sd, struct event_data *ev, const unsigned char *eventname){ + + if ( sd->npc_id!=0) { + //Enqueue the event trigger. + int i; + for(i=0;ieventqueue[i][0];i++); + + if (i==MAX_EVENTQUEUE) { + if (battle_config.error_log) + ShowWarning("npc_event: event queue is full !\n"); + }else //Event enqueued. + memcpy(sd->eventqueue[i],eventname,50); + return 1; + } + if (ev->nd->sc.option&OPTION_INVISIBLE) { + //Disabled npc, shouldn't trigger event. + npc_event_dequeue(sd); + return 2; + } + run_script(ev->nd->u.scr.script,ev->pos,sd->bl.id,ev->nd->bl.id); + return 0; +} + +/*========================================== + * イベント型のNPC処理 + *------------------------------------------ + */ +int npc_event (struct map_session_data *sd, const unsigned char *eventname, int mob_kill) +{ + struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname); + struct npc_data *nd; + int xs,ys; + unsigned char mobevent[100]; + + if (sd == NULL) { + nullpo_info(NLP_MARK); + return 0; + } + + if (ev == NULL && eventname && strcmp(((eventname)+strlen(eventname)-9),"::OnTouch") == 0) + return 1; + + if (ev == NULL || (nd = ev->nd) == NULL) { + if (mob_kill) { + strcpy( mobevent, eventname); + strcat( mobevent, "::OnMyMobDead"); + ev = strdb_get(ev_db, mobevent); + if (ev == NULL || (nd = ev->nd) == NULL) { + if (strnicmp(eventname, "GM_MONSTER",10) != 0) + ShowError("npc_event: (mob_kill) event not found [%s]\n", mobevent); + return 0; + } + } else { + if (battle_config.error_log) + ShowError("npc_event: event not found [%s]\n", eventname); + return 0; + } + } + + xs=nd->u.scr.xs; + ys=nd->u.scr.ys; + if (xs>=0 && ys>=0 && (strcmp(((eventname)+strlen(eventname)-6),"Global") != 0) ) + { + if (nd->bl.m >= 0) { //Non-invisible npc + if (nd->bl.m != sd->bl.m ) + return 1; + if ( xs>0 && (sd->bl.xbl.x-xs/2 || nd->bl.x+xs/2bl.x) ) + return 1; + if ( ys>0 && (sd->bl.ybl.y-ys/2 || nd->bl.y+ys/2bl.y) ) + return 1; + } + } + + return npc_event_sub(sd,ev,eventname); +} + + +int npc_command_sub(DBKey key,void *data,va_list ap) +{ + unsigned char *p = key.str; + struct event_data *ev=(struct event_data *)data; + unsigned char *npcname=va_arg(ap,char *); + char *command=va_arg(ap,char *); + unsigned char temp[100]; + + if(strcmp(ev->nd->name,npcname)==0 && (p=strchr(p,':')) && p && strnicmp("::OnCommand",p,10)==0 ){ + sscanf(&p[11],"%s",temp); + + if (strcmp(command,temp)==0) + run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id); + } + + return 0; +} + +int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command) +{ + ev_db->foreach(ev_db,npc_command_sub,npcname,command); + + return 0; +} +/*========================================== + * 接触型のNPC処理 + *------------------------------------------ + */ +int npc_touch_areanpc(struct map_session_data *sd,int m,int x,int y) +{ + int i,f=1; + int xs,ys; + + nullpo_retr(1, sd); + + if(sd->npc_id) + return 1; + + for(i=0;isc.option&OPTION_INVISIBLE) { // 無効化されている + f=0; + continue; + } + + switch(map[m].npc[i]->bl.subtype) { + case WARP: + xs=map[m].npc[i]->u.warp.xs; + ys=map[m].npc[i]->u.warp.ys; + break; + case SCRIPT: + xs=map[m].npc[i]->u.scr.xs; + ys=map[m].npc[i]->u.scr.ys; + break; + default: + continue; + } + if (x >= map[m].npc[i]->bl.x-xs/2 && x < map[m].npc[i]->bl.x-xs/2+xs && + y >= map[m].npc[i]->bl.y-ys/2 && y < map[m].npc[i]->bl.y-ys/2+ys) + break; + } + if (i==map[m].npc_num) { + if (f) { + if (battle_config.error_log) + ShowError("npc_touch_areanpc : some bug \n"); + } + return 1; + } + switch(map[m].npc[i]->bl.subtype) { + case WARP: + // hidden chars cannot use warps -- is it the same for scripts too? + if (sd->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || + (!battle_config.duel_allow_teleport && sd->duel_group)) // duel rstrct [LuzZza] + break; + pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0); + break; + case SCRIPT: + { + //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori] + char name[50]; // need 24 max + 9 for "::OnTouch" + + if(sd->areanpc_id == map[m].npc[i]->bl.id) + return 1; + sd->areanpc_id = map[m].npc[i]->bl.id; + + sprintf(name,"%s::OnTouch", map[m].npc[i]->exname); // It goes here too. exname being the unique identifier. [Lance] + + if( npc_event(sd,name,0)>0 ) { + pc_stop_walking(sd,1); //Make it stop walking! + npc_click(sd,map[m].npc[i]); + } + //aFree(name); + break; + } + } + return 0; +} + +int npc_touch_areanpc2(struct block_list *bl) +{ + int i,m=bl->m; + int xs,ys; + + for(i=0;isc.option&OPTION_INVISIBLE) + continue; + + if (map[m].npc[i]->bl.subtype!=WARP) + continue; + + xs=map[m].npc[i]->u.warp.xs; + ys=map[m].npc[i]->u.warp.ys; + + if (bl->x >= map[m].npc[i]->bl.x-xs/2 && bl->x < map[m].npc[i]->bl.x-xs/2+xs && + bl->y >= map[m].npc[i]->bl.y-ys/2 && bl->y < map[m].npc[i]->bl.y-ys/2+ys) + break; + } + if (i==map[m].npc_num) + return 0; + + xs = map_mapindex2mapid(map[m].npc[i]->u.warp.mapindex); + if (xs < 0) // Can't warp object between map servers... + return 0; + + if (unit_warp(bl, xs, map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0)) + return 0; //Failed to warp. + + return 1; +} + +/*========================================== + * 近くかどうかの判定 + *------------------------------------------ + */ +int npc_checknear2(struct map_session_data *sd,struct block_list *bl) +{ + nullpo_retr(1, sd); + if(bl == NULL) return 1; + + if(sd->state.using_fake_npc && sd->npc_id == bl->id) + return 0; + + if (status_get_class(bl)<0) //Class-less npc, enable click from anywhere. + return 0; + + if (bl->m!=sd->bl.m || + bl->xbl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 || + bl->ybl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1) + return 1; + + return 0; +} + +TBL_NPC *npc_checknear(struct map_session_data *sd,struct block_list *bl) +{ + struct npc_data *nd; + + nullpo_retr(NULL, sd); + if(bl == NULL) return NULL; + if(bl->type != BL_NPC) return NULL; + nd = (TBL_NPC*)bl; + + if(sd->state.using_fake_npc && sd->npc_id == bl->id) + return nd; + + if (nd->class_<0) //Class-less npc, enable click from anywhere. + return nd; + + if (bl->m!=sd->bl.m || + bl->xbl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 || + bl->ybl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1) + return NULL; + + return nd; +} + +/*========================================== + * NPCのオープンチャット発言 + *------------------------------------------ + */ +int npc_globalmessage(const char *name,char *mes) +{ + struct npc_data *nd=(struct npc_data *) strdb_get(npcname_db,(unsigned char*)name); + char temp[100]; + + if (!nd) + return 0; + + snprintf(temp, sizeof temp ,"%s : %s",name,mes); + clif_GlobalMessage(&nd->bl,temp); + + return 0; +} + +/*========================================== + * クリック時のNPC処理 + *------------------------------------------ + */ +int npc_click(struct map_session_data *sd,struct npc_data *nd) +{ + nullpo_retr(1, sd); + + if (sd->npc_id != 0) { + if (battle_config.error_log) + ShowError("npc_click: npc_id != 0\n"); + return 1; + } + + if(!nd) return 1; + if ((nd = npc_checknear(sd,&nd->bl)) == NULL) + return 1; + //Hidden/Disabled npc. + if (nd->class_ < 0 || nd->sc.option&OPTION_INVISIBLE) + return 1; + + switch(nd->bl.subtype) { + case SHOP: + clif_npcbuysell(sd,nd->bl.id); + npc_event_dequeue(sd); + break; + case SCRIPT: + run_script(nd->u.scr.script,0,sd->bl.id,nd->bl.id); + break; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int npc_scriptcont(struct map_session_data *sd,int id) +{ + nullpo_retr(1, sd); + + if (id!=sd->npc_id){ + ShowWarning("npc_scriptcont: sd->npc_id (%d) is not id (%d).\n", sd->npc_id, id); + return 1; + } + + if(id != fake_nd->bl.id) { // Not item script + if ((npc_checknear(sd,map_id2bl(id))) == NULL){ + ShowWarning("npc_scriptcont: failed npc_checknear test.\n"); + return 1; + } + } + run_script_main(sd->st); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int npc_buysellsel(struct map_session_data *sd,int id,int type) +{ + struct npc_data *nd; + + nullpo_retr(1, sd); + + if ((nd = npc_checknear(sd,map_id2bl(id))) == NULL) + return 1; + + if (nd->bl.subtype!=SHOP) { + if (battle_config.error_log) + ShowError("no such shop npc : %d\n",id); + if (sd->npc_id == id) + sd->npc_id=0; + return 1; + } + if (nd->sc.option&OPTION_INVISIBLE) // 無効化されている + return 1; + + sd->npc_shopid=id; + if (type==0) { + clif_buylist(sd,nd); + } else { + clif_selllist(sd); + } + return 0; +} + +//npc_buylist for script-controlled shops. +static int npc_buylist_sub( + struct map_session_data *sd,int n, + unsigned short *item_list, struct npc_data *nd) +{ + unsigned char npc_ev[51]; + int i; + int regkey = add_str("@bought_nameid"); + int regkey2 = add_str("@bought_quantity"); + sprintf(npc_ev, "%s::OnBuyItem", nd->exname); + for(i=0;inpc_shopid))) == NULL) + return 3; + + if (nd->master_nd) //Script-based shops. + return npc_buylist_sub(sd,n,item_list,nd->master_nd); + + if (nd->bl.subtype!=SHOP) + return 3; + + for(i=0,w=0,z=0;iu.shop_item[j].nameid;j++) { + if (nd->u.shop_item[j].nameid==item_list[i*2+1] || //Normal items + itemdb_viewid(nd->u.shop_item[j].nameid)==item_list[i*2+1]) //item_avail replacement + break; + } + if (nd->u.shop_item[j].nameid==0) + return 3; + + if (!itemdb_isstackable(nd->u.shop_item[j].nameid) && item_list[i*2] > 1) + { //Exploit? You can't buy more than 1 of equipment types o.O + ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %d!\n", + sd->status.name, sd->status.account_id, sd->status.char_id, item_list[i*2], nd->u.shop_item[j].nameid); + item_list[i*2] = 1; + } + if (itemdb_value_notdc(nd->u.shop_item[j].nameid)) + z+=(double)nd->u.shop_item[j].value * item_list[i*2]; + else + z+=(double)pc_modifybuyvalue(sd,nd->u.shop_item[j].value) * item_list[i*2]; + itemamount+=item_list[i*2]; + + switch(pc_checkadditem(sd,item_list[i*2+1],item_list[i*2])) { + case ADDITEM_EXIST: + break; + case ADDITEM_NEW: + new_++; + break; + case ADDITEM_OVERAMOUNT: + return 2; + } + + w+=itemdb_weight(item_list[i*2+1]) * item_list[i*2]; + } + if (z > (double)sd->status.zeny) + return 1; // zeny不足 + if (w+sd->weight > sd->max_weight) + return 2; // 重量超過 + if (pc_inventoryblank(sd) 0 ) + log_zeny(sd, "S", sd, -(int)z); + //Logs + + pc_payzeny(sd,(int)z); + for(i=0;i 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0) { + if (sd->status.skill[MC_DISCOUNT].flag != 0) + skill = sd->status.skill[MC_DISCOUNT].flag - 2; + if (skill > 0) { + z = z * (double)skill * (double)battle_config.shop_exp/10000.; + if (z < 1) + z = 1; + pc_gainexp(sd,NULL,0,(int)z); + } + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int npc_selllist(struct map_session_data *sd,int n,unsigned short *item_list) +{ + double z; + int i,skill,itemamount=0; + struct npc_data *nd; + + nullpo_retr(1, sd); + nullpo_retr(1, item_list); + + if ((nd = npc_checknear(sd,map_id2bl(sd->npc_shopid))) == NULL) + return 1; + nd = nd->master_nd; //For OnSell triggers. + + for(i=0,z=0;i=MAX_INVENTORY || qty < 0) + break; + + nameid=sd->status.inventory[idx].nameid; + if (nameid == 0 || !sd->inventory_data[idx] || + sd->status.inventory[idx].amount < qty) + break; + + if (sd->inventory_data[idx]->flag.value_notoc) + z+=(double)qty*sd->inventory_data[idx]->value_sell; + else + z+=(double)qty*pc_modifysellvalue(sd,sd->inventory_data[idx]->value_sell); + + if(sd->inventory_data[idx]->type==7 && sd->status.inventory[idx].card[0] == (short)0xff00) + { + if(search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0) + intif_delete_petdata(MakeDWord(sd->status.inventory[idx].card[1],sd->status.inventory[idx].card[2])); + } + + if(log_config.enable_logs&0x20) //Logs items, Sold to NPC (S)hop [Lupus] + log_pick_pc(sd, "S", nameid, -qty, &sd->status.inventory[idx]); + + if(nd) { + pc_setreg(sd,add_str("@sold_nameid")+(i<<24),(int)sd->status.inventory[idx].nameid); + pc_setreg(sd,add_str("@sold_quantity")+(i<<24),qty); + } + itemamount+=qty; + pc_delitem(sd,idx,qty,0); + } + + if (z > MAX_ZENY) z = MAX_ZENY; + + if(log_config.zeny) //Logs (S)hopping Zeny [Lupus] + log_zeny(sd, "S", sd, (int)z); + + pc_getzeny(sd,(int)z); + + if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_OVERCHARGE)) > 0) { + if (sd->status.skill[MC_OVERCHARGE].flag != 0) + skill = sd->status.skill[MC_OVERCHARGE].flag - 2; + if (skill > 0) { + z = z * (double)skill * (double)battle_config.shop_exp/10000.; + if (z < 1) + z = 1; + pc_gainexp(sd,NULL,0,(int)z); + } + } + + if(nd) { + unsigned char npc_ev[51]; + sprintf(npc_ev, "%s::OnSellItem", nd->exname); + npc_event(sd, npc_ev, 0); + } + + if (ifd); + return 1; + } + return 0; +} + +int npc_remove_map (struct npc_data *nd) +{ + int m,i; + nullpo_retr(1, nd); + + if(nd->bl.prev == NULL || nd->bl.m < 0) + return 1; //Not assigned to a map. + m = nd->bl.m; +#ifdef PCRE_SUPPORT + npc_chat_finalize(nd); +#endif + clif_clearchar_area(&nd->bl,2); + strdb_remove(npcname_db, (nd->bl.subtype < SCRIPT) ? nd->name : nd->exname); + //Remove corresponding NPC CELLs + if (nd->bl.subtype == WARP) { + int j, xs, ys, x, y; + x = nd->bl.x; + y = nd->bl.y; + xs = nd->u.warp.xs; + ys = nd->u.warp.ys; + + for (i = 0; i < ys; i++) { + for (j = 0; j < xs; j++) { + if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNPC)) + map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_CLRNPC); + } + } + } + map_delblock(&nd->bl); + map_deliddb(&nd->bl); + //Remove npc from map[].npc list. [Skotlex] + for(i=0;i= map[m].npc_num) return 2; //failed to find it? + + map[m].npc_num--; + for(; ind->exname,npcname)==0){ + db_remove(ev_db, key); + return 1; + } + return 0; +} + +static int npc_unload_dup_sub(DBKey key,void * data,va_list ap) +{ + struct npc_data *nd = (struct npc_data *)data; + int src_id; + + if(nd->bl.type!=BL_NPC || nd->bl.subtype != SCRIPT) + return 0; + + src_id=va_arg(ap,int); + if (nd->u.scr.src_id == src_id) + npc_unload(nd); + return 0; +} +//Removes all npcs that are duplicates of the passed one. [Skotlex] +void npc_unload_duplicates (struct npc_data *nd) +{ + map_foreachiddb(npc_unload_dup_sub,nd->bl.id); +} + +int npc_unload (struct npc_data *nd) +{ + nullpo_ret(nd); + + npc_remove_map (nd); + map_deliddb(&nd->bl); + + if (nd->chat_id) { + struct chat_data *cd = (struct chat_data*)map_id2bl(nd->chat_id); + if (cd) aFree (cd); + cd = NULL; + } + if (nd->bl.subtype == SCRIPT) { + ev_db->foreach(ev_db,npc_unload_ev,nd->exname); //Clean up all events related. + if (nd->u.scr.timerid != -1) { + struct TimerData *td = NULL; + td = get_timer(nd->u.scr.timerid); + if (td && td->data) + ers_free(timer_event_ers, (struct event_timer_data*)td->data); + delete_timer(nd->u.scr.timerid, npc_timerevent); + } + npc_cleareventtimer (nd); + if (nd->u.scr.timer_event) + aFree(nd->u.scr.timer_event); + if (nd->u.scr.src_id == 0) { + if(nd->u.scr.script) { + script_free_code(nd->u.scr.script); + nd->u.scr.script = NULL; + } + if (nd->u.scr.label_list) { + aFree(nd->u.scr.label_list); + nd->u.scr.label_list = NULL; + } + } + } + aFree(nd); + + return 0; +} + +// +// 初期化関係 +// + +/*========================================== + * 読み込むnpcファイルのクリア + *------------------------------------------ + */ +void npc_clearsrcfile (void) +{ + struct npc_src_list *p = npc_src_first, *p2; + + while (p) { + p2 = p; + p = p->next; + aFree(p2); + } + npc_src_first = NULL; + npc_src_last = NULL; +} +/*========================================== + * 読み込むnpcファイルの追加 + *------------------------------------------ + */ +void npc_addsrcfile (char *name) +{ + struct npc_src_list *nsl; + + if (strcmpi(name, "clear") == 0) { + npc_clearsrcfile(); + return; + } + + // prevent multiple insert of source files + nsl = npc_src_first; + while (nsl) + { // found the file, no need to insert it again + if (0 == strcmp(name, nsl->name)) + return; + nsl = nsl->next; + } + + nsl = (struct npc_src_list *) aMalloc (sizeof(*nsl) + strlen(name)); + nsl->next = NULL; + strncpy(nsl->name, name, strlen(name) + 1); + if (npc_src_first == NULL) + npc_src_first = nsl; + if (npc_src_last) + npc_src_last->next = nsl; + npc_src_last = nsl; +} +/*========================================== + * 読み込むnpcファイルの削除 + *------------------------------------------ + */ +void npc_delsrcfile (char *name) +{ + struct npc_src_list *p = npc_src_first, *pp = NULL, **lp = &npc_src_first; + + if (strcmpi(name, "all") == 0) { + npc_clearsrcfile(); + return; + } + + while (p) { + if (strcmp(p->name, name) == 0) { + *lp = p->next; + if (npc_src_last == p) + npc_src_last = pp; + aFree(p); + break; + } + lp = &p->next; + pp = p; + p = p->next; + } +} + +/*========================================== + * warp行解析 + *------------------------------------------ + */ +int npc_parse_warp (char *w1,char *w2,char *w3,char *w4) +{ + int x, y, xs, ys, to_x, to_y, m; + int i, j; + char mapname[MAP_NAME_LENGTH], to_mapname[MAP_NAME_LENGTH]; + struct npc_data *nd; + + // 引数の個数チェック + if (sscanf(w1, "%15[^,],%d,%d", mapname, &x, &y) != 3 || + sscanf(w4, "%d,%d,%15[^,],%d,%d", &xs, &ys, to_mapname, &to_x, &to_y) != 5) { + ShowError("bad warp line : %s\n", w3); + return 1; + } + + m = map_mapname2mapid(mapname); + i = mapindex_name2id(to_mapname); + if (!i) { + ShowError("bad warp line (destination map not found): %s\n", w3); + return 1; + } + + nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data)); + + nd->bl.id = npc_get_new_npc_id(); + nd->n = map_addnpc(m, nd); + nd->bl.prev = nd->bl.next = NULL; + nd->bl.m = m; + nd->bl.x = x; + nd->bl.y = y; + memcpy(nd->name, w3, NAME_LENGTH-1); + memcpy(nd->exname, w3, NAME_LENGTH-1); + + if (!battle_config.warp_point_debug) + nd->class_ = WARP_CLASS; + else + nd->class_ = WARP_DEBUG_CLASS; + nd->speed = 200; + + nd->u.warp.mapindex = (short)i; + xs += 2; + ys += 2; + nd->u.warp.x = to_x; + nd->u.warp.y = to_y; + nd->u.warp.xs = xs; + nd->u.warp.ys = ys; + + for (i = 0; i < ys; i++) { + for (j = 0; j < xs; j++) { + if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNOPASS)) + continue; + map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_SETNPC); + } + } + + npc_warp++; + nd->bl.type = BL_NPC; + nd->bl.subtype = WARP; + map_addblock(&nd->bl); + status_set_viewdata(&nd->bl, nd->class_); + status_change_init(&nd->bl); + unit_dataset(&nd->bl); + clif_spawn(&nd->bl); + strdb_put(npcname_db, nd->name, nd); + + return 0; +} + +/*========================================== + * shop行解析 + *------------------------------------------ + */ +static int npc_parse_shop (char *w1, char *w2, char *w3, char *w4) +{ + #define MAX_SHOPITEM 100 + char *p; + int x, y, dir, m, pos = 0; + char mapname[MAP_NAME_LENGTH]; + struct npc_data *nd; + + if (strcmp(w1, "-") == 0) { + x = 0; y = 0; dir = 0; m = -1; + } else { + // 引数の個数チェック + if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 || + strchr(w4, ',') == NULL) { + ShowError("bad shop line : %s\n", w3); + return 1; + } + m = map_mapname2mapid(mapname); + } + + nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data) + + sizeof(nd->u.shop_item[0]) * (MAX_SHOPITEM + 1)); + p = strchr(w4, ','); + + while (p && pos < MAX_SHOPITEM) { + int nameid, value; + struct item_data *id; + p++; + if (sscanf(p, "%d:%d", &nameid, &value) != 2) + break; + nd->u.shop_item[pos].nameid = nameid; + id = itemdb_search(nameid); + if (value < 0) + value = id->value_buy; + nd->u.shop_item[pos].value = value; + // check for bad prices that can possibly cause exploits + if (value/124. < id->value_sell/75.) { //Clened up formula to prevent overflows. + printf("\r"); //Carriage return to clear the 'loading..' line. [Skotlex] + if (value < id->value_sell) + ShowWarning ("Item %s [%d] buying price (%d) is less than selling price (%d) at %s\n", + id->name, id->nameid, value, id->value_sell, current_file); + else + ShowWarning ("Item %s [%d] discounted buying price (%d) is less than overcharged selling price (%d) at %s\n", + id->name, id->nameid, value/100*75, id->value_sell/100*124, current_file); + } + //for logs filters, atcommands and iteminfo script command + if (id->maxchance<=0) + id->maxchance = 10000; //10000 (100% drop chance)would show that the item's sold in NPC Shop + + pos++; + p = strchr(p, ','); + } + if (pos == 0) { + aFree(nd); + return 1; + } + nd->u.shop_item[pos++].nameid = 0; + + nd->bl.prev = nd->bl.next = NULL; + nd->bl.m = m; + nd->bl.x = x; + nd->bl.y = y; + nd->bl.id = npc_get_new_npc_id(); + memcpy(nd->name, w3, NAME_LENGTH-1); + nd->name[NAME_LENGTH-1] = '\0'; + nd->class_ = m==-1?-1:atoi(w4); + nd->speed = 200; + + nd = (struct npc_data *)aRealloc(nd, + sizeof(struct npc_data) + sizeof(nd->u.shop_item[0]) * pos); + + npc_shop++; + nd->bl.type = BL_NPC; + nd->bl.subtype = SHOP; + if (m >= 0) { + nd->n = map_addnpc(m,nd); + map_addblock(&nd->bl); + status_set_viewdata(&nd->bl, nd->class_); + status_change_init(&nd->bl); + unit_dataset(&nd->bl); + nd->ud.dir = dir; + clif_spawn(&nd->bl); + } else + // we skip map_addnpc, but still add it to the list of ID's + map_addiddb(&nd->bl); + strdb_put(npcname_db, nd->name,nd); + + return 0; +} + +/*========================================== + * NPCのラベルデータコンバート + *------------------------------------------ + */ +int npc_convertlabel_db (DBKey key, void *data, va_list ap) +{ + unsigned char *lname = key.str; + int pos = (int)data; + struct npc_data *nd; + struct npc_label_list *lst; + int num; + char *p; + char c; + + nullpo_retr(0, ap); + nullpo_retr(0, nd = va_arg(ap,struct npc_data *)); + + lst = nd->u.scr.label_list; + num = nd->u.scr.label_list_num; + if (!lst) { + lst = (struct npc_label_list *) aCallocA (1, sizeof(struct npc_label_list)); + num = 0; + } else + lst = (struct npc_label_list *) aRealloc (lst, sizeof(struct npc_label_list)*(num+1)); + + // In case of labels not terminated with ':', for user defined function support + p = lname; + while(isalnum(*(unsigned char*)p) || *p == '_') { p++; } + c = *p; + *p='\0'; + + // here we check if the label fit into the buffer + if (strlen(lname) > 23) { + ShowError("npc_parse_script: label name longer than 23 chars! '%s'\n (%s)", lname, current_file); + exit(1); + } + memcpy(lst[num].name, lname, strlen(lname)+1); //including EOS + + *p = c; + lst[num].pos = pos; + nd->u.scr.label_list = lst; + nd->u.scr.label_list_num = num+1; + + return 0; +} + +/*========================================== + * script行解析 + *------------------------------------------ + */ +static void npc_parse_script_line(unsigned char *p,int *curly_count,int line) { + int i = strlen((char *)p),j; + int string_flag = 0; + static int comment_flag = 0; + for(j = 0; j < i ; j++) { + if(comment_flag) { + if(p[j] == '*' && p[j+1] == '/') { + // マルチラインコメント終了 + j++; + (*curly_count)--; + comment_flag = 0; + } + } else if(string_flag) { + if(p[j] == '"') { + string_flag = 0; + } else if(p[j] == '\\' && p[j-1]<=0x7e) { + // エスケープ + j++; + } + } else { + if(p[j] == '"') { + string_flag = 1; + } else if(p[j] == '}') { + if(*curly_count == 0) { + break; + } else { + (*curly_count)--; + } + } else if(p[j] == '{') { + (*curly_count)++; + } else if(p[j] == '/' && p[j+1] == '/') { + // コメント + break; + } else if(p[j] == '/' && p[j+1] == '*') { + // マルチラインコメント + j++; + (*curly_count)++; + comment_flag = 1; + } + } + } + if(string_flag) { + printf("Missing '\"' at file %s line %d\n",current_file,line); + exit(1); + } +} + +// Like npc_parse_script, except it's sole use is to skip the contents of a script. [Skotlex] +static int npc_skip_script (char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines) +{ + unsigned char *srcbuf = NULL; + int srcsize = 65536; + int startline = 0; + unsigned char line[1024]; + int curly_count = 0; + + srcbuf = (unsigned char *)aMallocA(srcsize*sizeof(char)); + if (strchr(first_line, '{')) { + strcpy((char *)srcbuf, strchr(first_line, '{')); + startline = *lines; + } else + srcbuf[0] = 0; + npc_parse_script_line(srcbuf,&curly_count,*lines); + while (curly_count > 0) { + fgets ((char *)line, 1020, fp); + (*lines)++; + npc_parse_script_line(line,&curly_count,*lines); + if (feof(fp)) + break; + if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) { + srcsize += 65536; + srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize); + malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536); + } + if (srcbuf[0] != '{') { + if (strchr((char *) line,'{')) { + strcpy((char *) srcbuf, strchr((const char *) line, '{')); + startline = *lines; + } + } else + strcat((char *) srcbuf, (const char *) line); + } + if(curly_count > 0) + ShowError("Missing right curly at file %s, line %d\n",current_file, *lines); + aFree(srcbuf); + return 0; +} + +static int npc_parse_script(char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines,const char* file) +{ + int x, y, dir = 0, m, xs = 0, ys = 0, class_ = 0; // [Valaris] thanks to fov + char mapname[MAP_NAME_LENGTH]; + unsigned char *srcbuf = NULL; + struct script_code *script; + int srcsize = 65536; + int startline = 0; + unsigned char line[1024]; + int i; + struct npc_data *nd, *dnd; + struct dbt *label_db; + char *p; + struct npc_label_list *label_dup = NULL; + int label_dupnum = 0; + int src_id = 0; + + if (strcmp(w1, "-") == 0) { + x = 0; y = 0; m = -1; + } else { + // 引数の個数チェック + if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 || + (strcmp(w2, "script") == 0 && strchr(w4,',') == NULL)) { + ShowError("bad script line (in file %s): %s\n", file, w3); + return 1; + } + m = map_mapname2mapid(mapname); + } + + if (strcmp(w2, "script") == 0){ + // parsing script with curly + int curly_count = 0; + srcbuf = (unsigned char *)aMallocA(srcsize*sizeof(char)); + if (strchr(first_line, '{')) { + strcpy((char *)srcbuf, strchr(first_line, '{')); + startline = *lines; + } else + srcbuf[0] = 0; + npc_parse_script_line(srcbuf,&curly_count,*lines); + while (curly_count > 0) { + fgets ((char *)line, 1020, fp); + (*lines)++; + npc_parse_script_line(line,&curly_count,*lines); + if (feof(fp)) + break; + if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) { + srcsize += 65536; + srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize); + malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536); + } + if (srcbuf[0] != '{') { + if (strchr((char *) line,'{')) { + strcpy((char *) srcbuf, strchr((const char *) line, '{')); + startline = *lines; + } + } else + strcat((char *) srcbuf, (const char *) line); + } + if(curly_count > 0) { + ShowError("Missing right curly at file %s, line %d\n",file, *lines); + script = NULL; + } else { + // printf("Ok line %d\n",*lines); + script = parse_script((unsigned char *) srcbuf, file, startline); + } + if (script == NULL) { + // script parse error? + aFree(srcbuf); + return 1; + } + } else { + // duplicateする + char srcname[128]; + struct npc_data *dnd; + if (sscanf(w2, "duplicate(%[^)])", srcname) != 1) { + ShowError("bad duplicate name (in %s)! : %s", file, w2); + return 0; + } + if ((dnd = npc_name2id(srcname)) == NULL) { + ShowError("bad duplicate name (in %s)! (not exist) : %s\n", file, srcname); + return 0; + } + script = dnd->u.scr.script; + label_dup = dnd->u.scr.label_list; + label_dupnum = dnd->u.scr.label_list_num; + src_id = dnd->bl.id; + + }// end of スクリプト解析 + + nd = (struct npc_data *)aCalloc(1, sizeof(struct npc_data)); + + if (sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3) { + // 接触型NPC + int i, j; + + if (xs >= 0) xs = xs * 2 + 1; + if (ys >= 0) ys = ys * 2 + 1; + + if (m >= 0) { + for (i = 0; i < ys; i++) { + for (j = 0; j < xs; j++) { + if (map_getcell(m, x - xs/2 + j, y - ys/2 + i, CELL_CHKNOPASS)) + continue; + map_setcell(m, x - xs/2 + j, y - ys/2 + i, CELL_SETNPC); + } + } + } + nd->u.scr.xs = xs; + nd->u.scr.ys = ys; + } else { + // クリック型NPC + class_ = atoi(w4); + nd->u.scr.xs = 0; + nd->u.scr.ys = 0; + } + + while ((p = strchr(w3,':'))) { + if (p[1] == ':') break; + } + if (p) { + *p = 0; + memcpy(nd->name, w3, NAME_LENGTH-1); + memcpy(nd->exname, p+2, NAME_LENGTH-1); + } else { + memcpy(nd->name, w3, NAME_LENGTH-1); + memcpy(nd->exname, w3, NAME_LENGTH-1); + } + + if((dnd = npc_name2id(nd->exname))){ + if(battle_config.etc_log) + ShowInfo("npc_parse_script: Overriding NPC '%s::%s' to '%s::%d'.. in file '%s' (Duplicated System Name - Lazy scripters >_>) \n",nd->name,nd->exname,nd->name,npc_script,file); + sprintf(nd->exname, "%d", npc_script); + } + + nd->bl.prev = nd->bl.next = NULL; + nd->bl.m = m; + nd->bl.x = x; + nd->bl.y = y; + nd->bl.id = npc_get_new_npc_id(); + nd->class_ = class_; + nd->speed = 200; + nd->u.scr.script = script; + nd->u.scr.src_id = src_id; + + npc_script++; + nd->bl.type = BL_NPC; + nd->bl.subtype = SCRIPT; + + for (i = 0; i < MAX_EVENTTIMER; i++) + nd->eventtimer[i] = -1; + if (m >= 0) { + nd->n = map_addnpc(m, nd); + status_change_init(&nd->bl); + unit_dataset(&nd->bl); + nd->ud.dir = dir; + map_addblock(&nd->bl); + // Unused. You can always use xxx::OnXXXX events. Have this removed to improve perfomance. + /*if (evflag) { // イベント型 + struct event_data *ev = (struct event_data *)aCalloc(1, sizeof(struct event_data)); + ev->nd = nd; + ev->pos = 0; + strdb_put(ev_db, nd->exname, ev); + } else { + clif_spawn(&nd->bl); + }*/ + if (class_ >= 0){ + status_set_viewdata(&nd->bl, nd->class_); + clif_spawn(&nd->bl); + } + } else { + // we skip map_addnpc, but still add it to the list of ID's + map_addiddb(&nd->bl); + } + strdb_put(npcname_db, nd->exname, nd); + + //----------------------------------------- + // ラベルデータの準備 + if (srcbuf){ + // script本体がある場合の処理 + // ラベルデータのコンバート + label_db = script_get_label_db(); + label_db->foreach(label_db, npc_convertlabel_db, nd); + + // もう使わないのでバッファ解放 + aFree(srcbuf); + } else { + // duplicate + nd->u.scr.label_list = label_dup; // ラベルデータ共有 + nd->u.scr.label_list_num = label_dupnum; + } + + //----------------------------------------- + // イベント用ラベルデータのエクスポート + for (i = 0; i < nd->u.scr.label_list_num; i++){ + char *lname = nd->u.scr.label_list[i].name; + int pos = nd->u.scr.label_list[i].pos; + + if ((lname[0] == 'O' || lname[0] == 'o') && (lname[1] == 'N' || lname[1] == 'n')) { + // this check is useless here because the buffer is only 24 chars + // and already overwritten if this is here is reached + // I leave the check anyway but place it correctly to npc_convertlabel_db + if (strlen(lname)>NAME_LENGTH-1) { + ShowError("npc_parse_script: label name longer than %d chars! '%s' (%s)\n", NAME_LENGTH-1, lname, file); + exit(1); + } else { + struct event_data *ev; + unsigned char buf[51]; + // 51 comes from: 24 for npc name + 24 for label + 2 for a "::" and 1 for EOS + sprintf(buf,"%s::%s",nd->exname,lname); + + // remember the label is max 50 chars + eos; see the strdb_init below + // generate the data and insert it + ev=(struct event_data *)aMalloc(sizeof(struct event_data)); + ev->nd=nd; + ev->pos=pos; + if (strdb_put(ev_db,buf,ev) != NULL) //There was already another event of the same name? + ShowWarning("npc_parse_script : duplicate event %s (%s)\n",buf, file); + } + } + } + + //----------------------------------------- + // ラベルデータからタイマーイベント取り込み + for (i = 0; i < nd->u.scr.label_list_num; i++){ + int t = 0, k = 0; + char *lname = nd->u.scr.label_list[i].name; + int pos = nd->u.scr.label_list[i].pos; + if (sscanf(lname, "OnTimer%d%n", &t, &k) == 1 && lname[k] == '\0') { + // タイマーイベント + struct npc_timerevent_list *te = nd->u.scr.timer_event; + int j, k = nd->u.scr.timeramount; + if (te == NULL) + te = (struct npc_timerevent_list *)aMallocA(sizeof(struct npc_timerevent_list)); + else + te = (struct npc_timerevent_list *)aRealloc( te, sizeof(struct npc_timerevent_list) * (k+1) ); + for (j = 0; j < k; j++){ + if (te[j].timer > t){ + memmove(te+j+1, te+j, sizeof(struct npc_timerevent_list)*(k-j)); + break; + } + } + te[j].timer = t; + te[j].pos = pos; + nd->u.scr.timer_event = te; + nd->u.scr.timeramount++; + } + } + nd->u.scr.timerid = -1; + + return 0; +} + +/*========================================== + * function行解析 + *------------------------------------------ + */ +static int npc_parse_function (char *w1, char *w2, char *w3, char *w4, char *first_line, FILE *fp, int *lines,const char* file) +{ + unsigned char *srcbuf, *p; + struct script_code *script; + struct script_code *oldscript; + int srcsize = 65536; + int startline = 0; + char line[1024]; + int curly_count = 0; + struct dbt *user_db; + + // スクリプトの解析 + srcbuf = (unsigned char *) aMallocA (srcsize*sizeof(char)); + if (strchr(first_line,'{')) { + strcpy(srcbuf, strchr(first_line,'{')); + startline = *lines; + } else + srcbuf[0] = 0; + npc_parse_script_line(srcbuf,&curly_count,*lines); + + while (curly_count > 0) { + fgets(line, sizeof(line) - 1, fp); + (*lines)++; + npc_parse_script_line(line,&curly_count,*lines); + if (feof(fp)) + break; + if (strlen(srcbuf)+strlen(line)+1 >= (unsigned int)srcsize) { + srcsize += 65536; + srcbuf = (char *)aRealloc(srcbuf, srcsize); + malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536); + } + if (srcbuf[0]!='{') { + if (strchr(line,'{')) { + strcpy(srcbuf, strchr(line,'{')); + startline = *lines; + } + } else + strcat(srcbuf,line); + } + if(curly_count > 0) { + ShowError("Missing right curly at file %s, line %d\n",file, *lines); + script = NULL; + } else { + script = parse_script(srcbuf, file, startline); + } + if (script == NULL) { + // script parse error? + aFree(srcbuf); + return 1; + } + + p = (char *) aMallocA (50*sizeof(char)); + strncpy(p, w3, 50); + + user_db = script_get_userfunc_db(); + oldscript = (struct script_code *)strdb_get(user_db, p); + if(oldscript != NULL) { + printf("\r"); //Carriage return to clear the 'loading..' line. [Skotlex] + ShowInfo("parse_function: Overwriting user function [%s] (%s:%d)\n", p, file, *lines); + script_free_code(oldscript); + user_db->remove(user_db,str2key(p)); + strdb_put(user_db, p, script); + } else + strdb_put(user_db, p, script); + + // もう使わないのでバッファ解放 + aFree(srcbuf); + +// printf("function %s => %p\n",p,script); + + return 0; +} + + +/*========================================== + * Parse Mob 1 - Parse mob list into each map + * Parse Mob 2 - Actually Spawns Mob + * [Wizputer] + * If cached =1, it is a dynamic cached mob + * index points to the index in the mob_list of the map_data cache. + * -1 indicates that it is not stored on the map. + *------------------------------------------ + */ +int npc_parse_mob2 (struct spawn_data *mob, int index) +{ + int i; + struct mob_data *md; + + for (i = 0; i < mob->num; i++) { + md = mob_spawn_dataset(mob); + md->spawn = mob; + md->spawn_n = index; + md->special_state.cached = (index>=0); //If mob is cached on map, it is dynamically removed + mob_spawn(md); + } + + return 1; +} + +int npc_parse_mob (char *w1, char *w2, char *w3, char *w4) +{ + int level, num, class_, mode, x,y,xs,ys; + char mapname[MAP_NAME_LENGTH]; + char mobname[NAME_LENGTH]; + struct spawn_data mob, *data; + + malloc_set(&mob, 0, sizeof(struct spawn_data)); + + // 引数の個数チェック + if (sscanf(w1, "%15[^,],%d,%d,%d,%d", mapname, &x, &y, &xs, &ys) < 3 || + sscanf(w4, "%d,%d,%u,%u,%49[^\r\n]", &class_, &num, &mob.delay1, &mob.delay2, mob.eventname) < 2 ) { + ShowError("bad monster line : %s %s %s (file %s)\n", w1, w3, w4, current_file); + return 1; + } + if (!mapindex_name2id(mapname)) { + ShowError("wrong map name : %s %s (file %s)\n", w1,w3, current_file); + return 1; + } + mode = map_mapname2mapid(mapname); + if (mode < 0) //Not loaded on this map-server instance. + return 1; + mob.m = (unsigned short)mode; + + if (x < 0 || map[mob.m].xs <= x || y < 0 || map[mob.m].ys <= y) { + ShowError("Out of range spawn coordinates: %s (%d,%d), map size is (%d,%d) - %s %s (file %s)\n", map[mob.m].name, x, y, map[mob.m].xs-1, map[mob.m].ys-1, w1,w3, current_file); + return 1; + } + + // check monster ID if exists! + if (mobdb_checkid(class_)==0) { + ShowError("bad monster ID : %s %s (file %s)\n", w3, w4, current_file); + return 1; + } + + if (num < 1 || num>1000 ) { + ShowError("wrong number of monsters : %s %s (file %s)\n", w3, w4, current_file); + return 1; + } + + mob.num = (unsigned short)num; + mob.class_ = (short) class_; + mob.x = (unsigned short)x; + mob.y = (unsigned short)y; + mob.xs = (signed short)xs; + mob.ys = (signed short)ys; + + if (mob.num > 1 && battle_config.mob_count_rate != 100) { + if ((mob.num = mob.num * battle_config.mob_count_rate / 100) < 1) + mob.num = 1; + } + + if (battle_config.force_random_spawn || (mob.x == 0 && mob.y == 0)) + { //Force a random spawn anywhere on the map. + mob.x = mob.y = 0; + mob.xs = mob.ys = -1; + } + + //Apply the spawn delay fix [Skotlex] + mode = mob_db(class_)->status.mode; + if (mode & MD_BOSS) { //Bosses + if (battle_config.boss_spawn_delay != 100) + { + mob.delay1 = mob.delay1*battle_config.boss_spawn_delay/100; + mob.delay2 = mob.delay2*battle_config.boss_spawn_delay/100; + } + } else if (mode&MD_PLANT) { //Plants + if (battle_config.plant_spawn_delay != 100) + { + mob.delay1 = mob.delay1*battle_config.plant_spawn_delay/100; + mob.delay2 = mob.delay2*battle_config.plant_spawn_delay/100; + } + } else if (battle_config.mob_spawn_delay != 100) + { //Normal mobs + mob.delay1 = mob.delay1*battle_config.mob_spawn_delay/100; + mob.delay2 = mob.delay2*battle_config.mob_spawn_delay/100; + } + + // parse MOB_NAME,[MOB LEVEL] + if (sscanf(w3, "%23[^,],%d", mobname, &level) > 1) + mob.level = level; + + if(mob.delay1>0xfffffff || mob.delay2>0xfffffff) { + ShowError("wrong monsters spawn delays : %s %s (file %s)\n", w3, w4, current_file); + return 1; + } + + //Use db names instead of the spawn file ones. + if(battle_config.override_mob_names==1) + strcpy(mob.name,"--en--"); + else if (battle_config.override_mob_names==2) + strcpy(mob.name,"--ja--"); + else + strncpy(mob.name, mobname, NAME_LENGTH-1); + + if (!mob_parse_dataset(&mob)) //Verify dataset. + return 1; + + //Now that all has been validated. We allocate the actual memory + //that the re-spawn data will use. + data = aMalloc(sizeof(struct spawn_data)); + memcpy(data, &mob, sizeof(struct spawn_data)); + + if( !battle_config.dynamic_mobs || mob.delay1 || mob.delay2 ) { + npc_parse_mob2(data,-1); + npc_delay_mob += mob.num; + } else { + int index = map_addmobtolist(data->m, data); + if( index >= 0 ) { + // check if target map has players + // (usually shouldn't occur when map server is just starting, + // but not the case when we do @reloadscript + if (map[mob.m].users > 0) + npc_parse_mob2(data,index); + npc_cache_mob += mob.num; + } else { + // mobcache is full + // create them as delayed with one second + mob.delay1 = 1000; + mob.delay2 = 1000; + npc_parse_mob2(data,-1); + npc_delay_mob += mob.num; + } + } + + npc_mob++; + + return 0; +} + +/*========================================== + * マップフラグ行の解析 + *------------------------------------------ + */ +static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4) +{ + int m; + char mapname[MAP_NAME_LENGTH]; + int state = 1; + + // 引数の個数チェック + if (sscanf(w1, "%15[^,]",mapname) != 1) + return 1; + + m = map_mapname2mapid(mapname); + if (m < 0) + return 1; + if (w4 && strcmpi(w4, "off") == 0) + state = 0; //Disable mapflag rather than enable it. [Skotlex] + +//マップフラグ + if (strcmpi(w3, "nosave") == 0) { + char savemap[MAP_NAME_LENGTH]; + int savex, savey; + if (state == 0) + ; //Map flag disabled. + else if (strcmp(w4, "SavePoint") == 0) { + map[m].save.map = 0; + map[m].save.x = -1; + map[m].save.y = -1; + } else if (sscanf(w4, "%15[^,],%d,%d", savemap, &savex, &savey) == 3) { + map[m].save.map = mapindex_name2id(savemap); + map[m].save.x = savex; + map[m].save.y = savey; + if (!map[m].save.map) { + ShowWarning("Specified save point map '%s' for mapflag 'nosave' not found (file %s), using 'SavePoint'.\n",savemap,current_file); + map[m].save.x = -1; + map[m].save.y = -1; + } + } + map[m].flag.nosave = state; + } + else if (strcmpi(w3,"nomemo")==0) { + map[m].flag.nomemo=state; + } + else if (strcmpi(w3,"noteleport")==0) { + map[m].flag.noteleport=state; + } + else if (strcmpi(w3,"nowarp")==0) { + map[m].flag.nowarp=state; + } + else if (strcmpi(w3,"nowarpto")==0) { + map[m].flag.nowarpto=state; + } + else if (strcmpi(w3,"noreturn")==0) { + map[m].flag.noreturn=state; + } + else if (strcmpi(w3,"monster_noteleport")==0) { + map[m].flag.monster_noteleport=state; + } + else if (strcmpi(w3,"nobranch")==0) { + map[m].flag.nobranch=state; + } + else if (strcmpi(w3,"nopenalty")==0) { + map[m].flag.noexppenalty=state; + map[m].flag.nozenypenalty=state; + } + else if (strcmpi(w3,"pvp")==0) { + map[m].flag.pvp=state; + if (state) { + map[m].flag.gvg=0; + map[m].flag.gvg=0; + map[m].flag.gvg_dungeon=0; + map[m].flag.gvg_castle=0; + } + } + else if (strcmpi(w3,"pvp_noparty")==0) { + map[m].flag.pvp_noparty=state; + } + else if (strcmpi(w3,"pvp_noguild")==0) { + map[m].flag.pvp_noguild=state; + } + else if (strcmpi(w3, "pvp_nightmaredrop") == 0) { + char drop_arg1[16], drop_arg2[16]; + int drop_id = 0, drop_type = 0, drop_per = 0; + if (sscanf(w4, "%[^,],%[^,],%d", drop_arg1, drop_arg2, &drop_per) == 3) { + int i; + if (strcmp(drop_arg1, "random") == 0) + drop_id = -1; + else if (itemdb_exists((drop_id = atoi(drop_arg1))) == NULL) + drop_id = 0; + if (strcmp(drop_arg2, "inventory") == 0) + drop_type = 1; + else if (strcmp(drop_arg2,"equip") == 0) + drop_type = 2; + else if (strcmp(drop_arg2,"all") == 0) + drop_type = 3; + + if (drop_id != 0){ + for (i = 0; i < MAX_DROP_PER_MAP; i++) { + if (map[m].drop_list[i].drop_id == 0){ + map[m].drop_list[i].drop_id = drop_id; + map[m].drop_list[i].drop_type = drop_type; + map[m].drop_list[i].drop_per = drop_per; + break; + } + } + map[m].flag.pvp_nightmaredrop = 1; + } + } else if (!state) //Disable + map[m].flag.pvp_nightmaredrop = 0; + } + else if (strcmpi(w3,"pvp_nocalcrank")==0) { + map[m].flag.pvp_nocalcrank=state; + } + else if (strcmpi(w3,"gvg")==0) { + map[m].flag.gvg=state; + if (state) map[m].flag.pvp=0; + } + else if (strcmpi(w3,"gvg_noparty")==0) { + map[m].flag.gvg_noparty=state; + } + else if (strcmpi(w3,"gvg_dungeon")==0) { + map[m].flag.gvg_dungeon=state; + if (state) map[m].flag.pvp=0; + } + else if (strcmpi(w3,"gvg_castle")==0) { + map[m].flag.gvg_castle=state; + if (state) map[m].flag.pvp=0; + } + else if (strcmpi(w3,"noexppenalty")==0) { + map[m].flag.noexppenalty=state; + } + else if (strcmpi(w3,"nozenypenalty")==0) { + map[m].flag.nozenypenalty=state; + } + else if (strcmpi(w3,"notrade")==0) { + map[m].flag.notrade=state; + } + else if (strcmpi(w3,"novending")==0) { + map[m].flag.novending=state; + } + else if (strcmpi(w3,"nodrop")==0) { + map[m].flag.nodrop=state; + } + else if (strcmpi(w3,"noskill")==0) { + map[m].flag.noskill=state; + } + else if (strcmpi(w3,"noicewall")==0) { // noicewall [Valaris] + map[m].flag.noicewall=state; + } + else if (strcmpi(w3,"snow")==0) { // snow [Valaris] + map[m].flag.snow=state; + } + else if (strcmpi(w3,"clouds")==0) { + map[m].flag.clouds=state; + } + else if (strcmpi(w3,"clouds2")==0) { // clouds2 [Valaris] + map[m].flag.clouds2=state; + } + else if (strcmpi(w3,"fog")==0) { // fog [Valaris] + map[m].flag.fog=state; + } + else if (strcmpi(w3,"fireworks")==0) { + map[m].flag.fireworks=state; + } + else if (strcmpi(w3,"sakura")==0) { // sakura [Valaris] + map[m].flag.sakura=state; + } + else if (strcmpi(w3,"leaves")==0) { // leaves [Valaris] + map[m].flag.leaves=state; + } + else if (strcmpi(w3,"rain")==0) { // rain [Valaris] + map[m].flag.rain=state; + } + else if (strcmpi(w3,"indoors")==0) { // celest + map[m].flag.indoors=state; + } + else if (strcmpi(w3,"nightenabled")==0) { // Skotlex + map[m].flag.nightenabled=state; + } + else if (strcmpi(w3,"nogo")==0) { // celest + map[m].flag.nogo=state; + } + else if (strcmpi(w3,"noexp")==0) { // Lorky + map[m].flag.nobaseexp=state; + map[m].flag.nojobexp=state; + } + else if (strcmpi(w3,"nobaseexp")==0) { // Lorky + map[m].flag.nobaseexp=state; + } + else if (strcmpi(w3,"nojobexp")==0) { // Lorky + map[m].flag.nojobexp=state; + } + else if (strcmpi(w3,"noloot")==0) { // Lorky + map[m].flag.nomobloot=state; + map[m].flag.nomvploot=state; + } + else if (strcmpi(w3,"nomobloot")==0) { // Lorky + map[m].flag.nomobloot=state; + } + else if (strcmpi(w3,"nomvploot")==0) { // Lorky + map[m].flag.nomvploot=state; + } + else if (strcmpi(w3,"nocommand")==0) { // Skotlex + if (state) { + if (sscanf(w4, "%d", &state) == 1) + map[m].nocommand =state; + else //No level specified, block everyone. + map[m].nocommand =100; + } else + map[m].nocommand=0; + } + else if (strcmpi(w3,"restricted")==0) { // Komurka + if (state) { + map[m].flag.restricted=1; + sscanf(w4, "%d", &state); + map[m].zone |= 1<<(state+1); + } else { + map[m].flag.restricted=0; + map[m].zone = 0; + } + } + else if (strcmpi(w3,"jexp")==0) { + map[m].jexp = (state) ? atoi(w4) : 100; + if( map[m].jexp < 0 ) map[m].jexp = 100; + map[m].flag.nojobexp = (map[m].jexp==0)?1:0; + } + else if (strcmpi(w3,"bexp")==0) { + map[m].bexp = (state) ? atoi(w4) : 100; + if( map[m].bexp < 0 ) map[m].bexp = 100; + map[m].flag.nobaseexp = (map[m].bexp==0)?1:0; + } + else if (strcmpi(w3,"loadevent")==0) { // Skotlex + map[m].flag.loadevent=state; + } + else if (strcmpi(w3,"nochat")==0) { // Skotlex + map[m].flag.nochat=state; + } + else if (strcmpi(w3,"partylock")==0) { // Skotlex + map[m].flag.partylock=state; + } + else if (strcmpi(w3,"guildlock")==0) { // Skotlex + map[m].flag.guildlock=state; + } + + return 0; +} + +/*========================================== + * Setting up map cells + *------------------------------------------ + */ +static int npc_parse_mapcell (char *w1, char *w2, char *w3, char *w4) +{ + int m, cell, x, y, x0, y0, x1, y1; + char type[24], mapname[MAP_NAME_LENGTH]; + + if (sscanf(w1, "%15[^,]", mapname) != 1) + return 1; + + m = map_mapname2mapid(mapname); + if (m < 0) + return 1; + + if (sscanf(w3, "%23[^,],%d,%d,%d,%d", type, &x0, &y0, &x1, &y1) < 4) { + ShowError("Bad setcell line : %s\n",w3); + return 1; + } + cell = strtol(type, (char **)NULL, 0); + //printf ("0x%x %d %d %d %d\n", cell, x0, y0, x1, y1); + + if (x0 > x1) { int t = x0; x0 = x1; x1 = t; } + if (y0 > y1) { int t = y0; y0 = y1; y1 = t; } + + for (x = x0; x <= x1; x++) { + for (y = y0; y <= y1; y++) { + map_setcell(m, x, y, cell); + //printf ("setcell 0x%x %d %d %d\n", cell, m, x, y); + } + } + + return 0; +} + +void npc_parsesrcfile (char *name) +{ + int m, lines = 0; + char line[2048]; + + FILE *fp = fopen (name,"r"); + if (fp == NULL) { + ShowError ("File not found : %s\n", name); + return; + } + current_file = name; + + while (fgets(line, sizeof(line) - 1, fp)) { + char w1[2048], w2[2048], w3[2048], w4[2048], mapname[2048]; + int i, w4pos, count; + lines++; + + if (line[0] == '/' && line[1] == '/') + continue; + + if (!sscanf(line, " %n", &i) && i == strlen(line)) // just whitespace + continue; + + // 最初はタブ区切りでチェックしてみて、ダメならスペース区切りで確認 + w1[0] = w2[0] = w3[0] = w4[0] = '\0'; //It's best to initialize values + //to prevent passing previously parsed values to the parsers when not all + //fields are specified. [Skotlex] + if ((count = sscanf(line, "%[^\t\r\n]\t%[^\t\r\n]\t%[^\t\r\n]\t%n%[^\r\n]", w1, w2, w3, &w4pos, w4)) < 3) + { + if ((count = sscanf(line, "%s %s %[^\t]\t %n%[^\n]", w1, w2, w3, &w4pos, w4)) == 4 || + (count = sscanf(line, "%s %s %s %n%[^\n]\n", w1, w2, w3, &w4pos, w4)) >= 3) + { + ShowWarning("\r"); + ShowWarning("Incorrect separator syntax in file '%s', line '%i'. Use tabs instead of spaces!\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4); + } else { + ShowError("\r"); //Erase the npc spinner. + ShowError("Could not parse file '%s', line '%i'.\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4); + continue; + } + } + + // マップの存在確認 + if (strcmp(w1,"-") !=0 && strcmpi(w1,"function") != 0 ){ + sscanf(w1,"%[^,]",mapname); + if (!mapindex_name2id(mapname)) { //Incorrect map + ShowError("Invalid map '%s' in line %d, file %s\n", mapname, lines, current_file); + if (strcmpi(w2,"script") == 0 && count > 3) //we must skip the script info... + npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines); + continue; + } + if ((m = map_mapname2mapid(mapname)) < 0) { + // "mapname" is not assigned to this server + // we must skip the script info... + if (strcmpi(w2,"script") == 0 && count > 3) + npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines); + continue; + } + } + if (strcmpi(w2,"warp") == 0 && count > 3) { + npc_parse_warp(w1,w2,w3,w4); + } else if (strcmpi(w2,"shop") == 0 && count > 3) { + npc_parse_shop(w1,w2,w3,w4); + } else if (strcmpi(w2,"script") == 0 && count > 3) { + if (strcmpi(w1,"function") == 0) { + npc_parse_function(w1,w2,w3,w4,line+w4pos,fp,&lines,name); + } else { + npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines,name); + } + } else if ((i = 0, sscanf(w2,"duplicate%n",&i), (i > 0 && w2[i] == '(')) && count > 3) { + npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines,name); + } else if (strcmpi(w2,"monster") == 0 && count > 3) { + npc_parse_mob(w1,w2,w3,w4); + } else if (strcmpi(w2,"mapflag") == 0 && count >= 3) { + npc_parse_mapflag(w1,w2,w3,w4); + } else if (strcmpi(w2,"setcell") == 0 && count >= 3) { + npc_parse_mapcell(w1,w2,w3,w4); + } else { + ShowError("Probably TAB is missing in file '%s', line '%i':\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4); + } + } + fclose(fp); + + return; +} + +int npc_script_event(TBL_PC* sd, int type) { + int i; + if (type < 0 || type >= NPCE_MAX) + return 0; + if (!sd) { + if (battle_config.error_log) + ShowError("npc_script_event: NULL sd. Event Type %d\n", type); + return 0; + } + if (script_event[type].nd) { + TBL_NPC *nd = script_event[type].nd; + run_script(nd->u.scr.script,0,sd->bl.id,nd->bl.id); + return 1; + } else if (script_event[type].event_count) { + for (i = 0; i= UCHAR_MAX) return 0; + + if((p=strchr(p,':')) && p && strcmpi(name,p)==0 ) + { + event_buf[*count] = (struct event_data *)data; + event_name[*count] = key.str; + (*count)++; + return 1; + } + return 0; +} + +static void npc_read_event_script(void) +{ + int i; + unsigned char buf[64]="::"; + struct { + char *name; + char *event_name; + } config[] = { + {"Login Event",script_config.login_event_name}, + {"Logout Event",script_config.logout_event_name}, + {"Load Map Event",script_config.loadmap_event_name}, + {"Base LV Up Event",script_config.baselvup_event_name}, + {"Job LV Up Event",script_config.joblvup_event_name}, + {"Die Event",script_config.die_event_name}, + {"Kill PC Event",script_config.kill_pc_event_name}, + {"Kill NPC Event",script_config.kill_mob_event_name}, + }; + + for (i = 0; i < NPCE_MAX; i++) { + if (script_event[i].nd) + script_event[i].nd = NULL; + if (script_event[i].event_count) + script_event[i].event_count = 0; + if (!script_config.event_script_type) { + //Use a single NPC as event source. + script_event[i].nd = npc_name2id(config[i].event_name); + } else { + //Use an array of Events + strncpy(buf+2,config[i].event_name,62); + ev_db->foreach(ev_db,npc_read_event_script_sub,buf, + &script_event[i].event, + &script_event[i].event_name, + &script_event[i].event_count); + } + } + if (battle_config.etc_log) { + //Print summary. + for (i = 0; i < NPCE_MAX; i++) { + if(!script_config.event_script_type) { + if (script_event[i].nd) + ShowInfo("%s: Using NPC named '%s'.\n", config[i].name, config[i].event_name); + else + ShowInfo("%s: No NPC found with name '%s'.\n", config[i].name, config[i].event_name); + } else + ShowInfo("%s: %d '%s' events.\n", config[i].name, script_event[i].event_count, config[i].event_name); + } + } +} +static int npc_read_indoors (void) +{ + char *buf, *p; + int s, m; + + buf = (char *)grfio_reads("data\\indoorrswtable.txt",&s); + if (buf == NULL) + return -1; + buf[s] = 0; + + for (p = buf; p - buf < s; ) { + char map_name[64]; + if (sscanf(p, "%15[^#]#", map_name) == 1) { + size_t pos = strlen(map_name) - 4; // replace '.xxx' extension + memcpy(map_name+pos,".gat",4); // with '.gat' + if ((m = map_mapname2mapid(map_name)) >= 0) + map[m].flag.indoors = 1; + } + + p = strchr(p, 10); + if (!p) break; + p++; + } + aFree(buf); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\indoorrswtable.txt"); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ + +static int npc_cleanup_sub (struct block_list *bl, va_list ap) { + nullpo_retr(0, bl); + + switch(bl->type) { + case BL_NPC: + npc_unload((struct npc_data *)bl); + break; + case BL_MOB: + //This is used only on reloading npcs, so let's not free spawn-once mobs. [Skotlex] + if (((TBL_MOB*)bl)->spawn) + unit_free(bl,0); + break; + } + + return 0; +} + +static int npc_cleanup_dbsub(DBKey key,void * data,va_list ap) { + return npc_cleanup_sub((struct block_list*)data, 0); +} + +int npc_reload (void) +{ + struct npc_src_list *nsl; + int m, i; + time_t last_time = time(0); + int busy = 0, npc_new_min = npc_id; + char c = '-'; + + //Remove all npcs/mobs. [Skotlex] + map_foreachiddb(npc_cleanup_dbsub); + for (m = 0; m < map_num; m++) { + if(battle_config.dynamic_mobs) { //dynamic check by [random] + for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++) + if (map[m].moblist[i]) aFree(map[m].moblist[i]); + malloc_set (map[m].moblist, 0, sizeof(map[m].moblist)); + } + if (map[m].npc_num > 0 && battle_config.error_log) + ShowWarning("npc_reload: %d npcs weren't removed at map %s!\n", map[m].npc_num, map[m].name); + } + + // anything else we should cleanup? + // Reloading npc's now + ev_db->clear(ev_db,NULL); + npcname_db->clear(npcname_db,NULL); + npc_warp = npc_shop = npc_script = 0; + npc_mob = npc_cache_mob = npc_delay_mob = 0; + + for (nsl = npc_src_first; nsl; nsl = nsl->next) { + npc_parsesrcfile(nsl->name); + if (script_config.verbose_mode) { + printf("\r"); + ShowStatus("Loading NPCs... %-53s", nsl->name); + } else { + if (last_time != time(0)) { + printf("\r"); + ShowStatus("Loading NPCs... Working: "); + last_time = time(0); + switch(busy) { + case 0: c='\\'; busy++; break; + case 1: c='|'; busy++; break; + case 2: c='/'; busy++; break; + case 3: c='-'; busy=0; + } + printf("[%c]",c); + } + } + fflush(stdout); + } + printf("\r"); + ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'" + CL_WHITE"%d"CL_RESET"' Warps\n\t-'" + CL_WHITE"%d"CL_RESET"' Shops\n\t-'" + CL_WHITE"%d"CL_RESET"' Scripts\n\t-'" + CL_WHITE"%d"CL_RESET"' Mobs\n\t-'" + CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'" + CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n", + npc_id - npc_new_min, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob); + + //Re-read the NPC Script Events cache. + npc_read_event_script(); + + //Execute the OnInit event for freshly loaded npcs. [Skotlex] + ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '" + CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit")); + // Execute rest of the startup events if connected to char-server. [Lance] + if(!CheckForCharServer()){ + ShowStatus("Event '"CL_WHITE"OnCharIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnCharIfInit")); + ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit")); + ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInitOnce")); + } + return 0; +} + +/*========================================== + * 終了 + *------------------------------------------ + */ +int do_final_npc(void) +{ + int i; + struct block_list *bl; + + for (i = START_NPC_NUM; i < npc_id; i++){ + if ((bl = map_id2bl(i))){ + if (bl->type == BL_NPC) + npc_unload((struct npc_data *)bl); + else if (bl->type&(BL_MOB|BL_PET)) + unit_free(bl, 0); + } + } + + ev_db->destroy(ev_db, NULL); + //There is no free function for npcname_db because at this point there shouldn't be any npcs left! + //So if there is anything remaining, let the memory manager catch it and report it. + npcname_db->destroy(npcname_db, NULL); + ers_destroy(timer_event_ers); + npc_clearsrcfile(); + + return 0; +} + +static void npc_debug_warps_sub(struct npc_data *nd) +{ + int m; + if (nd->bl.type != BL_NPC || nd->bl.subtype != WARP || nd->bl.m < 0) + return; + + m = map_mapindex2mapid(nd->u.warp.mapindex); + if (m < 0) return; //Warps to another map, nothing to do about it. + + if (map_getcell(m, nd->u.warp.x, nd->u.warp.y, CELL_CHKNPC)) { + ShowWarning("Warp %s at %s(%d,%d) warps directly on top of an area npc at %s(%d,%d)\n", + nd->name, + map[nd->bl.m].name, nd->bl.x, nd->bl.y, + map[m].name, nd->u.warp.x, nd->u.warp.y + ); + } + if (map_getcell(m, nd->u.warp.x, nd->u.warp.y, CELL_CHKNOPASS)) { + ShowWarning("Warp %s at %s(%d,%d) warps to a non-walkable tile at %s(%d,%d)\n", + nd->name, + map[nd->bl.m].name, nd->bl.x, nd->bl.y, + map[m].name, nd->u.warp.x, nd->u.warp.y + ); + } +} + +static void npc_debug_warps(void) +{ + int m, i; + for (m = 0; m < map_num; m++) + for (i = 0; i < map[m].npc_num; i++) + npc_debug_warps_sub(map[m].npc[i]); +} + +/*========================================== + * npc初期化 + *------------------------------------------ + */ +int do_init_npc(void) +{ + struct npc_src_list *nsl; + time_t last_time = time(0); + int busy, i; + char c = '-'; + + //Stock view data for normal npcs. + malloc_set(&npc_viewdb, 0, sizeof(npc_viewdb)); + npc_viewdb[0].class_ = INVISIBLE_CLASS; //Invisible class is stored here. + for (busy = 1; busy < MAX_NPC_CLASS; busy++) + npc_viewdb[busy].class_ = busy; + busy = 0; + // indoorrswtable.txt and etcinfo.txt [Celest] + if (battle_config.indoors_override_grffile) + npc_read_indoors(); + + // comparing only the first 24 chars of labels that are 50 chars long isn't that nice + // will cause "duplicated" labels where actually no dup is... + ev_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA,51); + npcname_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_BASE,NAME_LENGTH); + + malloc_set(&ev_tm_b, -1, sizeof(ev_tm_b)); + timer_event_ers = ers_new((uint32)sizeof(struct timer_event_data)); + + for (nsl = npc_src_first; nsl; nsl = nsl->next) { + npc_parsesrcfile(nsl->name); + current_file = NULL; + printf("\r"); + if (script_config.verbose_mode) + ShowStatus ("Loading NPCs... %-53s", nsl->name); + else { + ShowStatus("Loading NPCs... Working: "); + if (last_time != time(0)) { + last_time = time(0); + switch(busy) { + case 0: c='\\'; busy++; break; + case 1: c='|'; busy++; break; + case 2: c='/'; busy++; break; + case 3: c='-'; busy=0; + } + } + printf("[%c]",c); + } + fflush(stdout); + } + printf("\r"); + ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'" + CL_WHITE"%d"CL_RESET"' Warps\n\t-'" + CL_WHITE"%d"CL_RESET"' Shops\n\t-'" + CL_WHITE"%d"CL_RESET"' Scripts\n\t-'" + CL_WHITE"%d"CL_RESET"' Mobs\n\t-'" + CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'" + CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n", + npc_id - START_NPC_NUM, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob); + + malloc_set(script_event, 0, sizeof(script_event)); + npc_read_event_script(); + //Debug function to locate all endless loop warps. + if (battle_config.warp_point_debug) + npc_debug_warps(); + + add_timer_func_list(npc_event_timer,"npc_event_timer"); + add_timer_func_list(npc_event_do_clock,"npc_event_do_clock"); + add_timer_func_list(npc_timerevent,"npc_timerevent"); + + // Init dummy NPC + fake_nd = (struct npc_data *)aCalloc(sizeof(struct npc_data),1); + fake_nd->bl.prev = fake_nd->bl.next = NULL; + fake_nd->bl.m = -1; + fake_nd->bl.x = 0; + fake_nd->bl.y = 0; + fake_nd->bl.id = npc_get_new_npc_id(); + fake_nd->class_ = -1; + fake_nd->speed = 200; + fake_nd->u.scr.script = NULL; + fake_nd->u.scr.src_id = 0; + fake_nd->chatdb = NULL; + for (i = 0; i < MAX_EVENTTIMER; i++) + fake_nd->eventtimer[i] = -1; + strcpy(fake_nd->name,"FAKE_NPC"); + memcpy(fake_nd->exname, fake_nd->name, 9); + + npc_script++; + fake_nd->bl.type = BL_NPC; + fake_nd->bl.subtype = SCRIPT; + + strdb_put(npcname_db, fake_nd->exname, fake_nd); + fake_nd->u.scr.timerid = -1; + map_addiddb(&fake_nd->bl); + // End of initialization + + return 0; +} +// [Lance] +int npc_changename(const char *name, const char *newname, short look){ + struct npc_data *nd= (struct npc_data *) strdb_remove(npcname_db,(unsigned char*)name); + if (nd==NULL) + return 0; + npc_enable(name,0); + strcpy(nd->name,newname); + nd->class_ = look; + npc_enable(newname,1); + return 0; +} diff --git a/src/map/npc.h b/src/map/npc.h index 2ea4f7eb7..63772089c 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -1,90 +1,90 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _NPC_H_ -#define _NPC_H_ - -#define START_NPC_NUM 110000000 - -#define WARP_CLASS 45 -#define WARP_DEBUG_CLASS 722 -#define INVISIBLE_CLASS 32767 - -#define MAX_NPC_CLASS 1000 -//Checks if a given id is a valid npc id. [Skotlex] -//Since new npcs are added all the time, the max valid value is the one before the first mob (Scorpion = 1001) -#define npcdb_checkid(id) ((id >= 46 && id <= 125) || id == 139 || (id >= 700 && id <= MAX_NPC_CLASS) || id == INVISIBLE_CLASS) - -#ifdef PCRE_SUPPORT -void npc_chat_finalize(struct npc_data *nd); -int mob_chat_sub(struct block_list *bl, va_list ap); -#endif - -//Script NPC events. -enum { - NPCE_LOGIN, - NPCE_LOGOUT, - NPCE_LOADMAP, - NPCE_BASELVUP, - NPCE_JOBLVUP, - NPCE_DIE, - NPCE_KILLPC, - NPCE_KILLNPC, - NPCE_MAX -}; -struct view_data* npc_get_viewdata(int class_); -int npc_chat_sub(struct block_list *bl, va_list ap); -int npc_event_dequeue(struct map_session_data *sd); -int npc_event_timer(int tid,unsigned int tick,int id,int data); -int npc_event(struct map_session_data *sd,const unsigned char *npcname,int); -int npc_timer_event(const unsigned char *eventname); // Added by RoVeRT -int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command); -int npc_touch_areanpc(struct map_session_data *,int,int,int); -int npc_touch_areanpc2(struct block_list *bl); // [Skotlex] -int npc_click(struct map_session_data *sd,struct npc_data *nd); -int npc_scriptcont(struct map_session_data *,int); -TBL_NPC *npc_checknear(struct map_session_data *sd,struct block_list *bl); -int npc_checknear2(struct map_session_data *sd,struct block_list *bl); -int npc_buysellsel(struct map_session_data *,int,int); -int npc_buylist(struct map_session_data *,int,unsigned short *); -int npc_selllist(struct map_session_data *,int,unsigned short *); -int npc_parse_mob(char *w1,char *w2,char *w3,char *w4); -int npc_parse_mob2 (struct spawn_data*, int index); // [Wizputer] -int npc_parse_warp(char *w1,char *w2,char *w3,char *w4); -int npc_globalmessage(const char *name,char *mes); - -int npc_enable(const char *name,int flag); -int npc_changename(const char *name, const char *newname, short look); // [Lance] -struct npc_data* npc_name2id(const char *name); - -int npc_get_new_npc_id(void); - -void npc_addsrcfile(char *); -void npc_delsrcfile(char *); -void npc_parsesrcfile(char *); -int do_final_npc(void); -int do_init_npc(void); -int npc_event_do_oninit(void); -int npc_do_ontimer(int,int); - -int npc_event_doall(const unsigned char *name); -int npc_event_do(const unsigned char *name); -int npc_event_doall_id(const unsigned char *name, int id); - -int npc_timerevent_start(struct npc_data *nd, int rid); -int npc_timerevent_stop(struct npc_data *nd); -void npc_timerevent_quit(struct map_session_data *sd); -int npc_gettimerevent_tick(struct npc_data *nd); -int npc_settimerevent_tick(struct npc_data *nd,int newtimer); -int npc_remove_map(struct npc_data *nd); -void npc_unload_duplicates (struct npc_data *nd); -int npc_unload(struct npc_data *nd); -int npc_reload(void); -int npc_script_event(TBL_PC* sd, int type); - -extern char *current_file; - -struct npc_data *fake_nd; - -#endif - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _NPC_H_ +#define _NPC_H_ + +#define START_NPC_NUM 110000000 + +#define WARP_CLASS 45 +#define WARP_DEBUG_CLASS 722 +#define INVISIBLE_CLASS 32767 + +#define MAX_NPC_CLASS 1000 +//Checks if a given id is a valid npc id. [Skotlex] +//Since new npcs are added all the time, the max valid value is the one before the first mob (Scorpion = 1001) +#define npcdb_checkid(id) ((id >= 46 && id <= 125) || id == 139 || (id >= 700 && id <= MAX_NPC_CLASS) || id == INVISIBLE_CLASS) + +#ifdef PCRE_SUPPORT +void npc_chat_finalize(struct npc_data *nd); +int mob_chat_sub(struct block_list *bl, va_list ap); +#endif + +//Script NPC events. +enum { + NPCE_LOGIN, + NPCE_LOGOUT, + NPCE_LOADMAP, + NPCE_BASELVUP, + NPCE_JOBLVUP, + NPCE_DIE, + NPCE_KILLPC, + NPCE_KILLNPC, + NPCE_MAX +}; +struct view_data* npc_get_viewdata(int class_); +int npc_chat_sub(struct block_list *bl, va_list ap); +int npc_event_dequeue(struct map_session_data *sd); +int npc_event_timer(int tid,unsigned int tick,int id,int data); +int npc_event(struct map_session_data *sd,const unsigned char *npcname,int); +int npc_timer_event(const unsigned char *eventname); // Added by RoVeRT +int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command); +int npc_touch_areanpc(struct map_session_data *,int,int,int); +int npc_touch_areanpc2(struct block_list *bl); // [Skotlex] +int npc_click(struct map_session_data *sd,struct npc_data *nd); +int npc_scriptcont(struct map_session_data *,int); +TBL_NPC *npc_checknear(struct map_session_data *sd,struct block_list *bl); +int npc_checknear2(struct map_session_data *sd,struct block_list *bl); +int npc_buysellsel(struct map_session_data *,int,int); +int npc_buylist(struct map_session_data *,int,unsigned short *); +int npc_selllist(struct map_session_data *,int,unsigned short *); +int npc_parse_mob(char *w1,char *w2,char *w3,char *w4); +int npc_parse_mob2 (struct spawn_data*, int index); // [Wizputer] +int npc_parse_warp(char *w1,char *w2,char *w3,char *w4); +int npc_globalmessage(const char *name,char *mes); + +int npc_enable(const char *name,int flag); +int npc_changename(const char *name, const char *newname, short look); // [Lance] +struct npc_data* npc_name2id(const char *name); + +int npc_get_new_npc_id(void); + +void npc_addsrcfile(char *); +void npc_delsrcfile(char *); +void npc_parsesrcfile(char *); +int do_final_npc(void); +int do_init_npc(void); +int npc_event_do_oninit(void); +int npc_do_ontimer(int,int); + +int npc_event_doall(const unsigned char *name); +int npc_event_do(const unsigned char *name); +int npc_event_doall_id(const unsigned char *name, int id); + +int npc_timerevent_start(struct npc_data *nd, int rid); +int npc_timerevent_stop(struct npc_data *nd); +void npc_timerevent_quit(struct map_session_data *sd); +int npc_gettimerevent_tick(struct npc_data *nd); +int npc_settimerevent_tick(struct npc_data *nd,int newtimer); +int npc_remove_map(struct npc_data *nd); +void npc_unload_duplicates (struct npc_data *nd); +int npc_unload(struct npc_data *nd); +int npc_reload(void); +int npc_script_event(TBL_PC* sd, int type); + +extern char *current_file; + +struct npc_data *fake_nd; + +#endif + diff --git a/src/map/npc_chat.c b/src/map/npc_chat.c index 1e5da1277..f7c955257 100644 --- a/src/map/npc_chat.c +++ b/src/map/npc_chat.c @@ -1,517 +1,517 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifdef PCRE_SUPPORT - -#include -#include -#include -#include -#include -#include - -#include "../common/timer.h" -#include "../common/malloc.h" -#include "../common/version.h" -#include "../common/nullpo.h" -#include "../common/showmsg.h" - -#include "map.h" -#include "status.h" -#include "npc.h" -#include "chat.h" -#include "script.h" -#include "battle.h" - -#include "pcre.h" - -/** - * Written by MouseJstr in a vision... (2/21/2005) - * - * This allows you to make npc listen for spoken text (global - * messages) and pattern match against that spoken text using perl - * regular expressions. - * - * Please feel free to copy this code into your own personal ragnarok - * servers or distributions but please leave my name. Also, please - * wait until I've put it into the main eA branch which means I - * believe it is ready for distribution. - * - * So, how do people use this? - * - * The first and most important function is defpattern - * - * defpattern 1, "[^:]+: (.*) loves (.*)", "label"; - * - * this defines a new pattern in set 1 using perl syntax - * (http://www.troubleshooters.com/codecorn/littperl/perlreg.htm) - * and tells it to jump to the supplied label when the pattern - * is matched. - * - * each of the matched Groups will result in a variable being - * set ($p1$ through $p9$ with $p0$ being the entire string) - * before the script gets executed. - * - * activatepset 1; - * - * This activates a set of patterns.. You can have many pattern - * sets defined and many active all at once. This feature allows - * you to set up "conversations" and ever changing expectations of - * the pattern matcher - * - * deactivatepset 1; - * - * turns off a pattern set; - * - * deactivatepset -1; - * - * turns off ALL pattern sets; - * - * deletepset 1; - * - * deletes a pset - */ - -/* Structure containing all info associated with a single pattern - block */ - -struct pcrematch_entry { - struct pcrematch_entry *next_; - char *pattern_; - pcre *pcre_; - pcre_extra *pcre_extra_; - char *label_; -}; - -/* A set of patterns that can be activated and deactived with a single - command */ - -struct pcrematch_set { - struct pcrematch_set *next_, *prev_; - struct pcrematch_entry *head_; - int setid_; -}; - -/* - * Entire data structure hung off a NPC - * - * The reason I have done it this way (a void * in npc_data and then - * this) was to reduce the number of patches that needed to be applied - * to a ragnarok distribution to bring this code online. I - * also wanted people to be able to grab this one file to get updates - * without having to do a large number of changes. - */ - -struct npc_parse { - struct pcrematch_set *active_; - struct pcrematch_set *inactive_; -}; - - -/** - * delete everythign associated with a entry - * - * This does NOT do the list management - */ - -void finalize_pcrematch_entry(struct pcrematch_entry *e) { -//TODO: For some odd reason this causes a already-free'd error under Windows, but not *nix! [Skotlex] -#ifndef _WIN32 - if (e->pcre_) { - free(e->pcre_); - e->pcre_ = NULL; - } -#endif - if (e->pcre_extra_) { - free(e->pcre_extra_); - e->pcre_ = NULL; - } - aFree(e->pattern_); - aFree(e->label_); -} - -/** - * Lookup (and possibly create) a new set of patterns by the set id - */ -static struct pcrematch_set * lookup_pcreset(struct npc_data *nd,int setid) -{ - struct pcrematch_set *pcreset; - struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; - if (npcParse == NULL) - nd->chatdb = npcParse = (struct npc_parse *) - aCalloc(sizeof(struct npc_parse), 1); - - pcreset = npcParse->active_; - - while (pcreset != NULL) { - if (pcreset->setid_ == setid) - break; - pcreset = pcreset->next_; - } - if (pcreset == NULL) - pcreset = npcParse->inactive_; - - while (pcreset != NULL) { - if (pcreset->setid_ == setid) - break; - pcreset = pcreset->next_; - } - - if (pcreset == NULL) { - pcreset = (struct pcrematch_set *) - aCalloc(sizeof(struct pcrematch_set), 1); - pcreset->next_ = npcParse->inactive_; - if (pcreset->next_ != NULL) - pcreset->next_->prev_ = pcreset; - pcreset->prev_ = 0; - npcParse->inactive_ = pcreset; - pcreset->setid_ = setid; - } - - return pcreset; -} - -/** - * activate a set of patterns. - * - * if the setid does not exist, this will silently return - */ - -static void activate_pcreset(struct npc_data *nd,int setid) { - struct pcrematch_set *pcreset; - struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; - if (npcParse == NULL) - return; // Nothing to activate... - pcreset = npcParse->inactive_; - while (pcreset != NULL) { - if (pcreset->setid_ == setid) - break; - pcreset = pcreset->next_; - } - if (pcreset == NULL) - return; // not in inactive list - if (pcreset->next_ != NULL) - pcreset->next_->prev_ = pcreset->prev_; - if (pcreset->prev_ != NULL) - pcreset->prev_->next_ = pcreset->next_; - else - npcParse->inactive_ = pcreset->next_; - - pcreset->prev_ = NULL; - pcreset->next_ = npcParse->active_; - if (pcreset->next_ != NULL) - pcreset->next_->prev_ = pcreset; - npcParse->active_ = pcreset; -} - -/** - * deactivate a set of patterns. - * - * if the setid does not exist, this will silently return - */ - -static void deactivate_pcreset(struct npc_data *nd,int setid) { - struct pcrematch_set *pcreset; - struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; - if (npcParse == NULL) - return; // Nothing to deactivate... - if (setid == -1) { - while(npcParse->active_ != NULL) - deactivate_pcreset(nd, npcParse->active_->setid_); - return; - } - pcreset = npcParse->active_; - while (pcreset != NULL) { - if (pcreset->setid_ == setid) - break; - pcreset = pcreset->next_; - } - if (pcreset == NULL) - return; // not in active list - if (pcreset->next_ != NULL) - pcreset->next_->prev_ = pcreset->prev_; - if (pcreset->prev_ != NULL) - pcreset->prev_->next_ = pcreset->next_; - else - npcParse->active_ = pcreset->next_; - - pcreset->prev_ = NULL; - pcreset->next_ = npcParse->inactive_; - if (pcreset->next_ != NULL) - pcreset->next_->prev_ = pcreset; - npcParse->inactive_ = pcreset; -} - -/** - * delete a set of patterns. - */ -static void delete_pcreset(struct npc_data *nd,int setid) { - int active = 1; - struct pcrematch_set *pcreset; - struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; - if (npcParse == NULL) - return; // Nothing to deactivate... - pcreset = npcParse->active_; - while (pcreset != NULL) { - if (pcreset->setid_ == setid) - break; - pcreset = pcreset->next_; - } - if (pcreset == NULL) { - active = 0; - pcreset = npcParse->inactive_; - while (pcreset != NULL) { - if (pcreset->setid_ == setid) - break; - pcreset = pcreset->next_; - } - } - if (pcreset == NULL) - return; - - if (pcreset->next_ != NULL) - pcreset->next_->prev_ = pcreset->prev_; - if (pcreset->prev_ != NULL) - pcreset->prev_->next_ = pcreset->next_; - - if(active) - npcParse->active_ = pcreset->next_; - else - npcParse->inactive_ = pcreset->next_; - - pcreset->prev_ = NULL; - pcreset->next_ = NULL; - - while (pcreset->head_) { - struct pcrematch_entry *n = pcreset->head_->next_; - finalize_pcrematch_entry(pcreset->head_); - aFree(pcreset->head_); // Cleanin' the last ones.. [Lance] - pcreset->head_ = n; - } - - aFree(pcreset); -} - -/** - * create a new pattern entry - */ -static struct pcrematch_entry *create_pcrematch_entry(struct pcrematch_set * set) { - struct pcrematch_entry * e = (struct pcrematch_entry *) - aCalloc(sizeof(struct pcrematch_entry), 1); - struct pcrematch_entry * last = set->head_; - - // Normally we would have just stuck it at the end of the list but - // this doesn't sink up with peoples usage pattern. They wanted - // the items defined first to have a higher priority then the - // items defined later.. as a result, we have to do some work up - // front.. - - /* if we are the first pattern, stick us at the end */ - if (last == NULL) { - set->head_ = e; - return e; - } - - /* Look for the last entry */ - while (last->next_ != NULL) - last = last->next_; - - last->next_ = e; - e->next_ = NULL; - - return e; -} - -/** - * define/compile a new pattern - */ - -void npc_chat_def_pattern(struct npc_data *nd, int setid, - const char *pattern, const char *label) -{ - const char *err; - int erroff; - - struct pcrematch_set * s = lookup_pcreset(nd, setid); - struct pcrematch_entry *e = create_pcrematch_entry(s); - e->pattern_ = aStrdup(pattern); - e->label_ = aStrdup(label); - e->pcre_ = pcre_compile(pattern, PCRE_CASELESS, &err, &erroff, NULL); - e->pcre_extra_ = pcre_study(e->pcre_, 0, &err); -} - -/** - * Delete everything associated with a NPC concerning the pattern - * matching code - * - * this could be more efficent but.. how often do you do this? - */ -void npc_chat_finalize(struct npc_data *nd) -{ - struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; - if (npcParse == NULL) - return; - - while(npcParse->active_) - delete_pcreset(nd, npcParse->active_->setid_); - - while(npcParse->inactive_) - delete_pcreset(nd, npcParse->inactive_->setid_); - - // Additional cleaning up [Lance] - aFree(npcParse); -} - -/** - * Handler called whenever a global message is spoken in a NPC's area - */ -int npc_chat_sub(struct block_list *bl, va_list ap) -{ - struct npc_data *nd = (struct npc_data *)bl; - struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; - unsigned char *msg; - int len, pos, i; - struct map_session_data *sd; - struct npc_label_list *lst; - struct pcrematch_set *pcreset; - - // Not interested in anything you might have to say... - if (npcParse == NULL || npcParse->active_ == NULL) - return 0; - - msg = va_arg(ap,unsigned char*); - len = va_arg(ap,int); - sd = va_arg(ap,struct map_session_data *); - - // grab the active list - pcreset = npcParse->active_; - - // interate across all active sets - while (pcreset != NULL) { - struct pcrematch_entry *e = pcreset->head_; - // interate across all patterns in that set - while (e != NULL) { - int offsets[20]; - char buf[255]; - // perform pattern match - int r = pcre_exec(e->pcre_, e->pcre_extra_, msg, len, 0, - 0, offsets, sizeof(offsets) / sizeof(offsets[0])); - if (r >= 0) { - // save out the matched strings - switch (r) { - case 10: - memcpy(buf, &msg[offsets[18]], offsets[19]); - buf[offsets[19]] = '\0'; - set_var(sd, "$p9$", buf); - case 9: - memcpy(buf, &msg[offsets[16]], offsets[17]); - buf[offsets[17]] = '\0'; - set_var(sd, "$p8$", buf); - case 8: - memcpy(buf, &msg[offsets[14]], offsets[15]); - buf[offsets[15]] = '\0'; - set_var(sd, "$p7$", buf); - case 7: - memcpy(buf, &msg[offsets[12]], offsets[13]); - buf[offsets[13]] = '\0'; - set_var(sd, "$p6$", buf); - case 6: - memcpy(buf, &msg[offsets[10]], offsets[11]); - buf[offsets[11]] = '\0'; - set_var(sd, "$p5$", buf); - case 5: - memcpy(buf, &msg[offsets[8]], offsets[9]); - buf[offsets[9]] = '\0'; - set_var(sd, "$p4$", buf); - case 4: - memcpy(buf, &msg[offsets[6]], offsets[7]); - buf[offsets[7]] = '\0'; - set_var(sd, "$p3$", buf); - case 3: - memcpy(buf, &msg[offsets[4]], offsets[5]); - buf[offsets[5]] = '\0'; - set_var(sd, "$p2$", buf); - case 2: - memcpy(buf, &msg[offsets[2]], offsets[3]); - buf[offsets[3]] = '\0'; - set_var(sd, "$p1$", buf); - case 1: - memcpy(buf, &msg[offsets[0]], offsets[1]); - buf[offsets[1]] = '\0'; - set_var(sd, "$p0$", buf); - } - - // find the target label.. this sucks.. - lst=nd->u.scr.label_list; - pos = -1; - for (i = 0; i < nd->u.scr.label_list_num; i++) { - if (strncmp(lst[i].name, e->label_, sizeof(lst[i].name)) == 0) { - pos = lst[i].pos; - break; - } - } - if (pos == -1) { - ShowWarning("Unable to find label: %s", e->label_); - // unable to find label... do something.. - return 0; - } - // run the npc script - run_script(nd->u.scr.script,pos,sd->bl.id,nd->bl.id); - return 0; - } - e = e->next_; - } - pcreset = pcreset->next_; - } - - return 0; -} - -int mob_chat_sub(struct block_list *bl, va_list ap){ - struct mob_data *md = (struct mob_data *)bl; - if(md->nd){ - npc_chat_sub(&md->nd->bl, ap); - } - return 0; -} - -// Various script builtins used to support these functions - -int buildin_defpattern(struct script_state *st) { - int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); - char *pattern=conv_str(st,& (st->stack->stack_data[st->start+3])); - char *label=conv_str(st,& (st->stack->stack_data[st->start+4])); - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - - npc_chat_def_pattern(nd, setid, pattern, label); - - return 0; -} - -int buildin_activatepset(struct script_state *st) { - int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - - activate_pcreset(nd, setid); - - return 0; -} -int buildin_deactivatepset(struct script_state *st) { - int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - - deactivate_pcreset(nd, setid); - - return 0; -} -int buildin_deletepset(struct script_state *st) { - int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - - delete_pcreset(nd, setid); - - return 0; -} - - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifdef PCRE_SUPPORT + +#include +#include +#include +#include +#include +#include + +#include "../common/timer.h" +#include "../common/malloc.h" +#include "../common/version.h" +#include "../common/nullpo.h" +#include "../common/showmsg.h" + +#include "map.h" +#include "status.h" +#include "npc.h" +#include "chat.h" +#include "script.h" +#include "battle.h" + +#include "pcre.h" + +/** + * Written by MouseJstr in a vision... (2/21/2005) + * + * This allows you to make npc listen for spoken text (global + * messages) and pattern match against that spoken text using perl + * regular expressions. + * + * Please feel free to copy this code into your own personal ragnarok + * servers or distributions but please leave my name. Also, please + * wait until I've put it into the main eA branch which means I + * believe it is ready for distribution. + * + * So, how do people use this? + * + * The first and most important function is defpattern + * + * defpattern 1, "[^:]+: (.*) loves (.*)", "label"; + * + * this defines a new pattern in set 1 using perl syntax + * (http://www.troubleshooters.com/codecorn/littperl/perlreg.htm) + * and tells it to jump to the supplied label when the pattern + * is matched. + * + * each of the matched Groups will result in a variable being + * set ($p1$ through $p9$ with $p0$ being the entire string) + * before the script gets executed. + * + * activatepset 1; + * + * This activates a set of patterns.. You can have many pattern + * sets defined and many active all at once. This feature allows + * you to set up "conversations" and ever changing expectations of + * the pattern matcher + * + * deactivatepset 1; + * + * turns off a pattern set; + * + * deactivatepset -1; + * + * turns off ALL pattern sets; + * + * deletepset 1; + * + * deletes a pset + */ + +/* Structure containing all info associated with a single pattern + block */ + +struct pcrematch_entry { + struct pcrematch_entry *next_; + char *pattern_; + pcre *pcre_; + pcre_extra *pcre_extra_; + char *label_; +}; + +/* A set of patterns that can be activated and deactived with a single + command */ + +struct pcrematch_set { + struct pcrematch_set *next_, *prev_; + struct pcrematch_entry *head_; + int setid_; +}; + +/* + * Entire data structure hung off a NPC + * + * The reason I have done it this way (a void * in npc_data and then + * this) was to reduce the number of patches that needed to be applied + * to a ragnarok distribution to bring this code online. I + * also wanted people to be able to grab this one file to get updates + * without having to do a large number of changes. + */ + +struct npc_parse { + struct pcrematch_set *active_; + struct pcrematch_set *inactive_; +}; + + +/** + * delete everythign associated with a entry + * + * This does NOT do the list management + */ + +void finalize_pcrematch_entry(struct pcrematch_entry *e) { +//TODO: For some odd reason this causes a already-free'd error under Windows, but not *nix! [Skotlex] +#ifndef _WIN32 + if (e->pcre_) { + free(e->pcre_); + e->pcre_ = NULL; + } +#endif + if (e->pcre_extra_) { + free(e->pcre_extra_); + e->pcre_ = NULL; + } + aFree(e->pattern_); + aFree(e->label_); +} + +/** + * Lookup (and possibly create) a new set of patterns by the set id + */ +static struct pcrematch_set * lookup_pcreset(struct npc_data *nd,int setid) +{ + struct pcrematch_set *pcreset; + struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; + if (npcParse == NULL) + nd->chatdb = npcParse = (struct npc_parse *) + aCalloc(sizeof(struct npc_parse), 1); + + pcreset = npcParse->active_; + + while (pcreset != NULL) { + if (pcreset->setid_ == setid) + break; + pcreset = pcreset->next_; + } + if (pcreset == NULL) + pcreset = npcParse->inactive_; + + while (pcreset != NULL) { + if (pcreset->setid_ == setid) + break; + pcreset = pcreset->next_; + } + + if (pcreset == NULL) { + pcreset = (struct pcrematch_set *) + aCalloc(sizeof(struct pcrematch_set), 1); + pcreset->next_ = npcParse->inactive_; + if (pcreset->next_ != NULL) + pcreset->next_->prev_ = pcreset; + pcreset->prev_ = 0; + npcParse->inactive_ = pcreset; + pcreset->setid_ = setid; + } + + return pcreset; +} + +/** + * activate a set of patterns. + * + * if the setid does not exist, this will silently return + */ + +static void activate_pcreset(struct npc_data *nd,int setid) { + struct pcrematch_set *pcreset; + struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; + if (npcParse == NULL) + return; // Nothing to activate... + pcreset = npcParse->inactive_; + while (pcreset != NULL) { + if (pcreset->setid_ == setid) + break; + pcreset = pcreset->next_; + } + if (pcreset == NULL) + return; // not in inactive list + if (pcreset->next_ != NULL) + pcreset->next_->prev_ = pcreset->prev_; + if (pcreset->prev_ != NULL) + pcreset->prev_->next_ = pcreset->next_; + else + npcParse->inactive_ = pcreset->next_; + + pcreset->prev_ = NULL; + pcreset->next_ = npcParse->active_; + if (pcreset->next_ != NULL) + pcreset->next_->prev_ = pcreset; + npcParse->active_ = pcreset; +} + +/** + * deactivate a set of patterns. + * + * if the setid does not exist, this will silently return + */ + +static void deactivate_pcreset(struct npc_data *nd,int setid) { + struct pcrematch_set *pcreset; + struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; + if (npcParse == NULL) + return; // Nothing to deactivate... + if (setid == -1) { + while(npcParse->active_ != NULL) + deactivate_pcreset(nd, npcParse->active_->setid_); + return; + } + pcreset = npcParse->active_; + while (pcreset != NULL) { + if (pcreset->setid_ == setid) + break; + pcreset = pcreset->next_; + } + if (pcreset == NULL) + return; // not in active list + if (pcreset->next_ != NULL) + pcreset->next_->prev_ = pcreset->prev_; + if (pcreset->prev_ != NULL) + pcreset->prev_->next_ = pcreset->next_; + else + npcParse->active_ = pcreset->next_; + + pcreset->prev_ = NULL; + pcreset->next_ = npcParse->inactive_; + if (pcreset->next_ != NULL) + pcreset->next_->prev_ = pcreset; + npcParse->inactive_ = pcreset; +} + +/** + * delete a set of patterns. + */ +static void delete_pcreset(struct npc_data *nd,int setid) { + int active = 1; + struct pcrematch_set *pcreset; + struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; + if (npcParse == NULL) + return; // Nothing to deactivate... + pcreset = npcParse->active_; + while (pcreset != NULL) { + if (pcreset->setid_ == setid) + break; + pcreset = pcreset->next_; + } + if (pcreset == NULL) { + active = 0; + pcreset = npcParse->inactive_; + while (pcreset != NULL) { + if (pcreset->setid_ == setid) + break; + pcreset = pcreset->next_; + } + } + if (pcreset == NULL) + return; + + if (pcreset->next_ != NULL) + pcreset->next_->prev_ = pcreset->prev_; + if (pcreset->prev_ != NULL) + pcreset->prev_->next_ = pcreset->next_; + + if(active) + npcParse->active_ = pcreset->next_; + else + npcParse->inactive_ = pcreset->next_; + + pcreset->prev_ = NULL; + pcreset->next_ = NULL; + + while (pcreset->head_) { + struct pcrematch_entry *n = pcreset->head_->next_; + finalize_pcrematch_entry(pcreset->head_); + aFree(pcreset->head_); // Cleanin' the last ones.. [Lance] + pcreset->head_ = n; + } + + aFree(pcreset); +} + +/** + * create a new pattern entry + */ +static struct pcrematch_entry *create_pcrematch_entry(struct pcrematch_set * set) { + struct pcrematch_entry * e = (struct pcrematch_entry *) + aCalloc(sizeof(struct pcrematch_entry), 1); + struct pcrematch_entry * last = set->head_; + + // Normally we would have just stuck it at the end of the list but + // this doesn't sink up with peoples usage pattern. They wanted + // the items defined first to have a higher priority then the + // items defined later.. as a result, we have to do some work up + // front.. + + /* if we are the first pattern, stick us at the end */ + if (last == NULL) { + set->head_ = e; + return e; + } + + /* Look for the last entry */ + while (last->next_ != NULL) + last = last->next_; + + last->next_ = e; + e->next_ = NULL; + + return e; +} + +/** + * define/compile a new pattern + */ + +void npc_chat_def_pattern(struct npc_data *nd, int setid, + const char *pattern, const char *label) +{ + const char *err; + int erroff; + + struct pcrematch_set * s = lookup_pcreset(nd, setid); + struct pcrematch_entry *e = create_pcrematch_entry(s); + e->pattern_ = aStrdup(pattern); + e->label_ = aStrdup(label); + e->pcre_ = pcre_compile(pattern, PCRE_CASELESS, &err, &erroff, NULL); + e->pcre_extra_ = pcre_study(e->pcre_, 0, &err); +} + +/** + * Delete everything associated with a NPC concerning the pattern + * matching code + * + * this could be more efficent but.. how often do you do this? + */ +void npc_chat_finalize(struct npc_data *nd) +{ + struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; + if (npcParse == NULL) + return; + + while(npcParse->active_) + delete_pcreset(nd, npcParse->active_->setid_); + + while(npcParse->inactive_) + delete_pcreset(nd, npcParse->inactive_->setid_); + + // Additional cleaning up [Lance] + aFree(npcParse); +} + +/** + * Handler called whenever a global message is spoken in a NPC's area + */ +int npc_chat_sub(struct block_list *bl, va_list ap) +{ + struct npc_data *nd = (struct npc_data *)bl; + struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb; + unsigned char *msg; + int len, pos, i; + struct map_session_data *sd; + struct npc_label_list *lst; + struct pcrematch_set *pcreset; + + // Not interested in anything you might have to say... + if (npcParse == NULL || npcParse->active_ == NULL) + return 0; + + msg = va_arg(ap,unsigned char*); + len = va_arg(ap,int); + sd = va_arg(ap,struct map_session_data *); + + // grab the active list + pcreset = npcParse->active_; + + // interate across all active sets + while (pcreset != NULL) { + struct pcrematch_entry *e = pcreset->head_; + // interate across all patterns in that set + while (e != NULL) { + int offsets[20]; + char buf[255]; + // perform pattern match + int r = pcre_exec(e->pcre_, e->pcre_extra_, msg, len, 0, + 0, offsets, sizeof(offsets) / sizeof(offsets[0])); + if (r >= 0) { + // save out the matched strings + switch (r) { + case 10: + memcpy(buf, &msg[offsets[18]], offsets[19]); + buf[offsets[19]] = '\0'; + set_var(sd, "$p9$", buf); + case 9: + memcpy(buf, &msg[offsets[16]], offsets[17]); + buf[offsets[17]] = '\0'; + set_var(sd, "$p8$", buf); + case 8: + memcpy(buf, &msg[offsets[14]], offsets[15]); + buf[offsets[15]] = '\0'; + set_var(sd, "$p7$", buf); + case 7: + memcpy(buf, &msg[offsets[12]], offsets[13]); + buf[offsets[13]] = '\0'; + set_var(sd, "$p6$", buf); + case 6: + memcpy(buf, &msg[offsets[10]], offsets[11]); + buf[offsets[11]] = '\0'; + set_var(sd, "$p5$", buf); + case 5: + memcpy(buf, &msg[offsets[8]], offsets[9]); + buf[offsets[9]] = '\0'; + set_var(sd, "$p4$", buf); + case 4: + memcpy(buf, &msg[offsets[6]], offsets[7]); + buf[offsets[7]] = '\0'; + set_var(sd, "$p3$", buf); + case 3: + memcpy(buf, &msg[offsets[4]], offsets[5]); + buf[offsets[5]] = '\0'; + set_var(sd, "$p2$", buf); + case 2: + memcpy(buf, &msg[offsets[2]], offsets[3]); + buf[offsets[3]] = '\0'; + set_var(sd, "$p1$", buf); + case 1: + memcpy(buf, &msg[offsets[0]], offsets[1]); + buf[offsets[1]] = '\0'; + set_var(sd, "$p0$", buf); + } + + // find the target label.. this sucks.. + lst=nd->u.scr.label_list; + pos = -1; + for (i = 0; i < nd->u.scr.label_list_num; i++) { + if (strncmp(lst[i].name, e->label_, sizeof(lst[i].name)) == 0) { + pos = lst[i].pos; + break; + } + } + if (pos == -1) { + ShowWarning("Unable to find label: %s", e->label_); + // unable to find label... do something.. + return 0; + } + // run the npc script + run_script(nd->u.scr.script,pos,sd->bl.id,nd->bl.id); + return 0; + } + e = e->next_; + } + pcreset = pcreset->next_; + } + + return 0; +} + +int mob_chat_sub(struct block_list *bl, va_list ap){ + struct mob_data *md = (struct mob_data *)bl; + if(md->nd){ + npc_chat_sub(&md->nd->bl, ap); + } + return 0; +} + +// Various script builtins used to support these functions + +int buildin_defpattern(struct script_state *st) { + int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); + char *pattern=conv_str(st,& (st->stack->stack_data[st->start+3])); + char *label=conv_str(st,& (st->stack->stack_data[st->start+4])); + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + + npc_chat_def_pattern(nd, setid, pattern, label); + + return 0; +} + +int buildin_activatepset(struct script_state *st) { + int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + + activate_pcreset(nd, setid); + + return 0; +} +int buildin_deactivatepset(struct script_state *st) { + int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + + deactivate_pcreset(nd, setid); + + return 0; +} +int buildin_deletepset(struct script_state *st) { + int setid=conv_num(st,& (st->stack->stack_data[st->start+2])); + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + + delete_pcreset(nd, setid); + + return 0; +} + + +#endif diff --git a/src/map/party.c b/src/map/party.c index 6c97bd464..babc9129d 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -1,902 +1,902 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include - -#include "../common/timer.h" -#include "../common/socket.h" -#include "../common/nullpo.h" -#include "../common/malloc.h" -#include "../common/showmsg.h" - -#include "party.h" -#include "pc.h" -#include "map.h" -#include "battle.h" -#include "intif.h" -#include "clif.h" -#include "log.h" -#include "skill.h" -#include "status.h" - -static struct dbt* party_db; -static struct party_data* party_cache = NULL; //party in cache for skipping consecutive lookups. [Skotlex] -int party_share_level = 10; -int party_send_xy_timer(int tid,unsigned int tick,int id,int data); - -/*========================================== - * Fills the given party_member structure according to the sd provided. - * Used when creating/adding people to a party. [Skotlex] - *------------------------------------------ - */ -static void party_fill_member(struct party_member *member, struct map_session_data *sd) { - member->account_id = sd->status.account_id; - member->char_id = sd->status.char_id; - memcpy(member->name,sd->status.name,NAME_LENGTH); - member->class_ = sd->status.class_; - member->map = sd->mapindex; - member->lv = sd->status.base_level; - member->online = 1; - member->leader = 0; -} - -/*========================================== - * 終了 - *------------------------------------------ - */ -void do_final_party(void) -{ - party_db->destroy(party_db,NULL); -} -// 初期化 -void do_init_party(void) -{ - party_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - add_timer_func_list(party_send_xy_timer,"party_send_xy_timer"); - add_timer_interval(gettick()+battle_config.party_update_interval,party_send_xy_timer,0,0,battle_config.party_update_interval); -} - -// 検索 -struct party_data *party_search(int party_id) -{ - if(!party_id) return NULL; - if (party_cache && party_cache->party.party_id == party_id) - return party_cache; - - party_cache = idb_get(party_db,party_id); - return party_cache; -} -int party_searchname_sub(DBKey key,void *data,va_list ap) -{ - struct party_data *p=(struct party_data *)data,**dst; - char *str; - str=va_arg(ap,char *); - dst=va_arg(ap,struct party_data **); - if(strncmpi(p->party.name,str,NAME_LENGTH)==0) - *dst=p; - return 0; -} - -struct party_data* party_searchname(char *str) -{ - struct party_data *p=NULL; - party_db->foreach(party_db,party_searchname_sub,str,&p); - return p; -} - -int party_create(struct map_session_data *sd,char *name,int item,int item2) -{ - struct party_member leader; - nullpo_retr(0, sd); - - if(sd->status.party_id) { - clif_party_created(sd,2); - return 0; - } - - party_fill_member(&leader, sd); - leader.leader = 1; - - intif_create_party(&leader,name,item,item2); - return 0; -} - - -int party_created(int account_id,int char_id,int fail,int party_id,char *name) -{ - struct map_session_data *sd; - struct party_data *p; - sd=map_id2sd(account_id); - - nullpo_retr(0, sd); - if (sd->status.char_id != char_id) - return 0; //unlikely failure... - - if(fail){ - clif_party_created(sd,1); - return 0; - } - sd->status.party_id=party_id; - if(idb_get(party_db,party_id)!=NULL){ - ShowFatalError("party: id already exists!\n"); - exit(1); - } - p=(struct party_data *)aCalloc(1,sizeof(struct party_data)); - p->party.party_id=party_id; - memcpy(p->party.name, name, NAME_LENGTH); - idb_put(party_db,party_id,p); - clif_party_created(sd,0); //Success message - clif_charnameupdate(sd); //Update other people's display. [Skotlex] - return 1; -} - -int party_request_info(int party_id) -{ - return intif_request_partyinfo(party_id); -} - -int party_check_member(struct party *p) -{ - int i, users; - struct map_session_data *sd, **all_sd; - - nullpo_retr(0, p); - - all_sd = map_getallusers(&users); - - for(i=0;istatus.party_id==p->party_id) - { - int j,f=1; - for(j=0;jmember[j].account_id==sd->status.account_id && - p->member[j].char_id==sd->status.char_id) - { - f=0; - break; - } - } - - if(f){ - sd->status.party_id=0; - if(battle_config.error_log) - ShowWarning("party: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name); - } - } - } - return 0; -} - -int party_recv_noinfo(int party_id) -{ - int i, users; - struct map_session_data *sd, **all_sd; - - all_sd = map_getallusers(&users); - - for(i=0;istatus.party_id==party_id) - sd->status.party_id=0; - } - return 0; -} - -static void* create_party(DBKey key, va_list args) { - struct party_data *p; - p=(struct party_data *)aCalloc(1,sizeof(struct party_data)); - return p; -} - -static void party_check_state(struct party_data *p) -{ - int i; - malloc_set(&p->state, 0, sizeof(p->state)); - for (i = 0; i < MAX_PARTY; i ++) - { - if (!p->party.member[i].online) continue; //Those not online shouldn't aport to skill usage and all that. - switch (p->party.member[i].class_) { - case JOB_MONK: - case JOB_BABY_MONK: - case JOB_CHAMPION: - p->state.monk = 1; - break; - case JOB_STAR_GLADIATOR: - p->state.sg = 1; - break; - case JOB_SUPER_NOVICE: - case JOB_SUPER_BABY: - p->state.snovice = 1; - break; - case JOB_TAEKWON: - p->state.tk = 1; - break; - } - } -} - -int party_recv_info(struct party *sp) -{ - struct map_session_data *sd; - struct party_data *p; - int i; - - nullpo_retr(0, sp); - - p= idb_ensure(party_db, sp->party_id, create_party); - if (!p->party.party_id) //party just received. - party_check_member(sp); - memcpy(&p->party,sp,sizeof(struct party)); - malloc_set(&p->state, 0, sizeof(p->state)); - malloc_set(&p->data, 0, sizeof(p->data)); - for(i=0;iparty.member[i].account_id) - continue; - sd = map_id2sd(p->party.member[i].account_id); - if (sd && sd->status.party_id==p->party.party_id - && sd->status.char_id == p->party.member[i].char_id - && !sd->state.waitingdisconnect) - p->data[i].sd = sd; - } - party_check_state(p); - for(i=0;idata[i].sd; - if(!sd || sd->state.party_sent) - continue; - clif_party_main_info(p,-1); - clif_party_option(p,sd,0x100); - clif_party_info(p,-1); - sd->state.party_sent=1; - } - - return 0; -} - -int party_invite(struct map_session_data *sd,struct map_session_data *tsd) -{ - struct party_data *p=party_search(sd->status.party_id); - int i,flag=0; - - nullpo_retr(0, sd); - - if(tsd==NULL || p==NULL) - return 0; - if(!battle_config.invite_request_check) { - if (tsd->guild_invite>0 || tsd->trade_partner) { - clif_party_inviteack(sd,tsd->status.name,0); - return 0; - } - } - if( tsd->status.party_id>0 || tsd->party_invite>0 ){ - clif_party_inviteack(sd,tsd->status.name,0); - return 0; - } - for(i=0;iparty.member[i].account_id == 0) //Room for a new member. - flag = 1; - /* By default Aegis BLOCKS more than one char from the same account on a party. - * But eA does support it... so this check is left commented. - if(p->party.member[i].account_id==tsd->status.account_id) - { - clif_party_inviteack(sd,tsd->status.name,4); - return 0; - } - */ - } - if (!flag) { //Full party. - clif_party_inviteack(sd,tsd->status.name,3); - return 0; - } - - tsd->party_invite=sd->status.party_id; - tsd->party_invite_account=sd->status.account_id; - - clif_party_invite(sd,tsd); - return 1; -} - -int party_reply_invite(struct map_session_data *sd,int account_id,int flag) -{ - struct map_session_data *tsd= map_id2sd(account_id); - struct party_member member; - nullpo_retr(0, sd); - - if(flag==1){ - party_fill_member(&member, sd); - intif_party_addmember(sd->party_invite, &member); - return 0; - } - sd->party_invite=0; - sd->party_invite_account=0; - if(tsd==NULL) - return 0; - clif_party_inviteack(tsd,sd->status.name,1); - return 1; -} - -int party_member_added(int party_id,int account_id,int char_id, int flag) -{ - struct map_session_data *sd = map_id2sd(account_id),*sd2; - struct party_data *p = party_search(party_id); - if(sd == NULL || sd->status.char_id != char_id){ - if (flag == 0) { - if(battle_config.error_log) - ShowError("party: member added error %d is not online\n",account_id); - intif_party_leave(party_id,account_id,char_id); - } - return 0; - } - sd->party_invite=0; - sd->party_invite_account=0; - - if (!p) { - if(battle_config.error_log) - ShowError("party_member_added: party %d not found.\n",party_id); - intif_party_leave(party_id,account_id,char_id); - return 0; - } - - if(!flag) { - sd->state.party_sent=0; - sd->status.party_id=party_id; - party_check_conflict(sd); - clif_party_join_info(&p->party,sd); - clif_party_hp(sd); - clif_party_xy(sd); - clif_charnameupdate(sd); //Update char name's display [Skotlex] - } - - sd2=map_id2sd(sd->party_invite_account); - if (sd2) - clif_party_inviteack(sd2,sd->status.name,flag?2:1); - return 0; -} - -int party_removemember(struct map_session_data *sd,int account_id,char *name) -{ - struct party_data *p; - int i; - - nullpo_retr(0, sd); - - if( (p = party_search(sd->status.party_id)) == NULL ) - return 0; - - for(i=0;iparty.member[i].account_id==sd->status.account_id && - p->party.member[i].char_id==sd->status.char_id) { - if(p->party.member[i].leader) - break; - return 0; - } - } - if (i >= MAX_PARTY) //Request from someone not in party? o.O - return 0; - - for(i=0;iparty.member[i].account_id==account_id && - strncmp(p->party.member[i].name,name,NAME_LENGTH)==0) - { - intif_party_leave(p->party.party_id,account_id,p->party.member[i].char_id); - return 1; - } - } - return 0; -} - -int party_leave(struct map_session_data *sd) -{ - struct party_data *p; - int i; - - nullpo_retr(0, sd); - - if( (p = party_search(sd->status.party_id)) == NULL ) - return 0; - - for(i=0;iparty.member[i].account_id==sd->status.account_id && - p->party.member[i].char_id==sd->status.char_id){ - intif_party_leave(p->party.party_id,sd->status.account_id,sd->status.char_id); - return 0; - } - } - return 0; -} - -int party_member_leaved(int party_id,int account_id,int char_id) -{ - struct map_session_data *sd=map_id2sd(account_id); - struct party_data *p=party_search(party_id); - int i; - if (sd && sd->status.char_id != char_id) //Wrong target - sd = NULL; - if(p!=NULL){ - for(i=0;iparty.member[i].account_id==account_id && - p->party.member[i].char_id==char_id){ - clif_party_leaved(p,sd,account_id,p->party.member[i].name,0x00); - malloc_set(&p->party.member[i], 0, sizeof(p->party.member[0])); - malloc_set(&p->data[i], 0, sizeof(p->data[0])); - p->party.count--; - party_check_state(p); - break; - } - } - if(sd!=NULL && sd->status.party_id==party_id){ - sd->status.party_id=0; - sd->state.party_sent=0; - clif_charnameupdate(sd); //Update name display [Skotlex] - } - return 0; -} - -int party_broken(int party_id) -{ - struct party_data *p; - int i; - if( (p=party_search(party_id))==NULL ) - return 0; - - for(i=0;idata[i].sd!=NULL){ - clif_party_leaved(p,p->data[i].sd, - p->party.member[i].account_id,p->party.member[i].name,0x10); - p->data[i].sd->status.party_id=0; - p->data[i].sd->state.party_sent=0; - } - } - if (party_cache && party_cache->party.party_id == party_id) - party_cache = NULL; - idb_remove(party_db,party_id); - return 0; -} - -int party_changeoption(struct map_session_data *sd,int exp,int item) -{ - nullpo_retr(0, sd); - - if( sd->status.party_id==0) - return 0; - intif_party_changeoption(sd->status.party_id,sd->status.account_id,exp,item); - return 0; -} - -int party_optionchanged(int party_id,int account_id,int exp,int item,int flag) -{ - struct party_data *p; - struct map_session_data *sd=map_id2sd(account_id); - if( (p=party_search(party_id))==NULL) - return 0; - - if(!(flag&0x01) && p->party.exp != exp) { - p->party.exp=exp; - clif_party_option(p,sd,flag); //This packet doesn't updates item info anymore... - } - if(!(flag&0x10) && p->party.item != item) { - p->party.item=item; - clif_party_main_info(p,-1); - } - if(flag&0x01) //Send denied message - clif_party_option(p,sd,flag); - return 0; -} - -int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv) -{ - struct party_data *p; - int i; - if( (p=party_search(party_id))==NULL) - return 0; - for(i=0;iparty.member[i]; - if(m->account_id==account_id && m->char_id==char_id){ - m->map = map; - m->online=online; - m->lv=lv; - //Check if they still exist on this map server - sd = map_id2sd(m->account_id); - p->data[i].sd = (sd!=NULL && sd->status.party_id==p->party.party_id && sd->status.char_id == m->char_id && !sd->state.waitingdisconnect)?sd:NULL; - break; - } - } - if(i==MAX_PARTY){ - if(battle_config.error_log) - ShowError("party: not found member %d/%d on %d[%s]",account_id,char_id,party_id,p->party.name); - return 0; - } - - clif_party_info(p,-1); - return 0; -} - -int party_send_movemap(struct map_session_data *sd) -{ - int i; - struct party_data *p; - - nullpo_retr(0, sd); - - if( sd->status.party_id==0 ) - return 0; - intif_party_changemap(sd,1); - - p=party_search(sd->status.party_id); - if (p && sd->fd) { - //Send dots of other party members to this char. [Skotlex] - for(i=0; i < MAX_PARTY; i++) { - if (!p->data[i].sd || p->data[i].sd == sd || - p->data[i].sd->bl.m != sd->bl.m) - continue; - clif_party_xy_single(sd->fd, p->data[i].sd); - } - - } - - if( sd->state.party_sent ) - return 0; - - party_check_conflict(sd); - - if(p){ - party_check_member(&p->party); - if(sd->status.party_id==p->party.party_id){ - clif_party_main_info(p,sd->fd); - clif_party_option(p,sd,0x100); - clif_party_info(p,sd->fd); - sd->state.party_sent=1; - } - } - - return 0; -} - -int party_send_logout(struct map_session_data *sd) -{ - struct party_data *p; - int i; - - if(!sd->status.party_id) - return 0; - - intif_party_changemap(sd,0); - p=party_search(sd->status.party_id); - if(!p) return 0; - - for(i=0;idata[i].sd != sd;i++); - if (i < MAX_PARTY) - malloc_set(&p->data[i], 0, sizeof(p->data[0])); - - return 1; -} - -int party_send_message(struct map_session_data *sd,char *mes,int len) -{ - if(sd->status.party_id==0) - return 0; - intif_party_message(sd->status.party_id,sd->status.account_id,mes,len); - party_recv_message(sd->status.party_id,sd->status.account_id,mes,len); - //Chat Logging support Type 'P' - if(log_config.chat&1 //we log everything then - || (log_config.chat&4 //if Party bit is on - && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF - log_chat("P", sd->status.party_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes); - - return 0; -} - -int party_recv_message(int party_id,int account_id,char *mes,int len) -{ - struct party_data *p; - if( (p=party_search(party_id))==NULL) - return 0; - clif_party_message(p,account_id,mes,len); - return 0; -} - -int party_check_conflict(struct map_session_data *sd) -{ - nullpo_retr(0, sd); - - intif_party_checkconflict(sd->status.party_id,sd->status.account_id,sd->status.char_id); - return 0; -} - -int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv) -{ - struct party_data *p; - struct map_session_data *p_sd; - int i; - - if(!party_id || (p=party_search(party_id))==NULL) - return 0; - switch(skillid) { - case TK_COUNTER: //Increase Triple Attack rate of Monks. - if (!p->state.monk) return 0; - break; - case MO_COMBOFINISH: //Increase Counter rate of Star Gladiators - if (!p->state.sg) return 0; - break; - case AM_TWILIGHT2: //Twilight Pharmacy, requires Super Novice - return p->state.snovice; - case AM_TWILIGHT3: //Twilight Pharmacy, Requires Taekwon - return p->state.tk; - default: - return 0; //Unknown case? - } - - for(i=0;idata[i].sd) == NULL) - continue; - if (sd->bl.m != p_sd->bl.m) - continue; - switch(skillid) { - case TK_COUNTER: //Increase Triple Attack rate of Monks. - if((p_sd->class_&MAPID_UPPERMASK) == MAPID_MONK - && pc_checkskill(p_sd,MO_TRIPLEATTACK)) { - sc_start4(&p_sd->bl,SC_SKILLRATE_UP,100,MO_TRIPLEATTACK, - 50+50*skilllv, //+100/150/200% rate - 0,0,skill_get_time(SG_FRIEND, 1)); - } - break; - case MO_COMBOFINISH: //Increase Counter rate of Star Gladiators - if((p_sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR - && sd->sc.data[SC_READYCOUNTER].timer != -1 - && pc_checkskill(p_sd,SG_FRIEND)) { - sc_start4(&p_sd->bl,SC_SKILLRATE_UP,100,TK_COUNTER, - 50+50*pc_checkskill(p_sd,SG_FRIEND), //+100/150/200% rate - 0,0,skill_get_time(SG_FRIEND, 1)); - } - break; - } - } - return 0; -} - -int party_send_xy_timer_sub(DBKey key,void *data,va_list ap) -{ - struct party_data *p=(struct party_data *)data; - struct map_session_data *sd; - int i; - - nullpo_retr(0, p); - - for(i=0;idata[i].sd) continue; - sd = p->data[i].sd; - if (p->data[i].x != sd->bl.x || p->data[i].y != sd->bl.y) - { - clif_party_xy(sd); - p->data[i].x = sd->bl.x; - p->data[i].y = sd->bl.y; - } - if (battle_config.party_hp_mode && - p->data[i].hp != sd->battle_status.hp) - { - clif_party_hp(sd); - p->data[i].hp = sd->battle_status.hp; - } - } - return 0; -} - -int party_send_xy_timer(int tid,unsigned int tick,int id,int data) -{ - party_db->foreach(party_db,party_send_xy_timer_sub,tick); - return 0; -} - -int party_send_xy_clear(struct party_data *p) -{ - int i; - - nullpo_retr(0, p); - - for(i=0;idata[i].sd) continue; - p->data[i].hp = 0; - p->data[i].x = 0; - p->data[i].y = 0; - } - return 0; -} - -// exp share and added zeny share [Valaris] -int party_exp_share(struct party_data *p,struct block_list *src,unsigned int base_exp,unsigned int job_exp,int zeny) -{ - struct map_session_data* sd[MAX_PARTY]; - int i; - unsigned short c; - - nullpo_retr(0, p); - - for (i = c = 0; i < MAX_PARTY; i++) - if ((sd[c] = p->data[i].sd)!=NULL && sd[c]->bl.m == src->m && !pc_isdead(sd[c])) { - if (battle_config.idle_no_share && (sd[c]->chatID || sd[c]->vender_id || (sd[c]->idletime < (last_tick - battle_config.idle_no_share)))) - continue; - c++; - } - if (c < 1) - return 0; - - base_exp/=c; - job_exp/=c; - zeny/=c; - - if (battle_config.party_even_share_bonus && c > 1) { - unsigned short bonus =100 + battle_config.party_even_share_bonus*(c-1); - if (base_exp) { - if (base_exp/100 > UINT_MAX/bonus) - base_exp= UINT_MAX; //Exp overflow - else if (base_exp > 10000) - base_exp = (base_exp/100)*bonus; //Calculation overflow protection - else - base_exp = base_exp*bonus/100; - } - if (job_exp) { - if (job_exp/100 > UINT_MAX/bonus) - job_exp = UINT_MAX; - else if (job_exp > 10000) - job_exp = (job_exp/100)*bonus; - else - job_exp = job_exp*bonus/100; - } - if (zeny) { - if (zeny/100 > INT_MAX/bonus) - zeny = INT_MAX; - else if (zeny > 10000) - zeny = (zeny/100)*bonus; - else - zeny = zeny*bonus/100; - } - } - - for (i = 0; i < c; i++) - { - pc_gainexp(sd[i], src, base_exp, job_exp); - if (zeny) // zeny from mobs [Valaris] - pc_getzeny(sd[i],zeny); - } - return 0; -} - -//Does party loot. first holds the id of the player who has time priority to take the item. -int party_share_loot(struct party_data *p, TBL_PC *sd, struct item *item_data, int first) -{ - TBL_PC *target=NULL; - int i; - if (p && p->party.item&2 && (first || !(battle_config.party_share_type&1))) { - //item distribution to party members. - if (battle_config.party_share_type&2) { //Round Robin - TBL_PC *psd; - i = p->itemc; - do { - i++; - if (i >= MAX_PARTY) - i = 0; // reset counter to 1st person in party so it'll stop when it reaches "itemc" - if ((psd=p->data[i].sd)==NULL || sd->bl.m != psd->bl.m || - pc_isdead(psd) || (battle_config.idle_no_share && ( - psd->chatID || psd->vender_id || (psd->idletime < (last_tick - battle_config.idle_no_share))) - )) - continue; - - if (pc_additem(psd,item_data,item_data->amount)) - continue; //Chosen char can't pick up loot. - //Successful pick. - p->itemc = i; - target = psd; - break; - } while (i != p->itemc); - } else { //Random pick - TBL_PC *psd[MAX_PARTY]; - int count=0; - //Collect pick candidates - for (i = 0; i < MAX_PARTY; i++) { - if ((psd[count]=p->data[i].sd) && psd[count]->bl.m == sd->bl.m && - !pc_isdead(psd[count]) && (!battle_config.idle_no_share || ( - !psd[count]->chatID && !psd[count]->vender_id && - (psd[count]->idletime >= (last_tick - battle_config.idle_no_share))) - )) - count++; - } - while (count > 0) { //Pick a random member. - i = rand()%count; - if (pc_additem(psd[i],item_data,item_data->amount)) - { //Discard this receiver. - psd[i] = psd[count-1]; - count--; - } else { //Successful pick. - target = psd[i]; - break; - } - } - } - } - if (!target) { //Give it to the owner. - target = sd; - if ((i=pc_additem(sd,item_data,item_data->amount))) - return i; - } - - if(log_config.enable_logs&0x8) //Logs items, taken by (P)layers [Lupus] - log_pick_pc(target, "P", item_data->nameid, item_data->amount, item_data); - //Logs - if(battle_config.party_show_share_picker && target != sd){ - char output[80]; - sprintf(output, "%s acquired the item.",target->status.name); - clif_disp_onlyself(sd,output,strlen(output)); - } - return 0; -} - -int party_send_dot_remove(struct map_session_data *sd) -{ - if (sd->status.party_id) - clif_party_xy_remove(sd); - return 0; -} - -// To use for Taekwon's "Fighting Chant" -// int c = 0; -// party_foreachsamemap(party_sub_count, sd, 0, &c); -int party_sub_count(struct block_list *bl, va_list ap) -{ - struct map_session_data *sd = (TBL_PC *)bl; - - if (sd->state.autotrade) - return 0; - - if (battle_config.idle_no_share && (sd->chatID || sd->vender_id || (sd->idletime < (last_tick - battle_config.idle_no_share)))) - return 0; - - return 1; -} - -int party_foreachsamemap(int (*func)(struct block_list*,va_list),struct map_session_data *sd,int range,...) -{ - struct party_data *p; - va_list ap; - int i; - int x0,y0,x1,y1; - struct block_list *list[MAX_PARTY]; - int blockcount=0; - int total = 0; //Return value. - - nullpo_retr(0,sd); - - if((p=party_search(sd->status.party_id))==NULL) - return 0; - - x0=sd->bl.x-range; - y0=sd->bl.y-range; - x1=sd->bl.x+range; - y1=sd->bl.y+range; - - va_start(ap,range); - - for(i=0;idata[i].sd; - if(!psd) continue; - if(psd->bl.m!=sd->bl.m || !psd->bl.prev) - continue; - if(range && - (psd->bl.xbl.ybl.x>x1 || psd->bl.y>y1 ) ) - continue; - list[blockcount++]=&psd->bl; - } - - map_freeblock_lock(); - - for(i=0;i +#include +#include +#include + +#include "../common/timer.h" +#include "../common/socket.h" +#include "../common/nullpo.h" +#include "../common/malloc.h" +#include "../common/showmsg.h" + +#include "party.h" +#include "pc.h" +#include "map.h" +#include "battle.h" +#include "intif.h" +#include "clif.h" +#include "log.h" +#include "skill.h" +#include "status.h" + +static struct dbt* party_db; +static struct party_data* party_cache = NULL; //party in cache for skipping consecutive lookups. [Skotlex] +int party_share_level = 10; +int party_send_xy_timer(int tid,unsigned int tick,int id,int data); + +/*========================================== + * Fills the given party_member structure according to the sd provided. + * Used when creating/adding people to a party. [Skotlex] + *------------------------------------------ + */ +static void party_fill_member(struct party_member *member, struct map_session_data *sd) { + member->account_id = sd->status.account_id; + member->char_id = sd->status.char_id; + memcpy(member->name,sd->status.name,NAME_LENGTH); + member->class_ = sd->status.class_; + member->map = sd->mapindex; + member->lv = sd->status.base_level; + member->online = 1; + member->leader = 0; +} + +/*========================================== + * 終了 + *------------------------------------------ + */ +void do_final_party(void) +{ + party_db->destroy(party_db,NULL); +} +// 初期化 +void do_init_party(void) +{ + party_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + add_timer_func_list(party_send_xy_timer,"party_send_xy_timer"); + add_timer_interval(gettick()+battle_config.party_update_interval,party_send_xy_timer,0,0,battle_config.party_update_interval); +} + +// 検索 +struct party_data *party_search(int party_id) +{ + if(!party_id) return NULL; + if (party_cache && party_cache->party.party_id == party_id) + return party_cache; + + party_cache = idb_get(party_db,party_id); + return party_cache; +} +int party_searchname_sub(DBKey key,void *data,va_list ap) +{ + struct party_data *p=(struct party_data *)data,**dst; + char *str; + str=va_arg(ap,char *); + dst=va_arg(ap,struct party_data **); + if(strncmpi(p->party.name,str,NAME_LENGTH)==0) + *dst=p; + return 0; +} + +struct party_data* party_searchname(char *str) +{ + struct party_data *p=NULL; + party_db->foreach(party_db,party_searchname_sub,str,&p); + return p; +} + +int party_create(struct map_session_data *sd,char *name,int item,int item2) +{ + struct party_member leader; + nullpo_retr(0, sd); + + if(sd->status.party_id) { + clif_party_created(sd,2); + return 0; + } + + party_fill_member(&leader, sd); + leader.leader = 1; + + intif_create_party(&leader,name,item,item2); + return 0; +} + + +int party_created(int account_id,int char_id,int fail,int party_id,char *name) +{ + struct map_session_data *sd; + struct party_data *p; + sd=map_id2sd(account_id); + + nullpo_retr(0, sd); + if (sd->status.char_id != char_id) + return 0; //unlikely failure... + + if(fail){ + clif_party_created(sd,1); + return 0; + } + sd->status.party_id=party_id; + if(idb_get(party_db,party_id)!=NULL){ + ShowFatalError("party: id already exists!\n"); + exit(1); + } + p=(struct party_data *)aCalloc(1,sizeof(struct party_data)); + p->party.party_id=party_id; + memcpy(p->party.name, name, NAME_LENGTH); + idb_put(party_db,party_id,p); + clif_party_created(sd,0); //Success message + clif_charnameupdate(sd); //Update other people's display. [Skotlex] + return 1; +} + +int party_request_info(int party_id) +{ + return intif_request_partyinfo(party_id); +} + +int party_check_member(struct party *p) +{ + int i, users; + struct map_session_data *sd, **all_sd; + + nullpo_retr(0, p); + + all_sd = map_getallusers(&users); + + for(i=0;istatus.party_id==p->party_id) + { + int j,f=1; + for(j=0;jmember[j].account_id==sd->status.account_id && + p->member[j].char_id==sd->status.char_id) + { + f=0; + break; + } + } + + if(f){ + sd->status.party_id=0; + if(battle_config.error_log) + ShowWarning("party: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name); + } + } + } + return 0; +} + +int party_recv_noinfo(int party_id) +{ + int i, users; + struct map_session_data *sd, **all_sd; + + all_sd = map_getallusers(&users); + + for(i=0;istatus.party_id==party_id) + sd->status.party_id=0; + } + return 0; +} + +static void* create_party(DBKey key, va_list args) { + struct party_data *p; + p=(struct party_data *)aCalloc(1,sizeof(struct party_data)); + return p; +} + +static void party_check_state(struct party_data *p) +{ + int i; + malloc_set(&p->state, 0, sizeof(p->state)); + for (i = 0; i < MAX_PARTY; i ++) + { + if (!p->party.member[i].online) continue; //Those not online shouldn't aport to skill usage and all that. + switch (p->party.member[i].class_) { + case JOB_MONK: + case JOB_BABY_MONK: + case JOB_CHAMPION: + p->state.monk = 1; + break; + case JOB_STAR_GLADIATOR: + p->state.sg = 1; + break; + case JOB_SUPER_NOVICE: + case JOB_SUPER_BABY: + p->state.snovice = 1; + break; + case JOB_TAEKWON: + p->state.tk = 1; + break; + } + } +} + +int party_recv_info(struct party *sp) +{ + struct map_session_data *sd; + struct party_data *p; + int i; + + nullpo_retr(0, sp); + + p= idb_ensure(party_db, sp->party_id, create_party); + if (!p->party.party_id) //party just received. + party_check_member(sp); + memcpy(&p->party,sp,sizeof(struct party)); + malloc_set(&p->state, 0, sizeof(p->state)); + malloc_set(&p->data, 0, sizeof(p->data)); + for(i=0;iparty.member[i].account_id) + continue; + sd = map_id2sd(p->party.member[i].account_id); + if (sd && sd->status.party_id==p->party.party_id + && sd->status.char_id == p->party.member[i].char_id + && !sd->state.waitingdisconnect) + p->data[i].sd = sd; + } + party_check_state(p); + for(i=0;idata[i].sd; + if(!sd || sd->state.party_sent) + continue; + clif_party_main_info(p,-1); + clif_party_option(p,sd,0x100); + clif_party_info(p,-1); + sd->state.party_sent=1; + } + + return 0; +} + +int party_invite(struct map_session_data *sd,struct map_session_data *tsd) +{ + struct party_data *p=party_search(sd->status.party_id); + int i,flag=0; + + nullpo_retr(0, sd); + + if(tsd==NULL || p==NULL) + return 0; + if(!battle_config.invite_request_check) { + if (tsd->guild_invite>0 || tsd->trade_partner) { + clif_party_inviteack(sd,tsd->status.name,0); + return 0; + } + } + if( tsd->status.party_id>0 || tsd->party_invite>0 ){ + clif_party_inviteack(sd,tsd->status.name,0); + return 0; + } + for(i=0;iparty.member[i].account_id == 0) //Room for a new member. + flag = 1; + /* By default Aegis BLOCKS more than one char from the same account on a party. + * But eA does support it... so this check is left commented. + if(p->party.member[i].account_id==tsd->status.account_id) + { + clif_party_inviteack(sd,tsd->status.name,4); + return 0; + } + */ + } + if (!flag) { //Full party. + clif_party_inviteack(sd,tsd->status.name,3); + return 0; + } + + tsd->party_invite=sd->status.party_id; + tsd->party_invite_account=sd->status.account_id; + + clif_party_invite(sd,tsd); + return 1; +} + +int party_reply_invite(struct map_session_data *sd,int account_id,int flag) +{ + struct map_session_data *tsd= map_id2sd(account_id); + struct party_member member; + nullpo_retr(0, sd); + + if(flag==1){ + party_fill_member(&member, sd); + intif_party_addmember(sd->party_invite, &member); + return 0; + } + sd->party_invite=0; + sd->party_invite_account=0; + if(tsd==NULL) + return 0; + clif_party_inviteack(tsd,sd->status.name,1); + return 1; +} + +int party_member_added(int party_id,int account_id,int char_id, int flag) +{ + struct map_session_data *sd = map_id2sd(account_id),*sd2; + struct party_data *p = party_search(party_id); + if(sd == NULL || sd->status.char_id != char_id){ + if (flag == 0) { + if(battle_config.error_log) + ShowError("party: member added error %d is not online\n",account_id); + intif_party_leave(party_id,account_id,char_id); + } + return 0; + } + sd->party_invite=0; + sd->party_invite_account=0; + + if (!p) { + if(battle_config.error_log) + ShowError("party_member_added: party %d not found.\n",party_id); + intif_party_leave(party_id,account_id,char_id); + return 0; + } + + if(!flag) { + sd->state.party_sent=0; + sd->status.party_id=party_id; + party_check_conflict(sd); + clif_party_join_info(&p->party,sd); + clif_party_hp(sd); + clif_party_xy(sd); + clif_charnameupdate(sd); //Update char name's display [Skotlex] + } + + sd2=map_id2sd(sd->party_invite_account); + if (sd2) + clif_party_inviteack(sd2,sd->status.name,flag?2:1); + return 0; +} + +int party_removemember(struct map_session_data *sd,int account_id,char *name) +{ + struct party_data *p; + int i; + + nullpo_retr(0, sd); + + if( (p = party_search(sd->status.party_id)) == NULL ) + return 0; + + for(i=0;iparty.member[i].account_id==sd->status.account_id && + p->party.member[i].char_id==sd->status.char_id) { + if(p->party.member[i].leader) + break; + return 0; + } + } + if (i >= MAX_PARTY) //Request from someone not in party? o.O + return 0; + + for(i=0;iparty.member[i].account_id==account_id && + strncmp(p->party.member[i].name,name,NAME_LENGTH)==0) + { + intif_party_leave(p->party.party_id,account_id,p->party.member[i].char_id); + return 1; + } + } + return 0; +} + +int party_leave(struct map_session_data *sd) +{ + struct party_data *p; + int i; + + nullpo_retr(0, sd); + + if( (p = party_search(sd->status.party_id)) == NULL ) + return 0; + + for(i=0;iparty.member[i].account_id==sd->status.account_id && + p->party.member[i].char_id==sd->status.char_id){ + intif_party_leave(p->party.party_id,sd->status.account_id,sd->status.char_id); + return 0; + } + } + return 0; +} + +int party_member_leaved(int party_id,int account_id,int char_id) +{ + struct map_session_data *sd=map_id2sd(account_id); + struct party_data *p=party_search(party_id); + int i; + if (sd && sd->status.char_id != char_id) //Wrong target + sd = NULL; + if(p!=NULL){ + for(i=0;iparty.member[i].account_id==account_id && + p->party.member[i].char_id==char_id){ + clif_party_leaved(p,sd,account_id,p->party.member[i].name,0x00); + malloc_set(&p->party.member[i], 0, sizeof(p->party.member[0])); + malloc_set(&p->data[i], 0, sizeof(p->data[0])); + p->party.count--; + party_check_state(p); + break; + } + } + if(sd!=NULL && sd->status.party_id==party_id){ + sd->status.party_id=0; + sd->state.party_sent=0; + clif_charnameupdate(sd); //Update name display [Skotlex] + } + return 0; +} + +int party_broken(int party_id) +{ + struct party_data *p; + int i; + if( (p=party_search(party_id))==NULL ) + return 0; + + for(i=0;idata[i].sd!=NULL){ + clif_party_leaved(p,p->data[i].sd, + p->party.member[i].account_id,p->party.member[i].name,0x10); + p->data[i].sd->status.party_id=0; + p->data[i].sd->state.party_sent=0; + } + } + if (party_cache && party_cache->party.party_id == party_id) + party_cache = NULL; + idb_remove(party_db,party_id); + return 0; +} + +int party_changeoption(struct map_session_data *sd,int exp,int item) +{ + nullpo_retr(0, sd); + + if( sd->status.party_id==0) + return 0; + intif_party_changeoption(sd->status.party_id,sd->status.account_id,exp,item); + return 0; +} + +int party_optionchanged(int party_id,int account_id,int exp,int item,int flag) +{ + struct party_data *p; + struct map_session_data *sd=map_id2sd(account_id); + if( (p=party_search(party_id))==NULL) + return 0; + + if(!(flag&0x01) && p->party.exp != exp) { + p->party.exp=exp; + clif_party_option(p,sd,flag); //This packet doesn't updates item info anymore... + } + if(!(flag&0x10) && p->party.item != item) { + p->party.item=item; + clif_party_main_info(p,-1); + } + if(flag&0x01) //Send denied message + clif_party_option(p,sd,flag); + return 0; +} + +int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv) +{ + struct party_data *p; + int i; + if( (p=party_search(party_id))==NULL) + return 0; + for(i=0;iparty.member[i]; + if(m->account_id==account_id && m->char_id==char_id){ + m->map = map; + m->online=online; + m->lv=lv; + //Check if they still exist on this map server + sd = map_id2sd(m->account_id); + p->data[i].sd = (sd!=NULL && sd->status.party_id==p->party.party_id && sd->status.char_id == m->char_id && !sd->state.waitingdisconnect)?sd:NULL; + break; + } + } + if(i==MAX_PARTY){ + if(battle_config.error_log) + ShowError("party: not found member %d/%d on %d[%s]",account_id,char_id,party_id,p->party.name); + return 0; + } + + clif_party_info(p,-1); + return 0; +} + +int party_send_movemap(struct map_session_data *sd) +{ + int i; + struct party_data *p; + + nullpo_retr(0, sd); + + if( sd->status.party_id==0 ) + return 0; + intif_party_changemap(sd,1); + + p=party_search(sd->status.party_id); + if (p && sd->fd) { + //Send dots of other party members to this char. [Skotlex] + for(i=0; i < MAX_PARTY; i++) { + if (!p->data[i].sd || p->data[i].sd == sd || + p->data[i].sd->bl.m != sd->bl.m) + continue; + clif_party_xy_single(sd->fd, p->data[i].sd); + } + + } + + if( sd->state.party_sent ) + return 0; + + party_check_conflict(sd); + + if(p){ + party_check_member(&p->party); + if(sd->status.party_id==p->party.party_id){ + clif_party_main_info(p,sd->fd); + clif_party_option(p,sd,0x100); + clif_party_info(p,sd->fd); + sd->state.party_sent=1; + } + } + + return 0; +} + +int party_send_logout(struct map_session_data *sd) +{ + struct party_data *p; + int i; + + if(!sd->status.party_id) + return 0; + + intif_party_changemap(sd,0); + p=party_search(sd->status.party_id); + if(!p) return 0; + + for(i=0;idata[i].sd != sd;i++); + if (i < MAX_PARTY) + malloc_set(&p->data[i], 0, sizeof(p->data[0])); + + return 1; +} + +int party_send_message(struct map_session_data *sd,char *mes,int len) +{ + if(sd->status.party_id==0) + return 0; + intif_party_message(sd->status.party_id,sd->status.account_id,mes,len); + party_recv_message(sd->status.party_id,sd->status.account_id,mes,len); + //Chat Logging support Type 'P' + if(log_config.chat&1 //we log everything then + || (log_config.chat&4 //if Party bit is on + && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF + log_chat("P", sd->status.party_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes); + + return 0; +} + +int party_recv_message(int party_id,int account_id,char *mes,int len) +{ + struct party_data *p; + if( (p=party_search(party_id))==NULL) + return 0; + clif_party_message(p,account_id,mes,len); + return 0; +} + +int party_check_conflict(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + intif_party_checkconflict(sd->status.party_id,sd->status.account_id,sd->status.char_id); + return 0; +} + +int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv) +{ + struct party_data *p; + struct map_session_data *p_sd; + int i; + + if(!party_id || (p=party_search(party_id))==NULL) + return 0; + switch(skillid) { + case TK_COUNTER: //Increase Triple Attack rate of Monks. + if (!p->state.monk) return 0; + break; + case MO_COMBOFINISH: //Increase Counter rate of Star Gladiators + if (!p->state.sg) return 0; + break; + case AM_TWILIGHT2: //Twilight Pharmacy, requires Super Novice + return p->state.snovice; + case AM_TWILIGHT3: //Twilight Pharmacy, Requires Taekwon + return p->state.tk; + default: + return 0; //Unknown case? + } + + for(i=0;idata[i].sd) == NULL) + continue; + if (sd->bl.m != p_sd->bl.m) + continue; + switch(skillid) { + case TK_COUNTER: //Increase Triple Attack rate of Monks. + if((p_sd->class_&MAPID_UPPERMASK) == MAPID_MONK + && pc_checkskill(p_sd,MO_TRIPLEATTACK)) { + sc_start4(&p_sd->bl,SC_SKILLRATE_UP,100,MO_TRIPLEATTACK, + 50+50*skilllv, //+100/150/200% rate + 0,0,skill_get_time(SG_FRIEND, 1)); + } + break; + case MO_COMBOFINISH: //Increase Counter rate of Star Gladiators + if((p_sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR + && sd->sc.data[SC_READYCOUNTER].timer != -1 + && pc_checkskill(p_sd,SG_FRIEND)) { + sc_start4(&p_sd->bl,SC_SKILLRATE_UP,100,TK_COUNTER, + 50+50*pc_checkskill(p_sd,SG_FRIEND), //+100/150/200% rate + 0,0,skill_get_time(SG_FRIEND, 1)); + } + break; + } + } + return 0; +} + +int party_send_xy_timer_sub(DBKey key,void *data,va_list ap) +{ + struct party_data *p=(struct party_data *)data; + struct map_session_data *sd; + int i; + + nullpo_retr(0, p); + + for(i=0;idata[i].sd) continue; + sd = p->data[i].sd; + if (p->data[i].x != sd->bl.x || p->data[i].y != sd->bl.y) + { + clif_party_xy(sd); + p->data[i].x = sd->bl.x; + p->data[i].y = sd->bl.y; + } + if (battle_config.party_hp_mode && + p->data[i].hp != sd->battle_status.hp) + { + clif_party_hp(sd); + p->data[i].hp = sd->battle_status.hp; + } + } + return 0; +} + +int party_send_xy_timer(int tid,unsigned int tick,int id,int data) +{ + party_db->foreach(party_db,party_send_xy_timer_sub,tick); + return 0; +} + +int party_send_xy_clear(struct party_data *p) +{ + int i; + + nullpo_retr(0, p); + + for(i=0;idata[i].sd) continue; + p->data[i].hp = 0; + p->data[i].x = 0; + p->data[i].y = 0; + } + return 0; +} + +// exp share and added zeny share [Valaris] +int party_exp_share(struct party_data *p,struct block_list *src,unsigned int base_exp,unsigned int job_exp,int zeny) +{ + struct map_session_data* sd[MAX_PARTY]; + int i; + unsigned short c; + + nullpo_retr(0, p); + + for (i = c = 0; i < MAX_PARTY; i++) + if ((sd[c] = p->data[i].sd)!=NULL && sd[c]->bl.m == src->m && !pc_isdead(sd[c])) { + if (battle_config.idle_no_share && (sd[c]->chatID || sd[c]->vender_id || (sd[c]->idletime < (last_tick - battle_config.idle_no_share)))) + continue; + c++; + } + if (c < 1) + return 0; + + base_exp/=c; + job_exp/=c; + zeny/=c; + + if (battle_config.party_even_share_bonus && c > 1) { + unsigned short bonus =100 + battle_config.party_even_share_bonus*(c-1); + if (base_exp) { + if (base_exp/100 > UINT_MAX/bonus) + base_exp= UINT_MAX; //Exp overflow + else if (base_exp > 10000) + base_exp = (base_exp/100)*bonus; //Calculation overflow protection + else + base_exp = base_exp*bonus/100; + } + if (job_exp) { + if (job_exp/100 > UINT_MAX/bonus) + job_exp = UINT_MAX; + else if (job_exp > 10000) + job_exp = (job_exp/100)*bonus; + else + job_exp = job_exp*bonus/100; + } + if (zeny) { + if (zeny/100 > INT_MAX/bonus) + zeny = INT_MAX; + else if (zeny > 10000) + zeny = (zeny/100)*bonus; + else + zeny = zeny*bonus/100; + } + } + + for (i = 0; i < c; i++) + { + pc_gainexp(sd[i], src, base_exp, job_exp); + if (zeny) // zeny from mobs [Valaris] + pc_getzeny(sd[i],zeny); + } + return 0; +} + +//Does party loot. first holds the id of the player who has time priority to take the item. +int party_share_loot(struct party_data *p, TBL_PC *sd, struct item *item_data, int first) +{ + TBL_PC *target=NULL; + int i; + if (p && p->party.item&2 && (first || !(battle_config.party_share_type&1))) { + //item distribution to party members. + if (battle_config.party_share_type&2) { //Round Robin + TBL_PC *psd; + i = p->itemc; + do { + i++; + if (i >= MAX_PARTY) + i = 0; // reset counter to 1st person in party so it'll stop when it reaches "itemc" + if ((psd=p->data[i].sd)==NULL || sd->bl.m != psd->bl.m || + pc_isdead(psd) || (battle_config.idle_no_share && ( + psd->chatID || psd->vender_id || (psd->idletime < (last_tick - battle_config.idle_no_share))) + )) + continue; + + if (pc_additem(psd,item_data,item_data->amount)) + continue; //Chosen char can't pick up loot. + //Successful pick. + p->itemc = i; + target = psd; + break; + } while (i != p->itemc); + } else { //Random pick + TBL_PC *psd[MAX_PARTY]; + int count=0; + //Collect pick candidates + for (i = 0; i < MAX_PARTY; i++) { + if ((psd[count]=p->data[i].sd) && psd[count]->bl.m == sd->bl.m && + !pc_isdead(psd[count]) && (!battle_config.idle_no_share || ( + !psd[count]->chatID && !psd[count]->vender_id && + (psd[count]->idletime >= (last_tick - battle_config.idle_no_share))) + )) + count++; + } + while (count > 0) { //Pick a random member. + i = rand()%count; + if (pc_additem(psd[i],item_data,item_data->amount)) + { //Discard this receiver. + psd[i] = psd[count-1]; + count--; + } else { //Successful pick. + target = psd[i]; + break; + } + } + } + } + if (!target) { //Give it to the owner. + target = sd; + if ((i=pc_additem(sd,item_data,item_data->amount))) + return i; + } + + if(log_config.enable_logs&0x8) //Logs items, taken by (P)layers [Lupus] + log_pick_pc(target, "P", item_data->nameid, item_data->amount, item_data); + //Logs + if(battle_config.party_show_share_picker && target != sd){ + char output[80]; + sprintf(output, "%s acquired the item.",target->status.name); + clif_disp_onlyself(sd,output,strlen(output)); + } + return 0; +} + +int party_send_dot_remove(struct map_session_data *sd) +{ + if (sd->status.party_id) + clif_party_xy_remove(sd); + return 0; +} + +// To use for Taekwon's "Fighting Chant" +// int c = 0; +// party_foreachsamemap(party_sub_count, sd, 0, &c); +int party_sub_count(struct block_list *bl, va_list ap) +{ + struct map_session_data *sd = (TBL_PC *)bl; + + if (sd->state.autotrade) + return 0; + + if (battle_config.idle_no_share && (sd->chatID || sd->vender_id || (sd->idletime < (last_tick - battle_config.idle_no_share)))) + return 0; + + return 1; +} + +int party_foreachsamemap(int (*func)(struct block_list*,va_list),struct map_session_data *sd,int range,...) +{ + struct party_data *p; + va_list ap; + int i; + int x0,y0,x1,y1; + struct block_list *list[MAX_PARTY]; + int blockcount=0; + int total = 0; //Return value. + + nullpo_retr(0,sd); + + if((p=party_search(sd->status.party_id))==NULL) + return 0; + + x0=sd->bl.x-range; + y0=sd->bl.y-range; + x1=sd->bl.x+range; + y1=sd->bl.y+range; + + va_start(ap,range); + + for(i=0;idata[i].sd; + if(!psd) continue; + if(psd->bl.m!=sd->bl.m || !psd->bl.prev) + continue; + if(range && + (psd->bl.xbl.ybl.x>x1 || psd->bl.y>y1 ) ) + continue; + list[blockcount++]=&psd->bl; + } + + map_freeblock_lock(); + + for(i=0;i -#include "map.h" - -extern int party_share_level; -struct party; -struct map_session_data; -struct block_list; - -void do_init_party(void); -void do_final_party(void); -struct party_data *party_search(int party_id); -struct party_data *party_searchname(char *str); - -int party_create(struct map_session_data *sd,char *name, int item, int item2); -int party_created(int account_id,int char_id,int fail,int party_id,char *name); -int party_request_info(int party_id); -int party_invite(struct map_session_data *sd,struct map_session_data *tsd); -int party_member_added(int party_id,int account_id,int char_id,int flag); -int party_leave(struct map_session_data *sd); -int party_removemember(struct map_session_data *sd,int account_id,char *name); -int party_member_leaved(int party_id,int account_id,int char_id); -int party_reply_invite(struct map_session_data *sd,int account_id,int flag); -int party_recv_noinfo(int party_id); -int party_recv_info(struct party *sp); -int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv); -int party_broken(int party_id); -int party_optionchanged(int party_id,int account_id,int exp,int item,int flag); -int party_changeoption(struct map_session_data *sd,int exp,int item); -int party_send_movemap(struct map_session_data *sd); -int party_send_logout(struct map_session_data *sd); -int party_send_message(struct map_session_data *sd,char *mes,int len); -int party_recv_message(int party_id,int account_id,char *mes,int len); -int party_check_conflict(struct map_session_data *sd); -int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv); -int party_send_xy_clear(struct party_data *p); -int party_exp_share(struct party_data *p,struct block_list *src,unsigned int base_exp,unsigned int job_exp,int zeny); -int party_share_loot(struct party_data *p, TBL_PC *sd, struct item *item_data, int type); -int party_send_dot_remove(struct map_session_data *sd); -int party_sub_count(struct block_list *bl, va_list ap); -int party_foreachsamemap(int (*func)(struct block_list *,va_list),struct map_session_data *sd,int type,...); - - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _PARTY_H_ +#define _PARTY_H_ + +#include +#include "map.h" + +extern int party_share_level; +struct party; +struct map_session_data; +struct block_list; + +void do_init_party(void); +void do_final_party(void); +struct party_data *party_search(int party_id); +struct party_data *party_searchname(char *str); + +int party_create(struct map_session_data *sd,char *name, int item, int item2); +int party_created(int account_id,int char_id,int fail,int party_id,char *name); +int party_request_info(int party_id); +int party_invite(struct map_session_data *sd,struct map_session_data *tsd); +int party_member_added(int party_id,int account_id,int char_id,int flag); +int party_leave(struct map_session_data *sd); +int party_removemember(struct map_session_data *sd,int account_id,char *name); +int party_member_leaved(int party_id,int account_id,int char_id); +int party_reply_invite(struct map_session_data *sd,int account_id,int flag); +int party_recv_noinfo(int party_id); +int party_recv_info(struct party *sp); +int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv); +int party_broken(int party_id); +int party_optionchanged(int party_id,int account_id,int exp,int item,int flag); +int party_changeoption(struct map_session_data *sd,int exp,int item); +int party_send_movemap(struct map_session_data *sd); +int party_send_logout(struct map_session_data *sd); +int party_send_message(struct map_session_data *sd,char *mes,int len); +int party_recv_message(int party_id,int account_id,char *mes,int len); +int party_check_conflict(struct map_session_data *sd); +int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv); +int party_send_xy_clear(struct party_data *p); +int party_exp_share(struct party_data *p,struct block_list *src,unsigned int base_exp,unsigned int job_exp,int zeny); +int party_share_loot(struct party_data *p, TBL_PC *sd, struct item *item_data, int type); +int party_send_dot_remove(struct map_session_data *sd); +int party_sub_count(struct block_list *bl, va_list ap); +int party_foreachsamemap(int (*func)(struct block_list *,va_list),struct map_session_data *sd,int type,...); + + +#endif diff --git a/src/map/pc.h b/src/map/pc.h index d4397db8f..9d9809263 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -1,318 +1,318 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _PC_H_ -#define _PC_H_ - -#include "map.h" -#include "unit.h" - -#define OPTION_MASK 0xd7b8 -#define CART_MASK 0x788 - -//Update this max as necessary. 53 is the value needed for Super Baby currently -#define MAX_SKILL_TREE 53 - -enum { - W_FIST, //Bare hands - W_DAGGER, //1 - W_1HSWORD, //2 - W_2HSWORD, //3 - W_1HSPEAR, //4 - W_2HSPEAR, //5 - W_1HAXE, //6 - W_2HAXE, //7 - W_MACE, //8 - W_UNKNOWN, //View 9 seems unused anywhere - W_STAFF, //10 - W_BOW, //11 - W_KNUCKLE, //12 - W_MUSICAL, //13 - W_WHIP, //14 - W_BOOK, //15 - W_KATAR, //16 - W_REVOLVER, //17 - W_RIFLE, //18 - W_SHOTGUN, //19 - W_GATLING, //20 - W_GRENADE, //21 - W_HUUMA, //22 - MAX_WEAPON_TYPE -} weapon_type; - -enum { - A_ARROW = 1, - A_DAGGER, //2 - A_BULLET, //3 - A_SHELL, //4 - A_GRENADE, //5 - A_SHURIKEN, //6 - A_KUNAI //7 -} ammo_type; -//Equip position constants -enum { - EQP_HEAD_LOW = 0x0001, - EQP_HEAD_MID = 0x0200, //512 - EQP_HEAD_TOP = 0x0100, //256 - EQP_HAND_R = 0x0002, - EQP_HAND_L = 0x0020, //32 - EQP_ARMOR = 0x0010, //16 - EQP_SHOES = 0x0040, //64 - EQP_GARMENT = 0x0004, - EQP_ACC_L = 0x0008, - EQP_ACC_R = 0x0080, //128 - EQP_AMMO = 0x8000, //32768 -} equip_pos_enum; - -#define EQP_WEAPON EQP_HAND_R -#define EQP_SHIELD EQP_HAND_L -#define EQP_ARMS (EQP_HAND_R|EQP_HAND_L) -#define EQP_HELM (EQP_HEAD_LOW|EQP_HEAD_MID|EQP_HEAD_TOP) -#define EQP_ACC (EQP_ACC_L|EQP_ACC_R) - -//Equip indexes constants. (eg: sd->equip_index[EQI_AMMO] returns the index -//where the arrows are equipped) -enum { - EQI_ACC_L = 0, - EQI_ACC_R, - EQI_SHOES, - EQI_GARMENT, - EQI_HEAD_LOW, - EQI_HEAD_MID, - EQI_HEAD_TOP, - EQI_ARMOR, - EQI_HAND_L, - EQI_HAND_R, - EQI_AMMO, - EQI_MAX -} equip_index_enum; - -#define pc_setdead(sd) ((sd)->state.dead_sit = (sd)->vd.dead_sit = 1) -#define pc_setsit(sd) ((sd)->state.dead_sit = (sd)->vd.dead_sit = 2) -#define pc_isdead(sd) ((sd)->state.dead_sit == 1) -#define pc_issit(sd) ((sd)->vd.dead_sit == 2) -#define pc_setdir(sd,b,h) ((sd)->ud.dir = (b) ,(sd)->head_dir = (h) ) -#define pc_setchatid(sd,n) ((sd)->chatID = n) -#define pc_ishiding(sd) ((sd)->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK)) -#define pc_iscloaking(sd) (!((sd)->sc.option&OPTION_CHASEWALK) && ((sd)->sc.option&OPTION_CLOAK)) -#define pc_ischasewalk(sd) ((sd)->sc.option&OPTION_CHASEWALK) -#define pc_iscarton(sd) ((sd)->sc.option&CART_MASK) -#define pc_isfalcon(sd) ((sd)->sc.option&OPTION_FALCON) -#define pc_isriding(sd) ((sd)->sc.option&OPTION_RIDING) -#define pc_isinvisible(sd) ((sd)->sc.option&OPTION_INVISIBLE) -#define pc_is50overweight(sd) (sd->weight*2 >= sd->max_weight) -#define pc_is90overweight(sd) (sd->weight*10 >= sd->max_weight*9) -#define pc_maxparameter(sd) ((sd->class_&JOBL_BABY) ? battle_config.max_baby_parameter : battle_config.max_parameter) - -#define pc_stop_attack(sd) { if (sd->ud.attacktimer!=-1) { unit_stop_attack(&sd->bl); sd->ud.target = 0; } } -#define pc_stop_walking(sd, type) { if (sd->ud.walktimer!=-1) unit_stop_walking(&sd->bl, type); } - -//Weapon check considering dual wielding. -#define pc_check_weapontype(sd, type) ((type)&((sd)->status.weapon < MAX_WEAPON_TYPE? \ - 1<<(sd)->status.weapon:(1<<(sd)->weapontype1)|(1<<(sd)->weapontype2))) -//Checks if the given class value corresponds to a player class. [Skotlex] -#define pcdb_checkid(class_) (class_ <= JOB_XMAS || (class_ >= JOB_NOVICE_HIGH && class_ <= JOB_SOUL_LINKER)) - -int pc_isGM(struct map_session_data *sd); -int pc_getrefinebonus(int lv,int type); -int pc_can_give_items(int level); //[Lupus] - -int pc_setrestartvalue(struct map_session_data *sd,int type); -int pc_makesavestatus(struct map_session_data *); -int pc_setnewpc(struct map_session_data*,int,int,int,unsigned int,int,int); -int pc_authok(struct map_session_data*, int, time_t, struct mmo_charstatus *); -int pc_authfail(struct map_session_data *); -int pc_reg_received(struct map_session_data *sd); - -int pc_isequip(struct map_session_data *sd,int n); -int pc_equippoint(struct map_session_data *sd,int n); - -int pc_checkskill(struct map_session_data *sd,int skill_id); -int pc_checkallowskill(struct map_session_data *sd); -int pc_checkequip(struct map_session_data *sd,int pos); - -int pc_calc_skilltree(struct map_session_data *sd); -int pc_calc_skilltree_normalize_job(struct map_session_data *sd); -int pc_clean_skilltree(struct map_session_data *sd); - -#define pc_checkoverhp(sd) (sd->battle_status.hp == sd->battle_status.max_hp) -#define pc_checkoversp(sd) (sd->battle_status.sp == sd->battle_status.max_sp) - -int pc_setpos(struct map_session_data*,unsigned short,int,int,int); -int pc_setsavepoint(struct map_session_data*,short,int,int); -int pc_randomwarp(struct map_session_data *sd,int type); -int pc_memo(struct map_session_data *sd,int i); -int pc_remove_map(struct map_session_data *sd,int clrtype); - -int pc_checkadditem(struct map_session_data*,int,int); -int pc_inventoryblank(struct map_session_data*); -int pc_search_inventory(struct map_session_data *sd,int item_id); -int pc_payzeny(struct map_session_data*,int); -int pc_additem(struct map_session_data*,struct item*,int); -int pc_getzeny(struct map_session_data*,int); -int pc_delitem(struct map_session_data*,int,int,int); - -int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount); -int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type); -int pc_putitemtocart(struct map_session_data *sd,int idx,int amount); -int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount); -int pc_cartitem_amount(struct map_session_data *sd,int idx,int amount); - -int pc_takeitem(struct map_session_data*,struct flooritem_data*); -int pc_dropitem(struct map_session_data*,int,int); - -int pc_checkweighticon(struct map_session_data *sd); - -int pc_bonus(struct map_session_data*,int,int); -int pc_bonus2(struct map_session_data *sd,int,int,int); -int pc_bonus3(struct map_session_data *sd,int,int,int,int); -int pc_bonus4(struct map_session_data *sd,int,int,int,int,int); -int pc_skill(struct map_session_data*,int,int,int); - -int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip); - -int pc_steal_item(struct map_session_data *sd,struct block_list *bl, int skilllv); -int pc_steal_coin(struct map_session_data *sd,struct block_list *bl); - -int pc_modifybuyvalue(struct map_session_data*,int); -int pc_modifysellvalue(struct map_session_data*,int); - -int pc_follow(struct map_session_data*, int); // [MouseJstr] -int pc_stop_following(struct map_session_data*); - -unsigned int pc_maxbaselv(struct map_session_data *sd); -unsigned int pc_maxjoblv(struct map_session_data *sd); -int pc_checkbaselevelup(struct map_session_data *sd); -int pc_checkjoblevelup(struct map_session_data *sd); -int pc_gainexp(struct map_session_data*,struct block_list*,unsigned int,unsigned int); -unsigned int pc_nextbaseexp(struct map_session_data *); -unsigned int pc_nextjobexp(struct map_session_data *); -int pc_need_status_point(struct map_session_data *,int); -int pc_statusup(struct map_session_data*,int); -int pc_statusup2(struct map_session_data*,int,int); -int pc_skillup(struct map_session_data*,int); -int pc_allskillup(struct map_session_data*); -int pc_resetlvl(struct map_session_data*,int type); -int pc_resetstate(struct map_session_data*); -int pc_resetskill(struct map_session_data*, int); -int pc_resetfeel(struct map_session_data*); -int pc_resethate(struct map_session_data*); -int pc_equipitem(struct map_session_data*,int,int); -int pc_unequipitem(struct map_session_data*,int,int); -int pc_checkitem(struct map_session_data*); -int pc_useitem(struct map_session_data*,int); - -void pc_damage(struct map_session_data *sd,struct block_list *src,unsigned int hp, unsigned int sp); -int pc_dead(struct map_session_data *sd,struct block_list *src); -void pc_revive(struct map_session_data *sd,unsigned int hp, unsigned int sp); -void pc_heal(struct map_session_data *sd,unsigned int hp,unsigned int sp, int type); -int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp); -int pc_percentheal(struct map_session_data *sd,int,int); -int pc_jobchange(struct map_session_data *,int, int); -int pc_setoption(struct map_session_data *,int); -int pc_setcart(struct map_session_data *sd,int type); -int pc_setfalcon(struct map_session_data *sd); -int pc_setriding(struct map_session_data *sd); -int pc_changelook(struct map_session_data *,int,int); -int pc_equiplookall(struct map_session_data *sd); - -int pc_readparam(struct map_session_data*,int); -int pc_setparam(struct map_session_data*,int,int); -int pc_readreg(struct map_session_data*,int); -int pc_setreg(struct map_session_data*,int,int); -char *pc_readregstr(struct map_session_data *sd,int reg); -int pc_setregstr(struct map_session_data *sd,int reg,char *str); - -#define pc_readglobalreg(sd,reg) pc_readregistry(sd,reg,3) -#define pc_setglobalreg(sd,reg,val) pc_setregistry(sd,reg,val,3) -#define pc_readglobalreg_str(sd,reg) pc_readregistry_str(sd,reg,3) -#define pc_setglobalreg_str(sd,reg,val) pc_setregistry_str(sd,reg,val,3) -#define pc_readaccountreg(sd,reg) pc_readregistry(sd,reg,2) -#define pc_setaccountreg(sd,reg,val) pc_setregistry(sd,reg,val,2) -#define pc_readaccountregstr(sd,reg) pc_readregistry_str(sd,reg,2) -#define pc_setaccountregstr(sd,reg,val) pc_setregistry_str(sd,reg,val,2) -#define pc_readaccountreg2(sd,reg) pc_readregistry(sd,reg,1) -#define pc_setaccountreg2(sd,reg,val) pc_setregistry(sd,reg,val,1) -#define pc_readaccountreg2str(sd,reg) pc_readregistry_str(sd,reg,1) -#define pc_setaccountreg2str(sd,reg,val) pc_setregistry_str(sd,reg,val,1) -int pc_readregistry(struct map_session_data*,const char*,int); -int pc_setregistry(struct map_session_data*,const char*,int,int); -char *pc_readregistry_str(struct map_session_data*,char*,int); -int pc_setregistry_str(struct map_session_data*,char*,char*,int); - -int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name); -int pc_deleventtimer(struct map_session_data *sd,const char *name); -int pc_cleareventtimer(struct map_session_data *sd); -int pc_addeventtimercount(struct map_session_data *sd,const char *name,int tick); - -int pc_calc_pvprank(struct map_session_data *sd); -int pc_calc_pvprank_timer(int tid,unsigned int tick,int id,int data); - -int pc_ismarried(struct map_session_data *sd); -int pc_marriage(struct map_session_data *sd,struct map_session_data *dstsd); -int pc_divorce(struct map_session_data *sd); -int pc_adoption(struct map_session_data *sd,struct map_session_data *dstsd,struct map_session_data *jasd); -struct map_session_data *pc_get_partner(struct map_session_data *sd); -struct map_session_data *pc_get_father(struct map_session_data *sd); -struct map_session_data *pc_get_mother(struct map_session_data *sd); -struct map_session_data *pc_get_child(struct map_session_data *sd); - -void pc_bleeding (struct map_session_data *sd, unsigned int diff_tick); - -int pc_set_gm_level(int account_id, int level); -void pc_setstand(struct map_session_data *sd); -int pc_candrop(struct map_session_data *sd,struct item *item); - -int pc_jobid2mapid(unsigned short b_class); // Skotlex -int pc_mapid2jobid(unsigned short class_, int sex); // Skotlex - -char * job_name(int class_); - -struct skill_tree_entry { - short id; - unsigned char max; - unsigned char joblv; - struct { - short id; - unsigned char lv; - } need[5]; -}; // Celest -extern struct skill_tree_entry skill_tree[MAX_PC_CLASS][MAX_SKILL_TREE]; - -int pc_read_gm_account(int fd); -int pc_setinvincibletimer(struct map_session_data *sd,int); -int pc_delinvincibletimer(struct map_session_data *sd); -int pc_addspiritball(struct map_session_data *sd,int,int); -int pc_delspiritball(struct map_session_data *sd,int,int); -void pc_addfame(struct map_session_data *sd,int count); -unsigned char pc_famerank(int char_id, int job); -int pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl); - -extern struct fame_list smith_fame_list[MAX_FAME_LIST]; -extern struct fame_list chemist_fame_list[MAX_FAME_LIST]; -extern struct fame_list taekwon_fame_list[MAX_FAME_LIST]; - -int pc_readdb(void); -int do_init_pc(void); -void do_final_pc(void); - -enum {ADDITEM_EXIST,ADDITEM_NEW,ADDITEM_OVERAMOUNT}; - -// timer for night.day -extern int day_timer_tid; -extern int night_timer_tid; -int map_day_timer(int,unsigned int,int,int); // by [yor] -int map_night_timer(int,unsigned int,int,int); // by [yor] - -//Duel functions // [LuzZza] -int duel_create(struct map_session_data* sd, const unsigned int maxpl); -int duel_invite(const unsigned int did, struct map_session_data* sd, struct map_session_data* target_sd); -int duel_accept(const unsigned int did, struct map_session_data* sd); -int duel_reject(const unsigned int did, struct map_session_data* sd); -int duel_leave(const unsigned int did, struct map_session_data* sd); -int duel_showinfo(const unsigned int did, struct map_session_data* sd); -int duel_checktime(struct map_session_data* sd); - -int pc_read_motd(void); // [Valaris] -int pc_disguise(struct map_session_data *sd, int class_); -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _PC_H_ +#define _PC_H_ + +#include "map.h" +#include "unit.h" + +#define OPTION_MASK 0xd7b8 +#define CART_MASK 0x788 + +//Update this max as necessary. 53 is the value needed for Super Baby currently +#define MAX_SKILL_TREE 53 + +enum { + W_FIST, //Bare hands + W_DAGGER, //1 + W_1HSWORD, //2 + W_2HSWORD, //3 + W_1HSPEAR, //4 + W_2HSPEAR, //5 + W_1HAXE, //6 + W_2HAXE, //7 + W_MACE, //8 + W_UNKNOWN, //View 9 seems unused anywhere + W_STAFF, //10 + W_BOW, //11 + W_KNUCKLE, //12 + W_MUSICAL, //13 + W_WHIP, //14 + W_BOOK, //15 + W_KATAR, //16 + W_REVOLVER, //17 + W_RIFLE, //18 + W_SHOTGUN, //19 + W_GATLING, //20 + W_GRENADE, //21 + W_HUUMA, //22 + MAX_WEAPON_TYPE +} weapon_type; + +enum { + A_ARROW = 1, + A_DAGGER, //2 + A_BULLET, //3 + A_SHELL, //4 + A_GRENADE, //5 + A_SHURIKEN, //6 + A_KUNAI //7 +} ammo_type; +//Equip position constants +enum { + EQP_HEAD_LOW = 0x0001, + EQP_HEAD_MID = 0x0200, //512 + EQP_HEAD_TOP = 0x0100, //256 + EQP_HAND_R = 0x0002, + EQP_HAND_L = 0x0020, //32 + EQP_ARMOR = 0x0010, //16 + EQP_SHOES = 0x0040, //64 + EQP_GARMENT = 0x0004, + EQP_ACC_L = 0x0008, + EQP_ACC_R = 0x0080, //128 + EQP_AMMO = 0x8000, //32768 +} equip_pos_enum; + +#define EQP_WEAPON EQP_HAND_R +#define EQP_SHIELD EQP_HAND_L +#define EQP_ARMS (EQP_HAND_R|EQP_HAND_L) +#define EQP_HELM (EQP_HEAD_LOW|EQP_HEAD_MID|EQP_HEAD_TOP) +#define EQP_ACC (EQP_ACC_L|EQP_ACC_R) + +//Equip indexes constants. (eg: sd->equip_index[EQI_AMMO] returns the index +//where the arrows are equipped) +enum { + EQI_ACC_L = 0, + EQI_ACC_R, + EQI_SHOES, + EQI_GARMENT, + EQI_HEAD_LOW, + EQI_HEAD_MID, + EQI_HEAD_TOP, + EQI_ARMOR, + EQI_HAND_L, + EQI_HAND_R, + EQI_AMMO, + EQI_MAX +} equip_index_enum; + +#define pc_setdead(sd) ((sd)->state.dead_sit = (sd)->vd.dead_sit = 1) +#define pc_setsit(sd) ((sd)->state.dead_sit = (sd)->vd.dead_sit = 2) +#define pc_isdead(sd) ((sd)->state.dead_sit == 1) +#define pc_issit(sd) ((sd)->vd.dead_sit == 2) +#define pc_setdir(sd,b,h) ((sd)->ud.dir = (b) ,(sd)->head_dir = (h) ) +#define pc_setchatid(sd,n) ((sd)->chatID = n) +#define pc_ishiding(sd) ((sd)->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK)) +#define pc_iscloaking(sd) (!((sd)->sc.option&OPTION_CHASEWALK) && ((sd)->sc.option&OPTION_CLOAK)) +#define pc_ischasewalk(sd) ((sd)->sc.option&OPTION_CHASEWALK) +#define pc_iscarton(sd) ((sd)->sc.option&CART_MASK) +#define pc_isfalcon(sd) ((sd)->sc.option&OPTION_FALCON) +#define pc_isriding(sd) ((sd)->sc.option&OPTION_RIDING) +#define pc_isinvisible(sd) ((sd)->sc.option&OPTION_INVISIBLE) +#define pc_is50overweight(sd) (sd->weight*2 >= sd->max_weight) +#define pc_is90overweight(sd) (sd->weight*10 >= sd->max_weight*9) +#define pc_maxparameter(sd) ((sd->class_&JOBL_BABY) ? battle_config.max_baby_parameter : battle_config.max_parameter) + +#define pc_stop_attack(sd) { if (sd->ud.attacktimer!=-1) { unit_stop_attack(&sd->bl); sd->ud.target = 0; } } +#define pc_stop_walking(sd, type) { if (sd->ud.walktimer!=-1) unit_stop_walking(&sd->bl, type); } + +//Weapon check considering dual wielding. +#define pc_check_weapontype(sd, type) ((type)&((sd)->status.weapon < MAX_WEAPON_TYPE? \ + 1<<(sd)->status.weapon:(1<<(sd)->weapontype1)|(1<<(sd)->weapontype2))) +//Checks if the given class value corresponds to a player class. [Skotlex] +#define pcdb_checkid(class_) (class_ <= JOB_XMAS || (class_ >= JOB_NOVICE_HIGH && class_ <= JOB_SOUL_LINKER)) + +int pc_isGM(struct map_session_data *sd); +int pc_getrefinebonus(int lv,int type); +int pc_can_give_items(int level); //[Lupus] + +int pc_setrestartvalue(struct map_session_data *sd,int type); +int pc_makesavestatus(struct map_session_data *); +int pc_setnewpc(struct map_session_data*,int,int,int,unsigned int,int,int); +int pc_authok(struct map_session_data*, int, time_t, struct mmo_charstatus *); +int pc_authfail(struct map_session_data *); +int pc_reg_received(struct map_session_data *sd); + +int pc_isequip(struct map_session_data *sd,int n); +int pc_equippoint(struct map_session_data *sd,int n); + +int pc_checkskill(struct map_session_data *sd,int skill_id); +int pc_checkallowskill(struct map_session_data *sd); +int pc_checkequip(struct map_session_data *sd,int pos); + +int pc_calc_skilltree(struct map_session_data *sd); +int pc_calc_skilltree_normalize_job(struct map_session_data *sd); +int pc_clean_skilltree(struct map_session_data *sd); + +#define pc_checkoverhp(sd) (sd->battle_status.hp == sd->battle_status.max_hp) +#define pc_checkoversp(sd) (sd->battle_status.sp == sd->battle_status.max_sp) + +int pc_setpos(struct map_session_data*,unsigned short,int,int,int); +int pc_setsavepoint(struct map_session_data*,short,int,int); +int pc_randomwarp(struct map_session_data *sd,int type); +int pc_memo(struct map_session_data *sd,int i); +int pc_remove_map(struct map_session_data *sd,int clrtype); + +int pc_checkadditem(struct map_session_data*,int,int); +int pc_inventoryblank(struct map_session_data*); +int pc_search_inventory(struct map_session_data *sd,int item_id); +int pc_payzeny(struct map_session_data*,int); +int pc_additem(struct map_session_data*,struct item*,int); +int pc_getzeny(struct map_session_data*,int); +int pc_delitem(struct map_session_data*,int,int,int); + +int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount); +int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type); +int pc_putitemtocart(struct map_session_data *sd,int idx,int amount); +int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount); +int pc_cartitem_amount(struct map_session_data *sd,int idx,int amount); + +int pc_takeitem(struct map_session_data*,struct flooritem_data*); +int pc_dropitem(struct map_session_data*,int,int); + +int pc_checkweighticon(struct map_session_data *sd); + +int pc_bonus(struct map_session_data*,int,int); +int pc_bonus2(struct map_session_data *sd,int,int,int); +int pc_bonus3(struct map_session_data *sd,int,int,int,int); +int pc_bonus4(struct map_session_data *sd,int,int,int,int,int); +int pc_skill(struct map_session_data*,int,int,int); + +int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip); + +int pc_steal_item(struct map_session_data *sd,struct block_list *bl, int skilllv); +int pc_steal_coin(struct map_session_data *sd,struct block_list *bl); + +int pc_modifybuyvalue(struct map_session_data*,int); +int pc_modifysellvalue(struct map_session_data*,int); + +int pc_follow(struct map_session_data*, int); // [MouseJstr] +int pc_stop_following(struct map_session_data*); + +unsigned int pc_maxbaselv(struct map_session_data *sd); +unsigned int pc_maxjoblv(struct map_session_data *sd); +int pc_checkbaselevelup(struct map_session_data *sd); +int pc_checkjoblevelup(struct map_session_data *sd); +int pc_gainexp(struct map_session_data*,struct block_list*,unsigned int,unsigned int); +unsigned int pc_nextbaseexp(struct map_session_data *); +unsigned int pc_nextjobexp(struct map_session_data *); +int pc_need_status_point(struct map_session_data *,int); +int pc_statusup(struct map_session_data*,int); +int pc_statusup2(struct map_session_data*,int,int); +int pc_skillup(struct map_session_data*,int); +int pc_allskillup(struct map_session_data*); +int pc_resetlvl(struct map_session_data*,int type); +int pc_resetstate(struct map_session_data*); +int pc_resetskill(struct map_session_data*, int); +int pc_resetfeel(struct map_session_data*); +int pc_resethate(struct map_session_data*); +int pc_equipitem(struct map_session_data*,int,int); +int pc_unequipitem(struct map_session_data*,int,int); +int pc_checkitem(struct map_session_data*); +int pc_useitem(struct map_session_data*,int); + +void pc_damage(struct map_session_data *sd,struct block_list *src,unsigned int hp, unsigned int sp); +int pc_dead(struct map_session_data *sd,struct block_list *src); +void pc_revive(struct map_session_data *sd,unsigned int hp, unsigned int sp); +void pc_heal(struct map_session_data *sd,unsigned int hp,unsigned int sp, int type); +int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp); +int pc_percentheal(struct map_session_data *sd,int,int); +int pc_jobchange(struct map_session_data *,int, int); +int pc_setoption(struct map_session_data *,int); +int pc_setcart(struct map_session_data *sd,int type); +int pc_setfalcon(struct map_session_data *sd); +int pc_setriding(struct map_session_data *sd); +int pc_changelook(struct map_session_data *,int,int); +int pc_equiplookall(struct map_session_data *sd); + +int pc_readparam(struct map_session_data*,int); +int pc_setparam(struct map_session_data*,int,int); +int pc_readreg(struct map_session_data*,int); +int pc_setreg(struct map_session_data*,int,int); +char *pc_readregstr(struct map_session_data *sd,int reg); +int pc_setregstr(struct map_session_data *sd,int reg,char *str); + +#define pc_readglobalreg(sd,reg) pc_readregistry(sd,reg,3) +#define pc_setglobalreg(sd,reg,val) pc_setregistry(sd,reg,val,3) +#define pc_readglobalreg_str(sd,reg) pc_readregistry_str(sd,reg,3) +#define pc_setglobalreg_str(sd,reg,val) pc_setregistry_str(sd,reg,val,3) +#define pc_readaccountreg(sd,reg) pc_readregistry(sd,reg,2) +#define pc_setaccountreg(sd,reg,val) pc_setregistry(sd,reg,val,2) +#define pc_readaccountregstr(sd,reg) pc_readregistry_str(sd,reg,2) +#define pc_setaccountregstr(sd,reg,val) pc_setregistry_str(sd,reg,val,2) +#define pc_readaccountreg2(sd,reg) pc_readregistry(sd,reg,1) +#define pc_setaccountreg2(sd,reg,val) pc_setregistry(sd,reg,val,1) +#define pc_readaccountreg2str(sd,reg) pc_readregistry_str(sd,reg,1) +#define pc_setaccountreg2str(sd,reg,val) pc_setregistry_str(sd,reg,val,1) +int pc_readregistry(struct map_session_data*,const char*,int); +int pc_setregistry(struct map_session_data*,const char*,int,int); +char *pc_readregistry_str(struct map_session_data*,char*,int); +int pc_setregistry_str(struct map_session_data*,char*,char*,int); + +int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name); +int pc_deleventtimer(struct map_session_data *sd,const char *name); +int pc_cleareventtimer(struct map_session_data *sd); +int pc_addeventtimercount(struct map_session_data *sd,const char *name,int tick); + +int pc_calc_pvprank(struct map_session_data *sd); +int pc_calc_pvprank_timer(int tid,unsigned int tick,int id,int data); + +int pc_ismarried(struct map_session_data *sd); +int pc_marriage(struct map_session_data *sd,struct map_session_data *dstsd); +int pc_divorce(struct map_session_data *sd); +int pc_adoption(struct map_session_data *sd,struct map_session_data *dstsd,struct map_session_data *jasd); +struct map_session_data *pc_get_partner(struct map_session_data *sd); +struct map_session_data *pc_get_father(struct map_session_data *sd); +struct map_session_data *pc_get_mother(struct map_session_data *sd); +struct map_session_data *pc_get_child(struct map_session_data *sd); + +void pc_bleeding (struct map_session_data *sd, unsigned int diff_tick); + +int pc_set_gm_level(int account_id, int level); +void pc_setstand(struct map_session_data *sd); +int pc_candrop(struct map_session_data *sd,struct item *item); + +int pc_jobid2mapid(unsigned short b_class); // Skotlex +int pc_mapid2jobid(unsigned short class_, int sex); // Skotlex + +char * job_name(int class_); + +struct skill_tree_entry { + short id; + unsigned char max; + unsigned char joblv; + struct { + short id; + unsigned char lv; + } need[5]; +}; // Celest +extern struct skill_tree_entry skill_tree[MAX_PC_CLASS][MAX_SKILL_TREE]; + +int pc_read_gm_account(int fd); +int pc_setinvincibletimer(struct map_session_data *sd,int); +int pc_delinvincibletimer(struct map_session_data *sd); +int pc_addspiritball(struct map_session_data *sd,int,int); +int pc_delspiritball(struct map_session_data *sd,int,int); +void pc_addfame(struct map_session_data *sd,int count); +unsigned char pc_famerank(int char_id, int job); +int pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl); + +extern struct fame_list smith_fame_list[MAX_FAME_LIST]; +extern struct fame_list chemist_fame_list[MAX_FAME_LIST]; +extern struct fame_list taekwon_fame_list[MAX_FAME_LIST]; + +int pc_readdb(void); +int do_init_pc(void); +void do_final_pc(void); + +enum {ADDITEM_EXIST,ADDITEM_NEW,ADDITEM_OVERAMOUNT}; + +// timer for night.day +extern int day_timer_tid; +extern int night_timer_tid; +int map_day_timer(int,unsigned int,int,int); // by [yor] +int map_night_timer(int,unsigned int,int,int); // by [yor] + +//Duel functions // [LuzZza] +int duel_create(struct map_session_data* sd, const unsigned int maxpl); +int duel_invite(const unsigned int did, struct map_session_data* sd, struct map_session_data* target_sd); +int duel_accept(const unsigned int did, struct map_session_data* sd); +int duel_reject(const unsigned int did, struct map_session_data* sd); +int duel_leave(const unsigned int did, struct map_session_data* sd); +int duel_showinfo(const unsigned int did, struct map_session_data* sd); +int duel_checktime(struct map_session_data* sd); + +int pc_read_motd(void); // [Valaris] +int pc_disguise(struct map_session_data *sd, int class_); +#endif diff --git a/src/map/pcre.h b/src/map/pcre.h index 244e55e0d..b2596a83d 100644 --- a/src/map/pcre.h +++ b/src/map/pcre.h @@ -1,258 +1,258 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ - -/* In its original form, this is the .in file that is transformed by -"configure" into pcre.h. - - Copyright (c) 1997-2005 University of Cambridge - ------------------------------------------------------------------------------ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the University of Cambridge nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------ -*/ - -#ifndef _PCRE_H -#define _PCRE_H - -/* The file pcre.h is build by "configure". Do not edit it; instead -make changes to pcre.in. */ - -#define PCRE_MAJOR 6 -#define PCRE_MINOR 3 -#define PCRE_DATE 15-Aug-2005 - -/* Win32 uses DLL by default; it needs special stuff for exported functions. */ - -#ifdef _WIN32 -# ifdef PCRE_DEFINITION -# ifdef DLL_EXPORT -# define PCRE_DATA_SCOPE __declspec(dllexport) -# endif -# else -# ifndef PCRE_STATIC -# define PCRE_DATA_SCOPE extern __declspec(dllimport) -# endif -# endif -#endif - -/* For other operating systems, we use the standard "extern". */ - -#ifndef PCRE_DATA_SCOPE -# ifdef __cplusplus -# define PCRE_DATA_SCOPE extern "C" -# else -# define PCRE_DATA_SCOPE extern -# endif -#endif - -/* Have to include stdlib.h in order to ensure that size_t is defined; -it is needed here for malloc. */ - -#include - -/* Allow for C++ users */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* Options */ - -#define PCRE_CASELESS 0x00000001 -#define PCRE_MULTILINE 0x00000002 -#define PCRE_DOTALL 0x00000004 -#define PCRE_EXTENDED 0x00000008 -#define PCRE_ANCHORED 0x00000010 -#define PCRE_DOLLAR_ENDONLY 0x00000020 -#define PCRE_EXTRA 0x00000040 -#define PCRE_NOTBOL 0x00000080 -#define PCRE_NOTEOL 0x00000100 -#define PCRE_UNGREEDY 0x00000200 -#define PCRE_NOTEMPTY 0x00000400 -#define PCRE_UTF8 0x00000800 -#define PCRE_NO_AUTO_CAPTURE 0x00001000 -#define PCRE_NO_UTF8_CHECK 0x00002000 -#define PCRE_AUTO_CALLOUT 0x00004000 -#define PCRE_PARTIAL 0x00008000 -#define PCRE_DFA_SHORTEST 0x00010000 -#define PCRE_DFA_RESTART 0x00020000 -#define PCRE_FIRSTLINE 0x00040000 - -/* Exec-time and get/set-time error codes */ - -#define PCRE_ERROR_NOMATCH (-1) -#define PCRE_ERROR_NULL (-2) -#define PCRE_ERROR_BADOPTION (-3) -#define PCRE_ERROR_BADMAGIC (-4) -#define PCRE_ERROR_UNKNOWN_NODE (-5) -#define PCRE_ERROR_NOMEMORY (-6) -#define PCRE_ERROR_NOSUBSTRING (-7) -#define PCRE_ERROR_MATCHLIMIT (-8) -#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */ -#define PCRE_ERROR_BADUTF8 (-10) -#define PCRE_ERROR_BADUTF8_OFFSET (-11) -#define PCRE_ERROR_PARTIAL (-12) -#define PCRE_ERROR_BADPARTIAL (-13) -#define PCRE_ERROR_INTERNAL (-14) -#define PCRE_ERROR_BADCOUNT (-15) -#define PCRE_ERROR_DFA_UITEM (-16) -#define PCRE_ERROR_DFA_UCOND (-17) -#define PCRE_ERROR_DFA_UMLIMIT (-18) -#define PCRE_ERROR_DFA_WSSIZE (-19) -#define PCRE_ERROR_DFA_RECURSE (-20) - -/* Request types for pcre_fullinfo() */ - -#define PCRE_INFO_OPTIONS 0 -#define PCRE_INFO_SIZE 1 -#define PCRE_INFO_CAPTURECOUNT 2 -#define PCRE_INFO_BACKREFMAX 3 -#define PCRE_INFO_FIRSTBYTE 4 -#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */ -#define PCRE_INFO_FIRSTTABLE 5 -#define PCRE_INFO_LASTLITERAL 6 -#define PCRE_INFO_NAMEENTRYSIZE 7 -#define PCRE_INFO_NAMECOUNT 8 -#define PCRE_INFO_NAMETABLE 9 -#define PCRE_INFO_STUDYSIZE 10 -#define PCRE_INFO_DEFAULT_TABLES 11 - -/* Request types for pcre_config() */ - -#define PCRE_CONFIG_UTF8 0 -#define PCRE_CONFIG_NEWLINE 1 -#define PCRE_CONFIG_LINK_SIZE 2 -#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3 -#define PCRE_CONFIG_MATCH_LIMIT 4 -#define PCRE_CONFIG_STACKRECURSE 5 -#define PCRE_CONFIG_UNICODE_PROPERTIES 6 - -/* Bit flags for the pcre_extra structure */ - -#define PCRE_EXTRA_STUDY_DATA 0x0001 -#define PCRE_EXTRA_MATCH_LIMIT 0x0002 -#define PCRE_EXTRA_CALLOUT_DATA 0x0004 -#define PCRE_EXTRA_TABLES 0x0008 - -/* Types */ - -struct real_pcre; /* declaration; the definition is private */ -typedef struct real_pcre pcre; - -/* The structure for passing additional data to pcre_exec(). This is defined in -such as way as to be extensible. Always add new fields at the end, in order to -remain compatible. */ - -typedef struct pcre_extra { - unsigned long int flags; /* Bits for which fields are set */ - void *study_data; /* Opaque data from pcre_study() */ - unsigned long int match_limit; /* Maximum number of calls to match() */ - void *callout_data; /* Data passed back in callouts */ - const unsigned char *tables; /* Pointer to character tables */ -} pcre_extra; - -/* The structure for passing out data via the pcre_callout_function. We use a -structure so that new fields can be added on the end in future versions, -without changing the API of the function, thereby allowing old clients to work -without modification. */ - -typedef struct pcre_callout_block { - int version; /* Identifies version of block */ - /* ------------------------ Version 0 ------------------------------- */ - int callout_number; /* Number compiled into pattern */ - int *offset_vector; /* The offset vector */ - const char *subject; /* The subject being matched */ - int subject_length; /* The length of the subject */ - int start_match; /* Offset to start of this match attempt */ - int current_position; /* Where we currently are in the subject */ - int capture_top; /* Max current capture */ - int capture_last; /* Most recently closed capture */ - void *callout_data; /* Data passed in with the call */ - /* ------------------- Added for Version 1 -------------------------- */ - int pattern_position; /* Offset to next item in the pattern */ - int next_item_length; /* Length of next item in the pattern */ - /* ------------------------------------------------------------------ */ -} pcre_callout_block; - -/* Indirection for store get and free functions. These can be set to -alternative malloc/free functions if required. Special ones are used in the -non-recursive case for "frames". There is also an optional callout function -that is triggered by the (?) regex item. For Virtual Pascal, these definitions -have to take another form. */ - -#ifndef VPCOMPAT -PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t); -PCRE_DATA_SCOPE void (*pcre_free)(void *); -PCRE_DATA_SCOPE void *(*pcre_stack_malloc)(size_t); -PCRE_DATA_SCOPE void (*pcre_stack_free)(void *); -PCRE_DATA_SCOPE int (*pcre_callout)(pcre_callout_block *); -#else /* VPCOMPAT */ -PCRE_DATA_SCOPE void *pcre_malloc(size_t); -PCRE_DATA_SCOPE void pcre_free(void *); -PCRE_DATA_SCOPE void *pcre_stack_malloc(size_t); -PCRE_DATA_SCOPE void pcre_stack_free(void *); -PCRE_DATA_SCOPE int pcre_callout(pcre_callout_block *); -#endif /* VPCOMPAT */ - -/* Exported PCRE functions */ - -PCRE_DATA_SCOPE pcre *pcre_compile(const char *, int, const char **, int *, - const unsigned char *); -PCRE_DATA_SCOPE pcre *pcre_compile2(const char *, int, int *, const char **, - int *, const unsigned char *); -PCRE_DATA_SCOPE int pcre_config(int, void *); -PCRE_DATA_SCOPE int pcre_copy_named_substring(const pcre *, const char *, - int *, int, const char *, char *, int); -PCRE_DATA_SCOPE int pcre_copy_substring(const char *, int *, int, int, char *, - int); -PCRE_DATA_SCOPE int pcre_dfa_exec(const pcre *, const pcre_extra *, - const char *, int, int, int, int *, int , int *, int); -PCRE_DATA_SCOPE int pcre_exec(const pcre *, const pcre_extra *, const char *, - int, int, int, int *, int); -PCRE_DATA_SCOPE void pcre_free_substring(const char *); -PCRE_DATA_SCOPE void pcre_free_substring_list(const char **); -PCRE_DATA_SCOPE int pcre_fullinfo(const pcre *, const pcre_extra *, int, - void *); -PCRE_DATA_SCOPE int pcre_get_named_substring(const pcre *, const char *, - int *, int, const char *, const char **); -PCRE_DATA_SCOPE int pcre_get_stringnumber(const pcre *, const char *); -PCRE_DATA_SCOPE int pcre_get_substring(const char *, int *, int, int, - const char **); -PCRE_DATA_SCOPE int pcre_get_substring_list(const char *, int *, int, - const char ***); -PCRE_DATA_SCOPE int pcre_info(const pcre *, int *, int *); -PCRE_DATA_SCOPE const unsigned char *pcre_maketables(void); -PCRE_DATA_SCOPE int pcre_refcount(pcre *, int); -PCRE_DATA_SCOPE pcre_extra *pcre_study(const pcre *, int, const char **); -PCRE_DATA_SCOPE const char *pcre_version(void); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* End of pcre.h */ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* In its original form, this is the .in file that is transformed by +"configure" into pcre.h. + + Copyright (c) 1997-2005 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +#ifndef _PCRE_H +#define _PCRE_H + +/* The file pcre.h is build by "configure". Do not edit it; instead +make changes to pcre.in. */ + +#define PCRE_MAJOR 6 +#define PCRE_MINOR 3 +#define PCRE_DATE 15-Aug-2005 + +/* Win32 uses DLL by default; it needs special stuff for exported functions. */ + +#ifdef _WIN32 +# ifdef PCRE_DEFINITION +# ifdef DLL_EXPORT +# define PCRE_DATA_SCOPE __declspec(dllexport) +# endif +# else +# ifndef PCRE_STATIC +# define PCRE_DATA_SCOPE extern __declspec(dllimport) +# endif +# endif +#endif + +/* For other operating systems, we use the standard "extern". */ + +#ifndef PCRE_DATA_SCOPE +# ifdef __cplusplus +# define PCRE_DATA_SCOPE extern "C" +# else +# define PCRE_DATA_SCOPE extern +# endif +#endif + +/* Have to include stdlib.h in order to ensure that size_t is defined; +it is needed here for malloc. */ + +#include + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options */ + +#define PCRE_CASELESS 0x00000001 +#define PCRE_MULTILINE 0x00000002 +#define PCRE_DOTALL 0x00000004 +#define PCRE_EXTENDED 0x00000008 +#define PCRE_ANCHORED 0x00000010 +#define PCRE_DOLLAR_ENDONLY 0x00000020 +#define PCRE_EXTRA 0x00000040 +#define PCRE_NOTBOL 0x00000080 +#define PCRE_NOTEOL 0x00000100 +#define PCRE_UNGREEDY 0x00000200 +#define PCRE_NOTEMPTY 0x00000400 +#define PCRE_UTF8 0x00000800 +#define PCRE_NO_AUTO_CAPTURE 0x00001000 +#define PCRE_NO_UTF8_CHECK 0x00002000 +#define PCRE_AUTO_CALLOUT 0x00004000 +#define PCRE_PARTIAL 0x00008000 +#define PCRE_DFA_SHORTEST 0x00010000 +#define PCRE_DFA_RESTART 0x00020000 +#define PCRE_FIRSTLINE 0x00040000 + +/* Exec-time and get/set-time error codes */ + +#define PCRE_ERROR_NOMATCH (-1) +#define PCRE_ERROR_NULL (-2) +#define PCRE_ERROR_BADOPTION (-3) +#define PCRE_ERROR_BADMAGIC (-4) +#define PCRE_ERROR_UNKNOWN_NODE (-5) +#define PCRE_ERROR_NOMEMORY (-6) +#define PCRE_ERROR_NOSUBSTRING (-7) +#define PCRE_ERROR_MATCHLIMIT (-8) +#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */ +#define PCRE_ERROR_BADUTF8 (-10) +#define PCRE_ERROR_BADUTF8_OFFSET (-11) +#define PCRE_ERROR_PARTIAL (-12) +#define PCRE_ERROR_BADPARTIAL (-13) +#define PCRE_ERROR_INTERNAL (-14) +#define PCRE_ERROR_BADCOUNT (-15) +#define PCRE_ERROR_DFA_UITEM (-16) +#define PCRE_ERROR_DFA_UCOND (-17) +#define PCRE_ERROR_DFA_UMLIMIT (-18) +#define PCRE_ERROR_DFA_WSSIZE (-19) +#define PCRE_ERROR_DFA_RECURSE (-20) + +/* Request types for pcre_fullinfo() */ + +#define PCRE_INFO_OPTIONS 0 +#define PCRE_INFO_SIZE 1 +#define PCRE_INFO_CAPTURECOUNT 2 +#define PCRE_INFO_BACKREFMAX 3 +#define PCRE_INFO_FIRSTBYTE 4 +#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */ +#define PCRE_INFO_FIRSTTABLE 5 +#define PCRE_INFO_LASTLITERAL 6 +#define PCRE_INFO_NAMEENTRYSIZE 7 +#define PCRE_INFO_NAMECOUNT 8 +#define PCRE_INFO_NAMETABLE 9 +#define PCRE_INFO_STUDYSIZE 10 +#define PCRE_INFO_DEFAULT_TABLES 11 + +/* Request types for pcre_config() */ + +#define PCRE_CONFIG_UTF8 0 +#define PCRE_CONFIG_NEWLINE 1 +#define PCRE_CONFIG_LINK_SIZE 2 +#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3 +#define PCRE_CONFIG_MATCH_LIMIT 4 +#define PCRE_CONFIG_STACKRECURSE 5 +#define PCRE_CONFIG_UNICODE_PROPERTIES 6 + +/* Bit flags for the pcre_extra structure */ + +#define PCRE_EXTRA_STUDY_DATA 0x0001 +#define PCRE_EXTRA_MATCH_LIMIT 0x0002 +#define PCRE_EXTRA_CALLOUT_DATA 0x0004 +#define PCRE_EXTRA_TABLES 0x0008 + +/* Types */ + +struct real_pcre; /* declaration; the definition is private */ +typedef struct real_pcre pcre; + +/* The structure for passing additional data to pcre_exec(). This is defined in +such as way as to be extensible. Always add new fields at the end, in order to +remain compatible. */ + +typedef struct pcre_extra { + unsigned long int flags; /* Bits for which fields are set */ + void *study_data; /* Opaque data from pcre_study() */ + unsigned long int match_limit; /* Maximum number of calls to match() */ + void *callout_data; /* Data passed back in callouts */ + const unsigned char *tables; /* Pointer to character tables */ +} pcre_extra; + +/* The structure for passing out data via the pcre_callout_function. We use a +structure so that new fields can be added on the end in future versions, +without changing the API of the function, thereby allowing old clients to work +without modification. */ + +typedef struct pcre_callout_block { + int version; /* Identifies version of block */ + /* ------------------------ Version 0 ------------------------------- */ + int callout_number; /* Number compiled into pattern */ + int *offset_vector; /* The offset vector */ + const char *subject; /* The subject being matched */ + int subject_length; /* The length of the subject */ + int start_match; /* Offset to start of this match attempt */ + int current_position; /* Where we currently are in the subject */ + int capture_top; /* Max current capture */ + int capture_last; /* Most recently closed capture */ + void *callout_data; /* Data passed in with the call */ + /* ------------------- Added for Version 1 -------------------------- */ + int pattern_position; /* Offset to next item in the pattern */ + int next_item_length; /* Length of next item in the pattern */ + /* ------------------------------------------------------------------ */ +} pcre_callout_block; + +/* Indirection for store get and free functions. These can be set to +alternative malloc/free functions if required. Special ones are used in the +non-recursive case for "frames". There is also an optional callout function +that is triggered by the (?) regex item. For Virtual Pascal, these definitions +have to take another form. */ + +#ifndef VPCOMPAT +PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t); +PCRE_DATA_SCOPE void (*pcre_free)(void *); +PCRE_DATA_SCOPE void *(*pcre_stack_malloc)(size_t); +PCRE_DATA_SCOPE void (*pcre_stack_free)(void *); +PCRE_DATA_SCOPE int (*pcre_callout)(pcre_callout_block *); +#else /* VPCOMPAT */ +PCRE_DATA_SCOPE void *pcre_malloc(size_t); +PCRE_DATA_SCOPE void pcre_free(void *); +PCRE_DATA_SCOPE void *pcre_stack_malloc(size_t); +PCRE_DATA_SCOPE void pcre_stack_free(void *); +PCRE_DATA_SCOPE int pcre_callout(pcre_callout_block *); +#endif /* VPCOMPAT */ + +/* Exported PCRE functions */ + +PCRE_DATA_SCOPE pcre *pcre_compile(const char *, int, const char **, int *, + const unsigned char *); +PCRE_DATA_SCOPE pcre *pcre_compile2(const char *, int, int *, const char **, + int *, const unsigned char *); +PCRE_DATA_SCOPE int pcre_config(int, void *); +PCRE_DATA_SCOPE int pcre_copy_named_substring(const pcre *, const char *, + int *, int, const char *, char *, int); +PCRE_DATA_SCOPE int pcre_copy_substring(const char *, int *, int, int, char *, + int); +PCRE_DATA_SCOPE int pcre_dfa_exec(const pcre *, const pcre_extra *, + const char *, int, int, int, int *, int , int *, int); +PCRE_DATA_SCOPE int pcre_exec(const pcre *, const pcre_extra *, const char *, + int, int, int, int *, int); +PCRE_DATA_SCOPE void pcre_free_substring(const char *); +PCRE_DATA_SCOPE void pcre_free_substring_list(const char **); +PCRE_DATA_SCOPE int pcre_fullinfo(const pcre *, const pcre_extra *, int, + void *); +PCRE_DATA_SCOPE int pcre_get_named_substring(const pcre *, const char *, + int *, int, const char *, const char **); +PCRE_DATA_SCOPE int pcre_get_stringnumber(const pcre *, const char *); +PCRE_DATA_SCOPE int pcre_get_substring(const char *, int *, int, int, + const char **); +PCRE_DATA_SCOPE int pcre_get_substring_list(const char *, int *, int, + const char ***); +PCRE_DATA_SCOPE int pcre_info(const pcre *, int *, int *); +PCRE_DATA_SCOPE const unsigned char *pcre_maketables(void); +PCRE_DATA_SCOPE int pcre_refcount(pcre *, int); +PCRE_DATA_SCOPE pcre_extra *pcre_study(const pcre *, int, const char **); +PCRE_DATA_SCOPE const char *pcre_version(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* End of pcre.h */ diff --git a/src/map/pet.c b/src/map/pet.c index 836c9fa97..4d153be8d 100644 --- a/src/map/pet.c +++ b/src/map/pet.c @@ -1,1400 +1,1400 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include - -#include "../common/db.h" -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/malloc.h" -#include "../common/showmsg.h" -#include "../common/ers.h" - -#include "pc.h" -#include "status.h" -#include "map.h" -#include "intif.h" -#include "clif.h" -#include "chrif.h" -#include "pet.h" -#include "itemdb.h" -#include "battle.h" -#include "mob.h" -#include "npc.h" -#include "script.h" -#include "skill.h" -#include "unit.h" - -#define MIN_PETTHINKTIME 100 - -struct pet_db pet_db[MAX_PET_DB]; - -static struct eri *item_drop_ers; //For loot drops delay structures. -static struct eri *item_drop_list_ers; - -int pet_hungry_val(struct pet_data *pd) -{ - nullpo_retr(0, pd); - - if(pd->pet.hungry > 90) - return 4; - else if(pd->pet.hungry > 75) - return 3; - else if(pd->pet.hungry > 25) - return 2; - else if(pd->pet.hungry > 10) - return 1; - else - return 0; -} - -static int pet_calc_pos(struct pet_data *pd,int tx,int ty,int dir) -{ - int x,y,dx,dy; - int i,k; - - nullpo_retr(0, pd); - - pd->ud.to_x = tx; - pd->ud.to_y = ty; - - if(dir < 0 || dir >= 8) - return 1; - - dx = -dirx[dir]*2; - dy = -diry[dir]*2; - x = tx + dx; - y = ty + dy; - if(!unit_can_reach_pos(&pd->bl,x,y,0)) { - if(dx > 0) x--; - else if(dx < 0) x++; - if(dy > 0) y--; - else if(dy < 0) y++; - if(!unit_can_reach_pos(&pd->bl,x,y,0)) { - for(i=0;i<12;i++) { - k = rand()%8; - dx = -dirx[k]*2; - dy = -diry[k]*2; - x = tx + dx; - y = ty + dy; - if(unit_can_reach_pos(&pd->bl,x,y,0)) - break; - else { - if(dx > 0) x--; - else if(dx < 0) x++; - if(dy > 0) y--; - else if(dy < 0) y++; - if(unit_can_reach_pos(&pd->bl,x,y,0)) - break; - } - } - if(i>=12) { - x = tx; - y = ty; - if(!unit_can_reach_pos(&pd->bl,x,y,0)) - return 1; - } - } - } - pd->ud.to_x = x; - pd->ud.to_y = y; - return 0; -} - -int pet_create_egg(struct map_session_data *sd, int item_id) -{ - int pet_id = search_petDB_index(item_id, PET_EGG); - if (pet_id < 0) return 0; //No pet egg here. - sd->catch_target_class = pet_db[pet_id].class_; - intif_create_pet(sd->status.account_id, sd->status.char_id, - (short)pet_db[pet_id].class_, - (short)mob_db(pet_db[pet_id].class_)->lv, - (short)pet_db[pet_id].EggID, 0, - (short)pet_db[pet_id].intimate, - 100, 0, 1, pet_db[pet_id].jname); - return 1; -} - -int pet_unlocktarget(struct pet_data *pd) -{ - nullpo_retr(0, pd); - - pd->target_id=0; - pet_stop_attack(pd); - pet_stop_walking(pd,1); - return 0; -} - -/*========================================== - * Pet Attack Skill [Skotlex] - *------------------------------------------ - */ -int pet_attackskill(struct pet_data *pd, int target_id) -{ - struct block_list *bl; - int inf; - - if (!battle_config.pet_status_support || !pd->a_skill || - (battle_config.pet_equip_required && !pd->pet.equip)) - return 0; - - if (DIFF_TICK(pd->ud.canact_tick, gettick()) > 0) - return 0; - - if (rand()%100 < (pd->a_skill->rate +pd->pet.intimate*pd->a_skill->bonusrate/1000)) - { //Skotlex: Use pet's skill - bl=map_id2bl(target_id); - if(bl == NULL || pd->bl.m != bl->m || bl->prev == NULL || status_isdead(bl) || - !check_distance_bl(&pd->bl, bl, pd->db->range3)) - return 0; - - inf = skill_get_inf(pd->a_skill->id); - if (inf & INF_GROUND_SKILL) - unit_skilluse_pos(&pd->bl, bl->x, bl->y, pd->a_skill->id, pd->a_skill->lv); - else //Offensive self skill? Could be stuff like GX. - unit_skilluse_id(&pd->bl,(inf&INF_SELF_SKILL?pd->bl.id:bl->id), pd->a_skill->id, pd->a_skill->lv); - return 1; //Skill invoked. - } - return 0; -} - -int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type) -{ - struct pet_data *pd; - int rate; - - pd = sd->pd; - - Assert((pd->msd == 0) || (pd->msd->pd == pd)); - - if(bl == NULL || bl->type != BL_MOB || bl->prev == NULL || - pd->pet.intimate < battle_config.pet_support_min_friendly || - pd->pet.hungry < 1 || - pd->pet.class_ == status_get_class(bl)) - return 0; - - if(pd->bl.m != bl->m || - !check_distance_bl(&pd->bl, bl, pd->db->range2)) - return 0; - - if (!status_check_skilluse(&pd->bl, bl, 0, 0)) - return 0; - - if(!type) { - rate = pd->petDB->attack_rate; - rate = rate * pd->rate_fix/1000; - if(pd->petDB->attack_rate > 0 && rate <= 0) - rate = 1; - } else { - rate = pd->petDB->defence_attack_rate; - rate = rate * pd->rate_fix/1000; - if(pd->petDB->defence_attack_rate > 0 && rate <= 0) - rate = 1; - } - if(rand()%10000 < rate) - { - if(pd->target_id == 0 || rand()%10000 < pd->petDB->change_target_rate) - pd->target_id = bl->id; - } - - return 0; -} -/*========================================== - * Pet SC Check [Skotlex] - *------------------------------------------ - */ -int pet_sc_check(struct map_session_data *sd, int type) -{ - struct pet_data *pd; - - nullpo_retr(0, sd); - pd = sd->pd; - - if (pd == NULL || - (battle_config.pet_equip_required && pd->pet.equip == 0) || - pd->recovery == NULL || - pd->recovery->timer != -1 || - pd->recovery->type != type) - return 1; - - pd->recovery->timer = add_timer(gettick()+pd->recovery->delay*1000,pet_recovery_timer,sd->bl.id,0); - - return 0; -} - -static int pet_hungry(int tid,unsigned int tick,int id,int data) -{ - struct map_session_data *sd; - struct pet_data *pd; - int interval,t; - - sd=map_id2sd(id); - if(!sd) - return 1; - - if(!sd->status.pet_id || !sd->pd) - return 1; - - pd = sd->pd; - if(pd->pet_hungry_timer != tid){ - if(battle_config.error_log) - ShowError("pet_hungry_timer %d != %d\n",pd->pet_hungry_timer,tid); - return 0; - } - pd->pet_hungry_timer = -1; - - if (pd->pet.intimate <= 0) - return 1; //You lost the pet already, the rest is irrelevant. - - pd->pet.hungry--; - t = pd->pet.intimate; - if(pd->pet.hungry < 0) { - pet_stop_attack(pd); - pd->pet.hungry = 0; - pd->pet.intimate -= battle_config.pet_hungry_friendly_decrease; - if(pd->pet.intimate <= 0) { - pd->pet.intimate = 0; - pd->status.speed = pd->db->status.speed; - } - status_calc_pet(pd, 0); - clif_send_petdata(sd,1,pd->pet.intimate); - } - clif_send_petdata(sd,2,pd->pet.hungry); - - if(battle_config.pet_hungry_delay_rate != 100) - interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100; - else - interval = pd->petDB->hungry_delay; - if(interval <= 0) - interval = 1; - pd->pet_hungry_timer = add_timer(tick+interval,pet_hungry,sd->bl.id,0); - - return 0; -} - -int search_petDB_index(int key,int type) -{ - int i; - - for(i=0;ipet_hungry_timer != -1) { - delete_timer(pd->pet_hungry_timer,pet_hungry); - pd->pet_hungry_timer = -1; - } - - return 1; -} - -static int pet_performance(struct map_session_data *sd, struct pet_data *pd) -{ - int val; - - if (pd->pet.intimate > 900) - val = (pd->petDB->s_perfor > 0)? 4:3; - else if(pd->pet.intimate > 750) - val = 2; - else - val = 1; - - pet_stop_walking(pd,2000<<8); - clif_pet_performance(&pd->bl,rand()%val + 1); - pet_lootitem_drop(pd,NULL); - return 1; -} - -static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) -{ - struct item tmp_item; - int flag; - - pet_lootitem_drop(pd,sd); - malloc_set(&tmp_item,0,sizeof(tmp_item)); - tmp_item.nameid = pd->petDB->EggID; - tmp_item.identify = 1; - tmp_item.card[0] = CARD0_PET; - tmp_item.card[1] = GetWord(pd->pet.pet_id,0); - tmp_item.card[2] = GetWord(pd->pet.pet_id,1); - tmp_item.card[3] = pd->pet.rename_flag; - if((flag = pc_additem(sd,&tmp_item,1))) { - clif_additem(sd,0,0,flag); - map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - pd->pet.incuvate = 1; - //No need, pet is saved on unit_free below. - //intif_save_petdata(sd->status.account_id,&pd->pet); - if(pd->state.skillbonus) { - pd->state.skillbonus = 0; - status_calc_pc(sd,0); - } - unit_free(&pd->bl,0); - sd->status.pet_id = 0; - - return 1; -} - -int pet_data_init(struct map_session_data *sd, struct s_pet *pet) -{ - struct pet_data *pd; - int i=0,interval=0; - - nullpo_retr(1, sd); - - Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); - - if(sd->status.account_id != pet->account_id || sd->status.char_id != pet->char_id) { - sd->status.pet_id = 0; - return 1; - } - if (sd->status.pet_id != pet->pet_id) { - if (sd->status.pet_id) { - //Wrong pet?? Set incuvate to no and send it back for saving. - pet->incuvate = 1; - intif_save_petdata(sd->status.account_id,pet); - sd->status.pet_id = 0; - return 1; - } - //The pet_id value was lost? odd... restore it. - sd->status.pet_id = pet->pet_id; - } - - i = search_petDB_index(pet->class_,PET_CLASS); - if(i < 0) { - sd->status.pet_id = 0; - return 1; - } - sd->pd = pd = (struct pet_data *)aCalloc(1,sizeof(struct pet_data)); - pd->petDB = &pet_db[i]; - memcpy(&pd->pet, pet, sizeof(struct s_pet)); - pd->bl.m = sd->bl.m; - pd->bl.x = sd->bl.x; - pd->bl.y = sd->bl.y; - pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->ud.dir); - pd->bl.x = pd->ud.to_x; - pd->bl.y = pd->ud.to_y; - pd->bl.id = npc_get_new_npc_id(); - pd->db = mob_db(pet->class_); - pd->bl.subtype = MONS; - pd->bl.type = BL_PET; - pd->msd = sd; - status_set_viewdata(&pd->bl, pet->class_); - unit_dataset(&pd->bl); - pd->ud.dir = sd->ud.dir; - pd->last_thinktime = gettick(); - - map_addiddb(&pd->bl); - - // initialise - status_calc_pet(pd,1); - - pd->state.skillbonus = 0; - if (battle_config.pet_status_support) //Skotlex - run_script(pet_db[i].script,0,sd->bl.id,0); - - if(battle_config.pet_hungry_delay_rate != 100) - interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100; - else - interval = pd->petDB->hungry_delay; - if(interval <= 0) - interval = 1; - pd->pet_hungry_timer = add_timer(gettick()+interval,pet_hungry,sd->bl.id,0); - return 0; -} - -int pet_birth_process(struct map_session_data *sd, struct s_pet *pet) -{ - nullpo_retr(1, sd); - - Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); - - if(sd->status.pet_id && pet->incuvate == 1) { - sd->status.pet_id = 0; - return 1; - } - - pet->incuvate = 0; - pet->account_id = sd->status.account_id; - pet->char_id = sd->status.char_id; - sd->status.pet_id = pet->pet_id; - if(pet_data_init(sd, pet)) { - sd->status.pet_id = 0; - return 1; - } - - intif_save_petdata(sd->status.account_id,pet); - if (save_settings&8) - chrif_save(sd,0); //is it REALLY Needed to save the char for hatching a pet? [Skotlex] - - if(sd->bl.prev != NULL) { - map_addblock(&sd->pd->bl); - clif_spawn(&sd->pd->bl); - clif_send_petdata(sd,0,0); - clif_send_petdata(sd,5,battle_config.pet_hair_style); - clif_pet_equip(sd->pd); - clif_send_petstatus(sd); - } - Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); - - return 0; -} - -int pet_recv_petdata(int account_id,struct s_pet *p,int flag) -{ - struct map_session_data *sd; - - sd = map_id2sd(account_id); - if(sd == NULL) - return 1; - if(flag == 1) { - sd->status.pet_id = 0; - return 1; - } - if(p->incuvate == 1) { - int i; - //Delete egg from inventory. [Skotlex] - for (i = 0; i < MAX_INVENTORY; i++) { - if(sd->status.inventory[i].card[0] == CARD0_PET && - p->pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2])) - break; - } - if(i >= MAX_INVENTORY) { - if (battle_config.error_log) - ShowError("pet_recv_petdata: Hatching pet (%d:%s) aborted, couldn't find egg in inventory for removal!\n",p->pet_id, p->name); - sd->status.pet_id = 0; - return 1; - } - if (!pet_birth_process(sd,p)) //Pet hatched. Delete egg. - pc_delitem(sd,i,1,0); - } else { - pet_data_init(sd,p); - if(sd->pd && sd->bl.prev != NULL) { - map_addblock(&sd->pd->bl); - clif_spawn(&sd->pd->bl); - clif_send_petdata(sd,0,0); - clif_send_petdata(sd,5,battle_config.pet_hair_style); - clif_pet_equip(sd->pd); - clif_send_petstatus(sd); - } - } - - return 0; -} - -int pet_select_egg(struct map_session_data *sd,short egg_index) -{ - nullpo_retr(0, sd); - - if(egg_index < 0 || egg_index >= MAX_INVENTORY) - return 0; //Forged packet! - - if(sd->status.inventory[egg_index].card[0] == CARD0_PET) - intif_request_petdata(sd->status.account_id, sd->status.char_id, MakeDWord(sd->status.inventory[egg_index].card[1], sd->status.inventory[egg_index].card[2]) ); - else { - if(battle_config.error_log) - ShowError("wrong egg item inventory %d\n",egg_index); - } - return 0; -} - -int pet_catch_process1(struct map_session_data *sd,int target_class) -{ - nullpo_retr(0, sd); - - sd->catch_target_class = target_class; - clif_catch_process(sd); - - return 0; -} - -int pet_catch_process2(struct map_session_data *sd,int target_id) -{ - struct mob_data *md; - int i=0,pet_catch_rate=0; - - nullpo_retr(1, sd); - - md=(struct mob_data*)map_id2bl(target_id); - if(!md || md->bl.type != BL_MOB || md->bl.prev == NULL){ - //Abort capture. - sd->catch_target_class = -1; - sd->itemid = sd->itemindex = -1; - return 1; - } - - if (sd->menuskill_id != SA_TAMINGMONSTER) - { //Exploit? - clif_pet_rulet(sd,0); - sd->catch_target_class = -1; - return 1; - } - - if (sd->menuskill_lv > 0) - { //Consume the pet lure [Skotlex] - i=pc_search_inventory(sd,sd->menuskill_lv); - if (i < 0) - { //they tried an exploit? - clif_pet_rulet(sd,0); - sd->catch_target_class = -1; - return 1; - } - //Delete the item - if (sd->itemid == sd->menuskill_lv) - sd->itemid = sd->itemindex = -1; - sd->menuskill_id = sd->menuskill_lv = 0; - pc_delitem(sd,i,1,0); - } - - i = search_petDB_index(md->class_,PET_CLASS); - //catch_target_class == 0 is used for universal lures. [Skotlex] - //for now universal lures do not include bosses. - if (sd->catch_target_class == 0 && !(md->status.mode&MD_BOSS)) - sd->catch_target_class = md->class_; - if(i < 0 || sd->catch_target_class != md->class_) { - clif_emotion(&md->bl, 7); //mob will do /ag if wrong lure is used on them. - clif_pet_rulet(sd,0); - sd->catch_target_class = -1; - return 1; - } - - pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - md->level)*30 + sd->battle_status.luk*20)*(200 - md->status.hp*100/md->status.max_hp)/100; - if(pet_catch_rate < 1) pet_catch_rate = 1; - if(battle_config.pet_catch_rate != 100) - pet_catch_rate = (pet_catch_rate*battle_config.pet_catch_rate)/100; - - if(rand()%10000 < pet_catch_rate) { - unit_remove_map(&md->bl,0); - status_kill(&md->bl); - clif_pet_rulet(sd,1); -// if(battle_config.etc_log) -// printf("rulet success %d\n",target_id); - intif_create_pet(sd->status.account_id,sd->status.char_id,pet_db[i].class_,mob_db(pet_db[i].class_)->lv, - pet_db[i].EggID,0,pet_db[i].intimate,100,0,1,pet_db[i].jname); - } - else - { - sd->catch_target_class = -1; - clif_pet_rulet(sd,0); - } - - return 0; -} - -int pet_get_egg(int account_id,int pet_id,int flag) -{ //This function is invoked when a new pet has been created, and at no other time! - struct map_session_data *sd; - struct item tmp_item; - int i=0,ret=0; - - if(flag) - return 0; - - sd = map_id2sd(account_id); - if(sd == NULL) - return 0; - - i = search_petDB_index(sd->catch_target_class,PET_CLASS); - sd->catch_target_class = -1; - - if(i < 0) { - intif_delete_petdata(pet_id); - return 0; - } - - malloc_set(&tmp_item,0,sizeof(tmp_item)); - tmp_item.nameid = pet_db[i].EggID; - tmp_item.identify = 1; - tmp_item.card[0] = CARD0_PET; - tmp_item.card[1] = GetWord(pet_id,0); - tmp_item.card[2] = GetWord(pet_id,1); - tmp_item.card[3] = 0; //New pets are not named. - if((ret = pc_additem(sd,&tmp_item,1))) { - clif_additem(sd,0,0,ret); - map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - - return 1; -} - -static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd); -static int pet_food(struct map_session_data *sd, struct pet_data *pd); -static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap); - -int pet_menu(struct map_session_data *sd,int menunum) -{ - nullpo_retr(0, sd); - if (sd->pd == NULL) - return 1; - - //You lost the pet already. - if(!sd->status.pet_id || sd->pd->pet.intimate <= 0) - return 1; - - switch(menunum) { - case 0: - clif_send_petstatus(sd); - break; - case 1: - pet_food(sd, sd->pd); - break; - case 2: - pet_performance(sd, sd->pd); - break; - case 3: - pet_return_egg(sd, sd->pd); - break; - case 4: - pet_unequipitem(sd, sd->pd); - break; - } - return 0; -} - -int pet_change_name(struct map_session_data *sd,char *name, int flag) //flag 0 = check name, 1 = good name -{ - int i; - struct pet_data *pd; - nullpo_retr(1, sd); - - pd = sd->pd; - if((pd == NULL) || (pd->pet.rename_flag == 1 && !battle_config.pet_rename)) - return 1; - - for(i=0;ipet.name, name, NAME_LENGTH-1); - - clif_charnameack (0,&pd->bl); - pd->pet.rename_flag = 1; - clif_pet_equip(pd); - clif_send_petstatus(sd); - - return 0; -} - -int pet_equipitem(struct map_session_data *sd,int index) -{ - struct pet_data *pd; - int nameid; - - nullpo_retr(1, sd); - pd = sd->pd; - if (!pd) return 1; - - nameid = sd->status.inventory[index].nameid; - - if(pd->petDB->AcceID == 0 || nameid != pd->petDB->AcceID || pd->pet.equip != 0) { - clif_equipitemack(sd,0,0,0); - return 1; - } - - pc_delitem(sd,index,1,0); - pd->pet.equip = nameid; - status_set_viewdata(&pd->bl, pd->pet.class_); //Updates view_data. - clif_pet_equip(pd); - if (battle_config.pet_equip_required) - { //Skotlex: start support timers if need - unsigned int tick = gettick(); - if (pd->s_skill && pd->s_skill->timer == -1) - { - if (pd->s_skill->id) - pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_skill_support_timer, sd->bl.id, 0); - else - pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_heal_timer, sd->bl.id, 0); - } - if (pd->bonus && pd->bonus->timer == -1) - pd->bonus->timer=add_timer(tick+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0); - } - - return 0; -} - -static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd) -{ - struct item tmp_item; - int nameid,flag; - - if(pd->pet.equip == 0) - return 1; - - nameid = pd->pet.equip; - pd->pet.equip = 0; - status_set_viewdata(&pd->bl, pd->pet.class_); - clif_pet_equip(pd); - malloc_set(&tmp_item,0,sizeof(tmp_item)); - tmp_item.nameid = nameid; - tmp_item.identify = 1; - if((flag = pc_additem(sd,&tmp_item,1))) { - clif_additem(sd,0,0,flag); - map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - if (battle_config.pet_equip_required) - { //Skotlex: halt support timers if needed - if(pd->state.skillbonus) { - pd->state.skillbonus = 0; - status_calc_pc(sd,0); - } - if (pd->s_skill && pd->s_skill->timer != -1) - { - if (pd->s_skill->id) - delete_timer(pd->s_skill->timer, pet_skill_support_timer); - else - delete_timer(pd->s_skill->timer, pet_heal_timer); - pd->s_skill->timer = -1; - } - if (pd->bonus && pd->bonus->timer != -1) - { - delete_timer(pd->bonus->timer, pet_skill_bonus_timer); - pd->bonus->timer = -1; - } - } - - return 0; -} - -static int pet_food(struct map_session_data *sd, struct pet_data *pd) -{ - int i,k; - - k=pd->petDB->FoodID; - i=pc_search_inventory(sd,k); - if(i < 0) { - clif_pet_food(sd,k,0); - return 1; - } - pc_delitem(sd,i,1,0); - - if(pd->pet.hungry > 90) - pd->pet.intimate -= pd->petDB->r_full; - else { - if(battle_config.pet_friendly_rate != 100) - k = (pd->petDB->r_hungry * battle_config.pet_friendly_rate)/100; - else - k = pd->petDB->r_hungry; - if(pd->pet.hungry > 75) { - k = k >> 1; - if(k <= 0) - k = 1; - } - pd->pet.intimate += k; - } - if(pd->pet.intimate <= 0) { - pd->pet.intimate = 0; - pet_stop_attack(pd); - pd->status.speed = pd->db->status.speed; - } - else if(pd->pet.intimate > 1000) - pd->pet.intimate = 1000; - status_calc_pet(pd, 0); - pd->pet.hungry += pd->petDB->fullness; - if(pd->pet.hungry > 100) - pd->pet.hungry = 100; - - clif_send_petdata(sd,2,pd->pet.hungry); - clif_send_petdata(sd,1,pd->pet.intimate); - clif_pet_food(sd,pd->petDB->FoodID,1); - - return 0; -} - -static int pet_randomwalk(struct pet_data *pd,unsigned int tick) -{ - const int retrycount=20; - - nullpo_retr(0, pd); - - Assert((pd->msd == 0) || (pd->msd->pd == pd)); - - if(DIFF_TICK(pd->next_walktime,tick) < 0 && unit_can_move(&pd->bl)) { - int i,x,y,c,d=12-pd->move_fail_count; - if(d<5) d=5; - for(i=0;ibl.x+r%(d*2+1)-d; - y=pd->bl.y+r/(d*2+1)%(d*2+1)-d; - if(map_getcell(pd->bl.m,x,y,CELL_CHKPASS) && unit_walktoxy(&pd->bl,x,y,0)){ - pd->move_fail_count=0; - break; - } - if(i+1>=retrycount){ - pd->move_fail_count++; - if(pd->move_fail_count>1000){ - if(battle_config.error_log) - ShowWarning("PET cant move. hold position %d, class = %d\n",pd->bl.id,pd->pet.class_); - pd->move_fail_count=0; - pd->ud.canmove_tick = tick + 60000; - return 0; - } - } - } - for(i=c=0;iud.walkpath.path_len;i++){ - if(pd->ud.walkpath.path[i]&1) - c+=pd->status.speed*14/10; - else - c+=pd->status.speed; - } - pd->next_walktime = tick+rand()%3000+3000+c; - - return 1; - } - return 0; -} - -static int pet_ai_sub_hard(struct pet_data *pd, struct map_session_data *sd, unsigned int tick) -{ - struct block_list *target = NULL; - - if(pd->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL) - return 0; - - if(DIFF_TICK(tick,pd->last_thinktime) < MIN_PETTHINKTIME) - return 0; - pd->last_thinktime=tick; - - if(pd->ud.attacktimer != -1 || pd->ud.skilltimer != -1 || pd->bl.m != sd->bl.m) - return 0; - - if(pd->ud.walktimer != -1 && pd->ud.walkpath.path_pos <= 2) - return 0; //No thinking when you just started to walk. - - if(pd->pet.intimate <= 0) { - //Pet should just... well, random walk. - pet_randomwalk(pd,tick); - return 0; - } - - if (!check_distance_bl(&sd->bl, &pd->bl, pd->db->range3)) { - //Master too far, chase. - if(pd->target_id) - pet_unlocktarget(pd); - if(pd->ud.walktimer != -1 && pd->ud.target == sd->bl.id) - return 0; //Already walking to him - if (DIFF_TICK(tick, pd->ud.canmove_tick) < 0) - return 0; //Can't move yet. - pd->status.speed = (sd->battle_status.speed>>1); - if(pd->status.speed <= 0) - pd->status.speed = 1; - if (!unit_walktobl(&pd->bl, &sd->bl, 3, 0)) - pet_randomwalk(pd,tick); - return 0; - } - - //Return speed to normal. - if (pd->status.speed != pd->petDB->speed) { - if (pd->ud.walktimer != -1) - return 0; //Wait until the pet finishes walking back to master. - pd->status.speed = pd->petDB->speed; - } - - if (pd->target_id) { - target= map_id2bl(pd->target_id); - if (!target || pd->bl.m != target->m || status_isdead(target) || - !check_distance_bl(&pd->bl, target, pd->db->range3)) - { - target = NULL; - pet_unlocktarget(pd); - } - } - - if(!target && pd->loot && pd->loot->count < pd->loot->max && DIFF_TICK(tick,pd->ud.canact_tick)>0) { - //Use half the pet's range of sight. - map_foreachinrange(pet_ai_sub_hard_lootsearch,&pd->bl, - pd->db->range2/2, BL_ITEM,pd,&target); - } - - if (!target) { - //Just walk around. - if (check_distance_bl(&sd->bl, &pd->bl, 3)) - return 0; //Already next to master. - - if(pd->ud.walktimer != -1 && check_distance_blxy(&sd->bl, pd->ud.to_x,pd->ud.to_y, 3)) - return 0; //Already walking to him - - pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->ud.dir); - if(!unit_walktoxy(&pd->bl,pd->ud.to_x,pd->ud.to_y,0)) - pet_randomwalk(pd,tick); - - return 0; - } - - if(pd->ud.target == target->id && - (pd->ud.attacktimer != -1 || pd->ud.walktimer != -1)) - return 0; //Target already locked. - - if (target->type != BL_ITEM) - { //enemy targetted - if(!battle_check_range(&pd->bl,target,pd->status.rhw.range)) - { //Chase - if(!unit_walktobl(&pd->bl, target, pd->status.rhw.range, 2)) - pet_unlocktarget(pd); //Unreachable target. - return 0; - } - //Continuous attack. - unit_attack(&pd->bl, pd->target_id, 1); - } else { //Item Targeted, attempt loot - if (!check_distance_bl(&pd->bl, target, 1)) - { //Out of range - if(!unit_walktobl(&pd->bl, target, 0, 1)) //Unreachable target. - pet_unlocktarget(pd); - return 0; - } else{ - struct flooritem_data *fitem = (struct flooritem_data *)target; - if(pd->loot->count < pd->loot->max){ - memcpy(&pd->loot->item[pd->loot->count++],&fitem->item_data,sizeof(pd->loot->item[0])); - pd->loot->weight += itemdb_search(fitem->item_data.nameid)->weight*fitem->item_data.amount; - map_clearflooritem(target->id); - } - //Target is unlocked regardless of whether it was picked or not. - pet_unlocktarget(pd); - } - } - return 0; -} - -static int pet_ai_sub_foreachclient(struct map_session_data *sd,va_list ap) -{ - unsigned int tick = va_arg(ap,unsigned int); - if(sd->status.pet_id && sd->pd) - pet_ai_sub_hard(sd->pd,sd,tick); - - return 0; -} - -static int pet_ai_hard(int tid,unsigned int tick,int id,int data) -{ - clif_foreachclient(pet_ai_sub_foreachclient,tick); - - return 0; -} - -static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap) -{ - struct pet_data* pd; - struct flooritem_data *fitem = (struct flooritem_data *)bl; - struct block_list **target; - int sd_id =0; - - pd=va_arg(ap,struct pet_data *); - target=va_arg(ap,struct block_list**); - - sd_id = fitem->first_get_id; - - if(sd_id && sd_id != pd->msd->bl.id) - return 0; - - if(unit_can_reach_bl(&pd->bl,bl, pd->db->range2, 1, NULL, NULL) && - ((*target) == NULL || //New target closer than previous one. - !check_distance_bl(&pd->bl, *target, distance_bl(&pd->bl, bl)))) - { - (*target) = bl; - pd->target_id = bl->id; - return 1; - } - - return 0; -} - -static int pet_delay_item_drop(int tid,unsigned int tick,int id,int data) -{ - struct item_drop_list *list; - struct item_drop *ditem, *ditem_prev; - list=(struct item_drop_list *)id; - ditem = list->item; - while (ditem) { - map_addflooritem(&ditem->item_data,ditem->item_data.amount, - list->m,list->x,list->y, - list->first_sd,list->second_sd,list->third_sd,0); - ditem_prev = ditem; - ditem = ditem->next; - ers_free(item_drop_ers, ditem_prev); - } - ers_free(item_drop_list_ers, list); - return 0; -} - -int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd) -{ - int i,flag=0; - struct item_drop_list *dlist; - struct item_drop *ditem; - struct item *it; - if(!pd || !pd->loot || !pd->loot->count) - return 0; - dlist = ers_alloc(item_drop_list_ers, struct item_drop_list); - dlist->m = pd->bl.m; - dlist->x = pd->bl.x; - dlist->y = pd->bl.y; - dlist->first_sd = NULL; - dlist->second_sd = NULL; - dlist->third_sd = NULL; - dlist->item = NULL; - - for(i=0;iloot->count;i++) { - it = &pd->loot->item[i]; - if(sd){ - if((flag = pc_additem(sd,it,it->amount))){ - clif_additem(sd,0,0,flag); - ditem = ers_alloc(item_drop_ers, struct item_drop); - memcpy(&ditem->item_data, it, sizeof(struct item)); - ditem->next = dlist->item; - dlist->item = ditem; - } - } - else { - ditem = ers_alloc(item_drop_ers, struct item_drop); - memcpy(&ditem->item_data, it, sizeof(struct item)); - ditem->next = dlist->item; - dlist->item = ditem; - } - } - //The smart thing to do is use pd->loot->max (thanks for pointing it out, Shinomori) - malloc_set(pd->loot->item,0,pd->loot->max * sizeof(struct item)); - pd->loot->count = 0; - pd->loot->weight = 0; - pd->ud.canact_tick = gettick()+10000; // 10*1000msの間拾わない - - if (dlist->item) - add_timer(gettick()+540,pet_delay_item_drop,(int)dlist,0); - else - ers_free(item_drop_list_ers, dlist); - return 1; -} - -/*========================================== - * pet bonus giving skills [Valaris] / Rewritten by [Skotlex] - *------------------------------------------ - */ -int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data) -{ - struct map_session_data *sd=map_id2sd(id); - struct pet_data *pd; - int bonus; - int timer = 0; - - if(sd == NULL || sd->pd==NULL || sd->pd->bonus == NULL) - return 1; - - pd=sd->pd; - - if(pd->bonus->timer != tid) { - if(battle_config.error_log) - { - ShowError("pet_skill_bonus_timer %d != %d\n",pd->bonus->timer,tid); - pd->bonus->timer = -1; - } - return 0; - } - - // determine the time for the next timer - if (pd->state.skillbonus && pd->bonus->delay > 0) { - bonus = 0; - timer = pd->bonus->delay*1000; // the duration until pet bonuses will be reactivated again - } else if (pd->pet.intimate) { - bonus = 1; - timer = pd->bonus->duration*1000; // the duration for pet bonuses to be in effect - } else { //Lost pet... - pd->bonus->timer = -1; - return 0; - } - - if (pd->state.skillbonus != bonus) { - pd->state.skillbonus = bonus; - status_calc_pc(sd, 0); - } - // wait for the next timer - pd->bonus->timer=add_timer(tick+timer,pet_skill_bonus_timer,sd->bl.id,0); - return 0; -} - -/*========================================== - * pet recovery skills [Valaris] / Rewritten by [Skotlex] - *------------------------------------------ - */ -int pet_recovery_timer(int tid,unsigned int tick,int id,int data) -{ - struct map_session_data *sd=map_id2sd(id); - struct pet_data *pd; - - if(sd==NULL || sd->pd == NULL || sd->pd->recovery == NULL) - return 1; - - pd=sd->pd; - - if(pd->recovery->timer != tid) { - if(battle_config.error_log) - ShowError("pet_recovery_timer %d != %d\n",pd->recovery->timer,tid); - return 0; - } - - if(sd->sc.count && sd->sc.data[pd->recovery->type].timer != -1) - { //Display a heal animation? - //Detoxify is chosen for now. - clif_skill_nodamage(&pd->bl,&sd->bl,TF_DETOXIFY,1,1); - status_change_end(&sd->bl,pd->recovery->type,-1); - clif_emotion(&pd->bl, 33); - } - - pd->recovery->timer = -1; - - return 0; -} - -int pet_heal_timer(int tid,unsigned int tick,int id,int data) -{ - struct map_session_data *sd=map_id2sd(id); - struct status_data *status; - struct pet_data *pd; - short rate = 100; - - if(sd==NULL || sd->pd == NULL || sd->pd->s_skill == NULL) - return 1; - - pd=sd->pd; - - if(pd->s_skill->timer != tid) { - if(battle_config.error_log) - ShowError("pet_heal_timer %d != %d\n",pd->s_skill->timer,tid); - return 0; - } - - status = status_get_status_data(&sd->bl); - - if(pc_isdead(sd) || - (rate = status->sp*100/status->max_sp) > pd->s_skill->sp || - (rate = status->hp*100/status->max_hp) > pd->s_skill->hp || - (rate = (pd->ud.skilltimer != -1)) //Another skill is in effect - ) { //Wait (how long? 1 sec for every 10% of remaining) - pd->s_skill->timer=add_timer(gettick()+(rate>10?rate:10)*100,pet_heal_timer,sd->bl.id,0); - return 0; - } - pet_stop_attack(pd); - pet_stop_walking(pd,1); - clif_skill_nodamage(&pd->bl,&sd->bl,AL_HEAL,pd->s_skill->lv,1); - status_heal(&sd->bl, pd->s_skill->lv,0, 0); - pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0); - return 0; -} - -/*========================================== - * pet support skills [Skotlex] - *------------------------------------------ - */ -int pet_skill_support_timer(int tid,unsigned int tick,int id,int data) -{ - struct map_session_data *sd=map_id2sd(id); - struct pet_data *pd; - struct status_data *status; - short rate = 100; - if(sd==NULL || sd->pd == NULL || sd->pd->s_skill == NULL) - return 1; - - pd=sd->pd; - - if(pd->s_skill->timer != tid) { - if(battle_config.error_log) - ShowError("pet_skill_support_timer %d != %d\n",pd->s_skill->timer,tid); - return 0; - } - - status = status_get_status_data(&sd->bl); - - if (DIFF_TICK(pd->ud.canact_tick, tick) > 0) - { //Wait until the pet can act again. - pd->s_skill->timer=add_timer(pd->ud.canact_tick,pet_skill_support_timer,sd->bl.id,0); - return 0; - } - - if(pc_isdead(sd) || - (rate = status->sp*100/status->max_sp) > pd->s_skill->sp || - (rate = status->hp*100/status->max_hp) > pd->s_skill->hp || - (rate = (pd->ud.skilltimer != -1)) //Another skill is in effect - ) { //Wait (how long? 1 sec for every 10% of remaining) - pd->s_skill->timer=add_timer(tick+(rate>10?rate:10)*100,pet_skill_support_timer,sd->bl.id,0); - return 0; - } - - pet_stop_attack(pd); - pet_stop_walking(pd,1); - - if (skill_get_inf(pd->s_skill->id) & INF_GROUND_SKILL) - unit_skilluse_pos(&pd->bl, sd->bl.x, sd->bl.y, pd->s_skill->id, pd->s_skill->lv); - else - unit_skilluse_id(&pd->bl, sd->bl.id, pd->s_skill->id, pd->s_skill->lv); - - pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0); - return 0; -} - -/*========================================== - *ペットデータ読み込み - *------------------------------------------ - */ -int read_petdb() -{ - FILE *fp; - char line[1024]; - int nameid,i,k; - int j=0; - int lines; - char *filename[]={"pet_db.txt","pet_db2.txt"}; - char *str[32],*p,*np; - - -//Remove any previous scripts in case reloaddb was invoked. - for(j =0; j < MAX_PET_DB; j++) - if (pet_db[j].script) { - aFree(pet_db[j].script); - pet_db[j].script = NULL; - } - j = 0; - malloc_set(pet_db,0,sizeof(pet_db)); - for(i=0;i<2;i++){ - sprintf(line, "%s/%s", db_path, filename[i]); - fp=fopen(line,"r"); - if(fp==NULL){ - if(i>0) - continue; - ShowError("can't read %s\n",line); - return -1; - } - lines = 0; - while(fgets(line,1020,fp) && j < MAX_PET_DB){ - - lines++; - - if(line[0] == '/' && line[1] == '/') - continue; - - for(k=0,p=line;k<20;k++){ - if((np=strchr(p,','))!=NULL){ - str[k]=p; - *np=0; - p=np+1; - } else { - str[k]=p; - p+=strlen(p); - } - } - - nameid=atoi(str[0]); - if(nameid<=0) - continue; - - if (!mobdb_checkid(nameid)) { - ShowWarning("pet_db reading: Invalid mob-class %d, pet not read.\n", nameid); - continue; - } - - pet_db[j].class_ = nameid; - memcpy(pet_db[j].name,str[1],NAME_LENGTH-1); - memcpy(pet_db[j].jname,str[2],NAME_LENGTH-1); - pet_db[j].itemID=atoi(str[3]); - pet_db[j].EggID=atoi(str[4]); - pet_db[j].AcceID=atoi(str[5]); - pet_db[j].FoodID=atoi(str[6]); - pet_db[j].fullness=atoi(str[7]); - pet_db[j].hungry_delay=atoi(str[8])*1000; - pet_db[j].r_hungry=atoi(str[9]); - if(pet_db[j].r_hungry <= 0) - pet_db[j].r_hungry=1; - pet_db[j].r_full=atoi(str[10]); - pet_db[j].intimate=atoi(str[11]); - pet_db[j].die=atoi(str[12]); - pet_db[j].capture=atoi(str[13]); - pet_db[j].speed=atoi(str[14]); - pet_db[j].s_perfor=(char)atoi(str[15]); - pet_db[j].talk_convert_class=atoi(str[16]); - pet_db[j].attack_rate=atoi(str[17]); - pet_db[j].defence_attack_rate=atoi(str[18]); - pet_db[j].change_target_rate=atoi(str[19]); - pet_db[j].script = NULL; - if((np=strchr(p,'{'))==NULL) - continue; - pet_db[j].script = parse_script((unsigned char *) np, filename[i], lines); - j++; - } - if (j >= MAX_PET_DB) - ShowWarning("read_petdb: Reached max number of pets [%d]. Remaining pets were not read.\n ", MAX_PET_DB); - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' pets in '"CL_WHITE"%s"CL_RESET"'.\n",j,filename[i]); - } - return 0; -} - -/*========================================== - * スキル関係初期化処理 - *------------------------------------------ - */ -int do_init_pet(void) -{ - malloc_set(pet_db,0,sizeof(pet_db)); - read_petdb(); - - item_drop_ers = ers_new((uint32)sizeof(struct item_drop)); - item_drop_list_ers = ers_new((uint32)sizeof(struct item_drop_list)); - - add_timer_func_list(pet_hungry,"pet_hungry"); - add_timer_func_list(pet_ai_hard,"pet_ai_hard"); - add_timer_func_list(pet_skill_bonus_timer,"pet_skill_bonus_timer"); // [Valaris] - add_timer_func_list(pet_delay_item_drop,"pet_delay_item_drop"); - add_timer_func_list(pet_skill_support_timer, "pet_skill_support_timer"); // [Skotlex] - add_timer_func_list(pet_recovery_timer,"pet_recovery_timer"); // [Valaris] - add_timer_func_list(pet_heal_timer,"pet_heal_timer"); // [Valaris] - add_timer_interval(gettick()+MIN_PETTHINKTIME,pet_ai_hard,0,0,MIN_PETTHINKTIME); - - return 0; -} - -int do_final_pet(void) { - int i; - for(i = 0;i < MAX_PET_DB; i++) { - if(pet_db[i].script) { - script_free_code(pet_db[i].script); - pet_db[i].script = NULL; - } - } - ers_destroy(item_drop_ers); - ers_destroy(item_drop_list_ers); - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include + +#include "../common/db.h" +#include "../common/timer.h" +#include "../common/nullpo.h" +#include "../common/malloc.h" +#include "../common/showmsg.h" +#include "../common/ers.h" + +#include "pc.h" +#include "status.h" +#include "map.h" +#include "intif.h" +#include "clif.h" +#include "chrif.h" +#include "pet.h" +#include "itemdb.h" +#include "battle.h" +#include "mob.h" +#include "npc.h" +#include "script.h" +#include "skill.h" +#include "unit.h" + +#define MIN_PETTHINKTIME 100 + +struct pet_db pet_db[MAX_PET_DB]; + +static struct eri *item_drop_ers; //For loot drops delay structures. +static struct eri *item_drop_list_ers; + +int pet_hungry_val(struct pet_data *pd) +{ + nullpo_retr(0, pd); + + if(pd->pet.hungry > 90) + return 4; + else if(pd->pet.hungry > 75) + return 3; + else if(pd->pet.hungry > 25) + return 2; + else if(pd->pet.hungry > 10) + return 1; + else + return 0; +} + +static int pet_calc_pos(struct pet_data *pd,int tx,int ty,int dir) +{ + int x,y,dx,dy; + int i,k; + + nullpo_retr(0, pd); + + pd->ud.to_x = tx; + pd->ud.to_y = ty; + + if(dir < 0 || dir >= 8) + return 1; + + dx = -dirx[dir]*2; + dy = -diry[dir]*2; + x = tx + dx; + y = ty + dy; + if(!unit_can_reach_pos(&pd->bl,x,y,0)) { + if(dx > 0) x--; + else if(dx < 0) x++; + if(dy > 0) y--; + else if(dy < 0) y++; + if(!unit_can_reach_pos(&pd->bl,x,y,0)) { + for(i=0;i<12;i++) { + k = rand()%8; + dx = -dirx[k]*2; + dy = -diry[k]*2; + x = tx + dx; + y = ty + dy; + if(unit_can_reach_pos(&pd->bl,x,y,0)) + break; + else { + if(dx > 0) x--; + else if(dx < 0) x++; + if(dy > 0) y--; + else if(dy < 0) y++; + if(unit_can_reach_pos(&pd->bl,x,y,0)) + break; + } + } + if(i>=12) { + x = tx; + y = ty; + if(!unit_can_reach_pos(&pd->bl,x,y,0)) + return 1; + } + } + } + pd->ud.to_x = x; + pd->ud.to_y = y; + return 0; +} + +int pet_create_egg(struct map_session_data *sd, int item_id) +{ + int pet_id = search_petDB_index(item_id, PET_EGG); + if (pet_id < 0) return 0; //No pet egg here. + sd->catch_target_class = pet_db[pet_id].class_; + intif_create_pet(sd->status.account_id, sd->status.char_id, + (short)pet_db[pet_id].class_, + (short)mob_db(pet_db[pet_id].class_)->lv, + (short)pet_db[pet_id].EggID, 0, + (short)pet_db[pet_id].intimate, + 100, 0, 1, pet_db[pet_id].jname); + return 1; +} + +int pet_unlocktarget(struct pet_data *pd) +{ + nullpo_retr(0, pd); + + pd->target_id=0; + pet_stop_attack(pd); + pet_stop_walking(pd,1); + return 0; +} + +/*========================================== + * Pet Attack Skill [Skotlex] + *------------------------------------------ + */ +int pet_attackskill(struct pet_data *pd, int target_id) +{ + struct block_list *bl; + int inf; + + if (!battle_config.pet_status_support || !pd->a_skill || + (battle_config.pet_equip_required && !pd->pet.equip)) + return 0; + + if (DIFF_TICK(pd->ud.canact_tick, gettick()) > 0) + return 0; + + if (rand()%100 < (pd->a_skill->rate +pd->pet.intimate*pd->a_skill->bonusrate/1000)) + { //Skotlex: Use pet's skill + bl=map_id2bl(target_id); + if(bl == NULL || pd->bl.m != bl->m || bl->prev == NULL || status_isdead(bl) || + !check_distance_bl(&pd->bl, bl, pd->db->range3)) + return 0; + + inf = skill_get_inf(pd->a_skill->id); + if (inf & INF_GROUND_SKILL) + unit_skilluse_pos(&pd->bl, bl->x, bl->y, pd->a_skill->id, pd->a_skill->lv); + else //Offensive self skill? Could be stuff like GX. + unit_skilluse_id(&pd->bl,(inf&INF_SELF_SKILL?pd->bl.id:bl->id), pd->a_skill->id, pd->a_skill->lv); + return 1; //Skill invoked. + } + return 0; +} + +int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type) +{ + struct pet_data *pd; + int rate; + + pd = sd->pd; + + Assert((pd->msd == 0) || (pd->msd->pd == pd)); + + if(bl == NULL || bl->type != BL_MOB || bl->prev == NULL || + pd->pet.intimate < battle_config.pet_support_min_friendly || + pd->pet.hungry < 1 || + pd->pet.class_ == status_get_class(bl)) + return 0; + + if(pd->bl.m != bl->m || + !check_distance_bl(&pd->bl, bl, pd->db->range2)) + return 0; + + if (!status_check_skilluse(&pd->bl, bl, 0, 0)) + return 0; + + if(!type) { + rate = pd->petDB->attack_rate; + rate = rate * pd->rate_fix/1000; + if(pd->petDB->attack_rate > 0 && rate <= 0) + rate = 1; + } else { + rate = pd->petDB->defence_attack_rate; + rate = rate * pd->rate_fix/1000; + if(pd->petDB->defence_attack_rate > 0 && rate <= 0) + rate = 1; + } + if(rand()%10000 < rate) + { + if(pd->target_id == 0 || rand()%10000 < pd->petDB->change_target_rate) + pd->target_id = bl->id; + } + + return 0; +} +/*========================================== + * Pet SC Check [Skotlex] + *------------------------------------------ + */ +int pet_sc_check(struct map_session_data *sd, int type) +{ + struct pet_data *pd; + + nullpo_retr(0, sd); + pd = sd->pd; + + if (pd == NULL || + (battle_config.pet_equip_required && pd->pet.equip == 0) || + pd->recovery == NULL || + pd->recovery->timer != -1 || + pd->recovery->type != type) + return 1; + + pd->recovery->timer = add_timer(gettick()+pd->recovery->delay*1000,pet_recovery_timer,sd->bl.id,0); + + return 0; +} + +static int pet_hungry(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd; + struct pet_data *pd; + int interval,t; + + sd=map_id2sd(id); + if(!sd) + return 1; + + if(!sd->status.pet_id || !sd->pd) + return 1; + + pd = sd->pd; + if(pd->pet_hungry_timer != tid){ + if(battle_config.error_log) + ShowError("pet_hungry_timer %d != %d\n",pd->pet_hungry_timer,tid); + return 0; + } + pd->pet_hungry_timer = -1; + + if (pd->pet.intimate <= 0) + return 1; //You lost the pet already, the rest is irrelevant. + + pd->pet.hungry--; + t = pd->pet.intimate; + if(pd->pet.hungry < 0) { + pet_stop_attack(pd); + pd->pet.hungry = 0; + pd->pet.intimate -= battle_config.pet_hungry_friendly_decrease; + if(pd->pet.intimate <= 0) { + pd->pet.intimate = 0; + pd->status.speed = pd->db->status.speed; + } + status_calc_pet(pd, 0); + clif_send_petdata(sd,1,pd->pet.intimate); + } + clif_send_petdata(sd,2,pd->pet.hungry); + + if(battle_config.pet_hungry_delay_rate != 100) + interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100; + else + interval = pd->petDB->hungry_delay; + if(interval <= 0) + interval = 1; + pd->pet_hungry_timer = add_timer(tick+interval,pet_hungry,sd->bl.id,0); + + return 0; +} + +int search_petDB_index(int key,int type) +{ + int i; + + for(i=0;ipet_hungry_timer != -1) { + delete_timer(pd->pet_hungry_timer,pet_hungry); + pd->pet_hungry_timer = -1; + } + + return 1; +} + +static int pet_performance(struct map_session_data *sd, struct pet_data *pd) +{ + int val; + + if (pd->pet.intimate > 900) + val = (pd->petDB->s_perfor > 0)? 4:3; + else if(pd->pet.intimate > 750) + val = 2; + else + val = 1; + + pet_stop_walking(pd,2000<<8); + clif_pet_performance(&pd->bl,rand()%val + 1); + pet_lootitem_drop(pd,NULL); + return 1; +} + +static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) +{ + struct item tmp_item; + int flag; + + pet_lootitem_drop(pd,sd); + malloc_set(&tmp_item,0,sizeof(tmp_item)); + tmp_item.nameid = pd->petDB->EggID; + tmp_item.identify = 1; + tmp_item.card[0] = CARD0_PET; + tmp_item.card[1] = GetWord(pd->pet.pet_id,0); + tmp_item.card[2] = GetWord(pd->pet.pet_id,1); + tmp_item.card[3] = pd->pet.rename_flag; + if((flag = pc_additem(sd,&tmp_item,1))) { + clif_additem(sd,0,0,flag); + map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + pd->pet.incuvate = 1; + //No need, pet is saved on unit_free below. + //intif_save_petdata(sd->status.account_id,&pd->pet); + if(pd->state.skillbonus) { + pd->state.skillbonus = 0; + status_calc_pc(sd,0); + } + unit_free(&pd->bl,0); + sd->status.pet_id = 0; + + return 1; +} + +int pet_data_init(struct map_session_data *sd, struct s_pet *pet) +{ + struct pet_data *pd; + int i=0,interval=0; + + nullpo_retr(1, sd); + + Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); + + if(sd->status.account_id != pet->account_id || sd->status.char_id != pet->char_id) { + sd->status.pet_id = 0; + return 1; + } + if (sd->status.pet_id != pet->pet_id) { + if (sd->status.pet_id) { + //Wrong pet?? Set incuvate to no and send it back for saving. + pet->incuvate = 1; + intif_save_petdata(sd->status.account_id,pet); + sd->status.pet_id = 0; + return 1; + } + //The pet_id value was lost? odd... restore it. + sd->status.pet_id = pet->pet_id; + } + + i = search_petDB_index(pet->class_,PET_CLASS); + if(i < 0) { + sd->status.pet_id = 0; + return 1; + } + sd->pd = pd = (struct pet_data *)aCalloc(1,sizeof(struct pet_data)); + pd->petDB = &pet_db[i]; + memcpy(&pd->pet, pet, sizeof(struct s_pet)); + pd->bl.m = sd->bl.m; + pd->bl.x = sd->bl.x; + pd->bl.y = sd->bl.y; + pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->ud.dir); + pd->bl.x = pd->ud.to_x; + pd->bl.y = pd->ud.to_y; + pd->bl.id = npc_get_new_npc_id(); + pd->db = mob_db(pet->class_); + pd->bl.subtype = MONS; + pd->bl.type = BL_PET; + pd->msd = sd; + status_set_viewdata(&pd->bl, pet->class_); + unit_dataset(&pd->bl); + pd->ud.dir = sd->ud.dir; + pd->last_thinktime = gettick(); + + map_addiddb(&pd->bl); + + // initialise + status_calc_pet(pd,1); + + pd->state.skillbonus = 0; + if (battle_config.pet_status_support) //Skotlex + run_script(pet_db[i].script,0,sd->bl.id,0); + + if(battle_config.pet_hungry_delay_rate != 100) + interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100; + else + interval = pd->petDB->hungry_delay; + if(interval <= 0) + interval = 1; + pd->pet_hungry_timer = add_timer(gettick()+interval,pet_hungry,sd->bl.id,0); + return 0; +} + +int pet_birth_process(struct map_session_data *sd, struct s_pet *pet) +{ + nullpo_retr(1, sd); + + Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); + + if(sd->status.pet_id && pet->incuvate == 1) { + sd->status.pet_id = 0; + return 1; + } + + pet->incuvate = 0; + pet->account_id = sd->status.account_id; + pet->char_id = sd->status.char_id; + sd->status.pet_id = pet->pet_id; + if(pet_data_init(sd, pet)) { + sd->status.pet_id = 0; + return 1; + } + + intif_save_petdata(sd->status.account_id,pet); + if (save_settings&8) + chrif_save(sd,0); //is it REALLY Needed to save the char for hatching a pet? [Skotlex] + + if(sd->bl.prev != NULL) { + map_addblock(&sd->pd->bl); + clif_spawn(&sd->pd->bl); + clif_send_petdata(sd,0,0); + clif_send_petdata(sd,5,battle_config.pet_hair_style); + clif_pet_equip(sd->pd); + clif_send_petstatus(sd); + } + Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); + + return 0; +} + +int pet_recv_petdata(int account_id,struct s_pet *p,int flag) +{ + struct map_session_data *sd; + + sd = map_id2sd(account_id); + if(sd == NULL) + return 1; + if(flag == 1) { + sd->status.pet_id = 0; + return 1; + } + if(p->incuvate == 1) { + int i; + //Delete egg from inventory. [Skotlex] + for (i = 0; i < MAX_INVENTORY; i++) { + if(sd->status.inventory[i].card[0] == CARD0_PET && + p->pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2])) + break; + } + if(i >= MAX_INVENTORY) { + if (battle_config.error_log) + ShowError("pet_recv_petdata: Hatching pet (%d:%s) aborted, couldn't find egg in inventory for removal!\n",p->pet_id, p->name); + sd->status.pet_id = 0; + return 1; + } + if (!pet_birth_process(sd,p)) //Pet hatched. Delete egg. + pc_delitem(sd,i,1,0); + } else { + pet_data_init(sd,p); + if(sd->pd && sd->bl.prev != NULL) { + map_addblock(&sd->pd->bl); + clif_spawn(&sd->pd->bl); + clif_send_petdata(sd,0,0); + clif_send_petdata(sd,5,battle_config.pet_hair_style); + clif_pet_equip(sd->pd); + clif_send_petstatus(sd); + } + } + + return 0; +} + +int pet_select_egg(struct map_session_data *sd,short egg_index) +{ + nullpo_retr(0, sd); + + if(egg_index < 0 || egg_index >= MAX_INVENTORY) + return 0; //Forged packet! + + if(sd->status.inventory[egg_index].card[0] == CARD0_PET) + intif_request_petdata(sd->status.account_id, sd->status.char_id, MakeDWord(sd->status.inventory[egg_index].card[1], sd->status.inventory[egg_index].card[2]) ); + else { + if(battle_config.error_log) + ShowError("wrong egg item inventory %d\n",egg_index); + } + return 0; +} + +int pet_catch_process1(struct map_session_data *sd,int target_class) +{ + nullpo_retr(0, sd); + + sd->catch_target_class = target_class; + clif_catch_process(sd); + + return 0; +} + +int pet_catch_process2(struct map_session_data *sd,int target_id) +{ + struct mob_data *md; + int i=0,pet_catch_rate=0; + + nullpo_retr(1, sd); + + md=(struct mob_data*)map_id2bl(target_id); + if(!md || md->bl.type != BL_MOB || md->bl.prev == NULL){ + //Abort capture. + sd->catch_target_class = -1; + sd->itemid = sd->itemindex = -1; + return 1; + } + + if (sd->menuskill_id != SA_TAMINGMONSTER) + { //Exploit? + clif_pet_rulet(sd,0); + sd->catch_target_class = -1; + return 1; + } + + if (sd->menuskill_lv > 0) + { //Consume the pet lure [Skotlex] + i=pc_search_inventory(sd,sd->menuskill_lv); + if (i < 0) + { //they tried an exploit? + clif_pet_rulet(sd,0); + sd->catch_target_class = -1; + return 1; + } + //Delete the item + if (sd->itemid == sd->menuskill_lv) + sd->itemid = sd->itemindex = -1; + sd->menuskill_id = sd->menuskill_lv = 0; + pc_delitem(sd,i,1,0); + } + + i = search_petDB_index(md->class_,PET_CLASS); + //catch_target_class == 0 is used for universal lures. [Skotlex] + //for now universal lures do not include bosses. + if (sd->catch_target_class == 0 && !(md->status.mode&MD_BOSS)) + sd->catch_target_class = md->class_; + if(i < 0 || sd->catch_target_class != md->class_) { + clif_emotion(&md->bl, 7); //mob will do /ag if wrong lure is used on them. + clif_pet_rulet(sd,0); + sd->catch_target_class = -1; + return 1; + } + + pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - md->level)*30 + sd->battle_status.luk*20)*(200 - md->status.hp*100/md->status.max_hp)/100; + if(pet_catch_rate < 1) pet_catch_rate = 1; + if(battle_config.pet_catch_rate != 100) + pet_catch_rate = (pet_catch_rate*battle_config.pet_catch_rate)/100; + + if(rand()%10000 < pet_catch_rate) { + unit_remove_map(&md->bl,0); + status_kill(&md->bl); + clif_pet_rulet(sd,1); +// if(battle_config.etc_log) +// printf("rulet success %d\n",target_id); + intif_create_pet(sd->status.account_id,sd->status.char_id,pet_db[i].class_,mob_db(pet_db[i].class_)->lv, + pet_db[i].EggID,0,pet_db[i].intimate,100,0,1,pet_db[i].jname); + } + else + { + sd->catch_target_class = -1; + clif_pet_rulet(sd,0); + } + + return 0; +} + +int pet_get_egg(int account_id,int pet_id,int flag) +{ //This function is invoked when a new pet has been created, and at no other time! + struct map_session_data *sd; + struct item tmp_item; + int i=0,ret=0; + + if(flag) + return 0; + + sd = map_id2sd(account_id); + if(sd == NULL) + return 0; + + i = search_petDB_index(sd->catch_target_class,PET_CLASS); + sd->catch_target_class = -1; + + if(i < 0) { + intif_delete_petdata(pet_id); + return 0; + } + + malloc_set(&tmp_item,0,sizeof(tmp_item)); + tmp_item.nameid = pet_db[i].EggID; + tmp_item.identify = 1; + tmp_item.card[0] = CARD0_PET; + tmp_item.card[1] = GetWord(pet_id,0); + tmp_item.card[2] = GetWord(pet_id,1); + tmp_item.card[3] = 0; //New pets are not named. + if((ret = pc_additem(sd,&tmp_item,1))) { + clif_additem(sd,0,0,ret); + map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + + return 1; +} + +static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd); +static int pet_food(struct map_session_data *sd, struct pet_data *pd); +static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap); + +int pet_menu(struct map_session_data *sd,int menunum) +{ + nullpo_retr(0, sd); + if (sd->pd == NULL) + return 1; + + //You lost the pet already. + if(!sd->status.pet_id || sd->pd->pet.intimate <= 0) + return 1; + + switch(menunum) { + case 0: + clif_send_petstatus(sd); + break; + case 1: + pet_food(sd, sd->pd); + break; + case 2: + pet_performance(sd, sd->pd); + break; + case 3: + pet_return_egg(sd, sd->pd); + break; + case 4: + pet_unequipitem(sd, sd->pd); + break; + } + return 0; +} + +int pet_change_name(struct map_session_data *sd,char *name, int flag) //flag 0 = check name, 1 = good name +{ + int i; + struct pet_data *pd; + nullpo_retr(1, sd); + + pd = sd->pd; + if((pd == NULL) || (pd->pet.rename_flag == 1 && !battle_config.pet_rename)) + return 1; + + for(i=0;ipet.name, name, NAME_LENGTH-1); + + clif_charnameack (0,&pd->bl); + pd->pet.rename_flag = 1; + clif_pet_equip(pd); + clif_send_petstatus(sd); + + return 0; +} + +int pet_equipitem(struct map_session_data *sd,int index) +{ + struct pet_data *pd; + int nameid; + + nullpo_retr(1, sd); + pd = sd->pd; + if (!pd) return 1; + + nameid = sd->status.inventory[index].nameid; + + if(pd->petDB->AcceID == 0 || nameid != pd->petDB->AcceID || pd->pet.equip != 0) { + clif_equipitemack(sd,0,0,0); + return 1; + } + + pc_delitem(sd,index,1,0); + pd->pet.equip = nameid; + status_set_viewdata(&pd->bl, pd->pet.class_); //Updates view_data. + clif_pet_equip(pd); + if (battle_config.pet_equip_required) + { //Skotlex: start support timers if need + unsigned int tick = gettick(); + if (pd->s_skill && pd->s_skill->timer == -1) + { + if (pd->s_skill->id) + pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_skill_support_timer, sd->bl.id, 0); + else + pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_heal_timer, sd->bl.id, 0); + } + if (pd->bonus && pd->bonus->timer == -1) + pd->bonus->timer=add_timer(tick+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0); + } + + return 0; +} + +static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd) +{ + struct item tmp_item; + int nameid,flag; + + if(pd->pet.equip == 0) + return 1; + + nameid = pd->pet.equip; + pd->pet.equip = 0; + status_set_viewdata(&pd->bl, pd->pet.class_); + clif_pet_equip(pd); + malloc_set(&tmp_item,0,sizeof(tmp_item)); + tmp_item.nameid = nameid; + tmp_item.identify = 1; + if((flag = pc_additem(sd,&tmp_item,1))) { + clif_additem(sd,0,0,flag); + map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + if (battle_config.pet_equip_required) + { //Skotlex: halt support timers if needed + if(pd->state.skillbonus) { + pd->state.skillbonus = 0; + status_calc_pc(sd,0); + } + if (pd->s_skill && pd->s_skill->timer != -1) + { + if (pd->s_skill->id) + delete_timer(pd->s_skill->timer, pet_skill_support_timer); + else + delete_timer(pd->s_skill->timer, pet_heal_timer); + pd->s_skill->timer = -1; + } + if (pd->bonus && pd->bonus->timer != -1) + { + delete_timer(pd->bonus->timer, pet_skill_bonus_timer); + pd->bonus->timer = -1; + } + } + + return 0; +} + +static int pet_food(struct map_session_data *sd, struct pet_data *pd) +{ + int i,k; + + k=pd->petDB->FoodID; + i=pc_search_inventory(sd,k); + if(i < 0) { + clif_pet_food(sd,k,0); + return 1; + } + pc_delitem(sd,i,1,0); + + if(pd->pet.hungry > 90) + pd->pet.intimate -= pd->petDB->r_full; + else { + if(battle_config.pet_friendly_rate != 100) + k = (pd->petDB->r_hungry * battle_config.pet_friendly_rate)/100; + else + k = pd->petDB->r_hungry; + if(pd->pet.hungry > 75) { + k = k >> 1; + if(k <= 0) + k = 1; + } + pd->pet.intimate += k; + } + if(pd->pet.intimate <= 0) { + pd->pet.intimate = 0; + pet_stop_attack(pd); + pd->status.speed = pd->db->status.speed; + } + else if(pd->pet.intimate > 1000) + pd->pet.intimate = 1000; + status_calc_pet(pd, 0); + pd->pet.hungry += pd->petDB->fullness; + if(pd->pet.hungry > 100) + pd->pet.hungry = 100; + + clif_send_petdata(sd,2,pd->pet.hungry); + clif_send_petdata(sd,1,pd->pet.intimate); + clif_pet_food(sd,pd->petDB->FoodID,1); + + return 0; +} + +static int pet_randomwalk(struct pet_data *pd,unsigned int tick) +{ + const int retrycount=20; + + nullpo_retr(0, pd); + + Assert((pd->msd == 0) || (pd->msd->pd == pd)); + + if(DIFF_TICK(pd->next_walktime,tick) < 0 && unit_can_move(&pd->bl)) { + int i,x,y,c,d=12-pd->move_fail_count; + if(d<5) d=5; + for(i=0;ibl.x+r%(d*2+1)-d; + y=pd->bl.y+r/(d*2+1)%(d*2+1)-d; + if(map_getcell(pd->bl.m,x,y,CELL_CHKPASS) && unit_walktoxy(&pd->bl,x,y,0)){ + pd->move_fail_count=0; + break; + } + if(i+1>=retrycount){ + pd->move_fail_count++; + if(pd->move_fail_count>1000){ + if(battle_config.error_log) + ShowWarning("PET cant move. hold position %d, class = %d\n",pd->bl.id,pd->pet.class_); + pd->move_fail_count=0; + pd->ud.canmove_tick = tick + 60000; + return 0; + } + } + } + for(i=c=0;iud.walkpath.path_len;i++){ + if(pd->ud.walkpath.path[i]&1) + c+=pd->status.speed*14/10; + else + c+=pd->status.speed; + } + pd->next_walktime = tick+rand()%3000+3000+c; + + return 1; + } + return 0; +} + +static int pet_ai_sub_hard(struct pet_data *pd, struct map_session_data *sd, unsigned int tick) +{ + struct block_list *target = NULL; + + if(pd->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL) + return 0; + + if(DIFF_TICK(tick,pd->last_thinktime) < MIN_PETTHINKTIME) + return 0; + pd->last_thinktime=tick; + + if(pd->ud.attacktimer != -1 || pd->ud.skilltimer != -1 || pd->bl.m != sd->bl.m) + return 0; + + if(pd->ud.walktimer != -1 && pd->ud.walkpath.path_pos <= 2) + return 0; //No thinking when you just started to walk. + + if(pd->pet.intimate <= 0) { + //Pet should just... well, random walk. + pet_randomwalk(pd,tick); + return 0; + } + + if (!check_distance_bl(&sd->bl, &pd->bl, pd->db->range3)) { + //Master too far, chase. + if(pd->target_id) + pet_unlocktarget(pd); + if(pd->ud.walktimer != -1 && pd->ud.target == sd->bl.id) + return 0; //Already walking to him + if (DIFF_TICK(tick, pd->ud.canmove_tick) < 0) + return 0; //Can't move yet. + pd->status.speed = (sd->battle_status.speed>>1); + if(pd->status.speed <= 0) + pd->status.speed = 1; + if (!unit_walktobl(&pd->bl, &sd->bl, 3, 0)) + pet_randomwalk(pd,tick); + return 0; + } + + //Return speed to normal. + if (pd->status.speed != pd->petDB->speed) { + if (pd->ud.walktimer != -1) + return 0; //Wait until the pet finishes walking back to master. + pd->status.speed = pd->petDB->speed; + } + + if (pd->target_id) { + target= map_id2bl(pd->target_id); + if (!target || pd->bl.m != target->m || status_isdead(target) || + !check_distance_bl(&pd->bl, target, pd->db->range3)) + { + target = NULL; + pet_unlocktarget(pd); + } + } + + if(!target && pd->loot && pd->loot->count < pd->loot->max && DIFF_TICK(tick,pd->ud.canact_tick)>0) { + //Use half the pet's range of sight. + map_foreachinrange(pet_ai_sub_hard_lootsearch,&pd->bl, + pd->db->range2/2, BL_ITEM,pd,&target); + } + + if (!target) { + //Just walk around. + if (check_distance_bl(&sd->bl, &pd->bl, 3)) + return 0; //Already next to master. + + if(pd->ud.walktimer != -1 && check_distance_blxy(&sd->bl, pd->ud.to_x,pd->ud.to_y, 3)) + return 0; //Already walking to him + + pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->ud.dir); + if(!unit_walktoxy(&pd->bl,pd->ud.to_x,pd->ud.to_y,0)) + pet_randomwalk(pd,tick); + + return 0; + } + + if(pd->ud.target == target->id && + (pd->ud.attacktimer != -1 || pd->ud.walktimer != -1)) + return 0; //Target already locked. + + if (target->type != BL_ITEM) + { //enemy targetted + if(!battle_check_range(&pd->bl,target,pd->status.rhw.range)) + { //Chase + if(!unit_walktobl(&pd->bl, target, pd->status.rhw.range, 2)) + pet_unlocktarget(pd); //Unreachable target. + return 0; + } + //Continuous attack. + unit_attack(&pd->bl, pd->target_id, 1); + } else { //Item Targeted, attempt loot + if (!check_distance_bl(&pd->bl, target, 1)) + { //Out of range + if(!unit_walktobl(&pd->bl, target, 0, 1)) //Unreachable target. + pet_unlocktarget(pd); + return 0; + } else{ + struct flooritem_data *fitem = (struct flooritem_data *)target; + if(pd->loot->count < pd->loot->max){ + memcpy(&pd->loot->item[pd->loot->count++],&fitem->item_data,sizeof(pd->loot->item[0])); + pd->loot->weight += itemdb_search(fitem->item_data.nameid)->weight*fitem->item_data.amount; + map_clearflooritem(target->id); + } + //Target is unlocked regardless of whether it was picked or not. + pet_unlocktarget(pd); + } + } + return 0; +} + +static int pet_ai_sub_foreachclient(struct map_session_data *sd,va_list ap) +{ + unsigned int tick = va_arg(ap,unsigned int); + if(sd->status.pet_id && sd->pd) + pet_ai_sub_hard(sd->pd,sd,tick); + + return 0; +} + +static int pet_ai_hard(int tid,unsigned int tick,int id,int data) +{ + clif_foreachclient(pet_ai_sub_foreachclient,tick); + + return 0; +} + +static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap) +{ + struct pet_data* pd; + struct flooritem_data *fitem = (struct flooritem_data *)bl; + struct block_list **target; + int sd_id =0; + + pd=va_arg(ap,struct pet_data *); + target=va_arg(ap,struct block_list**); + + sd_id = fitem->first_get_id; + + if(sd_id && sd_id != pd->msd->bl.id) + return 0; + + if(unit_can_reach_bl(&pd->bl,bl, pd->db->range2, 1, NULL, NULL) && + ((*target) == NULL || //New target closer than previous one. + !check_distance_bl(&pd->bl, *target, distance_bl(&pd->bl, bl)))) + { + (*target) = bl; + pd->target_id = bl->id; + return 1; + } + + return 0; +} + +static int pet_delay_item_drop(int tid,unsigned int tick,int id,int data) +{ + struct item_drop_list *list; + struct item_drop *ditem, *ditem_prev; + list=(struct item_drop_list *)id; + ditem = list->item; + while (ditem) { + map_addflooritem(&ditem->item_data,ditem->item_data.amount, + list->m,list->x,list->y, + list->first_sd,list->second_sd,list->third_sd,0); + ditem_prev = ditem; + ditem = ditem->next; + ers_free(item_drop_ers, ditem_prev); + } + ers_free(item_drop_list_ers, list); + return 0; +} + +int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd) +{ + int i,flag=0; + struct item_drop_list *dlist; + struct item_drop *ditem; + struct item *it; + if(!pd || !pd->loot || !pd->loot->count) + return 0; + dlist = ers_alloc(item_drop_list_ers, struct item_drop_list); + dlist->m = pd->bl.m; + dlist->x = pd->bl.x; + dlist->y = pd->bl.y; + dlist->first_sd = NULL; + dlist->second_sd = NULL; + dlist->third_sd = NULL; + dlist->item = NULL; + + for(i=0;iloot->count;i++) { + it = &pd->loot->item[i]; + if(sd){ + if((flag = pc_additem(sd,it,it->amount))){ + clif_additem(sd,0,0,flag); + ditem = ers_alloc(item_drop_ers, struct item_drop); + memcpy(&ditem->item_data, it, sizeof(struct item)); + ditem->next = dlist->item; + dlist->item = ditem; + } + } + else { + ditem = ers_alloc(item_drop_ers, struct item_drop); + memcpy(&ditem->item_data, it, sizeof(struct item)); + ditem->next = dlist->item; + dlist->item = ditem; + } + } + //The smart thing to do is use pd->loot->max (thanks for pointing it out, Shinomori) + malloc_set(pd->loot->item,0,pd->loot->max * sizeof(struct item)); + pd->loot->count = 0; + pd->loot->weight = 0; + pd->ud.canact_tick = gettick()+10000; // 10*1000msの間拾わない + + if (dlist->item) + add_timer(gettick()+540,pet_delay_item_drop,(int)dlist,0); + else + ers_free(item_drop_list_ers, dlist); + return 1; +} + +/*========================================== + * pet bonus giving skills [Valaris] / Rewritten by [Skotlex] + *------------------------------------------ + */ +int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd=map_id2sd(id); + struct pet_data *pd; + int bonus; + int timer = 0; + + if(sd == NULL || sd->pd==NULL || sd->pd->bonus == NULL) + return 1; + + pd=sd->pd; + + if(pd->bonus->timer != tid) { + if(battle_config.error_log) + { + ShowError("pet_skill_bonus_timer %d != %d\n",pd->bonus->timer,tid); + pd->bonus->timer = -1; + } + return 0; + } + + // determine the time for the next timer + if (pd->state.skillbonus && pd->bonus->delay > 0) { + bonus = 0; + timer = pd->bonus->delay*1000; // the duration until pet bonuses will be reactivated again + } else if (pd->pet.intimate) { + bonus = 1; + timer = pd->bonus->duration*1000; // the duration for pet bonuses to be in effect + } else { //Lost pet... + pd->bonus->timer = -1; + return 0; + } + + if (pd->state.skillbonus != bonus) { + pd->state.skillbonus = bonus; + status_calc_pc(sd, 0); + } + // wait for the next timer + pd->bonus->timer=add_timer(tick+timer,pet_skill_bonus_timer,sd->bl.id,0); + return 0; +} + +/*========================================== + * pet recovery skills [Valaris] / Rewritten by [Skotlex] + *------------------------------------------ + */ +int pet_recovery_timer(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd=map_id2sd(id); + struct pet_data *pd; + + if(sd==NULL || sd->pd == NULL || sd->pd->recovery == NULL) + return 1; + + pd=sd->pd; + + if(pd->recovery->timer != tid) { + if(battle_config.error_log) + ShowError("pet_recovery_timer %d != %d\n",pd->recovery->timer,tid); + return 0; + } + + if(sd->sc.count && sd->sc.data[pd->recovery->type].timer != -1) + { //Display a heal animation? + //Detoxify is chosen for now. + clif_skill_nodamage(&pd->bl,&sd->bl,TF_DETOXIFY,1,1); + status_change_end(&sd->bl,pd->recovery->type,-1); + clif_emotion(&pd->bl, 33); + } + + pd->recovery->timer = -1; + + return 0; +} + +int pet_heal_timer(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd=map_id2sd(id); + struct status_data *status; + struct pet_data *pd; + short rate = 100; + + if(sd==NULL || sd->pd == NULL || sd->pd->s_skill == NULL) + return 1; + + pd=sd->pd; + + if(pd->s_skill->timer != tid) { + if(battle_config.error_log) + ShowError("pet_heal_timer %d != %d\n",pd->s_skill->timer,tid); + return 0; + } + + status = status_get_status_data(&sd->bl); + + if(pc_isdead(sd) || + (rate = status->sp*100/status->max_sp) > pd->s_skill->sp || + (rate = status->hp*100/status->max_hp) > pd->s_skill->hp || + (rate = (pd->ud.skilltimer != -1)) //Another skill is in effect + ) { //Wait (how long? 1 sec for every 10% of remaining) + pd->s_skill->timer=add_timer(gettick()+(rate>10?rate:10)*100,pet_heal_timer,sd->bl.id,0); + return 0; + } + pet_stop_attack(pd); + pet_stop_walking(pd,1); + clif_skill_nodamage(&pd->bl,&sd->bl,AL_HEAL,pd->s_skill->lv,1); + status_heal(&sd->bl, pd->s_skill->lv,0, 0); + pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0); + return 0; +} + +/*========================================== + * pet support skills [Skotlex] + *------------------------------------------ + */ +int pet_skill_support_timer(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd=map_id2sd(id); + struct pet_data *pd; + struct status_data *status; + short rate = 100; + if(sd==NULL || sd->pd == NULL || sd->pd->s_skill == NULL) + return 1; + + pd=sd->pd; + + if(pd->s_skill->timer != tid) { + if(battle_config.error_log) + ShowError("pet_skill_support_timer %d != %d\n",pd->s_skill->timer,tid); + return 0; + } + + status = status_get_status_data(&sd->bl); + + if (DIFF_TICK(pd->ud.canact_tick, tick) > 0) + { //Wait until the pet can act again. + pd->s_skill->timer=add_timer(pd->ud.canact_tick,pet_skill_support_timer,sd->bl.id,0); + return 0; + } + + if(pc_isdead(sd) || + (rate = status->sp*100/status->max_sp) > pd->s_skill->sp || + (rate = status->hp*100/status->max_hp) > pd->s_skill->hp || + (rate = (pd->ud.skilltimer != -1)) //Another skill is in effect + ) { //Wait (how long? 1 sec for every 10% of remaining) + pd->s_skill->timer=add_timer(tick+(rate>10?rate:10)*100,pet_skill_support_timer,sd->bl.id,0); + return 0; + } + + pet_stop_attack(pd); + pet_stop_walking(pd,1); + + if (skill_get_inf(pd->s_skill->id) & INF_GROUND_SKILL) + unit_skilluse_pos(&pd->bl, sd->bl.x, sd->bl.y, pd->s_skill->id, pd->s_skill->lv); + else + unit_skilluse_id(&pd->bl, sd->bl.id, pd->s_skill->id, pd->s_skill->lv); + + pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0); + return 0; +} + +/*========================================== + *ペットデータ読み込み + *------------------------------------------ + */ +int read_petdb() +{ + FILE *fp; + char line[1024]; + int nameid,i,k; + int j=0; + int lines; + char *filename[]={"pet_db.txt","pet_db2.txt"}; + char *str[32],*p,*np; + + +//Remove any previous scripts in case reloaddb was invoked. + for(j =0; j < MAX_PET_DB; j++) + if (pet_db[j].script) { + aFree(pet_db[j].script); + pet_db[j].script = NULL; + } + j = 0; + malloc_set(pet_db,0,sizeof(pet_db)); + for(i=0;i<2;i++){ + sprintf(line, "%s/%s", db_path, filename[i]); + fp=fopen(line,"r"); + if(fp==NULL){ + if(i>0) + continue; + ShowError("can't read %s\n",line); + return -1; + } + lines = 0; + while(fgets(line,1020,fp) && j < MAX_PET_DB){ + + lines++; + + if(line[0] == '/' && line[1] == '/') + continue; + + for(k=0,p=line;k<20;k++){ + if((np=strchr(p,','))!=NULL){ + str[k]=p; + *np=0; + p=np+1; + } else { + str[k]=p; + p+=strlen(p); + } + } + + nameid=atoi(str[0]); + if(nameid<=0) + continue; + + if (!mobdb_checkid(nameid)) { + ShowWarning("pet_db reading: Invalid mob-class %d, pet not read.\n", nameid); + continue; + } + + pet_db[j].class_ = nameid; + memcpy(pet_db[j].name,str[1],NAME_LENGTH-1); + memcpy(pet_db[j].jname,str[2],NAME_LENGTH-1); + pet_db[j].itemID=atoi(str[3]); + pet_db[j].EggID=atoi(str[4]); + pet_db[j].AcceID=atoi(str[5]); + pet_db[j].FoodID=atoi(str[6]); + pet_db[j].fullness=atoi(str[7]); + pet_db[j].hungry_delay=atoi(str[8])*1000; + pet_db[j].r_hungry=atoi(str[9]); + if(pet_db[j].r_hungry <= 0) + pet_db[j].r_hungry=1; + pet_db[j].r_full=atoi(str[10]); + pet_db[j].intimate=atoi(str[11]); + pet_db[j].die=atoi(str[12]); + pet_db[j].capture=atoi(str[13]); + pet_db[j].speed=atoi(str[14]); + pet_db[j].s_perfor=(char)atoi(str[15]); + pet_db[j].talk_convert_class=atoi(str[16]); + pet_db[j].attack_rate=atoi(str[17]); + pet_db[j].defence_attack_rate=atoi(str[18]); + pet_db[j].change_target_rate=atoi(str[19]); + pet_db[j].script = NULL; + if((np=strchr(p,'{'))==NULL) + continue; + pet_db[j].script = parse_script((unsigned char *) np, filename[i], lines); + j++; + } + if (j >= MAX_PET_DB) + ShowWarning("read_petdb: Reached max number of pets [%d]. Remaining pets were not read.\n ", MAX_PET_DB); + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' pets in '"CL_WHITE"%s"CL_RESET"'.\n",j,filename[i]); + } + return 0; +} + +/*========================================== + * スキル関係初期化処理 + *------------------------------------------ + */ +int do_init_pet(void) +{ + malloc_set(pet_db,0,sizeof(pet_db)); + read_petdb(); + + item_drop_ers = ers_new((uint32)sizeof(struct item_drop)); + item_drop_list_ers = ers_new((uint32)sizeof(struct item_drop_list)); + + add_timer_func_list(pet_hungry,"pet_hungry"); + add_timer_func_list(pet_ai_hard,"pet_ai_hard"); + add_timer_func_list(pet_skill_bonus_timer,"pet_skill_bonus_timer"); // [Valaris] + add_timer_func_list(pet_delay_item_drop,"pet_delay_item_drop"); + add_timer_func_list(pet_skill_support_timer, "pet_skill_support_timer"); // [Skotlex] + add_timer_func_list(pet_recovery_timer,"pet_recovery_timer"); // [Valaris] + add_timer_func_list(pet_heal_timer,"pet_heal_timer"); // [Valaris] + add_timer_interval(gettick()+MIN_PETTHINKTIME,pet_ai_hard,0,0,MIN_PETTHINKTIME); + + return 0; +} + +int do_final_pet(void) { + int i; + for(i = 0;i < MAX_PET_DB; i++) { + if(pet_db[i].script) { + script_free_code(pet_db[i].script); + pet_db[i].script = NULL; + } + } + ers_destroy(item_drop_ers); + ers_destroy(item_drop_list_ers); + return 0; +} diff --git a/src/map/pet.h b/src/map/pet.h index 89ff3dbab..71c1684e5 100644 --- a/src/map/pet.h +++ b/src/map/pet.h @@ -1,68 +1,68 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _PET_H_ -#define _PET_H_ - -#define MAX_PET_DB 300 -#define MAX_PETLOOT_SIZE 30 // [Valaris] - Changed to MAX_PETLOOT_SIZE [Skotlex] - -struct pet_db { - short class_; - char name[NAME_LENGTH],jname[NAME_LENGTH]; - short itemID; - short EggID; - short AcceID; - short FoodID; - int fullness; - int hungry_delay; - int r_hungry; - int r_full; - int intimate; - int die; - int capture; - int speed; - char s_perfor; - int talk_convert_class; - int attack_rate; - int defence_attack_rate; - int change_target_rate; - struct script_code *script; -}; -extern struct pet_db pet_db[MAX_PET_DB]; - -enum { PET_CLASS,PET_CATCH,PET_EGG,PET_EQUIP,PET_FOOD }; - -int pet_create_egg(struct map_session_data *sd, int item_id); -int pet_hungry_val(struct pet_data *pd); -int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type); -int pet_unlocktarget(struct pet_data *pd); -int pet_sc_check(struct map_session_data *sd, int type); //Skotlex -int search_petDB_index(int key,int type); -int pet_hungry_timer_delete(struct pet_data *pd); -int pet_data_init(struct map_session_data *sd, struct s_pet *pet); -int pet_birth_process(struct map_session_data *sd, struct s_pet *pet); -int pet_recv_petdata(int account_id,struct s_pet *p,int flag); -int pet_select_egg(struct map_session_data *sd,short egg_index); -int pet_catch_process1(struct map_session_data *sd,int target_class); -int pet_catch_process2(struct map_session_data *sd,int target_id); -int pet_get_egg(int account_id,int pet_id,int flag); -int pet_menu(struct map_session_data *sd,int menunum); -int pet_change_name(struct map_session_data *sd,char *name,int flag); //flag 0 = check name, 1 = good name -int pet_equipitem(struct map_session_data *sd,int index); -int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd); -int pet_attackskill(struct pet_data *pd, int target_id); -int pet_skill_support_timer(int tid, unsigned int tick, int id, int data); // [Skotlex] -int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data); // [Valaris] -int pet_recovery_timer(int tid,unsigned int tick,int id,int data); // [Valaris] -int pet_heal_timer(int tid,unsigned int tick,int id,int data); // [Valaris] - -#define pet_stop_walking(pd, type) { if((pd)->ud.walktimer != -1) unit_stop_walking(&(pd)->bl, type); } -#define pet_stop_attack(pd) { if((pd)->ud.attacktimer != -1) unit_stop_attack(&(pd)->bl); } - -int read_petdb(void); -int do_init_pet(void); -int do_final_pet(void); - -#endif - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _PET_H_ +#define _PET_H_ + +#define MAX_PET_DB 300 +#define MAX_PETLOOT_SIZE 30 // [Valaris] - Changed to MAX_PETLOOT_SIZE [Skotlex] + +struct pet_db { + short class_; + char name[NAME_LENGTH],jname[NAME_LENGTH]; + short itemID; + short EggID; + short AcceID; + short FoodID; + int fullness; + int hungry_delay; + int r_hungry; + int r_full; + int intimate; + int die; + int capture; + int speed; + char s_perfor; + int talk_convert_class; + int attack_rate; + int defence_attack_rate; + int change_target_rate; + struct script_code *script; +}; +extern struct pet_db pet_db[MAX_PET_DB]; + +enum { PET_CLASS,PET_CATCH,PET_EGG,PET_EQUIP,PET_FOOD }; + +int pet_create_egg(struct map_session_data *sd, int item_id); +int pet_hungry_val(struct pet_data *pd); +int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type); +int pet_unlocktarget(struct pet_data *pd); +int pet_sc_check(struct map_session_data *sd, int type); //Skotlex +int search_petDB_index(int key,int type); +int pet_hungry_timer_delete(struct pet_data *pd); +int pet_data_init(struct map_session_data *sd, struct s_pet *pet); +int pet_birth_process(struct map_session_data *sd, struct s_pet *pet); +int pet_recv_petdata(int account_id,struct s_pet *p,int flag); +int pet_select_egg(struct map_session_data *sd,short egg_index); +int pet_catch_process1(struct map_session_data *sd,int target_class); +int pet_catch_process2(struct map_session_data *sd,int target_id); +int pet_get_egg(int account_id,int pet_id,int flag); +int pet_menu(struct map_session_data *sd,int menunum); +int pet_change_name(struct map_session_data *sd,char *name,int flag); //flag 0 = check name, 1 = good name +int pet_equipitem(struct map_session_data *sd,int index); +int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd); +int pet_attackskill(struct pet_data *pd, int target_id); +int pet_skill_support_timer(int tid, unsigned int tick, int id, int data); // [Skotlex] +int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data); // [Valaris] +int pet_recovery_timer(int tid,unsigned int tick,int id,int data); // [Valaris] +int pet_heal_timer(int tid,unsigned int tick,int id,int data); // [Valaris] + +#define pet_stop_walking(pd, type) { if((pd)->ud.walktimer != -1) unit_stop_walking(&(pd)->bl, type); } +#define pet_stop_attack(pd) { if((pd)->ud.attacktimer != -1) unit_stop_attack(&(pd)->bl); } + +int read_petdb(void); +int do_init_pet(void); +int do_final_pet(void); + +#endif + diff --git a/src/map/script.h b/src/map/script.h index 9fcd2f739..60fc3e990 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -1,91 +1,91 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _SCRIPT_H_ -#define _SCRIPT_H_ - -extern int potion_flag; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex] -extern int potion_hp, potion_per_hp, potion_sp, potion_per_sp; -extern int potion_target; - -extern struct Script_Config { - unsigned verbose_mode : 1; - unsigned warn_func_no_comma : 1; - unsigned warn_cmd_no_comma : 1; - unsigned warn_func_mismatch_paramnum : 1; - unsigned warn_cmd_mismatch_paramnum : 1; - int check_cmdcount; - int check_gotocount; - - unsigned event_script_type : 1; - unsigned event_requires_trigger : 1; - char die_event_name[NAME_LENGTH]; - char kill_pc_event_name[NAME_LENGTH]; - char kill_mob_event_name[NAME_LENGTH]; - char login_event_name[NAME_LENGTH]; - char logout_event_name[NAME_LENGTH]; - char loadmap_event_name[NAME_LENGTH]; - char baselvup_event_name[NAME_LENGTH]; - char joblvup_event_name[NAME_LENGTH]; -} script_config; - -struct script_data { - int type; - union { - int num; - char *str; - } u; - struct linkdb_node** ref; -}; - -// Moved defsp from script_state to script_stack since -// it must be saved when script state is RERUNLINE. [Eoe / jA 1094] -struct script_code { - int script_size; - unsigned char* script_buf; - struct linkdb_node* script_vars; -}; - -struct script_state { - struct script_stack { - int sp,sp_max,defsp; - struct script_data *stack_data; - struct linkdb_node **var_function; // 関数依存変数 - } *stack; - int start,end; - int pos,state; - int rid,oid; - struct script_code *script, *scriptroot; - struct sleep_data { - int tick,timer,charid; - } sleep; -}; - -struct script_code* parse_script(unsigned char *,const char*,int); -void run_script_sub(struct script_code *rootscript,int pos,int rid,int oid, char* file, int lineno); -void run_script(struct script_code*,int,int,int); - -int set_var(struct map_session_data *sd, char *name, void *val); -int conv_num(struct script_state *st,struct script_data *data); -char* conv_str(struct script_state *st,struct script_data *data); -void setd_sub(struct script_state *st, struct map_session_data *sd, char *varname, int elem, void *value, struct linkdb_node **ref); -int run_script_timer(int tid, unsigned int tick, int id, int data); -void run_script_main(struct script_state *st); - -struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n); -void script_free_stack(struct script_stack*); -void script_free_code(struct script_code* code); - -struct dbt* script_get_label_db(void); -struct dbt* script_get_userfunc_db(void); - -int script_config_read(char *cfgName); -int do_init_script(void); -int do_final_script(void); -int add_str(const unsigned char *p); -int script_reload(void); - -extern char mapreg_txt[]; - -#endif - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _SCRIPT_H_ +#define _SCRIPT_H_ + +extern int potion_flag; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex] +extern int potion_hp, potion_per_hp, potion_sp, potion_per_sp; +extern int potion_target; + +extern struct Script_Config { + unsigned verbose_mode : 1; + unsigned warn_func_no_comma : 1; + unsigned warn_cmd_no_comma : 1; + unsigned warn_func_mismatch_paramnum : 1; + unsigned warn_cmd_mismatch_paramnum : 1; + int check_cmdcount; + int check_gotocount; + + unsigned event_script_type : 1; + unsigned event_requires_trigger : 1; + char die_event_name[NAME_LENGTH]; + char kill_pc_event_name[NAME_LENGTH]; + char kill_mob_event_name[NAME_LENGTH]; + char login_event_name[NAME_LENGTH]; + char logout_event_name[NAME_LENGTH]; + char loadmap_event_name[NAME_LENGTH]; + char baselvup_event_name[NAME_LENGTH]; + char joblvup_event_name[NAME_LENGTH]; +} script_config; + +struct script_data { + int type; + union { + int num; + char *str; + } u; + struct linkdb_node** ref; +}; + +// Moved defsp from script_state to script_stack since +// it must be saved when script state is RERUNLINE. [Eoe / jA 1094] +struct script_code { + int script_size; + unsigned char* script_buf; + struct linkdb_node* script_vars; +}; + +struct script_state { + struct script_stack { + int sp,sp_max,defsp; + struct script_data *stack_data; + struct linkdb_node **var_function; // 関数依存変数 + } *stack; + int start,end; + int pos,state; + int rid,oid; + struct script_code *script, *scriptroot; + struct sleep_data { + int tick,timer,charid; + } sleep; +}; + +struct script_code* parse_script(unsigned char *,const char*,int); +void run_script_sub(struct script_code *rootscript,int pos,int rid,int oid, char* file, int lineno); +void run_script(struct script_code*,int,int,int); + +int set_var(struct map_session_data *sd, char *name, void *val); +int conv_num(struct script_state *st,struct script_data *data); +char* conv_str(struct script_state *st,struct script_data *data); +void setd_sub(struct script_state *st, struct map_session_data *sd, char *varname, int elem, void *value, struct linkdb_node **ref); +int run_script_timer(int tid, unsigned int tick, int id, int data); +void run_script_main(struct script_state *st); + +struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n); +void script_free_stack(struct script_stack*); +void script_free_code(struct script_code* code); + +struct dbt* script_get_label_db(void); +struct dbt* script_get_userfunc_db(void); + +int script_config_read(char *cfgName); +int do_init_script(void); +int do_final_script(void); +int add_str(const unsigned char *p); +int script_reload(void); + +extern char mapreg_txt[]; + +#endif + diff --git a/src/map/skill.h b/src/map/skill.h index 952448d74..973648499 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1,958 +1,958 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _SKILL_H_ -#define _SKILL_H_ - -#include "map.h" - -#define MAX_SKILL_DB 1100 -#define MAX_SKILL_PRODUCE_DB 150 -#define MAX_PRODUCE_RESOURCE 12 -#define MAX_SKILL_ARROW_DB 150 -#define MAX_SKILL_ABRA_DB 350 - -//Constants to identify the skill's inf value: -#define INF_ATTACK_SKILL 1 -#define INF_GROUND_SKILL 2 -// Skills casted on self where target is automatically chosen: -#define INF_SELF_SKILL 4 -#define INF_SUPPORT_SKILL 16 -#define INF_TARGET_TRAP 32 - -//Constants to identify a skill's nk value. -//The NK value applies only to non INF_GROUND_SKILL skills. -#define NK_NO_DAMAGE 0x1 -#define NK_SPLASH (0x2|0x4) // 0x4 = splash & split -#define NK_SPLASHSPLIT 0x4 -//A skill with 3 would be no damage + splash: area of effect. -//Constants to identify a skill's inf2 value. -#define INF2_QUEST_SKILL 1 -//NPC skills are those that players can't have in their skill tree. -#define INF2_NPC_SKILL 2 -#define INF2_WEDDING_SKILL 4 -#define INF2_SPIRIT_SKILL 8 -#define INF2_GUILD_SKILL 16 -#define INF2_SONG_DANCE 32 -#define INF2_ENSEMBLE_SKILL 64 -#define INF2_TRAP 128 -//Refers to ground placed skills that will target the caster as well (like Grandcross) -#define INF2_TARGET_SELF 256 -#define INF2_NO_TARGET_SELF 512 -#define INF2_PARTY_ONLY 1024 -#define INF2_GUILD_ONLY 2048 -//For Party/Guild only skills that can ALSO be used on enemies. -#define INF2_ALLOW_ENEMY 4096 - -//Walk intervals at which chase-skills are attempted to be triggered. -#define WALK_SKILL_INTERVAL 5 - -// To be passed to skill_attack, whether the skill damage should disable level or animation. [Skotlex] -#define SD_LEVEL 0x1000 -#define SD_ANIMATION 0x2000 - -// スキルデ?タベ?ス -struct skill_db { - char *name; - char *desc; - int range[MAX_SKILL_LEVEL],hit,inf,pl,nk,splash[MAX_SKILL_LEVEL],max; - int num[MAX_SKILL_LEVEL]; - int cast[MAX_SKILL_LEVEL],walkdelay[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL]; - int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL]; - int castcancel,cast_def_rate; - int inf2,maxcount,skill_type; - int blewcount[MAX_SKILL_LEVEL]; - int hp[MAX_SKILL_LEVEL],sp[MAX_SKILL_LEVEL],mhp[MAX_SKILL_LEVEL],hp_rate[MAX_SKILL_LEVEL],sp_rate[MAX_SKILL_LEVEL],zeny[MAX_SKILL_LEVEL]; - int weapon,ammo,ammo_qty[MAX_SKILL_LEVEL],state,spiritball[MAX_SKILL_LEVEL]; - int itemid[10],amount[10]; - int castnodex[MAX_SKILL_LEVEL]; - int delaynoagi[MAX_SKILL_LEVEL]; - int nocast; - int unit_id[2]; - int unit_layout_type[MAX_SKILL_LEVEL]; - int unit_range[MAX_SKILL_LEVEL]; - int unit_interval; - int unit_target; - int unit_flag; -}; -extern struct skill_db skill_db[MAX_SKILL_DB]; - -struct skill_name_db { - int id; // skill id - char *name; // search strings - char *desc; // description that shows up for search's -}; - -#define MAX_SKILL_UNIT_LAYOUT 50 -#define MAX_SQUARE_LAYOUT 5 // 11*11のユニット配置が最大 -#define MAX_SKILL_UNIT_COUNT ((MAX_SQUARE_LAYOUT*2+1)*(MAX_SQUARE_LAYOUT*2+1)) -struct skill_unit_layout { - int count; - int dx[MAX_SKILL_UNIT_COUNT]; - int dy[MAX_SKILL_UNIT_COUNT]; -}; - -enum { - UF_DEFNOTENEMY = 0x0001, // defnotenemy 設定でBCT_NOENEMYに切り替え - UF_NOREITERATION = 0x0002, // 重複置き禁止 - UF_NOFOOTSET = 0x0004, // 足元置き禁止 - UF_NOOVERLAP = 0x0008, // ユニット効果が重複しない - UF_NOPC = 0x0010, //May not target players - UF_NOMOB = 0x0020, //May not target mobs - UF_SKILL = 0x0080, //May target skills - UF_DANCE = 0x0100, //Dance - UF_ENSEMBLE = 0x0200, //Duet - UF_SONG = 0x0400, //Song - UF_DUALMODE = 0x0800, //Spells should trigger both ontimer and onplace/onout/onleft effects. -}; - -// アイテム作成デ?タベ?ス -struct skill_produce_db { - int nameid, trigger; - int req_skill,req_skill_lv,itemlv; - int mat_id[MAX_PRODUCE_RESOURCE],mat_amount[MAX_PRODUCE_RESOURCE]; -}; -extern struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB]; - -// 矢作成デ?タベ?ス -struct skill_arrow_db { - int nameid, trigger; - int cre_id[5],cre_amount[5]; -}; -extern struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB]; - -// アブラカダブラデ?タベ?ス -struct skill_abra_db { - int nameid; - int req_lv; - int per; -}; -extern struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB]; - -extern int enchant_eff[5]; -extern int deluge_eff[5]; - -struct block_list; -struct map_session_data; -struct skill_unit; -struct skill_unit_group; - -int do_init_skill(void); -int do_final_skill(void); - -//Returns the cast type of the skill: ground cast, castend damage, castend no damage -enum { CAST_GROUND, CAST_DAMAGE, CAST_NODAMAGE }; -int skill_get_casttype(int id); //[Skotlex] -// スキルデ?タベ?スへのアクセサ -// -int skill_get_type( int id ); -int skill_get_hit( int id ); -int skill_get_inf( int id ); -int skill_get_pl( int id ); -int skill_get_nk( int id ); -int skill_get_max( int id ); -int skill_get_range( int id , int lv ); -int skill_get_range2(struct block_list *bl, int id, int lv); -int skill_get_splash( int id , int lv ); -int skill_get_hp( int id ,int lv ); -int skill_get_mhp( int id ,int lv ); -int skill_get_sp( int id ,int lv ); -int skill_get_state(int id); -int skill_get_zeny( int id ,int lv ); -int skill_get_num( int id ,int lv ); -int skill_get_cast( int id ,int lv ); -int skill_get_delay( int id ,int lv ); -int skill_get_walkdelay( int id ,int lv ); -int skill_get_time( int id ,int lv ); -int skill_get_time2( int id ,int lv ); -int skill_get_castnodex( int id ,int lv ); -int skill_get_castdef( int id ); -int skill_get_weapontype( int id ); -int skill_get_ammotype( int id ); -int skill_get_ammo_qty( int id, int lv ); -int skill_get_nocast( int id ); -int skill_get_unit_id(int id,int flag); -int skill_get_inf2( int id ); -int skill_get_castcancel( int id ); -int skill_get_maxcount( int id ); -int skill_get_blewcount( int id ,int lv ); -int skill_get_unit_flag( int id ); -int skill_get_unit_target( int id ); -int skill_tree_get_max( int id, int b_class ); // Celest -const char* skill_get_name( int id ); // [Skotlex] - -int skill_isammotype(TBL_PC *sd, int skill); -int skill_castend_id( int tid, unsigned int tick, int id,int data ); -int skill_castend_pos( int tid, unsigned int tick, int id,int data ); -int skill_castend_map( struct map_session_data *sd,int skill_num, const char *map); - -int skill_cleartimerskill(struct block_list *src); -int skill_addtimerskill(struct block_list *src,unsigned int tick,int target,int x,int y,int skill_id,int skill_lv,int type,int flag); - -// 追加?果 -int skill_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick); -int skill_counter_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick); -int skill_blown( struct block_list *src, struct block_list *target,int count); -int skill_break_equip(struct block_list *bl, unsigned short where, int rate, int flag); -// ユニットスキル -struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag); -struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2); -int skill_delunit(struct skill_unit *unit, int flag); -struct skill_unit_group *skill_initunitgroup(struct block_list *src, - int count,int skillid,int skilllv,int unit_id, int limit, int interval); -int skill_delunitgroup(struct block_list *src, struct skill_unit_group *group, int flag); -int skill_clear_unitgroup(struct block_list *src); -int skill_clear_group(struct block_list *bl, int flag); - -int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl, - int damage,unsigned int tick); - -int skill_castfix( struct block_list *bl, int skill_id, int skill_lv); -int skill_castfix_sc( struct block_list *bl, int time); -int skill_delayfix( struct block_list *bl, int skill_id, int skill_lv); -int skill_check_condition( struct map_session_data *sd,int skill, int lv, int type); -int skill_check_pc_partner(struct map_session_data *sd, int skill_id, int* skill_lv, int range, int cast_flag); -// -- moonsoul (added skill_check_unit_cell) -int skill_check_unit_cell(int skillid,int m,int x,int y,int unit_id); -int skill_unit_out_all( struct block_list *bl,unsigned int tick,int range); -int skill_unit_move(struct block_list *bl,unsigned int tick,int flag); -int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy); -void skill_setmapcell(struct block_list *src, int skill_num, int skill_lv, int flag); - -struct skill_unit_group *skill_check_dancing( struct block_list *src ); -void skill_stop_dancing(struct block_list *src); - -// Guild skills [celest] -int skill_guildaura_sub (struct block_list *bl,va_list ap); - -// 詠唱キャンセル -int skill_castcancel(struct block_list *bl,int type); - -int skill_sit (struct map_session_data *sd, int type); -void skill_brandishspear_first(struct square *tc,int dir,int x,int y); -void skill_brandishspear_dir(struct square *tc,int dir,int are); -void skill_repairweapon(struct map_session_data *sd, int idx); -void skill_identify(struct map_session_data *sd,int idx); -void skill_weaponrefine(struct map_session_data *sd,int idx); // [Celest] -int skill_autospell(struct map_session_data *md,int skillid); - -int skill_calc_heal(struct block_list *bl, int skill_lv); - -int skill_check_cloaking(struct block_list *bl, struct status_change *sc); - -// ステ?タス異常 -int skill_enchant_elemental_end(struct block_list *bl, int type); -int skillnotok(int skillid, struct map_session_data *sd); -int skillnotok_hom (int skillid, struct homun_data *hd) ; //[orn] -int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap); //[orn] - -// アイテム作成 -int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty); -int skill_produce_mix( struct map_session_data *sd, - int skill_id, int nameid, int slot1, int slot2, int slot3, int qty ); - -int skill_arrow_create( struct map_session_data *sd,int nameid); - -// mobスキルのため -int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); -int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); -int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag); -int skill_blockpc_start (struct map_session_data*,int,int); // [celest] -int skill_blockmerc_start (struct homun_data*,int,int); //[orn] - -// スキル攻?一括?理 -int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc, - struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); - -void skill_reload(void); - -enum { - ST_NONE,ST_HIDING,ST_CLOAKING,ST_HIDDEN,ST_RIDING,ST_FALCON,ST_CART,ST_SHIELD,ST_SIGHT,ST_EXPLOSIONSPIRITS,ST_CARTBOOST, - ST_RECOV_WEIGHT_RATE,ST_MOVE_ENABLE,ST_WATER, -}; - -enum { - NV_BASIC = 1, - - SM_SWORD, - SM_TWOHAND, - SM_RECOVERY, - SM_BASH, - SM_PROVOKE, - SM_MAGNUM, - SM_ENDURE, - - MG_SRECOVERY, - MG_SIGHT, - MG_NAPALMBEAT, - MG_SAFETYWALL, - MG_SOULSTRIKE, - MG_COLDBOLT, - MG_FROSTDIVER, - MG_STONECURSE, - MG_FIREBALL, - MG_FIREWALL, - MG_FIREBOLT, - MG_LIGHTNINGBOLT, - MG_THUNDERSTORM, - - AL_DP, - AL_DEMONBANE, - AL_RUWACH, - AL_PNEUMA, - AL_TELEPORT, - AL_WARP, - AL_HEAL, - AL_INCAGI, - AL_DECAGI, - AL_HOLYWATER, - AL_CRUCIS, - AL_ANGELUS, - AL_BLESSING, - AL_CURE, - - MC_INCCARRY, - MC_DISCOUNT, - MC_OVERCHARGE, - MC_PUSHCART, - MC_IDENTIFY, - MC_VENDING, - MC_MAMMONITE, - - AC_OWL, - AC_VULTURE, - AC_CONCENTRATION, - AC_DOUBLE, - AC_SHOWER, - - TF_DOUBLE, - TF_MISS, - TF_STEAL, - TF_HIDING, - TF_POISON, - TF_DETOXIFY, - - ALL_RESURRECTION, - - KN_SPEARMASTERY, - KN_PIERCE, - KN_BRANDISHSPEAR, - KN_SPEARSTAB, - KN_SPEARBOOMERANG, - KN_TWOHANDQUICKEN, - KN_AUTOCOUNTER, - KN_BOWLINGBASH, - KN_RIDING, - KN_CAVALIERMASTERY, - - PR_MACEMASTERY, - PR_IMPOSITIO, - PR_SUFFRAGIUM, - PR_ASPERSIO, - PR_BENEDICTIO, - PR_SANCTUARY, - PR_SLOWPOISON, - PR_STRECOVERY, - PR_KYRIE, - PR_MAGNIFICAT, - PR_GLORIA, - PR_LEXDIVINA, - PR_TURNUNDEAD, - PR_LEXAETERNA, - PR_MAGNUS, - - WZ_FIREPILLAR, - WZ_SIGHTRASHER, - WZ_FIREIVY, - WZ_METEOR, - WZ_JUPITEL, - WZ_VERMILION, - WZ_WATERBALL, - WZ_ICEWALL, - WZ_FROSTNOVA, - WZ_STORMGUST, - WZ_EARTHSPIKE, - WZ_HEAVENDRIVE, - WZ_QUAGMIRE, - WZ_ESTIMATION, - - BS_IRON, - BS_STEEL, - BS_ENCHANTEDSTONE, - BS_ORIDEOCON, - BS_DAGGER, - BS_SWORD, - BS_TWOHANDSWORD, - BS_AXE, - BS_MACE, - BS_KNUCKLE, - BS_SPEAR, - BS_HILTBINDING, - BS_FINDINGORE, - BS_WEAPONRESEARCH, - BS_REPAIRWEAPON, - BS_SKINTEMPER, - BS_HAMMERFALL, - BS_ADRENALINE, - BS_WEAPONPERFECT, - BS_OVERTHRUST, - BS_MAXIMIZE, - - HT_SKIDTRAP, - HT_LANDMINE, - HT_ANKLESNARE, - HT_SHOCKWAVE, - HT_SANDMAN, - HT_FLASHER, - HT_FREEZINGTRAP, - HT_BLASTMINE, - HT_CLAYMORETRAP, - HT_REMOVETRAP, - HT_TALKIEBOX, - HT_BEASTBANE, - HT_FALCON, - HT_STEELCROW, - HT_BLITZBEAT, - HT_DETECTING, - HT_SPRINGTRAP, - - AS_RIGHT, - AS_LEFT, - AS_KATAR, - AS_CLOAKING, - AS_SONICBLOW, - AS_GRIMTOOTH, - AS_ENCHANTPOISON, - AS_POISONREACT, - AS_VENOMDUST, - AS_SPLASHER, - - NV_FIRSTAID, - NV_TRICKDEAD, - SM_MOVINGRECOVERY, - SM_FATALBLOW, - SM_AUTOBERSERK, - AC_MAKINGARROW, - AC_CHARGEARROW, - TF_SPRINKLESAND, - TF_BACKSLIDING, - TF_PICKSTONE, - TF_THROWSTONE, - MC_CARTREVOLUTION, - MC_CHANGECART, - MC_LOUD, - AL_HOLYLIGHT, - MG_ENERGYCOAT, - - NPC_PIERCINGATT, - NPC_MENTALBREAKER, - NPC_RANGEATTACK, - NPC_ATTRICHANGE, - NPC_CHANGEWATER, - NPC_CHANGEGROUND, - NPC_CHANGEFIRE, - NPC_CHANGEWIND, - NPC_CHANGEPOISON, - NPC_CHANGEHOLY, - NPC_CHANGEDARKNESS, - NPC_CHANGETELEKINESIS, - NPC_CRITICALSLASH, - NPC_COMBOATTACK, - NPC_GUIDEDATTACK, - NPC_SELFDESTRUCTION, - NPC_SPLASHATTACK, - NPC_SUICIDE, - NPC_POISON, - NPC_BLINDATTACK, - NPC_SILENCEATTACK, - NPC_STUNATTACK, - NPC_PETRIFYATTACK, - NPC_CURSEATTACK, - NPC_SLEEPATTACK, - NPC_RANDOMATTACK, - NPC_WATERATTACK, - NPC_GROUNDATTACK, - NPC_FIREATTACK, - NPC_WINDATTACK, - NPC_POISONATTACK, - NPC_HOLYATTACK, - NPC_DARKNESSATTACK, - NPC_TELEKINESISATTACK, - NPC_MAGICALATTACK, - NPC_METAMORPHOSIS, - NPC_PROVOCATION, - NPC_SMOKING, - NPC_SUMMONSLAVE, - NPC_EMOTION, - NPC_TRANSFORMATION, - NPC_BLOODDRAIN, - NPC_ENERGYDRAIN, - NPC_KEEPING, - NPC_DARKBREATH, - NPC_DARKBLESSING, - NPC_BARRIER, - NPC_DEFENDER, - NPC_LICK, - NPC_HALLUCINATION, - NPC_REBIRTH, - NPC_SUMMONMONSTER, - - RG_SNATCHER, - RG_STEALCOIN, - RG_BACKSTAP, - RG_TUNNELDRIVE, - RG_RAID, - RG_STRIPWEAPON, - RG_STRIPSHIELD, - RG_STRIPARMOR, - RG_STRIPHELM, - RG_INTIMIDATE, - RG_GRAFFITI, - RG_FLAGGRAFFITI, - RG_CLEANER, - RG_GANGSTER, - RG_COMPULSION, - RG_PLAGIARISM, - - AM_AXEMASTERY, - AM_LEARNINGPOTION, - AM_PHARMACY, - AM_DEMONSTRATION, - AM_ACIDTERROR, - AM_POTIONPITCHER, - AM_CANNIBALIZE, - AM_SPHEREMINE, - AM_CP_WEAPON, - AM_CP_SHIELD, - AM_CP_ARMOR, - AM_CP_HELM, - AM_BIOETHICS, - AM_BIOTECHNOLOGY, - AM_CREATECREATURE, - AM_CULTIVATION, - AM_FLAMECONTROL, - AM_CALLHOMUN, - AM_REST, - AM_DRILLMASTER, - AM_HEALHOMUN, - AM_RESURRECTHOMUN, - - CR_TRUST, - CR_AUTOGUARD, - CR_SHIELDCHARGE, - CR_SHIELDBOOMERANG, - CR_REFLECTSHIELD, - CR_HOLYCROSS, - CR_GRANDCROSS, - CR_DEVOTION, - CR_PROVIDENCE, - CR_DEFENDER, - CR_SPEARQUICKEN, - - MO_IRONHAND, - MO_SPIRITSRECOVERY, - MO_CALLSPIRITS, - MO_ABSORBSPIRITS, - MO_TRIPLEATTACK, - MO_BODYRELOCATION, - MO_DODGE, - MO_INVESTIGATE, - MO_FINGEROFFENSIVE, - MO_STEELBODY, - MO_BLADESTOP, - MO_EXPLOSIONSPIRITS, - MO_EXTREMITYFIST, - MO_CHAINCOMBO, - MO_COMBOFINISH, - - SA_ADVANCEDBOOK, - SA_CASTCANCEL, - SA_MAGICROD, - SA_SPELLBREAKER, - SA_FREECAST, - SA_AUTOSPELL, - SA_FLAMELAUNCHER, - SA_FROSTWEAPON, - SA_LIGHTNINGLOADER, - SA_SEISMICWEAPON, - SA_DRAGONOLOGY, - SA_VOLCANO, - SA_DELUGE, - SA_VIOLENTGALE, - SA_LANDPROTECTOR, - SA_DISPELL, - SA_ABRACADABRA, - SA_MONOCELL, - SA_CLASSCHANGE, - SA_SUMMONMONSTER, - SA_REVERSEORCISH, - SA_DEATH, - SA_FORTUNE, - SA_TAMINGMONSTER, - SA_QUESTION, - SA_GRAVITY, - SA_LEVELUP, - SA_INSTANTDEATH, - SA_FULLRECOVERY, - SA_COMA, - - BD_ADAPTATION, - BD_ENCORE, - BD_LULLABY, - BD_RICHMANKIM, - BD_ETERNALCHAOS, - BD_DRUMBATTLEFIELD, - BD_RINGNIBELUNGEN, - BD_ROKISWEIL, - BD_INTOABYSS, - BD_SIEGFRIED, - BD_RAGNAROK, - - BA_MUSICALLESSON, - BA_MUSICALSTRIKE, - BA_DISSONANCE, - BA_FROSTJOKE, - BA_WHISTLE, - BA_ASSASSINCROSS, - BA_POEMBRAGI, - BA_APPLEIDUN, - - DC_DANCINGLESSON, - DC_THROWARROW, - DC_UGLYDANCE, - DC_SCREAM, - DC_HUMMING, - DC_DONTFORGETME, - DC_FORTUNEKISS, - DC_SERVICEFORYOU, - - NPC_RANDOMMOVE, - NPC_SPEEDUP, - NPC_REVENGE, - - WE_MALE, - WE_FEMALE, - WE_CALLPARTNER, - - ITM_TOMAHAWK, - - NPC_DARKCROSS, - NPC_GRANDDARKNESS, - NPC_DARKSTRIKE, - NPC_DARKTHUNDER, - NPC_STOP, - NPC_BREAKWEAPON, - NPC_BREAKARMOR, - NPC_BREAKHELM, - NPC_BREAKSHIELD, - NPC_UNDEADATTACK, - NPC_CHANGEUNDEAD, - NPC_POWERUP, - NPC_AGIUP, - NPC_SIEGEMODE, - NPC_CALLSLAVE, - NPC_INVISIBLE, - NPC_RUN, - - LK_AURABLADE, - LK_PARRYING, - LK_CONCENTRATION, - LK_TENSIONRELAX, - LK_BERSERK, - LK_FURY, - HP_ASSUMPTIO, - HP_BASILICA, - HP_MEDITATIO, - HW_SOULDRAIN, - HW_MAGICCRASHER, - HW_MAGICPOWER, - PA_PRESSURE, - PA_SACRIFICE, - PA_GOSPEL, - CH_PALMSTRIKE, - CH_TIGERFIST, - CH_CHAINCRUSH, - PF_HPCONVERSION, - PF_SOULCHANGE, - PF_SOULBURN, - ASC_KATAR, - ASC_HALLUCINATION, - ASC_EDP, - ASC_BREAKER, - SN_SIGHT, - SN_FALCONASSAULT, - SN_SHARPSHOOTING, - SN_WINDWALK, - WS_MELTDOWN, - WS_CREATECOIN, - WS_CREATENUGGET, - WS_CARTBOOST, - WS_SYSTEMCREATE, - ST_CHASEWALK, - ST_REJECTSWORD, - ST_STEALBACKPACK, - CR_ALCHEMY, - CR_SYNTHESISPOTION, - CG_ARROWVULCAN, - CG_MOONLIT, - CG_MARIONETTE, - LK_SPIRALPIERCE, - LK_HEADCRUSH, - LK_JOINTBEAT, - HW_NAPALMVULCAN, - CH_SOULCOLLECT, - PF_MINDBREAKER, - PF_MEMORIZE, - PF_FOGWALL, - PF_SPIDERWEB, - ASC_METEORASSAULT, - ASC_CDP, - WE_BABY, - WE_CALLPARENT, - WE_CALLBABY, - - TK_RUN, - TK_READYSTORM, - TK_STORMKICK, - TK_READYDOWN, - TK_DOWNKICK, - TK_READYTURN, - TK_TURNKICK, - TK_READYCOUNTER, - TK_COUNTER, - TK_DODGE, - TK_JUMPKICK, - TK_HPTIME, - TK_SPTIME, - TK_POWER, - TK_SEVENWIND, - TK_HIGHJUMP, - SG_FEEL, - SG_SUN_WARM, - SG_MOON_WARM, - SG_STAR_WARM, - SG_SUN_COMFORT, - SG_MOON_COMFORT, - SG_STAR_COMFORT, - SG_HATE, - SG_SUN_ANGER, - SG_MOON_ANGER, - SG_STAR_ANGER, - SG_SUN_BLESS, - SG_MOON_BLESS, - SG_STAR_BLESS, - SG_DEVIL, - SG_FRIEND, - SG_KNOWLEDGE, - SG_FUSION, - SL_ALCHEMIST, - AM_BERSERKPITCHER, - SL_MONK, - SL_STAR, - SL_SAGE, - SL_CRUSADER, - SL_SUPERNOVICE, - SL_KNIGHT, - SL_WIZARD, - SL_PRIEST, - SL_BARDDANCER, - SL_ROGUE, - SL_ASSASIN, - SL_BLACKSMITH, - BS_ADRENALINE2, - SL_HUNTER, - SL_SOULLINKER, - SL_KAIZEL, - SL_KAAHI, - SL_KAUPE, - SL_KAITE, - SL_KAINA, - SL_STIN, - SL_STUN, - SL_SMA, - SL_SWOO, - SL_SKE, - SL_SKA, - - SM_SELFPROVOKE, - NPC_EMOTION_ON, - ST_PRESERVE, - ST_FULLSTRIP, - WS_WEAPONREFINE, - CR_SLIMPITCHER, - CR_FULLPROTECTION, - PA_SHIELDCHAIN, - HP_MANARECHARGE, - PF_DOUBLECASTING, - HW_GANBANTEIN, - HW_GRAVITATION, - WS_CARTTERMINATION, - WS_OVERTHRUSTMAX, - CG_LONGINGFREEDOM, - CG_HERMODE, - CG_TAROTCARD, - CR_ACIDDEMONSTRATION, - CR_CULTIVATION, - //492,missing? - TK_MISSION = 493, - SL_HIGH, - KN_ONEHAND, - AM_TWILIGHT1, - AM_TWILIGHT2, - AM_TWILIGHT3, - HT_POWER, - GS_GLITTERING, - GS_FLING, - GS_TRIPLEACTION, - GS_BULLSEYE, - GS_MADNESSCANCEL, - GS_ADJUSTMENT, - GS_INCREASING, - GS_MAGICALBULLET, - GS_CRACKER, - GS_SINGLEACTION, - GS_SNAKEEYE, - GS_CHAINACTION, - GS_TRACKING, - GS_DISARM, - GS_PIERCINGSHOT, - GS_RAPIDSHOWER, - GS_DESPERADO, - GS_GATLINGFEVER, - GS_DUST, - GS_FULLBUSTER, - GS_SPREADATTACK, - GS_GROUNDDRIFT, - NJ_TOBIDOUGU, - NJ_SYURIKEN, - NJ_KUNAI, - NJ_HUUMA, - NJ_ZENYNAGE, - NJ_TATAMIGAESHI, - NJ_KASUMIKIRI, - NJ_SHADOWJUMP, - NJ_KIRIKAGE, - NJ_UTSUSEMI, - NJ_BUNSINJYUTSU, - NJ_NINPOU, - NJ_KOUENKA, - NJ_KAENSIN, - NJ_BAKUENRYU, - NJ_HYOUSENSOU, - NJ_SUITON, - NJ_HYOUSYOURAKU, - NJ_HUUJIN, - NJ_RAIGEKISAI, - NJ_KAMAITACHI, - NJ_NEN, - NJ_ISSEN, - - KN_CHARGEATK = 1001, - CR_SHRINK, - AS_SONICACCEL, - AS_VENOMKNIFE, - RG_CLOSECONFINE, - WZ_SIGHTBLASTER, - SA_CREATECON, - SA_ELEMENTWATER, - HT_PHANTASMIC, - BA_PANGVOICE, - DC_WINKCHARM, - BS_UNFAIRLYTRICK, - BS_GREED, - PR_REDEMPTIO, - MO_KITRANSLATION, - MO_BALKYOUNG, - SA_ELEMENTGROUND, - SA_ELEMENTFIRE, - SA_ELEMENTWIND, - - HLIF_HEAL = 8001, - HLIF_AVOID, - HLIF_BRAIN, - HLIF_CHANGE, - HAMI_CASTLE, - HAMI_DEFENCE, - HAMI_SKIN, - HAMI_BLOODLUST, - HFLI_MOON, - HFLI_FLEET, - HFLI_SPEED, - HFLI_SBR44, - HVAN_CAPRICE, - HVAN_CHAOTIC, - HVAN_INSTRUCT, - HVAN_EXPLOSION, -}; - -enum { - UNT_SAFETYWALL = 0x7e, - UNT_FIREWALL, - UNT_WARP_WAITING, - UNT_WARP_ACTIVE, - //0x82 - UNT_SANCTUARY = 0x83, - UNT_MAGNUS, - UNT_PNEUMA, - UNT_ATTACK_SKILLS, //These show no effect on the client, therefore can be used for attack skills. - UNT_FIREPILLAR_WAITING, - UNT_FIREPILLAR_ACTIVE, - //0x89, 0x8a, 0x8b - UNT_USED_TRAPS = 0x8c, - UNT_ICEWALL, - UNT_QUAGMIRE, - UNT_BLASTMINE, - UNT_SKIDTRAP, - UNT_ANKLESNARE, - UNT_VENOMDUST, - UNT_LANDMINE, - UNT_SHOCKWAVE, - UNT_SANDMAN, - UNT_FLASHER, - UNT_FREEZINGTRAP, - UNT_CLAYMORETRAP, - UNT_TALKIEBOX, - UNT_VOLCANO, - UNT_DELUGE, - UNT_VIOLENTGALE, - UNT_LANDPROTECTOR, - UNT_LULLABY, - UNT_RICHMANKIM, - UNT_ETERNALCHAOS, - UNT_DRUMBATTLEFIELD, - UNT_RINGNIBELUNGEN, - UNT_ROKISWEIL, - UNT_INTOABYSS, - UNT_SIEGFRIED, - UNT_DISSONANCE, - UNT_WHISTLE, - UNT_ASSASSINCROSS, - UNT_POEMBRAGI, - UNT_APPLEIDUN, - UNT_UGLYDANCE, - UNT_HUMMING, - UNT_DONTFORGETME, - UNT_FORTUNEKISS, - UNT_SERVICEFORYOU, - UNT_GRAFFITI, - UNT_DEMONSTRATION, - UNT_CALLFAMILY, - UNT_GOSPEL, - UNT_BASILICA, - UNT_MOONLIT,//0xb5 //I HOPE this one doesn't shows any effects - UNT_FOGWALL = 0xb6, - UNT_SPIDERWEB, - UNT_GRAVITATION, - UNT_HERMODE, - UNT_DESPERADO, //0xba //Temporary setting until correct value is found. - UNT_SUITON = 0xbb, - UNT_TATAMIGAESHI, - UNT_KAENSIN, - UNT_GROUNDDRIFT_WIND, - UNT_GROUNDDRIFT_DARK, - UNT_GROUNDDRIFT_POISON, - UNT_GROUNDDRIFT_WATER, - UNT_GROUNDDRIFT_FIRE, -}; - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _SKILL_H_ +#define _SKILL_H_ + +#include "map.h" + +#define MAX_SKILL_DB 1100 +#define MAX_SKILL_PRODUCE_DB 150 +#define MAX_PRODUCE_RESOURCE 12 +#define MAX_SKILL_ARROW_DB 150 +#define MAX_SKILL_ABRA_DB 350 + +//Constants to identify the skill's inf value: +#define INF_ATTACK_SKILL 1 +#define INF_GROUND_SKILL 2 +// Skills casted on self where target is automatically chosen: +#define INF_SELF_SKILL 4 +#define INF_SUPPORT_SKILL 16 +#define INF_TARGET_TRAP 32 + +//Constants to identify a skill's nk value. +//The NK value applies only to non INF_GROUND_SKILL skills. +#define NK_NO_DAMAGE 0x1 +#define NK_SPLASH (0x2|0x4) // 0x4 = splash & split +#define NK_SPLASHSPLIT 0x4 +//A skill with 3 would be no damage + splash: area of effect. +//Constants to identify a skill's inf2 value. +#define INF2_QUEST_SKILL 1 +//NPC skills are those that players can't have in their skill tree. +#define INF2_NPC_SKILL 2 +#define INF2_WEDDING_SKILL 4 +#define INF2_SPIRIT_SKILL 8 +#define INF2_GUILD_SKILL 16 +#define INF2_SONG_DANCE 32 +#define INF2_ENSEMBLE_SKILL 64 +#define INF2_TRAP 128 +//Refers to ground placed skills that will target the caster as well (like Grandcross) +#define INF2_TARGET_SELF 256 +#define INF2_NO_TARGET_SELF 512 +#define INF2_PARTY_ONLY 1024 +#define INF2_GUILD_ONLY 2048 +//For Party/Guild only skills that can ALSO be used on enemies. +#define INF2_ALLOW_ENEMY 4096 + +//Walk intervals at which chase-skills are attempted to be triggered. +#define WALK_SKILL_INTERVAL 5 + +// To be passed to skill_attack, whether the skill damage should disable level or animation. [Skotlex] +#define SD_LEVEL 0x1000 +#define SD_ANIMATION 0x2000 + +// スキルデ?タベ?ス +struct skill_db { + char *name; + char *desc; + int range[MAX_SKILL_LEVEL],hit,inf,pl,nk,splash[MAX_SKILL_LEVEL],max; + int num[MAX_SKILL_LEVEL]; + int cast[MAX_SKILL_LEVEL],walkdelay[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL]; + int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL]; + int castcancel,cast_def_rate; + int inf2,maxcount,skill_type; + int blewcount[MAX_SKILL_LEVEL]; + int hp[MAX_SKILL_LEVEL],sp[MAX_SKILL_LEVEL],mhp[MAX_SKILL_LEVEL],hp_rate[MAX_SKILL_LEVEL],sp_rate[MAX_SKILL_LEVEL],zeny[MAX_SKILL_LEVEL]; + int weapon,ammo,ammo_qty[MAX_SKILL_LEVEL],state,spiritball[MAX_SKILL_LEVEL]; + int itemid[10],amount[10]; + int castnodex[MAX_SKILL_LEVEL]; + int delaynoagi[MAX_SKILL_LEVEL]; + int nocast; + int unit_id[2]; + int unit_layout_type[MAX_SKILL_LEVEL]; + int unit_range[MAX_SKILL_LEVEL]; + int unit_interval; + int unit_target; + int unit_flag; +}; +extern struct skill_db skill_db[MAX_SKILL_DB]; + +struct skill_name_db { + int id; // skill id + char *name; // search strings + char *desc; // description that shows up for search's +}; + +#define MAX_SKILL_UNIT_LAYOUT 50 +#define MAX_SQUARE_LAYOUT 5 // 11*11のユニット配置が最大 +#define MAX_SKILL_UNIT_COUNT ((MAX_SQUARE_LAYOUT*2+1)*(MAX_SQUARE_LAYOUT*2+1)) +struct skill_unit_layout { + int count; + int dx[MAX_SKILL_UNIT_COUNT]; + int dy[MAX_SKILL_UNIT_COUNT]; +}; + +enum { + UF_DEFNOTENEMY = 0x0001, // defnotenemy 設定でBCT_NOENEMYに切り替え + UF_NOREITERATION = 0x0002, // 重複置き禁止 + UF_NOFOOTSET = 0x0004, // 足元置き禁止 + UF_NOOVERLAP = 0x0008, // ユニット効果が重複しない + UF_NOPC = 0x0010, //May not target players + UF_NOMOB = 0x0020, //May not target mobs + UF_SKILL = 0x0080, //May target skills + UF_DANCE = 0x0100, //Dance + UF_ENSEMBLE = 0x0200, //Duet + UF_SONG = 0x0400, //Song + UF_DUALMODE = 0x0800, //Spells should trigger both ontimer and onplace/onout/onleft effects. +}; + +// アイテム作成デ?タベ?ス +struct skill_produce_db { + int nameid, trigger; + int req_skill,req_skill_lv,itemlv; + int mat_id[MAX_PRODUCE_RESOURCE],mat_amount[MAX_PRODUCE_RESOURCE]; +}; +extern struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB]; + +// 矢作成デ?タベ?ス +struct skill_arrow_db { + int nameid, trigger; + int cre_id[5],cre_amount[5]; +}; +extern struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB]; + +// アブラカダブラデ?タベ?ス +struct skill_abra_db { + int nameid; + int req_lv; + int per; +}; +extern struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB]; + +extern int enchant_eff[5]; +extern int deluge_eff[5]; + +struct block_list; +struct map_session_data; +struct skill_unit; +struct skill_unit_group; + +int do_init_skill(void); +int do_final_skill(void); + +//Returns the cast type of the skill: ground cast, castend damage, castend no damage +enum { CAST_GROUND, CAST_DAMAGE, CAST_NODAMAGE }; +int skill_get_casttype(int id); //[Skotlex] +// スキルデ?タベ?スへのアクセサ +// +int skill_get_type( int id ); +int skill_get_hit( int id ); +int skill_get_inf( int id ); +int skill_get_pl( int id ); +int skill_get_nk( int id ); +int skill_get_max( int id ); +int skill_get_range( int id , int lv ); +int skill_get_range2(struct block_list *bl, int id, int lv); +int skill_get_splash( int id , int lv ); +int skill_get_hp( int id ,int lv ); +int skill_get_mhp( int id ,int lv ); +int skill_get_sp( int id ,int lv ); +int skill_get_state(int id); +int skill_get_zeny( int id ,int lv ); +int skill_get_num( int id ,int lv ); +int skill_get_cast( int id ,int lv ); +int skill_get_delay( int id ,int lv ); +int skill_get_walkdelay( int id ,int lv ); +int skill_get_time( int id ,int lv ); +int skill_get_time2( int id ,int lv ); +int skill_get_castnodex( int id ,int lv ); +int skill_get_castdef( int id ); +int skill_get_weapontype( int id ); +int skill_get_ammotype( int id ); +int skill_get_ammo_qty( int id, int lv ); +int skill_get_nocast( int id ); +int skill_get_unit_id(int id,int flag); +int skill_get_inf2( int id ); +int skill_get_castcancel( int id ); +int skill_get_maxcount( int id ); +int skill_get_blewcount( int id ,int lv ); +int skill_get_unit_flag( int id ); +int skill_get_unit_target( int id ); +int skill_tree_get_max( int id, int b_class ); // Celest +const char* skill_get_name( int id ); // [Skotlex] + +int skill_isammotype(TBL_PC *sd, int skill); +int skill_castend_id( int tid, unsigned int tick, int id,int data ); +int skill_castend_pos( int tid, unsigned int tick, int id,int data ); +int skill_castend_map( struct map_session_data *sd,int skill_num, const char *map); + +int skill_cleartimerskill(struct block_list *src); +int skill_addtimerskill(struct block_list *src,unsigned int tick,int target,int x,int y,int skill_id,int skill_lv,int type,int flag); + +// 追加?果 +int skill_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick); +int skill_counter_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick); +int skill_blown( struct block_list *src, struct block_list *target,int count); +int skill_break_equip(struct block_list *bl, unsigned short where, int rate, int flag); +// ユニットスキル +struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag); +struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2); +int skill_delunit(struct skill_unit *unit, int flag); +struct skill_unit_group *skill_initunitgroup(struct block_list *src, + int count,int skillid,int skilllv,int unit_id, int limit, int interval); +int skill_delunitgroup(struct block_list *src, struct skill_unit_group *group, int flag); +int skill_clear_unitgroup(struct block_list *src); +int skill_clear_group(struct block_list *bl, int flag); + +int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl, + int damage,unsigned int tick); + +int skill_castfix( struct block_list *bl, int skill_id, int skill_lv); +int skill_castfix_sc( struct block_list *bl, int time); +int skill_delayfix( struct block_list *bl, int skill_id, int skill_lv); +int skill_check_condition( struct map_session_data *sd,int skill, int lv, int type); +int skill_check_pc_partner(struct map_session_data *sd, int skill_id, int* skill_lv, int range, int cast_flag); +// -- moonsoul (added skill_check_unit_cell) +int skill_check_unit_cell(int skillid,int m,int x,int y,int unit_id); +int skill_unit_out_all( struct block_list *bl,unsigned int tick,int range); +int skill_unit_move(struct block_list *bl,unsigned int tick,int flag); +int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy); +void skill_setmapcell(struct block_list *src, int skill_num, int skill_lv, int flag); + +struct skill_unit_group *skill_check_dancing( struct block_list *src ); +void skill_stop_dancing(struct block_list *src); + +// Guild skills [celest] +int skill_guildaura_sub (struct block_list *bl,va_list ap); + +// 詠唱キャンセル +int skill_castcancel(struct block_list *bl,int type); + +int skill_sit (struct map_session_data *sd, int type); +void skill_brandishspear_first(struct square *tc,int dir,int x,int y); +void skill_brandishspear_dir(struct square *tc,int dir,int are); +void skill_repairweapon(struct map_session_data *sd, int idx); +void skill_identify(struct map_session_data *sd,int idx); +void skill_weaponrefine(struct map_session_data *sd,int idx); // [Celest] +int skill_autospell(struct map_session_data *md,int skillid); + +int skill_calc_heal(struct block_list *bl, int skill_lv); + +int skill_check_cloaking(struct block_list *bl, struct status_change *sc); + +// ステ?タス異常 +int skill_enchant_elemental_end(struct block_list *bl, int type); +int skillnotok(int skillid, struct map_session_data *sd); +int skillnotok_hom (int skillid, struct homun_data *hd) ; //[orn] +int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap); //[orn] + +// アイテム作成 +int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty); +int skill_produce_mix( struct map_session_data *sd, + int skill_id, int nameid, int slot1, int slot2, int slot3, int qty ); + +int skill_arrow_create( struct map_session_data *sd,int nameid); + +// mobスキルのため +int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); +int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); +int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag); +int skill_blockpc_start (struct map_session_data*,int,int); // [celest] +int skill_blockmerc_start (struct homun_data*,int,int); //[orn] + +// スキル攻?一括?理 +int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc, + struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); + +void skill_reload(void); + +enum { + ST_NONE,ST_HIDING,ST_CLOAKING,ST_HIDDEN,ST_RIDING,ST_FALCON,ST_CART,ST_SHIELD,ST_SIGHT,ST_EXPLOSIONSPIRITS,ST_CARTBOOST, + ST_RECOV_WEIGHT_RATE,ST_MOVE_ENABLE,ST_WATER, +}; + +enum { + NV_BASIC = 1, + + SM_SWORD, + SM_TWOHAND, + SM_RECOVERY, + SM_BASH, + SM_PROVOKE, + SM_MAGNUM, + SM_ENDURE, + + MG_SRECOVERY, + MG_SIGHT, + MG_NAPALMBEAT, + MG_SAFETYWALL, + MG_SOULSTRIKE, + MG_COLDBOLT, + MG_FROSTDIVER, + MG_STONECURSE, + MG_FIREBALL, + MG_FIREWALL, + MG_FIREBOLT, + MG_LIGHTNINGBOLT, + MG_THUNDERSTORM, + + AL_DP, + AL_DEMONBANE, + AL_RUWACH, + AL_PNEUMA, + AL_TELEPORT, + AL_WARP, + AL_HEAL, + AL_INCAGI, + AL_DECAGI, + AL_HOLYWATER, + AL_CRUCIS, + AL_ANGELUS, + AL_BLESSING, + AL_CURE, + + MC_INCCARRY, + MC_DISCOUNT, + MC_OVERCHARGE, + MC_PUSHCART, + MC_IDENTIFY, + MC_VENDING, + MC_MAMMONITE, + + AC_OWL, + AC_VULTURE, + AC_CONCENTRATION, + AC_DOUBLE, + AC_SHOWER, + + TF_DOUBLE, + TF_MISS, + TF_STEAL, + TF_HIDING, + TF_POISON, + TF_DETOXIFY, + + ALL_RESURRECTION, + + KN_SPEARMASTERY, + KN_PIERCE, + KN_BRANDISHSPEAR, + KN_SPEARSTAB, + KN_SPEARBOOMERANG, + KN_TWOHANDQUICKEN, + KN_AUTOCOUNTER, + KN_BOWLINGBASH, + KN_RIDING, + KN_CAVALIERMASTERY, + + PR_MACEMASTERY, + PR_IMPOSITIO, + PR_SUFFRAGIUM, + PR_ASPERSIO, + PR_BENEDICTIO, + PR_SANCTUARY, + PR_SLOWPOISON, + PR_STRECOVERY, + PR_KYRIE, + PR_MAGNIFICAT, + PR_GLORIA, + PR_LEXDIVINA, + PR_TURNUNDEAD, + PR_LEXAETERNA, + PR_MAGNUS, + + WZ_FIREPILLAR, + WZ_SIGHTRASHER, + WZ_FIREIVY, + WZ_METEOR, + WZ_JUPITEL, + WZ_VERMILION, + WZ_WATERBALL, + WZ_ICEWALL, + WZ_FROSTNOVA, + WZ_STORMGUST, + WZ_EARTHSPIKE, + WZ_HEAVENDRIVE, + WZ_QUAGMIRE, + WZ_ESTIMATION, + + BS_IRON, + BS_STEEL, + BS_ENCHANTEDSTONE, + BS_ORIDEOCON, + BS_DAGGER, + BS_SWORD, + BS_TWOHANDSWORD, + BS_AXE, + BS_MACE, + BS_KNUCKLE, + BS_SPEAR, + BS_HILTBINDING, + BS_FINDINGORE, + BS_WEAPONRESEARCH, + BS_REPAIRWEAPON, + BS_SKINTEMPER, + BS_HAMMERFALL, + BS_ADRENALINE, + BS_WEAPONPERFECT, + BS_OVERTHRUST, + BS_MAXIMIZE, + + HT_SKIDTRAP, + HT_LANDMINE, + HT_ANKLESNARE, + HT_SHOCKWAVE, + HT_SANDMAN, + HT_FLASHER, + HT_FREEZINGTRAP, + HT_BLASTMINE, + HT_CLAYMORETRAP, + HT_REMOVETRAP, + HT_TALKIEBOX, + HT_BEASTBANE, + HT_FALCON, + HT_STEELCROW, + HT_BLITZBEAT, + HT_DETECTING, + HT_SPRINGTRAP, + + AS_RIGHT, + AS_LEFT, + AS_KATAR, + AS_CLOAKING, + AS_SONICBLOW, + AS_GRIMTOOTH, + AS_ENCHANTPOISON, + AS_POISONREACT, + AS_VENOMDUST, + AS_SPLASHER, + + NV_FIRSTAID, + NV_TRICKDEAD, + SM_MOVINGRECOVERY, + SM_FATALBLOW, + SM_AUTOBERSERK, + AC_MAKINGARROW, + AC_CHARGEARROW, + TF_SPRINKLESAND, + TF_BACKSLIDING, + TF_PICKSTONE, + TF_THROWSTONE, + MC_CARTREVOLUTION, + MC_CHANGECART, + MC_LOUD, + AL_HOLYLIGHT, + MG_ENERGYCOAT, + + NPC_PIERCINGATT, + NPC_MENTALBREAKER, + NPC_RANGEATTACK, + NPC_ATTRICHANGE, + NPC_CHANGEWATER, + NPC_CHANGEGROUND, + NPC_CHANGEFIRE, + NPC_CHANGEWIND, + NPC_CHANGEPOISON, + NPC_CHANGEHOLY, + NPC_CHANGEDARKNESS, + NPC_CHANGETELEKINESIS, + NPC_CRITICALSLASH, + NPC_COMBOATTACK, + NPC_GUIDEDATTACK, + NPC_SELFDESTRUCTION, + NPC_SPLASHATTACK, + NPC_SUICIDE, + NPC_POISON, + NPC_BLINDATTACK, + NPC_SILENCEATTACK, + NPC_STUNATTACK, + NPC_PETRIFYATTACK, + NPC_CURSEATTACK, + NPC_SLEEPATTACK, + NPC_RANDOMATTACK, + NPC_WATERATTACK, + NPC_GROUNDATTACK, + NPC_FIREATTACK, + NPC_WINDATTACK, + NPC_POISONATTACK, + NPC_HOLYATTACK, + NPC_DARKNESSATTACK, + NPC_TELEKINESISATTACK, + NPC_MAGICALATTACK, + NPC_METAMORPHOSIS, + NPC_PROVOCATION, + NPC_SMOKING, + NPC_SUMMONSLAVE, + NPC_EMOTION, + NPC_TRANSFORMATION, + NPC_BLOODDRAIN, + NPC_ENERGYDRAIN, + NPC_KEEPING, + NPC_DARKBREATH, + NPC_DARKBLESSING, + NPC_BARRIER, + NPC_DEFENDER, + NPC_LICK, + NPC_HALLUCINATION, + NPC_REBIRTH, + NPC_SUMMONMONSTER, + + RG_SNATCHER, + RG_STEALCOIN, + RG_BACKSTAP, + RG_TUNNELDRIVE, + RG_RAID, + RG_STRIPWEAPON, + RG_STRIPSHIELD, + RG_STRIPARMOR, + RG_STRIPHELM, + RG_INTIMIDATE, + RG_GRAFFITI, + RG_FLAGGRAFFITI, + RG_CLEANER, + RG_GANGSTER, + RG_COMPULSION, + RG_PLAGIARISM, + + AM_AXEMASTERY, + AM_LEARNINGPOTION, + AM_PHARMACY, + AM_DEMONSTRATION, + AM_ACIDTERROR, + AM_POTIONPITCHER, + AM_CANNIBALIZE, + AM_SPHEREMINE, + AM_CP_WEAPON, + AM_CP_SHIELD, + AM_CP_ARMOR, + AM_CP_HELM, + AM_BIOETHICS, + AM_BIOTECHNOLOGY, + AM_CREATECREATURE, + AM_CULTIVATION, + AM_FLAMECONTROL, + AM_CALLHOMUN, + AM_REST, + AM_DRILLMASTER, + AM_HEALHOMUN, + AM_RESURRECTHOMUN, + + CR_TRUST, + CR_AUTOGUARD, + CR_SHIELDCHARGE, + CR_SHIELDBOOMERANG, + CR_REFLECTSHIELD, + CR_HOLYCROSS, + CR_GRANDCROSS, + CR_DEVOTION, + CR_PROVIDENCE, + CR_DEFENDER, + CR_SPEARQUICKEN, + + MO_IRONHAND, + MO_SPIRITSRECOVERY, + MO_CALLSPIRITS, + MO_ABSORBSPIRITS, + MO_TRIPLEATTACK, + MO_BODYRELOCATION, + MO_DODGE, + MO_INVESTIGATE, + MO_FINGEROFFENSIVE, + MO_STEELBODY, + MO_BLADESTOP, + MO_EXPLOSIONSPIRITS, + MO_EXTREMITYFIST, + MO_CHAINCOMBO, + MO_COMBOFINISH, + + SA_ADVANCEDBOOK, + SA_CASTCANCEL, + SA_MAGICROD, + SA_SPELLBREAKER, + SA_FREECAST, + SA_AUTOSPELL, + SA_FLAMELAUNCHER, + SA_FROSTWEAPON, + SA_LIGHTNINGLOADER, + SA_SEISMICWEAPON, + SA_DRAGONOLOGY, + SA_VOLCANO, + SA_DELUGE, + SA_VIOLENTGALE, + SA_LANDPROTECTOR, + SA_DISPELL, + SA_ABRACADABRA, + SA_MONOCELL, + SA_CLASSCHANGE, + SA_SUMMONMONSTER, + SA_REVERSEORCISH, + SA_DEATH, + SA_FORTUNE, + SA_TAMINGMONSTER, + SA_QUESTION, + SA_GRAVITY, + SA_LEVELUP, + SA_INSTANTDEATH, + SA_FULLRECOVERY, + SA_COMA, + + BD_ADAPTATION, + BD_ENCORE, + BD_LULLABY, + BD_RICHMANKIM, + BD_ETERNALCHAOS, + BD_DRUMBATTLEFIELD, + BD_RINGNIBELUNGEN, + BD_ROKISWEIL, + BD_INTOABYSS, + BD_SIEGFRIED, + BD_RAGNAROK, + + BA_MUSICALLESSON, + BA_MUSICALSTRIKE, + BA_DISSONANCE, + BA_FROSTJOKE, + BA_WHISTLE, + BA_ASSASSINCROSS, + BA_POEMBRAGI, + BA_APPLEIDUN, + + DC_DANCINGLESSON, + DC_THROWARROW, + DC_UGLYDANCE, + DC_SCREAM, + DC_HUMMING, + DC_DONTFORGETME, + DC_FORTUNEKISS, + DC_SERVICEFORYOU, + + NPC_RANDOMMOVE, + NPC_SPEEDUP, + NPC_REVENGE, + + WE_MALE, + WE_FEMALE, + WE_CALLPARTNER, + + ITM_TOMAHAWK, + + NPC_DARKCROSS, + NPC_GRANDDARKNESS, + NPC_DARKSTRIKE, + NPC_DARKTHUNDER, + NPC_STOP, + NPC_BREAKWEAPON, + NPC_BREAKARMOR, + NPC_BREAKHELM, + NPC_BREAKSHIELD, + NPC_UNDEADATTACK, + NPC_CHANGEUNDEAD, + NPC_POWERUP, + NPC_AGIUP, + NPC_SIEGEMODE, + NPC_CALLSLAVE, + NPC_INVISIBLE, + NPC_RUN, + + LK_AURABLADE, + LK_PARRYING, + LK_CONCENTRATION, + LK_TENSIONRELAX, + LK_BERSERK, + LK_FURY, + HP_ASSUMPTIO, + HP_BASILICA, + HP_MEDITATIO, + HW_SOULDRAIN, + HW_MAGICCRASHER, + HW_MAGICPOWER, + PA_PRESSURE, + PA_SACRIFICE, + PA_GOSPEL, + CH_PALMSTRIKE, + CH_TIGERFIST, + CH_CHAINCRUSH, + PF_HPCONVERSION, + PF_SOULCHANGE, + PF_SOULBURN, + ASC_KATAR, + ASC_HALLUCINATION, + ASC_EDP, + ASC_BREAKER, + SN_SIGHT, + SN_FALCONASSAULT, + SN_SHARPSHOOTING, + SN_WINDWALK, + WS_MELTDOWN, + WS_CREATECOIN, + WS_CREATENUGGET, + WS_CARTBOOST, + WS_SYSTEMCREATE, + ST_CHASEWALK, + ST_REJECTSWORD, + ST_STEALBACKPACK, + CR_ALCHEMY, + CR_SYNTHESISPOTION, + CG_ARROWVULCAN, + CG_MOONLIT, + CG_MARIONETTE, + LK_SPIRALPIERCE, + LK_HEADCRUSH, + LK_JOINTBEAT, + HW_NAPALMVULCAN, + CH_SOULCOLLECT, + PF_MINDBREAKER, + PF_MEMORIZE, + PF_FOGWALL, + PF_SPIDERWEB, + ASC_METEORASSAULT, + ASC_CDP, + WE_BABY, + WE_CALLPARENT, + WE_CALLBABY, + + TK_RUN, + TK_READYSTORM, + TK_STORMKICK, + TK_READYDOWN, + TK_DOWNKICK, + TK_READYTURN, + TK_TURNKICK, + TK_READYCOUNTER, + TK_COUNTER, + TK_DODGE, + TK_JUMPKICK, + TK_HPTIME, + TK_SPTIME, + TK_POWER, + TK_SEVENWIND, + TK_HIGHJUMP, + SG_FEEL, + SG_SUN_WARM, + SG_MOON_WARM, + SG_STAR_WARM, + SG_SUN_COMFORT, + SG_MOON_COMFORT, + SG_STAR_COMFORT, + SG_HATE, + SG_SUN_ANGER, + SG_MOON_ANGER, + SG_STAR_ANGER, + SG_SUN_BLESS, + SG_MOON_BLESS, + SG_STAR_BLESS, + SG_DEVIL, + SG_FRIEND, + SG_KNOWLEDGE, + SG_FUSION, + SL_ALCHEMIST, + AM_BERSERKPITCHER, + SL_MONK, + SL_STAR, + SL_SAGE, + SL_CRUSADER, + SL_SUPERNOVICE, + SL_KNIGHT, + SL_WIZARD, + SL_PRIEST, + SL_BARDDANCER, + SL_ROGUE, + SL_ASSASIN, + SL_BLACKSMITH, + BS_ADRENALINE2, + SL_HUNTER, + SL_SOULLINKER, + SL_KAIZEL, + SL_KAAHI, + SL_KAUPE, + SL_KAITE, + SL_KAINA, + SL_STIN, + SL_STUN, + SL_SMA, + SL_SWOO, + SL_SKE, + SL_SKA, + + SM_SELFPROVOKE, + NPC_EMOTION_ON, + ST_PRESERVE, + ST_FULLSTRIP, + WS_WEAPONREFINE, + CR_SLIMPITCHER, + CR_FULLPROTECTION, + PA_SHIELDCHAIN, + HP_MANARECHARGE, + PF_DOUBLECASTING, + HW_GANBANTEIN, + HW_GRAVITATION, + WS_CARTTERMINATION, + WS_OVERTHRUSTMAX, + CG_LONGINGFREEDOM, + CG_HERMODE, + CG_TAROTCARD, + CR_ACIDDEMONSTRATION, + CR_CULTIVATION, + //492,missing? + TK_MISSION = 493, + SL_HIGH, + KN_ONEHAND, + AM_TWILIGHT1, + AM_TWILIGHT2, + AM_TWILIGHT3, + HT_POWER, + GS_GLITTERING, + GS_FLING, + GS_TRIPLEACTION, + GS_BULLSEYE, + GS_MADNESSCANCEL, + GS_ADJUSTMENT, + GS_INCREASING, + GS_MAGICALBULLET, + GS_CRACKER, + GS_SINGLEACTION, + GS_SNAKEEYE, + GS_CHAINACTION, + GS_TRACKING, + GS_DISARM, + GS_PIERCINGSHOT, + GS_RAPIDSHOWER, + GS_DESPERADO, + GS_GATLINGFEVER, + GS_DUST, + GS_FULLBUSTER, + GS_SPREADATTACK, + GS_GROUNDDRIFT, + NJ_TOBIDOUGU, + NJ_SYURIKEN, + NJ_KUNAI, + NJ_HUUMA, + NJ_ZENYNAGE, + NJ_TATAMIGAESHI, + NJ_KASUMIKIRI, + NJ_SHADOWJUMP, + NJ_KIRIKAGE, + NJ_UTSUSEMI, + NJ_BUNSINJYUTSU, + NJ_NINPOU, + NJ_KOUENKA, + NJ_KAENSIN, + NJ_BAKUENRYU, + NJ_HYOUSENSOU, + NJ_SUITON, + NJ_HYOUSYOURAKU, + NJ_HUUJIN, + NJ_RAIGEKISAI, + NJ_KAMAITACHI, + NJ_NEN, + NJ_ISSEN, + + KN_CHARGEATK = 1001, + CR_SHRINK, + AS_SONICACCEL, + AS_VENOMKNIFE, + RG_CLOSECONFINE, + WZ_SIGHTBLASTER, + SA_CREATECON, + SA_ELEMENTWATER, + HT_PHANTASMIC, + BA_PANGVOICE, + DC_WINKCHARM, + BS_UNFAIRLYTRICK, + BS_GREED, + PR_REDEMPTIO, + MO_KITRANSLATION, + MO_BALKYOUNG, + SA_ELEMENTGROUND, + SA_ELEMENTFIRE, + SA_ELEMENTWIND, + + HLIF_HEAL = 8001, + HLIF_AVOID, + HLIF_BRAIN, + HLIF_CHANGE, + HAMI_CASTLE, + HAMI_DEFENCE, + HAMI_SKIN, + HAMI_BLOODLUST, + HFLI_MOON, + HFLI_FLEET, + HFLI_SPEED, + HFLI_SBR44, + HVAN_CAPRICE, + HVAN_CHAOTIC, + HVAN_INSTRUCT, + HVAN_EXPLOSION, +}; + +enum { + UNT_SAFETYWALL = 0x7e, + UNT_FIREWALL, + UNT_WARP_WAITING, + UNT_WARP_ACTIVE, + //0x82 + UNT_SANCTUARY = 0x83, + UNT_MAGNUS, + UNT_PNEUMA, + UNT_ATTACK_SKILLS, //These show no effect on the client, therefore can be used for attack skills. + UNT_FIREPILLAR_WAITING, + UNT_FIREPILLAR_ACTIVE, + //0x89, 0x8a, 0x8b + UNT_USED_TRAPS = 0x8c, + UNT_ICEWALL, + UNT_QUAGMIRE, + UNT_BLASTMINE, + UNT_SKIDTRAP, + UNT_ANKLESNARE, + UNT_VENOMDUST, + UNT_LANDMINE, + UNT_SHOCKWAVE, + UNT_SANDMAN, + UNT_FLASHER, + UNT_FREEZINGTRAP, + UNT_CLAYMORETRAP, + UNT_TALKIEBOX, + UNT_VOLCANO, + UNT_DELUGE, + UNT_VIOLENTGALE, + UNT_LANDPROTECTOR, + UNT_LULLABY, + UNT_RICHMANKIM, + UNT_ETERNALCHAOS, + UNT_DRUMBATTLEFIELD, + UNT_RINGNIBELUNGEN, + UNT_ROKISWEIL, + UNT_INTOABYSS, + UNT_SIEGFRIED, + UNT_DISSONANCE, + UNT_WHISTLE, + UNT_ASSASSINCROSS, + UNT_POEMBRAGI, + UNT_APPLEIDUN, + UNT_UGLYDANCE, + UNT_HUMMING, + UNT_DONTFORGETME, + UNT_FORTUNEKISS, + UNT_SERVICEFORYOU, + UNT_GRAFFITI, + UNT_DEMONSTRATION, + UNT_CALLFAMILY, + UNT_GOSPEL, + UNT_BASILICA, + UNT_MOONLIT,//0xb5 //I HOPE this one doesn't shows any effects + UNT_FOGWALL = 0xb6, + UNT_SPIDERWEB, + UNT_GRAVITATION, + UNT_HERMODE, + UNT_DESPERADO, //0xba //Temporary setting until correct value is found. + UNT_SUITON = 0xbb, + UNT_TATAMIGAESHI, + UNT_KAENSIN, + UNT_GROUNDDRIFT_WIND, + UNT_GROUNDDRIFT_DARK, + UNT_GROUNDDRIFT_POISON, + UNT_GROUNDDRIFT_WATER, + UNT_GROUNDDRIFT_FIRE, +}; + +#endif diff --git a/src/map/status.h b/src/map/status.h index 5227edc68..c680ab89b 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -1,638 +1,638 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _STATUS_H_ -#define _STATUS_H_ - -#include "map.h" - -//Use this to refer the max refinery level [Skotlex] -#define MAX_REFINE 10 -#define MAX_REFINE_BONUS 5 - -extern unsigned long StatusChangeFlagTable[]; - -// Status changes listing. These code are for use by the server. -enum { - //First we enumerate common status ailments which are often used around. - SC_STONE = 0, - SC_FREEZE, - SC_STUN, - SC_SLEEP, - SC_POISON, - SC_CURSE, - SC_SILENCE, - SC_CONFUSION, - SC_BLIND, - SC_BLEEDING, - SC_DPOISON, //10 - - //Next up, we continue on 20, to leave enough room for additional "common" ailments in the future. - SC_PROVOKE = 20, - SC_ENDURE, - SC_TWOHANDQUICKEN, - SC_CONCENTRATE, - SC_HIDING, - SC_CLOAKING, - SC_ENCPOISON, - SC_POISONREACT, - SC_QUAGMIRE, - SC_ANGELUS, - SC_BLESSING, //30 - SC_SIGNUMCRUCIS, - SC_INCREASEAGI, - SC_DECREASEAGI, - SC_SLOWPOISON, - SC_IMPOSITIO , - SC_SUFFRAGIUM, - SC_ASPERSIO, - SC_BENEDICTIO, - SC_KYRIE, - SC_MAGNIFICAT, //40 - SC_GLORIA, - SC_AETERNA, - SC_ADRENALINE, - SC_WEAPONPERFECTION, - SC_OVERTHRUST, - SC_MAXIMIZEPOWER, - SC_TRICKDEAD, - SC_LOUD, - SC_ENERGYCOAT, - SC_BROKENARMOR, //50 - NOTE: These two aren't used anywhere, and they have an icon... - SC_BROKENWEAPON, - SC_HALLUCINATION, - SC_WEIGHT50, - SC_WEIGHT90, - SC_ASPDPOTION0, - SC_ASPDPOTION1, - SC_ASPDPOTION2, - SC_ASPDPOTION3, - SC_SPEEDUP0, - SC_SPEEDUP1, //60 - SC_ATKPOTION, - SC_MATKPOTION, - SC_WEDDING, - SC_SLOWDOWN, - SC_ANKLE, - SC_KEEPING, - SC_BARRIER, - SC_STRIPWEAPON, - SC_STRIPSHIELD, - SC_STRIPARMOR, //70 - SC_STRIPHELM, - SC_CP_WEAPON, - SC_CP_SHIELD, - SC_CP_ARMOR, - SC_CP_HELM, - SC_AUTOGUARD, - SC_REFLECTSHIELD, - SC_SPLASHER, - SC_PROVIDENCE, - SC_DEFENDER, //80 - SC_MAGICROD, - SC_SPELLBREAKER, - SC_AUTOSPELL, - SC_SIGHTTRASHER, - SC_AUTOBERSERK, - SC_SPEARQUICKEN, - SC_AUTOCOUNTER, - SC_SIGHT, - SC_SAFETYWALL, - SC_RUWACH, //90 - SC_EXTREMITYFIST, - SC_EXPLOSIONSPIRITS, - SC_COMBO, - SC_BLADESTOP_WAIT, - SC_BLADESTOP, - SC_FIREWEAPON, - SC_WATERWEAPON, - SC_WINDWEAPON, - SC_EARTHWEAPON, - SC_VOLCANO, //100, - SC_DELUGE, - SC_VIOLENTGALE, - SC_WATK_ELEMENT, - SC_ARMOR, - SC_ARMOR_ELEMENT, - SC_NOCHAT, - SC_BABY, - SC_AURABLADE, - SC_PARRYING, - SC_CONCENTRATION, //110 - SC_TENSIONRELAX, - SC_BERSERK, - SC_FURY, - SC_GOSPEL, - SC_ASSUMPTIO, - SC_BASILICA, - SC_GUILDAURA, - SC_MAGICPOWER, - SC_EDP, - SC_TRUESIGHT, //120 - SC_WINDWALK, - SC_MELTDOWN, - SC_CARTBOOST, - SC_CHASEWALK, - SC_REJECTSWORD, - SC_MARIONETTE, - SC_MARIONETTE2, - SC_UNUSED, //Unused (was SC_MOONLIT) - SC_JOINTBEAT, - SC_MINDBREAKER, //130 - SC_MEMORIZE, - SC_FOGWALL, - SC_SPIDERWEB, - SC_DEVOTION, - SC_SACRIFICE, - SC_STEELBODY, - SC_ORCISH, - SC_READYSTORM, - SC_READYDOWN, - SC_READYTURN, //140 - SC_READYCOUNTER, - SC_DODGE, - SC_RUN, - SC_SHADOWWEAPON, - SC_ADRENALINE2, - SC_GHOSTWEAPON, - SC_KAIZEL, - SC_KAAHI, - SC_KAUPE, - SC_ONEHAND, //150 - SC_PRESERVE, - SC_BATTLEORDERS, - SC_REGENERATION, - SC_DOUBLECAST, - SC_GRAVITATION, - SC_MAXOVERTHRUST, - SC_LONGING, - SC_HERMODE, - SC_SHRINK, - SC_SIGHTBLASTER, //160 - SC_WINKCHARM, - SC_CLOSECONFINE, - SC_CLOSECONFINE2, - SC_DANCING, - SC_ELEMENTALCHANGE, - SC_RICHMANKIM, - SC_ETERNALCHAOS, - SC_DRUMBATTLE, - SC_NIBELUNGEN, - SC_ROKISWEIL, //170 - SC_INTOABYSS, - SC_SIEGFRIED, - SC_WHISTLE, - SC_ASSNCROS, - SC_POEMBRAGI, - SC_APPLEIDUN, - SC_MODECHANGE, - SC_HUMMING, - SC_DONTFORGETME, - SC_FORTUNE, //180 - SC_SERVICE4U, - SC_STOP, //Prevents inflicted chars from walking. [Skotlex] - SC_SPURT, - SC_SPIRIT, - SC_COMA, //Not a real SC_, it makes a char's HP/SP hit 1. - SC_INTRAVISION, - SC_INCALLSTATUS, - SC_INCSTR, - SC_INCAGI, - SC_INCVIT, //190 - SC_INCINT, - SC_INCDEX, - SC_INCLUK, - SC_INCHIT, - SC_INCHITRATE, - SC_INCFLEE, - SC_INCFLEERATE, - SC_INCMHPRATE, - SC_INCMSPRATE, - SC_INCATKRATE, //200 - SC_INCMATKRATE, - SC_INCDEFRATE, - SC_STRFOOD, - SC_AGIFOOD, - SC_VITFOOD, - SC_INTFOOD, - SC_DEXFOOD, - SC_LUKFOOD, - SC_HITFOOD, - SC_FLEEFOOD, //210 - SC_BATKFOOD, - SC_WATKFOOD, - SC_MATKFOOD, - SC_SCRESIST, //Increases resistance to status changes. - SC_XMAS, // Xmas Suit [Valaris] - SC_WARM, //SG skills [Komurka] - SC_SUN_COMFORT, - SC_MOON_COMFORT, - SC_STAR_COMFORT, - SC_FUSION, //220 - SC_SKILLRATE_UP, - SC_SKE, - SC_KAITE, - SC_SWOO, // [marquis007] - SC_SKA, // [marquis007] - SC_TKREST, // [marquis007] - SC_MIRACLE, //SG 'hidden' skill [Komurka] - //Ninja/GS states - SC_MADNESSCANCEL, - SC_ADJUSTMENT, - SC_INCREASING, //230 - SC_GATLINGFEVER, - SC_TATAMIGAESHI, - SC_UTSUSEMI, - SC_BUNSINJYUTSU, - SC_KAENSIN, - SC_SUITON, - SC_NEN, - SC_KNOWLEDGE, - SC_SMA, - SC_FLING, //240 - SC_AVOID, - SC_CHANGE, - SC_BLOODLUST, - SC_FLEET, - SC_SPEED, //[orn] - SC_DEFENCE, //[orn] - SC_INCAGIRATE, - SC_INCDEXRATE, - SC_JAILED, - SC_MAX, //Automatically updated max, used in for's and at startup to check we are within bounds. [Skotlex] -}; -int SkillStatusChangeTable(int skill); -extern int StatusSkillChangeTable[SC_MAX]; - -//Numerates the Number for the status changes (client-dependent), imported from jA -enum { - SI_BLANK = -1, - SI_PROVOKE = 0, - SI_ENDURE = 1, - SI_TWOHANDQUICKEN = 2, - SI_CONCENTRATE = 3, - SI_HIDING = 4, - SI_CLOAKING = 5, - SI_ENCPOISON = 6, - SI_POISONREACT = 7, - SI_QUAGMIRE = 8, - SI_ANGELUS = 9, - SI_BLESSING = 10, - SI_SIGNUMCRUCIS = 11, - SI_INCREASEAGI = 12, - SI_DECREASEAGI = 13, - SI_SLOWPOISON = 14, - SI_IMPOSITIO = 15, - SI_SUFFRAGIUM = 16, - SI_ASPERSIO = 17, - SI_BENEDICTIO = 18, - SI_KYRIE = 19, - SI_MAGNIFICAT = 20, - SI_GLORIA = 21, - SI_AETERNA = 22, - SI_ADRENALINE = 23, - SI_WEAPONPERFECTION = 24, - SI_OVERTHRUST = 25, - SI_MAXIMIZEPOWER = 26, - SI_RIDING = 27, - SI_FALCON = 28, - SI_TRICKDEAD = 29, - SI_LOUD = 30, - SI_ENERGYCOAT = 31, - SI_BROKENARMOR = 32, - SI_BROKENWEAPON = 33, - SI_HALLUCINATION = 34, - SI_WEIGHT50 = 35, - SI_WEIGHT90 = 36, - SI_ASPDPOTION = 37, - //38: Again Aspd Potion - //39: Again Aspd Potion - //40: Again Aspd Potion - SI_SPEEDPOTION1 = 41, - SI_SPEEDPOTION2 = 42, - SI_STRIPWEAPON = 50, - SI_STRIPSHIELD = 51, - SI_STRIPARMOR = 52, - SI_STRIPHELM = 53, - SI_CP_WEAPON = 54, - SI_CP_SHIELD = 55, - SI_CP_ARMOR = 56, - SI_CP_HELM = 57, - SI_AUTOGUARD = 58, - SI_REFLECTSHIELD = 59, - SI_PROVIDENCE = 61, - SI_DEFENDER = 62, - SI_AUTOSPELL = 65, - SI_SPEARQUICKEN = 68, - SI_EXPLOSIONSPIRITS = 86, - SI_FURY = 87, - SI_FIREWEAPON = 90, - SI_WATERWEAPON = 91, - SI_WINDWEAPON = 92, - SI_EARTHWEAPON = 93, -// 102 = again gloria - from what I saw on screenshots, I wonder if it isn't gospel... [DracoRPG] - SI_AURABLADE = 103, - SI_PARRYING = 104, - SI_CONCENTRATION = 105, - SI_TENSIONRELAX = 106, - SI_BERSERK = 107, - SI_ASSUMPTIO = 110, - SI_LANDENDOW = 112, - SI_MAGICPOWER = 113, - SI_EDP = 114, - SI_TRUESIGHT = 115, - SI_WINDWALK = 116, - SI_MELTDOWN = 117, - SI_CARTBOOST = 118, - //119, blank - SI_REJECTSWORD = 120, - SI_MARIONETTE = 121, - SI_MARIONETTE2 = 122, - SI_MOONLIT = 123, - SI_BLEEDING = 124, - SI_JOINTBEAT = 125, - SI_DEVOTION = 130, - SI_STEELBODY = 132, - SI_RUN = 133, - SI_BUMP = 134, - SI_READYSTORM = 135, - SI_READYDOWN = 137, - SI_READYTURN = 139, - SI_READYCOUNTER = 141, - SI_DODGE = 143, - //SI_RUN = 144, //is not RUN. need info on what this is. - SI_SPURT = 145, - SI_SHADOWWEAPON = 146, - SI_ADRENALINE2 = 147, - SI_GHOSTWEAPON = 148, - SI_NIGHT = 149, - SI_SPIRIT = 149, - SI_DEVIL = 152, - SI_KAITE = 153, - SI_KAIZEL = 156, - SI_KAAHI = 157, - SI_KAUPE = 158, - SI_SMA = 159, -// 160 - SI_ONEHAND = 161, - SI_WARM = 165, -// 166 | The three show the exact same display: ultra red character (165, 166, 167) -// 167 | - SI_SUN_COMFORT = 169, - SI_MOON_COMFORT = 170, - SI_STAR_COMFORT = 171, - SI_PRESERVE = 181, - SI_INCSTR = 182, - SI_INTRAVISION = 184, //WTF?? creates the black shape of 4_m_02 NPC, with NPC talk cursor. Supposedly intravision shows this. - SI_DOUBLECAST = 186, - SI_MAXOVERTHRUST = 188, - SI_TAROT = 191, // the icon allows no doubt... but what is it really used for ?? [DracoRPG] - SI_SHRINK = 197, - SI_SIGHTBLASTER = 198, - SI_WINKCHARM = 199, - SI_CLOSECONFINE = 200, - SI_CLOSECONFINE2 = 201, - SI_MADNESSCANCEL = 203, //[blackhole89] - SI_GATLINGFEVER = 204, - SI_TKREST = 205, // 205 = Gloria again (but TK- Happy State looks like it) - SI_UTSUSEMI = 206, - SI_BUNSINJYUTSU = 207, - SI_NEN = 208, - SI_ADJUSTMENT = 209, - SI_ACCURACY = 210 -}; - -extern int current_equip_item_index; -extern int current_equip_card_id; - -extern int percentrefinery[5][MAX_REFINE+1]; //The last slot always has a 0% success chance [Skotlex] - -//Mode definitions to clear up code reading. [Skotlex] -#define MD_CANMOVE 0x001 -#define MD_LOOTER 0x002 -//MD_ANGRY mobs are also aggressive. -#define MD_AGGRESSIVE 0x804 -#define MD_ASSIST 0x008 -#define MD_CASTSENSOR 0x010 -#define MD_BOSS 0x020 -#define MD_PLANT 0x040 -#define MD_CANATTACK 0x080 -#define MD_DETECTOR 0x100 -#define MD_CHANGETARGET 0x200 -#define MD_CHANGECHASE 0x400 -#define MD_ANGRY 0x800 -#define MD_MASK 0xFFF - -//Status change option definitions (options are what makes status changes visible to chars -//who were not on your field of sight when it happened) -//opt1: Non stackable status changes. -enum { - OPT1_STONE = 1, //Petrified - OPT1_FREEZE, - OPT1_STUN, - OPT1_SLEEP, - //What is 5? - OPT1_STONEWAIT=6 //Petrifying -}; - -//opt2: Stackable status changes. -#define OPT2_POISON 0x001 -#define OPT2_CURSE 0x002 -#define OPT2_SILENCE 0x004 -#define OPT2_SIGNUMCRUCIS 0x008 -#define OPT2_BLIND 0x010 -//0x020 - nothing -//0x040 - nothing -#define OPT2_DPOISON 0x080 -//0x100 - -#define OPTION_SIGHT 0x00000001 -#define OPTION_HIDE 0x00000002 -#define OPTION_CLOAK 0x00000004 -#define OPTION_CART1 0x00000008 -#define OPTION_FALCON 0x00000010 -#define OPTION_RIDING 0x00000020 -#define OPTION_INVISIBLE 0x00000040 -#define OPTION_CART2 0x00000080 -#define OPTION_CART3 0x00000100 -#define OPTION_CART4 0x00000200 -#define OPTION_CART5 0x00000400 -#define OPTION_ORCISH 0x00000800 -#define OPTION_WEDDING 0x00001000 -#define OPTION_RUWACH 0x00002000 -#define OPTION_CHASEWALK 0x00004000 -//Note that clientside Flying and Xmas are 0x8000!! -#define OPTION_XMAS 0x00020000 -#define OPTION_FLYING 0x0008000 -//TODO: Get these Missing options... -#define OPTION_SIGHTTRASHER 0x00010000 - -#define OPTION_CART (OPTION_CART1|OPTION_CART2|OPTION_CART3|OPTION_CART4|OPTION_CART5) - -//Defines for the manner system [Skotlex] -#define MANNER_NOCHAT 0x01 -#define MANNER_NOSKILL 0x02 -#define MANNER_NOCOMMAND 0x04 -#define MANNER_NOITEM 0x08 -#define MANNER_NOROOM 0x10 - -//Define flags for the status_calc_bl function. [Skotlex] -#define SCB_NONE 0x00000000 -#define SCB_BASE 0x00000001 -#define SCB_MAXHP 0x00000002 -#define SCB_MAXSP 0x00000004 -#define SCB_STR 0x00000008 -#define SCB_AGI 0x00000010 -#define SCB_VIT 0x00000020 -#define SCB_INT 0x00000040 -#define SCB_DEX 0x00000080 -#define SCB_LUK 0x00000100 -#define SCB_BATK 0x00000200 -#define SCB_WATK 0x00000400 -#define SCB_MATK 0x00000800 -#define SCB_HIT 0x00001000 -#define SCB_FLEE 0x00002000 -#define SCB_DEF 0x00004000 -#define SCB_DEF2 0x00008000 -#define SCB_MDEF 0x00010000 -#define SCB_MDEF2 0x00020000 -#define SCB_SPEED 0x00040000 -#define SCB_ASPD 0x00080000 -#define SCB_DSPD 0x00100000 -#define SCB_CRI 0x00200000 -#define SCB_FLEE2 0x00400000 -#define SCB_ATK_ELE 0x00800000 -#define SCB_DEF_ELE 0x01000000 -#define SCB_MODE 0x02000000 -#define SCB_SIZE 0x04000000 -#define SCB_RACE 0x08000000 -#define SCB_RANGE 0x10000000 -#define SCB_REGEN 0x20000000 -//SCB_DYE means the sc should force cloth-dye change to 0 to avoid client crashes. -#define SCB_DYE 0x40000000 -#define SCB_PC 0x80000000 -#define SCB_ALL 0x3FFFFFFF - -//Define to determine who gets HP/SP consumed on doing skills/etc. [Skotlex] -#define BL_CONSUME (BL_PC|BL_HOM) -//Define to determine who has regen -#define BL_REGEN (BL_PC|BL_HOM) - -int status_damage(struct block_list *src,struct block_list *target,int hp,int sp, int walkdelay, int flag); -//Define for standard HP damage attacks. -#define status_fix_damage(src, target, hp, walkdelay) status_damage(src, target, hp, 0, walkdelay, 0) -//Define for standard HP/SP damage triggers. -#define status_zap(bl, hp, sp) status_damage(NULL, bl, hp, sp, 0, 1) -//Define for standard HP/SP skill-related cost triggers (mobs require no HP/SP to use skills) -#define status_charge(bl, hp, sp) (!((bl)->type&BL_CONSUME) || status_damage(NULL, bl, hp, sp, 0, 3)) -int status_percent_change(struct block_list *src,struct block_list *target,signed char hp_rate, signed char sp_rate, int flag); -//Easier handling of status_percent_change -#define status_percent_heal(bl, hp_rate, sp_rate) status_percent_change(NULL, bl, -(hp_rate), -(sp_rate), 1) -#define status_percent_damage(src, target, hp_rate, sp_rate) status_percent_change(src, target, hp_rate, sp_rate, 0) -//Instant kill with no drops/exp/etc -// -#define status_kill(bl) status_percent_damage(NULL, bl, 100, 0) -//Used to set the hp/sp of an object to an absolute value (can't kill) -int status_set_hp(struct block_list *bl, unsigned int hp, int flag); -int status_set_sp(struct block_list *bl, unsigned int sp, int flag); -int status_heal(struct block_list *bl,int hp,int sp, int flag); -int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per_sp); - -//Define for copying a status_data structure from b to a, without overwriting current Hp and Sp, nor messing the lhw pointer. -#define status_cpy(a, b) { memcpy(&((a)->max_hp), &((b)->max_hp), sizeof(struct status_data)-(sizeof((a)->hp)+sizeof((a)->sp)+sizeof((a)->lhw))); \ - if ((a)->lhw && (b)->lhw) { memcpy((a)->lhw, (b)->lhw, sizeof(struct weapon_atk)); }} - -struct regen_data *status_get_regen_data(struct block_list *bl); -struct status_data *status_get_status_data(struct block_list *bl); -struct status_data *status_get_base_status(struct block_list *bl); -const char * status_get_name(struct block_list *bl); -int status_get_class(struct block_list *bl); -int status_get_lv(struct block_list *bl); -#define status_get_range(bl) status_get_status_data(bl)->rhw.range -#define status_get_hp(bl) status_get_status_data(bl)->hp -#define status_get_max_hp(bl) status_get_status_data(bl)->max_hp -#define status_get_sp(bl) status_get_status_data(bl)->sp -#define status_get_max_sp(bl) status_get_status_data(bl)->max_sp -#define status_get_str(bl) status_get_status_data(bl)->str -#define status_get_agi(bl) status_get_status_data(bl)->agi -#define status_get_vit(bl) status_get_status_data(bl)->vit -#define status_get_int(bl) status_get_status_data(bl)->int_ -#define status_get_dex(bl) status_get_status_data(bl)->dex -#define status_get_luk(bl) status_get_status_data(bl)->luk -#define status_get_hit(bl) status_get_status_data(bl)->hit -#define status_get_flee(bl) status_get_status_data(bl)->flee -unsigned char status_get_def(struct block_list *bl); -#define status_get_mdef(bl) status_get_status_data(bl)->mdef -#define status_get_flee2(bl) status_get_status_data(bl)->flee2 -#define status_get_def2(bl) status_get_status_data(bl)->def2 -#define status_get_mdef2(bl) status_get_status_data(bl)->mdef2 -#define status_get_critical(bl) status_get_status_data(bl)->cri -#define status_get_batk(bl) status_get_status_data(bl)->batk -#define status_get_watk(bl) status_get_status_data(bl)->rhw.atk -#define status_get_watk2(bl) status_get_status_data(bl)->rhw.atk2 -#define status_get_matk_max(bl) status_get_status_data(bl)->matk_max -#define status_get_matk_min(bl) status_get_status_data(bl)->matk_min -unsigned short status_get_lwatk(struct block_list *bl); -unsigned short status_get_lwatk2(struct block_list *bl); -unsigned short status_get_speed(struct block_list *bl); -#define status_get_adelay(bl) status_get_status_data(bl)->adelay -#define status_get_amotion(bl) status_get_status_data(bl)->amotion -#define status_get_dmotion(bl) status_get_status_data(bl)->dmotion -#define status_get_element(bl) status_get_status_data(bl)->def_ele -#define status_get_element_level(bl) status_get_status_data(bl)->ele_lv -unsigned char status_calc_attack_element(struct block_list *bl, struct status_change *sc, int element); -#define status_get_attack_sc_element(bl, sc) status_calc_attack_element(bl, sc, 0) -#define status_get_attack_element(bl) status_get_status_data(bl)->rhw.ele -unsigned char status_get_attack_lelement(struct block_list *bl); -#define status_get_race(bl) status_get_status_data(bl)->race -#define status_get_size(bl) status_get_status_data(bl)->size -#define status_get_mode(bl) status_get_status_data(bl)->mode -int status_get_party_id(struct block_list *bl); -int status_get_guild_id(struct block_list *bl); -int status_get_mexp(struct block_list *bl); -int status_get_race2(struct block_list *bl); - -struct view_data *status_get_viewdata(struct block_list *bl); -void status_set_viewdata(struct block_list *bl, int class_); -void status_change_init(struct block_list *bl); -struct status_change *status_get_sc(struct block_list *bl); - -int status_isdead(struct block_list *bl); -int status_isimmune(struct block_list *bl); - -int status_get_sc_def(struct block_list *bl, int type); -#define status_get_sc_def_mdef(bl) (status_get_sc_def(bl, SP_MDEF1)) -#define status_get_sc_def_vit(bl) (status_get_sc_def(bl, SP_DEF2)) -#define status_get_sc_def_int(bl) (status_get_sc_def(bl, SP_MDEF2)) -#define status_get_sc_def_luk(bl) (status_get_sc_def(bl, SP_LUK)) - -//Short version, receives rate in 1->100 range, and does not uses a flag setting. -#define sc_start(bl, type, rate, val1, tick) status_change_start(bl,type,100*(rate),val1,0,0,0,tick,0) -#define sc_start4(bl, type, rate, val1, val2, val3, val4, tick) status_change_start(bl,type,100*(rate),val1,val2,val3,val4,tick,0) - -int status_change_start(struct block_list *bl,int type,int rate,int val1,int val2,int val3,int val4,int tick,int flag); -int status_change_end( struct block_list* bl , int type,int tid ); -int kaahi_heal_timer(int tid, unsigned int tick, int id, int data); -int status_change_timer(int tid, unsigned int tick, int id, int data); -int status_change_timer_sub(struct block_list *bl, va_list ap ); -int status_change_clear(struct block_list *bl,int type); -int status_change_clear_buffs(struct block_list *bl, int type); - -void status_calc_bl(struct block_list *bl, unsigned long flag); -int status_calc_pet(struct pet_data* pd, int first); // [Skotlex] -int status_calc_pc(struct map_session_data* sd,int first); -int status_calc_mob(struct mob_data* md, int first); //[Skotlex] -int status_calc_homunculus(struct homun_data *hd, int first); -void status_calc_misc(struct block_list *bl, struct status_data *status, int level); -void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen); -void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, struct status_change *sc); - -void status_freecast_switch(struct map_session_data *sd); -int status_getrefinebonus(int lv,int type); -int status_check_skilluse(struct block_list *src, struct block_list *target, int skill_num, int flag); // [Skotlex] -int status_check_visibility(struct block_list *src, struct block_list *target); //[Skotlex] - -int status_readdb(void); -int do_init_status(void); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _STATUS_H_ +#define _STATUS_H_ + +#include "map.h" + +//Use this to refer the max refinery level [Skotlex] +#define MAX_REFINE 10 +#define MAX_REFINE_BONUS 5 + +extern unsigned long StatusChangeFlagTable[]; + +// Status changes listing. These code are for use by the server. +enum { + //First we enumerate common status ailments which are often used around. + SC_STONE = 0, + SC_FREEZE, + SC_STUN, + SC_SLEEP, + SC_POISON, + SC_CURSE, + SC_SILENCE, + SC_CONFUSION, + SC_BLIND, + SC_BLEEDING, + SC_DPOISON, //10 + + //Next up, we continue on 20, to leave enough room for additional "common" ailments in the future. + SC_PROVOKE = 20, + SC_ENDURE, + SC_TWOHANDQUICKEN, + SC_CONCENTRATE, + SC_HIDING, + SC_CLOAKING, + SC_ENCPOISON, + SC_POISONREACT, + SC_QUAGMIRE, + SC_ANGELUS, + SC_BLESSING, //30 + SC_SIGNUMCRUCIS, + SC_INCREASEAGI, + SC_DECREASEAGI, + SC_SLOWPOISON, + SC_IMPOSITIO , + SC_SUFFRAGIUM, + SC_ASPERSIO, + SC_BENEDICTIO, + SC_KYRIE, + SC_MAGNIFICAT, //40 + SC_GLORIA, + SC_AETERNA, + SC_ADRENALINE, + SC_WEAPONPERFECTION, + SC_OVERTHRUST, + SC_MAXIMIZEPOWER, + SC_TRICKDEAD, + SC_LOUD, + SC_ENERGYCOAT, + SC_BROKENARMOR, //50 - NOTE: These two aren't used anywhere, and they have an icon... + SC_BROKENWEAPON, + SC_HALLUCINATION, + SC_WEIGHT50, + SC_WEIGHT90, + SC_ASPDPOTION0, + SC_ASPDPOTION1, + SC_ASPDPOTION2, + SC_ASPDPOTION3, + SC_SPEEDUP0, + SC_SPEEDUP1, //60 + SC_ATKPOTION, + SC_MATKPOTION, + SC_WEDDING, + SC_SLOWDOWN, + SC_ANKLE, + SC_KEEPING, + SC_BARRIER, + SC_STRIPWEAPON, + SC_STRIPSHIELD, + SC_STRIPARMOR, //70 + SC_STRIPHELM, + SC_CP_WEAPON, + SC_CP_SHIELD, + SC_CP_ARMOR, + SC_CP_HELM, + SC_AUTOGUARD, + SC_REFLECTSHIELD, + SC_SPLASHER, + SC_PROVIDENCE, + SC_DEFENDER, //80 + SC_MAGICROD, + SC_SPELLBREAKER, + SC_AUTOSPELL, + SC_SIGHTTRASHER, + SC_AUTOBERSERK, + SC_SPEARQUICKEN, + SC_AUTOCOUNTER, + SC_SIGHT, + SC_SAFETYWALL, + SC_RUWACH, //90 + SC_EXTREMITYFIST, + SC_EXPLOSIONSPIRITS, + SC_COMBO, + SC_BLADESTOP_WAIT, + SC_BLADESTOP, + SC_FIREWEAPON, + SC_WATERWEAPON, + SC_WINDWEAPON, + SC_EARTHWEAPON, + SC_VOLCANO, //100, + SC_DELUGE, + SC_VIOLENTGALE, + SC_WATK_ELEMENT, + SC_ARMOR, + SC_ARMOR_ELEMENT, + SC_NOCHAT, + SC_BABY, + SC_AURABLADE, + SC_PARRYING, + SC_CONCENTRATION, //110 + SC_TENSIONRELAX, + SC_BERSERK, + SC_FURY, + SC_GOSPEL, + SC_ASSUMPTIO, + SC_BASILICA, + SC_GUILDAURA, + SC_MAGICPOWER, + SC_EDP, + SC_TRUESIGHT, //120 + SC_WINDWALK, + SC_MELTDOWN, + SC_CARTBOOST, + SC_CHASEWALK, + SC_REJECTSWORD, + SC_MARIONETTE, + SC_MARIONETTE2, + SC_UNUSED, //Unused (was SC_MOONLIT) + SC_JOINTBEAT, + SC_MINDBREAKER, //130 + SC_MEMORIZE, + SC_FOGWALL, + SC_SPIDERWEB, + SC_DEVOTION, + SC_SACRIFICE, + SC_STEELBODY, + SC_ORCISH, + SC_READYSTORM, + SC_READYDOWN, + SC_READYTURN, //140 + SC_READYCOUNTER, + SC_DODGE, + SC_RUN, + SC_SHADOWWEAPON, + SC_ADRENALINE2, + SC_GHOSTWEAPON, + SC_KAIZEL, + SC_KAAHI, + SC_KAUPE, + SC_ONEHAND, //150 + SC_PRESERVE, + SC_BATTLEORDERS, + SC_REGENERATION, + SC_DOUBLECAST, + SC_GRAVITATION, + SC_MAXOVERTHRUST, + SC_LONGING, + SC_HERMODE, + SC_SHRINK, + SC_SIGHTBLASTER, //160 + SC_WINKCHARM, + SC_CLOSECONFINE, + SC_CLOSECONFINE2, + SC_DANCING, + SC_ELEMENTALCHANGE, + SC_RICHMANKIM, + SC_ETERNALCHAOS, + SC_DRUMBATTLE, + SC_NIBELUNGEN, + SC_ROKISWEIL, //170 + SC_INTOABYSS, + SC_SIEGFRIED, + SC_WHISTLE, + SC_ASSNCROS, + SC_POEMBRAGI, + SC_APPLEIDUN, + SC_MODECHANGE, + SC_HUMMING, + SC_DONTFORGETME, + SC_FORTUNE, //180 + SC_SERVICE4U, + SC_STOP, //Prevents inflicted chars from walking. [Skotlex] + SC_SPURT, + SC_SPIRIT, + SC_COMA, //Not a real SC_, it makes a char's HP/SP hit 1. + SC_INTRAVISION, + SC_INCALLSTATUS, + SC_INCSTR, + SC_INCAGI, + SC_INCVIT, //190 + SC_INCINT, + SC_INCDEX, + SC_INCLUK, + SC_INCHIT, + SC_INCHITRATE, + SC_INCFLEE, + SC_INCFLEERATE, + SC_INCMHPRATE, + SC_INCMSPRATE, + SC_INCATKRATE, //200 + SC_INCMATKRATE, + SC_INCDEFRATE, + SC_STRFOOD, + SC_AGIFOOD, + SC_VITFOOD, + SC_INTFOOD, + SC_DEXFOOD, + SC_LUKFOOD, + SC_HITFOOD, + SC_FLEEFOOD, //210 + SC_BATKFOOD, + SC_WATKFOOD, + SC_MATKFOOD, + SC_SCRESIST, //Increases resistance to status changes. + SC_XMAS, // Xmas Suit [Valaris] + SC_WARM, //SG skills [Komurka] + SC_SUN_COMFORT, + SC_MOON_COMFORT, + SC_STAR_COMFORT, + SC_FUSION, //220 + SC_SKILLRATE_UP, + SC_SKE, + SC_KAITE, + SC_SWOO, // [marquis007] + SC_SKA, // [marquis007] + SC_TKREST, // [marquis007] + SC_MIRACLE, //SG 'hidden' skill [Komurka] + //Ninja/GS states + SC_MADNESSCANCEL, + SC_ADJUSTMENT, + SC_INCREASING, //230 + SC_GATLINGFEVER, + SC_TATAMIGAESHI, + SC_UTSUSEMI, + SC_BUNSINJYUTSU, + SC_KAENSIN, + SC_SUITON, + SC_NEN, + SC_KNOWLEDGE, + SC_SMA, + SC_FLING, //240 + SC_AVOID, + SC_CHANGE, + SC_BLOODLUST, + SC_FLEET, + SC_SPEED, //[orn] + SC_DEFENCE, //[orn] + SC_INCAGIRATE, + SC_INCDEXRATE, + SC_JAILED, + SC_MAX, //Automatically updated max, used in for's and at startup to check we are within bounds. [Skotlex] +}; +int SkillStatusChangeTable(int skill); +extern int StatusSkillChangeTable[SC_MAX]; + +//Numerates the Number for the status changes (client-dependent), imported from jA +enum { + SI_BLANK = -1, + SI_PROVOKE = 0, + SI_ENDURE = 1, + SI_TWOHANDQUICKEN = 2, + SI_CONCENTRATE = 3, + SI_HIDING = 4, + SI_CLOAKING = 5, + SI_ENCPOISON = 6, + SI_POISONREACT = 7, + SI_QUAGMIRE = 8, + SI_ANGELUS = 9, + SI_BLESSING = 10, + SI_SIGNUMCRUCIS = 11, + SI_INCREASEAGI = 12, + SI_DECREASEAGI = 13, + SI_SLOWPOISON = 14, + SI_IMPOSITIO = 15, + SI_SUFFRAGIUM = 16, + SI_ASPERSIO = 17, + SI_BENEDICTIO = 18, + SI_KYRIE = 19, + SI_MAGNIFICAT = 20, + SI_GLORIA = 21, + SI_AETERNA = 22, + SI_ADRENALINE = 23, + SI_WEAPONPERFECTION = 24, + SI_OVERTHRUST = 25, + SI_MAXIMIZEPOWER = 26, + SI_RIDING = 27, + SI_FALCON = 28, + SI_TRICKDEAD = 29, + SI_LOUD = 30, + SI_ENERGYCOAT = 31, + SI_BROKENARMOR = 32, + SI_BROKENWEAPON = 33, + SI_HALLUCINATION = 34, + SI_WEIGHT50 = 35, + SI_WEIGHT90 = 36, + SI_ASPDPOTION = 37, + //38: Again Aspd Potion + //39: Again Aspd Potion + //40: Again Aspd Potion + SI_SPEEDPOTION1 = 41, + SI_SPEEDPOTION2 = 42, + SI_STRIPWEAPON = 50, + SI_STRIPSHIELD = 51, + SI_STRIPARMOR = 52, + SI_STRIPHELM = 53, + SI_CP_WEAPON = 54, + SI_CP_SHIELD = 55, + SI_CP_ARMOR = 56, + SI_CP_HELM = 57, + SI_AUTOGUARD = 58, + SI_REFLECTSHIELD = 59, + SI_PROVIDENCE = 61, + SI_DEFENDER = 62, + SI_AUTOSPELL = 65, + SI_SPEARQUICKEN = 68, + SI_EXPLOSIONSPIRITS = 86, + SI_FURY = 87, + SI_FIREWEAPON = 90, + SI_WATERWEAPON = 91, + SI_WINDWEAPON = 92, + SI_EARTHWEAPON = 93, +// 102 = again gloria - from what I saw on screenshots, I wonder if it isn't gospel... [DracoRPG] + SI_AURABLADE = 103, + SI_PARRYING = 104, + SI_CONCENTRATION = 105, + SI_TENSIONRELAX = 106, + SI_BERSERK = 107, + SI_ASSUMPTIO = 110, + SI_LANDENDOW = 112, + SI_MAGICPOWER = 113, + SI_EDP = 114, + SI_TRUESIGHT = 115, + SI_WINDWALK = 116, + SI_MELTDOWN = 117, + SI_CARTBOOST = 118, + //119, blank + SI_REJECTSWORD = 120, + SI_MARIONETTE = 121, + SI_MARIONETTE2 = 122, + SI_MOONLIT = 123, + SI_BLEEDING = 124, + SI_JOINTBEAT = 125, + SI_DEVOTION = 130, + SI_STEELBODY = 132, + SI_RUN = 133, + SI_BUMP = 134, + SI_READYSTORM = 135, + SI_READYDOWN = 137, + SI_READYTURN = 139, + SI_READYCOUNTER = 141, + SI_DODGE = 143, + //SI_RUN = 144, //is not RUN. need info on what this is. + SI_SPURT = 145, + SI_SHADOWWEAPON = 146, + SI_ADRENALINE2 = 147, + SI_GHOSTWEAPON = 148, + SI_NIGHT = 149, + SI_SPIRIT = 149, + SI_DEVIL = 152, + SI_KAITE = 153, + SI_KAIZEL = 156, + SI_KAAHI = 157, + SI_KAUPE = 158, + SI_SMA = 159, +// 160 + SI_ONEHAND = 161, + SI_WARM = 165, +// 166 | The three show the exact same display: ultra red character (165, 166, 167) +// 167 | + SI_SUN_COMFORT = 169, + SI_MOON_COMFORT = 170, + SI_STAR_COMFORT = 171, + SI_PRESERVE = 181, + SI_INCSTR = 182, + SI_INTRAVISION = 184, //WTF?? creates the black shape of 4_m_02 NPC, with NPC talk cursor. Supposedly intravision shows this. + SI_DOUBLECAST = 186, + SI_MAXOVERTHRUST = 188, + SI_TAROT = 191, // the icon allows no doubt... but what is it really used for ?? [DracoRPG] + SI_SHRINK = 197, + SI_SIGHTBLASTER = 198, + SI_WINKCHARM = 199, + SI_CLOSECONFINE = 200, + SI_CLOSECONFINE2 = 201, + SI_MADNESSCANCEL = 203, //[blackhole89] + SI_GATLINGFEVER = 204, + SI_TKREST = 205, // 205 = Gloria again (but TK- Happy State looks like it) + SI_UTSUSEMI = 206, + SI_BUNSINJYUTSU = 207, + SI_NEN = 208, + SI_ADJUSTMENT = 209, + SI_ACCURACY = 210 +}; + +extern int current_equip_item_index; +extern int current_equip_card_id; + +extern int percentrefinery[5][MAX_REFINE+1]; //The last slot always has a 0% success chance [Skotlex] + +//Mode definitions to clear up code reading. [Skotlex] +#define MD_CANMOVE 0x001 +#define MD_LOOTER 0x002 +//MD_ANGRY mobs are also aggressive. +#define MD_AGGRESSIVE 0x804 +#define MD_ASSIST 0x008 +#define MD_CASTSENSOR 0x010 +#define MD_BOSS 0x020 +#define MD_PLANT 0x040 +#define MD_CANATTACK 0x080 +#define MD_DETECTOR 0x100 +#define MD_CHANGETARGET 0x200 +#define MD_CHANGECHASE 0x400 +#define MD_ANGRY 0x800 +#define MD_MASK 0xFFF + +//Status change option definitions (options are what makes status changes visible to chars +//who were not on your field of sight when it happened) +//opt1: Non stackable status changes. +enum { + OPT1_STONE = 1, //Petrified + OPT1_FREEZE, + OPT1_STUN, + OPT1_SLEEP, + //What is 5? + OPT1_STONEWAIT=6 //Petrifying +}; + +//opt2: Stackable status changes. +#define OPT2_POISON 0x001 +#define OPT2_CURSE 0x002 +#define OPT2_SILENCE 0x004 +#define OPT2_SIGNUMCRUCIS 0x008 +#define OPT2_BLIND 0x010 +//0x020 - nothing +//0x040 - nothing +#define OPT2_DPOISON 0x080 +//0x100 + +#define OPTION_SIGHT 0x00000001 +#define OPTION_HIDE 0x00000002 +#define OPTION_CLOAK 0x00000004 +#define OPTION_CART1 0x00000008 +#define OPTION_FALCON 0x00000010 +#define OPTION_RIDING 0x00000020 +#define OPTION_INVISIBLE 0x00000040 +#define OPTION_CART2 0x00000080 +#define OPTION_CART3 0x00000100 +#define OPTION_CART4 0x00000200 +#define OPTION_CART5 0x00000400 +#define OPTION_ORCISH 0x00000800 +#define OPTION_WEDDING 0x00001000 +#define OPTION_RUWACH 0x00002000 +#define OPTION_CHASEWALK 0x00004000 +//Note that clientside Flying and Xmas are 0x8000!! +#define OPTION_XMAS 0x00020000 +#define OPTION_FLYING 0x0008000 +//TODO: Get these Missing options... +#define OPTION_SIGHTTRASHER 0x00010000 + +#define OPTION_CART (OPTION_CART1|OPTION_CART2|OPTION_CART3|OPTION_CART4|OPTION_CART5) + +//Defines for the manner system [Skotlex] +#define MANNER_NOCHAT 0x01 +#define MANNER_NOSKILL 0x02 +#define MANNER_NOCOMMAND 0x04 +#define MANNER_NOITEM 0x08 +#define MANNER_NOROOM 0x10 + +//Define flags for the status_calc_bl function. [Skotlex] +#define SCB_NONE 0x00000000 +#define SCB_BASE 0x00000001 +#define SCB_MAXHP 0x00000002 +#define SCB_MAXSP 0x00000004 +#define SCB_STR 0x00000008 +#define SCB_AGI 0x00000010 +#define SCB_VIT 0x00000020 +#define SCB_INT 0x00000040 +#define SCB_DEX 0x00000080 +#define SCB_LUK 0x00000100 +#define SCB_BATK 0x00000200 +#define SCB_WATK 0x00000400 +#define SCB_MATK 0x00000800 +#define SCB_HIT 0x00001000 +#define SCB_FLEE 0x00002000 +#define SCB_DEF 0x00004000 +#define SCB_DEF2 0x00008000 +#define SCB_MDEF 0x00010000 +#define SCB_MDEF2 0x00020000 +#define SCB_SPEED 0x00040000 +#define SCB_ASPD 0x00080000 +#define SCB_DSPD 0x00100000 +#define SCB_CRI 0x00200000 +#define SCB_FLEE2 0x00400000 +#define SCB_ATK_ELE 0x00800000 +#define SCB_DEF_ELE 0x01000000 +#define SCB_MODE 0x02000000 +#define SCB_SIZE 0x04000000 +#define SCB_RACE 0x08000000 +#define SCB_RANGE 0x10000000 +#define SCB_REGEN 0x20000000 +//SCB_DYE means the sc should force cloth-dye change to 0 to avoid client crashes. +#define SCB_DYE 0x40000000 +#define SCB_PC 0x80000000 +#define SCB_ALL 0x3FFFFFFF + +//Define to determine who gets HP/SP consumed on doing skills/etc. [Skotlex] +#define BL_CONSUME (BL_PC|BL_HOM) +//Define to determine who has regen +#define BL_REGEN (BL_PC|BL_HOM) + +int status_damage(struct block_list *src,struct block_list *target,int hp,int sp, int walkdelay, int flag); +//Define for standard HP damage attacks. +#define status_fix_damage(src, target, hp, walkdelay) status_damage(src, target, hp, 0, walkdelay, 0) +//Define for standard HP/SP damage triggers. +#define status_zap(bl, hp, sp) status_damage(NULL, bl, hp, sp, 0, 1) +//Define for standard HP/SP skill-related cost triggers (mobs require no HP/SP to use skills) +#define status_charge(bl, hp, sp) (!((bl)->type&BL_CONSUME) || status_damage(NULL, bl, hp, sp, 0, 3)) +int status_percent_change(struct block_list *src,struct block_list *target,signed char hp_rate, signed char sp_rate, int flag); +//Easier handling of status_percent_change +#define status_percent_heal(bl, hp_rate, sp_rate) status_percent_change(NULL, bl, -(hp_rate), -(sp_rate), 1) +#define status_percent_damage(src, target, hp_rate, sp_rate) status_percent_change(src, target, hp_rate, sp_rate, 0) +//Instant kill with no drops/exp/etc +// +#define status_kill(bl) status_percent_damage(NULL, bl, 100, 0) +//Used to set the hp/sp of an object to an absolute value (can't kill) +int status_set_hp(struct block_list *bl, unsigned int hp, int flag); +int status_set_sp(struct block_list *bl, unsigned int sp, int flag); +int status_heal(struct block_list *bl,int hp,int sp, int flag); +int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per_sp); + +//Define for copying a status_data structure from b to a, without overwriting current Hp and Sp, nor messing the lhw pointer. +#define status_cpy(a, b) { memcpy(&((a)->max_hp), &((b)->max_hp), sizeof(struct status_data)-(sizeof((a)->hp)+sizeof((a)->sp)+sizeof((a)->lhw))); \ + if ((a)->lhw && (b)->lhw) { memcpy((a)->lhw, (b)->lhw, sizeof(struct weapon_atk)); }} + +struct regen_data *status_get_regen_data(struct block_list *bl); +struct status_data *status_get_status_data(struct block_list *bl); +struct status_data *status_get_base_status(struct block_list *bl); +const char * status_get_name(struct block_list *bl); +int status_get_class(struct block_list *bl); +int status_get_lv(struct block_list *bl); +#define status_get_range(bl) status_get_status_data(bl)->rhw.range +#define status_get_hp(bl) status_get_status_data(bl)->hp +#define status_get_max_hp(bl) status_get_status_data(bl)->max_hp +#define status_get_sp(bl) status_get_status_data(bl)->sp +#define status_get_max_sp(bl) status_get_status_data(bl)->max_sp +#define status_get_str(bl) status_get_status_data(bl)->str +#define status_get_agi(bl) status_get_status_data(bl)->agi +#define status_get_vit(bl) status_get_status_data(bl)->vit +#define status_get_int(bl) status_get_status_data(bl)->int_ +#define status_get_dex(bl) status_get_status_data(bl)->dex +#define status_get_luk(bl) status_get_status_data(bl)->luk +#define status_get_hit(bl) status_get_status_data(bl)->hit +#define status_get_flee(bl) status_get_status_data(bl)->flee +unsigned char status_get_def(struct block_list *bl); +#define status_get_mdef(bl) status_get_status_data(bl)->mdef +#define status_get_flee2(bl) status_get_status_data(bl)->flee2 +#define status_get_def2(bl) status_get_status_data(bl)->def2 +#define status_get_mdef2(bl) status_get_status_data(bl)->mdef2 +#define status_get_critical(bl) status_get_status_data(bl)->cri +#define status_get_batk(bl) status_get_status_data(bl)->batk +#define status_get_watk(bl) status_get_status_data(bl)->rhw.atk +#define status_get_watk2(bl) status_get_status_data(bl)->rhw.atk2 +#define status_get_matk_max(bl) status_get_status_data(bl)->matk_max +#define status_get_matk_min(bl) status_get_status_data(bl)->matk_min +unsigned short status_get_lwatk(struct block_list *bl); +unsigned short status_get_lwatk2(struct block_list *bl); +unsigned short status_get_speed(struct block_list *bl); +#define status_get_adelay(bl) status_get_status_data(bl)->adelay +#define status_get_amotion(bl) status_get_status_data(bl)->amotion +#define status_get_dmotion(bl) status_get_status_data(bl)->dmotion +#define status_get_element(bl) status_get_status_data(bl)->def_ele +#define status_get_element_level(bl) status_get_status_data(bl)->ele_lv +unsigned char status_calc_attack_element(struct block_list *bl, struct status_change *sc, int element); +#define status_get_attack_sc_element(bl, sc) status_calc_attack_element(bl, sc, 0) +#define status_get_attack_element(bl) status_get_status_data(bl)->rhw.ele +unsigned char status_get_attack_lelement(struct block_list *bl); +#define status_get_race(bl) status_get_status_data(bl)->race +#define status_get_size(bl) status_get_status_data(bl)->size +#define status_get_mode(bl) status_get_status_data(bl)->mode +int status_get_party_id(struct block_list *bl); +int status_get_guild_id(struct block_list *bl); +int status_get_mexp(struct block_list *bl); +int status_get_race2(struct block_list *bl); + +struct view_data *status_get_viewdata(struct block_list *bl); +void status_set_viewdata(struct block_list *bl, int class_); +void status_change_init(struct block_list *bl); +struct status_change *status_get_sc(struct block_list *bl); + +int status_isdead(struct block_list *bl); +int status_isimmune(struct block_list *bl); + +int status_get_sc_def(struct block_list *bl, int type); +#define status_get_sc_def_mdef(bl) (status_get_sc_def(bl, SP_MDEF1)) +#define status_get_sc_def_vit(bl) (status_get_sc_def(bl, SP_DEF2)) +#define status_get_sc_def_int(bl) (status_get_sc_def(bl, SP_MDEF2)) +#define status_get_sc_def_luk(bl) (status_get_sc_def(bl, SP_LUK)) + +//Short version, receives rate in 1->100 range, and does not uses a flag setting. +#define sc_start(bl, type, rate, val1, tick) status_change_start(bl,type,100*(rate),val1,0,0,0,tick,0) +#define sc_start4(bl, type, rate, val1, val2, val3, val4, tick) status_change_start(bl,type,100*(rate),val1,val2,val3,val4,tick,0) + +int status_change_start(struct block_list *bl,int type,int rate,int val1,int val2,int val3,int val4,int tick,int flag); +int status_change_end( struct block_list* bl , int type,int tid ); +int kaahi_heal_timer(int tid, unsigned int tick, int id, int data); +int status_change_timer(int tid, unsigned int tick, int id, int data); +int status_change_timer_sub(struct block_list *bl, va_list ap ); +int status_change_clear(struct block_list *bl,int type); +int status_change_clear_buffs(struct block_list *bl, int type); + +void status_calc_bl(struct block_list *bl, unsigned long flag); +int status_calc_pet(struct pet_data* pd, int first); // [Skotlex] +int status_calc_pc(struct map_session_data* sd,int first); +int status_calc_mob(struct mob_data* md, int first); //[Skotlex] +int status_calc_homunculus(struct homun_data *hd, int first); +void status_calc_misc(struct block_list *bl, struct status_data *status, int level); +void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen); +void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, struct status_change *sc); + +void status_freecast_switch(struct map_session_data *sd); +int status_getrefinebonus(int lv,int type); +int status_check_skilluse(struct block_list *src, struct block_list *target, int skill_num, int flag); // [Skotlex] +int status_check_visibility(struct block_list *src, struct block_list *target); //[Skotlex] + +int status_readdb(void); +int do_init_status(void); + +#endif diff --git a/src/map/storage.c b/src/map/storage.c index 6f504eab3..ba44f0f0d 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -1,761 +1,761 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include - -#include "../common/nullpo.h" -#include "../common/malloc.h" -#include "../common/showmsg.h" - -#include "storage.h" -#include "chrif.h" -#include "itemdb.h" -#include "clif.h" -#include "intif.h" -#include "pc.h" -#include "guild.h" -#include "battle.h" -#include "atcommand.h" - -static struct dbt *storage_db; -static struct dbt *guild_storage_db; - -/*========================================== - * 倉庫内アイテムソート - *------------------------------------------ - */ -int storage_comp_item(const void *_i1, const void *_i2) -{ - struct item *i1 = (struct item *)_i1; - struct item *i2 = (struct item *)_i2; - - if (i1->nameid == i2->nameid) - return 0; - else if (!(i1->nameid) || !(i1->amount)) - return 1; - else if (!(i2->nameid) || !(i2->amount)) - return -1; - return i1->nameid - i2->nameid; -} - -void sortage_sortitem (struct storage *stor) -{ - nullpo_retv(stor); - qsort(stor->storage_, MAX_STORAGE, sizeof(struct item), storage_comp_item); -} - -void sortage_gsortitem (struct guild_storage* gstor) -{ - nullpo_retv(gstor); - qsort(gstor->storage_, MAX_GUILD_STORAGE, sizeof(struct item), storage_comp_item); -} - -/*========================================== - * 初期化とか - *------------------------------------------ - */ -int do_init_storage(void) // map.c::do_init()から呼ばれる -{ - storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - guild_storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - return 1; -} -void do_final_storage(void) // by [MC Cameri] -{ - storage_db->destroy(storage_db,NULL); - guild_storage_db->destroy(guild_storage_db,NULL); -} - - -static int storage_reconnect_sub(DBKey key,void *data,va_list ap) -{ //Parses storage and saves 'dirty' ones upon reconnect. [Skotlex] - int type = va_arg(ap, int); - if (type) - { //Guild Storage - struct guild_storage* stor = (struct guild_storage*) data; - if (stor->dirty && stor->storage_status == 0) //Save closed storages. - storage_guild_storagesave(0, stor->guild_id,0); - } - else - { //Account Storage - struct storage* stor = (struct storage*) data; - if (stor->dirty && stor->storage_status == 0) //Save closed storages. - storage_storage_save(stor->account_id, stor->dirty==2?1:0); - } - return 0; -} - -//Function to be invoked upon server reconnection to char. To save all 'dirty' storages [Skotlex -void do_reconnect_storage(void) -{ - storage_db->foreach(storage_db, storage_reconnect_sub, 0); - guild_storage_db->foreach(guild_storage_db, storage_reconnect_sub, 1); -} - -static void* create_storage(DBKey key, va_list args) { - struct storage *stor; - stor = (struct storage *) aCallocA (sizeof(struct storage), 1); - stor->account_id = key.i; - return stor; -} -struct storage *account2storage(int account_id) -{ - return idb_ensure(storage_db,account_id,create_storage); -} - -// Just to ask storage, without creation -struct storage *account2storage2(int account_id) -{ - return idb_get(storage_db, account_id); -} - -int storage_delete(int account_id) -{ - idb_remove(storage_db,account_id); - return 0; -} - -/*========================================== - * Opens a storage. Returns: - * 0 - success - * 1 - fail - * 2 - Storage requested from char-server (will open automatically later) - *------------------------------------------ - */ -int storage_storageopen(struct map_session_data *sd) -{ - struct storage *stor; - nullpo_retr(0, sd); - - if(sd->state.finalsave) //Refuse to open storage when you had your last save done. - return 1; - - if(sd->state.storage_flag) - return 1; //Already open? - - if(pc_can_give_items(pc_isGM(sd))) - { //check is this GM level is allowed to put items to storage - clif_displaymessage(sd->fd, msg_txt(246)); - return 1; - } - - if((stor = idb_get(storage_db,sd->status.account_id)) == NULL) - { //Request storage. - intif_request_storage(sd->status.account_id); - return 2; - } - - if (stor->storage_status) - return 1; //Already open/player already has it open... - - stor->storage_status = 1; - sd->state.storage_flag = 1; - clif_storagelist(sd,stor); - clif_updatestorageamount(sd,stor); - return 0; -} - -/*========================================== - * Internal add-item function. - *------------------------------------------ - */ -static int storage_additem(struct map_session_data *sd,struct storage *stor,struct item *item_data,int amount) -{ - struct item_data *data; - int i; - - if (sd->state.finalsave) - return 1; - - if(item_data->nameid <= 0 || amount <= 0) - return 1; - - data = itemdb_search(item_data->nameid); - - if (!itemdb_canstore(item_data, pc_isGM(sd))) - { //Check if item is storable. [Skotlex] - clif_displaymessage (sd->fd, msg_txt(264)); - return 1; - } - - if(itemdb_isstackable2(data)){ //Stackable - for(i=0;istorage_[i], item_data)) { - if(amount > MAX_AMOUNT - stor->storage_[i].amount) - return 1; - stor->storage_[i].amount+=amount; - clif_storageitemadded(sd,stor,i,amount); - stor->dirty = 1; - return 0; - } - } - } - //Add item - for(i=0;istorage_[i].nameid;i++); - - if(i>=MAX_STORAGE) - return 1; - - memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0])); - stor->storage_[i].amount=amount; - stor->storage_amount++; - clif_storageitemadded(sd,stor,i,amount); - clif_updatestorageamount(sd,stor); - stor->dirty = 1; - return 0; -} -/*========================================== - * Internal del-item function - *------------------------------------------ - */ -static int storage_delitem(struct map_session_data *sd,struct storage *stor,int n,int amount) -{ - - if(stor->storage_[n].nameid==0 || stor->storage_[n].amountstorage_[n].amount-=amount; - if(stor->storage_[n].amount==0){ - malloc_set(&stor->storage_[n],0,sizeof(stor->storage_[0])); - stor->storage_amount--; - clif_updatestorageamount(sd,stor); - } - clif_storageitemremoved(sd,n,amount); - - stor->dirty = 1; - return 0; -} -/*========================================== - * Add an item to the storage from the inventory. - *------------------------------------------ - */ -int storage_storageadd(struct map_session_data *sd,int index,int amount) -{ - struct storage *stor; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=account2storage2(sd->status.account_id)); - - if((stor->storage_amount > MAX_STORAGE) || !stor->storage_status) - return 0; // storage full / storage closed - - if(index<0 || index>=MAX_INVENTORY) - return 0; - - if(sd->status.inventory[index].nameid <= 0) - return 0; //No item on that spot - - if(amount < 1 || amount > sd->status.inventory[index].amount) - return 0; - -// log_tostorage(sd, index, 0); - if(storage_additem(sd,stor,&sd->status.inventory[index],amount)==0) - // remove item from inventory - pc_delitem(sd,index,amount,0); - - return 1; -} - -/*========================================== - * Retrieve an item from the storage. - *------------------------------------------ - */ -int storage_storageget(struct map_session_data *sd,int index,int amount) -{ - struct storage *stor; - int flag; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=account2storage2(sd->status.account_id)); - - - if(index<0 || index>=MAX_STORAGE) - return 0; - - if(stor->storage_[index].nameid <= 0) - return 0; //Nothing there - - if(amount < 1 || amount > stor->storage_[index].amount) - return 0; - - if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0) - storage_delitem(sd,stor,index,amount); - else - clif_additem(sd,0,0,flag); -// log_fromstorage(sd, index, 0); - return 1; -} -/*========================================== - * Move an item from cart to storage. - *------------------------------------------ - */ -int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount) -{ - struct storage *stor; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=account2storage2(sd->status.account_id)); - - if(stor->storage_amount > MAX_STORAGE || !stor->storage_status) - return 0; // storage full / storage closed - - if(index< 0 || index>=MAX_CART) - return 0; - - if(sd->status.cart[index].nameid <= 0) - return 0; //No item there. - - if(amount < 1 || amount > sd->status.cart[index].amount) - return 0; - - if(storage_additem(sd,stor,&sd->status.cart[index],amount)==0) - pc_cart_delitem(sd,index,amount,0); - - return 1; -} - -/*========================================== - * Get from Storage to the Cart - *------------------------------------------ - */ -int storage_storagegettocart(struct map_session_data *sd,int index,int amount) -{ - struct storage *stor; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=account2storage2(sd->status.account_id)); - - if(!stor->storage_status) - return 0; - - if(index< 0 || index>=MAX_STORAGE) - return 0; - - if(stor->storage_[index].nameid <= 0) - return 0; //Nothing there. - - if(amount < 1 || amount > stor->storage_[index].amount) - return 0; - - if(pc_cart_additem(sd,&stor->storage_[index],amount)==0) - storage_delitem(sd,stor,index,amount); - - return 1; -} - - -/*========================================== - * Modified By Valaris to save upon closing [massdriller] - *------------------------------------------ - */ -int storage_storageclose(struct map_session_data *sd) -{ - struct storage *stor; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=account2storage2(sd->status.account_id)); - - clif_storageclose(sd); - if (stor->storage_status) - { - if (save_settings&4) - chrif_save(sd,0); //Invokes the storage saving as well. - else - storage_storage_save(sd->status.account_id, 0); - } - stor->storage_status=0; - sd->state.storage_flag=0; - return 0; -} - -/*========================================== - * When quitting the game. - *------------------------------------------ - */ -int storage_storage_quit(struct map_session_data *sd, int flag) -{ - struct storage *stor; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=account2storage2(sd->status.account_id)); - - if (stor->storage_status) - { - if (save_settings&4) - chrif_save(sd, flag); //Invokes the storage saving as well. - else - storage_storage_save(sd->status.account_id, flag); - } - stor->storage_status = 0; - sd->state.storage_flag = 0; - return 0; -} - -void storage_storage_dirty(struct map_session_data *sd) -{ - struct storage *stor; - - stor=account2storage2(sd->status.account_id); - - if(stor) - stor->dirty = 1; -} - -int storage_storage_save(int account_id, int final) -{ - struct storage *stor; - - stor=account2storage2(account_id); - if(!stor) return 0; - - if(stor->dirty) - { - if (final) { - stor->dirty = 2; - stor->storage_status = 0; //To prevent further manipulation of it. - } - intif_send_storage(stor); - return 1; - } - if (final) - { //Clear storage from memory. Nothing to save. - storage_delete(account_id); - return 1; - } - - return 0; -} - -//Ack from Char-server indicating the storage was saved. [Skotlex] -int storage_storage_saved(int account_id) -{ - struct storage *stor; - - if((stor=account2storage2(account_id)) == NULL) - return 0; - - if (stor->dirty == 2) - { //Final save of storage. Remove from memory. - storage_delete(account_id); - return 1; - } - - if (stor->dirty && stor->storage_status == 0) - { //Only mark it clean if it's not in use. [Skotlex] - stor->dirty = 0; - sortage_sortitem(stor); - return 1; - } - return 0; -} - -static void* create_guildstorage(DBKey key, va_list args) { - struct guild_storage *gs = NULL; - gs = (struct guild_storage *) aCallocA(sizeof(struct guild_storage), 1); - gs->guild_id=key.i; - return gs; -} -struct guild_storage *guild2storage(int guild_id) -{ - struct guild_storage *gs = NULL; - if(guild_search(guild_id) != NULL) - gs=(struct guild_storage *) idb_ensure(guild_storage_db,guild_id,create_guildstorage); - return gs; -} - -struct guild_storage *guild2storage2(int guild_id) -{ //For just locating a storage without creating one. [Skotlex] - return idb_get(guild_storage_db,guild_id); -} - -int guild_storage_delete(int guild_id) -{ - idb_remove(guild_storage_db,guild_id); - return 0; -} - -int storage_guild_storageopen(struct map_session_data *sd) -{ - struct guild_storage *gstor; - - nullpo_retr(0, sd); - - if(sd->status.guild_id <= 0) - return 2; - - if(sd->state.finalsave) //Refuse to open storage when you had your last save done. - return 1; - - if(sd->state.storage_flag) - return 1; //Can't open both storages at a time. - - if( pc_can_give_items(pc_isGM(sd)) ) { //check is this GM level can open guild storage and store items [Lupus] - clif_displaymessage(sd->fd, msg_txt(246)); - return 1; - } - - if((gstor = guild2storage2(sd->status.guild_id)) == NULL) { - intif_request_guild_storage(sd->status.account_id,sd->status.guild_id); - return 0; - } - if(gstor->storage_status) - return 1; - - gstor->storage_status = 1; - sd->state.storage_flag = 2; - clif_guildstoragelist(sd,gstor); - clif_updateguildstorageamount(sd,gstor); - return 0; -} - -int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount) -{ - struct item_data *data; - int i; - - nullpo_retr(1, sd); - nullpo_retr(1, stor); - nullpo_retr(1, item_data); - nullpo_retr(1, data = itemdb_search(item_data->nameid)); - - if(item_data->nameid <= 0 || amount <= 0) - return 1; - - if (!itemdb_canguildstore(item_data, pc_isGM(sd))) - { //Check if item is storable. [Skotlex] - clif_displaymessage (sd->fd, msg_txt(264)); - return 1; - } - - if(itemdb_isstackable2(data)){ //Stackable - for(i=0;istorage_[i], item_data)) { - if(stor->storage_[i].amount+amount > MAX_AMOUNT) - return 1; - stor->storage_[i].amount+=amount; - clif_guildstorageitemadded(sd,stor,i,amount); - stor->dirty = 1; - return 0; - } - } - } - //Add item - for(i=0;istorage_[i].nameid;i++); - - if(i>=MAX_GUILD_STORAGE) - return 1; - - memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0])); - stor->storage_[i].amount=amount; - stor->storage_amount++; - clif_guildstorageitemadded(sd,stor,i,amount); - clif_updateguildstorageamount(sd,stor); - stor->dirty = 1; - return 0; -} - -int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount) -{ - nullpo_retr(1, sd); - nullpo_retr(1, stor); - - if(stor->storage_[n].nameid==0 || stor->storage_[n].amountstorage_[n].amount-=amount; - if(stor->storage_[n].amount==0){ - malloc_set(&stor->storage_[n],0,sizeof(stor->storage_[0])); - stor->storage_amount--; - clif_updateguildstorageamount(sd,stor); - } - clif_storageitemremoved(sd,n,amount); - stor->dirty = 1; - return 0; -} - -int storage_guild_storageadd(struct map_session_data *sd,int index,int amount) -{ - struct guild_storage *stor; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); - - if (!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE) - return 0; - - if(index<0 || index>=MAX_INVENTORY) - return 0; - - if(sd->status.inventory[index].nameid <= 0) - return 0; - - if(amount < 1 || amount > sd->status.inventory[index].amount) - return 0; - -// log_tostorage(sd, index, 1); - if(guild_storage_additem(sd,stor,&sd->status.inventory[index],amount)==0) - pc_delitem(sd,index,amount,0); - - return 1; -} - -int storage_guild_storageget(struct map_session_data *sd,int index,int amount) -{ - struct guild_storage *stor; - int flag; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); - - if(!stor->storage_status) - return 0; - - if(index<0 || index>=MAX_GUILD_STORAGE) - return 0; - - if(stor->storage_[index].nameid <= 0) - return 0; - - if(amount < 1 || amount > stor->storage_[index].amount) - return 0; - - if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0) - guild_storage_delitem(sd,stor,index,amount); - else - clif_additem(sd,0,0,flag); -// log_fromstorage(sd, index, 1); - - return 0; -} - -int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount) -{ - struct guild_storage *stor; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); - - if(!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE) - return 0; - - if(index<0 || index>=MAX_CART) - return 0; - - if(sd->status.cart[index].nameid <= 0) - return 0; - - if(amount < 1 || amount > sd->status.cart[index].amount) - return 0; - - if(guild_storage_additem(sd,stor,&sd->status.cart[index],amount)==0) - pc_cart_delitem(sd,index,amount,0); - - return 1; -} - -int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount) -{ - struct guild_storage *stor; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); - - if(!stor->storage_status) - return 0; - - if(index<0 || index>=MAX_GUILD_STORAGE) - return 0; - - if(stor->storage_[index].nameid<=0) - return 0; - - if(amount < 1 || amount > stor->storage_[index].amount) - return 0; - - if(pc_cart_additem(sd,&stor->storage_[index],amount)==0) - guild_storage_delitem(sd,stor,index,amount); - - return 1; -} - -int storage_guild_storagesave(int account_id, int guild_id, int flag) -{ - struct guild_storage *stor = guild2storage2(guild_id); - - if(stor) - { - if (flag) //Char quitting, close it. - stor->storage_status = 0; - if (stor->dirty) - intif_send_guild_storage(account_id,stor); - return 1; - } - return 0; -} - -int storage_guild_storagesaved(int guild_id) -{ - struct guild_storage *stor; - - if((stor=guild2storage2(guild_id)) != NULL) { - if (stor->dirty && stor->storage_status == 0) - { //Storage has been correctly saved. - stor->dirty = 0; - sortage_gsortitem(stor); - } - return 1; - } - return 0; -} - -int storage_guild_storageclose(struct map_session_data *sd) -{ - struct guild_storage *stor; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); - - clif_storageclose(sd); - if (stor->storage_status) - { - if (save_settings&4) - chrif_save(sd, 0); //This one also saves the storage. [Skotlex] - else - storage_guild_storagesave(sd->status.account_id, sd->status.guild_id,0); - stor->storage_status=0; - } - sd->state.storage_flag = 0; - - return 0; -} - -int storage_guild_storage_quit(struct map_session_data *sd,int flag) -{ - struct guild_storage *stor; - - nullpo_retr(0, sd); - nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); - - if(flag) - { //Only during a guild break flag is 1 (don't save storage) - sd->state.storage_flag = 0; - stor->storage_status = 0; - clif_storageclose(sd); - if (save_settings&4) - chrif_save(sd,0); - return 0; - } - - if(stor->storage_status) { - if (save_settings&4) - chrif_save(sd,0); - else - storage_guild_storagesave(sd->status.account_id,sd->status.guild_id,1); - } - sd->state.storage_flag = 0; - stor->storage_status = 0; - - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include + +#include "../common/nullpo.h" +#include "../common/malloc.h" +#include "../common/showmsg.h" + +#include "storage.h" +#include "chrif.h" +#include "itemdb.h" +#include "clif.h" +#include "intif.h" +#include "pc.h" +#include "guild.h" +#include "battle.h" +#include "atcommand.h" + +static struct dbt *storage_db; +static struct dbt *guild_storage_db; + +/*========================================== + * 倉庫内アイテムソート + *------------------------------------------ + */ +int storage_comp_item(const void *_i1, const void *_i2) +{ + struct item *i1 = (struct item *)_i1; + struct item *i2 = (struct item *)_i2; + + if (i1->nameid == i2->nameid) + return 0; + else if (!(i1->nameid) || !(i1->amount)) + return 1; + else if (!(i2->nameid) || !(i2->amount)) + return -1; + return i1->nameid - i2->nameid; +} + +void sortage_sortitem (struct storage *stor) +{ + nullpo_retv(stor); + qsort(stor->storage_, MAX_STORAGE, sizeof(struct item), storage_comp_item); +} + +void sortage_gsortitem (struct guild_storage* gstor) +{ + nullpo_retv(gstor); + qsort(gstor->storage_, MAX_GUILD_STORAGE, sizeof(struct item), storage_comp_item); +} + +/*========================================== + * 初期化とか + *------------------------------------------ + */ +int do_init_storage(void) // map.c::do_init()から呼ばれる +{ + storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + guild_storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + return 1; +} +void do_final_storage(void) // by [MC Cameri] +{ + storage_db->destroy(storage_db,NULL); + guild_storage_db->destroy(guild_storage_db,NULL); +} + + +static int storage_reconnect_sub(DBKey key,void *data,va_list ap) +{ //Parses storage and saves 'dirty' ones upon reconnect. [Skotlex] + int type = va_arg(ap, int); + if (type) + { //Guild Storage + struct guild_storage* stor = (struct guild_storage*) data; + if (stor->dirty && stor->storage_status == 0) //Save closed storages. + storage_guild_storagesave(0, stor->guild_id,0); + } + else + { //Account Storage + struct storage* stor = (struct storage*) data; + if (stor->dirty && stor->storage_status == 0) //Save closed storages. + storage_storage_save(stor->account_id, stor->dirty==2?1:0); + } + return 0; +} + +//Function to be invoked upon server reconnection to char. To save all 'dirty' storages [Skotlex +void do_reconnect_storage(void) +{ + storage_db->foreach(storage_db, storage_reconnect_sub, 0); + guild_storage_db->foreach(guild_storage_db, storage_reconnect_sub, 1); +} + +static void* create_storage(DBKey key, va_list args) { + struct storage *stor; + stor = (struct storage *) aCallocA (sizeof(struct storage), 1); + stor->account_id = key.i; + return stor; +} +struct storage *account2storage(int account_id) +{ + return idb_ensure(storage_db,account_id,create_storage); +} + +// Just to ask storage, without creation +struct storage *account2storage2(int account_id) +{ + return idb_get(storage_db, account_id); +} + +int storage_delete(int account_id) +{ + idb_remove(storage_db,account_id); + return 0; +} + +/*========================================== + * Opens a storage. Returns: + * 0 - success + * 1 - fail + * 2 - Storage requested from char-server (will open automatically later) + *------------------------------------------ + */ +int storage_storageopen(struct map_session_data *sd) +{ + struct storage *stor; + nullpo_retr(0, sd); + + if(sd->state.finalsave) //Refuse to open storage when you had your last save done. + return 1; + + if(sd->state.storage_flag) + return 1; //Already open? + + if(pc_can_give_items(pc_isGM(sd))) + { //check is this GM level is allowed to put items to storage + clif_displaymessage(sd->fd, msg_txt(246)); + return 1; + } + + if((stor = idb_get(storage_db,sd->status.account_id)) == NULL) + { //Request storage. + intif_request_storage(sd->status.account_id); + return 2; + } + + if (stor->storage_status) + return 1; //Already open/player already has it open... + + stor->storage_status = 1; + sd->state.storage_flag = 1; + clif_storagelist(sd,stor); + clif_updatestorageamount(sd,stor); + return 0; +} + +/*========================================== + * Internal add-item function. + *------------------------------------------ + */ +static int storage_additem(struct map_session_data *sd,struct storage *stor,struct item *item_data,int amount) +{ + struct item_data *data; + int i; + + if (sd->state.finalsave) + return 1; + + if(item_data->nameid <= 0 || amount <= 0) + return 1; + + data = itemdb_search(item_data->nameid); + + if (!itemdb_canstore(item_data, pc_isGM(sd))) + { //Check if item is storable. [Skotlex] + clif_displaymessage (sd->fd, msg_txt(264)); + return 1; + } + + if(itemdb_isstackable2(data)){ //Stackable + for(i=0;istorage_[i], item_data)) { + if(amount > MAX_AMOUNT - stor->storage_[i].amount) + return 1; + stor->storage_[i].amount+=amount; + clif_storageitemadded(sd,stor,i,amount); + stor->dirty = 1; + return 0; + } + } + } + //Add item + for(i=0;istorage_[i].nameid;i++); + + if(i>=MAX_STORAGE) + return 1; + + memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0])); + stor->storage_[i].amount=amount; + stor->storage_amount++; + clif_storageitemadded(sd,stor,i,amount); + clif_updatestorageamount(sd,stor); + stor->dirty = 1; + return 0; +} +/*========================================== + * Internal del-item function + *------------------------------------------ + */ +static int storage_delitem(struct map_session_data *sd,struct storage *stor,int n,int amount) +{ + + if(stor->storage_[n].nameid==0 || stor->storage_[n].amountstorage_[n].amount-=amount; + if(stor->storage_[n].amount==0){ + malloc_set(&stor->storage_[n],0,sizeof(stor->storage_[0])); + stor->storage_amount--; + clif_updatestorageamount(sd,stor); + } + clif_storageitemremoved(sd,n,amount); + + stor->dirty = 1; + return 0; +} +/*========================================== + * Add an item to the storage from the inventory. + *------------------------------------------ + */ +int storage_storageadd(struct map_session_data *sd,int index,int amount) +{ + struct storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=account2storage2(sd->status.account_id)); + + if((stor->storage_amount > MAX_STORAGE) || !stor->storage_status) + return 0; // storage full / storage closed + + if(index<0 || index>=MAX_INVENTORY) + return 0; + + if(sd->status.inventory[index].nameid <= 0) + return 0; //No item on that spot + + if(amount < 1 || amount > sd->status.inventory[index].amount) + return 0; + +// log_tostorage(sd, index, 0); + if(storage_additem(sd,stor,&sd->status.inventory[index],amount)==0) + // remove item from inventory + pc_delitem(sd,index,amount,0); + + return 1; +} + +/*========================================== + * Retrieve an item from the storage. + *------------------------------------------ + */ +int storage_storageget(struct map_session_data *sd,int index,int amount) +{ + struct storage *stor; + int flag; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=account2storage2(sd->status.account_id)); + + + if(index<0 || index>=MAX_STORAGE) + return 0; + + if(stor->storage_[index].nameid <= 0) + return 0; //Nothing there + + if(amount < 1 || amount > stor->storage_[index].amount) + return 0; + + if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0) + storage_delitem(sd,stor,index,amount); + else + clif_additem(sd,0,0,flag); +// log_fromstorage(sd, index, 0); + return 1; +} +/*========================================== + * Move an item from cart to storage. + *------------------------------------------ + */ +int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount) +{ + struct storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=account2storage2(sd->status.account_id)); + + if(stor->storage_amount > MAX_STORAGE || !stor->storage_status) + return 0; // storage full / storage closed + + if(index< 0 || index>=MAX_CART) + return 0; + + if(sd->status.cart[index].nameid <= 0) + return 0; //No item there. + + if(amount < 1 || amount > sd->status.cart[index].amount) + return 0; + + if(storage_additem(sd,stor,&sd->status.cart[index],amount)==0) + pc_cart_delitem(sd,index,amount,0); + + return 1; +} + +/*========================================== + * Get from Storage to the Cart + *------------------------------------------ + */ +int storage_storagegettocart(struct map_session_data *sd,int index,int amount) +{ + struct storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=account2storage2(sd->status.account_id)); + + if(!stor->storage_status) + return 0; + + if(index< 0 || index>=MAX_STORAGE) + return 0; + + if(stor->storage_[index].nameid <= 0) + return 0; //Nothing there. + + if(amount < 1 || amount > stor->storage_[index].amount) + return 0; + + if(pc_cart_additem(sd,&stor->storage_[index],amount)==0) + storage_delitem(sd,stor,index,amount); + + return 1; +} + + +/*========================================== + * Modified By Valaris to save upon closing [massdriller] + *------------------------------------------ + */ +int storage_storageclose(struct map_session_data *sd) +{ + struct storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=account2storage2(sd->status.account_id)); + + clif_storageclose(sd); + if (stor->storage_status) + { + if (save_settings&4) + chrif_save(sd,0); //Invokes the storage saving as well. + else + storage_storage_save(sd->status.account_id, 0); + } + stor->storage_status=0; + sd->state.storage_flag=0; + return 0; +} + +/*========================================== + * When quitting the game. + *------------------------------------------ + */ +int storage_storage_quit(struct map_session_data *sd, int flag) +{ + struct storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=account2storage2(sd->status.account_id)); + + if (stor->storage_status) + { + if (save_settings&4) + chrif_save(sd, flag); //Invokes the storage saving as well. + else + storage_storage_save(sd->status.account_id, flag); + } + stor->storage_status = 0; + sd->state.storage_flag = 0; + return 0; +} + +void storage_storage_dirty(struct map_session_data *sd) +{ + struct storage *stor; + + stor=account2storage2(sd->status.account_id); + + if(stor) + stor->dirty = 1; +} + +int storage_storage_save(int account_id, int final) +{ + struct storage *stor; + + stor=account2storage2(account_id); + if(!stor) return 0; + + if(stor->dirty) + { + if (final) { + stor->dirty = 2; + stor->storage_status = 0; //To prevent further manipulation of it. + } + intif_send_storage(stor); + return 1; + } + if (final) + { //Clear storage from memory. Nothing to save. + storage_delete(account_id); + return 1; + } + + return 0; +} + +//Ack from Char-server indicating the storage was saved. [Skotlex] +int storage_storage_saved(int account_id) +{ + struct storage *stor; + + if((stor=account2storage2(account_id)) == NULL) + return 0; + + if (stor->dirty == 2) + { //Final save of storage. Remove from memory. + storage_delete(account_id); + return 1; + } + + if (stor->dirty && stor->storage_status == 0) + { //Only mark it clean if it's not in use. [Skotlex] + stor->dirty = 0; + sortage_sortitem(stor); + return 1; + } + return 0; +} + +static void* create_guildstorage(DBKey key, va_list args) { + struct guild_storage *gs = NULL; + gs = (struct guild_storage *) aCallocA(sizeof(struct guild_storage), 1); + gs->guild_id=key.i; + return gs; +} +struct guild_storage *guild2storage(int guild_id) +{ + struct guild_storage *gs = NULL; + if(guild_search(guild_id) != NULL) + gs=(struct guild_storage *) idb_ensure(guild_storage_db,guild_id,create_guildstorage); + return gs; +} + +struct guild_storage *guild2storage2(int guild_id) +{ //For just locating a storage without creating one. [Skotlex] + return idb_get(guild_storage_db,guild_id); +} + +int guild_storage_delete(int guild_id) +{ + idb_remove(guild_storage_db,guild_id); + return 0; +} + +int storage_guild_storageopen(struct map_session_data *sd) +{ + struct guild_storage *gstor; + + nullpo_retr(0, sd); + + if(sd->status.guild_id <= 0) + return 2; + + if(sd->state.finalsave) //Refuse to open storage when you had your last save done. + return 1; + + if(sd->state.storage_flag) + return 1; //Can't open both storages at a time. + + if( pc_can_give_items(pc_isGM(sd)) ) { //check is this GM level can open guild storage and store items [Lupus] + clif_displaymessage(sd->fd, msg_txt(246)); + return 1; + } + + if((gstor = guild2storage2(sd->status.guild_id)) == NULL) { + intif_request_guild_storage(sd->status.account_id,sd->status.guild_id); + return 0; + } + if(gstor->storage_status) + return 1; + + gstor->storage_status = 1; + sd->state.storage_flag = 2; + clif_guildstoragelist(sd,gstor); + clif_updateguildstorageamount(sd,gstor); + return 0; +} + +int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount) +{ + struct item_data *data; + int i; + + nullpo_retr(1, sd); + nullpo_retr(1, stor); + nullpo_retr(1, item_data); + nullpo_retr(1, data = itemdb_search(item_data->nameid)); + + if(item_data->nameid <= 0 || amount <= 0) + return 1; + + if (!itemdb_canguildstore(item_data, pc_isGM(sd))) + { //Check if item is storable. [Skotlex] + clif_displaymessage (sd->fd, msg_txt(264)); + return 1; + } + + if(itemdb_isstackable2(data)){ //Stackable + for(i=0;istorage_[i], item_data)) { + if(stor->storage_[i].amount+amount > MAX_AMOUNT) + return 1; + stor->storage_[i].amount+=amount; + clif_guildstorageitemadded(sd,stor,i,amount); + stor->dirty = 1; + return 0; + } + } + } + //Add item + for(i=0;istorage_[i].nameid;i++); + + if(i>=MAX_GUILD_STORAGE) + return 1; + + memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0])); + stor->storage_[i].amount=amount; + stor->storage_amount++; + clif_guildstorageitemadded(sd,stor,i,amount); + clif_updateguildstorageamount(sd,stor); + stor->dirty = 1; + return 0; +} + +int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount) +{ + nullpo_retr(1, sd); + nullpo_retr(1, stor); + + if(stor->storage_[n].nameid==0 || stor->storage_[n].amountstorage_[n].amount-=amount; + if(stor->storage_[n].amount==0){ + malloc_set(&stor->storage_[n],0,sizeof(stor->storage_[0])); + stor->storage_amount--; + clif_updateguildstorageamount(sd,stor); + } + clif_storageitemremoved(sd,n,amount); + stor->dirty = 1; + return 0; +} + +int storage_guild_storageadd(struct map_session_data *sd,int index,int amount) +{ + struct guild_storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); + + if (!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE) + return 0; + + if(index<0 || index>=MAX_INVENTORY) + return 0; + + if(sd->status.inventory[index].nameid <= 0) + return 0; + + if(amount < 1 || amount > sd->status.inventory[index].amount) + return 0; + +// log_tostorage(sd, index, 1); + if(guild_storage_additem(sd,stor,&sd->status.inventory[index],amount)==0) + pc_delitem(sd,index,amount,0); + + return 1; +} + +int storage_guild_storageget(struct map_session_data *sd,int index,int amount) +{ + struct guild_storage *stor; + int flag; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); + + if(!stor->storage_status) + return 0; + + if(index<0 || index>=MAX_GUILD_STORAGE) + return 0; + + if(stor->storage_[index].nameid <= 0) + return 0; + + if(amount < 1 || amount > stor->storage_[index].amount) + return 0; + + if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0) + guild_storage_delitem(sd,stor,index,amount); + else + clif_additem(sd,0,0,flag); +// log_fromstorage(sd, index, 1); + + return 0; +} + +int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount) +{ + struct guild_storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); + + if(!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE) + return 0; + + if(index<0 || index>=MAX_CART) + return 0; + + if(sd->status.cart[index].nameid <= 0) + return 0; + + if(amount < 1 || amount > sd->status.cart[index].amount) + return 0; + + if(guild_storage_additem(sd,stor,&sd->status.cart[index],amount)==0) + pc_cart_delitem(sd,index,amount,0); + + return 1; +} + +int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount) +{ + struct guild_storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); + + if(!stor->storage_status) + return 0; + + if(index<0 || index>=MAX_GUILD_STORAGE) + return 0; + + if(stor->storage_[index].nameid<=0) + return 0; + + if(amount < 1 || amount > stor->storage_[index].amount) + return 0; + + if(pc_cart_additem(sd,&stor->storage_[index],amount)==0) + guild_storage_delitem(sd,stor,index,amount); + + return 1; +} + +int storage_guild_storagesave(int account_id, int guild_id, int flag) +{ + struct guild_storage *stor = guild2storage2(guild_id); + + if(stor) + { + if (flag) //Char quitting, close it. + stor->storage_status = 0; + if (stor->dirty) + intif_send_guild_storage(account_id,stor); + return 1; + } + return 0; +} + +int storage_guild_storagesaved(int guild_id) +{ + struct guild_storage *stor; + + if((stor=guild2storage2(guild_id)) != NULL) { + if (stor->dirty && stor->storage_status == 0) + { //Storage has been correctly saved. + stor->dirty = 0; + sortage_gsortitem(stor); + } + return 1; + } + return 0; +} + +int storage_guild_storageclose(struct map_session_data *sd) +{ + struct guild_storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); + + clif_storageclose(sd); + if (stor->storage_status) + { + if (save_settings&4) + chrif_save(sd, 0); //This one also saves the storage. [Skotlex] + else + storage_guild_storagesave(sd->status.account_id, sd->status.guild_id,0); + stor->storage_status=0; + } + sd->state.storage_flag = 0; + + return 0; +} + +int storage_guild_storage_quit(struct map_session_data *sd,int flag) +{ + struct guild_storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=guild2storage2(sd->status.guild_id)); + + if(flag) + { //Only during a guild break flag is 1 (don't save storage) + sd->state.storage_flag = 0; + stor->storage_status = 0; + clif_storageclose(sd); + if (save_settings&4) + chrif_save(sd,0); + return 0; + } + + if(stor->storage_status) { + if (save_settings&4) + chrif_save(sd,0); + else + storage_guild_storagesave(sd->status.account_id,sd->status.guild_id,1); + } + sd->state.storage_flag = 0; + stor->storage_status = 0; + + return 0; +} diff --git a/src/map/storage.h b/src/map/storage.h index b62ddb47a..2140a2ec5 100644 --- a/src/map/storage.h +++ b/src/map/storage.h @@ -1,45 +1,45 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _STORAGE_H_ -#define _STORAGE_H_ - -#include "../common/mmo.h" - -int storage_storageopen(struct map_session_data *sd); -int storage_storageadd(struct map_session_data *sd,int index,int amount); -int storage_storageget(struct map_session_data *sd,int index,int amount); -int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount); -int storage_storagegettocart(struct map_session_data *sd,int index,int amount); -int storage_storageclose(struct map_session_data *sd); -int do_init_storage(void); -void do_final_storage(void); -void do_reconnect_storage(void); -struct storage *account2storage(int account_id); -struct storage *account2storage2(int account_id); -int storage_delete(int account_id); -int storage_storage_quit(struct map_session_data *sd, int flag); -int storage_storage_save(int account_id, int final); -int storage_storage_saved(int account_id); //Ack from char server that guild store was saved. -void storage_storage_dirty(struct map_session_data *sd); - -struct guild_storage *guild2storage(int guild_id); -int guild_storage_delete(int guild_id); -int storage_guild_storageopen(struct map_session_data *sd); -int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount); -int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount); -int storage_guild_storageadd(struct map_session_data *sd,int index,int amount); -int storage_guild_storageget(struct map_session_data *sd,int index,int amount); -int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount); -int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount); -int storage_guild_storageclose(struct map_session_data *sd); -int storage_guild_storage_quit(struct map_session_data *sd,int flag); -int storage_guild_storagesave(int account_id, int guild_id, int flag); -int storage_guild_storagesaved(int guild_id); //Ack from char server that guild store was saved. - -int storage_comp_item(const void *_i1, const void *_i2); -//int storage_comp_item(const struct item* i1, const struct item* i2); -void sortage_sortitem(struct storage* stor); -void sortage_gsortitem(struct guild_storage* gstor); - -#endif +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _STORAGE_H_ +#define _STORAGE_H_ + +#include "../common/mmo.h" + +int storage_storageopen(struct map_session_data *sd); +int storage_storageadd(struct map_session_data *sd,int index,int amount); +int storage_storageget(struct map_session_data *sd,int index,int amount); +int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount); +int storage_storagegettocart(struct map_session_data *sd,int index,int amount); +int storage_storageclose(struct map_session_data *sd); +int do_init_storage(void); +void do_final_storage(void); +void do_reconnect_storage(void); +struct storage *account2storage(int account_id); +struct storage *account2storage2(int account_id); +int storage_delete(int account_id); +int storage_storage_quit(struct map_session_data *sd, int flag); +int storage_storage_save(int account_id, int final); +int storage_storage_saved(int account_id); //Ack from char server that guild store was saved. +void storage_storage_dirty(struct map_session_data *sd); + +struct guild_storage *guild2storage(int guild_id); +int guild_storage_delete(int guild_id); +int storage_guild_storageopen(struct map_session_data *sd); +int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount); +int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount); +int storage_guild_storageadd(struct map_session_data *sd,int index,int amount); +int storage_guild_storageget(struct map_session_data *sd,int index,int amount); +int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount); +int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount); +int storage_guild_storageclose(struct map_session_data *sd); +int storage_guild_storage_quit(struct map_session_data *sd,int flag); +int storage_guild_storagesave(int account_id, int guild_id, int flag); +int storage_guild_storagesaved(int guild_id); //Ack from char server that guild store was saved. + +int storage_comp_item(const void *_i1, const void *_i2); +//int storage_comp_item(const struct item* i1, const struct item* i2); +void sortage_sortitem(struct storage* stor); +void sortage_gsortitem(struct guild_storage* gstor); + +#endif diff --git a/src/map/trade.c b/src/map/trade.c index 02eb454d3..09b07e02f 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -1,553 +1,553 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include - -#include "../common/nullpo.h" -#include "clif.h" -#include "itemdb.h" -#include "map.h" -#include "trade.h" -#include "pc.h" -#include "npc.h" -#include "battle.h" -#include "chrif.h" -#include "storage.h" -#include "intif.h" -#include "atcommand.h" -#include "log.h" -#include "../common/malloc.h" - -//Max distance from traders to enable a trade to take place. -#define TRADE_DISTANCE 2 - -/*========================================== - * Initiates a trade request. - *------------------------------------------ - */ -void trade_traderequest(struct map_session_data *sd, struct map_session_data *target_sd) { - int level; - - nullpo_retv(sd); - - if (map[sd->bl.m].flag.notrade) { - clif_displaymessage (sd->fd, msg_txt(272)); - return; //Can't trade in notrade mapflag maps. - } - - if (target_sd == NULL || sd == target_sd) { - clif_tradestart(sd, 1); // character does not exist - return; - } - - if (!battle_config.invite_request_check) { - if (target_sd->guild_invite > 0 || target_sd->party_invite > 0) { - clif_tradestart(sd, 2); - return; - } - } - - if ((target_sd->trade_partner != 0) || (sd->trade_partner != 0)) { - trade_tradecancel(sd); // person is in another trade - return; - } - - level = pc_isGM(sd); - if ( pc_can_give_items(level) || pc_can_give_items(pc_isGM(target_sd)) ) //check if both GMs are allowed to trade - { - clif_displaymessage(sd->fd, msg_txt(246)); - trade_tradecancel(sd); // GM is not allowed to trade - return; - } - - //Fixed. Only real GMs can request trade from far away! [Lupus] - if (level < lowest_gm_level && (sd->bl.m != target_sd->bl.m || - !check_distance_bl(&sd->bl, &target_sd->bl, TRADE_DISTANCE) - )) { - clif_tradestart(sd, 0); // too far - return ; - } - - target_sd->trade_partner = sd->status.account_id; - sd->trade_partner = target_sd->status.account_id; - clif_traderequest(target_sd, sd->status.name); -} - -/*========================================== - * Reply to a trade-request. - *------------------------------------------ - */ -void trade_tradeack(struct map_session_data *sd, int type) { - struct map_session_data *target_sd; - nullpo_retv(sd); - - if (sd->state.trading || !sd->trade_partner) - return; //Already trading or no partner set. - - if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) { - sd->trade_partner=0; - return; - } - - if (target_sd->state.trading || target_sd->trade_partner != sd->bl.id) - return; //Already trading or wrong partner. - - //Copied here as well since the original character could had warped. - if (type == 3 && pc_isGM(target_sd) < lowest_gm_level && (sd->bl.m != target_sd->bl.m || - !check_distance_bl(&sd->bl, &target_sd->bl, TRADE_DISTANCE) - )) { - sd->trade_partner=0; - target_sd->trade_partner = 0; - clif_tradestart(sd, 0); // too far - return; - } - - //TODO: Type 4/3? What would 1/2 and the rest do? - if (type == 4) { // Cancel - sd->state.deal_locked = 0; - sd->trade_partner = 0; - target_sd->state.deal_locked = 0; - target_sd->trade_partner = 0; - clif_tradestart(target_sd, type); - clif_tradestart(sd, type); - } - - if (type == 3) { //Initiate trade - sd->state.trading = 1; - target_sd->state.trading = 1; - malloc_set(&sd->deal, 0, sizeof(sd->deal)); - malloc_set(&target_sd->deal, 0, sizeof(target_sd->deal)); - clif_tradestart(target_sd, type); - clif_tradestart(sd, type); - if (sd->npc_id) - npc_event_dequeue(sd); - if (target_sd->npc_id) - npc_event_dequeue(target_sd); - } -} - -/*========================================== - * Check here hacker for duplicate item in trade - * normal client refuse to have 2 same types of item (except equipment) in same trade window - * normal client authorise only no equiped item and only from inventory - *------------------------------------------ - */ -int impossible_trade_check(struct map_session_data *sd) { - struct item inventory[MAX_INVENTORY]; - char message_to_gm[200]; - int i, index; - - nullpo_retr(1, sd); - - if(sd->deal.zeny > sd->status.zeny) - { - pc_setglobalreg(sd,"ZENY_HACKER",1); - return -1; - } - - // get inventory of player - memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY); - - // remove this part: arrows can be trade and equiped - // re-added! [celest] - // remove equiped items (they can not be trade) - for (i = 0; i < MAX_INVENTORY; i++) - if (inventory[i].nameid > 0 && inventory[i].equip && !(inventory[i].equip & EQP_AMMO)) - malloc_set(&inventory[i], 0, sizeof(struct item)); - - // check items in player inventory - for(i = 0; i < 10; i++) { - if (!sd->deal.item[i].amount) - continue; - index = sd->deal.item[i].index; - if (inventory[index].amount < sd->deal.item[i].amount) - { // if more than the player have -> hack - sprintf(message_to_gm, msg_txt(538), sd->status.name, sd->status.account_id); // Hack on trade: character '%s' (account: %d) try to trade more items that he has. - intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm); - sprintf(message_to_gm, msg_txt(539), inventory[index].amount, inventory[index].nameid, sd->deal.item[i].amount); // This player has %d of a kind of item (id: %d), and try to trade %d of them. - intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm); - // if we block people - if (battle_config.ban_hack_trade < 0) { - chrif_char_ask_name(-1, sd->status.name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block - clif_setwaitclose(sd->fd); // forced to disconnect because of the hack - // message about the ban - sprintf(message_to_gm, msg_txt(540)); // This player has been definitivly blocked. - // if we ban people - } else if (battle_config.ban_hack_trade > 0) { - chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_hack_trade, 0); // type: 2 - ban (year, month, day, hour, minute, second) - clif_setwaitclose(sd->fd); // forced to disconnect because of the hack - // message about the ban - sprintf(message_to_gm, msg_txt(507), battle_config.ban_hack_trade); // This player has been banned for %d minute(s). - } else - // message about the ban - sprintf(message_to_gm, msg_txt(508)); // This player hasn't been banned (Ban option is disabled). - - intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm); - return 1; - } - inventory[index].amount -= sd->deal.item[i].amount; // remove item from inventory - } - return 0; -} - -/*========================================== - * Checks if trade is possible (against zeny limits, inventory limits, etc) - *------------------------------------------ - */ -int trade_check(struct map_session_data *sd, struct map_session_data *tsd) { - struct item inventory[MAX_INVENTORY]; - struct item inventory2[MAX_INVENTORY]; - struct item_data *data; - int trade_i, i, amount, n; - - // check zenys value against hackers (Zeny was already checked on time of adding, but you never know when you lost some zeny since then. - if(sd->deal.zeny > sd->status.zeny || (tsd->status.zeny > MAX_ZENY - sd->deal.zeny)) - return 0; - if(tsd->deal.zeny > tsd->status.zeny || (sd->status.zeny > MAX_ZENY - tsd->deal.zeny)) - return 0; - - // get inventory of player - memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY); - memcpy(&inventory2, &tsd->status.inventory, sizeof(struct item) * MAX_INVENTORY); - - // check free slot in both inventory - for(trade_i = 0; trade_i < 10; trade_i++) { - amount = sd->deal.item[trade_i].amount; - if (amount) { - n = sd->deal.item[trade_i].index; - if (amount > inventory[n].amount) - return 0; //qty Exploit? - - data = itemdb_search(inventory[n].nameid); - i = MAX_INVENTORY; - if (itemdb_isstackable2(data)) { //Stackable item. - for(i = 0; i < MAX_INVENTORY; i++) - if (inventory2[i].nameid == inventory[n].nameid && - inventory2[i].card[0] == inventory[n].card[0] && inventory2[i].card[1] == inventory[n].card[1] && - inventory2[i].card[2] == inventory[n].card[2] && inventory2[i].card[3] == inventory[n].card[3]) { - if (inventory2[i].amount + amount > MAX_AMOUNT) - return 0; - inventory2[i].amount += amount; - inventory[n].amount -= amount; - break; - } - } - - if (i == MAX_INVENTORY) {// look for an empty slot. - for(i = 0; i < MAX_INVENTORY && inventory2[i].nameid; i++); - if (i == MAX_INVENTORY) - return 0; - memcpy(&inventory2[i], &inventory[n], sizeof(struct item)); - inventory2[i].amount = amount; - inventory[n].amount -= amount; - } - } - amount = tsd->deal.item[trade_i].amount; - if (!amount) - continue; - n = tsd->deal.item[trade_i].index; - if (amount > inventory2[n].amount) - return 0; - // search if it's possible to add item (for full inventory) - data = itemdb_search(inventory2[n].nameid); - i = MAX_INVENTORY; - if (itemdb_isstackable2(data)) { - for(i = 0; i < MAX_INVENTORY; i++) - if (inventory[i].nameid == inventory2[n].nameid && - inventory[i].card[0] == inventory2[n].card[0] && inventory[i].card[1] == inventory2[n].card[1] && - inventory[i].card[2] == inventory2[n].card[2] && inventory[i].card[3] == inventory2[n].card[3]) { - if (inventory[i].amount + amount > MAX_AMOUNT) - return 0; - inventory[i].amount += amount; - inventory2[n].amount -= amount; - break; - } - } - if (i == MAX_INVENTORY) { - for(i = 0; i < MAX_INVENTORY && inventory[i].nameid; i++); - if (i == MAX_INVENTORY) - return 0; - memcpy(&inventory[i], &inventory2[n], sizeof(struct item)); - inventory[i].amount = amount; - inventory2[n].amount -= amount; - } - } - - return 1; -} - -/*========================================== - * Adds an item/qty to the trade window [rewrite by Skotlex] - *------------------------------------------ - */ -void trade_tradeadditem(struct map_session_data *sd, int index, int amount) { - struct map_session_data *target_sd; - struct item *item; - int trade_i, trade_weight; - - nullpo_retv(sd); - if (!sd->state.trading || sd->state.deal_locked > 0) - return; //Can't add stuff. - - if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) { - trade_tradecancel(sd); - return; - } - - if (amount == 0) - { //Why do this.. ~.~ just send an ack, the item won't display on the trade window. - clif_tradeitemok(sd, index, 0); - return; - } - - if (index == 0) - { //Adding Zeny - if (amount >= 0 && amount <= sd->status.zeny && // check amount - (amount <= MAX_ZENY - target_sd->status.zeny)) // fix positiv overflow - { //Check Ok - sd->deal.zeny = amount; - clif_tradeadditem(sd, target_sd, 0, amount); - } else //Send overweight when trying to add too much zeny? Hope they get the idea... - clif_tradeitemok(sd, 0, 1); - return; - } - - index = index -2; //Why the actual index used is -2? - //Item checks... - if (index < 0 || index >= MAX_INVENTORY) - return; - if (amount < 0 || amount > sd->status.inventory[index].amount) - return; - - item = &sd->status.inventory[index]; - trade_i = pc_isGM(sd); //Recycling the variables to check for trad restrict. - trade_weight = pc_isGM(target_sd); - if (!itemdb_cantrade(item, trade_i, trade_weight) && //Can't trade - (pc_get_partner(sd) != target_sd || - !itemdb_canpartnertrade(item, trade_i, trade_weight))) //Can't partner-trade - { - clif_displaymessage (sd->fd, msg_txt(260)); - return; - } - - for(trade_i = 0; trade_i < 10; trade_i++) - { //Locate a trade position - if (sd->deal.item[trade_i].index == index || - sd->deal.item[trade_i].amount == 0) - break; - } - if (trade_i >= 10) //No space left - { - clif_tradeitemok(sd, index+2, 1); - return; - } - - trade_weight = sd->inventory_data[index]->weight * amount; - if (target_sd->weight + sd->deal.weight + trade_weight > target_sd->max_weight) - { //fail to add item -- the player was over weighted. - clif_tradeitemok(sd, index+2, 1); - return; - } - - if (sd->deal.item[trade_i].index == index) - { //The same item as before is being readjusted. - if (sd->deal.item[trade_i].amount + amount > sd->status.inventory[index].amount) - { //packet deal exploit check - amount = sd->status.inventory[index].amount - sd->deal.item[trade_i].amount; - trade_weight = sd->inventory_data[index]->weight * amount; - } - sd->deal.item[trade_i].amount += amount; - } else { //New deal item - sd->deal.item[trade_i].index = index; - sd->deal.item[trade_i].amount = amount; - } - sd->deal.weight += trade_weight; - - clif_tradeitemok(sd, index+2, 0); // Return the index as it was received - clif_tradeadditem(sd, target_sd, index+2, amount); //index fix -} - -/*========================================== - * 'Ok' button on the trade window is pressed. - *------------------------------------------ - */ -void trade_tradeok(struct map_session_data *sd) { - struct map_session_data *target_sd; - - if(sd->state.deal_locked || !sd->state.trading) - return; - - if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) { - trade_tradecancel(sd); - return; - } - sd->state.deal_locked = 1; - clif_tradeitemok(sd, 0, 0); - clif_tradedeal_lock(sd, 0); - clif_tradedeal_lock(target_sd, 1); -} - -/*========================================== - * 'Cancel' is pressed. (or trade was force-cancelled by the code) - *------------------------------------------ - */ -void trade_tradecancel(struct map_session_data *sd) { - struct map_session_data *target_sd; - int trade_i; - - if(!sd->state.trading) - return; - - for(trade_i = 0; trade_i < 10; trade_i++) { // give items back (only virtual) - if (!sd->deal.item[trade_i].amount) - continue; - clif_additem(sd, sd->deal.item[trade_i].index, sd->deal.item[trade_i].amount, 0); - sd->deal.item[trade_i].index = 0; - sd->deal.item[trade_i].amount = 0; - } - if (sd->deal.zeny) { - clif_updatestatus(sd, SP_ZENY); - sd->deal.zeny = 0; - } - - target_sd = map_id2sd(sd->trade_partner); - sd->state.deal_locked = 0; - sd->state.trading = 0; - sd->trade_partner = 0; - clif_tradecancelled(sd); - - if (!target_sd) - return; - - for(trade_i = 0; trade_i < 10; trade_i++) { // give items back (only virtual) - if (!target_sd->deal.item[trade_i].amount) - continue; - clif_additem(target_sd, target_sd->deal.item[trade_i].index, target_sd->deal.item[trade_i].amount, 0); - target_sd->deal.item[trade_i].index = 0; - target_sd->deal.item[trade_i].amount = 0; - } - - if (target_sd->deal.zeny) { - clif_updatestatus(target_sd, SP_ZENY); - target_sd->deal.zeny = 0; - } - target_sd->state.deal_locked = 0; - target_sd->trade_partner = 0; - target_sd->state.trading = 0; - clif_tradecancelled(target_sd); -} - -/*========================================== - * 取引許諾(trade押し) - *------------------------------------------ - */ -void trade_tradecommit(struct map_session_data *sd) { - struct map_session_data *tsd; - int trade_i; - int flag; - - if (!sd->state.trading || !sd->state.deal_locked) //Locked should be 1 (pressed ok) before you can press trade. - return; - - if ((tsd = map_id2sd(sd->trade_partner)) == NULL) { - trade_tradecancel(sd); - return; - } - - sd->state.deal_locked = 2; - - if (tsd->state.deal_locked < 2) - return; //Not yet time for trading. - - //Now is a good time (to save on resources) to check that the trade can indeed be made and it's not exploitable. - // check exploit (trade more items that you have) - if (impossible_trade_check(sd)) { - trade_tradecancel(sd); - return; - } - // check exploit (trade more items that you have) - if (impossible_trade_check(tsd)) { - trade_tradecancel(tsd); - return; - } - // check for full inventory (can not add traded items) - if (!trade_check(sd,tsd)) { // check the both players - trade_tradecancel(sd); - return; - } - - // trade is accepted and correct. - for(trade_i = 0; trade_i < 10; trade_i++) { - int n; - if (sd->deal.item[trade_i].amount) { - n = sd->deal.item[trade_i].index; - - flag = pc_additem(tsd, &sd->status.inventory[n], sd->deal.item[trade_i].amount); - if (flag == 0) { - //Logs (T)rade [Lupus] - if(log_config.enable_logs&0x2) { - log_pick_pc(sd, "T", sd->status.inventory[n].nameid, -(sd->deal.item[trade_i].amount), &sd->status.inventory[n]); - log_pick_pc(tsd, "T", sd->status.inventory[n].nameid, sd->deal.item[trade_i].amount, &sd->status.inventory[n]); - } - //Logs - pc_delitem(sd, n, sd->deal.item[trade_i].amount, 1); - } else - clif_additem(sd, n, sd->deal.item[trade_i].amount, 0); - sd->deal.item[trade_i].index = 0; - sd->deal.item[trade_i].amount = 0; - } - if (tsd->deal.item[trade_i].amount) { - n = tsd->deal.item[trade_i].index; - - flag = pc_additem(sd, &tsd->status.inventory[n], tsd->deal.item[trade_i].amount); - if (flag == 0) { - //Logs (T)rade [Lupus] - if(log_config.enable_logs&0x2) { - log_pick_pc(tsd, "T", tsd->status.inventory[n].nameid, -(tsd->deal.item[trade_i].amount), &tsd->status.inventory[n]); - log_pick_pc(sd, "T", tsd->status.inventory[n].nameid, tsd->deal.item[trade_i].amount, &tsd->status.inventory[n]); - } - //Logs - pc_delitem(tsd, n, tsd->deal.item[trade_i].amount, 1); - } else - clif_additem(tsd, n, tsd->deal.item[trade_i].amount, 0); - tsd->deal.item[trade_i].index = 0; - tsd->deal.item[trade_i].amount = 0; - } - } - if (sd->deal.zeny || tsd->deal.zeny) { - if (sd->deal.zeny) { - sd->status.zeny -= sd->deal.zeny; - tsd->status.zeny += sd->deal.zeny; - if (log_config.zeny) - log_zeny(tsd, "T", sd, sd->deal.zeny);//Logs Zeny (T)rade [Lupus] - sd->deal.zeny = 0; - } - if (tsd->deal.zeny) { - tsd->status.zeny -= tsd->deal.zeny; - sd->status.zeny += tsd->deal.zeny; - if (log_config.zeny) - log_zeny(sd, "T", tsd, tsd->deal.zeny);//Logs Zeny (T)rade [Lupus] - tsd->deal.zeny = 0; - } - clif_updatestatus(sd, SP_ZENY); - clif_updatestatus(tsd, SP_ZENY); - } - - sd->state.deal_locked = 0; - sd->trade_partner = 0; - sd->state.trading = 0; - - tsd->state.deal_locked = 0; - tsd->trade_partner = 0; - tsd->state.trading = 0; - - clif_tradecompleted(sd, 0); - clif_tradecompleted(tsd, 0); - // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players - if (save_settings&1) - { - chrif_save(sd,0); - chrif_save(tsd,0); - } -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include + +#include "../common/nullpo.h" +#include "clif.h" +#include "itemdb.h" +#include "map.h" +#include "trade.h" +#include "pc.h" +#include "npc.h" +#include "battle.h" +#include "chrif.h" +#include "storage.h" +#include "intif.h" +#include "atcommand.h" +#include "log.h" +#include "../common/malloc.h" + +//Max distance from traders to enable a trade to take place. +#define TRADE_DISTANCE 2 + +/*========================================== + * Initiates a trade request. + *------------------------------------------ + */ +void trade_traderequest(struct map_session_data *sd, struct map_session_data *target_sd) { + int level; + + nullpo_retv(sd); + + if (map[sd->bl.m].flag.notrade) { + clif_displaymessage (sd->fd, msg_txt(272)); + return; //Can't trade in notrade mapflag maps. + } + + if (target_sd == NULL || sd == target_sd) { + clif_tradestart(sd, 1); // character does not exist + return; + } + + if (!battle_config.invite_request_check) { + if (target_sd->guild_invite > 0 || target_sd->party_invite > 0) { + clif_tradestart(sd, 2); + return; + } + } + + if ((target_sd->trade_partner != 0) || (sd->trade_partner != 0)) { + trade_tradecancel(sd); // person is in another trade + return; + } + + level = pc_isGM(sd); + if ( pc_can_give_items(level) || pc_can_give_items(pc_isGM(target_sd)) ) //check if both GMs are allowed to trade + { + clif_displaymessage(sd->fd, msg_txt(246)); + trade_tradecancel(sd); // GM is not allowed to trade + return; + } + + //Fixed. Only real GMs can request trade from far away! [Lupus] + if (level < lowest_gm_level && (sd->bl.m != target_sd->bl.m || + !check_distance_bl(&sd->bl, &target_sd->bl, TRADE_DISTANCE) + )) { + clif_tradestart(sd, 0); // too far + return ; + } + + target_sd->trade_partner = sd->status.account_id; + sd->trade_partner = target_sd->status.account_id; + clif_traderequest(target_sd, sd->status.name); +} + +/*========================================== + * Reply to a trade-request. + *------------------------------------------ + */ +void trade_tradeack(struct map_session_data *sd, int type) { + struct map_session_data *target_sd; + nullpo_retv(sd); + + if (sd->state.trading || !sd->trade_partner) + return; //Already trading or no partner set. + + if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) { + sd->trade_partner=0; + return; + } + + if (target_sd->state.trading || target_sd->trade_partner != sd->bl.id) + return; //Already trading or wrong partner. + + //Copied here as well since the original character could had warped. + if (type == 3 && pc_isGM(target_sd) < lowest_gm_level && (sd->bl.m != target_sd->bl.m || + !check_distance_bl(&sd->bl, &target_sd->bl, TRADE_DISTANCE) + )) { + sd->trade_partner=0; + target_sd->trade_partner = 0; + clif_tradestart(sd, 0); // too far + return; + } + + //TODO: Type 4/3? What would 1/2 and the rest do? + if (type == 4) { // Cancel + sd->state.deal_locked = 0; + sd->trade_partner = 0; + target_sd->state.deal_locked = 0; + target_sd->trade_partner = 0; + clif_tradestart(target_sd, type); + clif_tradestart(sd, type); + } + + if (type == 3) { //Initiate trade + sd->state.trading = 1; + target_sd->state.trading = 1; + malloc_set(&sd->deal, 0, sizeof(sd->deal)); + malloc_set(&target_sd->deal, 0, sizeof(target_sd->deal)); + clif_tradestart(target_sd, type); + clif_tradestart(sd, type); + if (sd->npc_id) + npc_event_dequeue(sd); + if (target_sd->npc_id) + npc_event_dequeue(target_sd); + } +} + +/*========================================== + * Check here hacker for duplicate item in trade + * normal client refuse to have 2 same types of item (except equipment) in same trade window + * normal client authorise only no equiped item and only from inventory + *------------------------------------------ + */ +int impossible_trade_check(struct map_session_data *sd) { + struct item inventory[MAX_INVENTORY]; + char message_to_gm[200]; + int i, index; + + nullpo_retr(1, sd); + + if(sd->deal.zeny > sd->status.zeny) + { + pc_setglobalreg(sd,"ZENY_HACKER",1); + return -1; + } + + // get inventory of player + memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY); + + // remove this part: arrows can be trade and equiped + // re-added! [celest] + // remove equiped items (they can not be trade) + for (i = 0; i < MAX_INVENTORY; i++) + if (inventory[i].nameid > 0 && inventory[i].equip && !(inventory[i].equip & EQP_AMMO)) + malloc_set(&inventory[i], 0, sizeof(struct item)); + + // check items in player inventory + for(i = 0; i < 10; i++) { + if (!sd->deal.item[i].amount) + continue; + index = sd->deal.item[i].index; + if (inventory[index].amount < sd->deal.item[i].amount) + { // if more than the player have -> hack + sprintf(message_to_gm, msg_txt(538), sd->status.name, sd->status.account_id); // Hack on trade: character '%s' (account: %d) try to trade more items that he has. + intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm); + sprintf(message_to_gm, msg_txt(539), inventory[index].amount, inventory[index].nameid, sd->deal.item[i].amount); // This player has %d of a kind of item (id: %d), and try to trade %d of them. + intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm); + // if we block people + if (battle_config.ban_hack_trade < 0) { + chrif_char_ask_name(-1, sd->status.name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block + clif_setwaitclose(sd->fd); // forced to disconnect because of the hack + // message about the ban + sprintf(message_to_gm, msg_txt(540)); // This player has been definitivly blocked. + // if we ban people + } else if (battle_config.ban_hack_trade > 0) { + chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_hack_trade, 0); // type: 2 - ban (year, month, day, hour, minute, second) + clif_setwaitclose(sd->fd); // forced to disconnect because of the hack + // message about the ban + sprintf(message_to_gm, msg_txt(507), battle_config.ban_hack_trade); // This player has been banned for %d minute(s). + } else + // message about the ban + sprintf(message_to_gm, msg_txt(508)); // This player hasn't been banned (Ban option is disabled). + + intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm); + return 1; + } + inventory[index].amount -= sd->deal.item[i].amount; // remove item from inventory + } + return 0; +} + +/*========================================== + * Checks if trade is possible (against zeny limits, inventory limits, etc) + *------------------------------------------ + */ +int trade_check(struct map_session_data *sd, struct map_session_data *tsd) { + struct item inventory[MAX_INVENTORY]; + struct item inventory2[MAX_INVENTORY]; + struct item_data *data; + int trade_i, i, amount, n; + + // check zenys value against hackers (Zeny was already checked on time of adding, but you never know when you lost some zeny since then. + if(sd->deal.zeny > sd->status.zeny || (tsd->status.zeny > MAX_ZENY - sd->deal.zeny)) + return 0; + if(tsd->deal.zeny > tsd->status.zeny || (sd->status.zeny > MAX_ZENY - tsd->deal.zeny)) + return 0; + + // get inventory of player + memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY); + memcpy(&inventory2, &tsd->status.inventory, sizeof(struct item) * MAX_INVENTORY); + + // check free slot in both inventory + for(trade_i = 0; trade_i < 10; trade_i++) { + amount = sd->deal.item[trade_i].amount; + if (amount) { + n = sd->deal.item[trade_i].index; + if (amount > inventory[n].amount) + return 0; //qty Exploit? + + data = itemdb_search(inventory[n].nameid); + i = MAX_INVENTORY; + if (itemdb_isstackable2(data)) { //Stackable item. + for(i = 0; i < MAX_INVENTORY; i++) + if (inventory2[i].nameid == inventory[n].nameid && + inventory2[i].card[0] == inventory[n].card[0] && inventory2[i].card[1] == inventory[n].card[1] && + inventory2[i].card[2] == inventory[n].card[2] && inventory2[i].card[3] == inventory[n].card[3]) { + if (inventory2[i].amount + amount > MAX_AMOUNT) + return 0; + inventory2[i].amount += amount; + inventory[n].amount -= amount; + break; + } + } + + if (i == MAX_INVENTORY) {// look for an empty slot. + for(i = 0; i < MAX_INVENTORY && inventory2[i].nameid; i++); + if (i == MAX_INVENTORY) + return 0; + memcpy(&inventory2[i], &inventory[n], sizeof(struct item)); + inventory2[i].amount = amount; + inventory[n].amount -= amount; + } + } + amount = tsd->deal.item[trade_i].amount; + if (!amount) + continue; + n = tsd->deal.item[trade_i].index; + if (amount > inventory2[n].amount) + return 0; + // search if it's possible to add item (for full inventory) + data = itemdb_search(inventory2[n].nameid); + i = MAX_INVENTORY; + if (itemdb_isstackable2(data)) { + for(i = 0; i < MAX_INVENTORY; i++) + if (inventory[i].nameid == inventory2[n].nameid && + inventory[i].card[0] == inventory2[n].card[0] && inventory[i].card[1] == inventory2[n].card[1] && + inventory[i].card[2] == inventory2[n].card[2] && inventory[i].card[3] == inventory2[n].card[3]) { + if (inventory[i].amount + amount > MAX_AMOUNT) + return 0; + inventory[i].amount += amount; + inventory2[n].amount -= amount; + break; + } + } + if (i == MAX_INVENTORY) { + for(i = 0; i < MAX_INVENTORY && inventory[i].nameid; i++); + if (i == MAX_INVENTORY) + return 0; + memcpy(&inventory[i], &inventory2[n], sizeof(struct item)); + inventory[i].amount = amount; + inventory2[n].amount -= amount; + } + } + + return 1; +} + +/*========================================== + * Adds an item/qty to the trade window [rewrite by Skotlex] + *------------------------------------------ + */ +void trade_tradeadditem(struct map_session_data *sd, int index, int amount) { + struct map_session_data *target_sd; + struct item *item; + int trade_i, trade_weight; + + nullpo_retv(sd); + if (!sd->state.trading || sd->state.deal_locked > 0) + return; //Can't add stuff. + + if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) { + trade_tradecancel(sd); + return; + } + + if (amount == 0) + { //Why do this.. ~.~ just send an ack, the item won't display on the trade window. + clif_tradeitemok(sd, index, 0); + return; + } + + if (index == 0) + { //Adding Zeny + if (amount >= 0 && amount <= sd->status.zeny && // check amount + (amount <= MAX_ZENY - target_sd->status.zeny)) // fix positiv overflow + { //Check Ok + sd->deal.zeny = amount; + clif_tradeadditem(sd, target_sd, 0, amount); + } else //Send overweight when trying to add too much zeny? Hope they get the idea... + clif_tradeitemok(sd, 0, 1); + return; + } + + index = index -2; //Why the actual index used is -2? + //Item checks... + if (index < 0 || index >= MAX_INVENTORY) + return; + if (amount < 0 || amount > sd->status.inventory[index].amount) + return; + + item = &sd->status.inventory[index]; + trade_i = pc_isGM(sd); //Recycling the variables to check for trad restrict. + trade_weight = pc_isGM(target_sd); + if (!itemdb_cantrade(item, trade_i, trade_weight) && //Can't trade + (pc_get_partner(sd) != target_sd || + !itemdb_canpartnertrade(item, trade_i, trade_weight))) //Can't partner-trade + { + clif_displaymessage (sd->fd, msg_txt(260)); + return; + } + + for(trade_i = 0; trade_i < 10; trade_i++) + { //Locate a trade position + if (sd->deal.item[trade_i].index == index || + sd->deal.item[trade_i].amount == 0) + break; + } + if (trade_i >= 10) //No space left + { + clif_tradeitemok(sd, index+2, 1); + return; + } + + trade_weight = sd->inventory_data[index]->weight * amount; + if (target_sd->weight + sd->deal.weight + trade_weight > target_sd->max_weight) + { //fail to add item -- the player was over weighted. + clif_tradeitemok(sd, index+2, 1); + return; + } + + if (sd->deal.item[trade_i].index == index) + { //The same item as before is being readjusted. + if (sd->deal.item[trade_i].amount + amount > sd->status.inventory[index].amount) + { //packet deal exploit check + amount = sd->status.inventory[index].amount - sd->deal.item[trade_i].amount; + trade_weight = sd->inventory_data[index]->weight * amount; + } + sd->deal.item[trade_i].amount += amount; + } else { //New deal item + sd->deal.item[trade_i].index = index; + sd->deal.item[trade_i].amount = amount; + } + sd->deal.weight += trade_weight; + + clif_tradeitemok(sd, index+2, 0); // Return the index as it was received + clif_tradeadditem(sd, target_sd, index+2, amount); //index fix +} + +/*========================================== + * 'Ok' button on the trade window is pressed. + *------------------------------------------ + */ +void trade_tradeok(struct map_session_data *sd) { + struct map_session_data *target_sd; + + if(sd->state.deal_locked || !sd->state.trading) + return; + + if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) { + trade_tradecancel(sd); + return; + } + sd->state.deal_locked = 1; + clif_tradeitemok(sd, 0, 0); + clif_tradedeal_lock(sd, 0); + clif_tradedeal_lock(target_sd, 1); +} + +/*========================================== + * 'Cancel' is pressed. (or trade was force-cancelled by the code) + *------------------------------------------ + */ +void trade_tradecancel(struct map_session_data *sd) { + struct map_session_data *target_sd; + int trade_i; + + if(!sd->state.trading) + return; + + for(trade_i = 0; trade_i < 10; trade_i++) { // give items back (only virtual) + if (!sd->deal.item[trade_i].amount) + continue; + clif_additem(sd, sd->deal.item[trade_i].index, sd->deal.item[trade_i].amount, 0); + sd->deal.item[trade_i].index = 0; + sd->deal.item[trade_i].amount = 0; + } + if (sd->deal.zeny) { + clif_updatestatus(sd, SP_ZENY); + sd->deal.zeny = 0; + } + + target_sd = map_id2sd(sd->trade_partner); + sd->state.deal_locked = 0; + sd->state.trading = 0; + sd->trade_partner = 0; + clif_tradecancelled(sd); + + if (!target_sd) + return; + + for(trade_i = 0; trade_i < 10; trade_i++) { // give items back (only virtual) + if (!target_sd->deal.item[trade_i].amount) + continue; + clif_additem(target_sd, target_sd->deal.item[trade_i].index, target_sd->deal.item[trade_i].amount, 0); + target_sd->deal.item[trade_i].index = 0; + target_sd->deal.item[trade_i].amount = 0; + } + + if (target_sd->deal.zeny) { + clif_updatestatus(target_sd, SP_ZENY); + target_sd->deal.zeny = 0; + } + target_sd->state.deal_locked = 0; + target_sd->trade_partner = 0; + target_sd->state.trading = 0; + clif_tradecancelled(target_sd); +} + +/*========================================== + * 取引許諾(trade押し) + *------------------------------------------ + */ +void trade_tradecommit(struct map_session_data *sd) { + struct map_session_data *tsd; + int trade_i; + int flag; + + if (!sd->state.trading || !sd->state.deal_locked) //Locked should be 1 (pressed ok) before you can press trade. + return; + + if ((tsd = map_id2sd(sd->trade_partner)) == NULL) { + trade_tradecancel(sd); + return; + } + + sd->state.deal_locked = 2; + + if (tsd->state.deal_locked < 2) + return; //Not yet time for trading. + + //Now is a good time (to save on resources) to check that the trade can indeed be made and it's not exploitable. + // check exploit (trade more items that you have) + if (impossible_trade_check(sd)) { + trade_tradecancel(sd); + return; + } + // check exploit (trade more items that you have) + if (impossible_trade_check(tsd)) { + trade_tradecancel(tsd); + return; + } + // check for full inventory (can not add traded items) + if (!trade_check(sd,tsd)) { // check the both players + trade_tradecancel(sd); + return; + } + + // trade is accepted and correct. + for(trade_i = 0; trade_i < 10; trade_i++) { + int n; + if (sd->deal.item[trade_i].amount) { + n = sd->deal.item[trade_i].index; + + flag = pc_additem(tsd, &sd->status.inventory[n], sd->deal.item[trade_i].amount); + if (flag == 0) { + //Logs (T)rade [Lupus] + if(log_config.enable_logs&0x2) { + log_pick_pc(sd, "T", sd->status.inventory[n].nameid, -(sd->deal.item[trade_i].amount), &sd->status.inventory[n]); + log_pick_pc(tsd, "T", sd->status.inventory[n].nameid, sd->deal.item[trade_i].amount, &sd->status.inventory[n]); + } + //Logs + pc_delitem(sd, n, sd->deal.item[trade_i].amount, 1); + } else + clif_additem(sd, n, sd->deal.item[trade_i].amount, 0); + sd->deal.item[trade_i].index = 0; + sd->deal.item[trade_i].amount = 0; + } + if (tsd->deal.item[trade_i].amount) { + n = tsd->deal.item[trade_i].index; + + flag = pc_additem(sd, &tsd->status.inventory[n], tsd->deal.item[trade_i].amount); + if (flag == 0) { + //Logs (T)rade [Lupus] + if(log_config.enable_logs&0x2) { + log_pick_pc(tsd, "T", tsd->status.inventory[n].nameid, -(tsd->deal.item[trade_i].amount), &tsd->status.inventory[n]); + log_pick_pc(sd, "T", tsd->status.inventory[n].nameid, tsd->deal.item[trade_i].amount, &tsd->status.inventory[n]); + } + //Logs + pc_delitem(tsd, n, tsd->deal.item[trade_i].amount, 1); + } else + clif_additem(tsd, n, tsd->deal.item[trade_i].amount, 0); + tsd->deal.item[trade_i].index = 0; + tsd->deal.item[trade_i].amount = 0; + } + } + if (sd->deal.zeny || tsd->deal.zeny) { + if (sd->deal.zeny) { + sd->status.zeny -= sd->deal.zeny; + tsd->status.zeny += sd->deal.zeny; + if (log_config.zeny) + log_zeny(tsd, "T", sd, sd->deal.zeny);//Logs Zeny (T)rade [Lupus] + sd->deal.zeny = 0; + } + if (tsd->deal.zeny) { + tsd->status.zeny -= tsd->deal.zeny; + sd->status.zeny += tsd->deal.zeny; + if (log_config.zeny) + log_zeny(sd, "T", tsd, tsd->deal.zeny);//Logs Zeny (T)rade [Lupus] + tsd->deal.zeny = 0; + } + clif_updatestatus(sd, SP_ZENY); + clif_updatestatus(tsd, SP_ZENY); + } + + sd->state.deal_locked = 0; + sd->trade_partner = 0; + sd->state.trading = 0; + + tsd->state.deal_locked = 0; + tsd->trade_partner = 0; + tsd->state.trading = 0; + + clif_tradecompleted(sd, 0); + clif_tradecompleted(tsd, 0); + // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players + if (save_settings&1) + { + chrif_save(sd,0); + chrif_save(tsd,0); + } +} diff --git a/src/map/trade.h b/src/map/trade.h index a695a50e8..bcd609271 100644 --- a/src/map/trade.h +++ b/src/map/trade.h @@ -1,15 +1,15 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _TRADE_H_ -#define _TRADE_H_ - -#include "map.h" -void trade_traderequest(struct map_session_data *sd, struct map_session_data *target_sd); -void trade_tradeack(struct map_session_data *sd,int type); -void trade_tradeadditem(struct map_session_data *sd,int index,int amount); -void trade_tradeok(struct map_session_data *sd); -void trade_tradecancel(struct map_session_data *sd); -void trade_tradecommit(struct map_session_data *sd); - -#endif // _TRADE_H_ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _TRADE_H_ +#define _TRADE_H_ + +#include "map.h" +void trade_traderequest(struct map_session_data *sd, struct map_session_data *target_sd); +void trade_tradeack(struct map_session_data *sd,int type); +void trade_tradeadditem(struct map_session_data *sd,int index,int amount); +void trade_tradeok(struct map_session_data *sd); +void trade_tradecancel(struct map_session_data *sd); +void trade_tradecommit(struct map_session_data *sd); + +#endif // _TRADE_H_ diff --git a/src/map/vending.c b/src/map/vending.c index 0f8ceaa30..9b4b797e0 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -1,267 +1,267 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include - -#include "../common/nullpo.h" -#include "clif.h" -#include "itemdb.h" -#include "atcommand.h" -#include "map.h" -#include "chrif.h" -#include "vending.h" -#include "pc.h" -#include "skill.h" -#include "battle.h" -#include "log.h" - -#include "irc.h" - -/*========================================== - * 露店閉鎖 - *------------------------------------------ -*/ -void vending_closevending(struct map_session_data *sd) -{ - nullpo_retv(sd); - - sd->vender_id=0; - clif_closevendingboard(&sd->bl,0); - if(use_irc && irc_announce_shop_flag) - irc_announce_shop(sd,0); -} - -/*========================================== - * 露店アイテムリスト要求 - *------------------------------------------ - */ -void vending_vendinglistreq(struct map_session_data *sd,int id) -{ - struct map_session_data *vsd; - - nullpo_retv(sd); - - if( (vsd=map_id2sd(id)) == NULL ) - return; - if(vsd->vender_id==0) - return; - clif_vendinglist(sd,id,vsd->vending); -} - -/*========================================== - * 露店アイテム購入 - *------------------------------------------ - */ -void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p) -{ - int i, j, w, new_ = 0, blank, vend_list[MAX_VENDING]; - double z; - unsigned short amount; - short idx; - struct map_session_data *vsd = map_id2sd(id); - struct vending vending[MAX_VENDING]; // against duplicate packets - - nullpo_retv(sd); - - if (vsd == NULL) - return; - if (vsd->vender_id == 0) - return; - if (vsd->vender_id == sd->bl.id) - return; - - // check number of buying items - if (len < 8 + 4 || len > 8 + 4 * MAX_VENDING) { - clif_buyvending(sd, 0, 32767, 4); // not enough quantity (index and amount are unknown) - return; - } - - blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory - - // duplicate item in vending to check hacker with multiple packets - memcpy(&vending, &vsd->vending, sizeof(struct vending) * MAX_VENDING); // copy vending list - - // some checks - z = 0.; - w = 0; - for(i = 0; 8 + 4 * i < len; i++) { - amount = *(unsigned short*)(p + 4 * i); - idx = *(short*)(p + 2 + 4 * i) - 2; - - if (amount <= 0) - return; - - // check of item index in the cart - if (idx < 0 || idx >= MAX_CART) - return; - - for(j = 0; j < vsd->vend_num; j++) { - if (vsd->vending[j].index == idx) { - vend_list[i] = j; - break; - } - } - if (j == vsd->vend_num) - return; //picked non-existing item - - z += ((double)vsd->vending[j].value * (double)amount); - if (z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY) { // fix positiv overflow (buyer) - clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny - return; // zeny s'< - } - if (z + (double)vsd->status.zeny > (double)MAX_ZENY) { // fix positiv overflow (merchand) - clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow - return; // zeny s'< - } - w += itemdb_weight(vsd->status.cart[idx].nameid) * amount; - if (w + sd->weight > sd->max_weight) { - clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight - return; - } - - if (vending[j].amount > vsd->status.cart[idx].amount) //Check to see if cart/vend info is in sync. - vending[j].amount = vsd->status.cart[idx].amount; - - // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples). - // here, we check cumulativ amounts - if (vending[j].amount < amount) { - // send more quantity is not a hack (an other player can have buy items just before) - clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity - return; - } else - vending[j].amount -= amount; - - switch(pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount)) { - case ADDITEM_EXIST: - break; //We'd add this item to the existing one (in buyers inventory) - case ADDITEM_NEW: - new_++; - if (new_ > blank) - return; //Buyer has no space in his inventory - break; - case ADDITEM_OVERAMOUNT: - return; //too many items - } - } - - //Logs (V)ending Zeny [Lupus] - if(log_config.zeny > 0 ) - log_zeny(vsd, "V", sd, (int)z); - //Logs - - pc_payzeny(sd, (int)z); - pc_getzeny(vsd, (int)z); - - for(i = 0; 8 + 4 * i < len; i++) { - amount = *(short*)(p + 4 *i); - idx = *(short*)(p + 2 + 4 * i) - 2; - //if (amount < 0) break; // tested at start of the function - - //Logs sold (V)ending items [Lupus] - if(log_config.enable_logs&0x4) { - log_pick_pc(vsd, "V", vsd->status.cart[idx].nameid, -amount, (struct item*)&vsd->status.cart[idx]); - log_pick_pc( sd, "V", vsd->status.cart[idx].nameid, amount, (struct item*)&vsd->status.cart[idx]); - } - //Logs - - // vending item - pc_additem(sd, &vsd->status.cart[idx], amount); - vsd->vending[vend_list[i]].amount -= amount; - pc_cart_delitem(vsd, idx, amount, 0); - clif_vendingreport(vsd, idx, amount); - - //print buyer's name - if(battle_config.buyer_name) { - char temp[256]; - sprintf(temp, msg_txt(265), sd->status.name); - clif_disp_onlyself(vsd,temp,strlen(temp)); - } - } - - //Always save BOTH: buyer and customer - if (save_settings&2) { - chrif_save(sd,0); - chrif_save(vsd,0); - } - //check for @AUTOTRADE users [durf] - if (vsd->state.autotrade) - { - //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex] - for(i = 0; i < vsd->vend_num && vsd->vending[i].amount < 1; i++); - if (i == vsd->vend_num) - { - vending_closevending(vsd); - map_quit(vsd); //They have no reason to stay around anymore, do they? - } - } -} - -/*========================================== - * 露店開設 - *------------------------------------------ - */ -void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p) -{ - int i, j; - int vending_skill_lvl; - nullpo_retv(sd); - - if (map[sd->bl.m].flag.novending) { - clif_displaymessage (sd->fd, msg_txt(276)); - return; //Can't vend in novending mapflag maps. - } - - //check shopname len - if(message[0] == '\0') - return; - - vending_skill_lvl = pc_checkskill(sd, MC_VENDING); - if(!vending_skill_lvl || !pc_iscarton(sd)) { // cart skill and cart check [Valaris] - clif_skill_fail(sd,MC_VENDING,0,0); - return; - } - - if (flag) { - // check number of items in shop - if (len < 85 + 8 || len > 85 + 8 * MAX_VENDING || len > 85 + 8 * (2 + vending_skill_lvl)) { - clif_skill_fail(sd, MC_VENDING, 0, 0); - return; - } - for(i = 0, j = 0; (85 + 8 * j < len) && (i < MAX_VENDING); i++, j++) { - sd->vending[i].index = *(short*)(p+8*j)-2; - if (sd->vending[i].index < 0 || sd->vending[i].index >= MAX_CART || - !itemdb_cantrade(&sd->status.cart[sd->vending[i].index], pc_isGM(sd), pc_isGM(sd))) - { - i--; //Preserve the vending index, skip to the next item. - continue; - } - sd->vending[i].amount = *(short*)(p+2+8*j); - sd->vending[i].value = *(int*)(p+4+8*j); - if(sd->vending[i].value > battle_config.vending_max_value) - sd->vending[i].value=battle_config.vending_max_value; - else if(sd->vending[i].value < 1) - sd->vending[i].value = 1000000; // auto set to 1 million [celest] - // カート内のアイテム数と販売するアイテム数に相違があったら中止 - if(pc_cartitem_amount(sd, sd->vending[i].index, sd->vending[i].amount) < 0) { // fixes by Valaris and fritz - clif_skill_fail(sd, MC_VENDING, 0, 0); - return; - } - } - if (i != j) - { //Some items were not vended. [Skotlex] - clif_displaymessage (sd->fd, msg_txt(266)); - } - sd->vender_id = sd->bl.id; - sd->vend_num = i; - memcpy(sd->message,message, MESSAGE_SIZE-1); - if (clif_openvending(sd,sd->vender_id,sd->vending) > 0){ - pc_stop_walking(sd,1); - clif_showvendingboard(&sd->bl,message,0); - if(use_irc && irc_announce_shop_flag) - irc_announce_shop(sd,1); - } else - sd->vender_id = 0; - } -} - +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include + +#include "../common/nullpo.h" +#include "clif.h" +#include "itemdb.h" +#include "atcommand.h" +#include "map.h" +#include "chrif.h" +#include "vending.h" +#include "pc.h" +#include "skill.h" +#include "battle.h" +#include "log.h" + +#include "irc.h" + +/*========================================== + * 露店閉鎖 + *------------------------------------------ +*/ +void vending_closevending(struct map_session_data *sd) +{ + nullpo_retv(sd); + + sd->vender_id=0; + clif_closevendingboard(&sd->bl,0); + if(use_irc && irc_announce_shop_flag) + irc_announce_shop(sd,0); +} + +/*========================================== + * 露店アイテムリスト要求 + *------------------------------------------ + */ +void vending_vendinglistreq(struct map_session_data *sd,int id) +{ + struct map_session_data *vsd; + + nullpo_retv(sd); + + if( (vsd=map_id2sd(id)) == NULL ) + return; + if(vsd->vender_id==0) + return; + clif_vendinglist(sd,id,vsd->vending); +} + +/*========================================== + * 露店アイテム購入 + *------------------------------------------ + */ +void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p) +{ + int i, j, w, new_ = 0, blank, vend_list[MAX_VENDING]; + double z; + unsigned short amount; + short idx; + struct map_session_data *vsd = map_id2sd(id); + struct vending vending[MAX_VENDING]; // against duplicate packets + + nullpo_retv(sd); + + if (vsd == NULL) + return; + if (vsd->vender_id == 0) + return; + if (vsd->vender_id == sd->bl.id) + return; + + // check number of buying items + if (len < 8 + 4 || len > 8 + 4 * MAX_VENDING) { + clif_buyvending(sd, 0, 32767, 4); // not enough quantity (index and amount are unknown) + return; + } + + blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory + + // duplicate item in vending to check hacker with multiple packets + memcpy(&vending, &vsd->vending, sizeof(struct vending) * MAX_VENDING); // copy vending list + + // some checks + z = 0.; + w = 0; + for(i = 0; 8 + 4 * i < len; i++) { + amount = *(unsigned short*)(p + 4 * i); + idx = *(short*)(p + 2 + 4 * i) - 2; + + if (amount <= 0) + return; + + // check of item index in the cart + if (idx < 0 || idx >= MAX_CART) + return; + + for(j = 0; j < vsd->vend_num; j++) { + if (vsd->vending[j].index == idx) { + vend_list[i] = j; + break; + } + } + if (j == vsd->vend_num) + return; //picked non-existing item + + z += ((double)vsd->vending[j].value * (double)amount); + if (z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY) { // fix positiv overflow (buyer) + clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny + return; // zeny s'< + } + if (z + (double)vsd->status.zeny > (double)MAX_ZENY) { // fix positiv overflow (merchand) + clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow + return; // zeny s'< + } + w += itemdb_weight(vsd->status.cart[idx].nameid) * amount; + if (w + sd->weight > sd->max_weight) { + clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight + return; + } + + if (vending[j].amount > vsd->status.cart[idx].amount) //Check to see if cart/vend info is in sync. + vending[j].amount = vsd->status.cart[idx].amount; + + // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples). + // here, we check cumulativ amounts + if (vending[j].amount < amount) { + // send more quantity is not a hack (an other player can have buy items just before) + clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity + return; + } else + vending[j].amount -= amount; + + switch(pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount)) { + case ADDITEM_EXIST: + break; //We'd add this item to the existing one (in buyers inventory) + case ADDITEM_NEW: + new_++; + if (new_ > blank) + return; //Buyer has no space in his inventory + break; + case ADDITEM_OVERAMOUNT: + return; //too many items + } + } + + //Logs (V)ending Zeny [Lupus] + if(log_config.zeny > 0 ) + log_zeny(vsd, "V", sd, (int)z); + //Logs + + pc_payzeny(sd, (int)z); + pc_getzeny(vsd, (int)z); + + for(i = 0; 8 + 4 * i < len; i++) { + amount = *(short*)(p + 4 *i); + idx = *(short*)(p + 2 + 4 * i) - 2; + //if (amount < 0) break; // tested at start of the function + + //Logs sold (V)ending items [Lupus] + if(log_config.enable_logs&0x4) { + log_pick_pc(vsd, "V", vsd->status.cart[idx].nameid, -amount, (struct item*)&vsd->status.cart[idx]); + log_pick_pc( sd, "V", vsd->status.cart[idx].nameid, amount, (struct item*)&vsd->status.cart[idx]); + } + //Logs + + // vending item + pc_additem(sd, &vsd->status.cart[idx], amount); + vsd->vending[vend_list[i]].amount -= amount; + pc_cart_delitem(vsd, idx, amount, 0); + clif_vendingreport(vsd, idx, amount); + + //print buyer's name + if(battle_config.buyer_name) { + char temp[256]; + sprintf(temp, msg_txt(265), sd->status.name); + clif_disp_onlyself(vsd,temp,strlen(temp)); + } + } + + //Always save BOTH: buyer and customer + if (save_settings&2) { + chrif_save(sd,0); + chrif_save(vsd,0); + } + //check for @AUTOTRADE users [durf] + if (vsd->state.autotrade) + { + //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex] + for(i = 0; i < vsd->vend_num && vsd->vending[i].amount < 1; i++); + if (i == vsd->vend_num) + { + vending_closevending(vsd); + map_quit(vsd); //They have no reason to stay around anymore, do they? + } + } +} + +/*========================================== + * 露店開設 + *------------------------------------------ + */ +void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p) +{ + int i, j; + int vending_skill_lvl; + nullpo_retv(sd); + + if (map[sd->bl.m].flag.novending) { + clif_displaymessage (sd->fd, msg_txt(276)); + return; //Can't vend in novending mapflag maps. + } + + //check shopname len + if(message[0] == '\0') + return; + + vending_skill_lvl = pc_checkskill(sd, MC_VENDING); + if(!vending_skill_lvl || !pc_iscarton(sd)) { // cart skill and cart check [Valaris] + clif_skill_fail(sd,MC_VENDING,0,0); + return; + } + + if (flag) { + // check number of items in shop + if (len < 85 + 8 || len > 85 + 8 * MAX_VENDING || len > 85 + 8 * (2 + vending_skill_lvl)) { + clif_skill_fail(sd, MC_VENDING, 0, 0); + return; + } + for(i = 0, j = 0; (85 + 8 * j < len) && (i < MAX_VENDING); i++, j++) { + sd->vending[i].index = *(short*)(p+8*j)-2; + if (sd->vending[i].index < 0 || sd->vending[i].index >= MAX_CART || + !itemdb_cantrade(&sd->status.cart[sd->vending[i].index], pc_isGM(sd), pc_isGM(sd))) + { + i--; //Preserve the vending index, skip to the next item. + continue; + } + sd->vending[i].amount = *(short*)(p+2+8*j); + sd->vending[i].value = *(int*)(p+4+8*j); + if(sd->vending[i].value > battle_config.vending_max_value) + sd->vending[i].value=battle_config.vending_max_value; + else if(sd->vending[i].value < 1) + sd->vending[i].value = 1000000; // auto set to 1 million [celest] + // カート内のアイテム数と販売するアイテム数に相違があったら中止 + if(pc_cartitem_amount(sd, sd->vending[i].index, sd->vending[i].amount) < 0) { // fixes by Valaris and fritz + clif_skill_fail(sd, MC_VENDING, 0, 0); + return; + } + } + if (i != j) + { //Some items were not vended. [Skotlex] + clif_displaymessage (sd->fd, msg_txt(266)); + } + sd->vender_id = sd->bl.id; + sd->vend_num = i; + memcpy(sd->message,message, MESSAGE_SIZE-1); + if (clif_openvending(sd,sd->vender_id,sd->vending) > 0){ + pc_stop_walking(sd,1); + clif_showvendingboard(&sd->bl,message,0); + if(use_irc && irc_announce_shop_flag) + irc_announce_shop(sd,1); + } else + sd->vender_id = 0; + } +} + diff --git a/src/map/vending.h b/src/map/vending.h index 021866d25..f4014a894 100644 --- a/src/map/vending.h +++ b/src/map/vending.h @@ -1,14 +1,14 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#ifndef _VENDING_H_ -#define _VENDING_H_ - -#include "map.h" - -void vending_closevending(struct map_session_data *sd); -void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p); -void vending_vendinglistreq(struct map_session_data *sd,int id); -void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p); - -#endif // _VENDING_H_ +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#ifndef _VENDING_H_ +#define _VENDING_H_ + +#include "map.h" + +void vending_closevending(struct map_session_data *sd); +void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p); +void vending_vendinglistreq(struct map_session_data *sd,int id); +void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p); + +#endif // _VENDING_H_ diff --git a/src/plugins/gui.c b/src/plugins/gui.c index 6c9a8d85c..e8c097d21 100644 --- a/src/plugins/gui.c +++ b/src/plugins/gui.c @@ -1,101 +1,101 @@ - -#include -#include -#include -#include "../common/plugin.h" -//Needed for strcmpi -#include "../common/mmo.h" - -// "I'm Alive" and "Flush stdout" Originally by Mugendai -// Ported to plugin by Celest - -PLUGIN_INFO = { - "AthenaGUI", - PLUGIN_CORE, - "1.0", - PLUGIN_VERSION, - "Core plugin for Athena GUI functions" -}; - -PLUGIN_EVENTS_TABLE = { - { "gui_init", "Plugin_Init" }, - { NULL, NULL } -}; - -unsigned int (*gettick)(); -int (*add_timer_func_list)(int (*)(int,unsigned int,int,int),char*); -int (*add_timer_interval)(unsigned int,int (*)(int,unsigned int,int,int),int,int,int); - -//----------------------------------------------------- -//I'm Alive Alert -//Used to output 'I'm Alive' every few seconds -//Intended to let frontends know if the app froze -//----------------------------------------------------- -int imalive_timer(int tid, unsigned int tick, int id, int data){ - printf("I'm Alive\n"); - return 0; -} - -//----------------------------------------------------- -//Flush stdout -//stdout buffer needs flushed to be seen in GUI -//----------------------------------------------------- -int flush_timer(int tid, unsigned int tick, int id, int data){ - fflush(stdout); - return 0; -} - -void gui_init () -{ - char line[1024], w1[1024], w2[1024]; - int flush_on = 0; - int flush_time = 100; - int imalive_on = 0; - int imalive_time = 30; - char **argv; - int *argc; - FILE *fp; - int i; - - IMPORT_SYMBOL(argc, 2); - IMPORT_SYMBOL(argv, 3); - IMPORT_SYMBOL(gettick, 5); - IMPORT_SYMBOL(add_timer_interval, 8); - IMPORT_SYMBOL(add_timer_func_list, 9); - - do { - fp = fopen("plugins/gui.conf","r"); - if (fp == NULL) - break; - - while(fgets(line, sizeof(line) -1, fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { - if(strcmpi(w1,"imalive_on")==0){ - imalive_on = atoi(w2); - } else if(strcmpi(w1,"imalive_time")==0){ - imalive_time = atoi(w2); - } else if(strcmpi(w1,"flush_on")==0){ - flush_on = atoi(w2); - } else if(strcmpi(w1,"flush_time")==0){ - flush_time = atoi(w2); - } - } - } - fclose(fp); - } while (0); - - for (i = 1; i < *argc ; i++) - if (strcmp(argv[i], "--gui") == 0) - flush_on = imalive_on = 1; - - if (flush_on) { - add_timer_func_list(flush_timer, "flush_timer"); - add_timer_interval(gettick()+1000,flush_timer,0,0,flush_time); - } - if (imalive_on) { - add_timer_func_list(imalive_timer, "imalive_timer"); - add_timer_interval(gettick()+10, imalive_timer,0,0,imalive_time*1000); - } -} + +#include +#include +#include +#include "../common/plugin.h" +//Needed for strcmpi +#include "../common/mmo.h" + +// "I'm Alive" and "Flush stdout" Originally by Mugendai +// Ported to plugin by Celest + +PLUGIN_INFO = { + "AthenaGUI", + PLUGIN_CORE, + "1.0", + PLUGIN_VERSION, + "Core plugin for Athena GUI functions" +}; + +PLUGIN_EVENTS_TABLE = { + { "gui_init", "Plugin_Init" }, + { NULL, NULL } +}; + +unsigned int (*gettick)(); +int (*add_timer_func_list)(int (*)(int,unsigned int,int,int),char*); +int (*add_timer_interval)(unsigned int,int (*)(int,unsigned int,int,int),int,int,int); + +//----------------------------------------------------- +//I'm Alive Alert +//Used to output 'I'm Alive' every few seconds +//Intended to let frontends know if the app froze +//----------------------------------------------------- +int imalive_timer(int tid, unsigned int tick, int id, int data){ + printf("I'm Alive\n"); + return 0; +} + +//----------------------------------------------------- +//Flush stdout +//stdout buffer needs flushed to be seen in GUI +//----------------------------------------------------- +int flush_timer(int tid, unsigned int tick, int id, int data){ + fflush(stdout); + return 0; +} + +void gui_init () +{ + char line[1024], w1[1024], w2[1024]; + int flush_on = 0; + int flush_time = 100; + int imalive_on = 0; + int imalive_time = 30; + char **argv; + int *argc; + FILE *fp; + int i; + + IMPORT_SYMBOL(argc, 2); + IMPORT_SYMBOL(argv, 3); + IMPORT_SYMBOL(gettick, 5); + IMPORT_SYMBOL(add_timer_interval, 8); + IMPORT_SYMBOL(add_timer_func_list, 9); + + do { + fp = fopen("plugins/gui.conf","r"); + if (fp == NULL) + break; + + while(fgets(line, sizeof(line) -1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { + if(strcmpi(w1,"imalive_on")==0){ + imalive_on = atoi(w2); + } else if(strcmpi(w1,"imalive_time")==0){ + imalive_time = atoi(w2); + } else if(strcmpi(w1,"flush_on")==0){ + flush_on = atoi(w2); + } else if(strcmpi(w1,"flush_time")==0){ + flush_time = atoi(w2); + } + } + } + fclose(fp); + } while (0); + + for (i = 1; i < *argc ; i++) + if (strcmp(argv[i], "--gui") == 0) + flush_on = imalive_on = 1; + + if (flush_on) { + add_timer_func_list(flush_timer, "flush_timer"); + add_timer_interval(gettick()+1000,flush_timer,0,0,flush_time); + } + if (imalive_on) { + add_timer_func_list(imalive_timer, "imalive_timer"); + add_timer_interval(gettick()+10, imalive_timer,0,0,imalive_time*1000); + } +} diff --git a/src/plugins/gui.txt b/src/plugins/gui.txt index 87f5ac742..03554aa8d 100644 --- a/src/plugins/gui.txt +++ b/src/plugins/gui.txt @@ -1,15 +1,15 @@ -// -// GUI Plugin Configuration -// - -// Enable I'm Alive? -imalive_on: 0 - -// How often to display I'm Alive (in seconds) -imalive_time: 30 - -// Enable GUI flushing for Mugendai's GUI? -flush_on: 0 - -// How often to flush the buffer on-screen (in seconds) +// +// GUI Plugin Configuration +// + +// Enable I'm Alive? +imalive_on: 0 + +// How often to display I'm Alive (in seconds) +imalive_time: 30 + +// Enable GUI flushing for Mugendai's GUI? +flush_on: 0 + +// How often to flush the buffer on-screen (in seconds) flush_time: 60 \ No newline at end of file diff --git a/src/plugins/httpd.c b/src/plugins/httpd.c index de994766f..5e88c7dfd 100644 --- a/src/plugins/httpd.c +++ b/src/plugins/httpd.c @@ -1,751 +1,751 @@ - -#ifdef __WIN32 -#include -#else -#include -#endif -#include -#include -#include -#include - -#include "../common/db.h" -//mmo required for definition of stricmp -#include "../common/mmo.h" -#include "../common/utils.h" -#include "../common/malloc.h" -#include "../common/socket.h" -#include "../common/plugin.h" -//#include "httpd.h" - -/** Created by End_of_Exam, ported to plugin and modified by Celest **/ - -PLUGIN_INFO = { - "HttpDaemon", - PLUGIN_CORE, - "0.1", - PLUGIN_VERSION, - "HTTP Daemon" -}; - -PLUGIN_EVENTS_TABLE = { - { "do_init", "Plugin_Init" }, - { "do_final", "Plugin_Final" }, - { NULL, NULL } -}; - -enum HTTPD_STATUS { - HTTPD_REQUEST_WAIT = 0, // リクエスト待ち - HTTPD_REQUEST_WAIT_POST, // リクエスト待ち(post) - HTTPD_REQUEST_OK, // リクエスト解釈完了 - HTTPD_SEND_HEADER, // ヘッダ送信完了 - HTTPD_WAITING_SEND // データが送信し終わるまで待っている状態 -}; -enum { - HTTPD_METHOD_UNKNOWN = 0, - HTTPD_METHOD_GET, - HTTPD_METHOD_POST -}; - -struct httpd_session_data { - int fd; - int status; - int http_ver; - int header_len; - int data_len; - int method; - int persist; - int request_count; - unsigned int tick; - const unsigned char* url; - const unsigned char* query; -}; - -// undefine socket operations included from socket.h, -// since we are going to use 'sessiond' instead -#undef RFIFOP -#undef RFIFOREST -#undef WFIFOP - -#define RFIFOP(fd,pos) (sessiond[fd]->rdata+sessiond[fd]->rdata_pos+(pos)) -#define RFIFOREST(fd) (sessiond[fd]->rdata_size-sessiond[fd]->rdata_pos) -#define WFIFOP(fd,pos) (sessiond[fd]->wdata+sessiond[fd]->wdata_size+(pos)) - -struct socket_data **sessiond; -char *server_type; -unsigned int (*gettick)(); -int (*add_timer_interval)(unsigned int,int (*)(int,unsigned int,int,int),int,int,int); -int *max_fd; -int (*delete_sessiond)(int); -int (*_WFIFOSET)(int,int); -int (*_RFIFOSKIP)(int,int); - -static int max_persist_requests = 32; // 持続通信での最大リクエスト数 -static int request_timeout[] = { 2500, 60*1000 }; // タイムアウト(最初、持続) -static char document_root[256] = "./httpd/"; // ドキュメントルート - -// httpd に入っているページと、呼び出すコールバック関数の一覧 -struct dbt *httpd_files; - -void httpd_send(struct httpd_session_data*, int, const char *, int, const void *); - -int httpd_check (struct socket_data *sd) -{ - // httpd に回すどうかの判定がまだ行われてない - // 先頭2バイトが GE ならhttpd に回してみる - if (sd->rdata_size >= 2 && - sd->rdata[0] == 'G' && sd->rdata[1] == 'E') - return 1; - - return 0; -} - -int httpd_strcasencmp(const char *s1, const char *s2,int len) -{ - while(len-- && (*s1 || *s2) ) { - if((*s1 | 0x20) != (*s2 | 0x20)) { - return ((*s1 | 0x20) > (*s2 | 0x20) ? 1 : -1); - } - s1++; s2++; - } - return 0; -} - -// httpd にページを追加する -// for などでページ名を合成できるように、key はstrdup()したものを使う - -void httpd_pages (const char* url, void (*httpd_func)(struct httpd_session_data*, const char*)) -{ - if (strdb_get(httpd_files,(unsigned char*)(url+1)) == NULL) { - strdb_put(httpd_files, (unsigned char*)aStrdup(url+1), httpd_func); - } else { - strdb_put(httpd_files, (unsigned char*)(url+1), httpd_func); - } -} - -static void (*httpd_default)(struct httpd_session_data* sd,const char* url); - -const char *httpd_get_error( struct httpd_session_data* sd, int* status ) -{ - const char* msg; - // httpd のステータスを決める - switch(*status) { - case 200: msg = "OK"; break; - case 400: msg = "Bad Request"; break; - case 401: msg = "Unauthorized"; break; // 未使用 - case 403: msg = "Forbidden"; break; // 未使用 - case 404: msg = "Not Found"; break; - case 408: msg = "Request Timedout"; break; - case 411: msg = "Length Required"; break; - case 413: msg = "Request Entity Too Large"; break; - default: - *status = 500; msg = "Internal Server Error"; break; - } - return msg; -} - -void httpd_send_error(struct httpd_session_data* sd,int status) -{ - const char* msg = httpd_get_error( sd, &status ); - httpd_send(sd, status, "text/plain",strlen(msg),msg); -} - -void httpd_send_head (struct httpd_session_data* sd, int status, const char *content_type, int content_len) -{ - char head[256]; - int len; - const char* msg; - - if (sd->status != HTTPD_REQUEST_OK) - return; - msg = httpd_get_error( sd, &status ); - - if(content_len == -1 || ++sd->request_count >= max_persist_requests ) { - // 長さが分からない or リクエスト限界を超えたので切断する - len = sprintf( - head, - "HTTP/1.%d %d %s\r\nContent-Type: %s\r\nConnection: close\r\n\r\n", - sd->http_ver,status,msg,content_type - ); - sd->persist = 0; - len = sprintf( - head, - "HTTP/1.%d %d %s\r\nContent-Type: %s\r\n\r\n", - sd->http_ver,status,msg,content_type - ); - sd->http_ver = 0; // 長さが分からないので、HTTP/1.0 扱い(自動切断)にする - } else { - len = sprintf( - head, - "HTTP/1.%d %d %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n", - sd->http_ver,status,msg,content_type,content_len - ); - } - memcpy(WFIFOP(sd->fd,0),head,len); - _WFIFOSET(sd->fd,len); - sd->status = HTTPD_SEND_HEADER; - sd->data_len = content_len; -} - -void httpd_send_data (struct httpd_session_data* sd, int content_len, const void *data) -{ - const char* msg = (const char*)data; - if (sd->status == HTTPD_REQUEST_OK) { - // ヘッダの送信忘れているので、適当に補う - httpd_send_head(sd,200,"application/octet-stream",-1); - } else if(sd->status != HTTPD_SEND_HEADER && sd->status != HTTPD_WAITING_SEND) { - return; - } - sd->data_len -= content_len; - - // 巨大なサイズのファイルも送信出来るように分割して送る - while (content_len > 0) { - int send_byte = content_len; - if(send_byte > 12*1024) send_byte = 12*1024; - memcpy(WFIFOP(sd->fd,0),msg,send_byte); - _WFIFOSET(sd->fd,send_byte); - msg += send_byte; content_len -= send_byte; - } - sd->status = HTTPD_WAITING_SEND; -} - -void httpd_send (struct httpd_session_data* sd, int status, const char *content_type, int content_len, const void *data) -{ - httpd_send_head(sd,status,content_type,content_len); - httpd_send_data(sd,content_len,data); -} - -void httpd_parse_header(struct httpd_session_data* sd); -void httpd_parse_request_ok(struct httpd_session_data *sd); - -int httpd_parse (int fd) -{ - struct httpd_session_data *sd = (struct httpd_session_data *)sessiond[fd]->session_data2; - if (sessiond[fd]->eof) { - delete_sessiond(fd); - return 0; - } - if (sd == NULL) { - sd = (struct httpd_session_data*) aMalloc (sizeof(struct httpd_session_data)); - sd->fd = fd; - sessiond[fd]->session_data2 = sd; - sd->tick = gettick(); - sd->persist = 0; - sd->request_count = 0; - } - printf ("status %d\n", sd->status); - switch(sd->status) { - case HTTPD_REQUEST_WAIT: - // リクエスト待ち - if(RFIFOREST(fd) > 1024) { - // リクエストが長すぎるので、エラー扱いする - sd->status = HTTPD_REQUEST_OK; - httpd_send_error(sd,400); // Bad Request - } else if( (int)( gettick() - sd->tick ) > request_timeout[sd->persist] ) { - // リクエストに時間がかかりすぎているので、エラー扱いする - sd->status = HTTPD_REQUEST_OK; - httpd_send_error(sd,408); // Request Timeout - } else if(sd->header_len == RFIFOREST(fd)) { - // 状態が以前と同じなので、リクエストを再解析する必要は無い - } else { - int limit = RFIFOREST(fd); - unsigned char *req = RFIFOP(fd,0); - sd->header_len = RFIFOREST(fd); - do { - if(*req == '\n' && limit > 0) { - limit--; req++; - if(*req == '\r' && limit > 0) { limit--; req++; } - if(*req == '\n') { - // HTTPヘッダの終点を見つけた - *req = 0; - sd->header_len = (req - RFIFOP(fd,0)) + 1; - httpd_parse_header(sd); - break; - } - } - } while(req++,--limit > 0); - } - break; - case HTTPD_REQUEST_WAIT_POST: - if(RFIFOREST(sd->fd) >= sd->header_len) { - unsigned char temp = RFIFOB(sd->fd,sd->header_len); - RFIFOB(sd->fd,sd->header_len) = 0; - httpd_parse_request_ok(sd); - RFIFOB(sd->fd,sd->header_len) = temp; - } - break; - case HTTPD_REQUEST_OK: - case HTTPD_SEND_HEADER: - // リクエストが終わったまま何も送信されていない状態なので、 - // 強制切断 - printf ("httpd: eof\n"); - sessiond[fd]->eof = 1; - break; - case HTTPD_WAITING_SEND: - // データの送信が終わるまで待機 - //if(sessiond[fd]->wdata_size == sessiond[fd]->wdata_pos) { - // i *hope* this is correct o.o; - if(sessiond[fd]->wdata_size == 0) { - // HTTP/1.0は手動切断 -// if(sd->http_ver == 0) { - if(sd->persist == 0) { - printf ("httpd: eof\n"); - sessiond[fd]->eof = 1; - } - // RFIFO からリクエストデータの消去と構造体の初期化 - _RFIFOSKIP(fd,sd->header_len); - sd->status = HTTPD_REQUEST_WAIT; - sd->tick = gettick(); - sd->header_len = 0; - sd->query = NULL; -// sd->http_ver = 0; // ver は保持 - sd->method = HTTPD_METHOD_UNKNOWN; - printf("httpd_parse: [% 3d] request sended RFIFOREST:%d\n", fd, RFIFOREST(fd)); - } - break; - } - return 0; -} - -void httpd_parse_header_sub( struct httpd_session_data* sd, const char *p1, int* plen ) -{ - int len = 0; - // HTTPのバージョンを調査 - if(!strncmp(p1 ,"HTTP/1.1",8)) { - sd->http_ver = 1; - sd->persist = 1; - } else { - sd->http_ver = 0; - sd->persist = 0; - } - - p1 = strchr(p1,'\n'); - while(p1) { - // Content-Length: の調査 - if(!httpd_strcasencmp(p1+1,"Content-Length: ",16)) { - len = atoi(p1 + 17); - } - // Connection: の調査 - if(!httpd_strcasencmp(p1+1,"Connection: ",12)) { - if( httpd_strcasencmp(p1+13,"close",5)==0 && sd->http_ver==1 ) - sd->persist = 0; - if( httpd_strcasencmp(p1+13,"Keep-Alive",10)==0 && sd->http_ver==0 ) - sd->persist = 1; - } - p1 = strchr(p1+1,'\n'); - } - if(plen) *plen = len; - return; -} - -void httpd_parse_header(struct httpd_session_data* sd) -{ - int i; - int status = 400; // Bad Request - unsigned char* req = RFIFOP(sd->fd,0); - do { - if(!strncmp(req,"GET /",5)) { - // GET リクエスト - req += 5; - for(i = 0;req[i]; i++) { - if(req[i] == ' ' || req[i] == '?') break; - if(!isalnum(req[i]) && req[i] != '.' && req[i] != '_' && req[i] != '-') break; - } - if(req[i] == ' ') { - req[i] = 0; - sd->url = req; - sd->query = NULL; - sd->status = HTTPD_REQUEST_OK; - } else if(req[i] == '?') { - req[i] = 0; - sd->query = &req[++i]; - for(;req[i];i++) { - if( - isalnum(req[i]) || req[i] == '+' || req[i] == '%' || req[i] == '&' || - req[i] == '=' - ) { - continue; - } else { - break; - } - } - if(req[i] != ' ') { - break; - } - req[i] = 0; - sd->url = req; - } else { - break; - } - // ヘッダ解析 - httpd_parse_header_sub( sd, &req[i+1], NULL ); - - printf("httpd: request %s %s\n", sd->url, sd->query); - sd->method = HTTPD_METHOD_GET; - httpd_parse_request_ok(sd); - } else if(!strncmp(req,"POST /",6)) { - int len; - req += 6; status = 404; - for(i = 0;req[i]; i++) { - if(req[i] == ' ') break; - if(!isalnum(req[i]) && req[i] != '.' && req[i] != '_' && req[i] != '-') break; - } - if(req[i] != ' ') { - break; - } - req[i] = 0; - sd->url = req; - - // ヘッダ解析 - httpd_parse_header_sub( sd, &req[i+1], &len ); - - if(len <= 0 || len >= 32*1024) { - // とりあえず32KB以上のリクエストは不正扱い - status = ( len==0 )? 411 : ( len>32*1024 )? 413 : 400; - break; - } - - sd->query = RFIFOP(sd->fd,sd->header_len); - sd->method = HTTPD_METHOD_POST; - sd->header_len += len; - if(RFIFOREST(sd->fd) >= sd->header_len) { - unsigned char temp = RFIFOB(sd->fd,sd->header_len); - RFIFOB(sd->fd,sd->header_len) = 0; - httpd_parse_request_ok(sd); - RFIFOB(sd->fd,sd->header_len) = temp; - } else { - // POSTのデータが送られてくるのを待つ - sd->status = HTTPD_REQUEST_WAIT_POST; - } - } else { - break; - } - } while(0); - if(sd->status == HTTPD_REQUEST_WAIT) { - sd->status = HTTPD_REQUEST_OK; - httpd_send_error(sd,status); - } -} - -void httpd_parse_request_ok (struct httpd_session_data *sd) -{ - void (*httpd_parse_func)(struct httpd_session_data*,const char*); - sd->status = HTTPD_REQUEST_OK; - - // ファイル名が求まったので、ページが無いか検索する - // printf("httpd_parse: [% 3d] request /%s\n", fd, req); - httpd_parse_func = strdb_get(httpd_files,(unsigned char*)sd->url); - if(httpd_parse_func == NULL) { - httpd_parse_func = httpd_default; - } - if(httpd_parse_func == NULL) { - httpd_send_error(sd,404); // Not Found - } else { - httpd_parse_func(sd,sd->url); - if(sd->status == HTTPD_REQUEST_OK) { - httpd_send_error(sd,404); // Not Found - } - } - if(sd->persist == 1 && sd->data_len) { - // 長さが変なデータ(こんなの送るなよ…) - printf("httpd_parse: send size mismatch when parsing /%s\n", sd->url); - sessiond[sd->fd]->eof = 1; - } - if(sd->status == HTTPD_REQUEST_OK) { - httpd_send_error(sd,404); - } -} - -char* httpd_get_value(struct httpd_session_data* sd,const char* val) -{ - int src_len = strlen(val); - const unsigned char* src_p = sd->query; - if(src_p == NULL) return aStrdup(""); - - do { - if(!memcmp(src_p,val,src_len) && src_p[src_len] == '=') { - break; - } - src_p = strchr(src_p + 1,'&'); - if(src_p) src_p++; - } while(src_p); - - if(src_p != NULL) { - // 目的の文字列を見つけた - const unsigned char* p2; - int dest_len; - char* dest_p; - src_p += src_len + 1; - p2 = strchr(src_p,'&'); - if(p2 == NULL) { - src_len = strlen(src_p); - } else { - src_len = (p2 - src_p); - } - dest_p = aMalloc(src_len + 1); - dest_len = 0; - while(src_len > 0) { - if(*src_p == '%' && src_len > 2) { - int c1 = 0,c2 = 0; - if(src_p[1] >= '0' && src_p[1] <= '9') c1 = src_p[1] - '0'; - if(src_p[1] >= 'A' && src_p[1] <= 'F') c1 = src_p[1] - 'A' + 10; - if(src_p[1] >= 'a' && src_p[1] <= 'f') c1 = src_p[1] - 'a' + 10; - if(src_p[2] >= '0' && src_p[2] <= '9') c2 = src_p[2] - '0'; - if(src_p[2] >= 'A' && src_p[2] <= 'F') c2 = src_p[2] - 'A' + 10; - if(src_p[2] >= 'a' && src_p[2] <= 'f') c2 = src_p[2] - 'a' + 10; - dest_p[dest_len++] = (c1 << 4) | c2; - src_p += 3; src_len -= 3; - } else if(*src_p == '+') { - dest_p[dest_len++] = ' '; - src_p++; src_len--; - } else { - dest_p[dest_len++] = *(src_p++); src_len--; - } - } - dest_p[dest_len] = 0; - return dest_p; - } - return aStrdup(""); -} - -// MIMEタイプ判定。主要なものだけ判定して、残りはapplication/octet-stream -static const char* httpd_mimetype(const char* url) -{ - char *ext = strrchr(url,'.'); - if(ext) { - if(!strcmp(ext,".html")) return "text/html"; - if(!strcmp(ext,".htm")) return "text/html"; - if(!strcmp(ext,".css")) return "text/css"; - if(!strcmp(ext,".js")) return "text/javascript"; - if(!strcmp(ext,".txt")) return "text/plain"; - if(!strcmp(ext,".gif")) return "image/gif"; - if(!strcmp(ext,".jpg")) return "image/jpeg"; - if(!strcmp(ext,".jpeg")) return "image/jpeg"; - if(!strcmp(ext,".png")) return "image/png"; - if(!strcmp(ext,".xbm")) return "image/xbm"; - if(!strcmp(ext,".zip")) return "application/zip"; - } - return "application/octet-stream"; -} - -void httpd_send_file(struct httpd_session_data* sd, const char* url) -{ - FILE *fp; - int file_size; - char file_buf[8192]; - if(sd->status != HTTPD_REQUEST_OK) return; - if(url[0] == '\0') url = "index.html"; - - // url の最大長は約1010バイトなので、バッファオーバーフローの心配は無し - sprintf(file_buf, "%s%s", document_root, url); - - fp = fopen(file_buf,"rb"); - if(fp == NULL) { - httpd_send_error(sd,404); - } else { - fseek(fp,0,SEEK_END); - file_size = ftell(fp); - fseek(fp,0,SEEK_SET); - httpd_send_head(sd,200,httpd_mimetype(url),file_size); - while(file_size > 0) { - int read_byte = file_size; - if(file_size > 8192) read_byte = 8192; - fread(file_buf,1,read_byte,fp); - httpd_send_data(sd,read_byte,file_buf); - file_size -= read_byte; - } - fclose(fp); - } -} - - -char* httpd_binary_encode(const char* val) -{ - char *buf = aMalloc(strlen(val) * 3 + 1); - char *p = buf; - while(*val) { - if(isalnum((unsigned char)*val)) { - *(p++) = *(val++); - } else { - unsigned char c1 = *(val++); - unsigned char c2 = (c1 >> 4); - unsigned char c3 = (c1 & 0x0F); - *(p++) = '%'; - *(p++) = c2 + (c2 >= 10 ? 'A'-10 : '0'); - *(p++) = c3 + (c3 >= 10 ? 'A'-10 : '0'); - } - } - *p = 0; - return buf; -} - -char* httpd_quote_meta(const char* p1) -{ - char *buf = aMalloc(strlen(p1) * 6 + 1); - char *p2 = buf; - while(*p1) { - switch(*p1) { - case '<': memcpy(p2,"<",4); p2 += 4; p1++; break; - case '>': memcpy(p2,">",4); p2 += 4; p1++; break; - case '&': memcpy(p2,"&",5); p2 += 5; p1++; break; - case '"': memcpy(p2,""",6); p2 += 6; p1++; break; - default: *(p2++) = *(p1++); - } - } - *p2 = 0; - return buf; -} - -///////// Graph / HTML snippets functions ///////////////////////////// - -struct file_entry { - char *filename; - struct file_entry *next; -}; -struct file_entry *fileentry_head = NULL; - -static void httpd_graph_load (const char *filename) -{ - struct file_entry *entry; - char type = *server_type + 'a'; - int len = strlen(filename); - - if (len <= 7 || filename[len - 7] != type) - return; - - entry = fileentry_head; - while (entry) { - if (strcmpi(entry->filename, filename) == 0) - return; - entry = entry->next; - } - - entry = (struct file_entry *) aMalloc (sizeof(struct file_entry)); - entry->filename = aStrdup(filename); - entry->next = fileentry_head; - fileentry_head = entry; -} - -// scan for available html snippets -static int httpd_graph_find (int tid, unsigned int tick, int id, int data) -{ - findfile("httpd", ".graph", httpd_graph_load); - return 0; -} - -static void httpd_graph_parse (struct httpd_session_data *sd,const char* url) -{ - // output html - struct file_entry *entry = fileentry_head; - char buf[8192]; - char *p = buf; - FILE *fp; - - p += sprintf(p,"Athena Sensors\n\n\n"); - p += sprintf(p,"

Athena Sensors

\n\n"); - - while (entry) { - // insert snippets into html - char line[1024]; - fp = fopen(entry->filename, "r"); - if (fp == NULL) { - entry = entry->next; - continue; - } - while(fgets(line, sizeof(line) -1, fp)) - p += sprintf(p, line); - fclose(fp); - entry = entry->next; - } - p += sprintf(p,"\n"); - httpd_send(sd,200,"text/html",p - buf,buf); -} - -//////////////// Initialise / Finalise ///////////////////////////// - -void do_final (void) -{ - int fd; - struct file_entry *entry = fileentry_head, *entry2; - - // clear up graph entries - while (entry) { - entry2 = entry->next; - aFree(entry->filename); - aFree(entry); - entry = entry2; - } - // clear up existing http connections - for (fd = 0; fd < *max_fd; fd++) - if (sessiond[fd] && sessiond[fd]->type == SESSION_HTTP) - delete_sessiond(fd); - - httpd_files->destroy(httpd_files,NULL); - // clear up the database - db_final(); - // clear up allocated memory - // note: the memory manager, if enabled, would be - // separate from the parent program, which is also - // why we need to delete our http sessions - // separately above - malloc_final(); -} - -void do_init (void) -{ - struct func_parse_table *parse_table; - int enable_httpd = 1; - - do { - char line[1024], w1[1024], w2[1024]; - FILE *fp = fopen("plugins/httpd.conf","r"); - if (fp == NULL) - break; - - while(fgets(line, sizeof(line) -1, fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { - if(strcmpi(w1,"enable_httpd")==0){ - enable_httpd = atoi(w2); - } else if(strcmpi(w1,"document_root")==0){ - strcpy(document_root, w2); - } else if(strcmpi(w1,"request_timeout_first")==0){ - request_timeout[0] = atoi(w2); - } else if(strcmpi(w1,"request_timeout_persist")==0){ - request_timeout[1] = atoi(w2); - } else if(strcmpi(w1,"max_persist_request")==0){ - max_persist_requests = atoi(w2); - } - } - } - fclose(fp); - } while (0); - - if (!enable_httpd) - return; - - malloc_init(); - db_init(); - IMPORT_SYMBOL(server_type, 0); - IMPORT_SYMBOL(gettick, 5); - IMPORT_SYMBOL(add_timer_interval, 8); - IMPORT_SYMBOL(max_fd, 13); - IMPORT_SYMBOL(sessiond, 14); - IMPORT_SYMBOL(delete_sessiond, 15); - IMPORT_SYMBOL(_WFIFOSET, 16); - IMPORT_SYMBOL(_RFIFOSKIP, 17); - IMPORT_SYMBOL(parse_table, 18); - - // register http parsing function - parse_table[SESSION_HTTP].check = httpd_check; - parse_table[SESSION_HTTP].func = httpd_parse; - - httpd_files = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_RELEASE_KEY,50); - httpd_default = httpd_send_file; - - httpd_pages ("/graph", httpd_graph_parse); - add_timer_interval(gettick()+10000,httpd_graph_find,0,0,10000); - - return; -} + +#ifdef __WIN32 +#include +#else +#include +#endif +#include +#include +#include +#include + +#include "../common/db.h" +//mmo required for definition of stricmp +#include "../common/mmo.h" +#include "../common/utils.h" +#include "../common/malloc.h" +#include "../common/socket.h" +#include "../common/plugin.h" +//#include "httpd.h" + +/** Created by End_of_Exam, ported to plugin and modified by Celest **/ + +PLUGIN_INFO = { + "HttpDaemon", + PLUGIN_CORE, + "0.1", + PLUGIN_VERSION, + "HTTP Daemon" +}; + +PLUGIN_EVENTS_TABLE = { + { "do_init", "Plugin_Init" }, + { "do_final", "Plugin_Final" }, + { NULL, NULL } +}; + +enum HTTPD_STATUS { + HTTPD_REQUEST_WAIT = 0, // リクエスト待ち + HTTPD_REQUEST_WAIT_POST, // リクエスト待ち(post) + HTTPD_REQUEST_OK, // リクエスト解釈完了 + HTTPD_SEND_HEADER, // ヘッダ送信完了 + HTTPD_WAITING_SEND // データが送信し終わるまで待っている状態 +}; +enum { + HTTPD_METHOD_UNKNOWN = 0, + HTTPD_METHOD_GET, + HTTPD_METHOD_POST +}; + +struct httpd_session_data { + int fd; + int status; + int http_ver; + int header_len; + int data_len; + int method; + int persist; + int request_count; + unsigned int tick; + const unsigned char* url; + const unsigned char* query; +}; + +// undefine socket operations included from socket.h, +// since we are going to use 'sessiond' instead +#undef RFIFOP +#undef RFIFOREST +#undef WFIFOP + +#define RFIFOP(fd,pos) (sessiond[fd]->rdata+sessiond[fd]->rdata_pos+(pos)) +#define RFIFOREST(fd) (sessiond[fd]->rdata_size-sessiond[fd]->rdata_pos) +#define WFIFOP(fd,pos) (sessiond[fd]->wdata+sessiond[fd]->wdata_size+(pos)) + +struct socket_data **sessiond; +char *server_type; +unsigned int (*gettick)(); +int (*add_timer_interval)(unsigned int,int (*)(int,unsigned int,int,int),int,int,int); +int *max_fd; +int (*delete_sessiond)(int); +int (*_WFIFOSET)(int,int); +int (*_RFIFOSKIP)(int,int); + +static int max_persist_requests = 32; // 持続通信での最大リクエスト数 +static int request_timeout[] = { 2500, 60*1000 }; // タイムアウト(最初、持続) +static char document_root[256] = "./httpd/"; // ドキュメントルート + +// httpd に入っているページと、呼び出すコールバック関数の一覧 +struct dbt *httpd_files; + +void httpd_send(struct httpd_session_data*, int, const char *, int, const void *); + +int httpd_check (struct socket_data *sd) +{ + // httpd に回すどうかの判定がまだ行われてない + // 先頭2バイトが GE ならhttpd に回してみる + if (sd->rdata_size >= 2 && + sd->rdata[0] == 'G' && sd->rdata[1] == 'E') + return 1; + + return 0; +} + +int httpd_strcasencmp(const char *s1, const char *s2,int len) +{ + while(len-- && (*s1 || *s2) ) { + if((*s1 | 0x20) != (*s2 | 0x20)) { + return ((*s1 | 0x20) > (*s2 | 0x20) ? 1 : -1); + } + s1++; s2++; + } + return 0; +} + +// httpd にページを追加する +// for などでページ名を合成できるように、key はstrdup()したものを使う + +void httpd_pages (const char* url, void (*httpd_func)(struct httpd_session_data*, const char*)) +{ + if (strdb_get(httpd_files,(unsigned char*)(url+1)) == NULL) { + strdb_put(httpd_files, (unsigned char*)aStrdup(url+1), httpd_func); + } else { + strdb_put(httpd_files, (unsigned char*)(url+1), httpd_func); + } +} + +static void (*httpd_default)(struct httpd_session_data* sd,const char* url); + +const char *httpd_get_error( struct httpd_session_data* sd, int* status ) +{ + const char* msg; + // httpd のステータスを決める + switch(*status) { + case 200: msg = "OK"; break; + case 400: msg = "Bad Request"; break; + case 401: msg = "Unauthorized"; break; // 未使用 + case 403: msg = "Forbidden"; break; // 未使用 + case 404: msg = "Not Found"; break; + case 408: msg = "Request Timedout"; break; + case 411: msg = "Length Required"; break; + case 413: msg = "Request Entity Too Large"; break; + default: + *status = 500; msg = "Internal Server Error"; break; + } + return msg; +} + +void httpd_send_error(struct httpd_session_data* sd,int status) +{ + const char* msg = httpd_get_error( sd, &status ); + httpd_send(sd, status, "text/plain",strlen(msg),msg); +} + +void httpd_send_head (struct httpd_session_data* sd, int status, const char *content_type, int content_len) +{ + char head[256]; + int len; + const char* msg; + + if (sd->status != HTTPD_REQUEST_OK) + return; + msg = httpd_get_error( sd, &status ); + + if(content_len == -1 || ++sd->request_count >= max_persist_requests ) { + // 長さが分からない or リクエスト限界を超えたので切断する + len = sprintf( + head, + "HTTP/1.%d %d %s\r\nContent-Type: %s\r\nConnection: close\r\n\r\n", + sd->http_ver,status,msg,content_type + ); + sd->persist = 0; + len = sprintf( + head, + "HTTP/1.%d %d %s\r\nContent-Type: %s\r\n\r\n", + sd->http_ver,status,msg,content_type + ); + sd->http_ver = 0; // 長さが分からないので、HTTP/1.0 扱い(自動切断)にする + } else { + len = sprintf( + head, + "HTTP/1.%d %d %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n", + sd->http_ver,status,msg,content_type,content_len + ); + } + memcpy(WFIFOP(sd->fd,0),head,len); + _WFIFOSET(sd->fd,len); + sd->status = HTTPD_SEND_HEADER; + sd->data_len = content_len; +} + +void httpd_send_data (struct httpd_session_data* sd, int content_len, const void *data) +{ + const char* msg = (const char*)data; + if (sd->status == HTTPD_REQUEST_OK) { + // ヘッダの送信忘れているので、適当に補う + httpd_send_head(sd,200,"application/octet-stream",-1); + } else if(sd->status != HTTPD_SEND_HEADER && sd->status != HTTPD_WAITING_SEND) { + return; + } + sd->data_len -= content_len; + + // 巨大なサイズのファイルも送信出来るように分割して送る + while (content_len > 0) { + int send_byte = content_len; + if(send_byte > 12*1024) send_byte = 12*1024; + memcpy(WFIFOP(sd->fd,0),msg,send_byte); + _WFIFOSET(sd->fd,send_byte); + msg += send_byte; content_len -= send_byte; + } + sd->status = HTTPD_WAITING_SEND; +} + +void httpd_send (struct httpd_session_data* sd, int status, const char *content_type, int content_len, const void *data) +{ + httpd_send_head(sd,status,content_type,content_len); + httpd_send_data(sd,content_len,data); +} + +void httpd_parse_header(struct httpd_session_data* sd); +void httpd_parse_request_ok(struct httpd_session_data *sd); + +int httpd_parse (int fd) +{ + struct httpd_session_data *sd = (struct httpd_session_data *)sessiond[fd]->session_data2; + if (sessiond[fd]->eof) { + delete_sessiond(fd); + return 0; + } + if (sd == NULL) { + sd = (struct httpd_session_data*) aMalloc (sizeof(struct httpd_session_data)); + sd->fd = fd; + sessiond[fd]->session_data2 = sd; + sd->tick = gettick(); + sd->persist = 0; + sd->request_count = 0; + } + printf ("status %d\n", sd->status); + switch(sd->status) { + case HTTPD_REQUEST_WAIT: + // リクエスト待ち + if(RFIFOREST(fd) > 1024) { + // リクエストが長すぎるので、エラー扱いする + sd->status = HTTPD_REQUEST_OK; + httpd_send_error(sd,400); // Bad Request + } else if( (int)( gettick() - sd->tick ) > request_timeout[sd->persist] ) { + // リクエストに時間がかかりすぎているので、エラー扱いする + sd->status = HTTPD_REQUEST_OK; + httpd_send_error(sd,408); // Request Timeout + } else if(sd->header_len == RFIFOREST(fd)) { + // 状態が以前と同じなので、リクエストを再解析する必要は無い + } else { + int limit = RFIFOREST(fd); + unsigned char *req = RFIFOP(fd,0); + sd->header_len = RFIFOREST(fd); + do { + if(*req == '\n' && limit > 0) { + limit--; req++; + if(*req == '\r' && limit > 0) { limit--; req++; } + if(*req == '\n') { + // HTTPヘッダの終点を見つけた + *req = 0; + sd->header_len = (req - RFIFOP(fd,0)) + 1; + httpd_parse_header(sd); + break; + } + } + } while(req++,--limit > 0); + } + break; + case HTTPD_REQUEST_WAIT_POST: + if(RFIFOREST(sd->fd) >= sd->header_len) { + unsigned char temp = RFIFOB(sd->fd,sd->header_len); + RFIFOB(sd->fd,sd->header_len) = 0; + httpd_parse_request_ok(sd); + RFIFOB(sd->fd,sd->header_len) = temp; + } + break; + case HTTPD_REQUEST_OK: + case HTTPD_SEND_HEADER: + // リクエストが終わったまま何も送信されていない状態なので、 + // 強制切断 + printf ("httpd: eof\n"); + sessiond[fd]->eof = 1; + break; + case HTTPD_WAITING_SEND: + // データの送信が終わるまで待機 + //if(sessiond[fd]->wdata_size == sessiond[fd]->wdata_pos) { + // i *hope* this is correct o.o; + if(sessiond[fd]->wdata_size == 0) { + // HTTP/1.0は手動切断 +// if(sd->http_ver == 0) { + if(sd->persist == 0) { + printf ("httpd: eof\n"); + sessiond[fd]->eof = 1; + } + // RFIFO からリクエストデータの消去と構造体の初期化 + _RFIFOSKIP(fd,sd->header_len); + sd->status = HTTPD_REQUEST_WAIT; + sd->tick = gettick(); + sd->header_len = 0; + sd->query = NULL; +// sd->http_ver = 0; // ver は保持 + sd->method = HTTPD_METHOD_UNKNOWN; + printf("httpd_parse: [% 3d] request sended RFIFOREST:%d\n", fd, RFIFOREST(fd)); + } + break; + } + return 0; +} + +void httpd_parse_header_sub( struct httpd_session_data* sd, const char *p1, int* plen ) +{ + int len = 0; + // HTTPのバージョンを調査 + if(!strncmp(p1 ,"HTTP/1.1",8)) { + sd->http_ver = 1; + sd->persist = 1; + } else { + sd->http_ver = 0; + sd->persist = 0; + } + + p1 = strchr(p1,'\n'); + while(p1) { + // Content-Length: の調査 + if(!httpd_strcasencmp(p1+1,"Content-Length: ",16)) { + len = atoi(p1 + 17); + } + // Connection: の調査 + if(!httpd_strcasencmp(p1+1,"Connection: ",12)) { + if( httpd_strcasencmp(p1+13,"close",5)==0 && sd->http_ver==1 ) + sd->persist = 0; + if( httpd_strcasencmp(p1+13,"Keep-Alive",10)==0 && sd->http_ver==0 ) + sd->persist = 1; + } + p1 = strchr(p1+1,'\n'); + } + if(plen) *plen = len; + return; +} + +void httpd_parse_header(struct httpd_session_data* sd) +{ + int i; + int status = 400; // Bad Request + unsigned char* req = RFIFOP(sd->fd,0); + do { + if(!strncmp(req,"GET /",5)) { + // GET リクエスト + req += 5; + for(i = 0;req[i]; i++) { + if(req[i] == ' ' || req[i] == '?') break; + if(!isalnum(req[i]) && req[i] != '.' && req[i] != '_' && req[i] != '-') break; + } + if(req[i] == ' ') { + req[i] = 0; + sd->url = req; + sd->query = NULL; + sd->status = HTTPD_REQUEST_OK; + } else if(req[i] == '?') { + req[i] = 0; + sd->query = &req[++i]; + for(;req[i];i++) { + if( + isalnum(req[i]) || req[i] == '+' || req[i] == '%' || req[i] == '&' || + req[i] == '=' + ) { + continue; + } else { + break; + } + } + if(req[i] != ' ') { + break; + } + req[i] = 0; + sd->url = req; + } else { + break; + } + // ヘッダ解析 + httpd_parse_header_sub( sd, &req[i+1], NULL ); + + printf("httpd: request %s %s\n", sd->url, sd->query); + sd->method = HTTPD_METHOD_GET; + httpd_parse_request_ok(sd); + } else if(!strncmp(req,"POST /",6)) { + int len; + req += 6; status = 404; + for(i = 0;req[i]; i++) { + if(req[i] == ' ') break; + if(!isalnum(req[i]) && req[i] != '.' && req[i] != '_' && req[i] != '-') break; + } + if(req[i] != ' ') { + break; + } + req[i] = 0; + sd->url = req; + + // ヘッダ解析 + httpd_parse_header_sub( sd, &req[i+1], &len ); + + if(len <= 0 || len >= 32*1024) { + // とりあえず32KB以上のリクエストは不正扱い + status = ( len==0 )? 411 : ( len>32*1024 )? 413 : 400; + break; + } + + sd->query = RFIFOP(sd->fd,sd->header_len); + sd->method = HTTPD_METHOD_POST; + sd->header_len += len; + if(RFIFOREST(sd->fd) >= sd->header_len) { + unsigned char temp = RFIFOB(sd->fd,sd->header_len); + RFIFOB(sd->fd,sd->header_len) = 0; + httpd_parse_request_ok(sd); + RFIFOB(sd->fd,sd->header_len) = temp; + } else { + // POSTのデータが送られてくるのを待つ + sd->status = HTTPD_REQUEST_WAIT_POST; + } + } else { + break; + } + } while(0); + if(sd->status == HTTPD_REQUEST_WAIT) { + sd->status = HTTPD_REQUEST_OK; + httpd_send_error(sd,status); + } +} + +void httpd_parse_request_ok (struct httpd_session_data *sd) +{ + void (*httpd_parse_func)(struct httpd_session_data*,const char*); + sd->status = HTTPD_REQUEST_OK; + + // ファイル名が求まったので、ページが無いか検索する + // printf("httpd_parse: [% 3d] request /%s\n", fd, req); + httpd_parse_func = strdb_get(httpd_files,(unsigned char*)sd->url); + if(httpd_parse_func == NULL) { + httpd_parse_func = httpd_default; + } + if(httpd_parse_func == NULL) { + httpd_send_error(sd,404); // Not Found + } else { + httpd_parse_func(sd,sd->url); + if(sd->status == HTTPD_REQUEST_OK) { + httpd_send_error(sd,404); // Not Found + } + } + if(sd->persist == 1 && sd->data_len) { + // 長さが変なデータ(こんなの送るなよ…) + printf("httpd_parse: send size mismatch when parsing /%s\n", sd->url); + sessiond[sd->fd]->eof = 1; + } + if(sd->status == HTTPD_REQUEST_OK) { + httpd_send_error(sd,404); + } +} + +char* httpd_get_value(struct httpd_session_data* sd,const char* val) +{ + int src_len = strlen(val); + const unsigned char* src_p = sd->query; + if(src_p == NULL) return aStrdup(""); + + do { + if(!memcmp(src_p,val,src_len) && src_p[src_len] == '=') { + break; + } + src_p = strchr(src_p + 1,'&'); + if(src_p) src_p++; + } while(src_p); + + if(src_p != NULL) { + // 目的の文字列を見つけた + const unsigned char* p2; + int dest_len; + char* dest_p; + src_p += src_len + 1; + p2 = strchr(src_p,'&'); + if(p2 == NULL) { + src_len = strlen(src_p); + } else { + src_len = (p2 - src_p); + } + dest_p = aMalloc(src_len + 1); + dest_len = 0; + while(src_len > 0) { + if(*src_p == '%' && src_len > 2) { + int c1 = 0,c2 = 0; + if(src_p[1] >= '0' && src_p[1] <= '9') c1 = src_p[1] - '0'; + if(src_p[1] >= 'A' && src_p[1] <= 'F') c1 = src_p[1] - 'A' + 10; + if(src_p[1] >= 'a' && src_p[1] <= 'f') c1 = src_p[1] - 'a' + 10; + if(src_p[2] >= '0' && src_p[2] <= '9') c2 = src_p[2] - '0'; + if(src_p[2] >= 'A' && src_p[2] <= 'F') c2 = src_p[2] - 'A' + 10; + if(src_p[2] >= 'a' && src_p[2] <= 'f') c2 = src_p[2] - 'a' + 10; + dest_p[dest_len++] = (c1 << 4) | c2; + src_p += 3; src_len -= 3; + } else if(*src_p == '+') { + dest_p[dest_len++] = ' '; + src_p++; src_len--; + } else { + dest_p[dest_len++] = *(src_p++); src_len--; + } + } + dest_p[dest_len] = 0; + return dest_p; + } + return aStrdup(""); +} + +// MIMEタイプ判定。主要なものだけ判定して、残りはapplication/octet-stream +static const char* httpd_mimetype(const char* url) +{ + char *ext = strrchr(url,'.'); + if(ext) { + if(!strcmp(ext,".html")) return "text/html"; + if(!strcmp(ext,".htm")) return "text/html"; + if(!strcmp(ext,".css")) return "text/css"; + if(!strcmp(ext,".js")) return "text/javascript"; + if(!strcmp(ext,".txt")) return "text/plain"; + if(!strcmp(ext,".gif")) return "image/gif"; + if(!strcmp(ext,".jpg")) return "image/jpeg"; + if(!strcmp(ext,".jpeg")) return "image/jpeg"; + if(!strcmp(ext,".png")) return "image/png"; + if(!strcmp(ext,".xbm")) return "image/xbm"; + if(!strcmp(ext,".zip")) return "application/zip"; + } + return "application/octet-stream"; +} + +void httpd_send_file(struct httpd_session_data* sd, const char* url) +{ + FILE *fp; + int file_size; + char file_buf[8192]; + if(sd->status != HTTPD_REQUEST_OK) return; + if(url[0] == '\0') url = "index.html"; + + // url の最大長は約1010バイトなので、バッファオーバーフローの心配は無し + sprintf(file_buf, "%s%s", document_root, url); + + fp = fopen(file_buf,"rb"); + if(fp == NULL) { + httpd_send_error(sd,404); + } else { + fseek(fp,0,SEEK_END); + file_size = ftell(fp); + fseek(fp,0,SEEK_SET); + httpd_send_head(sd,200,httpd_mimetype(url),file_size); + while(file_size > 0) { + int read_byte = file_size; + if(file_size > 8192) read_byte = 8192; + fread(file_buf,1,read_byte,fp); + httpd_send_data(sd,read_byte,file_buf); + file_size -= read_byte; + } + fclose(fp); + } +} + + +char* httpd_binary_encode(const char* val) +{ + char *buf = aMalloc(strlen(val) * 3 + 1); + char *p = buf; + while(*val) { + if(isalnum((unsigned char)*val)) { + *(p++) = *(val++); + } else { + unsigned char c1 = *(val++); + unsigned char c2 = (c1 >> 4); + unsigned char c3 = (c1 & 0x0F); + *(p++) = '%'; + *(p++) = c2 + (c2 >= 10 ? 'A'-10 : '0'); + *(p++) = c3 + (c3 >= 10 ? 'A'-10 : '0'); + } + } + *p = 0; + return buf; +} + +char* httpd_quote_meta(const char* p1) +{ + char *buf = aMalloc(strlen(p1) * 6 + 1); + char *p2 = buf; + while(*p1) { + switch(*p1) { + case '<': memcpy(p2,"<",4); p2 += 4; p1++; break; + case '>': memcpy(p2,">",4); p2 += 4; p1++; break; + case '&': memcpy(p2,"&",5); p2 += 5; p1++; break; + case '"': memcpy(p2,""",6); p2 += 6; p1++; break; + default: *(p2++) = *(p1++); + } + } + *p2 = 0; + return buf; +} + +///////// Graph / HTML snippets functions ///////////////////////////// + +struct file_entry { + char *filename; + struct file_entry *next; +}; +struct file_entry *fileentry_head = NULL; + +static void httpd_graph_load (const char *filename) +{ + struct file_entry *entry; + char type = *server_type + 'a'; + int len = strlen(filename); + + if (len <= 7 || filename[len - 7] != type) + return; + + entry = fileentry_head; + while (entry) { + if (strcmpi(entry->filename, filename) == 0) + return; + entry = entry->next; + } + + entry = (struct file_entry *) aMalloc (sizeof(struct file_entry)); + entry->filename = aStrdup(filename); + entry->next = fileentry_head; + fileentry_head = entry; +} + +// scan for available html snippets +static int httpd_graph_find (int tid, unsigned int tick, int id, int data) +{ + findfile("httpd", ".graph", httpd_graph_load); + return 0; +} + +static void httpd_graph_parse (struct httpd_session_data *sd,const char* url) +{ + // output html + struct file_entry *entry = fileentry_head; + char buf[8192]; + char *p = buf; + FILE *fp; + + p += sprintf(p,"Athena Sensors\n\n\n"); + p += sprintf(p,"

Athena Sensors

\n\n"); + + while (entry) { + // insert snippets into html + char line[1024]; + fp = fopen(entry->filename, "r"); + if (fp == NULL) { + entry = entry->next; + continue; + } + while(fgets(line, sizeof(line) -1, fp)) + p += sprintf(p, line); + fclose(fp); + entry = entry->next; + } + p += sprintf(p,"\n"); + httpd_send(sd,200,"text/html",p - buf,buf); +} + +//////////////// Initialise / Finalise ///////////////////////////// + +void do_final (void) +{ + int fd; + struct file_entry *entry = fileentry_head, *entry2; + + // clear up graph entries + while (entry) { + entry2 = entry->next; + aFree(entry->filename); + aFree(entry); + entry = entry2; + } + // clear up existing http connections + for (fd = 0; fd < *max_fd; fd++) + if (sessiond[fd] && sessiond[fd]->type == SESSION_HTTP) + delete_sessiond(fd); + + httpd_files->destroy(httpd_files,NULL); + // clear up the database + db_final(); + // clear up allocated memory + // note: the memory manager, if enabled, would be + // separate from the parent program, which is also + // why we need to delete our http sessions + // separately above + malloc_final(); +} + +void do_init (void) +{ + struct func_parse_table *parse_table; + int enable_httpd = 1; + + do { + char line[1024], w1[1024], w2[1024]; + FILE *fp = fopen("plugins/httpd.conf","r"); + if (fp == NULL) + break; + + while(fgets(line, sizeof(line) -1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { + if(strcmpi(w1,"enable_httpd")==0){ + enable_httpd = atoi(w2); + } else if(strcmpi(w1,"document_root")==0){ + strcpy(document_root, w2); + } else if(strcmpi(w1,"request_timeout_first")==0){ + request_timeout[0] = atoi(w2); + } else if(strcmpi(w1,"request_timeout_persist")==0){ + request_timeout[1] = atoi(w2); + } else if(strcmpi(w1,"max_persist_request")==0){ + max_persist_requests = atoi(w2); + } + } + } + fclose(fp); + } while (0); + + if (!enable_httpd) + return; + + malloc_init(); + db_init(); + IMPORT_SYMBOL(server_type, 0); + IMPORT_SYMBOL(gettick, 5); + IMPORT_SYMBOL(add_timer_interval, 8); + IMPORT_SYMBOL(max_fd, 13); + IMPORT_SYMBOL(sessiond, 14); + IMPORT_SYMBOL(delete_sessiond, 15); + IMPORT_SYMBOL(_WFIFOSET, 16); + IMPORT_SYMBOL(_RFIFOSKIP, 17); + IMPORT_SYMBOL(parse_table, 18); + + // register http parsing function + parse_table[SESSION_HTTP].check = httpd_check; + parse_table[SESSION_HTTP].func = httpd_parse; + + httpd_files = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_RELEASE_KEY,50); + httpd_default = httpd_send_file; + + httpd_pages ("/graph", httpd_graph_parse); + add_timer_interval(gettick()+10000,httpd_graph_find,0,0,10000); + + return; +} diff --git a/src/plugins/httpd.h b/src/plugins/httpd.h index 9eef7d915..aa6989421 100644 --- a/src/plugins/httpd.h +++ b/src/plugins/httpd.h @@ -1,107 +1,107 @@ -#ifndef _HTTPD_H_ -#define _HTTPD_H_ - -struct httpd_session_data; - -// NOTE by Celest: This file is not used by httpd.c, but included only as an API reference. - -// 注意 -// 1.athena内蔵のhttpd で大きなファイルを送信することはお勧めしません。 -// 200KB を超えるようなファイルは、別のソフトを利用することを勧めます。 -// 2.ファイル名に使える文字は、[A-Za-z0-9-_\.] です。他の文字を使うと、 -// BAD REQUEST で弾かれます。 - - - -void httpd_pages(const char* url,void(*httpd_func)(struct httpd_session_data* sd,const char* url)); - -// 指定されたURL に対するコールバック関数を設定する。この関数は、以下のように -// 実装する必要がある。 -// -// 1. URL は、先頭のスラッシュが省かれたファイル名です。例えば、"GET / HTTP/1.0" -// という風にリクエストされた時、URL には""(空文字)が入り、"GET /hoge HTTP/1.0" -// の時には、"hoge"が入ります。 -// 2. リクエストされたページが見つかったら、httpd_send() または、httpd_send_head() -// とhttpd_send_data() の組を呼び出し、データを出力する。 -// 3. httpd_send_file を指定すると、httpd/ 以下にあるファイルを出力する。ファイルに -// 空文字が指定された時は、index.htmlが指定されたものとみなされる。 - - - -char* httpd_get_value(struct httpd_session_data* sd,const char* val); - -// リクエストされたアドレスに渡されたフォームデータのうち、該当する文字列を返す。 -// 例えば、"GET /status/graph?image=users HTTP/1.0"というリクエストの場合、 -// httpd_get_value(sd,"image"); は、 "users"を返す。この関数の戻り値は、呼び出し元が -// 解放しなければならない。また、該当する文字列が無い時は、空の文字列を返す。 - -unsigned int httpd_get_ip(struct httpd_session_data *sd); - -// クライアントのIPを返す。 - - -void httpd_default_page(void(*httpd_func)(struct httpd_session_data* sd,const char* url)); - -// 指定されたURL が登録されていない時に呼び出す関数を設定する。この関数を呼び出さないか、 -// 関数の引数にNULLを指定すると、404 Not Found を返す。 - - - - -void httpd_send(struct httpd_session_data* sd,int status,const char *content_type,int content_len,const void *data); - -// HTTPヘッダ、データを組にして送信する。この関数を呼び出した後に、httpd_send_data を -// 呼び出してはならない。 -// -// sd : httpd_set_parse_func() に渡されたものをそのまま渡すこと。 -// status : HTTPヘッダに加えるstatus。通常は200。 -// content_type : 送信するデータのタイプ。text/html , image/jpeg など。 -// content_len : 送信するデータの長さ。 -// data : 送信するデータへのポインタ - - - -void httpd_send_head(struct httpd_session_data* sd,int status,const char *content_type,int content_len); - -// HTTPヘッダを送信する。 -// -// sd : 同上 -// status : 同上 -// content_type : 同上 -// content_len : content_lenを-1に指定することで、この関数が呼ばれた時点で -// 長さが分からないデータを送信することができる。この場合は -// 強制的にHTTP/1.0 接続となり、オーバーヘッドが大きくなるので、 -// あまりお勧めはしない。 - - - - -void httpd_send_data(struct httpd_session_data* sd,int content_len,const void *data); - -// データを送信する。この関数を、httpd_send_head() を呼び出す前に呼び出された場合、 -// content_type = application/octet-stream, content_len = -1 としてヘッダが送信される。 -// sd : 同上 -// content_len : 送信するデータのdata長さを指定する。 -// data : 送信するデータ - - - -void httpd_send_file(struct httpd_session_data* sd,const char* url); - -// ファイルを送信する。この関数は、httpd_send_head() を呼び出す前に呼び出さなければ -// ならない。ファイルに空文字が指定されたときは、index.htmlが指定されたと見なされる。 - - - -void httpd_send_error(struct httpd_session_data* sd,int status); - -// HTTPエラーメッセージを送信する。status はHTTPのエラーコードと同じ。 -// 400 Bad Request, 404 Not Found, 500 Internal Server Error など。 - -int httpd_parse(int fd); - -// 初期化処理 -void do_init_httpd(void); -void do_final_httpd(void); - -#endif +#ifndef _HTTPD_H_ +#define _HTTPD_H_ + +struct httpd_session_data; + +// NOTE by Celest: This file is not used by httpd.c, but included only as an API reference. + +// 注意 +// 1.athena内蔵のhttpd で大きなファイルを送信することはお勧めしません。 +// 200KB を超えるようなファイルは、別のソフトを利用することを勧めます。 +// 2.ファイル名に使える文字は、[A-Za-z0-9-_\.] です。他の文字を使うと、 +// BAD REQUEST で弾かれます。 + + + +void httpd_pages(const char* url,void(*httpd_func)(struct httpd_session_data* sd,const char* url)); + +// 指定されたURL に対するコールバック関数を設定する。この関数は、以下のように +// 実装する必要がある。 +// +// 1. URL は、先頭のスラッシュが省かれたファイル名です。例えば、"GET / HTTP/1.0" +// という風にリクエストされた時、URL には""(空文字)が入り、"GET /hoge HTTP/1.0" +// の時には、"hoge"が入ります。 +// 2. リクエストされたページが見つかったら、httpd_send() または、httpd_send_head() +// とhttpd_send_data() の組を呼び出し、データを出力する。 +// 3. httpd_send_file を指定すると、httpd/ 以下にあるファイルを出力する。ファイルに +// 空文字が指定された時は、index.htmlが指定されたものとみなされる。 + + + +char* httpd_get_value(struct httpd_session_data* sd,const char* val); + +// リクエストされたアドレスに渡されたフォームデータのうち、該当する文字列を返す。 +// 例えば、"GET /status/graph?image=users HTTP/1.0"というリクエストの場合、 +// httpd_get_value(sd,"image"); は、 "users"を返す。この関数の戻り値は、呼び出し元が +// 解放しなければならない。また、該当する文字列が無い時は、空の文字列を返す。 + +unsigned int httpd_get_ip(struct httpd_session_data *sd); + +// クライアントのIPを返す。 + + +void httpd_default_page(void(*httpd_func)(struct httpd_session_data* sd,const char* url)); + +// 指定されたURL が登録されていない時に呼び出す関数を設定する。この関数を呼び出さないか、 +// 関数の引数にNULLを指定すると、404 Not Found を返す。 + + + + +void httpd_send(struct httpd_session_data* sd,int status,const char *content_type,int content_len,const void *data); + +// HTTPヘッダ、データを組にして送信する。この関数を呼び出した後に、httpd_send_data を +// 呼び出してはならない。 +// +// sd : httpd_set_parse_func() に渡されたものをそのまま渡すこと。 +// status : HTTPヘッダに加えるstatus。通常は200。 +// content_type : 送信するデータのタイプ。text/html , image/jpeg など。 +// content_len : 送信するデータの長さ。 +// data : 送信するデータへのポインタ + + + +void httpd_send_head(struct httpd_session_data* sd,int status,const char *content_type,int content_len); + +// HTTPヘッダを送信する。 +// +// sd : 同上 +// status : 同上 +// content_type : 同上 +// content_len : content_lenを-1に指定することで、この関数が呼ばれた時点で +// 長さが分からないデータを送信することができる。この場合は +// 強制的にHTTP/1.0 接続となり、オーバーヘッドが大きくなるので、 +// あまりお勧めはしない。 + + + + +void httpd_send_data(struct httpd_session_data* sd,int content_len,const void *data); + +// データを送信する。この関数を、httpd_send_head() を呼び出す前に呼び出された場合、 +// content_type = application/octet-stream, content_len = -1 としてヘッダが送信される。 +// sd : 同上 +// content_len : 送信するデータのdata長さを指定する。 +// data : 送信するデータ + + + +void httpd_send_file(struct httpd_session_data* sd,const char* url); + +// ファイルを送信する。この関数は、httpd_send_head() を呼び出す前に呼び出さなければ +// ならない。ファイルに空文字が指定されたときは、index.htmlが指定されたと見なされる。 + + + +void httpd_send_error(struct httpd_session_data* sd,int status); + +// HTTPエラーメッセージを送信する。status はHTTPのエラーコードと同じ。 +// 400 Bad Request, 404 Not Found, 500 Internal Server Error など。 + +int httpd_parse(int fd); + +// 初期化処理 +void do_init_httpd(void); +void do_final_httpd(void); + +#endif diff --git a/src/plugins/httpd.txt b/src/plugins/httpd.txt index 2de84e3d8..5a575f680 100644 --- a/src/plugins/httpd.txt +++ b/src/plugins/httpd.txt @@ -1,20 +1,20 @@ -// -// HTTP Daemon Plugin Configuration -// - -// Enabled the http daemon? -enable_httpd: 1 - -// WWW Root path -//(The ending slash is required!) -document_root: httpd/ - -// Request timeout (first request) -// Both of the following are in milliseconds -request_timeout_first: 2500 - -// Request timeout (consequent requests) -request_timeout_persist: 60000 - -// Maximum persistent requests +// +// HTTP Daemon Plugin Configuration +// + +// Enabled the http daemon? +enable_httpd: 1 + +// WWW Root path +//(The ending slash is required!) +document_root: httpd/ + +// Request timeout (first request) +// Both of the following are in milliseconds +request_timeout_first: 2500 + +// Request timeout (consequent requests) +request_timeout_persist: 60000 + +// Maximum persistent requests max_persist_request: 32 \ No newline at end of file diff --git a/src/plugins/pid.c b/src/plugins/pid.c index 1ceb49b6f..62eb6878c 100644 --- a/src/plugins/pid.c +++ b/src/plugins/pid.c @@ -1,54 +1,54 @@ - -#include -#include -#ifndef _WIN32 - #include -#else - #define getpid GetCurrentProcessId -#endif -#ifdef MINGW - #include - #include -#endif -#include "../common/plugin.h" - -PLUGIN_INFO = { - "ProcessId", - PLUGIN_ALL, - "1.0", - PLUGIN_VERSION, - "Logs the process ID" -}; - -PLUGIN_EVENTS_TABLE = { - { "pid_create", "Plugin_Init" }, - { "pid_delete", "Plugin_Final" }, - { NULL, NULL } -}; - -char pid_file[256]; -char *server_name; - -void pid_create () -{ - FILE *fp; - int len; - - IMPORT_SYMBOL(server_name, 1); - len = strlen(server_name); - strcpy(pid_file, server_name); - if(len > 4 && pid_file[len - 4] == '.') { - pid_file[len - 4] = 0; - } - strcat(pid_file, ".pid"); - fp = fopen(pid_file, "w"); - if (fp) { - fprintf(fp, "%d", getpid()); - fclose(fp); - } -} - -void pid_delete () -{ - unlink(pid_file); -} + +#include +#include +#ifndef _WIN32 + #include +#else + #define getpid GetCurrentProcessId +#endif +#ifdef MINGW + #include + #include +#endif +#include "../common/plugin.h" + +PLUGIN_INFO = { + "ProcessId", + PLUGIN_ALL, + "1.0", + PLUGIN_VERSION, + "Logs the process ID" +}; + +PLUGIN_EVENTS_TABLE = { + { "pid_create", "Plugin_Init" }, + { "pid_delete", "Plugin_Final" }, + { NULL, NULL } +}; + +char pid_file[256]; +char *server_name; + +void pid_create () +{ + FILE *fp; + int len; + + IMPORT_SYMBOL(server_name, 1); + len = strlen(server_name); + strcpy(pid_file, server_name); + if(len > 4 && pid_file[len - 4] == '.') { + pid_file[len - 4] = 0; + } + strcat(pid_file, ".pid"); + fp = fopen(pid_file, "w"); + if (fp) { + fprintf(fp, "%d", getpid()); + fclose(fp); + } +} + +void pid_delete () +{ + unlink(pid_file); +} diff --git a/src/plugins/sample.c b/src/plugins/sample.c index 5a8e2a286..39e95752b 100644 --- a/src/plugins/sample.c +++ b/src/plugins/sample.c @@ -1,77 +1,77 @@ -// Sample Athena plugin - -#include -#include -#include "../common/plugin.h" - -////// Plugin information //////// -// -PLUGIN_INFO = { -// change only the following area - "Test", // Plugin name - PLUGIN_ALL, // Which servers is this plugin for - "0.1", // Plugin version - PLUGIN_VERSION, // Minimum plugin engine version to run - "A sample plugin" // Short description of plugin -}; - -////// Plugin event list ////////// -// Format: , -// All registered functions to a event gets executed -// (In descending order) when its called. -// Multiple functions can be called by multiple events too, -// So it's up to your creativity ^^ -// -PLUGIN_EVENTS_TABLE = { -// change only the following area - { "test_me", "Plugin_Test" }, // when the plugin is tested for compatibility - { "do_init", "Plugin_Init" }, // when plugins are loaded - { "do_final", "Plugin_Final" }, // when plugins are unloaded - { "some_function", "some_event" }, - { "some_function", "another_event" }, - { NULL, NULL } -}; - -///// Variables ///// -char *server_type; -char *server_name; - -//////// Plugin functions ////////// -int do_init () -{ - // import symbols from the server - IMPORT_SYMBOL(server_type, 0); - IMPORT_SYMBOL(server_name, 1); - - printf ("Server type is "); - switch (*server_type) { - case PLUGIN_LOGIN: printf ("Login\n"); break; - case PLUGIN_CHAR: printf ("Char\n"); break; - case PLUGIN_MAP: printf ("Map\n"); break; - } - printf ("Filename is %s\n", server_name); - - return 1; -} - -int do_final () -{ - printf ("Bye world\n"); - - return 1; -} - -int some_function () -{ - printf ("Some function\n"); - return 0; -} - -// return 1 if the testing passes, otherwise 0 -// (where the plugin will be deactivated) -int test_me () -{ - if (1 + 1 == 2) - return 1; - return 0; -} +// Sample Athena plugin + +#include +#include +#include "../common/plugin.h" + +////// Plugin information //////// +// +PLUGIN_INFO = { +// change only the following area + "Test", // Plugin name + PLUGIN_ALL, // Which servers is this plugin for + "0.1", // Plugin version + PLUGIN_VERSION, // Minimum plugin engine version to run + "A sample plugin" // Short description of plugin +}; + +////// Plugin event list ////////// +// Format: , +// All registered functions to a event gets executed +// (In descending order) when its called. +// Multiple functions can be called by multiple events too, +// So it's up to your creativity ^^ +// +PLUGIN_EVENTS_TABLE = { +// change only the following area + { "test_me", "Plugin_Test" }, // when the plugin is tested for compatibility + { "do_init", "Plugin_Init" }, // when plugins are loaded + { "do_final", "Plugin_Final" }, // when plugins are unloaded + { "some_function", "some_event" }, + { "some_function", "another_event" }, + { NULL, NULL } +}; + +///// Variables ///// +char *server_type; +char *server_name; + +//////// Plugin functions ////////// +int do_init () +{ + // import symbols from the server + IMPORT_SYMBOL(server_type, 0); + IMPORT_SYMBOL(server_name, 1); + + printf ("Server type is "); + switch (*server_type) { + case PLUGIN_LOGIN: printf ("Login\n"); break; + case PLUGIN_CHAR: printf ("Char\n"); break; + case PLUGIN_MAP: printf ("Map\n"); break; + } + printf ("Filename is %s\n", server_name); + + return 1; +} + +int do_final () +{ + printf ("Bye world\n"); + + return 1; +} + +int some_function () +{ + printf ("Some function\n"); + return 0; +} + +// return 1 if the testing passes, otherwise 0 +// (where the plugin will be deactivated) +int test_me () +{ + if (1 + 1 == 2) + return 1; + return 0; +} diff --git a/src/plugins/sig.c b/src/plugins/sig.c index 7ddfaf2dc..edd0e55a2 100644 --- a/src/plugins/sig.c +++ b/src/plugins/sig.c @@ -1,211 +1,211 @@ -// $Id: sig.c 1 2005-6-13 3:17:17 PM Celestia $ - -#include -#include -#include -#include -#include -#include "../common/plugin.h" -#include "../common/version.h" -#include "../common/showmsg.h" - -PLUGIN_INFO = { - "Signals", - PLUGIN_CORE, - "1.1", - PLUGIN_VERSION, - "Handles program signals" -}; - -PLUGIN_EVENTS_TABLE = { - { "sig_init", "Plugin_Init" }, - { "sig_final", "Plugin_Final" }, - { NULL, NULL } -}; - -////////////////////////////////////// - -#if defined(_WIN32) || defined(MINGW) - int sig_init() { - ShowError("sig: This plugin is not supported - Enable 'exchndl' instead!\n"); - return 0; - } - int sig_final() { return 0; } -#elif defined (__NETBSD__) || defined (__FREEBSD__) - int sig_init() { - ShowError("sig: This plugin is not supported!\n"); - return 0; - } - int sig_final() { return 0; } -#else - -////////////////////////////////////// - -#if !defined(CYGWIN) - #include -#endif - -const char* (*getrevision)(); -unsigned long (*getuptime)(); -char *server_name; -int crash_flag = 0; - -extern const char *strsignal(int); -int sig_final (); - -// by Gabuzomeu -// This is an implementation of signal() using sigaction() for portability. -// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced -// Programming in the UNIX Environment_. -// -#ifdef WIN32 // windows don't have SIGPIPE -#define SIGPIPE SIGINT -#endif - -#ifndef POSIX -#define compat_signal(signo, func) signal(signo, func) -#else -sigfunc *compat_signal(int signo, sigfunc *func) -{ - struct sigaction sact, oact; - - sact.sa_handler = func; - sigemptyset(&sact.sa_mask); - sact.sa_flags = 0; -#ifdef SA_INTERRUPT - sact.sa_flags |= SA_INTERRUPT; /* SunOS */ -#endif - - if (sigaction(signo, &sact, &oact) < 0) - return (SIG_ERR); - - return (oact.sa_handler); -} -#endif - -/*========================================= - * Dumps the stack using glibc's backtrace - *----------------------------------------- - */ -#ifdef CYGWIN - #define FOPEN_ freopen - extern void cygwin_stackdump(); -#else - #define FOPEN_(fn,m,s) fopen(fn,m) -#endif -void sig_dump(int sn) -{ - FILE *fp; - char file[256]; - int no = 0; - - crash_flag = 1; - // search for a usable filename - do { - sprintf (file, "log/%s%04d.stackdump", server_name, ++no); - } while((fp = fopen(file,"r")) && (fclose(fp), no < 9999)); - // dump the trace into the file - - if ((fp = FOPEN_(file, "w", stderr)) != NULL) { - const char *revision; - #ifndef CYGWIN - void* array[20]; - char **stack; - size_t size; - #endif - - ShowNotice ("Dumping stack to '"CL_WHITE"%s"CL_RESET"'...\n", file); - if ((revision = getrevision()) != NULL) - fprintf(fp, "Version: svn%s \n", revision); - else - fprintf(fp, "Version: %2d.%02d.%02d mod%02d \n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION, ATHENA_MOD_VERSION); - fprintf(fp, "Exception: %s \n", strsignal(sn)); - fflush (fp); - - #ifdef CYGWIN - cygwin_stackdump (); - #else - fprintf(fp, "Stack trace:\n"); - size = backtrace (array, 20); - stack = backtrace_symbols (array, size); - for (no = 0; no < size; no++) { - fprintf(fp, "%s\n", stack[no]); - } - fprintf(fp,"End of stack trace\n"); - free(stack); - #endif - - ShowNotice("%s Saved.\n", file); - fflush(stdout); - fclose(fp); - } - - sig_final(); // Log our uptime - // Pass the signal to the system's default handler - compat_signal(sn, SIG_DFL); - raise(sn); -} - -/*========================================= - * Shutting down (Program did not crash ^^) - * - Log our current up time - *----------------------------------------- - */ -int sig_final () -{ - time_t curtime; - char curtime2[24]; - FILE *fp; - long seconds = 0, day = 24*60*60, hour = 60*60, - minute = 60, days = 0, hours = 0, minutes = 0; - - fp = fopen("log/uptime.log","a"); - if (fp) { - time(&curtime); - strftime(curtime2, 24, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); - - seconds = getuptime(); - days = seconds/day; - seconds -= (seconds/day>0)?(seconds/day)*day:0; - hours = seconds/hour; - seconds -= (seconds/hour>0)?(seconds/hour)*hour:0; - minutes = seconds/minute; - seconds -= (seconds/minute>0)?(seconds/minute)*minute:0; - - fprintf(fp, "%s: %s %s - %ld days, %ld hours, %ld minutes, %ld seconds.\n", - curtime2, server_name, (crash_flag ? "crashed" : "uptime"), - days, hours, minutes, seconds); - fclose(fp); - } - - return 1; -} - -/*========================================= - * Register the signal handlers - *----------------------------------------- - */ -int sig_init () -{ - void (*func) = sig_dump; -#ifdef CYGWIN // test if dumper is enabled - char *buf = getenv ("CYGWIN"); - if (buf && strstr(buf, "error_start") != NULL) - func = SIG_DFL; -#endif - - IMPORT_SYMBOL(server_name, 1); - IMPORT_SYMBOL(getrevision, 6); - IMPORT_SYMBOL(getuptime, 11); - - compat_signal(SIGSEGV, func); - compat_signal(SIGFPE, func); - compat_signal(SIGILL, func); - #ifndef __WIN32 - compat_signal(SIGBUS, func); - #endif - - return 1; -} -#endif - +// $Id: sig.c 1 2005-6-13 3:17:17 PM Celestia $ + +#include +#include +#include +#include +#include +#include "../common/plugin.h" +#include "../common/version.h" +#include "../common/showmsg.h" + +PLUGIN_INFO = { + "Signals", + PLUGIN_CORE, + "1.1", + PLUGIN_VERSION, + "Handles program signals" +}; + +PLUGIN_EVENTS_TABLE = { + { "sig_init", "Plugin_Init" }, + { "sig_final", "Plugin_Final" }, + { NULL, NULL } +}; + +////////////////////////////////////// + +#if defined(_WIN32) || defined(MINGW) + int sig_init() { + ShowError("sig: This plugin is not supported - Enable 'exchndl' instead!\n"); + return 0; + } + int sig_final() { return 0; } +#elif defined (__NETBSD__) || defined (__FREEBSD__) + int sig_init() { + ShowError("sig: This plugin is not supported!\n"); + return 0; + } + int sig_final() { return 0; } +#else + +////////////////////////////////////// + +#if !defined(CYGWIN) + #include +#endif + +const char* (*getrevision)(); +unsigned long (*getuptime)(); +char *server_name; +int crash_flag = 0; + +extern const char *strsignal(int); +int sig_final (); + +// by Gabuzomeu +// This is an implementation of signal() using sigaction() for portability. +// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced +// Programming in the UNIX Environment_. +// +#ifdef WIN32 // windows don't have SIGPIPE +#define SIGPIPE SIGINT +#endif + +#ifndef POSIX +#define compat_signal(signo, func) signal(signo, func) +#else +sigfunc *compat_signal(int signo, sigfunc *func) +{ + struct sigaction sact, oact; + + sact.sa_handler = func; + sigemptyset(&sact.sa_mask); + sact.sa_flags = 0; +#ifdef SA_INTERRUPT + sact.sa_flags |= SA_INTERRUPT; /* SunOS */ +#endif + + if (sigaction(signo, &sact, &oact) < 0) + return (SIG_ERR); + + return (oact.sa_handler); +} +#endif + +/*========================================= + * Dumps the stack using glibc's backtrace + *----------------------------------------- + */ +#ifdef CYGWIN + #define FOPEN_ freopen + extern void cygwin_stackdump(); +#else + #define FOPEN_(fn,m,s) fopen(fn,m) +#endif +void sig_dump(int sn) +{ + FILE *fp; + char file[256]; + int no = 0; + + crash_flag = 1; + // search for a usable filename + do { + sprintf (file, "log/%s%04d.stackdump", server_name, ++no); + } while((fp = fopen(file,"r")) && (fclose(fp), no < 9999)); + // dump the trace into the file + + if ((fp = FOPEN_(file, "w", stderr)) != NULL) { + const char *revision; + #ifndef CYGWIN + void* array[20]; + char **stack; + size_t size; + #endif + + ShowNotice ("Dumping stack to '"CL_WHITE"%s"CL_RESET"'...\n", file); + if ((revision = getrevision()) != NULL) + fprintf(fp, "Version: svn%s \n", revision); + else + fprintf(fp, "Version: %2d.%02d.%02d mod%02d \n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION, ATHENA_MOD_VERSION); + fprintf(fp, "Exception: %s \n", strsignal(sn)); + fflush (fp); + + #ifdef CYGWIN + cygwin_stackdump (); + #else + fprintf(fp, "Stack trace:\n"); + size = backtrace (array, 20); + stack = backtrace_symbols (array, size); + for (no = 0; no < size; no++) { + fprintf(fp, "%s\n", stack[no]); + } + fprintf(fp,"End of stack trace\n"); + free(stack); + #endif + + ShowNotice("%s Saved.\n", file); + fflush(stdout); + fclose(fp); + } + + sig_final(); // Log our uptime + // Pass the signal to the system's default handler + compat_signal(sn, SIG_DFL); + raise(sn); +} + +/*========================================= + * Shutting down (Program did not crash ^^) + * - Log our current up time + *----------------------------------------- + */ +int sig_final () +{ + time_t curtime; + char curtime2[24]; + FILE *fp; + long seconds = 0, day = 24*60*60, hour = 60*60, + minute = 60, days = 0, hours = 0, minutes = 0; + + fp = fopen("log/uptime.log","a"); + if (fp) { + time(&curtime); + strftime(curtime2, 24, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); + + seconds = getuptime(); + days = seconds/day; + seconds -= (seconds/day>0)?(seconds/day)*day:0; + hours = seconds/hour; + seconds -= (seconds/hour>0)?(seconds/hour)*hour:0; + minutes = seconds/minute; + seconds -= (seconds/minute>0)?(seconds/minute)*minute:0; + + fprintf(fp, "%s: %s %s - %ld days, %ld hours, %ld minutes, %ld seconds.\n", + curtime2, server_name, (crash_flag ? "crashed" : "uptime"), + days, hours, minutes, seconds); + fclose(fp); + } + + return 1; +} + +/*========================================= + * Register the signal handlers + *----------------------------------------- + */ +int sig_init () +{ + void (*func) = sig_dump; +#ifdef CYGWIN // test if dumper is enabled + char *buf = getenv ("CYGWIN"); + if (buf && strstr(buf, "error_start") != NULL) + func = SIG_DFL; +#endif + + IMPORT_SYMBOL(server_name, 1); + IMPORT_SYMBOL(getrevision, 6); + IMPORT_SYMBOL(getuptime, 11); + + compat_signal(SIGSEGV, func); + compat_signal(SIGFPE, func); + compat_signal(SIGILL, func); + #ifndef __WIN32 + compat_signal(SIGBUS, func); + #endif + + return 1; +} +#endif + diff --git a/src/plugins/upnp.txt b/src/plugins/upnp.txt index 32d0e75bf..d5d0d43ff 100644 --- a/src/plugins/upnp.txt +++ b/src/plugins/upnp.txt @@ -1,31 +1,31 @@ -// -// UPnP Plugin Configuration -// - -// Enable UPnP -enable_upnp: 1 - -// Remove mapped router ports when shutting down -release_mappings: 1 - -// Close opened firewall ports when shutting down -close_ports: 1 - -// -// You can set these if necessary -// login server port -//login_port: 6900 -// -// char server port -//char_port: 6121 -// -// map server port -//map_port: 5121 -// -// NAT IP address to map your ports to -//nat_ip: 192.168.0.1 - - -// Note: This plugin only works on Windows XP or higher -// For more info on UPnP try here: +// +// UPnP Plugin Configuration +// + +// Enable UPnP +enable_upnp: 1 + +// Remove mapped router ports when shutting down +release_mappings: 1 + +// Close opened firewall ports when shutting down +close_ports: 1 + +// +// You can set these if necessary +// login server port +//login_port: 6900 +// +// char server port +//char_port: 6121 +// +// map server port +//map_port: 5121 +// +// NAT IP address to map your ports to +//nat_ip: 192.168.0.1 + + +// Note: This plugin only works on Windows XP or higher +// For more info on UPnP try here: // http://www.google.com/search?q=what+is+upnp \ No newline at end of file diff --git a/src/tool/adduser.c b/src/tool/adduser.c index 800154997..b8f5d4718 100644 --- a/src/tool/adduser.c +++ b/src/tool/adduser.c @@ -1,100 +1,100 @@ -// (c) eAthena Dev Team - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -/* - This program adds an user to account.txt - Don't usr it When login-sever is working. -*/ - -#include -#include -#include - -char *account_txt = "../save/account.txt"; - -//----------------------------------------------------- -// Function to suppress control characters in a string. -//----------------------------------------------------- -int remove_control_chars(unsigned char *str) { - int i; - int change = 0; - - for(i = 0; str[i]; i++) { - if (str[i] < 32) { - str[i] = '_'; - change = 1; - } - } - - return change; -} - -int main(int argc, char *argv[]) { - - char username[24]; - char password[24]; - char sex[2]; - - int next_id, id; - char line[1024]; - FILE *FPaccin,*FPaccout; - - // Check to see if account.txt exists. - printf("Checking if '%s' file exists...\n", account_txt); - FPaccin = fopen(account_txt, "r"); - if (FPaccin == NULL) { - printf("'%s' file not found!\n", account_txt); - printf("Run the setup wizard please.\n"); - exit(0); - } - - next_id = 2000000; - while(fgets(line, sizeof(line)-1, FPaccin)) { - if (line[0] == '/' && line[1] == '/') { continue; } - if (sscanf(line, "%d\t%%newid%%\n", &id) == 1) { - if (next_id < id) { - next_id = id; - } - } else { - sscanf(line,"%i%[^ ]", &id); - if (next_id <= id) { - next_id = id +1; - } - } - } - close(FPaccin); - printf("File exists.\n"); - - printf("Don't create an account if the login-server is online!!!\n"); - printf("If the login-server is online, press ctrl+C now to stop this software.\n"); - printf("\n"); - - strcpy(username, ""); - while (strlen(username) < 4 || strlen(username) > 23) { - printf("Enter an username (4-23 characters): "); - scanf("%s", &username); - username[23] = 0; - remove_control_chars(username); - } - - strcpy(password, ""); - while (strlen(password) < 4 || strlen(password) > 23) { - printf("Enter a password (4-23 characters): "); - scanf("%s", &password); - password[23] = 0; - remove_control_chars(password); - } - - strcpy(sex, ""); - while (strcmp(sex, "F") != 0 && strcmp(sex, "M") != 0) { - printf("Enter a gender (M for male, F for female): "); - scanf("%s", &sex); - } - - FPaccout = fopen(account_txt, "r+"); - fseek(FPaccout, 0, SEEK_END); - fprintf(FPaccout, "%i %s %s - %s -\r\n", next_id, username, password, sex); - close(FPaccout); - - printf("Account added.\n"); -} +// (c) eAthena Dev Team - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +/* + This program adds an user to account.txt + Don't usr it When login-sever is working. +*/ + +#include +#include +#include + +char *account_txt = "../save/account.txt"; + +//----------------------------------------------------- +// Function to suppress control characters in a string. +//----------------------------------------------------- +int remove_control_chars(unsigned char *str) { + int i; + int change = 0; + + for(i = 0; str[i]; i++) { + if (str[i] < 32) { + str[i] = '_'; + change = 1; + } + } + + return change; +} + +int main(int argc, char *argv[]) { + + char username[24]; + char password[24]; + char sex[2]; + + int next_id, id; + char line[1024]; + FILE *FPaccin,*FPaccout; + + // Check to see if account.txt exists. + printf("Checking if '%s' file exists...\n", account_txt); + FPaccin = fopen(account_txt, "r"); + if (FPaccin == NULL) { + printf("'%s' file not found!\n", account_txt); + printf("Run the setup wizard please.\n"); + exit(0); + } + + next_id = 2000000; + while(fgets(line, sizeof(line)-1, FPaccin)) { + if (line[0] == '/' && line[1] == '/') { continue; } + if (sscanf(line, "%d\t%%newid%%\n", &id) == 1) { + if (next_id < id) { + next_id = id; + } + } else { + sscanf(line,"%i%[^ ]", &id); + if (next_id <= id) { + next_id = id +1; + } + } + } + close(FPaccin); + printf("File exists.\n"); + + printf("Don't create an account if the login-server is online!!!\n"); + printf("If the login-server is online, press ctrl+C now to stop this software.\n"); + printf("\n"); + + strcpy(username, ""); + while (strlen(username) < 4 || strlen(username) > 23) { + printf("Enter an username (4-23 characters): "); + scanf("%s", &username); + username[23] = 0; + remove_control_chars(username); + } + + strcpy(password, ""); + while (strlen(password) < 4 || strlen(password) > 23) { + printf("Enter a password (4-23 characters): "); + scanf("%s", &password); + password[23] = 0; + remove_control_chars(password); + } + + strcpy(sex, ""); + while (strcmp(sex, "F") != 0 && strcmp(sex, "M") != 0) { + printf("Enter a gender (M for male, F for female): "); + scanf("%s", &sex); + } + + FPaccout = fopen(account_txt, "r+"); + fseek(FPaccout, 0, SEEK_END); + fprintf(FPaccout, "%i %s %s - %s -\r\n", next_id, username, password, sex); + close(FPaccout); + + printf("Account added.\n"); +} diff --git a/src/tool/convert.c b/src/tool/convert.c index 2e81bfedd..a71c8c4a6 100644 --- a/src/tool/convert.c +++ b/src/tool/convert.c @@ -1,299 +1,299 @@ -// (c) eAthena Dev Team - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include - -#define RETCODE "\r\n" - -#define MAX_INVENTORY 100 -#define MAX_CART 100 -#define MAX_SKILL 350 -#define GLOBAL_REG_NUM 16 - -struct item { - int id; - short nameid; - short amount; - short equip; - char identify; - char refine; - char attribute; - short card[4]; -}; -struct point{ - char map[16]; - short x,y; -}; -struct skill { - unsigned short id,lv,flag; -}; -struct global_reg { - char str[16]; - int value; -}; - -struct mmo_charstatus { - int char_id; - int account_id; - int base_exp,job_exp,zeny; - - short class; - short status_point,skill_point; - short hp,max_hp,sp,max_sp; - short option,karma,manner; - short hair,hair_color,clothes_color; - int party_id,guild_id,pet_id; - - short weapon,shield; - short head_top,head_mid,head_bottom; - - char name[24]; - unsigned char base_level,job_level; - unsigned char str,agi,vit,int_,dex,luk,char_num,sex; - - struct point last_point,save_point,memo_point[3]; - struct item inventory[MAX_INVENTORY],cart[MAX_CART]; - struct skill skill[MAX_SKILL]; - int global_reg_num; - struct global_reg global_reg[GLOBAL_REG_NUM]; -}; - -int mmo_char_tostr(char *str,struct mmo_charstatus *p) -{ - int i; - sprintf(str,"%d\t%d,%d\t%s\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" - "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" - "\t%s,%d,%d\t%s,%d,%d", - p->char_id,p->account_id,p->char_num,p->name, // - p->class,p->base_level,p->job_level, - p->base_exp,p->job_exp,p->zeny, - p->hp,p->max_hp,p->sp,p->max_sp, - p->str,p->agi,p->vit,p->int_,p->dex,p->luk, - p->status_point,p->skill_point, - p->option,p->karma,p->manner, // - p->party_id,p->guild_id,p->pet_id, - p->hair,p->hair_color,p->clothes_color, - p->weapon,p->shield,p->head_top,p->head_mid,p->head_bottom, - p->last_point.map,p->last_point.x,p->last_point.y, // - p->save_point.map,p->save_point.x,p->save_point.y - ); - strcat(str,"\t"); - for(i=0;i<3;i++) - if(p->memo_point[i].map[0]){ - sprintf(str+strlen(str),"%s,%d,%d",p->memo_point[i].map,p->memo_point[i].x,p->memo_point[i].y); - } - strcat(str,"\t"); - for(i=0;iinventory[i].nameid){ - sprintf(str+strlen(str),"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", - p->inventory[i].id,p->inventory[i].nameid,p->inventory[i].amount,p->inventory[i].equip, - p->inventory[i].identify,p->inventory[i].refine,p->inventory[i].attribute, - p->inventory[i].card[0],p->inventory[i].card[1],p->inventory[i].card[2],p->inventory[i].card[3]); - } - strcat(str,"\t"); - for(i=0;icart[i].nameid){ - sprintf(str+strlen(str),"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", - p->cart[i].id,p->cart[i].nameid,p->cart[i].amount,p->cart[i].equip, - p->cart[i].identify,p->cart[i].refine,p->cart[i].attribute, - p->cart[i].card[0],p->cart[i].card[1],p->cart[i].card[2],p->cart[i].card[3]); - } - strcat(str,"\t"); - for(i=0;iskill[i].id){ - sprintf(str+strlen(str),"%d,%d ",p->skill[i].id,p->skill[i].lv); - } - strcat(str,"\t"); - for(i=0;iglobal_reg_num;i++) - sprintf(str+strlen(str),"%s,%d ",p->global_reg[i].str,p->global_reg[i].value); - strcat(str,"\t"); - return 0; -} - -int mmo_char_fromstr(char *str,struct mmo_charstatus *p) -{ - int tmp_int[256]; - int set,next,len,i; - - set=sscanf(str,"%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" - "\t%d,%d,%d\t%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" - "\t%[^,],%d,%d\t%[^,],%d,%d%n", - &tmp_int[0],&tmp_int[1],&tmp_int[2],p->name, // - &tmp_int[3],&tmp_int[4],&tmp_int[5], - &tmp_int[6],&tmp_int[7],&tmp_int[8], - &tmp_int[9],&tmp_int[10],&tmp_int[11],&tmp_int[12], - &tmp_int[13],&tmp_int[14],&tmp_int[15],&tmp_int[16],&tmp_int[17],&tmp_int[18], - &tmp_int[19],&tmp_int[20], - &tmp_int[21],&tmp_int[22],&tmp_int[23], // - &tmp_int[24],&tmp_int[25], - &tmp_int[26],&tmp_int[27],&tmp_int[28], - &tmp_int[29],&tmp_int[30],&tmp_int[31],&tmp_int[32],&tmp_int[33], - p->last_point.map,&tmp_int[34],&tmp_int[35], // - p->save_point.map,&tmp_int[36],&tmp_int[37],&next - ); - p->char_id=tmp_int[0]; - p->account_id=tmp_int[1]; - p->char_num=tmp_int[2]; - p->class=tmp_int[3]; - p->base_level=tmp_int[4]; - p->job_level=tmp_int[5]; - p->base_exp=tmp_int[6]; - p->job_exp=tmp_int[7]; - p->zeny=tmp_int[8]; - p->hp=tmp_int[9]; - p->max_hp=tmp_int[10]; - p->sp=tmp_int[11]; - p->max_sp=tmp_int[12]; - p->str=tmp_int[13]; - p->agi=tmp_int[14]; - p->vit=tmp_int[15]; - p->int_=tmp_int[16]; - p->dex=tmp_int[17]; - p->luk=tmp_int[18]; - p->status_point=tmp_int[19]; - p->skill_point=tmp_int[20]; - p->option=tmp_int[21]; - p->karma=tmp_int[22]; - p->manner=tmp_int[23]; - p->party_id=tmp_int[24]; - p->guild_id=tmp_int[25]; - p->pet_id=0; - p->hair=tmp_int[26]; - p->hair_color=tmp_int[27]; - p->clothes_color=tmp_int[28]; - p->weapon=tmp_int[29]; - p->shield=tmp_int[30]; - p->head_top=tmp_int[31]; - p->head_mid=tmp_int[32]; - p->head_bottom=tmp_int[33]; - p->last_point.x=tmp_int[34]; - p->last_point.y=tmp_int[35]; - p->save_point.x=tmp_int[36]; - p->save_point.y=tmp_int[37]; - if(set!=41) - return 0; - if(str[next]=='\n' || str[next]=='\r') - return 1; // 新規データ - next++; - for(i=0;str[next] && str[next]!='\t';i++){ - set=sscanf(str+next,"%[^,],%d,%d%n",p->memo_point[i].map,&tmp_int[0],&tmp_int[1],&len); - if(set!=3) - return 0; - p->memo_point[i].x=tmp_int[0]; - p->memo_point[i].y=tmp_int[1]; - next+=len; - if(str[next]==' ') - next++; - } - next++; - for(i=0;str[next] && str[next]!='\t';i++){ - set=sscanf(str+next,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0],&tmp_int[1],&tmp_int[2],&tmp_int[3], - &tmp_int[4],&tmp_int[5],&tmp_int[6], - &tmp_int[7],&tmp_int[8],&tmp_int[9],&tmp_int[10],&len); - if(set!=11) - return 0; - p->inventory[i].id=tmp_int[0]; - p->inventory[i].nameid=tmp_int[1]; - p->inventory[i].amount=tmp_int[2]; - p->inventory[i].equip=tmp_int[3]; - p->inventory[i].identify=tmp_int[4]; - p->inventory[i].refine=tmp_int[5]; - p->inventory[i].attribute=tmp_int[6]; - p->inventory[i].card[0]=tmp_int[7]; - p->inventory[i].card[1]=tmp_int[8]; - p->inventory[i].card[2]=tmp_int[9]; - p->inventory[i].card[3]=tmp_int[10]; - next+=len; - if(str[next]==' ') - next++; - } - next++; - for(i=0;str[next] && str[next]!='\t';i++){ - set=sscanf(str+next,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", - &tmp_int[0],&tmp_int[1],&tmp_int[2],&tmp_int[3], - &tmp_int[4],&tmp_int[5],&tmp_int[6], - &tmp_int[7],&tmp_int[8],&tmp_int[9],&tmp_int[10],&len); - if(set!=11) - return 0; - p->cart[i].id=tmp_int[0]; - p->cart[i].nameid=tmp_int[1]; - p->cart[i].amount=tmp_int[2]; - p->cart[i].equip=tmp_int[3]; - p->cart[i].identify=tmp_int[4]; - p->cart[i].refine=tmp_int[5]; - p->cart[i].attribute=tmp_int[6]; - p->cart[i].card[0]=tmp_int[7]; - p->cart[i].card[1]=tmp_int[8]; - p->cart[i].card[2]=tmp_int[9]; - p->cart[i].card[3]=tmp_int[10]; - next+=len; - if(str[next]==' ') - next++; - } - next++; - for(i=0;str[next] && str[next]!='\t';i++){ - set=sscanf(str+next,"%d,%d%n", - &tmp_int[0],&tmp_int[1],&len); - if(set!=2) - return 0; - p->skill[tmp_int[0]].id=tmp_int[0]; - p->skill[tmp_int[0]].lv=tmp_int[1]; - next+=len; - if(str[next]==' ') - next++; - } - next++; - for(i=0;str[next] && str[next]!='\t' && str[next]!='\n' && str[next]!='\r';i++){ //global_reg実装以前のathena.txt互換のため一応'\n'チェック - set=sscanf(str+next,"%[^,],%d%n", - p->global_reg[i].str,&p->global_reg[i].value,&len); - if(set!=2) - return 0; - next+=len; - if(str[next]==' ') - next++; - } - p->global_reg_num=i; - return 1; -} - -int mmo_char_convert(char *fname1,char *fname2) -{ - char line[65536]; - int ret; - struct mmo_charstatus char_dat; - FILE *ifp,*ofp; - - ifp=fopen(fname1,"r"); - ofp=fopen(fname2,"w"); - if(ifp==NULL) { - printf("file not found %s\n",fname1); - return 0; - } - if(ofp==NULL) { - printf("file open error %s\n",fname2); - return 0; - } - while(fgets(line,65535,ifp)){ - memset(&char_dat,0,sizeof(struct mmo_charstatus)); - ret=mmo_char_fromstr(line,&char_dat); - if(ret){ - mmo_char_tostr(line,&char_dat); - fprintf(ofp,"%s" RETCODE,line); - } - } - fcloseall(); - return 0; -} - -int main(int argc,char *argv[]) -{ - if(argc < 3) { - printf("Usage: convert \n"); - exit(0); - } - mmo_char_convert(argv[1],argv[2]); - - return 0; -} +// (c) eAthena Dev Team - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include + +#define RETCODE "\r\n" + +#define MAX_INVENTORY 100 +#define MAX_CART 100 +#define MAX_SKILL 350 +#define GLOBAL_REG_NUM 16 + +struct item { + int id; + short nameid; + short amount; + short equip; + char identify; + char refine; + char attribute; + short card[4]; +}; +struct point{ + char map[16]; + short x,y; +}; +struct skill { + unsigned short id,lv,flag; +}; +struct global_reg { + char str[16]; + int value; +}; + +struct mmo_charstatus { + int char_id; + int account_id; + int base_exp,job_exp,zeny; + + short class; + short status_point,skill_point; + short hp,max_hp,sp,max_sp; + short option,karma,manner; + short hair,hair_color,clothes_color; + int party_id,guild_id,pet_id; + + short weapon,shield; + short head_top,head_mid,head_bottom; + + char name[24]; + unsigned char base_level,job_level; + unsigned char str,agi,vit,int_,dex,luk,char_num,sex; + + struct point last_point,save_point,memo_point[3]; + struct item inventory[MAX_INVENTORY],cart[MAX_CART]; + struct skill skill[MAX_SKILL]; + int global_reg_num; + struct global_reg global_reg[GLOBAL_REG_NUM]; +}; + +int mmo_char_tostr(char *str,struct mmo_charstatus *p) +{ + int i; + sprintf(str,"%d\t%d,%d\t%s\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%s,%d,%d\t%s,%d,%d", + p->char_id,p->account_id,p->char_num,p->name, // + p->class,p->base_level,p->job_level, + p->base_exp,p->job_exp,p->zeny, + p->hp,p->max_hp,p->sp,p->max_sp, + p->str,p->agi,p->vit,p->int_,p->dex,p->luk, + p->status_point,p->skill_point, + p->option,p->karma,p->manner, // + p->party_id,p->guild_id,p->pet_id, + p->hair,p->hair_color,p->clothes_color, + p->weapon,p->shield,p->head_top,p->head_mid,p->head_bottom, + p->last_point.map,p->last_point.x,p->last_point.y, // + p->save_point.map,p->save_point.x,p->save_point.y + ); + strcat(str,"\t"); + for(i=0;i<3;i++) + if(p->memo_point[i].map[0]){ + sprintf(str+strlen(str),"%s,%d,%d",p->memo_point[i].map,p->memo_point[i].x,p->memo_point[i].y); + } + strcat(str,"\t"); + for(i=0;iinventory[i].nameid){ + sprintf(str+strlen(str),"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->inventory[i].id,p->inventory[i].nameid,p->inventory[i].amount,p->inventory[i].equip, + p->inventory[i].identify,p->inventory[i].refine,p->inventory[i].attribute, + p->inventory[i].card[0],p->inventory[i].card[1],p->inventory[i].card[2],p->inventory[i].card[3]); + } + strcat(str,"\t"); + for(i=0;icart[i].nameid){ + sprintf(str+strlen(str),"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->cart[i].id,p->cart[i].nameid,p->cart[i].amount,p->cart[i].equip, + p->cart[i].identify,p->cart[i].refine,p->cart[i].attribute, + p->cart[i].card[0],p->cart[i].card[1],p->cart[i].card[2],p->cart[i].card[3]); + } + strcat(str,"\t"); + for(i=0;iskill[i].id){ + sprintf(str+strlen(str),"%d,%d ",p->skill[i].id,p->skill[i].lv); + } + strcat(str,"\t"); + for(i=0;iglobal_reg_num;i++) + sprintf(str+strlen(str),"%s,%d ",p->global_reg[i].str,p->global_reg[i].value); + strcat(str,"\t"); + return 0; +} + +int mmo_char_fromstr(char *str,struct mmo_charstatus *p) +{ + int tmp_int[256]; + int set,next,len,i; + + set=sscanf(str,"%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%[^,],%d,%d\t%[^,],%d,%d%n", + &tmp_int[0],&tmp_int[1],&tmp_int[2],p->name, // + &tmp_int[3],&tmp_int[4],&tmp_int[5], + &tmp_int[6],&tmp_int[7],&tmp_int[8], + &tmp_int[9],&tmp_int[10],&tmp_int[11],&tmp_int[12], + &tmp_int[13],&tmp_int[14],&tmp_int[15],&tmp_int[16],&tmp_int[17],&tmp_int[18], + &tmp_int[19],&tmp_int[20], + &tmp_int[21],&tmp_int[22],&tmp_int[23], // + &tmp_int[24],&tmp_int[25], + &tmp_int[26],&tmp_int[27],&tmp_int[28], + &tmp_int[29],&tmp_int[30],&tmp_int[31],&tmp_int[32],&tmp_int[33], + p->last_point.map,&tmp_int[34],&tmp_int[35], // + p->save_point.map,&tmp_int[36],&tmp_int[37],&next + ); + p->char_id=tmp_int[0]; + p->account_id=tmp_int[1]; + p->char_num=tmp_int[2]; + p->class=tmp_int[3]; + p->base_level=tmp_int[4]; + p->job_level=tmp_int[5]; + p->base_exp=tmp_int[6]; + p->job_exp=tmp_int[7]; + p->zeny=tmp_int[8]; + p->hp=tmp_int[9]; + p->max_hp=tmp_int[10]; + p->sp=tmp_int[11]; + p->max_sp=tmp_int[12]; + p->str=tmp_int[13]; + p->agi=tmp_int[14]; + p->vit=tmp_int[15]; + p->int_=tmp_int[16]; + p->dex=tmp_int[17]; + p->luk=tmp_int[18]; + p->status_point=tmp_int[19]; + p->skill_point=tmp_int[20]; + p->option=tmp_int[21]; + p->karma=tmp_int[22]; + p->manner=tmp_int[23]; + p->party_id=tmp_int[24]; + p->guild_id=tmp_int[25]; + p->pet_id=0; + p->hair=tmp_int[26]; + p->hair_color=tmp_int[27]; + p->clothes_color=tmp_int[28]; + p->weapon=tmp_int[29]; + p->shield=tmp_int[30]; + p->head_top=tmp_int[31]; + p->head_mid=tmp_int[32]; + p->head_bottom=tmp_int[33]; + p->last_point.x=tmp_int[34]; + p->last_point.y=tmp_int[35]; + p->save_point.x=tmp_int[36]; + p->save_point.y=tmp_int[37]; + if(set!=41) + return 0; + if(str[next]=='\n' || str[next]=='\r') + return 1; // 新規データ + next++; + for(i=0;str[next] && str[next]!='\t';i++){ + set=sscanf(str+next,"%[^,],%d,%d%n",p->memo_point[i].map,&tmp_int[0],&tmp_int[1],&len); + if(set!=3) + return 0; + p->memo_point[i].x=tmp_int[0]; + p->memo_point[i].y=tmp_int[1]; + next+=len; + if(str[next]==' ') + next++; + } + next++; + for(i=0;str[next] && str[next]!='\t';i++){ + set=sscanf(str+next,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0],&tmp_int[1],&tmp_int[2],&tmp_int[3], + &tmp_int[4],&tmp_int[5],&tmp_int[6], + &tmp_int[7],&tmp_int[8],&tmp_int[9],&tmp_int[10],&len); + if(set!=11) + return 0; + p->inventory[i].id=tmp_int[0]; + p->inventory[i].nameid=tmp_int[1]; + p->inventory[i].amount=tmp_int[2]; + p->inventory[i].equip=tmp_int[3]; + p->inventory[i].identify=tmp_int[4]; + p->inventory[i].refine=tmp_int[5]; + p->inventory[i].attribute=tmp_int[6]; + p->inventory[i].card[0]=tmp_int[7]; + p->inventory[i].card[1]=tmp_int[8]; + p->inventory[i].card[2]=tmp_int[9]; + p->inventory[i].card[3]=tmp_int[10]; + next+=len; + if(str[next]==' ') + next++; + } + next++; + for(i=0;str[next] && str[next]!='\t';i++){ + set=sscanf(str+next,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0],&tmp_int[1],&tmp_int[2],&tmp_int[3], + &tmp_int[4],&tmp_int[5],&tmp_int[6], + &tmp_int[7],&tmp_int[8],&tmp_int[9],&tmp_int[10],&len); + if(set!=11) + return 0; + p->cart[i].id=tmp_int[0]; + p->cart[i].nameid=tmp_int[1]; + p->cart[i].amount=tmp_int[2]; + p->cart[i].equip=tmp_int[3]; + p->cart[i].identify=tmp_int[4]; + p->cart[i].refine=tmp_int[5]; + p->cart[i].attribute=tmp_int[6]; + p->cart[i].card[0]=tmp_int[7]; + p->cart[i].card[1]=tmp_int[8]; + p->cart[i].card[2]=tmp_int[9]; + p->cart[i].card[3]=tmp_int[10]; + next+=len; + if(str[next]==' ') + next++; + } + next++; + for(i=0;str[next] && str[next]!='\t';i++){ + set=sscanf(str+next,"%d,%d%n", + &tmp_int[0],&tmp_int[1],&len); + if(set!=2) + return 0; + p->skill[tmp_int[0]].id=tmp_int[0]; + p->skill[tmp_int[0]].lv=tmp_int[1]; + next+=len; + if(str[next]==' ') + next++; + } + next++; + for(i=0;str[next] && str[next]!='\t' && str[next]!='\n' && str[next]!='\r';i++){ //global_reg実装以前のathena.txt互換のため一応'\n'チェック + set=sscanf(str+next,"%[^,],%d%n", + p->global_reg[i].str,&p->global_reg[i].value,&len); + if(set!=2) + return 0; + next+=len; + if(str[next]==' ') + next++; + } + p->global_reg_num=i; + return 1; +} + +int mmo_char_convert(char *fname1,char *fname2) +{ + char line[65536]; + int ret; + struct mmo_charstatus char_dat; + FILE *ifp,*ofp; + + ifp=fopen(fname1,"r"); + ofp=fopen(fname2,"w"); + if(ifp==NULL) { + printf("file not found %s\n",fname1); + return 0; + } + if(ofp==NULL) { + printf("file open error %s\n",fname2); + return 0; + } + while(fgets(line,65535,ifp)){ + memset(&char_dat,0,sizeof(struct mmo_charstatus)); + ret=mmo_char_fromstr(line,&char_dat); + if(ret){ + mmo_char_tostr(line,&char_dat); + fprintf(ofp,"%s" RETCODE,line); + } + } + fcloseall(); + return 0; +} + +int main(int argc,char *argv[]) +{ + if(argc < 3) { + printf("Usage: convert \n"); + exit(0); + } + mmo_char_convert(argv[1],argv[2]); + + return 0; +} diff --git a/src/txt-converter/char-converter.c b/src/txt-converter/char-converter.c index 4d435b4d1..29d1c393f 100644 --- a/src/txt-converter/char-converter.c +++ b/src/txt-converter/char-converter.c @@ -1,281 +1,281 @@ -// (c) eAthena Dev Team - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include - -#include "../common/core.h" -#include "../common/strlib.h" -#include "../common/mmo.h" -#include "../common/showmsg.h" -#include "../common/mapindex.h" - -#include "../char/char.h" -#include "../char/int_storage.h" -#include "../char/int_pet.h" -#include "../char/int_party.h" -#include "../char/int_guild.h" -#include "../char/inter.h" - -#include "../char_sql/char.h" -#include "../char_sql/int_storage.h" -#include "../char_sql/int_pet.h" -#include "../char_sql/int_party.h" -#include "../char_sql/int_guild.h" -#include "../char_sql/inter.h" - -char t_name[256]; - -#define CHAR_CONF_NAME "conf/char_athena.conf" -#define SQL_CONF_NAME "conf/inter_athena.conf" -#define INTER_CONF_NAME "conf/inter_athena.conf" -//-------------------------------------------------------- -int convert_init(void){ - char line[65536]; - int ret; - int set,tmp_int[2], lineno, count; - char input; - FILE *fp; - - ShowWarning("Make sure you backup your databases before continuing!\n"); - printf("\n"); - ShowNotice("Do you wish to convert your Character Database to SQL? (y/n) : "); - input=getchar(); - if(input == 'y' || input == 'Y'){ - struct character_data char_dat; - struct accreg reg; - - ShowStatus("Converting Character Database...\n"); - fp = fopen(char_txt, "r"); - memset (&char_dat, 0, sizeof(struct character_data)); - if(fp==NULL) { - ShowError("Unable to open file [%s]!\n", char_txt); - return 0; - } - lineno = count = 0; - while(fgets(line, 65535, fp)){ - lineno++; - memset(&char_dat, 0, sizeof(char_dat)); - ret=mmo_char_fromstr(line, &char_dat.status, char_dat.global, &char_dat.global_num); - if(ret > 0){ - count++; - parse_friend_txt(&char_dat.status); //Retrieve friends. - mmo_char_tosql(char_dat.status.char_id , &char_dat.status); - - memset(®, 0, sizeof(reg)); - reg.account_id = char_dat.status.account_id; - reg.char_id = char_dat.status.char_id; - reg.reg_num = char_dat.global_num; - memcpy(®.reg, &char_dat.global, reg.reg_num*sizeof(struct global_reg)); - inter_accreg_tosql(reg.account_id, reg.char_id, ®, 3); //Type 3: Character regs - } else { - ShowError("Error %d converting character line [%s] (at %s:%d).\n", ret, line, char_txt, lineno); - } - } - ShowStatus("Converted %d characters.\n", count); - fclose(fp); - ShowStatus("Converting Account variables Database...\n"); - if( (fp=fopen(accreg_txt,"r")) ==NULL ) - { - ShowError("Unable to open file %s!", accreg_txt); - return 1; - } - lineno=count=0; - while(fgets(line, sizeof(line), fp)){ - lineno++; - memset (®, 0, sizeof(struct accreg)); - if(inter_accreg_fromstr(line, ®) == 0 && reg.account_id > 0) { - count++; - inter_accreg_tosql(reg.account_id, 0, ®, 2); //Type 2: Account regs - }else{ - ShowError("accreg reading: broken data [%s] at %s:%d\n", line, accreg_txt, lineno); - } - } - ShowStatus("Converted %d account registries.\n", count); - fclose(fp); - } - - while(getchar() != '\n'); - printf("\n"); - ShowNotice("Do you wish to convert your Storage Database to SQL? (y/n) : "); - input=getchar(); - if(input == 'y' || input == 'Y') { - struct storage storage_; - printf("\n"); - ShowStatus("Converting Storage Database...\n"); - fp=fopen(storage_txt,"r"); - if(fp==NULL){ - ShowError("cant't read : %s\n",storage_txt); - return 0; - } - lineno=count=0; - while(fgets(line,65535,fp)){ - lineno++; - set=sscanf(line,"%d,%d",&tmp_int[0],&tmp_int[1]); - if(set==2) { - memset(&storage_, 0, sizeof(struct storage)); - storage_.account_id=tmp_int[0]; - if (storage_fromstr(line,&storage_) == 0) { - count++; - storage_tosql(storage_.account_id,&storage_); //to sql. (dump) - } else { - ShowError("Error parsing storage line [%s] (at %s:%d)\n", line, storage_txt, lineno); - } - } - } - ShowStatus("Converted %d storages.\n", count); - fclose(fp); - } - - while(getchar() != '\n'); - printf("\n"); - ShowNotice("Do you wish to convert your Pet Database to SQL? (y/n) : "); - input=getchar(); - if(input == 'y' || input == 'Y') { - struct s_pet p; - printf("\n"); - ShowStatus("Converting Pet Database...\n"); - if( (fp=fopen(pet_txt,"r")) ==NULL ) - { - ShowError("Unable to open file %s!", pet_txt); - return 1; - } - lineno=count=0; - while(fgets(line, sizeof(line), fp)){ - lineno++; - memset (&p, 0, sizeof(struct s_pet)); - if(inter_pet_fromstr(line, &p)==0 && p.pet_id>0){ - count++; - inter_pet_tosql(p.pet_id,&p); - }else{ - ShowError("pet reading: broken data [%s] at %s:%d\n", line, pet_txt, lineno); - } - } - ShowStatus("Converted %d pets.\n", count); - fclose(fp); - } - - while(getchar() != '\n'); - printf("\n"); - ShowNotice("Do you wish to convert your Party Database to SQL? (y/n) : "); - input=getchar(); - if(input == 'y' || input == 'Y') { - struct party p; - printf("\n"); - ShowStatus("Converting Party Database...\n"); - if( (fp=fopen(party_txt,"r")) ==NULL ) - { - ShowError("Unable to open file %s!", party_txt); - return 1; - } - lineno=count=0; - while(fgets(line, sizeof(line), fp)){ - lineno++; - memset (&p, 0, sizeof(struct party)); - if(inter_party_fromstr(line, &p) == 0 && - p.party_id > 0 && - inter_party_tosql(&p, PS_CREATE, 0)) - count++; - else{ - ShowError("party reading: broken data [%s] at %s:%d\n", line, pet_txt, lineno); - } - } - ShowStatus("Converted %d parties.\n", count); - fclose(fp); - } - - while(getchar() != '\n'); - printf("\n"); - ShowNotice("Do you wish to convert your Guilds/Guild Castles Database to SQL? (y/n) : "); - input=getchar(); - if(input == 'y' || input == 'Y') { - struct guild g; - struct guild_castle gc; - printf("\n"); - ShowStatus("Converting Guild Database...\n"); - if( (fp=fopen(guild_txt,"r")) ==NULL ) - { - ShowError("Unable to open file %s!", guild_txt); - return 1; - } - lineno=count=0; - while(fgets(line, sizeof(line), fp)){ - lineno++; - memset (&g, 0, sizeof(struct guild)); - if (inter_guild_fromstr(line, &g) == 0 && - g.guild_id > 0 && - inter_guild_tosql(&g,GS_MASK)) - count++; - else - ShowError("guild reading: broken data [%s] at %s:%d\n", line, guild_txt, lineno); - } - ShowStatus("Converted %d guilds.\n", count); - fclose(fp); - ShowStatus("Converting Guild Castles Database...\n"); - if( (fp=fopen(castle_txt,"r")) ==NULL ) - { - ShowError("Unable to open file %s!", castle_txt); - return 1; - } - lineno=count=0; - while(fgets(line, sizeof(line), fp)){ - lineno++; - memset (&gc, 0, sizeof(struct guild_castle)); - if (inter_guildcastle_fromstr(line, &gc) == 0) { - inter_guildcastle_tosql(&gc); - count++; - } - else - ShowError("guild castle reading: broken data [%s] at %s:%d\n", line, castle_txt, lineno); - } - ShowStatus("Converted %d guild castles.\n", count); - fclose(fp); - } - - while(getchar() != '\n'); - printf("\n"); - ShowNotice("Do you wish to convert your Guild Storage Database to SQL? (y/n) : "); - input=getchar(); - if(input == 'y' || input == 'Y') { - struct guild_storage storage_; - printf("\n"); - ShowStatus("Converting Guild Storage Database...\n"); - fp=fopen(guild_storage_txt,"r"); - if(fp==NULL){ - ShowError("cant't read : %s\n",guild_storage_txt); - return 0; - } - lineno=count=0; - while(fgets(line,65535,fp)){ - lineno++; - memset(&storage_, 0, sizeof(struct guild_storage)); - if (sscanf(line,"%d",&storage_.guild_id) == 1 && - storage_.guild_id > 0 && - guild_storage_fromstr(line,&storage_) == 0 - ) { - count++; - guild_storage_tosql(storage_.guild_id, &storage_); - } else - ShowError("Error parsing guild storage line [%s] (at %s:%d)\n", line, guild_storage_txt, lineno); - } - ShowStatus("Converted %d guild storages.\n", count); - fclose(fp); - } - return 0; -} - -int do_init(int argc, char **argv){ - - char_config_read((argc>1)?argv[1]:CHAR_CONF_NAME); - mapindex_init(); - sql_config_read((argc>2)?argv[2]:SQL_CONF_NAME); - inter_init_txt((argc > 3) ? argv[3] :INTER_CONF_NAME); - inter_init_sql((argc > 3) ? argv[3] :INTER_CONF_NAME); - convert_init(); - ShowStatus("Everything's been converted!\n"); - mapindex_final(); - exit (0); -} - -void do_final () {} +// (c) eAthena Dev Team - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include + +#include "../common/core.h" +#include "../common/strlib.h" +#include "../common/mmo.h" +#include "../common/showmsg.h" +#include "../common/mapindex.h" + +#include "../char/char.h" +#include "../char/int_storage.h" +#include "../char/int_pet.h" +#include "../char/int_party.h" +#include "../char/int_guild.h" +#include "../char/inter.h" + +#include "../char_sql/char.h" +#include "../char_sql/int_storage.h" +#include "../char_sql/int_pet.h" +#include "../char_sql/int_party.h" +#include "../char_sql/int_guild.h" +#include "../char_sql/inter.h" + +char t_name[256]; + +#define CHAR_CONF_NAME "conf/char_athena.conf" +#define SQL_CONF_NAME "conf/inter_athena.conf" +#define INTER_CONF_NAME "conf/inter_athena.conf" +//-------------------------------------------------------- +int convert_init(void){ + char line[65536]; + int ret; + int set,tmp_int[2], lineno, count; + char input; + FILE *fp; + + ShowWarning("Make sure you backup your databases before continuing!\n"); + printf("\n"); + ShowNotice("Do you wish to convert your Character Database to SQL? (y/n) : "); + input=getchar(); + if(input == 'y' || input == 'Y'){ + struct character_data char_dat; + struct accreg reg; + + ShowStatus("Converting Character Database...\n"); + fp = fopen(char_txt, "r"); + memset (&char_dat, 0, sizeof(struct character_data)); + if(fp==NULL) { + ShowError("Unable to open file [%s]!\n", char_txt); + return 0; + } + lineno = count = 0; + while(fgets(line, 65535, fp)){ + lineno++; + memset(&char_dat, 0, sizeof(char_dat)); + ret=mmo_char_fromstr(line, &char_dat.status, char_dat.global, &char_dat.global_num); + if(ret > 0){ + count++; + parse_friend_txt(&char_dat.status); //Retrieve friends. + mmo_char_tosql(char_dat.status.char_id , &char_dat.status); + + memset(®, 0, sizeof(reg)); + reg.account_id = char_dat.status.account_id; + reg.char_id = char_dat.status.char_id; + reg.reg_num = char_dat.global_num; + memcpy(®.reg, &char_dat.global, reg.reg_num*sizeof(struct global_reg)); + inter_accreg_tosql(reg.account_id, reg.char_id, ®, 3); //Type 3: Character regs + } else { + ShowError("Error %d converting character line [%s] (at %s:%d).\n", ret, line, char_txt, lineno); + } + } + ShowStatus("Converted %d characters.\n", count); + fclose(fp); + ShowStatus("Converting Account variables Database...\n"); + if( (fp=fopen(accreg_txt,"r")) ==NULL ) + { + ShowError("Unable to open file %s!", accreg_txt); + return 1; + } + lineno=count=0; + while(fgets(line, sizeof(line), fp)){ + lineno++; + memset (®, 0, sizeof(struct accreg)); + if(inter_accreg_fromstr(line, ®) == 0 && reg.account_id > 0) { + count++; + inter_accreg_tosql(reg.account_id, 0, ®, 2); //Type 2: Account regs + }else{ + ShowError("accreg reading: broken data [%s] at %s:%d\n", line, accreg_txt, lineno); + } + } + ShowStatus("Converted %d account registries.\n", count); + fclose(fp); + } + + while(getchar() != '\n'); + printf("\n"); + ShowNotice("Do you wish to convert your Storage Database to SQL? (y/n) : "); + input=getchar(); + if(input == 'y' || input == 'Y') { + struct storage storage_; + printf("\n"); + ShowStatus("Converting Storage Database...\n"); + fp=fopen(storage_txt,"r"); + if(fp==NULL){ + ShowError("cant't read : %s\n",storage_txt); + return 0; + } + lineno=count=0; + while(fgets(line,65535,fp)){ + lineno++; + set=sscanf(line,"%d,%d",&tmp_int[0],&tmp_int[1]); + if(set==2) { + memset(&storage_, 0, sizeof(struct storage)); + storage_.account_id=tmp_int[0]; + if (storage_fromstr(line,&storage_) == 0) { + count++; + storage_tosql(storage_.account_id,&storage_); //to sql. (dump) + } else { + ShowError("Error parsing storage line [%s] (at %s:%d)\n", line, storage_txt, lineno); + } + } + } + ShowStatus("Converted %d storages.\n", count); + fclose(fp); + } + + while(getchar() != '\n'); + printf("\n"); + ShowNotice("Do you wish to convert your Pet Database to SQL? (y/n) : "); + input=getchar(); + if(input == 'y' || input == 'Y') { + struct s_pet p; + printf("\n"); + ShowStatus("Converting Pet Database...\n"); + if( (fp=fopen(pet_txt,"r")) ==NULL ) + { + ShowError("Unable to open file %s!", pet_txt); + return 1; + } + lineno=count=0; + while(fgets(line, sizeof(line), fp)){ + lineno++; + memset (&p, 0, sizeof(struct s_pet)); + if(inter_pet_fromstr(line, &p)==0 && p.pet_id>0){ + count++; + inter_pet_tosql(p.pet_id,&p); + }else{ + ShowError("pet reading: broken data [%s] at %s:%d\n", line, pet_txt, lineno); + } + } + ShowStatus("Converted %d pets.\n", count); + fclose(fp); + } + + while(getchar() != '\n'); + printf("\n"); + ShowNotice("Do you wish to convert your Party Database to SQL? (y/n) : "); + input=getchar(); + if(input == 'y' || input == 'Y') { + struct party p; + printf("\n"); + ShowStatus("Converting Party Database...\n"); + if( (fp=fopen(party_txt,"r")) ==NULL ) + { + ShowError("Unable to open file %s!", party_txt); + return 1; + } + lineno=count=0; + while(fgets(line, sizeof(line), fp)){ + lineno++; + memset (&p, 0, sizeof(struct party)); + if(inter_party_fromstr(line, &p) == 0 && + p.party_id > 0 && + inter_party_tosql(&p, PS_CREATE, 0)) + count++; + else{ + ShowError("party reading: broken data [%s] at %s:%d\n", line, pet_txt, lineno); + } + } + ShowStatus("Converted %d parties.\n", count); + fclose(fp); + } + + while(getchar() != '\n'); + printf("\n"); + ShowNotice("Do you wish to convert your Guilds/Guild Castles Database to SQL? (y/n) : "); + input=getchar(); + if(input == 'y' || input == 'Y') { + struct guild g; + struct guild_castle gc; + printf("\n"); + ShowStatus("Converting Guild Database...\n"); + if( (fp=fopen(guild_txt,"r")) ==NULL ) + { + ShowError("Unable to open file %s!", guild_txt); + return 1; + } + lineno=count=0; + while(fgets(line, sizeof(line), fp)){ + lineno++; + memset (&g, 0, sizeof(struct guild)); + if (inter_guild_fromstr(line, &g) == 0 && + g.guild_id > 0 && + inter_guild_tosql(&g,GS_MASK)) + count++; + else + ShowError("guild reading: broken data [%s] at %s:%d\n", line, guild_txt, lineno); + } + ShowStatus("Converted %d guilds.\n", count); + fclose(fp); + ShowStatus("Converting Guild Castles Database...\n"); + if( (fp=fopen(castle_txt,"r")) ==NULL ) + { + ShowError("Unable to open file %s!", castle_txt); + return 1; + } + lineno=count=0; + while(fgets(line, sizeof(line), fp)){ + lineno++; + memset (&gc, 0, sizeof(struct guild_castle)); + if (inter_guildcastle_fromstr(line, &gc) == 0) { + inter_guildcastle_tosql(&gc); + count++; + } + else + ShowError("guild castle reading: broken data [%s] at %s:%d\n", line, castle_txt, lineno); + } + ShowStatus("Converted %d guild castles.\n", count); + fclose(fp); + } + + while(getchar() != '\n'); + printf("\n"); + ShowNotice("Do you wish to convert your Guild Storage Database to SQL? (y/n) : "); + input=getchar(); + if(input == 'y' || input == 'Y') { + struct guild_storage storage_; + printf("\n"); + ShowStatus("Converting Guild Storage Database...\n"); + fp=fopen(guild_storage_txt,"r"); + if(fp==NULL){ + ShowError("cant't read : %s\n",guild_storage_txt); + return 0; + } + lineno=count=0; + while(fgets(line,65535,fp)){ + lineno++; + memset(&storage_, 0, sizeof(struct guild_storage)); + if (sscanf(line,"%d",&storage_.guild_id) == 1 && + storage_.guild_id > 0 && + guild_storage_fromstr(line,&storage_) == 0 + ) { + count++; + guild_storage_tosql(storage_.guild_id, &storage_); + } else + ShowError("Error parsing guild storage line [%s] (at %s:%d)\n", line, guild_storage_txt, lineno); + } + ShowStatus("Converted %d guild storages.\n", count); + fclose(fp); + } + return 0; +} + +int do_init(int argc, char **argv){ + + char_config_read((argc>1)?argv[1]:CHAR_CONF_NAME); + mapindex_init(); + sql_config_read((argc>2)?argv[2]:SQL_CONF_NAME); + inter_init_txt((argc > 3) ? argv[3] :INTER_CONF_NAME); + inter_init_sql((argc > 3) ? argv[3] :INTER_CONF_NAME); + convert_init(); + ShowStatus("Everything's been converted!\n"); + mapindex_final(); + exit (0); +} + +void do_final () {} diff --git a/src/txt-converter/login-converter.c b/src/txt-converter/login-converter.c index 667b331e3..ab236d322 100644 --- a/src/txt-converter/login-converter.c +++ b/src/txt-converter/login-converter.c @@ -1,228 +1,228 @@ -// (c) eAthena Dev Team - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include -#include - -#include "../common/core.h" -#include "../common/db.h" -#include "../common/mmo.h" - -struct auth_dat_ { - int account_id, sex; - char userid[24], pass[24], lastlogin[24]; - int logincount; - int state; // packet 0x006a value + 1 (0: compte OK) - char email[40]; // e-mail (by default: a@a.com) - char error_message[20]; // Message of error code #6 = You are Prohibited to log in until %s (packet 0x006a) - time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) - time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - char last_ip[16]; // save of last IP of connection - char memo[255]; // a memo field - int account_reg2_num; - struct global_reg account_reg2[ACCOUNT_REG2_NUM]; -} *auth_dat; - -char login_account_id[256]="account_id"; -char login_userid[256]="userid"; -char login_user_pass[256]="user_pass"; -char login_db[256]="login"; - -static struct dbt *gm_account_db; - -int db_server_port = 3306; -char db_server_ip[16] = "127.0.0.1"; -char db_server_id[32] = "ragnarok"; -char db_server_pw[32] = "ragnarok"; -char db_server_logindb[32] = "ragnarok"; - -#define INTER_CONF_NAME "conf/inter_athena.conf" - -int isGM(int account_id) -{ - struct gm_account *p; - p = idb_get(gm_account_db,account_id); - if( p == NULL) - return 0; - return p->level; -} - -int read_gm_account() -{ - char line[8192]; - struct gm_account *p; - FILE *fp; - int c=0; - - gm_account_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); - - printf("Starting reading gm_account\n"); - - if( (fp=fopen("conf/GM_account.txt","r"))==NULL ) - return 1; - while(fgets(line,sizeof(line),fp)){ - if(line[0] == '/' || line[1] == '/' || line[2] == '/') - continue; - - p = (struct gm_account*)malloc(sizeof(struct gm_account)); - if(p==NULL){ - printf("gm_account: out of memory!\n"); - exit(0); - } - - if(sscanf(line,"%d %d",&p->account_id,&p->level) != 2 || p->level <= 0) { - printf("gm_account: broken data [conf/GM_account.txt] line %d\n",c); - continue; - } - else { - if(p->level > 99) - p->level = 99; - idb_put(gm_account_db,p->account_id,p); - c++; - printf("GM ID: %d Level: %d\n",p->account_id,p->level); - } - } - fclose(fp); - printf("%d ID of gm_accounts read.\n",c); - return 0; -} - -int mmo_auth_init(void) -{ - MYSQL mysql_handle; - char tmpsql[1024]; - MYSQL_RES* sql_res ; - MYSQL_ROW sql_row ; - FILE *fp; - int account_id, logincount, user_level, state, n, i; - char line[2048], userid[2048], pass[2048], lastlogin[2048], sex, email[2048], error_message[2048], last_ip[2048], memo[2048]; - time_t ban_until_time; - time_t connect_until_time; - char t_uid[256]; - - mysql_init(&mysql_handle); - if(!mysql_real_connect(&mysql_handle, db_server_ip, db_server_id, db_server_pw, - db_server_logindb ,db_server_port, (char *)NULL, 0)) { - //pointer check - printf("%s\n",mysql_error(&mysql_handle)); - exit(1); - } - else { - printf ("Connect: Success!\n"); - } - printf ("Convert start...\n"); - - - fp=fopen("save/account.txt","r"); - auth_dat = (struct auth_dat_*)malloc(sizeof(auth_dat[0])*256); - if(fp==NULL) - return 0; - while(fgets(line,1023,fp)!=NULL){ - - if(line[0]=='/' && line[1]=='/') - continue; - - i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" - "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]\t%ld%n", - &account_id, userid, pass, lastlogin, &sex, &logincount, &state, - email, error_message, &connect_until_time, last_ip, memo, &ban_until_time, &n); - - sprintf(tmpsql, "SELECT `%s`,`%s`,`%s`,`lastlogin`,`logincount`,`sex`,`connect_until`,`last_ip`,`ban_until`,`state`" - " FROM `%s` WHERE `%s`='%s'", login_account_id, login_userid, login_user_pass, login_db, login_userid, t_uid); - - if(mysql_query(&mysql_handle, tmpsql) ) { - printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); - } - user_level = isGM(account_id); - printf ("userlevel: %s (%d)- %d\n",userid, account_id, user_level); - sql_res = mysql_store_result(&mysql_handle) ; - sql_row = mysql_fetch_row(sql_res); //row fetching - if (!sql_row) //no row -> insert - sprintf(tmpsql, "INSERT INTO `login` (`account_id`, `userid`, `user_pass`, `lastlogin`, `sex`, `logincount`, `email`, `level`) VALUES (%d, '%s', '%s', '%s', '%c', %d, 'user@athena', %d);",account_id , userid, pass,lastlogin,sex,logincount, user_level); - else //row reside -> updating - sprintf(tmpsql, "UPDATE `login` SET `account_id`='%d', `userid`='%s', `user_pass`='%s', `lastlogin`='%s', `sex`='%c', `logincount`='%d', `email`='user@athena', `level`='%d'\nWHERE `account_id`='%d';",account_id , userid, pass,lastlogin,sex,logincount, user_level, account_id); - printf ("Query: %s\n",tmpsql); - mysql_free_result(sql_res) ; //resource free - if(mysql_query(&mysql_handle, tmpsql) ) { - printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); - } - } - fclose(fp); - - printf ("Convert end...\n"); - - return 0; -} - -int login_config_read(const char *cfgName){ - int i; - char line[1024], w1[1024], w2[1024]; - FILE *fp; - - printf ("Start reading interserver configuration: %s\n",cfgName); - - fp=fopen(cfgName,"r"); - if(fp==NULL){ - printf("File not found: %s\n", cfgName); - return 1; - } - - while(fgets(line, 1020, fp)){ - if(line[0] == '/' && line[1] == '/') - continue; - - i=sscanf(line,"%[^:]:%s", w1, w2); - if(i!=2) - continue; - - //add for DB connection - if(strcmpi(w1,"db_server_ip")==0){ - strcpy(db_server_ip, w2); - printf ("set db_server_ip : %s\n",w2); - } - else if(strcmpi(w1,"db_server_port")==0){ - db_server_port=atoi(w2); - printf ("set db_server_port : %s\n",w2); - } - else if(strcmpi(w1,"db_server_id")==0){ - strcpy(db_server_id, w2); - printf ("set db_server_id : %s\n",w2); - } - else if(strcmpi(w1,"db_server_pw")==0){ - strcpy(db_server_pw, w2); - printf ("set db_server_pw : %s\n",w2); - } - else if(strcmpi(w1,"db_server_logindb")==0){ - strcpy(db_server_logindb, w2); - printf ("set db_server_logindb : %s\n",w2); - } - //support the import command, just like any other config - else if(strcmpi(w1,"import")==0){ - login_config_read(w2); - } - } - fclose(fp); - printf ("End reading interserver configuration...\n"); - return 0; -} - -int do_init(int argc,char **argv) -{ - char input; - login_config_read( (argc>1)?argv[1]:INTER_CONF_NAME ); - read_gm_account(); - - printf("\nWarning : Make sure you backup your databases before continuing!\n"); - printf("\nDo you wish to convert your Login Database to SQL? (y/n) : "); - input=getchar(); - if(input == 'y' || input == 'Y') - mmo_auth_init(); - printf ("Everything's been converted!\n"); - exit (0); -} - - -void do_final() {} +// (c) eAthena Dev Team - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include +#include + +#include "../common/core.h" +#include "../common/db.h" +#include "../common/mmo.h" + +struct auth_dat_ { + int account_id, sex; + char userid[24], pass[24], lastlogin[24]; + int logincount; + int state; // packet 0x006a value + 1 (0: compte OK) + char email[40]; // e-mail (by default: a@a.com) + char error_message[20]; // Message of error code #6 = You are Prohibited to log in until %s (packet 0x006a) + time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + char last_ip[16]; // save of last IP of connection + char memo[255]; // a memo field + int account_reg2_num; + struct global_reg account_reg2[ACCOUNT_REG2_NUM]; +} *auth_dat; + +char login_account_id[256]="account_id"; +char login_userid[256]="userid"; +char login_user_pass[256]="user_pass"; +char login_db[256]="login"; + +static struct dbt *gm_account_db; + +int db_server_port = 3306; +char db_server_ip[16] = "127.0.0.1"; +char db_server_id[32] = "ragnarok"; +char db_server_pw[32] = "ragnarok"; +char db_server_logindb[32] = "ragnarok"; + +#define INTER_CONF_NAME "conf/inter_athena.conf" + +int isGM(int account_id) +{ + struct gm_account *p; + p = idb_get(gm_account_db,account_id); + if( p == NULL) + return 0; + return p->level; +} + +int read_gm_account() +{ + char line[8192]; + struct gm_account *p; + FILE *fp; + int c=0; + + gm_account_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); + + printf("Starting reading gm_account\n"); + + if( (fp=fopen("conf/GM_account.txt","r"))==NULL ) + return 1; + while(fgets(line,sizeof(line),fp)){ + if(line[0] == '/' || line[1] == '/' || line[2] == '/') + continue; + + p = (struct gm_account*)malloc(sizeof(struct gm_account)); + if(p==NULL){ + printf("gm_account: out of memory!\n"); + exit(0); + } + + if(sscanf(line,"%d %d",&p->account_id,&p->level) != 2 || p->level <= 0) { + printf("gm_account: broken data [conf/GM_account.txt] line %d\n",c); + continue; + } + else { + if(p->level > 99) + p->level = 99; + idb_put(gm_account_db,p->account_id,p); + c++; + printf("GM ID: %d Level: %d\n",p->account_id,p->level); + } + } + fclose(fp); + printf("%d ID of gm_accounts read.\n",c); + return 0; +} + +int mmo_auth_init(void) +{ + MYSQL mysql_handle; + char tmpsql[1024]; + MYSQL_RES* sql_res ; + MYSQL_ROW sql_row ; + FILE *fp; + int account_id, logincount, user_level, state, n, i; + char line[2048], userid[2048], pass[2048], lastlogin[2048], sex, email[2048], error_message[2048], last_ip[2048], memo[2048]; + time_t ban_until_time; + time_t connect_until_time; + char t_uid[256]; + + mysql_init(&mysql_handle); + if(!mysql_real_connect(&mysql_handle, db_server_ip, db_server_id, db_server_pw, + db_server_logindb ,db_server_port, (char *)NULL, 0)) { + //pointer check + printf("%s\n",mysql_error(&mysql_handle)); + exit(1); + } + else { + printf ("Connect: Success!\n"); + } + printf ("Convert start...\n"); + + + fp=fopen("save/account.txt","r"); + auth_dat = (struct auth_dat_*)malloc(sizeof(auth_dat[0])*256); + if(fp==NULL) + return 0; + while(fgets(line,1023,fp)!=NULL){ + + if(line[0]=='/' && line[1]=='/') + continue; + + i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" + "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]\t%ld%n", + &account_id, userid, pass, lastlogin, &sex, &logincount, &state, + email, error_message, &connect_until_time, last_ip, memo, &ban_until_time, &n); + + sprintf(tmpsql, "SELECT `%s`,`%s`,`%s`,`lastlogin`,`logincount`,`sex`,`connect_until`,`last_ip`,`ban_until`,`state`" + " FROM `%s` WHERE `%s`='%s'", login_account_id, login_userid, login_user_pass, login_db, login_userid, t_uid); + + if(mysql_query(&mysql_handle, tmpsql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + user_level = isGM(account_id); + printf ("userlevel: %s (%d)- %d\n",userid, account_id, user_level); + sql_res = mysql_store_result(&mysql_handle) ; + sql_row = mysql_fetch_row(sql_res); //row fetching + if (!sql_row) //no row -> insert + sprintf(tmpsql, "INSERT INTO `login` (`account_id`, `userid`, `user_pass`, `lastlogin`, `sex`, `logincount`, `email`, `level`) VALUES (%d, '%s', '%s', '%s', '%c', %d, 'user@athena', %d);",account_id , userid, pass,lastlogin,sex,logincount, user_level); + else //row reside -> updating + sprintf(tmpsql, "UPDATE `login` SET `account_id`='%d', `userid`='%s', `user_pass`='%s', `lastlogin`='%s', `sex`='%c', `logincount`='%d', `email`='user@athena', `level`='%d'\nWHERE `account_id`='%d';",account_id , userid, pass,lastlogin,sex,logincount, user_level, account_id); + printf ("Query: %s\n",tmpsql); + mysql_free_result(sql_res) ; //resource free + if(mysql_query(&mysql_handle, tmpsql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + } + fclose(fp); + + printf ("Convert end...\n"); + + return 0; +} + +int login_config_read(const char *cfgName){ + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + printf ("Start reading interserver configuration: %s\n",cfgName); + + fp=fopen(cfgName,"r"); + if(fp==NULL){ + printf("File not found: %s\n", cfgName); + return 1; + } + + while(fgets(line, 1020, fp)){ + if(line[0] == '/' && line[1] == '/') + continue; + + i=sscanf(line,"%[^:]:%s", w1, w2); + if(i!=2) + continue; + + //add for DB connection + if(strcmpi(w1,"db_server_ip")==0){ + strcpy(db_server_ip, w2); + printf ("set db_server_ip : %s\n",w2); + } + else if(strcmpi(w1,"db_server_port")==0){ + db_server_port=atoi(w2); + printf ("set db_server_port : %s\n",w2); + } + else if(strcmpi(w1,"db_server_id")==0){ + strcpy(db_server_id, w2); + printf ("set db_server_id : %s\n",w2); + } + else if(strcmpi(w1,"db_server_pw")==0){ + strcpy(db_server_pw, w2); + printf ("set db_server_pw : %s\n",w2); + } + else if(strcmpi(w1,"db_server_logindb")==0){ + strcpy(db_server_logindb, w2); + printf ("set db_server_logindb : %s\n",w2); + } + //support the import command, just like any other config + else if(strcmpi(w1,"import")==0){ + login_config_read(w2); + } + } + fclose(fp); + printf ("End reading interserver configuration...\n"); + return 0; +} + +int do_init(int argc,char **argv) +{ + char input; + login_config_read( (argc>1)?argv[1]:INTER_CONF_NAME ); + read_gm_account(); + + printf("\nWarning : Make sure you backup your databases before continuing!\n"); + printf("\nDo you wish to convert your Login Database to SQL? (y/n) : "); + input=getchar(); + if(input == 'y' || input == 'Y') + mmo_auth_init(); + printf ("Everything's been converted!\n"); + exit (0); +} + + +void do_final() {} diff --git a/src/webserver/doc/API.txt b/src/webserver/doc/API.txt index 92f88c5e3..c80f7bd44 100644 --- a/src/webserver/doc/API.txt +++ b/src/webserver/doc/API.txt @@ -1,50 +1,50 @@ -Here's the webserver API, so you can work on the webserver. - -My personal goal is to make this interface simple, so that coding it -will be like coding in some scripting language... - - - -char *get_param(char in_string[500], char swhat[500]); - -This function simply returns various data from the query string. - *Pass get_param NOTHING longer than 500 in length! - - What do I pass where in_string is? - The query string. - - What do I pass where swhat is? - One of two things... - Either 0 for the path of the 'page' - or you can pass it the param you wish to lookup. - - - - - - -char *get_query(char *inquery); - -This function simply returns a query string from the raw server request. -This is used once in main, I doubt you'll need it. - - - - - -void web_send(int sockin, char *in_data); - -Super easy way of sending data to a webpage! -Simply put in the socket name and then the data. - - Ex: - web_send(socket, "I like cheese!\n"); - - - - -char *html_header(char* title); -Easy way to print the eAthena header for the server. - - Ex: - web_send(sockethere, html_header("About")); +Here's the webserver API, so you can work on the webserver. + +My personal goal is to make this interface simple, so that coding it +will be like coding in some scripting language... + + + +char *get_param(char in_string[500], char swhat[500]); + +This function simply returns various data from the query string. + *Pass get_param NOTHING longer than 500 in length! + + What do I pass where in_string is? + The query string. + + What do I pass where swhat is? + One of two things... + Either 0 for the path of the 'page' + or you can pass it the param you wish to lookup. + + + + + + +char *get_query(char *inquery); + +This function simply returns a query string from the raw server request. +This is used once in main, I doubt you'll need it. + + + + + +void web_send(int sockin, char *in_data); + +Super easy way of sending data to a webpage! +Simply put in the socket name and then the data. + + Ex: + web_send(socket, "I like cheese!\n"); + + + + +char *html_header(char* title); +Easy way to print the eAthena header for the server. + + Ex: + web_send(sockethere, html_header("About")); diff --git a/src/webserver/generate.c b/src/webserver/generate.c index 26d2c7492..ad050db4c 100644 --- a/src/webserver/generate.c +++ b/src/webserver/generate.c @@ -1,38 +1,38 @@ - -void generate_page(char password[25], int sock_in, char *query, char *ip) -{ - char *page = get_param(query, 0); - char *ppass = get_param(query, "password"); - - - if ( (ppass == 0) || (strcmp(password, ppass) != 0) ) - { - web_send(sock_in, html_header("Enter your password")); - web_send(sock_in, "

NOT LOGGED IN!

\n"); - web_send(sock_in, "Enter your password:
\n\n"); - web_send(sock_in, "\n"); - } - else - { - - - //To make this simple, we will have a bunch of if statements - //that then shoot out data off into functions. - - - //The 'index' - if ( strcmp(page, "/") == 0 ) - generate_notdone(sock_in, query, ip); - - - //About page: - if ( strcmp(page, "/about.html") == 0 ) - generate_about(sock_in, query, ip); - - - //Test page: - if ( strcmp(page, "/testing/") == 0 ) - generate_sample(sock_in, query, ip); - - } -} + +void generate_page(char password[25], int sock_in, char *query, char *ip) +{ + char *page = get_param(query, 0); + char *ppass = get_param(query, "password"); + + + if ( (ppass == 0) || (strcmp(password, ppass) != 0) ) + { + web_send(sock_in, html_header("Enter your password")); + web_send(sock_in, "

NOT LOGGED IN!

\n"); + web_send(sock_in, "Enter your password:
\n\n"); + web_send(sock_in, "\n"); + } + else + { + + + //To make this simple, we will have a bunch of if statements + //that then shoot out data off into functions. + + + //The 'index' + if ( strcmp(page, "/") == 0 ) + generate_notdone(sock_in, query, ip); + + + //About page: + if ( strcmp(page, "/about.html") == 0 ) + generate_about(sock_in, query, ip); + + + //Test page: + if ( strcmp(page, "/testing/") == 0 ) + generate_sample(sock_in, query, ip); + + } +} diff --git a/src/webserver/htmlstyle.c b/src/webserver/htmlstyle.c index a1320a385..c3a4b927a 100644 --- a/src/webserver/htmlstyle.c +++ b/src/webserver/htmlstyle.c @@ -1,51 +1,51 @@ -char output[10000]; - -char *html_header(char *title) -{ - memset(output, 0x0, 10000); - char *text = "\n" - "
\n" - "\n" - "
\n" - "\"Athena\"\n" - "
\n" - "
\n"; - "\n" - "
" - "
" - "\n" - "To the Forum
\n" - "\n" - "" - "
\n" - "
\n" - "Athena « Portal »
\n"; - - sprintf(output, "%s\n%s\n", title, text); - - return output; -} - - - -char *html_start_form(char *location, char *action) -{ - memset(output, 0x0, 10000); - sprintf(output, "", location, action); - return output; - - -} - - -char *html_end_forum(void) -{ - return "
"; -} - - - +char output[10000]; + +char *html_header(char *title) +{ + memset(output, 0x0, 10000); + char *text = "\n" + "
\n" + "\n" + "
\n" + "\"Athena\"\n" + "
\n" + "
\n"; + "\n" + "
" + "
" + "\n" + "To the Forum
\n" + "\n" + "" + "
\n" + "
\n" + "Athena « Portal »
\n"; + + sprintf(output, "%s\n%s\n", title, text); + + return output; +} + + + +char *html_start_form(char *location, char *action) +{ + memset(output, 0x0, 10000); + sprintf(output, "
", location, action); + return output; + + +} + + +char *html_end_forum(void) +{ + return "
"; +} + + + diff --git a/src/webserver/logs.c b/src/webserver/logs.c index faa1abf80..405b4882b 100644 --- a/src/webserver/logs.c +++ b/src/webserver/logs.c @@ -1,8 +1,8 @@ -#include - -void log_visit(char *query, char *ip) -{ - time_t timer; - timer=time(NULL); - printf("%s - \"%s\" - %s", ip, query, asctime(localtime(&timer))); -} +#include + +void log_visit(char *query, char *ip) +{ + time_t timer; + timer=time(NULL); + printf("%s - \"%s\" - %s", ip, query, asctime(localtime(&timer))); +} diff --git a/src/webserver/main.c b/src/webserver/main.c index ac27c5e71..59362558e 100644 --- a/src/webserver/main.c +++ b/src/webserver/main.c @@ -1,142 +1,142 @@ -/*************************************************************************** - description - ------------------- - author : (C) 2004 by Michael J. Flickinger - email : mjflick@cpan.org - - ***************************************************************************/ - -/*************************************************************************** - * * - * 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 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BLOG 10 - -char *header = "\n"; -char recvin[500], password[25]; -int s_port; - -void sigchld_handler(int s) -{ - while(wait(NULL) > 0); -} - -int main(int argc, char **argv) -{ - if (argc < 3) - { - printf("eAthena Web Server\n"); - printf("usage: %s [password] [port]\n", argv[0]); - exit(0); - } - - s_port = atoi(argv[2]); - - if ((s_port < 1) || (s_port > 65534)) - { - printf("Error: The port you choose is not valid port.\n"); - exit(0); - } - - if (strlen(argv[1]) > 25) - { - printf("Error: Your password is too long.\n"); - printf("It must be shorter than 25 characters.\n"); - exit(0); - } - - memset(password, 0x0, 25); - memcpy(password, argv[1], strlen(argv[1])); - - int sockfd, new_fd; - struct sockaddr_in my_addr; - struct sockaddr_in their_addr; - int sin_size; - - struct sigaction sa; - - int yes=1; - - if ((sockfd = socket(AF_INET, SOCK_STREAM,0)) == -1) - { - perror("Darn, this is broken."); - exit(0); - } - - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) - { - perror("Error... :-("); - } - - //Now we know we have a working socket. :-) - - my_addr.sin_family = AF_INET; - my_addr.sin_port = htons(s_port); - my_addr.sin_addr.s_addr = INADDR_ANY; - memset(&(my_addr.sin_zero), '\0', 8); - - if ( bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) - { - perror("can not bind to this port"); - exit(0); - } - - if ( listen(sockfd, BLOG) == -1) - { - perror("can not listen on port"); - exit(0); - } - - sa.sa_handler = sigchld_handler; - - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - - if (sigaction(SIGCHLD, &sa, NULL) == -1) - { - perror("sigaction sucks"); - exit(0); - } - - printf("The eAthena webserver is up and listening on port %i.\n", s_port); - - while(1) - { - sin_size = sizeof(struct sockaddr_in); - new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); - - if (!fork()) - { - close(sockfd); - memset(recvin, 0x0, 500); - recv(new_fd, recvin, 500, 0); - send(new_fd, header, strlen(header), 0); - generate_page(password, new_fd, get_query(recvin), inet_ntoa(their_addr.sin_addr)); - log_visit(get_query(recvin), inet_ntoa(their_addr.sin_addr)); - - close(new_fd); - exit(0); - } - close(new_fd); - } - - return 0; -} +/*************************************************************************** + description + ------------------- + author : (C) 2004 by Michael J. Flickinger + email : mjflick@cpan.org + + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLOG 10 + +char *header = "\n"; +char recvin[500], password[25]; +int s_port; + +void sigchld_handler(int s) +{ + while(wait(NULL) > 0); +} + +int main(int argc, char **argv) +{ + if (argc < 3) + { + printf("eAthena Web Server\n"); + printf("usage: %s [password] [port]\n", argv[0]); + exit(0); + } + + s_port = atoi(argv[2]); + + if ((s_port < 1) || (s_port > 65534)) + { + printf("Error: The port you choose is not valid port.\n"); + exit(0); + } + + if (strlen(argv[1]) > 25) + { + printf("Error: Your password is too long.\n"); + printf("It must be shorter than 25 characters.\n"); + exit(0); + } + + memset(password, 0x0, 25); + memcpy(password, argv[1], strlen(argv[1])); + + int sockfd, new_fd; + struct sockaddr_in my_addr; + struct sockaddr_in their_addr; + int sin_size; + + struct sigaction sa; + + int yes=1; + + if ((sockfd = socket(AF_INET, SOCK_STREAM,0)) == -1) + { + perror("Darn, this is broken."); + exit(0); + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) + { + perror("Error... :-("); + } + + //Now we know we have a working socket. :-) + + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons(s_port); + my_addr.sin_addr.s_addr = INADDR_ANY; + memset(&(my_addr.sin_zero), '\0', 8); + + if ( bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) + { + perror("can not bind to this port"); + exit(0); + } + + if ( listen(sockfd, BLOG) == -1) + { + perror("can not listen on port"); + exit(0); + } + + sa.sa_handler = sigchld_handler; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(SIGCHLD, &sa, NULL) == -1) + { + perror("sigaction sucks"); + exit(0); + } + + printf("The eAthena webserver is up and listening on port %i.\n", s_port); + + while(1) + { + sin_size = sizeof(struct sockaddr_in); + new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); + + if (!fork()) + { + close(sockfd); + memset(recvin, 0x0, 500); + recv(new_fd, recvin, 500, 0); + send(new_fd, header, strlen(header), 0); + generate_page(password, new_fd, get_query(recvin), inet_ntoa(their_addr.sin_addr)); + log_visit(get_query(recvin), inet_ntoa(their_addr.sin_addr)); + + close(new_fd); + exit(0); + } + close(new_fd); + } + + return 0; +} diff --git a/src/webserver/pages/about.c b/src/webserver/pages/about.c index f1d94d1e0..2b0002ad8 100644 --- a/src/webserver/pages/about.c +++ b/src/webserver/pages/about.c @@ -1,6 +1,6 @@ -void generate_about(int sock_in, char *query, char *ip) -{ -//printf("%s", html_header("About")); - web_send(sock_in, html_header("About")); - web_send(sock_in, "
eAthena Web Server!
\n"); -} +void generate_about(int sock_in, char *query, char *ip) +{ +//printf("%s", html_header("About")); + web_send(sock_in, html_header("About")); + web_send(sock_in, "
eAthena Web Server!
\n"); +} diff --git a/src/webserver/pages/notdone.c b/src/webserver/pages/notdone.c index 07abd33da..a6492e361 100644 --- a/src/webserver/pages/notdone.c +++ b/src/webserver/pages/notdone.c @@ -1,5 +1,5 @@ -void generate_notdone(int sock_in, char *query, char *ip) -{ - web_send(sock_in, "Not here!\n"); - web_send(sock_in, "

This page/feature is not done yet.
\n

"); -} +void generate_notdone(int sock_in, char *query, char *ip) +{ + web_send(sock_in, "Not here!\n"); + web_send(sock_in, "

This page/feature is not done yet.
\n

"); +} diff --git a/src/webserver/pages/sample.c b/src/webserver/pages/sample.c index 7bec663f2..be900a1bf 100644 --- a/src/webserver/pages/sample.c +++ b/src/webserver/pages/sample.c @@ -1,24 +1,24 @@ - - -void generate_sample(int sock_in, char *query, char *ip) -{ - - char *name = get_param(query, "name"); - - web_send(sock_in, "SAMPLE\n"); - - - //If a name was not entered... - if ( name == '\0' ) - { - web_send(sock_in, "
\n"); - web_send(sock_in, "\n"); - web_send(sock_in, "\n"); - } - else - { - web_send(sock_in, "Your name is: "); - web_send(sock_in, get_param(query, "name")); - } -printf("OK!\n"); -} + + +void generate_sample(int sock_in, char *query, char *ip) +{ + + char *name = get_param(query, "name"); + + web_send(sock_in, "SAMPLE\n"); + + + //If a name was not entered... + if ( name == '\0' ) + { + web_send(sock_in, "\n"); + web_send(sock_in, "\n"); + web_send(sock_in, "\n"); + } + else + { + web_send(sock_in, "Your name is: "); + web_send(sock_in, get_param(query, "name")); + } +printf("OK!\n"); +} diff --git a/src/webserver/parse.c b/src/webserver/parse.c index 323261c6c..8e54a81de 100644 --- a/src/webserver/parse.c +++ b/src/webserver/parse.c @@ -1,135 +1,135 @@ -#include - -char filtered_query[2000]; -char rdata[500]; -char param_n[500]; -char param_d[500]; - - -char *get_query(char *inquery) -{ - memset(filtered_query, 0x0, 2000); - sscanf(inquery, "GET %s %[$]", filtered_query); - return(filtered_query); -} - -void web_send(int sockin, char *in_data) -{ - send(sockin, in_data, strlen(in_data), 0); -} - - -//THIS IS BAD CODE BE CAREFULL WITH IT! -//Watch out for buffer overflow... -//When using please make sure to check the string size. - -//Also note: -//I take no pride in this code, it is a really bad way of doing this... -char *get_param(char in_string[500], char swhat[500]) -{ - int i = 0; - int marker, iswitch, pint, dint; - char flux[500]; - memset(flux, 0x0, 500); - - //Get the path of out "page" - if (swhat == 0) - { - //while i is not equal to array size - while (i != 500) - { - //if there is a question mark, halt! - if (in_string[i] == '?') - { - i = 499; - } - else - rdata[i] = in_string[i]; - - i++; - } - return rdata; - } - else //so, we want a param... - { - //calculate where param begins - while (i != 500) - { - if (in_string[i] == '?') - { - marker = i + 1; - i = 499; - } - i++; - } - - i = 0; - - //keep morons from trying to crash this - if ((marker > 500)||(marker < 1)) - marker = 500; - - while(marker != 500) - { - if ((in_string[marker] != '&') && (in_string[marker] != '\0')) - { - flux[i] = in_string[marker]; - i++; - } - else - { - - //we have a param, now we must dig through it - - //clear temp vars - memset(param_n, 0x0, 500); - memset(param_d, 0x0, 500); - iswitch = 0; - pint = 0; - dint = 0; - i = 0; - - //split result into param_n and param_d - while(i != 500) - { - if ( (flux[i] != '=') && (flux[i] != '\0') ) - { - if (iswitch == 0) - { - param_n[pint] = flux[i]; - pint++; - } - else - { - param_d[dint] = flux[i]; - dint++; - } - } - else - { - iswitch = 1; - } - if (flux[i] == '\0') - i = 499; - - i++; - } - - if ( strcmp(param_n, swhat) == 0 ) - { - return param_d; - } - - i = 0; - } - - if (in_string[marker] == '\0') - { - marker = 499; - } - marker++; - } - return 0; - } -} - +#include + +char filtered_query[2000]; +char rdata[500]; +char param_n[500]; +char param_d[500]; + + +char *get_query(char *inquery) +{ + memset(filtered_query, 0x0, 2000); + sscanf(inquery, "GET %s %[$]", filtered_query); + return(filtered_query); +} + +void web_send(int sockin, char *in_data) +{ + send(sockin, in_data, strlen(in_data), 0); +} + + +//THIS IS BAD CODE BE CAREFULL WITH IT! +//Watch out for buffer overflow... +//When using please make sure to check the string size. + +//Also note: +//I take no pride in this code, it is a really bad way of doing this... +char *get_param(char in_string[500], char swhat[500]) +{ + int i = 0; + int marker, iswitch, pint, dint; + char flux[500]; + memset(flux, 0x0, 500); + + //Get the path of out "page" + if (swhat == 0) + { + //while i is not equal to array size + while (i != 500) + { + //if there is a question mark, halt! + if (in_string[i] == '?') + { + i = 499; + } + else + rdata[i] = in_string[i]; + + i++; + } + return rdata; + } + else //so, we want a param... + { + //calculate where param begins + while (i != 500) + { + if (in_string[i] == '?') + { + marker = i + 1; + i = 499; + } + i++; + } + + i = 0; + + //keep morons from trying to crash this + if ((marker > 500)||(marker < 1)) + marker = 500; + + while(marker != 500) + { + if ((in_string[marker] != '&') && (in_string[marker] != '\0')) + { + flux[i] = in_string[marker]; + i++; + } + else + { + + //we have a param, now we must dig through it + + //clear temp vars + memset(param_n, 0x0, 500); + memset(param_d, 0x0, 500); + iswitch = 0; + pint = 0; + dint = 0; + i = 0; + + //split result into param_n and param_d + while(i != 500) + { + if ( (flux[i] != '=') && (flux[i] != '\0') ) + { + if (iswitch == 0) + { + param_n[pint] = flux[i]; + pint++; + } + else + { + param_d[dint] = flux[i]; + dint++; + } + } + else + { + iswitch = 1; + } + if (flux[i] == '\0') + i = 499; + + i++; + } + + if ( strcmp(param_n, swhat) == 0 ) + { + return param_d; + } + + i = 0; + } + + if (in_string[marker] == '\0') + { + marker = 499; + } + marker++; + } + return 0; + } +} + diff --git a/src/zlib/crypt.h b/src/zlib/crypt.h index f14a628b4..622f4bc2e 100644 --- a/src/zlib/crypt.h +++ b/src/zlib/crypt.h @@ -1,132 +1,132 @@ -/* crypt.h -- base code for crypt/uncrypt ZIPfile - - - Version 1.01e, February 12th, 2005 - - Copyright (C) 1998-2005 Gilles Vollant - - This code is a modified version of crypting code in Infozip distribution - - The encryption/decryption parts of this source code (as opposed to the - non-echoing password parts) were originally written in Europe. The - whole source package can be freely distributed, including from the USA. - (Prior to January 2000, re-export from the US was a violation of US law.) - - This encryption code is a direct transcription of the algorithm from - Roger Schlafly, described by Phil Katz in the file appnote.txt. This - file (appnote.txt) is distributed with the PKZIP program (even in the - version without encryption capabilities). - - If you don't need crypting in your application, just define symbols - NOCRYPT and NOUNCRYPT. - - This code support the "Traditional PKWARE Encryption". - - The new AES encryption added on Zip format by Winzip (see the page - http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong - Encryption is not supported. -*/ - -#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) - -/*********************************************************************** - * Return the next byte in the pseudo-random sequence - */ -static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) -{ - unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an - * unpredictable manner on 16-bit systems; not a problem - * with any known compiler so far, though */ - - temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; - return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); -} - -/*********************************************************************** - * Update the encryption keys with the next byte of plain text - */ -static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) -{ - (*(pkeys+0)) = CRC32((*(pkeys+0)), c); - (*(pkeys+1)) += (*(pkeys+0)) & 0xff; - (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; - { - register int keyshift = (int)((*(pkeys+1)) >> 24); - (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); - } - return c; -} - - -/*********************************************************************** - * Initialize the encryption keys and the random header according to - * the given password. - */ -static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) -{ - *(pkeys+0) = 305419896L; - *(pkeys+1) = 591751049L; - *(pkeys+2) = 878082192L; - while (*passwd != '\0') { - update_keys(pkeys,pcrc_32_tab,(int)*passwd); - passwd++; - } -} - -#define zdecode(pkeys,pcrc_32_tab,c) \ - (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) - -#define zencode(pkeys,pcrc_32_tab,c,t) \ - (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) - -#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED - -#define RAND_HEAD_LEN 12 - /* "last resort" source for second part of crypt seed pattern */ -# ifndef ZCR_SEED2 -# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ -# endif - -static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) - const char *passwd; /* password string */ - unsigned char *buf; /* where to write header */ - int bufSize; - unsigned long* pkeys; - const unsigned long* pcrc_32_tab; - unsigned long crcForCrypting; -{ - int n; /* index in random header */ - int t; /* temporary */ - int c; /* random byte */ - unsigned char header[RAND_HEAD_LEN-2]; /* random header */ - static unsigned calls = 0; /* ensure different random header each time */ - - if (bufSize> 7) & 0xff; - header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); - } - /* Encrypt random header (last two bytes is high word of crc) */ - init_keys(passwd, pkeys, pcrc_32_tab); - for (n = 0; n < RAND_HEAD_LEN-2; n++) - { - buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); - } - buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); - buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); - return n; -} - -#endif +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) + const char *passwd; /* password string */ + unsigned char *buf; /* where to write header */ + int bufSize; + unsigned long* pkeys; + const unsigned long* pcrc_32_tab; + unsigned long crcForCrypting; +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/src/zlib/ioapi.c b/src/zlib/ioapi.c index 7f20c182f..f1bee23e6 100644 --- a/src/zlib/ioapi.c +++ b/src/zlib/ioapi.c @@ -1,177 +1,177 @@ -/* ioapi.c -- IO base function header for compress/uncompress .zip - files using zlib + zip or unzip API - - Version 1.01e, February 12th, 2005 - - Copyright (C) 1998-2005 Gilles Vollant -*/ - -#include -#include -#include - -#include "zlib.h" -#include "ioapi.h" - - - -/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ - -#ifndef SEEK_CUR -#define SEEK_CUR 1 -#endif - -#ifndef SEEK_END -#define SEEK_END 2 -#endif - -#ifndef SEEK_SET -#define SEEK_SET 0 -#endif - -voidpf ZCALLBACK fopen_file_func OF(( - voidpf opaque, - const char* filename, - int mode)); - -uLong ZCALLBACK fread_file_func OF(( - voidpf opaque, - voidpf stream, - void* buf, - uLong size)); - -uLong ZCALLBACK fwrite_file_func OF(( - voidpf opaque, - voidpf stream, - const void* buf, - uLong size)); - -long ZCALLBACK ftell_file_func OF(( - voidpf opaque, - voidpf stream)); - -long ZCALLBACK fseek_file_func OF(( - voidpf opaque, - voidpf stream, - uLong offset, - int origin)); - -int ZCALLBACK fclose_file_func OF(( - voidpf opaque, - voidpf stream)); - -int ZCALLBACK ferror_file_func OF(( - voidpf opaque, - voidpf stream)); - - -voidpf ZCALLBACK fopen_file_func (opaque, filename, mode) - voidpf opaque; - const char* filename; - int mode; -{ - FILE* file = NULL; - const char* mode_fopen = NULL; - if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) - mode_fopen = "rb"; - else - if (mode & ZLIB_FILEFUNC_MODE_EXISTING) - mode_fopen = "r+b"; - else - if (mode & ZLIB_FILEFUNC_MODE_CREATE) - mode_fopen = "wb"; - - if ((filename!=NULL) && (mode_fopen != NULL)) - file = fopen(filename, mode_fopen); - return file; -} - - -uLong ZCALLBACK fread_file_func (opaque, stream, buf, size) - voidpf opaque; - voidpf stream; - void* buf; - uLong size; -{ - uLong ret; - ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); - return ret; -} - - -uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size) - voidpf opaque; - voidpf stream; - const void* buf; - uLong size; -{ - uLong ret; - ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); - return ret; -} - -long ZCALLBACK ftell_file_func (opaque, stream) - voidpf opaque; - voidpf stream; -{ - long ret; - ret = ftell((FILE *)stream); - return ret; -} - -long ZCALLBACK fseek_file_func (opaque, stream, offset, origin) - voidpf opaque; - voidpf stream; - uLong offset; - int origin; -{ - int fseek_origin=0; - long ret; - switch (origin) - { - case ZLIB_FILEFUNC_SEEK_CUR : - fseek_origin = SEEK_CUR; - break; - case ZLIB_FILEFUNC_SEEK_END : - fseek_origin = SEEK_END; - break; - case ZLIB_FILEFUNC_SEEK_SET : - fseek_origin = SEEK_SET; - break; - default: return -1; - } - ret = 0; - fseek((FILE *)stream, offset, fseek_origin); - return ret; -} - -int ZCALLBACK fclose_file_func (opaque, stream) - voidpf opaque; - voidpf stream; -{ - int ret; - ret = fclose((FILE *)stream); - return ret; -} - -int ZCALLBACK ferror_file_func (opaque, stream) - voidpf opaque; - voidpf stream; -{ - int ret; - ret = ferror((FILE *)stream); - return ret; -} - -void fill_fopen_filefunc (pzlib_filefunc_def) - zlib_filefunc_def* pzlib_filefunc_def; -{ - pzlib_filefunc_def->zopen_file = fopen_file_func; - pzlib_filefunc_def->zread_file = fread_file_func; - pzlib_filefunc_def->zwrite_file = fwrite_file_func; - pzlib_filefunc_def->ztell_file = ftell_file_func; - pzlib_filefunc_def->zseek_file = fseek_file_func; - pzlib_filefunc_def->zclose_file = fclose_file_func; - pzlib_filefunc_def->zerror_file = ferror_file_func; - pzlib_filefunc_def->opaque = NULL; -} +/* ioapi.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#include +#include +#include + +#include "zlib.h" +#include "ioapi.h" + + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +voidpf ZCALLBACK fopen_file_func OF(( + voidpf opaque, + const char* filename, + int mode)); + +uLong ZCALLBACK fread_file_func OF(( + voidpf opaque, + voidpf stream, + void* buf, + uLong size)); + +uLong ZCALLBACK fwrite_file_func OF(( + voidpf opaque, + voidpf stream, + const void* buf, + uLong size)); + +long ZCALLBACK ftell_file_func OF(( + voidpf opaque, + voidpf stream)); + +long ZCALLBACK fseek_file_func OF(( + voidpf opaque, + voidpf stream, + uLong offset, + int origin)); + +int ZCALLBACK fclose_file_func OF(( + voidpf opaque, + voidpf stream)); + +int ZCALLBACK ferror_file_func OF(( + voidpf opaque, + voidpf stream)); + + +voidpf ZCALLBACK fopen_file_func (opaque, filename, mode) + voidpf opaque; + const char* filename; + int mode; +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + + +uLong ZCALLBACK fread_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + + +uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + const void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +long ZCALLBACK ftell_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + +long ZCALLBACK fseek_file_func (opaque, stream, offset, origin) + voidpf opaque; + voidpf stream; + uLong offset; + int origin; +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + fseek((FILE *)stream, offset, fseek_origin); + return ret; +} + +int ZCALLBACK fclose_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +int ZCALLBACK ferror_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/src/zlib/ioapi.h b/src/zlib/ioapi.h index e73a3b2bd..7d457baab 100644 --- a/src/zlib/ioapi.h +++ b/src/zlib/ioapi.h @@ -1,75 +1,75 @@ -/* ioapi.h -- IO base function header for compress/uncompress .zip - files using zlib + zip or unzip API - - Version 1.01e, February 12th, 2005 - - Copyright (C) 1998-2005 Gilles Vollant -*/ - -#ifndef _ZLIBIOAPI_H -#define _ZLIBIOAPI_H - - -#define ZLIB_FILEFUNC_SEEK_CUR (1) -#define ZLIB_FILEFUNC_SEEK_END (2) -#define ZLIB_FILEFUNC_SEEK_SET (0) - -#define ZLIB_FILEFUNC_MODE_READ (1) -#define ZLIB_FILEFUNC_MODE_WRITE (2) -#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) - -#define ZLIB_FILEFUNC_MODE_EXISTING (4) -#define ZLIB_FILEFUNC_MODE_CREATE (8) - - -#ifndef ZCALLBACK - -#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) -#define ZCALLBACK CALLBACK -#else -#define ZCALLBACK -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); -typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); -typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); -typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); -typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); -typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); -typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); - -typedef struct zlib_filefunc_def_s -{ - open_file_func zopen_file; - read_file_func zread_file; - write_file_func zwrite_file; - tell_file_func ztell_file; - seek_file_func zseek_file; - close_file_func zclose_file; - testerror_file_func zerror_file; - voidpf opaque; -} zlib_filefunc_def; - - - -void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); - -#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) -#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) -#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) -#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) -#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) -#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) - - -#ifdef __cplusplus -} -#endif - -#endif - +/* ioapi.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#ifndef _ZLIBIOAPI_H +#define _ZLIBIOAPI_H + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + +#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +#define ZCALLBACK CALLBACK +#else +#define ZCALLBACK +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + + + +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) +#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) +#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) +#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) +#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/zlib/iowin32.c b/src/zlib/iowin32.c index 694bc033b..a9b5f7839 100644 --- a/src/zlib/iowin32.c +++ b/src/zlib/iowin32.c @@ -1,270 +1,270 @@ -/* iowin32.c -- IO base function header for compress/uncompress .zip - files using zlib + zip or unzip API - This IO API version uses the Win32 API (for Microsoft Windows) - - Version 1.01e, February 12th, 2005 - - Copyright (C) 1998-2005 Gilles Vollant -*/ - -#include - -#include "zlib.h" -#include "ioapi.h" -#include "iowin32.h" - -#ifndef INVALID_HANDLE_VALUE -#define INVALID_HANDLE_VALUE (0xFFFFFFFF) -#endif - -#ifndef INVALID_SET_FILE_POINTER -#define INVALID_SET_FILE_POINTER ((DWORD)-1) -#endif - -voidpf ZCALLBACK win32_open_file_func OF(( - voidpf opaque, - const char* filename, - int mode)); - -uLong ZCALLBACK win32_read_file_func OF(( - voidpf opaque, - voidpf stream, - void* buf, - uLong size)); - -uLong ZCALLBACK win32_write_file_func OF(( - voidpf opaque, - voidpf stream, - const void* buf, - uLong size)); - -long ZCALLBACK win32_tell_file_func OF(( - voidpf opaque, - voidpf stream)); - -long ZCALLBACK win32_seek_file_func OF(( - voidpf opaque, - voidpf stream, - uLong offset, - int origin)); - -int ZCALLBACK win32_close_file_func OF(( - voidpf opaque, - voidpf stream)); - -int ZCALLBACK win32_error_file_func OF(( - voidpf opaque, - voidpf stream)); - -typedef struct -{ - HANDLE hf; - int error; -} WIN32FILE_IOWIN; - -voidpf ZCALLBACK win32_open_file_func (opaque, filename, mode) - voidpf opaque; - const char* filename; - int mode; -{ - const char* mode_fopen = NULL; - DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; - HANDLE hFile = 0; - voidpf ret=NULL; - - dwDesiredAccess = dwShareMode = dwFlagsAndAttributes = 0; - - if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) - { - dwDesiredAccess = GENERIC_READ; - dwCreationDisposition = OPEN_EXISTING; - dwShareMode = FILE_SHARE_READ; - } - else - if (mode & ZLIB_FILEFUNC_MODE_EXISTING) - { - dwDesiredAccess = GENERIC_WRITE | GENERIC_READ; - dwCreationDisposition = OPEN_EXISTING; - } - else - if (mode & ZLIB_FILEFUNC_MODE_CREATE) - { - dwDesiredAccess = GENERIC_WRITE | GENERIC_READ; - dwCreationDisposition = CREATE_ALWAYS; - } - - if ((filename!=NULL) && (dwDesiredAccess != 0)) - hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, - dwCreationDisposition, dwFlagsAndAttributes, NULL); - - if (hFile == INVALID_HANDLE_VALUE) - hFile = NULL; - - if (hFile != NULL) - { - WIN32FILE_IOWIN w32fiow; - w32fiow.hf = hFile; - w32fiow.error = 0; - ret = malloc(sizeof(WIN32FILE_IOWIN)); - if (ret==NULL) - CloseHandle(hFile); - else *((WIN32FILE_IOWIN*)ret) = w32fiow; - } - return ret; -} - - -uLong ZCALLBACK win32_read_file_func (opaque, stream, buf, size) - voidpf opaque; - voidpf stream; - void* buf; - uLong size; -{ - uLong ret=0; - HANDLE hFile = NULL; - if (stream!=NULL) - hFile = ((WIN32FILE_IOWIN*)stream) -> hf; - if (hFile != NULL) - if (!ReadFile(hFile, buf, size, &ret, NULL)) - { - DWORD dwErr = GetLastError(); - if (dwErr == ERROR_HANDLE_EOF) - dwErr = 0; - ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; - } - - return ret; -} - - -uLong ZCALLBACK win32_write_file_func (opaque, stream, buf, size) - voidpf opaque; - voidpf stream; - const void* buf; - uLong size; -{ - uLong ret=0; - HANDLE hFile = NULL; - if (stream!=NULL) - hFile = ((WIN32FILE_IOWIN*)stream) -> hf; - - if (hFile !=NULL) - if (!WriteFile(hFile, buf, size, &ret, NULL)) - { - DWORD dwErr = GetLastError(); - if (dwErr == ERROR_HANDLE_EOF) - dwErr = 0; - ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; - } - - return ret; -} - -long ZCALLBACK win32_tell_file_func (opaque, stream) - voidpf opaque; - voidpf stream; -{ - long ret=-1; - HANDLE hFile = NULL; - if (stream!=NULL) - hFile = ((WIN32FILE_IOWIN*)stream) -> hf; - if (hFile != NULL) - { - DWORD dwSet = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); - if (dwSet == INVALID_SET_FILE_POINTER) - { - DWORD dwErr = GetLastError(); - ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; - ret = -1; - } - else - ret=(long)dwSet; - } - return ret; -} - -long ZCALLBACK win32_seek_file_func (opaque, stream, offset, origin) - voidpf opaque; - voidpf stream; - uLong offset; - int origin; -{ - DWORD dwMoveMethod=0xFFFFFFFF; - HANDLE hFile = NULL; - - long ret=-1; - if (stream!=NULL) - hFile = ((WIN32FILE_IOWIN*)stream) -> hf; - switch (origin) - { - case ZLIB_FILEFUNC_SEEK_CUR : - dwMoveMethod = FILE_CURRENT; - break; - case ZLIB_FILEFUNC_SEEK_END : - dwMoveMethod = FILE_END; - break; - case ZLIB_FILEFUNC_SEEK_SET : - dwMoveMethod = FILE_BEGIN; - break; - default: return -1; - } - - if (hFile != NULL) - { - DWORD dwSet = SetFilePointer(hFile, offset, NULL, dwMoveMethod); - if (dwSet == INVALID_SET_FILE_POINTER) - { - DWORD dwErr = GetLastError(); - ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; - ret = -1; - } - else - ret=0; - } - return ret; -} - -int ZCALLBACK win32_close_file_func (opaque, stream) - voidpf opaque; - voidpf stream; -{ - int ret=-1; - - if (stream!=NULL) - { - HANDLE hFile; - hFile = ((WIN32FILE_IOWIN*)stream) -> hf; - if (hFile != NULL) - { - CloseHandle(hFile); - ret=0; - } - free(stream); - } - return ret; -} - -int ZCALLBACK win32_error_file_func (opaque, stream) - voidpf opaque; - voidpf stream; -{ - int ret=-1; - if (stream!=NULL) - { - ret = ((WIN32FILE_IOWIN*)stream) -> error; - } - return ret; -} - -void fill_win32_filefunc (pzlib_filefunc_def) - zlib_filefunc_def* pzlib_filefunc_def; -{ - pzlib_filefunc_def->zopen_file = win32_open_file_func; - pzlib_filefunc_def->zread_file = win32_read_file_func; - pzlib_filefunc_def->zwrite_file = win32_write_file_func; - pzlib_filefunc_def->ztell_file = win32_tell_file_func; - pzlib_filefunc_def->zseek_file = win32_seek_file_func; - pzlib_filefunc_def->zclose_file = win32_close_file_func; - pzlib_filefunc_def->zerror_file = win32_error_file_func; - pzlib_filefunc_def->opaque=NULL; -} +/* iowin32.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + This IO API version uses the Win32 API (for Microsoft Windows) + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#include + +#include "zlib.h" +#include "ioapi.h" +#include "iowin32.h" + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE (0xFFFFFFFF) +#endif + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +voidpf ZCALLBACK win32_open_file_func OF(( + voidpf opaque, + const char* filename, + int mode)); + +uLong ZCALLBACK win32_read_file_func OF(( + voidpf opaque, + voidpf stream, + void* buf, + uLong size)); + +uLong ZCALLBACK win32_write_file_func OF(( + voidpf opaque, + voidpf stream, + const void* buf, + uLong size)); + +long ZCALLBACK win32_tell_file_func OF(( + voidpf opaque, + voidpf stream)); + +long ZCALLBACK win32_seek_file_func OF(( + voidpf opaque, + voidpf stream, + uLong offset, + int origin)); + +int ZCALLBACK win32_close_file_func OF(( + voidpf opaque, + voidpf stream)); + +int ZCALLBACK win32_error_file_func OF(( + voidpf opaque, + voidpf stream)); + +typedef struct +{ + HANDLE hf; + int error; +} WIN32FILE_IOWIN; + +voidpf ZCALLBACK win32_open_file_func (opaque, filename, mode) + voidpf opaque; + const char* filename; + int mode; +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = 0; + voidpf ret=NULL; + + dwDesiredAccess = dwShareMode = dwFlagsAndAttributes = 0; + + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + { + dwDesiredAccess = GENERIC_READ; + dwCreationDisposition = OPEN_EXISTING; + dwShareMode = FILE_SHARE_READ; + } + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + { + dwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + dwCreationDisposition = OPEN_EXISTING; + } + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + { + dwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + dwCreationDisposition = CREATE_ALWAYS; + } + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, + dwCreationDisposition, dwFlagsAndAttributes, NULL); + + if (hFile == INVALID_HANDLE_VALUE) + hFile = NULL; + + if (hFile != NULL) + { + WIN32FILE_IOWIN w32fiow; + w32fiow.hf = hFile; + w32fiow.error = 0; + ret = malloc(sizeof(WIN32FILE_IOWIN)); + if (ret==NULL) + CloseHandle(hFile); + else *((WIN32FILE_IOWIN*)ret) = w32fiow; + } + return ret; +} + + +uLong ZCALLBACK win32_read_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + void* buf; + uLong size; +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + if (!ReadFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + + return ret; +} + + +uLong ZCALLBACK win32_write_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + const void* buf; + uLong size; +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile !=NULL) + if (!WriteFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + + return ret; +} + +long ZCALLBACK win32_tell_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + long ret=-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + DWORD dwSet = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=(long)dwSet; + } + return ret; +} + +long ZCALLBACK win32_seek_file_func (opaque, stream, offset, origin) + voidpf opaque; + voidpf stream; + uLong offset; + int origin; +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + + long ret=-1; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile != NULL) + { + DWORD dwSet = SetFilePointer(hFile, offset, NULL, dwMoveMethod); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +int ZCALLBACK win32_close_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret=-1; + + if (stream!=NULL) + { + HANDLE hFile; + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + CloseHandle(hFile); + ret=0; + } + free(stream); + } + return ret; +} + +int ZCALLBACK win32_error_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret=-1; + if (stream!=NULL) + { + ret = ((WIN32FILE_IOWIN*)stream) -> error; + } + return ret; +} + +void fill_win32_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = win32_open_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell_file = win32_tell_file_func; + pzlib_filefunc_def->zseek_file = win32_seek_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque=NULL; +} diff --git a/src/zlib/iowin32.h b/src/zlib/iowin32.h index e9c5f8b90..a3a437adf 100644 --- a/src/zlib/iowin32.h +++ b/src/zlib/iowin32.h @@ -1,21 +1,21 @@ -/* iowin32.h -- IO base function header for compress/uncompress .zip - files using zlib + zip or unzip API - This IO API version uses the Win32 API (for Microsoft Windows) - - Version 1.01e, February 12th, 2005 - - Copyright (C) 1998-2005 Gilles Vollant -*/ - -#include - - -#ifdef __cplusplus -extern "C" { -#endif - -void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); - -#ifdef __cplusplus -} -#endif +/* iowin32.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + This IO API version uses the Win32 API (for Microsoft Windows) + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +#ifdef __cplusplus +} +#endif diff --git a/src/zlib/unzip.c b/src/zlib/unzip.c index 37b4f144b..8b5b30204 100644 --- a/src/zlib/unzip.c +++ b/src/zlib/unzip.c @@ -1,1602 +1,1602 @@ -/* unzip.c -- IO for uncompress .zip files using zlib - Version 1.01e, February 12th, 2005 - - Copyright (C) 1998-2005 Gilles Vollant - - Read unzip.h for more info -*/ - -/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of -compatibility with older software. The following is from the original crypt.c. Code -woven in by Terry Thorsen 1/2003. -*/ -/* - Copyright (c) 1990-2000 Info-ZIP. All rights reserved. - - See the accompanying file LICENSE, version 2000-Apr-09 or later - (the contents of which are also included in zip.h) for terms of use. - If, for some reason, all these files are missing, the Info-ZIP license - also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html -*/ -/* - crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] - - The encryption/decryption parts of this source code (as opposed to the - non-echoing password parts) were originally written in Europe. The - whole source package can be freely distributed, including from the USA. - (Prior to January 2000, re-export from the US was a violation of US law.) - */ - -/* - This encryption code is a direct transcription of the algorithm from - Roger Schlafly, described by Phil Katz in the file appnote.txt. This - file (appnote.txt) is distributed with the PKZIP program (even in the - version without encryption capabilities). - */ - - -#include -#include -#include -#include "zlib.h" -#include "unzip.h" - -#ifdef STDC -# include -# include -# include -#endif -#ifdef NO_ERRNO_H - extern int errno; -#else -# include -#endif - - -#ifndef local -# define local static -#endif -/* compile with -Dlocal if your debugger can't find static symbols */ - - -#ifndef CASESENSITIVITYDEFAULT_NO -# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) -# define CASESENSITIVITYDEFAULT_NO -# endif -#endif - - -#ifndef UNZ_BUFSIZE -#define UNZ_BUFSIZE (16384) -#endif - -#ifndef UNZ_MAXFILENAMEINZIP -#define UNZ_MAXFILENAMEINZIP (256) -#endif - -#ifndef ALLOC -# define ALLOC(size) (malloc(size)) -#endif -#ifndef TRYFREE -# define TRYFREE(p) {if (p) free(p);} -#endif - -#define SIZECENTRALDIRITEM (0x2e) -#define SIZEZIPLOCALHEADER (0x1e) - - - - -const char unz_copyright[] = - " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; - -/* unz_file_info_interntal contain internal info about a file in zipfile*/ -typedef struct unz_file_info_internal_s -{ - uLong offset_curfile;/* relative offset of local header 4 bytes */ -} unz_file_info_internal; - - -/* file_in_zip_read_info_s contain internal information about a file in zipfile, - when reading and decompress it */ -typedef struct -{ - char *read_buffer; /* internal buffer for compressed data */ - z_stream stream; /* zLib stream structure for inflate */ - - uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ - uLong stream_initialised; /* flag set if stream structure is initialised*/ - - uLong offset_local_extrafield;/* offset of the local extra field */ - uInt size_local_extrafield;/* size of the local extra field */ - uLong pos_local_extrafield; /* position in the local extra field in read*/ - - uLong crc32; /* crc32 of all data uncompressed */ - uLong crc32_wait; /* crc32 we must obtain after decompress all */ - uLong rest_read_compressed; /* number of byte to be decompressed */ - uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ - zlib_filefunc_def z_filefunc; - voidpf filestream; /* io structore of the zipfile */ - uLong compression_method; /* compression method (0==store) */ - uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ - int raw; -} file_in_zip_read_info_s; - - -/* unz_s contain internal information about the zipfile -*/ -typedef struct -{ - zlib_filefunc_def z_filefunc; - voidpf filestream; /* io structore of the zipfile */ - unz_global_info gi; /* public global information */ - uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ - uLong num_file; /* number of the current file in the zipfile*/ - uLong pos_in_central_dir; /* pos of the current file in the central dir*/ - uLong current_file_ok; /* flag about the usability of the current file*/ - uLong central_pos; /* position of the beginning of the central dir*/ - - uLong size_central_dir; /* size of the central directory */ - uLong offset_central_dir; /* offset of start of central directory with - respect to the starting disk number */ - - unz_file_info cur_file_info; /* public info about the current file in zip*/ - unz_file_info_internal cur_file_info_internal; /* private info about it*/ - file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current - file if we are decompressing it */ - int encrypted; -# ifndef NOUNCRYPT - unsigned long keys[3]; /* keys defining the pseudo-random sequence */ - const unsigned long* pcrc_32_tab; -# endif -} unz_s; - - -#ifndef NOUNCRYPT -#include "crypt.h" -#endif - -/* =========================================================================== - Read a byte from a gz_stream; update next_in and avail_in. Return EOF - for end of file. - IN assertion: the stream s has been sucessfully opened for reading. -*/ - - -local int unzlocal_getByte OF(( - const zlib_filefunc_def* pzlib_filefunc_def, - voidpf filestream, - int *pi)); - -local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) - const zlib_filefunc_def* pzlib_filefunc_def; - voidpf filestream; - int *pi; -{ - unsigned char c; - int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); - if (err==1) - { - *pi = (int)c; - return UNZ_OK; - } - else - { - if (ZERROR(*pzlib_filefunc_def,filestream)) - return UNZ_ERRNO; - else - return UNZ_EOF; - } -} - - -/* =========================================================================== - Reads a long in LSB order from the given gz_stream. Sets -*/ -local int unzlocal_getShort OF(( - const zlib_filefunc_def* pzlib_filefunc_def, - voidpf filestream, - uLong *pX)); - -local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) - const zlib_filefunc_def* pzlib_filefunc_def; - voidpf filestream; - uLong *pX; -{ - uLong x ; - int i; - int err; - - err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); - x = (uLong)i; - - if (err==UNZ_OK) - err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); - x += ((uLong)i)<<8; - - if (err==UNZ_OK) - *pX = x; - else - *pX = 0; - return err; -} - -local int unzlocal_getLong OF(( - const zlib_filefunc_def* pzlib_filefunc_def, - voidpf filestream, - uLong *pX)); - -local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) - const zlib_filefunc_def* pzlib_filefunc_def; - voidpf filestream; - uLong *pX; -{ - uLong x ; - int i; - int err; - - err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); - x = (uLong)i; - - if (err==UNZ_OK) - err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); - x += ((uLong)i)<<8; - - if (err==UNZ_OK) - err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); - x += ((uLong)i)<<16; - - if (err==UNZ_OK) - err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); - x += ((uLong)i)<<24; - - if (err==UNZ_OK) - *pX = x; - else - *pX = 0; - return err; -} - - -/* My own strcmpi / strcasecmp */ -local int strcmpcasenosensitive_internal (fileName1,fileName2) - const char* fileName1; - const char* fileName2; -{ - for (;;) - { - char c1=*(fileName1++); - char c2=*(fileName2++); - if ((c1>='a') && (c1<='z')) - c1 -= 0x20; - if ((c2>='a') && (c2<='z')) - c2 -= 0x20; - if (c1=='\0') - return ((c2=='\0') ? 0 : -1); - if (c2=='\0') - return 1; - if (c1c2) - return 1; - } -} - - -#ifdef CASESENSITIVITYDEFAULT_NO -#define CASESENSITIVITYDEFAULTVALUE 2 -#else -#define CASESENSITIVITYDEFAULTVALUE 1 -#endif - -#ifndef STRCMPCASENOSENTIVEFUNCTION -#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal -#endif - -/* - Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi - or strcasecmp) - If iCaseSenisivity = 0, case sensitivity is defaut of your operating system - (like 1 on Unix, 2 on Windows) - -*/ -extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) - const char* fileName1; - const char* fileName2; - int iCaseSensitivity; -{ - if (iCaseSensitivity==0) - iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; - - if (iCaseSensitivity==1) - return strcmp(fileName1,fileName2); - - return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); -} - -#ifndef BUFREADCOMMENT -#define BUFREADCOMMENT (0x400) -#endif - -/* - Locate the Central directory of a zipfile (at the end, just before - the global comment) -*/ -local uLong unzlocal_SearchCentralDir OF(( - const zlib_filefunc_def* pzlib_filefunc_def, - voidpf filestream)); - -local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) - const zlib_filefunc_def* pzlib_filefunc_def; - voidpf filestream; -{ - unsigned char* buf; - uLong uSizeFile; - uLong uBackRead; - uLong uMaxBack=0xffff; /* maximum size of global comment */ - uLong uPosFound=0; - - if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) - return 0; - - - uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); - - if (uMaxBack>uSizeFile) - uMaxBack = uSizeFile; - - buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); - if (buf==NULL) - return 0; - - uBackRead = 4; - while (uBackReaduMaxBack) - uBackRead = uMaxBack; - else - uBackRead+=BUFREADCOMMENT; - uReadPos = uSizeFile-uBackRead ; - - uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? - (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); - if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) - break; - - if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) - break; - - for (i=(int)uReadSize-3; (i--)>0;) - if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && - ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) - { - uPosFound = uReadPos+i; - break; - } - - if (uPosFound!=0) - break; - } - TRYFREE(buf); - return uPosFound; -} - -/* - Open a Zip file. path contain the full pathname (by example, - on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer - "zlib/zlib114.zip". - If the zipfile cannot be opened (file doesn't exist or in not valid), the - return value is NULL. - Else, the return value is a unzFile Handle, usable with other function - of this unzip package. -*/ -extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def) - const char *path; - zlib_filefunc_def* pzlib_filefunc_def; -{ - unz_s us; - unz_s *s; - uLong central_pos,uL; - - uLong number_disk; /* number of the current dist, used for - spaning ZIP, unsupported, always 0*/ - uLong number_disk_with_CD; /* number the the disk with central dir, used - for spaning ZIP, unsupported, always 0*/ - uLong number_entry_CD; /* total number of entries in - the central dir - (same than number_entry on nospan) */ - - int err=UNZ_OK; - - if (unz_copyright[0]!=' ') - return NULL; - - if (pzlib_filefunc_def==NULL) - fill_fopen_filefunc(&us.z_filefunc); - else - us.z_filefunc = *pzlib_filefunc_def; - - us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, - path, - ZLIB_FILEFUNC_MODE_READ | - ZLIB_FILEFUNC_MODE_EXISTING); - if (us.filestream==NULL) - return NULL; - - central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); - if (central_pos==0) - err=UNZ_ERRNO; - - if (ZSEEK(us.z_filefunc, us.filestream, - central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) - err=UNZ_ERRNO; - - /* the signature, already checked */ - if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) - err=UNZ_ERRNO; - - /* number of this disk */ - if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) - err=UNZ_ERRNO; - - /* number of the disk with the start of the central directory */ - if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) - err=UNZ_ERRNO; - - /* total number of entries in the central dir on this disk */ - if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) - err=UNZ_ERRNO; - - /* total number of entries in the central dir */ - if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) - err=UNZ_ERRNO; - - if ((number_entry_CD!=us.gi.number_entry) || - (number_disk_with_CD!=0) || - (number_disk!=0)) - err=UNZ_BADZIPFILE; - - /* size of the central directory */ - if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) - err=UNZ_ERRNO; - - /* offset of start of central directory with respect to the - starting disk number */ - if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) - err=UNZ_ERRNO; - - /* zipfile comment length */ - if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) - err=UNZ_ERRNO; - - if ((central_pospfile_in_zip_read!=NULL) - unzCloseCurrentFile(file); - - ZCLOSE(s->z_filefunc, s->filestream); - TRYFREE(s); - return UNZ_OK; -} - - -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. */ -extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) - unzFile file; - unz_global_info *pglobal_info; -{ - unz_s* s; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - *pglobal_info=s->gi; - return UNZ_OK; -} - - -/* - Translate date/time from Dos format to tm_unz (readable more easilty) -*/ -local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) - uLong ulDosDate; - tm_unz* ptm; -{ - uLong uDate; - uDate = (uLong)(ulDosDate>>16); - ptm->tm_mday = (uInt)(uDate&0x1f) ; - ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; - ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; - - ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); - ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; - ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; -} - -/* - Get Info about the current file in the zipfile, with internal only info -*/ -local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, - unz_file_info *pfile_info, - unz_file_info_internal - *pfile_info_internal, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize)); - -local int unzlocal_GetCurrentFileInfoInternal (file, - pfile_info, - pfile_info_internal, - szFileName, fileNameBufferSize, - extraField, extraFieldBufferSize, - szComment, commentBufferSize) - unzFile file; - unz_file_info *pfile_info; - unz_file_info_internal *pfile_info_internal; - char *szFileName; - uLong fileNameBufferSize; - void *extraField; - uLong extraFieldBufferSize; - char *szComment; - uLong commentBufferSize; -{ - unz_s* s; - unz_file_info file_info; - unz_file_info_internal file_info_internal; - int err=UNZ_OK; - uLong uMagic; - long lSeek=0; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (ZSEEK(s->z_filefunc, s->filestream, - s->pos_in_central_dir+s->byte_before_the_zipfile, - ZLIB_FILEFUNC_SEEK_SET)!=0) - err=UNZ_ERRNO; - - - /* we check the magic */ - if (err==UNZ_OK) { - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) - err=UNZ_ERRNO; - else if (uMagic!=0x02014b50) - err=UNZ_BADZIPFILE; - } - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) - err=UNZ_ERRNO; - - unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); - - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) - err=UNZ_ERRNO; - - lSeek+=file_info.size_filename; - if ((err==UNZ_OK) && (szFileName!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_filename0) && (fileNameBufferSize>0)) - if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) - err=UNZ_ERRNO; - lSeek -= uSizeRead; - } - - - if ((err==UNZ_OK) && (extraField!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) - lSeek=0; - else - err=UNZ_ERRNO; - } - if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) - if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) - err=UNZ_ERRNO; - lSeek += file_info.size_file_extra - uSizeRead; - } - else - lSeek+=file_info.size_file_extra; - - - if ((err==UNZ_OK) && (szComment!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) - lSeek=0; - else - err=UNZ_ERRNO; - } - if ((file_info.size_file_comment>0) && (commentBufferSize>0)) - if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) - err=UNZ_ERRNO; - lSeek+=file_info.size_file_comment - uSizeRead; - } - else - lSeek+=file_info.size_file_comment; - - if ((err==UNZ_OK) && (pfile_info!=NULL)) - *pfile_info=file_info; - - if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) - *pfile_info_internal=file_info_internal; - - return err; -} - - - -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. -*/ -extern int ZEXPORT unzGetCurrentFileInfo (file, - pfile_info, - szFileName, fileNameBufferSize, - extraField, extraFieldBufferSize, - szComment, commentBufferSize) - unzFile file; - unz_file_info *pfile_info; - char *szFileName; - uLong fileNameBufferSize; - void *extraField; - uLong extraFieldBufferSize; - char *szComment; - uLong commentBufferSize; -{ - return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, - szFileName,fileNameBufferSize, - extraField,extraFieldBufferSize, - szComment,commentBufferSize); -} - -/* - Set the current file of the zipfile to the first file. - return UNZ_OK if there is no problem -*/ -extern int ZEXPORT unzGoToFirstFile (file) - unzFile file; -{ - int err=UNZ_OK; - unz_s* s; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - s->pos_in_central_dir=s->offset_central_dir; - s->num_file=0; - err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, - &s->cur_file_info_internal, - NULL,0,NULL,0,NULL,0); - s->current_file_ok = (err == UNZ_OK); - return err; -} - -/* - Set the current file of the zipfile to the next file. - return UNZ_OK if there is no problem - return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. -*/ -extern int ZEXPORT unzGoToNextFile (file) - unzFile file; -{ - unz_s* s; - int err; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_END_OF_LIST_OF_FILE; - if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ - if (s->num_file+1==s->gi.number_entry) - return UNZ_END_OF_LIST_OF_FILE; - - s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + - s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; - s->num_file++; - err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, - &s->cur_file_info_internal, - NULL,0,NULL,0,NULL,0); - s->current_file_ok = (err == UNZ_OK); - return err; -} - - -/* - Try locate the file szFileName in the zipfile. - For the iCaseSensitivity signification, see unzipStringFileNameCompare - - return value : - UNZ_OK if the file is found. It becomes the current file. - UNZ_END_OF_LIST_OF_FILE if the file is not found -*/ -extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) - unzFile file; - const char *szFileName; - int iCaseSensitivity; -{ - unz_s* s; - int err; - - /* We remember the 'current' position in the file so that we can jump - * back there if we fail. - */ - unz_file_info cur_file_infoSaved; - unz_file_info_internal cur_file_info_internalSaved; - uLong num_fileSaved; - uLong pos_in_central_dirSaved; - - - if (file==NULL) - return UNZ_PARAMERROR; - - if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) - return UNZ_PARAMERROR; - - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_END_OF_LIST_OF_FILE; - - /* Save the current state */ - num_fileSaved = s->num_file; - pos_in_central_dirSaved = s->pos_in_central_dir; - cur_file_infoSaved = s->cur_file_info; - cur_file_info_internalSaved = s->cur_file_info_internal; - - err = unzGoToFirstFile(file); - - while (err == UNZ_OK) - { - char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; - err = unzGetCurrentFileInfo(file,NULL, - szCurrentFileName,sizeof(szCurrentFileName)-1, - NULL,0,NULL,0); - if (err == UNZ_OK) - { - if (unzStringFileNameCompare(szCurrentFileName, - szFileName,iCaseSensitivity)==0) - return UNZ_OK; - err = unzGoToNextFile(file); - } - } - - /* We failed, so restore the state of the 'current file' to where we - * were. - */ - s->num_file = num_fileSaved ; - s->pos_in_central_dir = pos_in_central_dirSaved ; - s->cur_file_info = cur_file_infoSaved; - s->cur_file_info_internal = cur_file_info_internalSaved; - return err; -} - - -/* -/////////////////////////////////////////// -// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) -// I need random access -// -// Further optimization could be realized by adding an ability -// to cache the directory in memory. The goal being a single -// comprehensive file read to put the file I need in a memory. -*/ - -/* -typedef struct unz_file_pos_s -{ - uLong pos_in_zip_directory; // offset in file - uLong num_of_file; // # of file -} unz_file_pos; -*/ - -extern int ZEXPORT unzGetFilePos(file, file_pos) - unzFile file; - unz_file_pos* file_pos; -{ - unz_s* s; - - if (file==NULL || file_pos==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_END_OF_LIST_OF_FILE; - - file_pos->pos_in_zip_directory = s->pos_in_central_dir; - file_pos->num_of_file = s->num_file; - - return UNZ_OK; -} - -extern int ZEXPORT unzGoToFilePos(file, file_pos) - unzFile file; - unz_file_pos* file_pos; -{ - unz_s* s; - int err; - - if (file==NULL || file_pos==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - - /* jump to the right spot */ - s->pos_in_central_dir = file_pos->pos_in_zip_directory; - s->num_file = file_pos->num_of_file; - - /* set the current file */ - err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, - &s->cur_file_info_internal, - NULL,0,NULL,0,NULL,0); - /* return results */ - s->current_file_ok = (err == UNZ_OK); - return err; -} - -/* -// Unzip Helper Functions - should be here? -/////////////////////////////////////////// -*/ - -/* - Read the local header of the current zipfile - Check the coherency of the local header and info in the end of central - directory about this file - store in *piSizeVar the size of extra info in local header - (filename and size of extra field data) -*/ -local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, - poffset_local_extrafield, - psize_local_extrafield) - unz_s* s; - uInt* piSizeVar; - uLong *poffset_local_extrafield; - uInt *psize_local_extrafield; -{ - uLong uMagic,uData,uFlags; - uLong size_filename; - uLong size_extra_field; - int err=UNZ_OK; - - *piSizeVar = 0; - *poffset_local_extrafield = 0; - *psize_local_extrafield = 0; - - if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + - s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) - return UNZ_ERRNO; - - - if (err==UNZ_OK) { - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) - err=UNZ_ERRNO; - else if (uMagic!=0x04034b50) - err=UNZ_BADZIPFILE; - } - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) - err=UNZ_ERRNO; -/* - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) - err=UNZ_BADZIPFILE; -*/ - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) - err=UNZ_BADZIPFILE; - - if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && - (s->cur_file_info.compression_method!=Z_DEFLATED)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ - err=UNZ_ERRNO; - - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) - err=UNZ_BADZIPFILE; - - *piSizeVar += (uInt)size_filename; - - if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) - err=UNZ_ERRNO; - *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + - SIZEZIPLOCALHEADER + size_filename; - *psize_local_extrafield = (uInt)size_extra_field; - - *piSizeVar += (uInt)size_extra_field; - - return err; -} - -/* - Open for reading data the current file in the zipfile. - If there is no error and the file is opened, the return value is UNZ_OK. -*/ -extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) - unzFile file; - int* method; - int* level; - int raw; - const char* password; -{ - int err=UNZ_OK; - uInt iSizeVar; - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - uLong offset_local_extrafield; /* offset of the local extra field */ - uInt size_local_extrafield; /* size of the local extra field */ -# ifndef NOUNCRYPT - char source[12]; -# else - if (password != NULL) - return UNZ_PARAMERROR; -# endif - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_PARAMERROR; - - if (s->pfile_in_zip_read != NULL) - unzCloseCurrentFile(file); - - if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, - &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) - return UNZ_BADZIPFILE; - - pfile_in_zip_read_info = (file_in_zip_read_info_s*) - ALLOC(sizeof(file_in_zip_read_info_s)); - if (pfile_in_zip_read_info==NULL) - return UNZ_INTERNALERROR; - - pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); - pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; - pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; - pfile_in_zip_read_info->pos_local_extrafield=0; - pfile_in_zip_read_info->raw=raw; - - if (pfile_in_zip_read_info->read_buffer==NULL) - { - TRYFREE(pfile_in_zip_read_info); - return UNZ_INTERNALERROR; - } - - pfile_in_zip_read_info->stream_initialised=0; - - if (method!=NULL) - *method = (int)s->cur_file_info.compression_method; - - if (level!=NULL) - { - *level = 6; - switch (s->cur_file_info.flag & 0x06) - { - case 6 : *level = 1; break; - case 4 : *level = 2; break; - case 2 : *level = 9; break; - } - } - - if ((s->cur_file_info.compression_method!=0) && - (s->cur_file_info.compression_method!=Z_DEFLATED)) - err=UNZ_BADZIPFILE; - - pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; - pfile_in_zip_read_info->crc32=0; - pfile_in_zip_read_info->compression_method = - s->cur_file_info.compression_method; - pfile_in_zip_read_info->filestream=s->filestream; - pfile_in_zip_read_info->z_filefunc=s->z_filefunc; - pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; - - pfile_in_zip_read_info->stream.total_out = 0; - - if ((s->cur_file_info.compression_method==Z_DEFLATED) && - (!raw)) - { - pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; - pfile_in_zip_read_info->stream.zfree = (free_func)0; - pfile_in_zip_read_info->stream.opaque = (voidpf)0; - pfile_in_zip_read_info->stream.next_in = (voidpf)0; - pfile_in_zip_read_info->stream.avail_in = 0; - - err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); - if (err == Z_OK) - pfile_in_zip_read_info->stream_initialised=1; - else - { - TRYFREE(pfile_in_zip_read_info); - return err; - } - /* windowBits is passed < 0 to tell that there is no zlib header. - * Note that in this case inflate *requires* an extra "dummy" byte - * after the compressed stream in order to complete decompression and - * return Z_STREAM_END. - * In unzip, i don't wait absolutely Z_STREAM_END because I known the - * size of both compressed and uncompressed data - */ - } - pfile_in_zip_read_info->rest_read_compressed = - s->cur_file_info.compressed_size ; - pfile_in_zip_read_info->rest_read_uncompressed = - s->cur_file_info.uncompressed_size ; - - - pfile_in_zip_read_info->pos_in_zipfile = - s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + - iSizeVar; - - pfile_in_zip_read_info->stream.avail_in = (uInt)0; - - s->pfile_in_zip_read = pfile_in_zip_read_info; - -# ifndef NOUNCRYPT - if (password != NULL) - { - int i; - s->pcrc_32_tab = get_crc_table(); - init_keys(password,s->keys,s->pcrc_32_tab); - if (ZSEEK(s->z_filefunc, s->filestream, - s->pfile_in_zip_read->pos_in_zipfile + - s->pfile_in_zip_read->byte_before_the_zipfile, - SEEK_SET)!=0) - return UNZ_INTERNALERROR; - if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) - return UNZ_INTERNALERROR; - - for (i = 0; i<12; i++) - zdecode(s->keys,s->pcrc_32_tab,source[i]); - - s->pfile_in_zip_read->pos_in_zipfile+=12; - s->encrypted=1; - } -# endif - - - return UNZ_OK; -} - -extern int ZEXPORT unzOpenCurrentFile (file) - unzFile file; -{ - return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); -} - -extern int ZEXPORT unzOpenCurrentFilePassword (file, password) - unzFile file; - const char* password; -{ - return unzOpenCurrentFile3(file, NULL, NULL, 0, password); -} - -extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) - unzFile file; - int* method; - int* level; - int raw; -{ - return unzOpenCurrentFile3(file, method, level, raw, NULL); -} - -/* - Read bytes from the current file. - buf contain buffer where data must be copied - len the size of buf. - - return the number of byte copied if somes bytes are copied - return 0 if the end of file was reached - return <0 with error code if there is an error - (UNZ_ERRNO for IO error, or zLib error for uncompress error) -*/ -extern int ZEXPORT unzReadCurrentFile (file, buf, len) - unzFile file; - voidp buf; - unsigned len; -{ - int err=UNZ_OK; - uInt iRead = 0; - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - - if ((pfile_in_zip_read_info->read_buffer == NULL)) - return UNZ_END_OF_LIST_OF_FILE; - if (len==0) - return 0; - - pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; - - pfile_in_zip_read_info->stream.avail_out = (uInt)len; - - if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && - (!(pfile_in_zip_read_info->raw))) - pfile_in_zip_read_info->stream.avail_out = - (uInt)pfile_in_zip_read_info->rest_read_uncompressed; - - if ((len>pfile_in_zip_read_info->rest_read_compressed+ - pfile_in_zip_read_info->stream.avail_in) && - (pfile_in_zip_read_info->raw)) - pfile_in_zip_read_info->stream.avail_out = - (uInt)pfile_in_zip_read_info->rest_read_compressed+ - pfile_in_zip_read_info->stream.avail_in; - - while (pfile_in_zip_read_info->stream.avail_out>0) - { - if ((pfile_in_zip_read_info->stream.avail_in==0) && - (pfile_in_zip_read_info->rest_read_compressed>0)) - { - uInt uReadThis = UNZ_BUFSIZE; - if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; - if (uReadThis == 0) - return UNZ_EOF; - if (ZSEEK(pfile_in_zip_read_info->z_filefunc, - pfile_in_zip_read_info->filestream, - pfile_in_zip_read_info->pos_in_zipfile + - pfile_in_zip_read_info->byte_before_the_zipfile, - ZLIB_FILEFUNC_SEEK_SET)!=0) - return UNZ_ERRNO; - if (ZREAD(pfile_in_zip_read_info->z_filefunc, - pfile_in_zip_read_info->filestream, - pfile_in_zip_read_info->read_buffer, - uReadThis)!=uReadThis) - return UNZ_ERRNO; - - -# ifndef NOUNCRYPT - if(s->encrypted) - { - uInt i; - for(i=0;iread_buffer[i] = - zdecode(s->keys,s->pcrc_32_tab, - pfile_in_zip_read_info->read_buffer[i]); - } -# endif - - - pfile_in_zip_read_info->pos_in_zipfile += uReadThis; - - pfile_in_zip_read_info->rest_read_compressed-=uReadThis; - - pfile_in_zip_read_info->stream.next_in = - (Bytef*)pfile_in_zip_read_info->read_buffer; - pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; - } - - if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) - { - uInt uDoCopy,i ; - - if ((pfile_in_zip_read_info->stream.avail_in == 0) && - (pfile_in_zip_read_info->rest_read_compressed == 0)) - return (iRead==0) ? UNZ_EOF : iRead; - - if (pfile_in_zip_read_info->stream.avail_out < - pfile_in_zip_read_info->stream.avail_in) - uDoCopy = pfile_in_zip_read_info->stream.avail_out ; - else - uDoCopy = pfile_in_zip_read_info->stream.avail_in ; - - for (i=0;istream.next_out+i) = - *(pfile_in_zip_read_info->stream.next_in+i); - - pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, - pfile_in_zip_read_info->stream.next_out, - uDoCopy); - pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; - pfile_in_zip_read_info->stream.avail_in -= uDoCopy; - pfile_in_zip_read_info->stream.avail_out -= uDoCopy; - pfile_in_zip_read_info->stream.next_out += uDoCopy; - pfile_in_zip_read_info->stream.next_in += uDoCopy; - pfile_in_zip_read_info->stream.total_out += uDoCopy; - iRead += uDoCopy; - } - else - { - uLong uTotalOutBefore,uTotalOutAfter; - const Bytef *bufBefore; - uLong uOutThis; - int flush=Z_SYNC_FLUSH; - - uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; - bufBefore = pfile_in_zip_read_info->stream.next_out; - - /* - if ((pfile_in_zip_read_info->rest_read_uncompressed == - pfile_in_zip_read_info->stream.avail_out) && - (pfile_in_zip_read_info->rest_read_compressed == 0)) - flush = Z_FINISH; - */ - err=inflate(&pfile_in_zip_read_info->stream,flush); - - if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) - err = Z_DATA_ERROR; - - uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; - uOutThis = uTotalOutAfter-uTotalOutBefore; - - pfile_in_zip_read_info->crc32 = - crc32(pfile_in_zip_read_info->crc32,bufBefore, - (uInt)(uOutThis)); - - pfile_in_zip_read_info->rest_read_uncompressed -= - uOutThis; - - iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); - - if (err==Z_STREAM_END) - return (iRead==0) ? UNZ_EOF : iRead; - if (err!=Z_OK) - break; - } - } - - if (err==Z_OK) - return iRead; - return err; -} - - -/* - Give the current position in uncompressed data -*/ -extern z_off_t ZEXPORT unztell (file) - unzFile file; -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - return (z_off_t)pfile_in_zip_read_info->stream.total_out; -} - - -/* - return 1 if the end of file was reached, 0 elsewhere -*/ -extern int ZEXPORT unzeof (file) - unzFile file; -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - if (pfile_in_zip_read_info->rest_read_uncompressed == 0) - return 1; - else - return 0; -} - - - -/* - Read extra field from the current file (opened by unzOpenCurrentFile) - This is the local-header version of the extra field (sometimes, there is - more info in the local-header version than in the central-header) - - if buf==NULL, it return the size of the local extra field that can be read - - if buf!=NULL, len is the size of the buffer, the extra header is copied in - buf. - the return value is the number of bytes copied in buf, or (if <0) - the error code -*/ -extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) - unzFile file; - voidp buf; - unsigned len; -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - uInt read_now; - uLong size_to_read; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - size_to_read = (pfile_in_zip_read_info->size_local_extrafield - - pfile_in_zip_read_info->pos_local_extrafield); - - if (buf==NULL) - return (int)size_to_read; - - if (len>size_to_read) - read_now = (uInt)size_to_read; - else - read_now = (uInt)len ; - - if (read_now==0) - return 0; - - if (ZSEEK(pfile_in_zip_read_info->z_filefunc, - pfile_in_zip_read_info->filestream, - pfile_in_zip_read_info->offset_local_extrafield + - pfile_in_zip_read_info->pos_local_extrafield, - ZLIB_FILEFUNC_SEEK_SET)!=0) - return UNZ_ERRNO; - - if (ZREAD(pfile_in_zip_read_info->z_filefunc, - pfile_in_zip_read_info->filestream, - buf,read_now)!=read_now) - return UNZ_ERRNO; - - return (int)read_now; -} - -/* - Close the file in zip opened with unzipOpenCurrentFile - Return UNZ_CRCERROR if all the file was read but the CRC is not good -*/ -extern int ZEXPORT unzCloseCurrentFile (file) - unzFile file; -{ - int err=UNZ_OK; - - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - - if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && - (!pfile_in_zip_read_info->raw)) - { - if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) - err=UNZ_CRCERROR; - } - - - TRYFREE(pfile_in_zip_read_info->read_buffer); - pfile_in_zip_read_info->read_buffer = NULL; - if (pfile_in_zip_read_info->stream_initialised) - inflateEnd(&pfile_in_zip_read_info->stream); - - pfile_in_zip_read_info->stream_initialised = 0; - TRYFREE(pfile_in_zip_read_info); - - s->pfile_in_zip_read=NULL; - - return err; -} - - -/* - Get the global comment string of the ZipFile, in the szComment buffer. - uSizeBuf is the size of the szComment buffer. - return the number of byte copied or an error code <0 -*/ -extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) - unzFile file; - char *szComment; - uLong uSizeBuf; -{ - // int err=UNZ_OK; // Unused [Lance] - unz_s* s; - uLong uReadThis ; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - - uReadThis = uSizeBuf; - if (uReadThis>s->gi.size_comment) - uReadThis = s->gi.size_comment; - - if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) - return UNZ_ERRNO; - - if (uReadThis>0) - { - *szComment='\0'; - if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) - return UNZ_ERRNO; - } - - if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) - *(szComment+s->gi.size_comment)='\0'; - return (int)uReadThis; -} - -/* Additions by RX '2004 */ -extern uLong ZEXPORT unzGetOffset (file) - unzFile file; -{ - unz_s* s; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (!s->current_file_ok) - return 0; - if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) - if (s->num_file==s->gi.number_entry) - return 0; - return s->pos_in_central_dir; -} - -extern int ZEXPORT unzSetOffset (file, pos) - unzFile file; - uLong pos; -{ - unz_s* s; - int err; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - - s->pos_in_central_dir = pos; - s->num_file = s->gi.number_entry; /* hack */ - err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, - &s->cur_file_info_internal, - NULL,0,NULL,0,NULL,0); - s->current_file_ok = (err == UNZ_OK); - return err; -} +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + Read unzip.h for more info +*/ + +/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of +compatibility with older software. The following is from the original crypt.c. Code +woven in by Terry Thorsen 1/2003. +*/ +/* + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html +*/ +/* + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + */ + +/* + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + */ + + +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (fileName1,fileName2) + const char* fileName1; + const char* fileName2; +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) + const char* fileName1; + const char* fileName2; + int iCaseSensitivity; +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def) + const char *path; + zlib_filefunc_def* pzlib_filefunc_def; +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + if (pzlib_filefunc_def==NULL) + fill_fopen_filefunc(&us.z_filefunc); + else + us.z_filefunc = *pzlib_filefunc_def; + + us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + if (ZSEEK(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) + unzFile file; + unz_global_info *pglobal_info; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) + uLong ulDosDate; + tm_unz* ptm; +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (file, + pfile_info, + pfile_info_internal, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + unz_file_info_internal *pfile_info_internal; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (ZSEEK(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (file, + pfile_info, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (file) + unzFile file; +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (file) + unzFile file; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) + unzFile file; + const char *szFileName; + int iCaseSensitivity; +{ + unz_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info cur_file_infoSaved; + unz_file_info_internal cur_file_info_internalSaved; + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; // offset in file + uLong num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGoToFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, + poffset_local_extrafield, + psize_local_extrafield) + unz_s* s; + uInt* piSizeVar; + uLong *poffset_local_extrafield; + uInt *psize_local_extrafield; +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) + unzFile file; + int* method; + int* level; + int raw; + const char* password; +{ + int err=UNZ_OK; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_DEFLATED) && + (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (file) + unzFile file; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (file, password) + unzFile file; + const char* password; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) + unzFile file; + int* method; + int* level; + int raw; +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) + unzFile file; + voidp buf; + unsigned len; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) + unzFile file; + char *szComment; + uLong uSizeBuf; +{ + // int err=UNZ_OK; // Unused [Lance] + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern uLong ZEXPORT unzGetOffset (file) + unzFile file; +{ + unz_s* s; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern int ZEXPORT unzSetOffset (file, pos) + unzFile file; + uLong pos; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} diff --git a/src/zlib/unzip.h b/src/zlib/unzip.h index c3206a058..b247937c8 100644 --- a/src/zlib/unzip.h +++ b/src/zlib/unzip.h @@ -1,354 +1,354 @@ -/* unzip.h -- IO for uncompress .zip files using zlib - Version 1.01e, February 12th, 2005 - - Copyright (C) 1998-2005 Gilles Vollant - - This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g - WinZip, InfoZip tools and compatible. - - Multi volume ZipFile (span) are not supported. - Encryption compatible with pkzip 2.04g only supported - Old compressions used by old PKZip 1.x are not supported - - - I WAIT FEEDBACK at mail info@winimage.com - Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution - - Condition of use and distribution are the same than zlib : - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - -*/ - -/* for more info about .ZIP format, see - http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip - http://www.info-zip.org/pub/infozip/doc/ - PkWare has also a specification at : - ftp://ftp.pkware.com/probdesc.zip -*/ - -#ifndef _unz_H -#define _unz_H - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef _ZLIB_H -#include "zlib.h" -#endif - -#ifndef _ZLIBIOAPI_H -#include "ioapi.h" -#endif - -#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) -/* like the STRICT of WIN32, we define a pointer that cannot be converted - from (void*) without cast */ -typedef struct TagunzFile__ { int unused; } unzFile__; -typedef unzFile__ *unzFile; -#else -typedef voidp unzFile; -#endif - - -#define UNZ_OK (0) -#define UNZ_END_OF_LIST_OF_FILE (-100) -#define UNZ_ERRNO (Z_ERRNO) -#define UNZ_EOF (0) -#define UNZ_PARAMERROR (-102) -#define UNZ_BADZIPFILE (-103) -#define UNZ_INTERNALERROR (-104) -#define UNZ_CRCERROR (-105) - -/* tm_unz contain date/time info */ -typedef struct tm_unz_s -{ - uInt tm_sec; /* seconds after the minute - [0,59] */ - uInt tm_min; /* minutes after the hour - [0,59] */ - uInt tm_hour; /* hours since midnight - [0,23] */ - uInt tm_mday; /* day of the month - [1,31] */ - uInt tm_mon; /* months since January - [0,11] */ - uInt tm_year; /* years - [1980..2044] */ -} tm_unz; - -/* unz_global_info structure contain global data about the ZIPfile - These data comes from the end of central dir */ -typedef struct unz_global_info_s -{ - uLong number_entry; /* total number of entries in - the central dir on this disk */ - uLong size_comment; /* size of the global comment of the zipfile */ -} unz_global_info; - - -/* unz_file_info contain information about a file in the zipfile */ -typedef struct unz_file_info_s -{ - uLong version; /* version made by 2 bytes */ - uLong version_needed; /* version needed to extract 2 bytes */ - uLong flag; /* general purpose bit flag 2 bytes */ - uLong compression_method; /* compression method 2 bytes */ - uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ - uLong crc; /* crc-32 4 bytes */ - uLong compressed_size; /* compressed size 4 bytes */ - uLong uncompressed_size; /* uncompressed size 4 bytes */ - uLong size_filename; /* filename length 2 bytes */ - uLong size_file_extra; /* extra field length 2 bytes */ - uLong size_file_comment; /* file comment length 2 bytes */ - - uLong disk_num_start; /* disk number start 2 bytes */ - uLong internal_fa; /* internal file attributes 2 bytes */ - uLong external_fa; /* external file attributes 4 bytes */ - - tm_unz tmu_date; -} unz_file_info; - -extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, - const char* fileName2, - int iCaseSensitivity)); -/* - Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi - or strcasecmp) - If iCaseSenisivity = 0, case sensitivity is defaut of your operating system - (like 1 on Unix, 2 on Windows) -*/ - - -extern unzFile ZEXPORT unzOpen OF((const char *path)); -/* - Open a Zip file. path contain the full pathname (by example, - on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer - "zlib/zlib113.zip". - If the zipfile cannot be opened (file don't exist or in not valid), the - return value is NULL. - Else, the return value is a unzFile Handle, usable with other function - of this unzip package. -*/ - -extern unzFile ZEXPORT unzOpen2 OF((const char *path, - zlib_filefunc_def* pzlib_filefunc_def)); -/* - Open a Zip file, like unzOpen, but provide a set of file low level API - for read/write the zip file (see ioapi.h) -*/ - -extern int ZEXPORT unzClose OF((unzFile file)); -/* - Close a ZipFile opened with unzipOpen. - If there is files inside the .Zip opened with unzOpenCurrentFile (see later), - these files MUST be closed with unzipCloseCurrentFile before call unzipClose. - return UNZ_OK if there is no problem. */ - -extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, - unz_global_info *pglobal_info)); -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. */ - - -extern int ZEXPORT unzGetGlobalComment OF((unzFile file, - char *szComment, - uLong uSizeBuf)); -/* - Get the global comment string of the ZipFile, in the szComment buffer. - uSizeBuf is the size of the szComment buffer. - return the number of byte copied or an error code <0 -*/ - - -/***************************************************************************/ -/* Unzip package allow you browse the directory of the zipfile */ - -extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); -/* - Set the current file of the zipfile to the first file. - return UNZ_OK if there is no problem -*/ - -extern int ZEXPORT unzGoToNextFile OF((unzFile file)); -/* - Set the current file of the zipfile to the next file. - return UNZ_OK if there is no problem - return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. -*/ - -extern int ZEXPORT unzLocateFile OF((unzFile file, - const char *szFileName, - int iCaseSensitivity)); -/* - Try locate the file szFileName in the zipfile. - For the iCaseSensitivity signification, see unzStringFileNameCompare - - return value : - UNZ_OK if the file is found. It becomes the current file. - UNZ_END_OF_LIST_OF_FILE if the file is not found -*/ - - -/* ****************************************** */ -/* Ryan supplied functions */ -/* unz_file_info contain information about a file in the zipfile */ -typedef struct unz_file_pos_s -{ - uLong pos_in_zip_directory; /* offset in zip file directory */ - uLong num_of_file; /* # of file */ -} unz_file_pos; - -extern int ZEXPORT unzGetFilePos( - unzFile file, - unz_file_pos* file_pos); - -extern int ZEXPORT unzGoToFilePos( - unzFile file, - unz_file_pos* file_pos); - -/* ****************************************** */ - -extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, - unz_file_info *pfile_info, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize)); -/* - Get Info about the current file - if pfile_info!=NULL, the *pfile_info structure will contain somes info about - the current file - if szFileName!=NULL, the filemane string will be copied in szFileName - (fileNameBufferSize is the size of the buffer) - if extraField!=NULL, the extra field information will be copied in extraField - (extraFieldBufferSize is the size of the buffer). - This is the Central-header version of the extra field - if szComment!=NULL, the comment string of the file will be copied in szComment - (commentBufferSize is the size of the buffer) -*/ - -/***************************************************************************/ -/* for reading the content of the current zipfile, you can open it, read data - from it, and close it (you can close it before reading all the file) - */ - -extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); -/* - Open for reading data the current file in the zipfile. - If there is no error, the return value is UNZ_OK. -*/ - -extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, - const char* password)); -/* - Open for reading data the current file in the zipfile. - password is a crypting password - If there is no error, the return value is UNZ_OK. -*/ - -extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, - int* method, - int* level, - int raw)); -/* - Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) - if raw==1 - *method will receive method of compression, *level will receive level of - compression - note : you can set level parameter as NULL (if you did not want known level, - but you CANNOT set method parameter as NULL -*/ - -extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, - int* method, - int* level, - int raw, - const char* password)); -/* - Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) - if raw==1 - *method will receive method of compression, *level will receive level of - compression - note : you can set level parameter as NULL (if you did not want known level, - but you CANNOT set method parameter as NULL -*/ - - -extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); -/* - Close the file in zip opened with unzOpenCurrentFile - Return UNZ_CRCERROR if all the file was read but the CRC is not good -*/ - -extern int ZEXPORT unzReadCurrentFile OF((unzFile file, - voidp buf, - unsigned len)); -/* - Read bytes from the current file (opened by unzOpenCurrentFile) - buf contain buffer where data must be copied - len the size of buf. - - return the number of byte copied if somes bytes are copied - return 0 if the end of file was reached - return <0 with error code if there is an error - (UNZ_ERRNO for IO error, or zLib error for uncompress error) -*/ - -extern z_off_t ZEXPORT unztell OF((unzFile file)); -/* - Give the current position in uncompressed data -*/ - -extern int ZEXPORT unzeof OF((unzFile file)); -/* - return 1 if the end of file was reached, 0 elsewhere -*/ - -extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, - voidp buf, - unsigned len)); -/* - Read extra field from the current file (opened by unzOpenCurrentFile) - This is the local-header version of the extra field (sometimes, there is - more info in the local-header version than in the central-header) - - if buf==NULL, it return the size of the local extra field - - if buf!=NULL, len is the size of the buffer, the extra header is copied in - buf. - the return value is the number of bytes copied in buf, or (if <0) - the error code -*/ - -/***************************************************************************/ - -/* Get the current file offset */ -extern uLong ZEXPORT unzGetOffset (unzFile file); - -/* Set the current file offset */ -extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); - - - -#ifdef __cplusplus -} -#endif - -#endif /* _unz_H */ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff --git a/src/zlib/zconf.h b/src/zlib/zconf.h index e3b0c962e..03a9431c8 100644 --- a/src/zlib/zconf.h +++ b/src/zlib/zconf.h @@ -1,332 +1,332 @@ -/* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2005 Jean-loup Gailly. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#ifndef ZCONF_H -#define ZCONF_H - -/* - * If you *really* need a unique prefix for all types and library functions, - * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. - */ -#ifdef Z_PREFIX -# define deflateInit_ z_deflateInit_ -# define deflate z_deflate -# define deflateEnd z_deflateEnd -# define inflateInit_ z_inflateInit_ -# define inflate z_inflate -# define inflateEnd z_inflateEnd -# define deflateInit2_ z_deflateInit2_ -# define deflateSetDictionary z_deflateSetDictionary -# define deflateCopy z_deflateCopy -# define deflateReset z_deflateReset -# define deflateParams z_deflateParams -# define deflateBound z_deflateBound -# define deflatePrime z_deflatePrime -# define inflateInit2_ z_inflateInit2_ -# define inflateSetDictionary z_inflateSetDictionary -# define inflateSync z_inflateSync -# define inflateSyncPoint z_inflateSyncPoint -# define inflateCopy z_inflateCopy -# define inflateReset z_inflateReset -# define inflateBack z_inflateBack -# define inflateBackEnd z_inflateBackEnd -# define compress z_compress -# define compress2 z_compress2 -# define compressBound z_compressBound -# define uncompress z_uncompress -# define adler32 z_adler32 -# define crc32 z_crc32 -# define get_crc_table z_get_crc_table -# define zError z_zError - -# define alloc_func z_alloc_func -# define free_func z_free_func -# define in_func z_in_func -# define out_func z_out_func -# define Byte z_Byte -# define uInt z_uInt -# define uLong z_uLong -# define Bytef z_Bytef -# define charf z_charf -# define intf z_intf -# define uIntf z_uIntf -# define uLongf z_uLongf -# define voidpf z_voidpf -# define voidp z_voidp -#endif - -#if defined(__MSDOS__) && !defined(MSDOS) -# define MSDOS -#endif -#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) -# define OS2 -#endif -#if defined(_WINDOWS) && !defined(WINDOWS) -# define WINDOWS -#endif -#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) -# ifndef WIN32 -# define WIN32 -# endif -#endif -#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) -# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) -# ifndef SYS16BIT -# define SYS16BIT -# endif -# endif -#endif - -/* - * Compile with -DMAXSEG_64K if the alloc function cannot allocate more - * than 64k bytes at a time (needed on systems with 16-bit int). - */ -#ifdef SYS16BIT -# define MAXSEG_64K -#endif -#ifdef MSDOS -# define UNALIGNED_OK -#endif - -#ifdef __STDC_VERSION__ -# ifndef STDC -# define STDC -# endif -# if __STDC_VERSION__ >= 199901L -# ifndef STDC99 -# define STDC99 -# endif -# endif -#endif -#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) -# define STDC -#endif -#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) -# define STDC -#endif -#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) -# define STDC -#endif -#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) -# define STDC -#endif - -#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ -# define STDC -#endif - -#ifndef STDC -# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ -# define const /* note: need a more gentle solution here */ -# endif -#endif - -/* Some Mac compilers merge all .h files incorrectly: */ -#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) -# define NO_DUMMY_DECL -#endif - -/* Maximum value for memLevel in deflateInit2 */ -#ifndef MAX_MEM_LEVEL -# ifdef MAXSEG_64K -# define MAX_MEM_LEVEL 8 -# else -# define MAX_MEM_LEVEL 9 -# endif -#endif - -/* Maximum value for windowBits in deflateInit2 and inflateInit2. - * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files - * created by gzip. (Files created by minigzip can still be extracted by - * gzip.) - */ -#ifndef MAX_WBITS -# define MAX_WBITS 15 /* 32K LZ77 window */ -#endif - -/* The memory requirements for deflate are (in bytes): - (1 << (windowBits+2)) + (1 << (memLevel+9)) - that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) - plus a few kilobytes for small objects. For example, if you want to reduce - the default memory requirements from 256K to 128K, compile with - make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" - Of course this will generally degrade compression (there's no free lunch). - - The memory requirements for inflate are (in bytes) 1 << windowBits - that is, 32K for windowBits=15 (default value) plus a few kilobytes - for small objects. -*/ - - /* Type declarations */ - -#ifndef OF /* function prototypes */ -# ifdef STDC -# define OF(args) args -# else -# define OF(args) () -# endif -#endif - -/* The following definitions for FAR are needed only for MSDOS mixed - * model programming (small or medium model with some far allocations). - * This was tested only with MSC; for other MSDOS compilers you may have - * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, - * just define FAR to be empty. - */ -#ifdef SYS16BIT -# if defined(M_I86SM) || defined(M_I86MM) - /* MSC small or medium model */ -# define SMALL_MEDIUM -# ifdef _MSC_VER -# define FAR _far -# else -# define FAR far -# endif -# endif -# if (defined(__SMALL__) || defined(__MEDIUM__)) - /* Turbo C small or medium model */ -# define SMALL_MEDIUM -# ifdef __BORLANDC__ -# define FAR _far -# else -# define FAR far -# endif -# endif -#endif - -#if defined(WINDOWS) || defined(WIN32) - /* If building or using zlib as a DLL, define ZLIB_DLL. - * This is not mandatory, but it offers a little performance increase. - */ -# ifdef ZLIB_DLL -# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) -# ifdef ZLIB_INTERNAL -# define ZEXTERN extern __declspec(dllexport) -# else -# define ZEXTERN extern __declspec(dllimport) -# endif -# endif -# endif /* ZLIB_DLL */ - /* If building or using zlib with the WINAPI/WINAPIV calling convention, - * define ZLIB_WINAPI. - * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. - */ -# ifdef ZLIB_WINAPI -# ifdef FAR -# undef FAR -# endif -# include - /* No need for _export, use ZLIB.DEF instead. */ - /* For complete Windows compatibility, use WINAPI, not __stdcall. */ -# define ZEXPORT WINAPI -# ifdef WIN32 -# define ZEXPORTVA WINAPIV -# else -# define ZEXPORTVA FAR CDECL -# endif -# endif -#endif - -#if defined (__BEOS__) -# ifdef ZLIB_DLL -# ifdef ZLIB_INTERNAL -# define ZEXPORT __declspec(dllexport) -# define ZEXPORTVA __declspec(dllexport) -# else -# define ZEXPORT __declspec(dllimport) -# define ZEXPORTVA __declspec(dllimport) -# endif -# endif -#endif - -#ifndef ZEXTERN -# define ZEXTERN extern -#endif -#ifndef ZEXPORT -# define ZEXPORT -#endif -#ifndef ZEXPORTVA -# define ZEXPORTVA -#endif - -#ifndef FAR -# define FAR -#endif - -#if !defined(__MACTYPES__) -typedef unsigned char Byte; /* 8 bits */ -#endif -typedef unsigned int uInt; /* 16 bits or more */ -typedef unsigned long uLong; /* 32 bits or more */ - -#ifdef SMALL_MEDIUM - /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ -# define Bytef Byte FAR -#else - typedef Byte FAR Bytef; -#endif -typedef char FAR charf; -typedef int FAR intf; -typedef uInt FAR uIntf; -typedef uLong FAR uLongf; - -#ifdef STDC - typedef void const *voidpc; - typedef void FAR *voidpf; - typedef void *voidp; -#else - typedef Byte const *voidpc; - typedef Byte FAR *voidpf; - typedef Byte *voidp; -#endif - -#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ -# include /* for off_t */ -# include /* for SEEK_* and off_t */ -# ifdef VMS -# include /* for off_t */ -# endif -# define z_off_t off_t -#endif -#ifndef SEEK_SET -# define SEEK_SET 0 /* Seek from beginning of file. */ -# define SEEK_CUR 1 /* Seek from current position. */ -# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ -#endif -#ifndef z_off_t -# define z_off_t long -#endif - -#if defined(__OS400__) -# define NO_vsnprintf -#endif - -#if defined(__MVS__) -# define NO_vsnprintf -# ifdef FAR -# undef FAR -# endif -#endif - -/* MVS linker does not support external names larger than 8 bytes */ -#if defined(__MVS__) -# pragma map(deflateInit_,"DEIN") -# pragma map(deflateInit2_,"DEIN2") -# pragma map(deflateEnd,"DEEND") -# pragma map(deflateBound,"DEBND") -# pragma map(inflateInit_,"ININ") -# pragma map(inflateInit2_,"ININ2") -# pragma map(inflateEnd,"INEND") -# pragma map(inflateSync,"INSY") -# pragma map(inflateSetDictionary,"INSEDI") -# pragma map(compressBound,"CMBND") -# pragma map(inflate_table,"INTABL") -# pragma map(inflate_fast,"INFA") -# pragma map(inflate_copyright,"INCOPY") -#endif - -#endif /* ZCONF_H */ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/zlib/zlib.h b/src/zlib/zlib.h index 62d0e4675..022817927 100644 --- a/src/zlib/zlib.h +++ b/src/zlib/zlib.h @@ -1,1357 +1,1357 @@ -/* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.3, July 18th, 2005 - - Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu - - - The data format used by the zlib library is described by RFCs (Request for - Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt - (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). -*/ - -#ifndef ZLIB_H -#define ZLIB_H - -#include "zconf.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define ZLIB_VERSION "1.2.3" -#define ZLIB_VERNUM 0x1230 - -/* - The 'zlib' compression library provides in-memory compression and - decompression functions, including integrity checks of the uncompressed - data. This version of the library supports only one compression method - (deflation) but other algorithms will be added later and will have the same - stream interface. - - Compression can be done in a single step if the buffers are large - enough (for example if an input file is mmap'ed), or can be done by - repeated calls of the compression function. In the latter case, the - application must provide more input and/or consume the output - (providing more output space) before each call. - - The compressed data format used by default by the in-memory functions is - the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped - around a deflate stream, which is itself documented in RFC 1951. - - The library also supports reading and writing files in gzip (.gz) format - with an interface similar to that of stdio using the functions that start - with "gz". The gzip format is different from the zlib format. gzip is a - gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. - - This library can optionally read and write gzip streams in memory as well. - - The zlib format was designed to be compact and fast for use in memory - and on communications channels. The gzip format was designed for single- - file compression on file systems, has a larger header than zlib to maintain - directory information, and uses a different, slower check method than zlib. - - The library does not install any signal handler. The decoder checks - the consistency of the compressed data, so the library should never - crash even in case of corrupted input. -*/ - -typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); -typedef void (*free_func) OF((voidpf opaque, voidpf address)); - -struct internal_state; - -typedef struct z_stream_s { - Bytef *next_in; /* next input byte */ - uInt avail_in; /* number of bytes available at next_in */ - uLong total_in; /* total nb of input bytes read so far */ - - Bytef *next_out; /* next output byte should be put there */ - uInt avail_out; /* remaining free space at next_out */ - uLong total_out; /* total nb of bytes output so far */ - - char *msg; /* last error message, NULL if no error */ - struct internal_state FAR *state; /* not visible by applications */ - - alloc_func zalloc; /* used to allocate the internal state */ - free_func zfree; /* used to free the internal state */ - voidpf opaque; /* private data object passed to zalloc and zfree */ - - int data_type; /* best guess about the data type: binary or text */ - uLong adler; /* adler32 value of the uncompressed data */ - uLong reserved; /* reserved for future use */ -} z_stream; - -typedef z_stream FAR *z_streamp; - -/* - gzip header information passed to and from zlib routines. See RFC 1952 - for more details on the meanings of these fields. -*/ -typedef struct gz_header_s { - int text; /* true if compressed data believed to be text */ - uLong time; /* modification time */ - int xflags; /* extra flags (not used when writing a gzip file) */ - int os; /* operating system */ - Bytef *extra; /* pointer to extra field or Z_NULL if none */ - uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ - uInt extra_max; /* space at extra (only when reading header) */ - Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ - uInt name_max; /* space at name (only when reading header) */ - Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ - uInt comm_max; /* space at comment (only when reading header) */ - int hcrc; /* true if there was or will be a header crc */ - int done; /* true when done reading gzip header (not used - when writing a gzip file) */ -} gz_header; - -typedef gz_header FAR *gz_headerp; - -/* - The application must update next_in and avail_in when avail_in has - dropped to zero. It must update next_out and avail_out when avail_out - has dropped to zero. The application must initialize zalloc, zfree and - opaque before calling the init function. All other fields are set by the - compression library and must not be updated by the application. - - The opaque value provided by the application will be passed as the first - parameter for calls of zalloc and zfree. This can be useful for custom - memory management. The compression library attaches no meaning to the - opaque value. - - zalloc must return Z_NULL if there is not enough memory for the object. - If zlib is used in a multi-threaded application, zalloc and zfree must be - thread safe. - - On 16-bit systems, the functions zalloc and zfree must be able to allocate - exactly 65536 bytes, but will not be required to allocate more than this - if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, - pointers returned by zalloc for objects of exactly 65536 bytes *must* - have their offset normalized to zero. The default allocation function - provided by this library ensures this (see zutil.c). To reduce memory - requirements and avoid any allocation of 64K objects, at the expense of - compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). - - The fields total_in and total_out can be used for statistics or - progress reports. After compression, total_in holds the total size of - the uncompressed data and may be saved for use in the decompressor - (particularly if the decompressor wants to decompress everything in - a single step). -*/ - - /* constants */ - -#define Z_NO_FLUSH 0 -#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ -#define Z_SYNC_FLUSH 2 -#define Z_FULL_FLUSH 3 -#define Z_FINISH 4 -#define Z_BLOCK 5 -/* Allowed flush values; see deflate() and inflate() below for details */ - -#define Z_OK 0 -#define Z_STREAM_END 1 -#define Z_NEED_DICT 2 -#define Z_ERRNO (-1) -#define Z_STREAM_ERROR (-2) -#define Z_DATA_ERROR (-3) -#define Z_MEM_ERROR (-4) -#define Z_BUF_ERROR (-5) -#define Z_VERSION_ERROR (-6) -/* Return codes for the compression/decompression functions. Negative - * values are errors, positive values are used for special but normal events. - */ - -#define Z_NO_COMPRESSION 0 -#define Z_BEST_SPEED 1 -#define Z_BEST_COMPRESSION 9 -#define Z_DEFAULT_COMPRESSION (-1) -/* compression levels */ - -#define Z_FILTERED 1 -#define Z_HUFFMAN_ONLY 2 -#define Z_RLE 3 -#define Z_FIXED 4 -#define Z_DEFAULT_STRATEGY 0 -/* compression strategy; see deflateInit2() below for details */ - -#define Z_BINARY 0 -#define Z_TEXT 1 -#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ -#define Z_UNKNOWN 2 -/* Possible values of the data_type field (though see inflate()) */ - -#define Z_DEFLATED 8 -/* The deflate compression method (the only one supported in this version) */ - -#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ - -#define zlib_version zlibVersion() -/* for compatibility with versions < 1.0.2 */ - - /* basic functions */ - -ZEXTERN const char * ZEXPORT zlibVersion OF((void)); -/* The application can compare zlibVersion and ZLIB_VERSION for consistency. - If the first character differs, the library code actually used is - not compatible with the zlib.h header file used by the application. - This check is automatically made by deflateInit and inflateInit. - */ - -/* -ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); - - Initializes the internal stream state for compression. The fields - zalloc, zfree and opaque must be initialized before by the caller. - If zalloc and zfree are set to Z_NULL, deflateInit updates them to - use default allocation functions. - - The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: - 1 gives best speed, 9 gives best compression, 0 gives no compression at - all (the input data is simply copied a block at a time). - Z_DEFAULT_COMPRESSION requests a default compromise between speed and - compression (currently equivalent to level 6). - - deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if level is not a valid compression level, - Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible - with the version assumed by the caller (ZLIB_VERSION). - msg is set to null if there is no error message. deflateInit does not - perform any compression: this will be done by deflate(). -*/ - - -ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); -/* - deflate compresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce some - output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. deflate performs one or both of the - following actions: - - - Compress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in and avail_in are updated and - processing will resume at this point for the next call of deflate(). - - - Provide more output starting at next_out and update next_out and avail_out - accordingly. This action is forced if the parameter flush is non zero. - Forcing flush frequently degrades the compression ratio, so this parameter - should be set only when necessary (in interactive applications). - Some output may be provided even if flush is not set. - - Before the call of deflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming - more output, and updating avail_in or avail_out accordingly; avail_out - should never be zero before the call. The application can consume the - compressed output when it wants, for example when the output buffer is full - (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK - and with zero avail_out, it must be called again after making room in the - output buffer because there might be more output pending. - - Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to - decide how much data to accumualte before producing output, in order to - maximize compression. - - If the parameter flush is set to Z_SYNC_FLUSH, all pending output is - flushed to the output buffer and the output is aligned on a byte boundary, so - that the decompressor can get all input data available so far. (In particular - avail_in is zero after the call if enough output space has been provided - before the call.) Flushing may degrade compression for some compression - algorithms and so it should be used only when necessary. - - If flush is set to Z_FULL_FLUSH, all output is flushed as with - Z_SYNC_FLUSH, and the compression state is reset so that decompression can - restart from this point if previous compressed data has been damaged or if - random access is desired. Using Z_FULL_FLUSH too often can seriously degrade - compression. - - If deflate returns with avail_out == 0, this function must be called again - with the same value of the flush parameter and more output space (updated - avail_out), until the flush is complete (deflate returns with non-zero - avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that - avail_out is greater than six to avoid repeated flush markers due to - avail_out == 0 on return. - - If the parameter flush is set to Z_FINISH, pending input is processed, - pending output is flushed and deflate returns with Z_STREAM_END if there - was enough output space; if deflate returns with Z_OK, this function must be - called again with Z_FINISH and more output space (updated avail_out) but no - more input data, until it returns with Z_STREAM_END or an error. After - deflate has returned Z_STREAM_END, the only possible operations on the - stream are deflateReset or deflateEnd. - - Z_FINISH can be used immediately after deflateInit if all the compression - is to be done in a single step. In this case, avail_out must be at least - the value returned by deflateBound (see below). If deflate does not return - Z_STREAM_END, then it must be called again as described above. - - deflate() sets strm->adler to the adler32 checksum of all input read - so far (that is, total_in bytes). - - deflate() may update strm->data_type if it can make a good guess about - the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered - binary. This field is only for information purposes and does not affect - the compression algorithm in any manner. - - deflate() returns Z_OK if some progress has been made (more input - processed or more output produced), Z_STREAM_END if all input has been - consumed and all output has been produced (only when flush is set to - Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible - (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not - fatal, and deflate() can be called again with more input and more output - space to continue compressing. -*/ - - -ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. - - deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the - stream state was inconsistent, Z_DATA_ERROR if the stream was freed - prematurely (some input or output was discarded). In the error case, - msg may be set but then points to a static string (which must not be - deallocated). -*/ - - -/* -ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); - - Initializes the internal stream state for decompression. The fields - next_in, avail_in, zalloc, zfree and opaque must be initialized before by - the caller. If next_in is not Z_NULL and avail_in is large enough (the exact - value depends on the compression method), inflateInit determines the - compression method from the zlib header and allocates all data structures - accordingly; otherwise the allocation will be deferred to the first call of - inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to - use default allocation functions. - - inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller. msg is set to null if there is no error - message. inflateInit does not perform any decompression apart from reading - the zlib header if present: this will be done by inflate(). (So next_in and - avail_in may be modified, but next_out and avail_out are unchanged.) -*/ - - -ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); -/* - inflate decompresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce - some output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. inflate performs one or both of the - following actions: - - - Decompress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in is updated and processing - will resume at this point for the next call of inflate(). - - - Provide more output starting at next_out and update next_out and avail_out - accordingly. inflate() provides as much output as possible, until there - is no more input data or no more space in the output buffer (see below - about the flush parameter). - - Before the call of inflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming - more output, and updating the next_* and avail_* values accordingly. - The application can consume the uncompressed output when it wants, for - example when the output buffer is full (avail_out == 0), or after each - call of inflate(). If inflate returns Z_OK and with zero avail_out, it - must be called again after making room in the output buffer because there - might be more output pending. - - The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, - Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much - output as possible to the output buffer. Z_BLOCK requests that inflate() stop - if and when it gets to the next deflate block boundary. When decoding the - zlib or gzip format, this will cause inflate() to return immediately after - the header and before the first block. When doing a raw inflate, inflate() - will go ahead and process the first block, and will return when it gets to - the end of that block, or when it runs out of data. - - The Z_BLOCK option assists in appending to or combining deflate streams. - Also to assist in this, on return inflate() will set strm->data_type to the - number of unused bits in the last byte taken from strm->next_in, plus 64 - if inflate() is currently decoding the last block in the deflate stream, - plus 128 if inflate() returned immediately after decoding an end-of-block - code or decoding the complete header up to just before the first byte of the - deflate stream. The end-of-block will not be indicated until all of the - uncompressed data from that block has been written to strm->next_out. The - number of unused bits may in general be greater than seven, except when - bit 7 of data_type is set, in which case the number of unused bits will be - less than eight. - - inflate() should normally be called until it returns Z_STREAM_END or an - error. However if all decompression is to be performed in a single step - (a single call of inflate), the parameter flush should be set to - Z_FINISH. In this case all pending input is processed and all pending - output is flushed; avail_out must be large enough to hold all the - uncompressed data. (The size of the uncompressed data may have been saved - by the compressor for this purpose.) The next operation on this stream must - be inflateEnd to deallocate the decompression state. The use of Z_FINISH - is never required, but can be used to inform inflate that a faster approach - may be used for the single inflate() call. - - In this implementation, inflate() always flushes as much output as - possible to the output buffer, and always uses the faster approach on the - first call. So the only effect of the flush parameter in this implementation - is on the return value of inflate(), as noted below, or when it returns early - because Z_BLOCK is used. - - If a preset dictionary is needed after this call (see inflateSetDictionary - below), inflate sets strm->adler to the adler32 checksum of the dictionary - chosen by the compressor and returns Z_NEED_DICT; otherwise it sets - strm->adler to the adler32 checksum of all output produced so far (that is, - total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described - below. At the end of the stream, inflate() checks that its computed adler32 - checksum is equal to that saved by the compressor and returns Z_STREAM_END - only if the checksum is correct. - - inflate() will decompress and check either zlib-wrapped or gzip-wrapped - deflate data. The header type is detected automatically. Any information - contained in the gzip header is not retained, so applications that need that - information should instead use raw inflate, see inflateInit2() below, or - inflateBack() and perform their own processing of the gzip header and - trailer. - - inflate() returns Z_OK if some progress has been made (more input processed - or more output produced), Z_STREAM_END if the end of the compressed data has - been reached and all uncompressed output has been produced, Z_NEED_DICT if a - preset dictionary is needed at this point, Z_DATA_ERROR if the input data was - corrupted (input stream not conforming to the zlib format or incorrect check - value), Z_STREAM_ERROR if the stream structure was inconsistent (for example - if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, - Z_BUF_ERROR if no progress is possible or if there was not enough room in the - output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and - inflate() can be called again with more input and more output space to - continue decompressing. If Z_DATA_ERROR is returned, the application may then - call inflateSync() to look for a good compression block if a partial recovery - of the data is desired. -*/ - - -ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. - - inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state - was inconsistent. In the error case, msg may be set but then points to a - static string (which must not be deallocated). -*/ - - /* Advanced functions */ - -/* - The following functions are needed only in some special applications. -*/ - -/* -ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, - int level, - int method, - int windowBits, - int memLevel, - int strategy)); - - This is another version of deflateInit with more compression options. The - fields next_in, zalloc, zfree and opaque must be initialized before by - the caller. - - The method parameter is the compression method. It must be Z_DEFLATED in - this version of the library. - - The windowBits parameter is the base two logarithm of the window size - (the size of the history buffer). It should be in the range 8..15 for this - version of the library. Larger values of this parameter result in better - compression at the expense of memory usage. The default value is 15 if - deflateInit is used instead. - - windowBits can also be -8..-15 for raw deflate. In this case, -windowBits - determines the window size. deflate() will then generate raw deflate data - with no zlib header or trailer, and will not compute an adler32 check value. - - windowBits can also be greater than 15 for optional gzip encoding. Add - 16 to windowBits to write a simple gzip header and trailer around the - compressed data instead of a zlib wrapper. The gzip header will have no - file name, no extra data, no comment, no modification time (set to zero), - no header crc, and the operating system will be set to 255 (unknown). If a - gzip stream is being written, strm->adler is a crc32 instead of an adler32. - - The memLevel parameter specifies how much memory should be allocated - for the internal compression state. memLevel=1 uses minimum memory but - is slow and reduces compression ratio; memLevel=9 uses maximum memory - for optimal speed. The default value is 8. See zconf.h for total memory - usage as a function of windowBits and memLevel. - - The strategy parameter is used to tune the compression algorithm. Use the - value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a - filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no - string match), or Z_RLE to limit match distances to one (run-length - encoding). Filtered data consists mostly of small values with a somewhat - random distribution. In this case, the compression algorithm is tuned to - compress them better. The effect of Z_FILTERED is to force more Huffman - coding and less string matching; it is somewhat intermediate between - Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as - Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy - parameter only affects the compression ratio but not the correctness of the - compressed output even if it is not set appropriately. Z_FIXED prevents the - use of dynamic Huffman codes, allowing for a simpler decoder for special - applications. - - deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid - method). msg is set to null if there is no error message. deflateInit2 does - not perform any compression: this will be done by deflate(). -*/ - -ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); -/* - Initializes the compression dictionary from the given byte sequence - without producing any compressed output. This function must be called - immediately after deflateInit, deflateInit2 or deflateReset, before any - call of deflate. The compressor and decompressor must use exactly the same - dictionary (see inflateSetDictionary). - - The dictionary should consist of strings (byte sequences) that are likely - to be encountered later in the data to be compressed, with the most commonly - used strings preferably put towards the end of the dictionary. Using a - dictionary is most useful when the data to be compressed is short and can be - predicted with good accuracy; the data can then be compressed better than - with the default empty dictionary. - - Depending on the size of the compression data structures selected by - deflateInit or deflateInit2, a part of the dictionary may in effect be - discarded, for example if the dictionary is larger than the window size in - deflate or deflate2. Thus the strings most likely to be useful should be - put at the end of the dictionary, not at the front. In addition, the - current implementation of deflate will use at most the window size minus - 262 bytes of the provided dictionary. - - Upon return of this function, strm->adler is set to the adler32 value - of the dictionary; the decompressor may later use this value to determine - which dictionary has been used by the compressor. (The adler32 value - applies to the whole dictionary even if only a subset of the dictionary is - actually used by the compressor.) If a raw deflate was requested, then the - adler32 value is not computed and strm->adler is not set. - - deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is - inconsistent (for example if deflate has already been called for this stream - or if the compression method is bsort). deflateSetDictionary does not - perform any compression: this will be done by deflate(). -*/ - -ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, - z_streamp source)); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when several compression strategies will be - tried, for example when there are several ways of pre-processing the input - data with a filter. The streams that will be discarded should then be freed - by calling deflateEnd. Note that deflateCopy duplicates the internal - compression state which can be quite large, so this strategy is slow and - can consume lots of memory. - - deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and - destination. -*/ - -ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); -/* - This function is equivalent to deflateEnd followed by deflateInit, - but does not free and reallocate all the internal compression state. - The stream will keep the same compression level and any other attributes - that may have been set by deflateInit2. - - deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). -*/ - -ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, - int level, - int strategy)); -/* - Dynamically update the compression level and compression strategy. The - interpretation of level and strategy is as in deflateInit2. This can be - used to switch between compression and straight copy of the input data, or - to switch to a different kind of input data requiring a different - strategy. If the compression level is changed, the input available so far - is compressed with the old level (and may be flushed); the new level will - take effect only at the next call of deflate(). - - Before the call of deflateParams, the stream state must be set as for - a call of deflate(), since the currently available input may have to - be compressed and flushed. In particular, strm->avail_out must be non-zero. - - deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source - stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR - if strm->avail_out was zero. -*/ - -ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, - int good_length, - int max_lazy, - int nice_length, - int max_chain)); -/* - Fine tune deflate's internal compression parameters. This should only be - used by someone who understands the algorithm used by zlib's deflate for - searching for the best matching string, and even then only by the most - fanatic optimizer trying to squeeze out the last compressed bit for their - specific input data. Read the deflate.c source code for the meaning of the - max_lazy, good_length, nice_length, and max_chain parameters. - - deflateTune() can be called after deflateInit() or deflateInit2(), and - returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. - */ - -ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, - uLong sourceLen)); -/* - deflateBound() returns an upper bound on the compressed size after - deflation of sourceLen bytes. It must be called after deflateInit() - or deflateInit2(). This would be used to allocate an output buffer - for deflation in a single pass, and so would be called before deflate(). -*/ - -ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, - int bits, - int value)); -/* - deflatePrime() inserts bits in the deflate output stream. The intent - is that this function is used to start off the deflate output with the - bits leftover from a previous deflate stream when appending to it. As such, - this function can only be used for raw deflate, and must be used before the - first deflate() call after a deflateInit2() or deflateReset(). bits must be - less than or equal to 16, and that many of the least significant bits of - value will be inserted in the output. - - deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, - gz_headerp head)); -/* - deflateSetHeader() provides gzip header information for when a gzip - stream is requested by deflateInit2(). deflateSetHeader() may be called - after deflateInit2() or deflateReset() and before the first call of - deflate(). The text, time, os, extra field, name, and comment information - in the provided gz_header structure are written to the gzip header (xflag is - ignored -- the extra flags are set according to the compression level). The - caller must assure that, if not Z_NULL, name and comment are terminated with - a zero byte, and that if extra is not Z_NULL, that extra_len bytes are - available there. If hcrc is true, a gzip header crc is included. Note that - the current versions of the command-line version of gzip (up through version - 1.3.x) do not support header crc's, and will report that it is a "multi-part - gzip file" and give up. - - If deflateSetHeader is not used, the default gzip header has text false, - the time set to zero, and os set to 255, with no extra, name, or comment - fields. The gzip header is returned to the default state by deflateReset(). - - deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -/* -ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, - int windowBits)); - - This is another version of inflateInit with an extra parameter. The - fields next_in, avail_in, zalloc, zfree and opaque must be initialized - before by the caller. - - The windowBits parameter is the base two logarithm of the maximum window - size (the size of the history buffer). It should be in the range 8..15 for - this version of the library. The default value is 15 if inflateInit is used - instead. windowBits must be greater than or equal to the windowBits value - provided to deflateInit2() while compressing, or it must be equal to 15 if - deflateInit2() was not used. If a compressed stream with a larger window - size is given as input, inflate() will return with the error code - Z_DATA_ERROR instead of trying to allocate a larger window. - - windowBits can also be -8..-15 for raw inflate. In this case, -windowBits - determines the window size. inflate() will then process raw deflate data, - not looking for a zlib or gzip header, not generating a check value, and not - looking for any check values for comparison at the end of the stream. This - is for use with other formats that use the deflate compressed data format - such as zip. Those formats provide their own check values. If a custom - format is developed using the raw deflate format for compressed data, it is - recommended that a check value such as an adler32 or a crc32 be applied to - the uncompressed data as is done in the zlib, gzip, and zip formats. For - most applications, the zlib format should be used as is. Note that comments - above on the use in deflateInit2() applies to the magnitude of windowBits. - - windowBits can also be greater than 15 for optional gzip decoding. Add - 32 to windowBits to enable zlib and gzip decoding with automatic header - detection, or add 16 to decode only the gzip format (the zlib format will - return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is - a crc32 instead of an adler32. - - inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg - is set to null if there is no error message. inflateInit2 does not perform - any decompression apart from reading the zlib header if present: this will - be done by inflate(). (So next_in and avail_in may be modified, but next_out - and avail_out are unchanged.) -*/ - -ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); -/* - Initializes the decompression dictionary from the given uncompressed byte - sequence. This function must be called immediately after a call of inflate, - if that call returned Z_NEED_DICT. The dictionary chosen by the compressor - can be determined from the adler32 value returned by that call of inflate. - The compressor and decompressor must use exactly the same dictionary (see - deflateSetDictionary). For raw inflate, this function can be called - immediately after inflateInit2() or inflateReset() and before any call of - inflate() to set the dictionary. The application must insure that the - dictionary that was used for compression is provided. - - inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is - inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect adler32 value). inflateSetDictionary does not - perform any decompression: this will be done by subsequent calls of - inflate(). -*/ - -ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); -/* - Skips invalid compressed data until a full flush point (see above the - description of deflate with Z_FULL_FLUSH) can be found, or until all - available input is skipped. No output is provided. - - inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR - if no more input was provided, Z_DATA_ERROR if no flush point has been found, - or Z_STREAM_ERROR if the stream structure was inconsistent. In the success - case, the application may save the current current value of total_in which - indicates where valid compressed data was found. In the error case, the - application may repeatedly call inflateSync, providing more input each time, - until success or end of the input data. -*/ - -ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, - z_streamp source)); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when randomly accessing a large stream. The - first pass through the stream can periodically record the inflate state, - allowing restarting inflate at those points when randomly accessing the - stream. - - inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and - destination. -*/ - -ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); -/* - This function is equivalent to inflateEnd followed by inflateInit, - but does not free and reallocate all the internal decompression state. - The stream will keep attributes that may have been set by inflateInit2. - - inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). -*/ - -ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, - int bits, - int value)); -/* - This function inserts bits in the inflate input stream. The intent is - that this function is used to start inflating at a bit position in the - middle of a byte. The provided bits will be used before any bytes are used - from next_in. This function should only be used with raw inflate, and - should be used before the first inflate() call after inflateInit2() or - inflateReset(). bits must be less than or equal to 16, and that many of the - least significant bits of value will be inserted in the input. - - inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, - gz_headerp head)); -/* - inflateGetHeader() requests that gzip header information be stored in the - provided gz_header structure. inflateGetHeader() may be called after - inflateInit2() or inflateReset(), and before the first call of inflate(). - As inflate() processes the gzip stream, head->done is zero until the header - is completed, at which time head->done is set to one. If a zlib stream is - being decoded, then head->done is set to -1 to indicate that there will be - no gzip header information forthcoming. Note that Z_BLOCK can be used to - force inflate() to return immediately after header processing is complete - and before any actual data is decompressed. - - The text, time, xflags, and os fields are filled in with the gzip header - contents. hcrc is set to true if there is a header CRC. (The header CRC - was valid if done is set to one.) If extra is not Z_NULL, then extra_max - contains the maximum number of bytes to write to extra. Once done is true, - extra_len contains the actual extra field length, and extra contains the - extra field, or that field truncated if extra_max is less than extra_len. - If name is not Z_NULL, then up to name_max characters are written there, - terminated with a zero unless the length is greater than name_max. If - comment is not Z_NULL, then up to comm_max characters are written there, - terminated with a zero unless the length is greater than comm_max. When - any of extra, name, or comment are not Z_NULL and the respective field is - not present in the header, then that field is set to Z_NULL to signal its - absence. This allows the use of deflateSetHeader() with the returned - structure to duplicate the header. However if those fields are set to - allocated memory, then the application will need to save those pointers - elsewhere so that they can be eventually freed. - - If inflateGetHeader is not used, then the header information is simply - discarded. The header is always checked for validity, including the header - CRC if present. inflateReset() will reset the process to discard the header - information. The application would need to call inflateGetHeader() again to - retrieve the header from the next gzip stream. - - inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -/* -ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, - unsigned char FAR *window)); - - Initialize the internal stream state for decompression using inflateBack() - calls. The fields zalloc, zfree and opaque in strm must be initialized - before the call. If zalloc and zfree are Z_NULL, then the default library- - derived memory allocation routines are used. windowBits is the base two - logarithm of the window size, in the range 8..15. window is a caller - supplied buffer of that size. Except for special applications where it is - assured that deflate was used with small window sizes, windowBits must be 15 - and a 32K byte window must be supplied to be able to decompress general - deflate streams. - - See inflateBack() for the usage of these routines. - - inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of - the paramaters are invalid, Z_MEM_ERROR if the internal state could not - be allocated, or Z_VERSION_ERROR if the version of the library does not - match the version of the header file. -*/ - -typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); -typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); - -ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, - in_func in, void FAR *in_desc, - out_func out, void FAR *out_desc)); -/* - inflateBack() does a raw inflate with a single call using a call-back - interface for input and output. This is more efficient than inflate() for - file i/o applications in that it avoids copying between the output and the - sliding window by simply making the window itself the output buffer. This - function trusts the application to not change the output buffer passed by - the output function, at least until inflateBack() returns. - - inflateBackInit() must be called first to allocate the internal state - and to initialize the state with the user-provided window buffer. - inflateBack() may then be used multiple times to inflate a complete, raw - deflate stream with each call. inflateBackEnd() is then called to free - the allocated state. - - A raw deflate stream is one with no zlib or gzip header or trailer. - This routine would normally be used in a utility that reads zip or gzip - files and writes out uncompressed files. The utility would decode the - header and process the trailer on its own, hence this routine expects - only the raw deflate stream to decompress. This is different from the - normal behavior of inflate(), which expects either a zlib or gzip header and - trailer around the deflate stream. - - inflateBack() uses two subroutines supplied by the caller that are then - called by inflateBack() for input and output. inflateBack() calls those - routines until it reads a complete deflate stream and writes out all of the - uncompressed data, or until it encounters an error. The function's - parameters and return types are defined above in the in_func and out_func - typedefs. inflateBack() will call in(in_desc, &buf) which should return the - number of bytes of provided input, and a pointer to that input in buf. If - there is no input available, in() must return zero--buf is ignored in that - case--and inflateBack() will return a buffer error. inflateBack() will call - out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() - should return zero on success, or non-zero on failure. If out() returns - non-zero, inflateBack() will return with an error. Neither in() nor out() - are permitted to change the contents of the window provided to - inflateBackInit(), which is also the buffer that out() uses to write from. - The length written by out() will be at most the window size. Any non-zero - amount of input may be provided by in(). - - For convenience, inflateBack() can be provided input on the first call by - setting strm->next_in and strm->avail_in. If that input is exhausted, then - in() will be called. Therefore strm->next_in must be initialized before - calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called - immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in - must also be initialized, and then if strm->avail_in is not zero, input will - initially be taken from strm->next_in[0 .. strm->avail_in - 1]. - - The in_desc and out_desc parameters of inflateBack() is passed as the - first parameter of in() and out() respectively when they are called. These - descriptors can be optionally used to pass any information that the caller- - supplied in() and out() functions need to do their job. - - On return, inflateBack() will set strm->next_in and strm->avail_in to - pass back any unused input that was provided by the last in() call. The - return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR - if in() or out() returned an error, Z_DATA_ERROR if there was a format - error in the deflate stream (in which case strm->msg is set to indicate the - nature of the error), or Z_STREAM_ERROR if the stream was not properly - initialized. In the case of Z_BUF_ERROR, an input or output error can be - distinguished using strm->next_in which will be Z_NULL only if in() returned - an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to - out() returning non-zero. (in() will always be called before out(), so - strm->next_in is assured to be defined if out() returns non-zero.) Note - that inflateBack() cannot return Z_OK. -*/ - -ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); -/* - All memory allocated by inflateBackInit() is freed. - - inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream - state was inconsistent. -*/ - -ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); -/* Return flags indicating compile-time options. - - Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: - 1.0: size of uInt - 3.2: size of uLong - 5.4: size of voidpf (pointer) - 7.6: size of z_off_t - - Compiler, assembler, and debug options: - 8: DEBUG - 9: ASMV or ASMINF -- use ASM code - 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention - 11: 0 (reserved) - - One-time table building (smaller code, but not thread-safe if true): - 12: BUILDFIXED -- build static block decoding tables when needed - 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed - 14,15: 0 (reserved) - - Library content (indicates missing functionality): - 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking - deflate code when not needed) - 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect - and decode gzip streams (to avoid linking crc code) - 18-19: 0 (reserved) - - Operation variations (changes in library functionality): - 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate - 21: FASTEST -- deflate algorithm with only one, lowest compression level - 22,23: 0 (reserved) - - The sprintf variant used by gzprintf (zero is best): - 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format - 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! - 26: 0 = returns value, 1 = void -- 1 means inferred string length returned - - Remainder: - 27-31: 0 (reserved) - */ - - - /* utility functions */ - -/* - The following utility functions are implemented on top of the - basic stream-oriented functions. To simplify the interface, some - default options are assumed (compression level and memory usage, - standard memory allocation functions). The source code of these - utility functions can easily be modified if you need special options. -*/ - -ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); -/* - Compresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be at least the value returned - by compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. - This function can be used to compress a whole file at once if the - input file is mmap'ed. - compress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer. -*/ - -ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen, - int level)); -/* - Compresses the source buffer into the destination buffer. The level - parameter has the same meaning as in deflateInit. sourceLen is the byte - length of the source buffer. Upon entry, destLen is the total size of the - destination buffer, which must be at least the value returned by - compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. - - compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_BUF_ERROR if there was not enough room in the output buffer, - Z_STREAM_ERROR if the level parameter is invalid. -*/ - -ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); -/* - compressBound() returns an upper bound on the compressed size after - compress() or compress2() on sourceLen bytes. It would be used before - a compress() or compress2() call to allocate the destination buffer. -*/ - -ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); -/* - Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be large enough to hold the - entire uncompressed data. (The size of the uncompressed data must have - been saved previously by the compressor and transmitted to the decompressor - by some mechanism outside the scope of this compression library.) - Upon exit, destLen is the actual size of the compressed buffer. - This function can be used to decompress a whole file at once if the - input file is mmap'ed. - - uncompress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. -*/ - - -typedef voidp gzFile; - -ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); -/* - Opens a gzip (.gz) file for reading or writing. The mode parameter - is as in fopen ("rb" or "wb") but can also include a compression level - ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for - Huffman only compression as in "wb1h", or 'R' for run-length encoding - as in "wb1R". (See the description of deflateInit2 for more information - about the strategy parameter.) - - gzopen can be used to read a file which is not in gzip format; in this - case gzread will directly read from the file without decompression. - - gzopen returns NULL if the file could not be opened or if there was - insufficient memory to allocate the (de)compression state; errno - can be checked to distinguish the two cases (if errno is zero, the - zlib error is Z_MEM_ERROR). */ - -ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); -/* - gzdopen() associates a gzFile with the file descriptor fd. File - descriptors are obtained from calls like open, dup, creat, pipe or - fileno (in the file has been previously opened with fopen). - The mode parameter is as in gzopen. - The next call of gzclose on the returned gzFile will also close the - file descriptor fd, just like fclose(fdopen(fd), mode) closes the file - descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). - gzdopen returns NULL if there was insufficient memory to allocate - the (de)compression state. -*/ - -ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); -/* - Dynamically update the compression level or strategy. See the description - of deflateInit2 for the meaning of these parameters. - gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not - opened for writing. -*/ - -ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); -/* - Reads the given number of uncompressed bytes from the compressed file. - If the input file was not in gzip format, gzread copies the given number - of bytes into the buffer. - gzread returns the number of uncompressed bytes actually read (0 for - end of file, -1 for error). */ - -ZEXTERN int ZEXPORT gzwrite OF((gzFile file, - voidpc buf, unsigned len)); -/* - Writes the given number of uncompressed bytes into the compressed file. - gzwrite returns the number of uncompressed bytes actually written - (0 in case of error). -*/ - -ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); -/* - Converts, formats, and writes the args to the compressed file under - control of the format string, as in fprintf. gzprintf returns the number of - uncompressed bytes actually written (0 in case of error). The number of - uncompressed bytes written is limited to 4095. The caller should assure that - this limit is not exceeded. If it is exceeded, then gzprintf() will return - return an error (0) with nothing written. In this case, there may also be a - buffer overflow with unpredictable consequences, which is possible only if - zlib was compiled with the insecure functions sprintf() or vsprintf() - because the secure snprintf() or vsnprintf() functions were not available. -*/ - -ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); -/* - Writes the given null-terminated string to the compressed file, excluding - the terminating null character. - gzputs returns the number of characters written, or -1 in case of error. -*/ - -ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); -/* - Reads bytes from the compressed file until len-1 characters are read, or - a newline character is read and transferred to buf, or an end-of-file - condition is encountered. The string is then terminated with a null - character. - gzgets returns buf, or Z_NULL in case of error. -*/ - -ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); -/* - Writes c, converted to an unsigned char, into the compressed file. - gzputc returns the value that was written, or -1 in case of error. -*/ - -ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); -/* - Reads one byte from the compressed file. gzgetc returns this byte - or -1 in case of end of file or error. -*/ - -ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); -/* - Push one character back onto the stream to be read again later. - Only one character of push-back is allowed. gzungetc() returns the - character pushed, or -1 on failure. gzungetc() will fail if a - character has been pushed but not read yet, or if c is -1. The pushed - character will be discarded if the stream is repositioned with gzseek() - or gzrewind(). -*/ - -ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); -/* - Flushes all pending output into the compressed file. The parameter - flush is as in the deflate() function. The return value is the zlib - error number (see function gzerror below). gzflush returns Z_OK if - the flush parameter is Z_FINISH and all output could be flushed. - gzflush should be called only when strictly necessary because it can - degrade compression. -*/ - -ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, - z_off_t offset, int whence)); -/* - Sets the starting position for the next gzread or gzwrite on the - given compressed file. The offset represents a number of bytes in the - uncompressed data stream. The whence parameter is defined as in lseek(2); - the value SEEK_END is not supported. - If the file is opened for reading, this function is emulated but can be - extremely slow. If the file is opened for writing, only forward seeks are - supported; gzseek then compresses a sequence of zeroes up to the new - starting position. - - gzseek returns the resulting offset location as measured in bytes from - the beginning of the uncompressed stream, or -1 in case of error, in - particular if the file is opened for writing and the new starting position - would be before the current position. -*/ - -ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); -/* - Rewinds the given file. This function is supported only for reading. - - gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) -*/ - -ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); -/* - Returns the starting position for the next gzread or gzwrite on the - given compressed file. This position represents a number of bytes in the - uncompressed data stream. - - gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) -*/ - -ZEXTERN int ZEXPORT gzeof OF((gzFile file)); -/* - Returns 1 when EOF has previously been detected reading the given - input stream, otherwise zero. -*/ - -ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); -/* - Returns 1 if file is being read directly without decompression, otherwise - zero. -*/ - -ZEXTERN int ZEXPORT gzclose OF((gzFile file)); -/* - Flushes all pending output if necessary, closes the compressed file - and deallocates all the (de)compression state. The return value is the zlib - error number (see function gzerror below). -*/ - -ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); -/* - Returns the error message for the last error which occurred on the - given compressed file. errnum is set to zlib error number. If an - error occurred in the file system and not in the compression library, - errnum is set to Z_ERRNO and the application may consult errno - to get the exact error code. -*/ - -ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); -/* - Clears the error and end-of-file flags for file. This is analogous to the - clearerr() function in stdio. This is useful for continuing to read a gzip - file that is being written concurrently. -*/ - - /* checksum functions */ - -/* - These functions are not related to compression but are exported - anyway because they might be useful in applications using the - compression library. -*/ - -ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); -/* - Update a running Adler-32 checksum with the bytes buf[0..len-1] and - return the updated checksum. If buf is NULL, this function returns - the required initial value for the checksum. - An Adler-32 checksum is almost as reliable as a CRC32 but can be computed - much faster. Usage example: - - uLong adler = adler32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) { - adler = adler32(adler, buffer, length); - } - if (adler != original_adler) error(); -*/ - -ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, - z_off_t len2)); -/* - Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 - and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for - each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of - seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. -*/ - -ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); -/* - Update a running CRC-32 with the bytes buf[0..len-1] and return the - updated CRC-32. If buf is NULL, this function returns the required initial - value for the for the crc. Pre- and post-conditioning (one's complement) is - performed within this function so it shouldn't be done by the application. - Usage example: - - uLong crc = crc32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) { - crc = crc32(crc, buffer, length); - } - if (crc != original_crc) error(); -*/ - -ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); - -/* - Combine two CRC-32 check values into one. For two sequences of bytes, - seq1 and seq2 with lengths len1 and len2, CRC-32 check values were - calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 - check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and - len2. -*/ - - - /* various hacks, don't look :) */ - -/* deflateInit and inflateInit are macros to allow checking the zlib version - * and the compiler's view of z_stream: - */ -ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, - int windowBits, int memLevel, - int strategy, const char *version, - int stream_size)); -ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, - unsigned char FAR *window, - const char *version, - int stream_size)); -#define deflateInit(strm, level) \ - deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) -#define inflateInit(strm) \ - inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) -#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ - deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, sizeof(z_stream)) -#define inflateInit2(strm, windowBits) \ - inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) -#define inflateBackInit(strm, windowBits, window) \ - inflateBackInit_((strm), (windowBits), (window), \ - ZLIB_VERSION, sizeof(z_stream)) - - -#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) - struct internal_state {int dummy;}; /* hack for buggy compilers */ -#endif - -ZEXTERN const char * ZEXPORT zError OF((int)); -ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); -ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); - -#ifdef __cplusplus -} -#endif - -#endif /* ZLIB_H */ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ -- cgit v1.2.3-60-g2f50