@@ -48,7 +48,16 @@ bool SQLiteClient::Connect() {
4848 return false ;
4949 }
5050
51+ // --- New: Set busy timeout to 5 seconds ---
52+ sqlite3_busy_timeout (db_, 5000 );
53+
5154 char * errMsg = nullptr ;
55+ rc = sqlite3_exec (db_, " PRAGMA journal_mode=WAL;" , nullptr , nullptr , &errMsg);
56+ if (rc != SQLITE_OK ) {
57+ Logger::Warn (" Failed to enable WAL mode: {}" , errMsg ? errMsg : " unknown error" );
58+ sqlite3_free (errMsg);
59+ }
60+
5261 rc = sqlite3_exec (db_, " PRAGMA foreign_keys = ON;" , nullptr , nullptr , &errMsg);
5362 if (rc != SQLITE_OK ) {
5463 Logger::Warn (" Failed to enable foreign keys: {}" , errMsg ? errMsg : " unknown error" );
@@ -541,11 +550,73 @@ std::vector<std::string> SQLiteClient::ListGameStates() {
541550
542551// =============== World Data Operations ===============
543552bool SQLiteClient::SaveChunkData (int chunkX, int chunkZ, const nlohmann::json& chunkData) {
553+ std::lock_guard<std::mutex> lock (dbMutex_);
554+ if (!db_) {
555+ Logger::Error (" SaveChunkData: database not connected" );
556+ return false ;
557+ }
558+
544559 std::string sql = sqlProvider_.GetQuery (" save_chunk_data" );
545560 if (sql.empty ()) {
546561 sql = " INSERT OR REPLACE INTO world_chunks (chunk_x, chunk_z, biome, data, last_updated) VALUES (?, ?, ?, ?, datetime('now'));" ;
547562 }
548- return ExecuteWithParams (sql, { std::to_string (chunkX), std::to_string (chunkZ), " 0" , chunkData.dump () });
563+
564+ // Begin immediate transaction
565+ char * errMsg = nullptr ;
566+ int rc = sqlite3_exec (db_, " BEGIN IMMEDIATE;" , nullptr , nullptr , &errMsg);
567+ if (rc != SQLITE_OK ) {
568+ Logger::Error (" Failed to begin transaction: {}" , errMsg ? errMsg : " unknown" );
569+ sqlite3_free (errMsg);
570+ return false ;
571+ }
572+
573+ // Prepare and execute the INSERT statement
574+ sqlite3_stmt* stmt = nullptr ;
575+ rc = sqlite3_prepare_v2 (db_, sql.c_str (), -1 , &stmt, nullptr );
576+ if (rc != SQLITE_OK ) {
577+ Logger::Error (" Failed to prepare chunk save statement: {}" , sqlite3_errmsg (db_));
578+ sqlite3_exec (db_, " ROLLBACK;" , nullptr , nullptr , nullptr );
579+ return false ;
580+ }
581+
582+ std::string chunkXStr = std::to_string (chunkX);
583+ std::string chunkZStr = std::to_string (chunkZ);
584+ std::string dataStr = chunkData.dump ();
585+
586+ sqlite3_bind_text (stmt, 1 , chunkXStr.c_str (), -1 , SQLITE_TRANSIENT );
587+ sqlite3_bind_text (stmt, 2 , chunkZStr.c_str (), -1 , SQLITE_TRANSIENT );
588+ sqlite3_bind_int (stmt, 3 , 0 ); // biome default
589+ sqlite3_bind_text (stmt, 4 , dataStr.c_str (), -1 , SQLITE_TRANSIENT );
590+
591+ rc = sqlite3_step (stmt);
592+ bool success = (rc == SQLITE_DONE );
593+ if (!success) {
594+ Logger::Error (" Failed to execute chunk save: {}" , sqlite3_errmsg (db_));
595+ }
596+
597+ sqlite3_finalize (stmt);
598+
599+ // Commit or rollback
600+ if (success) {
601+ rc = sqlite3_exec (db_, " COMMIT;" , nullptr , nullptr , &errMsg);
602+ if (rc != SQLITE_OK ) {
603+ Logger::Error (" Failed to commit chunk save: {}" , errMsg ? errMsg : " unknown" );
604+ sqlite3_free (errMsg);
605+ success = false ;
606+ }
607+ } else {
608+ sqlite3_exec (db_, " ROLLBACK;" , nullptr , nullptr , nullptr );
609+ }
610+
611+ if (success) {
612+ lastInsertId_ = sqlite3_last_insert_rowid (db_);
613+ affectedRows_ = sqlite3_changes (db_);
614+ stats_.totalQueries ++;
615+ } else {
616+ stats_.failedQueries ++;
617+ }
618+
619+ return success;
549620}
550621
551622nlohmann::json SQLiteClient::LoadChunkData (int chunkX, int chunkZ) {
0 commit comments