diff --git a/src/common/config.h b/src/common/config.h index b4471a4bd..4910f28df 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -23,6 +23,10 @@ #define CONFIG_H #ifdef _MSC_VER +# define VC_EXTRALEAN +# define WIN32_LEAN_AND_MEAN +# include +# undef MOUSE_MOVED # define STDIO_CP 1252 /* log.c, convert to console character set */ # pragma warning (disable: 4201 4214 4514 4115 4711) # pragma warning(disable: 4056) @@ -226,11 +230,7 @@ extern char * strdup(const char *s); #endif #if !defined(MAX_PATH) -# ifdef WIN32 -# define VC_EXTRALEAN -# include -# undef MOUSE_MOVED -# elif defined(PATH_MAX) +# if defined(PATH_MAX) # define MAX_PATH PATH_MAX # else # define MAX_PATH 1024 @@ -242,10 +242,10 @@ extern char * strdup(const char *s); **** ****/ #ifndef NOMINMAX #ifndef MIN -# define MIN(a,b) ((a) < (b) ? (a) : (b)) +# define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX -# define MAX(a,b) ((a) > (b) ? (a) : (b)) +# define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif #endif @@ -260,9 +260,7 @@ extern char * strdup(const char *s); /**** **** ** The Eressea boolean type ** **** ****/ -#if defined(WIN32) - typedef unsigned char boolean; -#elif defined(BOOLEAN) +#if defined(BOOLEAN) # define boolean BOOLEAN #else typedef int boolean; /* not bool! wrong size. */ diff --git a/src/common/kernel/eressea.c b/src/common/kernel/eressea.c index 77ace202e..fc23330fd 100644 --- a/src/common/kernel/eressea.c +++ b/src/common/kernel/eressea.c @@ -2293,18 +2293,9 @@ strdup(const char *s) #endif void -remove_empty_factions(boolean writedropouts) +remove_empty_factions(void) { faction **fp, *f3; - FILE *dofp = NULL; - char zText[MAX_PATH]; - sprintf(zText, "%s/dropouts.%d", basepath(), turn); - - if (writedropouts) dofp = fopen(zText, "w"); - if (dofp) { - const unsigned char utf8_bom[4] = { 0xef, 0xbb, 0xbf }; - fwrite(utf8_bom, 1, 3, dofp); - } for (fp = &factions; *fp;) { faction * f = *fp; @@ -2318,11 +2309,6 @@ remove_empty_factions(boolean writedropouts) /* Einfach in eine Datei schreiben und später vermailen */ - if (dofp) { - fprintf(dofp, "%3d %s %s %s\n", - f->age, LOC(default_locale, rc_name(f->race, 0)), f->email, - factionname(f)); - } if (updatelog) fprintf(updatelog, "dropout %s\n", itoa36(f->no)); for (f3 = factions; f3; f3 = f3->next) { @@ -2361,8 +2347,6 @@ remove_empty_factions(boolean writedropouts) } else fp = &(*fp)->next; } - - if (dofp) fclose(dofp); } void diff --git a/src/common/kernel/eressea.h b/src/common/kernel/eressea.h index 74820b9a6..f23cf817f 100644 --- a/src/common/kernel/eressea.h +++ b/src/common/kernel/eressea.h @@ -130,7 +130,7 @@ typedef struct ally { void remove_empty_units_in_region(struct region *r); void remove_empty_units(void); -void remove_empty_factions(boolean writedropouts); +void remove_empty_factions(void); typedef struct strlist { struct strlist *next; diff --git a/src/common/kernel/save.c b/src/common/kernel/save.c index 9efc090e1..384b55a4b 100644 --- a/src/common/kernel/save.c +++ b/src/common/kernel/save.c @@ -1598,7 +1598,7 @@ readgame(const char * filename, int mode, int backup) } } if (loadplane || maxregions>=0) { - remove_empty_factions(false); + remove_empty_factions(); } log_info((1, "Done loading turn %d.\n", turn)); return 0; diff --git a/src/common/kernel/sqlite.c b/src/common/kernel/sqlite.c index 1c071b6d4..6e6dd50a5 100644 --- a/src/common/kernel/sqlite.c +++ b/src/common/kernel/sqlite.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -19,133 +20,212 @@ faction * get_faction_by_id(int uid) } #define SQL_EXPECT(res, val) if (res!=val) { return val; } +#define MAX_EMAIL_LENGTH 64 +#define MD5_LENGTH 32 +#define MD5_LENGTH_0 (MD5_LENGTH+1) /* MD5 + zero-terminator */ +#define MAX_FACTION_NAME 64 +#define MAX_FACTION_NAME_0 (MAX_FACTION_NAME+1) +typedef struct stmt_cache { + sqlite3 * db; + sqlite3_stmt * stmt; + const char * sql; + int inuse; +} stmt_cache; + +#define MAX_STMT_CACHE 64 +static stmt_cache cache[MAX_STMT_CACHE]; +static int cache_insert; + +static sqlite3_stmt * +stmt_cache_get(sqlite3 * db, const char * sql) +{ + int i, res; + + for (i=0;i!=MAX_STMT_CACHE && cache[i].db;++i) { + if (cache[i].sql==sql && cache[i].db==db) { + cache[i].inuse = 1; + res = sqlite3_reset(cache[i].stmt); + return cache[i].stmt; + } + } + if (i==MAX_STMT_CACHE) { + while (cache[cache_insert].inuse) { + cache[cache_insert].inuse = 0; + cache_insert = (cache_insert+1)&(MAX_STMT_CACHE-1); + } + i = cache_insert; + res = sqlite3_finalize(cache[i].stmt); + SQL_EXPECT(res, SQLITE_OK); + } + cache[i].inuse = 1; + cache[i].db = db; + cache[i].sql = sql; + res = sqlite3_prepare_v2(db, sql, -1, &cache[i].stmt, NULL); + SQL_EXPECT(res, SQLITE_OK); + return cache[i].stmt; +} + + +typedef struct db_faction { + sqlite3_uint64 id_faction; + sqlite3_uint64 id_email; + int no; + const char * email; + const char * passwd_md5; + const char * name; +} db_faction; + +static int +db_update_email(sqlite3 * db, const faction * f, const db_faction * dbstate, boolean force, /* [OUT] */ sqlite3_uint64 * id_email) +{ + boolean update = force; + int res = SQLITE_OK; + char email_lc[MAX_EMAIL_LENGTH]; + + if (update) { + unicode_utf8_tolower(email_lc, sizeof(email_lc), f->email); + } else { + if (strcmp(dbstate->email, f->email)!=0) { + unicode_utf8_tolower(email_lc, sizeof(email_lc), f->email); + if (strcmp(dbstate->email, email_lc)!=0) { + update = true; + } + } + } + + if (update) { + char email_md5[MD5_LENGTH_0]; + int i; + md5_state_t ms; + md5_byte_t digest[16]; + const char sql_insert_email[] = + "INSERT OR FAIL INTO email (email,md5) VALUES (?,?)"; + const char sql_select_email[] = + "SELECT id FROM email WHERE md5=?"; + sqlite3_stmt *stmt_insert_email = stmt_cache_get(db, sql_insert_email); + sqlite3_stmt *stmt_select_email = stmt_cache_get(db, sql_select_email); + + md5_init(&ms); + md5_append(&ms, (md5_byte_t*)email_lc, (int)strlen(email_lc)); + md5_finish(&ms, digest); + for (i=0;i!=16;++i) sprintf(email_md5+2*i, "%.02x", digest[i]); + + res = sqlite3_bind_text(stmt_insert_email, 1, email_lc, -1, SQLITE_TRANSIENT); + res = sqlite3_bind_text(stmt_insert_email, 2, email_md5, MD5_LENGTH, SQLITE_TRANSIENT); + res = sqlite3_step(stmt_insert_email); + + if (res==SQLITE_DONE) { + *id_email = sqlite3_last_insert_rowid(db); + } else { + res = sqlite3_bind_text(stmt_select_email, 1, email_md5, MD5_LENGTH, SQLITE_TRANSIENT); + res = sqlite3_step(stmt_select_email); + SQL_EXPECT(res, SQLITE_ROW); + *id_email = sqlite3_column_int64(stmt_select_email, 0); + } + } + + return SQLITE_OK; +} int -db_update_factions(sqlite3 * db) +db_update_factions(sqlite3 * db, boolean force) { int game_id = 6; -// const char * sql = "INSERT OR REPLACE INTO email (md5, email) VALUES(?, ?)"; const char sql_select[] = - "SELECT faction.id, faction.email_id, faction.code, email.email FROM email, faction" - " WHERE email.id=faction.email_id AND faction.game_id=?"; - const char sql_insert_email[] = - "INSERT OR FAIL INTO email (email,md5) VALUES (?,?)"; - const char sql_select_email[] = - "SELECT id FROM email WHERE md5=?"; - const char sql_update_faction[] = - "UPDATE faction SET email_id=?, lastturn=?, code=?, name=? WHERE id=?"; - + "SELECT faction.id, faction.email_id, faction.code, email.email, faction.password_md5, faction.name, faction.lastturn FROM email, faction" + " WHERE email.id=faction.email_id AND faction.game_id=? AND (lastturn IS NULL OR lastturn>?)"; + sqlite3_stmt *stmt_select = stmt_cache_get(db, sql_select); faction * f; - sqlite3_stmt *stmt_insert_email; - sqlite3_stmt *stmt_select_email; - sqlite3_stmt *stmt_update_faction; - sqlite3_stmt *stmt_select; int res; - res = sqlite3_prepare_v2(db, sql_select_email, (int)sizeof(sql_select_email), &stmt_select_email, NULL); - if (res!=SQLITE_OK) return res; - res = sqlite3_prepare_v2(db, sql_insert_email, (int)sizeof(sql_insert_email), &stmt_insert_email, NULL); - if (res!=SQLITE_OK) return res; - res = sqlite3_prepare_v2(db, sql_select, (int)sizeof(sql_select), &stmt_select, NULL); - if (res!=SQLITE_OK) return res; - res = sqlite3_prepare_v2(db, sql_update_faction, sizeof(sql_update_faction), &stmt_update_faction, NULL); - if (res!=SQLITE_OK) return res; - res = sqlite3_bind_int(stmt_select, 1, game_id); + SQL_EXPECT(res, SQLITE_OK); + res = sqlite3_bind_int(stmt_select, 2, turn-2); + SQL_EXPECT(res, SQLITE_OK); for (;;) { - sqlite3_uint64 idfaction, idemail; - char email[64]; - int length, no; + sqlite3_uint64 id_faction; + int lastturn; res = sqlite3_step(stmt_select); if (res!=SQLITE_ROW) break; - idfaction = sqlite3_column_int64(stmt_select, 0); - idemail = sqlite3_column_int64(stmt_select, 1); - no = sqlite3_column_int(stmt_select, 2); - length = sqlite3_column_bytes(stmt_select, 3); - assert(lengthno); - if (strcmp(f->email, email)!=0) { - char lower[64]; - unicode_utf8_tolower(lower, sizeof(lower), f->email); - if (strcmp(email, lower)!=0) { - char md5hash[33]; - md5_state_t ms; - md5_byte_t digest[16]; - int i; - md5_init(&ms); - md5_append(&ms, (md5_byte_t*)lower, (int)strlen(lower)); - md5_finish(&ms, digest); - for (i=0;i!=16;++i) sprintf(md5hash+2*i, "%.02x", digest[i]); + id_faction = sqlite3_column_int64(stmt_select, 0); + lastturn = sqlite3_column_int(stmt_select, 6); + f = get_faction_by_id((int)id_faction); - idemail = 0; - res = sqlite3_bind_text(stmt_insert_email, 1, lower, -1, SQLITE_TRANSIENT); - res = sqlite3_bind_text(stmt_insert_email, 2, md5hash, -1, SQLITE_TRANSIENT); - res = sqlite3_step(stmt_insert_email); - if (res==SQLITE_DONE) { - idemail = sqlite3_last_insert_rowid(db); - } else { - res = sqlite3_bind_text(stmt_select_email, 1, md5hash, -1, SQLITE_TRANSIENT); - res = sqlite3_step(stmt_select_email); - if (res==SQLITE_ROW) { - idemail = sqlite3_column_int64(stmt_select_email, 0); - } - res = sqlite3_reset(stmt_select_email); - SQL_EXPECT(res, SQLITE_OK); - } - res = sqlite3_reset(stmt_insert_email); - } + if (f==NULL || !f->alive) { + if (lastturn==0) { + const char sql_update[] = "UPDATE faction SET lastturn=? WHERE id=?"; + sqlite3_stmt *stmt = stmt_cache_get(db, sql_update); + + lastturn = f?f->lastorders:turn-1; + sqlite3_bind_int(stmt, 1, lastturn); + sqlite3_bind_int64(stmt, 2, id_faction); + res = sqlite3_step(stmt); + SQL_EXPECT(res, SQLITE_DONE); } - res = sqlite3_bind_int64(stmt_update_faction, 1, idemail); - SQL_EXPECT(res, SQLITE_OK); - res = sqlite3_bind_int(stmt_update_faction, 2, f->lastorders); - SQL_EXPECT(res, SQLITE_OK); - res = sqlite3_bind_text(stmt_update_faction, 3, code, -1, SQLITE_TRANSIENT); - SQL_EXPECT(res, SQLITE_OK); - res = sqlite3_bind_text(stmt_update_faction, 4, f->name, -1, SQLITE_TRANSIENT); - SQL_EXPECT(res, SQLITE_OK); - res = sqlite3_bind_int64(stmt_update_faction, 5, idfaction); - SQL_EXPECT(res, SQLITE_OK); - res = sqlite3_step(stmt_update_faction); - SQL_EXPECT(res, SQLITE_DONE); - res = sqlite3_reset(stmt_update_faction); + } else { + md5_state_t ms; + md5_byte_t digest[16]; + int i; + char passwd_md5[MD5_LENGTH_0]; + sqlite3_uint64 id_email; + boolean update = force; + db_faction dbstate; + const char * no_b36; + + fset(f, FFL_MARK); + dbstate.id_faction = id_faction; + dbstate.id_email = sqlite3_column_int64(stmt_select, 1); + no_b36 = (const char *)sqlite3_column_text(stmt_select, 2); + dbstate.no = no_b36?atoi36(no_b36):-1; + dbstate.email = (const char *)sqlite3_column_text(stmt_select, 3); + dbstate.passwd_md5 = (const char *)sqlite3_column_text(stmt_select, 4); + dbstate.name = (const char *)sqlite3_column_text(stmt_select, 5); + + id_email = dbstate.id_email; + res = db_update_email(db, f, &dbstate, force, &id_email); SQL_EXPECT(res, SQLITE_OK); + + md5_init(&ms); + md5_append(&ms, (md5_byte_t*)f->passw, (int)strlen(f->passw)); + md5_finish(&ms, digest); + for (i=0;i!=16;++i) sprintf(passwd_md5+2*i, "%.02x", digest[i]); + + if (!update) { + update = ((id_email!=0 && dbstate.id_email!=id_email) || dbstate.no!=f->no + || dbstate.passwd_md5==NULL || strcmp(passwd_md5, dbstate.passwd_md5)!=0 + || dbstate.name==NULL || strncmp(f->name, dbstate.name, MAX_FACTION_NAME)!=0); + } + if (update) { + const char sql_update_faction[] = + "UPDATE faction SET email_id=?, password_md5=?, code=?, name=?, firstturn=? WHERE id=?"; + sqlite3_stmt *stmt_update_faction = stmt_cache_get(db, sql_update_faction); + + res = sqlite3_bind_int64(stmt_update_faction, 1, id_email); + SQL_EXPECT(res, SQLITE_OK); + res = sqlite3_bind_text(stmt_update_faction, 2, passwd_md5, MD5_LENGTH, SQLITE_TRANSIENT); + SQL_EXPECT(res, SQLITE_OK); + res = sqlite3_bind_text(stmt_update_faction, 3, no_b36, -1, SQLITE_TRANSIENT); + SQL_EXPECT(res, SQLITE_OK); + res = sqlite3_bind_text(stmt_update_faction, 4, f->name, -1, SQLITE_TRANSIENT); + SQL_EXPECT(res, SQLITE_OK); + res = sqlite3_bind_int(stmt_update_faction, 5, turn-f->age); + SQL_EXPECT(res, SQLITE_OK); + res = sqlite3_bind_int64(stmt_update_faction, 6, f->subscription); + SQL_EXPECT(res, SQLITE_OK); + res = sqlite3_step(stmt_update_faction); + SQL_EXPECT(res, SQLITE_DONE); + } } } - sqlite3_finalize(stmt_select); -/* + for (f=factions;f;f=f->next) { - char email[64]; - char hash[33]; - md5_state_t ms; - md5_byte_t digest[16]; - int i; - sqlite3_int64 rowid; - - unicode_utf8_tolower(email, sizeof(email), f->email); - - md5_init(&ms); - md5_append(&ms, (md5_byte_t*)email, strlen(email)); - md5_finish(&ms, digest); - for (i=0;i!=16;++i) sprintf(hash+2*i, "%.02x", digest[i]); - - res = sqlite3_bind_text(stmt, 1, hash, -1, SQLITE_TRANSIENT); - res = sqlite3_bind_text(stmt, 2, email, -1, SQLITE_TRANSIENT); - do { - res = sqlite3_step(stmt); - } while (res!=SQLITE_DONE); - rowid = sqlite3_last_insert_rowid(db); - // TODO: use rowid to update faction - sqlite3_reset(stmt); + if (!fval(f, FFL_MARK)) { + log_error(("%s (sub=%d, email=%s) has no entry in the database\n", factionname(f), f->subscription, f->email)); + } else { + freset(f, FFL_MARK); + } } - sqlite3_finalize(stmt); - */ return SQLITE_OK; } diff --git a/src/eressea/eressea-lua.vcproj b/src/eressea/eressea-lua.vcproj index 472608b8f..3fdf58b08 100644 --- a/src/eressea/eressea-lua.vcproj +++ b/src/eressea/eressea-lua.vcproj @@ -41,7 +41,7 @@ Name="VCCLCompilerTool" AdditionalOptions="/MP" Optimization="0" - AdditionalIncludeDirectories="..;../common" + AdditionalIncludeDirectories="..;../common;../external" PreprocessorDefinitions="WIN32;_CRT_SECURE_NO_DEPRECATE" MinimalRebuild="true" BasicRuntimeChecks="3" diff --git a/src/eressea/tolua/bind_sqlite.c b/src/eressea/tolua/bind_sqlite.c index 3acf5f94b..65c15cbce 100644 --- a/src/eressea/tolua/bind_sqlite.c +++ b/src/eressea/tolua/bind_sqlite.c @@ -21,12 +21,12 @@ without prior permission by the authors of Eressea. #define LTYPE_DB TOLUA_CAST "db" -extern int db_update_factions(sqlite3 * db); +extern int db_update_factions(sqlite3 * db, boolean force); static int tolua_db_update_factions(lua_State* L) { sqlite3 * db = (sqlite3 *)tolua_tousertype(L, 1, 0); - db_update_factions(db); + db_update_factions(db, tolua_toboolean(L, 2, 0)); return 0; } diff --git a/src/eressea/tolua/bindings.c b/src/eressea/tolua/bindings.c index e143cd27d..6897dbd4d 100644 --- a/src/eressea/tolua/bindings.c +++ b/src/eressea/tolua/bindings.c @@ -623,7 +623,7 @@ tolua_write_game(lua_State* L) int result, m = IO_BINARY; if (mode && strcmp(mode, "text")==0) m = IO_TEXT; - remove_empty_factions(true); + remove_empty_factions(); result = writegame(filename, m); tolua_pushnumber(L, (lua_Number)result);