#include "conf.h" #include "sysdep.h" #include <stdarg.h> #include "structs.h" #include "utils.h" #include "db.h" #include "handler.h" #include "dbinterface.h" #include "comm.h" /* MySQL Database Connection Routines */ void connect_database (void) { SQLdb = (MYSQL *) malloc (sizeof(MYSQL)); mysql_init(SQLdb); if (!mysql_real_connect(SQLdb, mySQL_host, mySQL_user, mySQL_pass, "players", mySQL_port, NULL, 0)) { sprintf(buf, "Could not connect to mySQL host database server: %s", mysql_error(SQLdb)); log (buf); exit(0); } } /* Stormilicious (c) 2000 MySQL Database Server Playerfile Insertion/Retrieval Routines, Manipulation HOWTO When you add a new element to the character structures and you want it to save, you have to do the following: 1. Increment or decrement (if you're adding a saving element or removing one, respectively) NUM_PLAYERS_MAIN_ROW_ELEMENTS by the appropriate number (for fools that don't get it: if you're adding an element, add 1 to NUM_PLAYERSetc.) 2. Add a column into the MySQL player_main table and name it appropriatley (I don't know how to do this myself yet, so you're on your own). 3. In the init_querystring function, add: COLUMN_[type]("[column_name]",[pointer]); type is NRM for numeric variables, and STR for strings. column_name is the name of the column you added to the player_main table. pointer is the pointer defined off of struct char_data *ch. COLUMN_NULL is provided for compatibility, it accepts a column name as it's only argument - use this when you have a column and don't want it to be loaded in on a player entry retrieve. Examples: COLUMN_NRM("idnum",GET_IDNUM(ch)); ^^ I'm adding a numeric saving value, which is saved in the idnum column of my table, and is referenced in the code by GET_IDNUM(ch). COLUMN_STR("name",GET_NAME(ch)); ^^ I'm adding a string saving value, which is saved in the name column of my table, and is referenced in the code by GET_NAME(ch) *or* ch->player.name. Order does NOT matter (as it did in earlier versions), you may place your COLUMN_x declarations any way you want, however, the amount of column entries SHOULD EQUAL NUM_PLAYERS_MAIN_ROW_ELEMENTS, and as a good rule of thumb you should have every column (even if it isn't loaded) entered into the list (if you read above, ones that aren't loaded are denoted COLUMN_NULL). */ #define NUM_PLAYER_MAIN_ROW_ELEMENTS 83 #define COLUMN_INCR (++i) #define COLUMN_NRM(query,element) \ if (mode==0) { \ if (querystring[COLUMN_INCR]) \ free(querystring[i]); \ querystring[i]=strdup(query); \ structurevarsize[i]=sizeof(element); \ } \ else { \ columnptrs[COLUMN_INCR].ptr=(long *) &element; \ columnptrs[i].type=0; \ } \ #define COLUMN_STR(query,element) \ if (mode==0) { \ if (querystring[COLUMN_INCR]) \ free(querystring[i]); \ querystring[i]=strdup(query); \ structurevarsize[i]=sizeof(element); \ } \ else { \ columnptrs[COLUMN_INCR].ptr=(long *) &element; \ columnptrs[i].type=1; \ } \ #define COLUMN_NULL(query) \ if (mode==0) { \ if (querystring[COLUMN_INCR]) \ free(querystring[i]); \ querystring[i]=strdup(query); \ structurevarsize[i]=0; \ } \ else { \ columnptrs[COLUMN_INCR].ptr=NULL; \ columnptrs[i].type=-1; \ } \ char *querystring[NUM_PLAYER_MAIN_ROW_ELEMENTS]; char structurevarsize[NUM_PLAYER_MAIN_ROW_ELEMENTS]; #define TYPECAST(i, x) (structurevarsize[i] == 1 ? (byte) x : (structurevarsize[i] == 2 ? (short int) x : structurevarsize[i] == 4 ? (long) x : (long) x )) struct { long *ptr; char type; /* 0 for normals, 1 for strings. */ } columnptrs[NUM_PLAYER_MAIN_ROW_ELEMENTS]; char *char_last_host=NULL; void QUERY_DATABASE(MYSQL *db, char *query, int len) { static char tries=0; if (mysql_real_query(db, query, len)) { /* This is BAD! */ if (tries>=2) { log("ERROR: Tried twice, MySQL server appears to be down. Cannot continue, giving up."); exit(0); } tries++; if (tries==1) { log("ERROR: Query to MySQL database failed to process! Resetting connection to server and retrying ..."); log(" Failed query follows:"); query[len]=0; /* Just to make sure we don't buffer overflow. */ log(query); } else log("ERROR: Attempt failed, trying again ..."); mysql_close(SQLdb); /* Whether the DB connection died or something else... reset the connection and try again. */ connect_database(); QUERY_DATABASE(db, query, len); /* Retry. */ } tries=0; } MYSQL_RES *STORE_RESULT (MYSQL *db) { MYSQL_RES *result; if (!(result=mysql_store_result(db))) { /* If this is NULL (and we're SELECTING) we have an error. */ sprintf(buf, "ERROR: Could not store result: %s", mysql_error(db)); log(buf); return NULL; } return result; } MYSQL_ROW FETCH_ROW (MYSQL_RES *result) { MYSQL_ROW row; if (!(row=mysql_fetch_row(result)) && mysql_errno(SQLdb)) { /* Server must of died.. PANIC! */ sprintf(buf, "ERROR: While attempting to fetch row: %s", mysql_error(SQLdb)); log(buf); return NULL; } return row; } void init_querystring (struct char_data *ch, int mode) { int i=-1; /* These can be in any order, just make sure you have the right column name string matching with it's appropriate pointer on the character. */ COLUMN_NRM("idnum",GET_IDNUM(ch)); COLUMN_STR("name",GET_NAME(ch)); COLUMN_STR("description",ch->player.description); COLUMN_STR("title",ch->player.title); COLUMN_NRM("sex",GET_SEX(ch)); COLUMN_NRM("class",GET_CLASS(ch)); COLUMN_NRM("race",GET_RACE(ch)); COLUMN_NRM("deity",GET_DEITY(ch)); COLUMN_NRM("level",GET_LEVEL(ch)); COLUMN_NRM("hometown",ch->player.hometown); COLUMN_NRM("birth",ch->player.time.birth); COLUMN_NRM("played",ch->player.time.played); COLUMN_NRM("weight",ch->player.weight); COLUMN_NRM("height",ch->player.height); COLUMN_STR("pwd",ch->player.passwd); COLUMN_NRM("last_logon", ch->player.time.logon); COLUMN_STR("host", char_last_host); COLUMN_NRM("mana",GET_MANA(ch)); COLUMN_NRM("max_mana",GET_MAX_MANA(ch)); COLUMN_NRM("hit",GET_HIT(ch)); COLUMN_NRM("max_hit",GET_MAX_HIT(ch)); COLUMN_NRM("move",GET_MOVE(ch)); COLUMN_NRM("max_move",GET_MAX_MOVE(ch)); COLUMN_NRM("gold",GET_GOLD(ch)); COLUMN_NRM("bank_gold",GET_BANK_GOLD(ch)); COLUMN_NRM("exp",GET_EXP(ch)); COLUMN_NRM("power",GET_POWER(ch)); COLUMN_NRM("mpower",GET_MPOWER(ch)); COLUMN_NRM("defense",GET_DEFENSE(ch)); COLUMN_NRM("mdefense",GET_MDEFENSE(ch)); COLUMN_NRM("technique",GET_TECHNIQUE(ch)); COLUMN_NRM("str",ch->real_abils.str); COLUMN_NRM("str_add",ch->real_abils.str_add); COLUMN_NRM("intel",ch->real_abils.intel); COLUMN_NRM("wis",ch->real_abils.wis); COLUMN_NRM("dex",ch->real_abils.dex); COLUMN_NRM("con",ch->real_abils.con); COLUMN_NRM("cha",ch->real_abils.cha); COLUMN_NRM("PADDING0",ch->player_specials->saved.PADDING0); COLUMN_NRM("talks1",ch->player_specials->saved.talks[0]); COLUMN_NRM("talks2",ch->player_specials->saved.talks[2]); COLUMN_NRM("talks3",ch->player_specials->saved.talks[3]); COLUMN_NRM("wimp_level",ch->player_specials->saved.wimp_level); COLUMN_NRM("freeze_level",ch->player_specials->saved.freeze_level); COLUMN_NRM("invis_level",ch->player_specials->saved.invis_level); COLUMN_NRM("load_room",ch->player_specials->saved.load_room); COLUMN_NRM("pref",ch->player_specials->saved.pref); COLUMN_NRM("bad_pws",ch->player_specials->saved.bad_pws); COLUMN_NRM("cond1",ch->player_specials->saved.conditions[0]); COLUMN_NRM("cond2",ch->player_specials->saved.conditions[1]); COLUMN_NRM("cond3",ch->player_specials->saved.conditions[2]); COLUMN_NRM("death_timer",ch->player_specials->saved.death_timer); COLUMN_NRM("citizen",ch->player_specials->saved.citizen); COLUMN_NRM("training",ch->player_specials->saved.training); COLUMN_NRM("newbie",ch->player_specials->saved.newbie); COLUMN_NRM("arena",ch->player_specials->saved.arena); COLUMN_NRM("spells_to_learn",ch->player_specials->saved.spells_to_learn); COLUMN_NRM("questpoints",ch->player_specials->saved.questpoints); COLUMN_NRM("nextquest",ch->player_specials->saved.nextquest); COLUMN_NRM("countdown",ch->player_specials->saved.countdown); COLUMN_NRM("questobj",ch->player_specials->saved.questobj); COLUMN_NRM("questmob",ch->player_specials->saved.questmob); COLUMN_NRM("recall_level",ch->player_specials->saved.recall_level); COLUMN_NRM("retreat_level",ch->player_specials->saved.retreat_level); COLUMN_NRM("trust",ch->player_specials->saved.trust); COLUMN_NRM("bail_amt",ch->player_specials->saved.bail_amt); COLUMN_NRM("wins",ch->player_specials->saved.wins); COLUMN_NRM("losses",ch->player_specials->saved.losses); COLUMN_NRM("pref2",ch->player_specials->saved.pref2); COLUMN_NRM("godcmds1",ch->player_specials->saved.godcmds1); COLUMN_NRM("godcmds2",ch->player_specials->saved.godcmds2); COLUMN_NRM("godcmds3",ch->player_specials->saved.godcmds3); COLUMN_NRM("godcmds4",ch->player_specials->saved.godcmds4); COLUMN_NRM("clan",ch->player_specials->saved.clan); COLUMN_NRM("clan_rank",ch->player_specials->saved.clan_rank); COLUMN_NRM("mapx",ch->player_specials->saved.mapx); COLUMN_NRM("mapy",ch->player_specials->saved.mapy); COLUMN_NRM("buildmodezone",ch->player_specials->saved.buildmodezone); COLUMN_NRM("buildmoderoom",ch->player_specials->saved.buildmoderoom); COLUMN_NRM("tloadroom",ch->player_specials->saved.tloadroom); COLUMN_NRM("alignment",ch->char_specials.saved.alignment); COLUMN_NRM("act",ch->char_specials.saved.act); COLUMN_NRM("affected_by",ch->char_specials.saved.affected_by); if (i+1!=NUM_PLAYER_MAIN_ROW_ELEMENTS) { /* Goddamnit, you screwed up fool. */ sprintf(buf, "ERROR: Number of elements in init_querystring (%d) don't match hard-coded number in NUM_PLAYER_MAIN_ROW_ELEMENTS (%d).", i+1, NUM_PLAYER_MAIN_ROW_ELEMENTS); log(buf); exit(0); } } void setzero (void *pointer, int size) { int i; char *ptr=(char *) pointer; if (size <= 0) return; for (i=0; i<size; i++) *(ptr+i) = 0; } int querybuild=0; #define MODE_INSERT 1 #define MODE_UPDATE 2 /* This uses buf1. */ char * mkqueryset (char *query) { sprintf(buf1, ",%s=", query); return buf1; } #define MODE_DELETE 1 #define MODE_STORE 2 #define MODE_RETRIEVE 3 /* Affect/skill loading/storing. */ void dbmodify_player_affects (struct char_data *ch, int mode) { /* mode = 0 for delete, = 1 for store, = 2 for retrieve */ struct affected_type *aff, af; MYSQL_RES *result; MYSQL_ROW row; if (mode != MODE_RETRIEVE) { /* Delete em, unless we're retrieving. */ sprintf(buf, "DELETE from player_affects WHERE idnum=%ld", GET_IDNUM(ch)); QUERY_DATABASE(SQLdb, buf, strlen(buf)); } if (!mode) return; if (mode == MODE_STORE) { /* Do some storage. */ aff=ch->affected; sprintf(buf, "INSERT into player_affects (idnum,type,duration,modifier,location,bitvector) VALUES"); while (aff) { sprintf(buf+strlen(buf), "%s%ld,%ld,%ld,%ld,%d,%ld)", aff==ch->affected ? "(" : ",(", GET_IDNUM(ch), aff->type, aff->duration, aff->modifier, aff->location, aff->bitvector); aff=aff->next; } if (buf[strlen(buf)-1]==')') QUERY_DATABASE(SQLdb, buf, strlen(buf)); } else if (mode == MODE_RETRIEVE) { /* Do some retrieval. */ sprintf(buf, "SELECT type,duration,modifier,location,bitvector from player_affects WHERE idnum=%ld", GET_IDNUM(ch)); QUERY_DATABASE(SQLdb, buf, strlen(buf)); if (!(result=STORE_RESULT(SQLdb))) return; while ((row=FETCH_ROW(result))) { af.type=ATOIROW(0); af.duration=ATOIROW(1); af.modifier=ATOIROW(2); af.location=ATOIROW(3); af.bitvector=ATOIROW(4); affect_to_char (ch, &af); } mysql_free_result(result); } } // This provides storage space for 1024 spells, we currently have 1000. long spellinfo[32]; /* Which spells are active, which are not. */ #define SP_SET_BIT(spellnum) (SET_BIT(spellinfo[(spellnum+1)/32], 1 << MAX_SKILLS%(spellnum+1))) #define SP_IS_SET(spellnum) (IS_SET(spellinfo[(spellnum+1)/32], 1 << MAX_SKILLS%(spellnum+1))) void dbmodify_player_skills (struct char_data *ch, int mode) { /* mode = 0 for delete, = 1 for store, = 2 for retrieve */ int i, skillnum; static int cache=NULL; MYSQL_RES *result; MYSQL_ROW row; extern char *spells[]; int fp=0; if (!cache) { cache=1; for (i=0; i<MAX_SKILLS; i++) if (strcmp(spells[i], "!UNUSED!")) SP_SET_BIT(i); } if (mode != MODE_RETRIEVE) { /* Delete em, unless we're retrieving. */ sprintf(buf, "DELETE from player_skills WHERE idnum=%ld", GET_IDNUM(ch)); QUERY_DATABASE(SQLdb, buf, strlen(buf)); } if (!mode) return; if (mode == MODE_STORE) { /* Do some storage. */ sprintf(buf, "INSERT into player_skills (idnum,skill,learned) VALUES"); for (i=0; i<MAX_SKILLS; i++) if (SP_IS_SET(i) && ch->player_specials->saved.skills[i]>0) { sprintf(buf+strlen(buf), "%s%ld,%d,%d)", !fp ? "(" : ",(", GET_IDNUM(ch), i, ch->player_specials->saved.skills[i]); if (!fp) fp=1; } if (buf[strlen(buf)-1]==')') QUERY_DATABASE(SQLdb, buf, strlen(buf)); } else if (mode == MODE_RETRIEVE) { /* Do some retrieval. */ sprintf(buf, "SELECT idnum,skill,learned from player_skills WHERE idnum=%ld", GET_IDNUM(ch)); QUERY_DATABASE(SQLdb, buf, strlen(buf)); if (!(result=STORE_RESULT(SQLdb))) return; while ((row=FETCH_ROW(result))) if ((skillnum=ATOIROW(1)) >= 0 && skillnum <= MAX_SKILLS) ch->player_specials->saved.skills[skillnum]=ATOIROW(2); mysql_free_result(result); } } int delete_player_entry (int idnum) { int rows; sprintf(buf, "DELETE from player_main WHERE idnum='%d'", idnum); QUERY_DATABASE(SQLdb, buf, strlen(buf)); if ((rows=mysql_affected_rows(SQLdb)) != 1) { if (!rows) { sprintf(buf, "ERROR: Could not find idnum '%d' for DELETE operation.", idnum); log(buf); return 0; } sprintf(buf, "ERROR: While executing DELETE operation on idnum '%d', deleted %d matching rows!", idnum, rows); log(buf); } sprintf(buf, "DELETE from player_affects WHERE idnum='%d'", idnum); QUERY_DATABASE(SQLdb, buf, strlen(buf)); sprintf(buf, "DELETE from player_skills WHERE idnum='%d'", idnum); QUERY_DATABASE(SQLdb, buf, strlen(buf)); return 1; } /* Benchmarking routines used for debugging. struct timeval ts,te; char tmpbuf[100]; #define time_start (gettimeofday(&ts, NULL)) #define time_end \ gettimeofday(&te, NULL); \ sprintf(tmpbuf, "%ld.%6lds\r\n", te.tv_sec-ts.tv_sec, te.tv_usec-ts.tv_usec); \ if (ch->desc) send_to_char(tmpbuf,ch); \ */ /* This uses buf and buf2. */ int insert_player_entry (struct char_data *ch) { int i, fpass=0; int mode=0; MYSQL_RES *result; struct obj_data *char_eq[NUM_WEARS]; char tmpstr[300]; if (!ch || !ch->player_specials) return 0; if (!querybuild) { init_querystring(ch, 0); querybuild=1; } /* Store all affects ... */ dbmodify_player_affects(ch, MODE_STORE); dbmodify_player_skills(ch, MODE_STORE); /* Lets store the skills while we're at it. */ sprintf(buf, "SELECT idnum FROM player_main WHERE name='%s'", GET_NAME(ch)); QUERY_DATABASE(SQLdb, buf, strlen(buf)); if (!(result=STORE_RESULT(SQLdb))) return NULL; /* Remove all EQ affects ... We need to write only bare character data. */ for (i = 0; i < NUM_WEARS; i++) { if (GET_EQ (ch, i)) char_eq[i] = nunequip_char (ch, i); else char_eq[i] = NULL; } /* Remove all other affects from memory ... */ while (ch->affected) affect_remove (ch, ch->affected); /* Update player time data. */ ch->player.time.played+=(long) (time (0) - ch->player.time.logon); ch->player.time.logon = time (0); if (char_last_host) free(char_last_host); if (ch->desc && ch->desc->host) char_last_host=strdup(ch->desc->host); else char_last_host=NULL; if (mysql_num_rows(result)==0) mode=MODE_INSERT; else mode=MODE_UPDATE; mysql_free_result(result); if (mode==MODE_INSERT) { sprintf(buf, "INSERT into player_main "); for (i=0; i<NUM_PLAYER_MAIN_ROW_ELEMENTS; i++) sprintf(buf+strlen(buf), "%c%s", !i ? '(' : ',', querystring[i]); sprintf(buf+strlen(buf), ") VALUES("); } else if (mode==MODE_UPDATE) sprintf(buf, "UPDATE player_main SET "); init_querystring(ch, 1); for (i=0; i<NUM_PLAYER_MAIN_ROW_ELEMENTS; i++) { buf2[0]=0; if (!columnptrs[i].ptr || !TYPECAST(i, *columnptrs[i].ptr) || (columnptrs[i].type==1 && !*(char *)*columnptrs[i].ptr)) sprintf(tmpstr, "%sNULL", mode==MODE_UPDATE ? mkqueryset(querystring[i]) : "," ); else switch (columnptrs[i].type) { case 0: sprintf(tmpstr, "%s%ld", mode==MODE_UPDATE ? mkqueryset(querystring[i]) : ",", TYPECAST(i, *columnptrs[i].ptr)); break; case 1: mysql_escape_string(buf2, (char *)*columnptrs[i].ptr, strlen((char *)*columnptrs[i].ptr)); sprintf(tmpstr, "%s'%s'", mode==MODE_UPDATE ? mkqueryset(querystring[i]) : ",", buf2); break; } /* sprintf(buf1, "'%s' %d", tmpstr, i); log(buf1); */ if (!fpass) { fpass=1; strcat(buf, tmpstr+1); } else strcat(buf, tmpstr); } if (mode==MODE_INSERT) strcat(buf, ")"); else if (mode==MODE_UPDATE) sprintf(buf+strlen(buf), " WHERE name='%s'", GET_NAME(ch)); QUERY_DATABASE(SQLdb, buf, strlen(buf)); dbmodify_player_affects(ch, MODE_RETRIEVE); for (i = 0; i < NUM_WEARS; i++) { if (char_eq[i]) nequip_char (ch, char_eq[i], i); } if (mysql_affected_rows(SQLdb) != 1 && mysql_errno(SQLdb)) { sprintf(buf, "ERROR: Could not INSERT/UPDATE playerfile properly (%d rows affected) for %s: %s", (int) mysql_affected_rows(SQLdb), GET_NAME(ch), mysql_error(SQLdb)); log(buf); return 0; } return 1; } /* Retrieve a player entry into an existing character. */ struct char_data *retrieve_player_entry (char *name, struct char_data *ch) { int i; my_ulonglong rows; MYSQL_RES *result; MYSQL_ROW row; unsigned int fields; unsigned long *lengths; if (ch->player_specials == NULL) CREATE (ch->player_specials, struct player_special_data, 1); if (!querybuild) { init_querystring(ch, 0); querybuild=1; } if (!name || !*name) return NULL; sprintf(buf, "SELECT"); for (i=0; i<NUM_PLAYER_MAIN_ROW_ELEMENTS; i++) sprintf(buf+strlen(buf), "%c%s", !i ? ' ' : ',', querystring[i]); sprintf(buf+strlen(buf), " FROM player_main WHERE name='%s'", name); QUERY_DATABASE(SQLdb, buf, strlen(buf)); if (!(result=STORE_RESULT(SQLdb))) return NULL; if ((rows=mysql_num_rows(result)) > 1 || rows<0) { sprintf(buf, "ERROR: SELECT for %s returned %d rows! Duplicate entries for seemingly VIABLE player!?", name, (int) rows); log(buf); } if (!rows) { mysql_free_result(result); return NULL; } if ((fields=mysql_num_fields(result)) != NUM_PLAYER_MAIN_ROW_ELEMENTS) { /* Someone changed the database and didn't modify the code to match, bitch them out and DO NOT DO ANY CONVERSIONS. */ sprintf(buf, "WARNING: SELECT for %s returned %d columns per row! Code set to process %d columns!", name, fields, NUM_PLAYER_MAIN_ROW_ELEMENTS); log(buf); log( " MODIFY THE CODE IN DB.C AND DO IT CORRECTLY OR YOU WILL CORRUPT THE PLAYERFILE!"); return NULL; } if (!(row=FETCH_ROW(result))) { mysql_free_result(result); return NULL; } lengths=mysql_fetch_lengths(result); init_querystring(ch, 1); for (i=0; i<NUM_PLAYER_MAIN_ROW_ELEMENTS; i++) { if (!lengths[i] && columnptrs[i].type!=-1) { if (columnptrs[i].type==0) setzero(columnptrs[i].ptr, structurevarsize[i]); else { (char *)*columnptrs[i].ptr=(char *) malloc(1); *(char *)*columnptrs[i].ptr=0; } } else { if (!columnptrs[i].type) { *columnptrs[i].ptr=ATOIROW(i); } else if (columnptrs[i].type==1) { if (*columnptrs[i].ptr) free((char *)*columnptrs[i].ptr); (char *)*columnptrs[i].ptr=(char *) malloc(lengths[i]+1); memcpy((char *)*columnptrs[i].ptr, row[i], lengths[i]); ((char *)*columnptrs[i].ptr)[lengths[i]]=0; } else continue; } } memcpy(&ch->aff_abils, &ch->real_abils, sizeof(ch->real_abils)); ch->player.short_descr = NULL; ch->player.long_descr = NULL; ch->player.time.logon = time (0); if (ch->points.max_mana < 100) ch->points.max_mana = 100; POOFIN (ch) = NULL; POOFOUT (ch) = NULL; ch->char_specials.carry_weight = 0; ch->char_specials.carry_items = 0; /* * If you're not poisioned and you've been away for more than an hour of * real time, we'll set your HMV back to full */ if (!IS_AFFECTED (ch, AFF_POISON) && (((long) (time (0) - ch->player.time.logon)) >= SECS_PER_REAL_HOUR)) { GET_HIT (ch) = GET_MAX_HIT (ch); GET_MOVE (ch) = GET_MAX_MOVE (ch); GET_MANA (ch) = GET_MAX_MANA (ch); } mysql_free_result(result); dbmodify_player_affects(ch, MODE_RETRIEVE); dbmodify_player_skills(ch, MODE_RETRIEVE); return ch; } int count_player_entries ( void ) { my_ulonglong rows; MYSQL_RES *result; sprintf(buf, "SELECT idnum FROM player_main"); QUERY_DATABASE(SQLdb, buf, strlen(buf)); if (!(result=STORE_RESULT(SQLdb))) return NULL; rows=mysql_num_rows(result); if (rows<0) { /* Wtf? */ sprintf(buf, "ERROR: SELECT for all players returned %d rows: %s", (int) rows, mysql_error(SQLdb)); log(buf); } mysql_free_result(result); return rows; } /* The first argument is the target name to load. The second argument is the type of each element in the third argument (either n for number or s for string). The third is a comma delimited list of column names you want to retrieve. The fourth argument and on are pointers you want to retrieve them into. eg. pe_printf("Storm", "nnsns", "idnum,level,title,race,description", GET_IDNUM(ch), GET_LEVEL(ch), GET_RACE(ch), GET_DESCRIPTION(ch)); */ void pe_printf (char *name, char *types, char *querystr, ...) { va_list ap; int i=0, len; my_ulonglong rows; MYSQL_RES *result; MYSQL_ROW row; char buffer[32000]; if (!name || !*name) return; sprintf(buffer, "SELECT %s from player_main WHERE name='%s'", querystr, name); QUERY_DATABASE(SQLdb, buffer, strlen(buffer)); if (!(result=STORE_RESULT(SQLdb))) return; if ((rows=mysql_num_rows(result)) > 1 || rows<=0) { sprintf(buf, "ERROR: SELECT for %s returned %d rows! Duplicate or no player entries for seemingly VIABLE player!?", name, (int) rows); log(buf); } if (!(row=FETCH_ROW(result))) return; va_start(ap, querystr); len=strlen(types); while (i<len) { switch(types[i]) { case 'n': *(int *)va_arg(ap, int *)=ATOIROW(i); break; case 's': if (row[i]) strcpy(va_arg(ap, char *), row[i]); else strcpy(va_arg(ap, char *), "\0"); break; } i++; } va_end(ap); mysql_free_result(result); }