diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 3da32c03c..924d3770b 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -60,41 +60,32 @@ TaskManager::~TaskManager() { } bool TaskManager::LoadTaskSets() { - const char *TaskSetQuery = "SELECT `id`, `taskid` from `tasksets` WHERE `id` > 0 AND `id` < %i " - "AND `taskid` >= 0 AND `taskid` < %i ORDER BY `id`, `taskid` ASC"; - const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTaskSets: %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; // Clear all task sets in memory. Done so we can reload them on the fly if required by just calling // this method again. - for(int i=0; iwrite(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT `id`, `taskid` from `tasksets` " + "WHERE `id` > 0 AND `id` < %i " + "AND `taskid` >= 0 AND `taskid` < %i " + "ORDER BY `id`, `taskid` ASC", + MAXTASKSETS, MAXTASKS); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadTaskSets: %s", results.ErrorMessage().c_str()); return false; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + int taskSet = atoi(row[0]); + int taskID = atoi(row[1]); + + TaskSets[taskSet].push_back(taskID); + _log(TASKS__GLOBALLOAD, "Adding TaskID %4i to TaskSet %4i", taskID, taskSet); + } return true; - } bool TaskManager::LoadSingleTask(int TaskID) { @@ -126,203 +117,177 @@ void TaskManager::ReloadGoalLists() { _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadLists failed"); } -bool TaskManager::LoadTasks(int SingleTask) { +bool TaskManager::LoadTasks(int singleTask) { // If TaskID !=0, then just load the task specified. - - const char *AllTaskQuery = "SELECT `id`, `duration`, `title`, `description`, `reward`, `rewardid`," - "`cashreward`, `xpreward`, `rewardmethod`, `startzone`, `minlevel`, `maxlevel`, `repeatable` " - "from `tasks` WHERE `id` < %i"; - - const char *SingleTaskQuery = "SELECT `id`, `duration`, `title`, `description`, `reward`, `rewardid`," - "`cashreward`, `xpreward`, `rewardmethod`, `startzone`, `minlevel`, `maxlevel`, `repeatable` " - "from `tasks` WHERE `id` = %i"; - - const char *AllActivityQuery = "SELECT `taskid`, `step`, `activityid`, `activitytype`, `text1`, `text2`," - "`text3`, `goalid`, `goalmethod`, `goalcount`, `delivertonpc`, " - "`zoneid`, `optional` from `activities` WHERE " - "`taskid` < %i AND `activityid` < %i ORDER BY taskid, activityid ASC"; - - const char *SingleTaskActivityQuery = "SELECT `taskid`, `step`, `activityid`, `activitytype`, `text1`, `text2`," - "`text3`, `goalid`, `goalmethod`, `goalcount`, `delivertonpc`, " - "`zoneid`, `optional` from `activities` WHERE " - "`taskid` = %i AND `activityid` < %i ORDER BY taskid, activityid ASC"; - - const char *ERR_TASK_OOR = "[TASKS]Task ID %i out of range while loading tasks from database"; - - const char *ERR_TASK_OR_ACTIVITY_OOR = "[TASKS]Task or Activity ID (%i, %i) out of range while loading" - "activities from database"; - - const char *ERR_NOTASK = "[TASKS]Activity for non-existent task (%i, %i) while loading activities from database"; - - const char *ERR_SEQERR = "[TASKS]Activities for Task %i are not sequential starting at 0. Not loading task."; - - const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTasks: %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - int QueryLength = 0; - MYSQL_RES *result; - MYSQL_ROW row; - _log(TASKS__GLOBALLOAD, "TaskManager::LoadTasks Called"); - if(SingleTask == 0) { + std::string query; + if(singleTask == 0) { if(!GoalListManager.LoadLists()) _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadLists failed"); if(!LoadTaskSets()) _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadTaskSets failed"); - QueryLength = MakeAnyLenString(&query,AllTaskQuery,MAXTASKS); + query = StringFormat("SELECT `id`, `duration`, `title`, `description`, `reward`, " + "`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, " + "`startzone`, `minlevel`, `maxlevel`, `repeatable` " + "FROM `tasks` WHERE `id` < %i", MAXTASKS); } else - QueryLength = MakeAnyLenString(&query,SingleTaskQuery,SingleTask); + query = StringFormat("SELECT `id`, `duration`, `title`, `description`, `reward`, " + "`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, " + "`startzone`, `minlevel`, `maxlevel`, `repeatable` " + "FROM `tasks` WHERE `id` = %i",singleTask); - if(database.RunQuery(query,QueryLength,errbuf,&result)) { + const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTasks: %s"; - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - if((TaskID <= 0) || (TaskID >= MAXTASKS)) { - // This shouldn't happen, as the SELECT is bounded by MAXTASKS - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR, TaskID); - continue; - } - Tasks[TaskID] = new TaskInformation; - Tasks[TaskID]->Duration = atoi(row[1]); - Tasks[TaskID]->Title = new char[strlen(row[2]) + 1]; - strcpy(Tasks[TaskID]->Title, row[2]); - Tasks[TaskID]->Description = new char[strlen(row[3]) + 1]; - strcpy(Tasks[TaskID]->Description, row[3]); - Tasks[TaskID]->Reward = new char[strlen(row[4]) + 1]; - strcpy(Tasks[TaskID]->Reward, row[4]); - Tasks[TaskID]->RewardID = atoi(row[5]); - Tasks[TaskID]->CashReward = atoi(row[6]); - Tasks[TaskID]->XPReward = atoi(row[7]); - Tasks[TaskID]->RewardMethod = (TaskMethodType)atoi(row[8]); - Tasks[TaskID]->StartZone = atoi(row[9]); - Tasks[TaskID]->MinLevel = atoi(row[10]); - Tasks[TaskID]->MaxLevel = atoi(row[11]); - Tasks[TaskID]->Repeatable = atoi(row[12]); - Tasks[TaskID]->ActivityCount = 0; - Tasks[TaskID]->SequenceMode = ActivitiesSequential; - Tasks[TaskID]->LastStep = 0; - - _log(TASKS__GLOBALLOAD,"TaskID: %5i, Duration: %8i, StartZone: %3i Reward: %s MinLevel %i MaxLevel %i Repeatable: %s", - TaskID, Tasks[TaskID]->Duration, Tasks[TaskID]->StartZone, Tasks[TaskID]->Reward, - Tasks[TaskID]->MinLevel, Tasks[TaskID]->MaxLevel, - Tasks[TaskID]->Repeatable ? "Yes" : "No"); - _log(TASKS__GLOBALLOAD,"Title: %s ", Tasks[TaskID]->Title); - //_log(TASKS__GLOBALLOAD,"Description: %s ", Tasks[TaskID]->Description); - - } - mysql_free_result(result); - safe_delete_array(query); - - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - safe_delete_array(query); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); return false; - } + } - if(SingleTask==0) - QueryLength = MakeAnyLenString(&query,AllActivityQuery,MAXTASKS, MAXACTIVITIESPERTASK); + for(auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + + if((taskID <= 0) || (taskID >= MAXTASKS)) { + // This shouldn't happen, as the SELECT is bounded by MAXTASKS + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading tasks from database", taskID); + continue; + } + + Tasks[taskID] = new TaskInformation; + Tasks[taskID]->Duration = atoi(row[1]); + Tasks[taskID]->Title = new char[strlen(row[2]) + 1]; + strcpy(Tasks[taskID]->Title, row[2]); + Tasks[taskID]->Description = new char[strlen(row[3]) + 1]; + strcpy(Tasks[taskID]->Description, row[3]); + Tasks[taskID]->Reward = new char[strlen(row[4]) + 1]; + strcpy(Tasks[taskID]->Reward, row[4]); + Tasks[taskID]->RewardID = atoi(row[5]); + Tasks[taskID]->CashReward = atoi(row[6]); + Tasks[taskID]->XPReward = atoi(row[7]); + Tasks[taskID]->RewardMethod = (TaskMethodType)atoi(row[8]); + Tasks[taskID]->StartZone = atoi(row[9]); + Tasks[taskID]->MinLevel = atoi(row[10]); + Tasks[taskID]->MaxLevel = atoi(row[11]); + Tasks[taskID]->Repeatable = atoi(row[12]); + Tasks[taskID]->ActivityCount = 0; + Tasks[taskID]->SequenceMode = ActivitiesSequential; + Tasks[taskID]->LastStep = 0; + + _log(TASKS__GLOBALLOAD,"TaskID: %5i, Duration: %8i, StartZone: %3i Reward: %s MinLevel %i MaxLevel %i Repeatable: %s", + taskID, Tasks[taskID]->Duration, Tasks[taskID]->StartZone, Tasks[taskID]->Reward, + Tasks[taskID]->MinLevel, Tasks[taskID]->MaxLevel, + Tasks[taskID]->Repeatable ? "Yes" : "No"); + _log(TASKS__GLOBALLOAD,"Title: %s ", Tasks[taskID]->Title); + } + + + if(singleTask==0) + query = StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, " + "`text1`, `text2`, `text3`, `goalid`, `goalmethod`, " + "`goalcount`, `delivertonpc`, `zoneid`, `optional` " + "FROM `activities` " + "WHERE `taskid` < %i AND `activityid` < %i " + "ORDER BY taskid, activityid ASC", MAXTASKS, MAXACTIVITIESPERTASK); else - QueryLength = MakeAnyLenString(&query,SingleTaskActivityQuery, SingleTask, MAXACTIVITIESPERTASK); - - if(database.RunQuery(query,QueryLength, errbuf, &result)) { - - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - int Step = atoi(row[1]); - - int ActivityID = atoi(row[2]); - - if((TaskID <= 0) || (TaskID >= MAXTASKS) || (ActivityID < 0) || (ActivityID >= MAXACTIVITIESPERTASK)) { - // This shouldn't happen, as the SELECT is bounded by MAXTASKS - LogFile->write(EQEMuLog::Error, ERR_TASK_OR_ACTIVITY_OOR, TaskID, ActivityID); - continue; - } - if(Tasks[TaskID]==nullptr) { - LogFile->write(EQEMuLog::Error, ERR_NOTASK, TaskID, ActivityID); - continue; - } - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].StepNumber = Step; - - if(Step != 0) - Tasks[TaskID]->SequenceMode = ActivitiesStepped; - - if(Step >Tasks[TaskID]->LastStep) Tasks[TaskID]->LastStep = Step; - - // Task Activities MUST be numbered sequentially from 0. If not, log an error - // and set the task to nullptr. Subsequent activities for this task will raise - // ERR_NOTASK errors. - // Change to (ActivityID != (Tasks[TaskID]->ActivityCount + 1)) to index from 1 - if(ActivityID != Tasks[TaskID]->ActivityCount) { - LogFile->write(EQEMuLog::Error, ERR_SEQERR, TaskID, ActivityID); - Tasks[TaskID] = nullptr; - continue; - } - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Type = atoi(row[3]); - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1 = new char[strlen(row[4]) + 1]; - - if(strlen(row[4])>0) - strcpy(Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1, row[4]); - else - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1[0]=0; - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2 = new char[strlen(row[5]) + 1]; - - if(strlen(row[5])>0) - strcpy(Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2, row[5]); - else - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2[0]=0; - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3 = new char[strlen(row[6]) + 1]; - - if(strlen(row[6])>0) - strcpy(Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3, row[6]); - else - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3[0]=0; - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalID = atoi(row[7]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalMethod = (TaskMethodType)atoi(row[8]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalCount = atoi(row[9]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].DeliverToNPC = atoi(row[10]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].ZoneID = atoi(row[11]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Optional = atoi(row[12]); - - _log(TASKS__GLOBALLOAD, "Activity Slot %2i: ID %i for Task %5i. Type: %3i, GoalID: %8i, " - "GoalMethod: %i, GoalCount: %3i, ZoneID:%3i", - Tasks[TaskID]->ActivityCount, ActivityID, TaskID, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Type, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalID, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalMethod, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalCount, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].ZoneID); - - _log(TASKS__GLOBALLOAD, " Text1: %s", - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1); - _log(TASKS__GLOBALLOAD, " Text2: %s", - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2); - _log(TASKS__GLOBALLOAD, " Text3: %s", - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3); - - Tasks[TaskID]->ActivityCount++; - - } - mysql_free_result(result); - safe_delete_array(query); - - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - safe_delete_array(query); + query = StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, " + "`text1`, `text2`, `text3`, `goalid`, `goalmethod`, " + "`goalcount`, `delivertonpc`, `zoneid`, `optional` " + "FROM `activities` " + "WHERE `taskid` = %i AND `activityid` < %i " + "ORDER BY taskid, activityid ASC", singleTask, MAXACTIVITIESPERTASK); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); return false; + } + + for(auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + int step = atoi(row[1]); + + int activityID = atoi(row[2]); + + if((taskID <= 0) || (taskID >= MAXTASKS) || (activityID < 0) || (activityID >= MAXACTIVITIESPERTASK)) { + // This shouldn't happen, as the SELECT is bounded by MAXTASKS + LogFile->write(EQEMuLog::Error, "[TASKS]Task or Activity ID (%i, %i) out of range while loading " + "activities from database", taskID, activityID); + continue; + } + + if(Tasks[taskID]==nullptr) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity for non-existent task (%i, %i) while loading activities from database", taskID, activityID); + continue; + } + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].StepNumber = step; + + if(step != 0) + Tasks[taskID]->SequenceMode = ActivitiesStepped; + + if(step >Tasks[taskID]->LastStep) + Tasks[taskID]->LastStep = step; + + // Task Activities MUST be numbered sequentially from 0. If not, log an error + // and set the task to nullptr. Subsequent activities for this task will raise + // ERR_NOTASK errors. + // Change to (activityID != (Tasks[taskID]->ActivityCount + 1)) to index from 1 + if(activityID != Tasks[taskID]->ActivityCount) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activities for Task %i are not sequential starting at 0. Not loading task.", taskID, activityID); + Tasks[taskID] = nullptr; + continue; + } + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Type = atoi(row[3]); + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1 = new char[strlen(row[4]) + 1]; + + if(strlen(row[4])>0) + strcpy(Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1, row[4]); + else + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1[0]=0; + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2 = new char[strlen(row[5]) + 1]; + + if(strlen(row[5])>0) + strcpy(Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2, row[5]); + else + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2[0]=0; + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3 = new char[strlen(row[6]) + 1]; + + if(strlen(row[6])>0) + strcpy(Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3, row[6]); + else + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3[0]=0; + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID = atoi(row[7]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod = (TaskMethodType)atoi(row[8]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount = atoi(row[9]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].DeliverToNPC = atoi(row[10]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID = atoi(row[11]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Optional = atoi(row[12]); + + _log(TASKS__GLOBALLOAD, "Activity Slot %2i: ID %i for Task %5i. Type: %3i, GoalID: %8i, " + "GoalMethod: %i, GoalCount: %3i, ZoneID:%3i", + Tasks[taskID]->ActivityCount, activityID, taskID, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Type, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID); + + _log(TASKS__GLOBALLOAD, " Text1: %s", Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1); + _log(TASKS__GLOBALLOAD, " Text2: %s", Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2); + _log(TASKS__GLOBALLOAD, " Text3: %s", Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3); + + Tasks[taskID]->ActivityCount++; } + return true; } @@ -333,146 +298,127 @@ bool TaskManager::SaveClientState(Client *c, ClientTaskState *state) { // in that slot had more activities than the one now occupying it. Hopefully retaining the slot number for the // duration of a session will overcome this. // - const char *TaskQuery="REPLACE INTO character_tasks (charid, taskid, slot, acceptedtime) " - "VALUES (%i, %i, %i, %i)"; - - const char *ActivityQuery="REPLACE INTO character_activities (charid, taskid, activityid, donecount, completed) " - "VALUES "; - - const char *CompletedTaskQuery="REPLACE INTO completed_tasks (charid, completedtime, taskid, activityid) " - "VALUES (%i, %i, %i, %i)"; + if(!c || !state) + return false; const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::SaveClientState %s"; - if(!c || !state) return false; + int characterID = c->CharacterID(); - int CharacterID = c->CharacterID(); - - _log(TASKS__CLIENTSAVE,"TaskManager::SaveClientState for character ID %d", CharacterID); - - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; + _log(TASKS__CLIENTSAVE,"TaskManager::SaveClientState for character ID %d", characterID); if(state->ActiveTaskCount > 0) { - for(int Task=0; TaskActiveTasks[Task].TaskID; - if(TaskID==TASKSLOTEMPTY) continue; - if(state->ActiveTasks[Task].Updated) { + for(int task=0; taskActiveTasks[task].TaskID; + if(taskID==TASKSLOTEMPTY) + continue; - _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState for character ID %d, Updating TaskIndex %i TaskID %i", - CharacterID, Task, TaskID); + if(state->ActiveTasks[task].Updated) { - if(!database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, - CharacterID, - TaskID, - Task, - state->ActiveTasks[Task].AcceptedTime), errbuf)) { + _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState for character ID %d, Updating TaskIndex %i TaskID %i", characterID, task, taskID); - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - } + std::string query = StringFormat("REPLACE INTO character_tasks (charid, taskid, slot, acceptedtime) " + "VALUES (%i, %i, %i, %i)", + characterID, taskID, task, state->ActiveTasks[task].AcceptedTime); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); else - state->ActiveTasks[Task].Updated = false; + state->ActiveTasks[task].Updated = false; - safe_delete_array(query); } - int UpdatedActivityCount = 0; - std::string UpdateActivityQuery = ActivityQuery; - char *buf = 0; + std::string query = "REPLACE INTO character_activities (charid, taskid, activityid, donecount, completed) " + "VALUES "; - for(int Activity=0; ActivityActivityCount; Activity++) { + int updatedActivityCount = 0; + for(int activityIndex = 0; activityIndexActivityCount; ++activityIndex) { - if(state->ActiveTasks[Task].Activity[Activity].Updated) { + if(!state->ActiveTasks[task].Activity[activityIndex].Updated) + continue; - _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientSate for character ID %d, " - "Updating Activity %i, %i", - CharacterID, Task, Activity); + _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientSate for character ID %d, Updating Activity %i, %i", + characterID, task, activityIndex); - if(UpdatedActivityCount==0) { - MakeAnyLenString(&buf, "(%i, %i, %i, %i, %i)", CharacterID, TaskID, - Activity, - state->ActiveTasks[Task].Activity[Activity].DoneCount, - state->ActiveTasks[Task].Activity[Activity].State == - ActivityCompleted); - } - else { - MakeAnyLenString(&buf, ", (%i, %i, %i, %i, %i)", CharacterID, TaskID, - Activity, - state->ActiveTasks[Task].Activity[Activity].DoneCount, - state->ActiveTasks[Task].Activity[Activity].State == - ActivityCompleted); - } - UpdateActivityQuery = UpdateActivityQuery + buf; - safe_delete_array(buf); - UpdatedActivityCount++; - } + if(updatedActivityCount==0) + query += StringFormat("(%i, %i, %i, %i, %i)", + characterID, taskID, activityIndex, + state->ActiveTasks[task].Activity[activityIndex].DoneCount, + state->ActiveTasks[task].Activity[activityIndex].State == ActivityCompleted); + else + query += StringFormat(", (%i, %i, %i, %i, %i)", + characterID, taskID, activityIndex, + state->ActiveTasks[task].Activity[activityIndex].DoneCount, + state->ActiveTasks[task].Activity[activityIndex].State == ActivityCompleted); + + updatedActivityCount++; } - if(UpdatedActivityCount > 0) { - _log(TASKS__CLIENTSAVE, "Executing query %s", UpdateActivityQuery.c_str()); - if(!database.RunQuery(query,MakeAnyLenString(&query, UpdateActivityQuery.c_str()), - errbuf)) { + if(updatedActivityCount == 0) + continue; - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - } - else { - state->ActiveTasks[Task].Updated=false; - for(int Activity=0; ActivityActivityCount; Activity++) - state->ActiveTasks[Task].Activity[Activity].Updated=false; + _log(TASKS__CLIENTSAVE, "Executing query %s", query.c_str()); + auto results = database.QueryDatabase(query); - } + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + continue; + } + + state->ActiveTasks[task].Updated=false; + for(int activityIndex=0; activityIndexActivityCount; ++activityIndex) + state->ActiveTasks[task].Activity[activityIndex].Updated=false; - safe_delete_array(query); - } } - - } - if(RuleB(TaskSystem, RecordCompletedTasks) && - (state->CompletedTasks.size() > (unsigned int)state->LastCompletedTaskLoaded)) { - - for(unsigned int i=state->LastCompletedTaskLoaded; iCompletedTasks.size(); i++) { - - _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState Saving Completed Task at slot %i", i); - int TaskID = state->CompletedTasks[i].TaskID; - if((TaskID<=0) || (TaskID>=MAXTASKS) || (Tasks[TaskID]==nullptr)) continue; - - // First we save a record with an ActivityID of -1. - // This indicates this task was completed at the given time. We infer that all - // none optional activities were completed. - // - if(!database.RunQuery(query,MakeAnyLenString(&query, CompletedTaskQuery, - CharacterID, - state->CompletedTasks[i].CompletedTime, - TaskID, -1), errbuf)) { - - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - continue; - } - safe_delete_array(query); - - // If the Rule to record non-optional task completion is not enabled, don't save it - if(!RuleB(TaskSystem, RecordCompletedOptionalActivities)) continue; - - // Insert one record for each completed optional task. - - for(int j=0; jActivityCount; j++) { - if(Tasks[TaskID]->Activity[j].Optional && state->CompletedTasks[i].ActivityDone[j]) { - - if(!database.RunQuery(query,MakeAnyLenString(&query, CompletedTaskQuery, - CharacterID, - state->CompletedTasks[i].CompletedTime, - TaskID, j), errbuf)) { - - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - } - safe_delete_array(query); - } - } - } - state->LastCompletedTaskLoaded = state->CompletedTasks.size(); } + if(!RuleB(TaskSystem, RecordCompletedTasks) || (state->CompletedTasks.size() <= (unsigned int)state->LastCompletedTaskLoaded)) { + state->LastCompletedTaskLoaded = state->CompletedTasks.size(); + return true; + } + + const char* completedTaskQuery = "REPLACE INTO completed_tasks (charid, completedtime, taskid, activityid) " + "VALUES (%i, %i, %i, %i)"; + + for(unsigned int i=state->LastCompletedTaskLoaded; iCompletedTasks.size(); i++) { + + _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState Saving Completed Task at slot %i", i); + int taskID = state->CompletedTasks[i].TaskID; + + if((taskID <= 0) || (taskID >= MAXTASKS) || (Tasks[taskID] == nullptr)) + continue; + + // First we save a record with an ActivityID of -1. + // This indicates this task was completed at the given time. We infer that all + // none optional activities were completed. + // + std::string query = StringFormat(completedTaskQuery, characterID, state->CompletedTasks[i].CompletedTime, taskID, -1); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + continue; + } + + // If the Rule to record non-optional task completion is not enabled, don't save it + if(!RuleB(TaskSystem, RecordCompletedOptionalActivities)) + continue; + + // Insert one record for each completed optional task. + + for(int j=0; jActivityCount; j++) { + if(!Tasks[taskID]->Activity[j].Optional || !state->CompletedTasks[i].ActivityDone[j]) + continue; + + query = StringFormat(completedTaskQuery, characterID, state->CompletedTasks[i].CompletedTime, taskID, j); + results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + + } + + } + + state->LastCompletedTaskLoaded = state->CompletedTasks.size(); return true; } @@ -505,329 +451,261 @@ void Client::RemoveClientTaskState() { bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) { - const char *TaskQuery = "SELECT `taskid`, `slot`, `acceptedtime` from `character_tasks` " - "WHERE `charid` = %i ORDER BY acceptedtime"; + if(!c || !state) + return false; - const char *ERR_TASK_OOR1 = "[TASKS]Task ID %i out of range while loading character tasks from database"; - - const char *ERR_SLOT_OOR = "[TASKS] Slot %i out of range while loading character tasks from database"; - - const char *ERR_DUP_SLOT = "[TASKS] Slot %i for Task %is is already occupied."; - - const char *ERR_MYSQLERROR1 = "[TASKS]Error in TaskManager::LoadClientState load Tasks: %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(!c || !state) return false; - - int CharacterID = c->CharacterID(); + int characterID = c->CharacterID(); state->ActiveTaskCount = 0; - _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientSate for character ID %d", CharacterID); + _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState for character ID %d", characterID); - if(database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, CharacterID), errbuf, &result)) { - - while((row = mysql_fetch_row(result))) { - - int TaskID = atoi(row[0]); - int Slot = atoi(row[1]); - - if((TaskID<0) || (TaskID>=MAXTASKS)) { - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR1, TaskID); - continue; - } - - if((Slot<0) || (Slot>=MAXACTIVETASKS)) { - LogFile->write(EQEMuLog::Error, ERR_SLOT_OOR, Slot); - continue; - } - - if(state->ActiveTasks[Slot].TaskID != TASKSLOTEMPTY) { - LogFile->write(EQEMuLog::Error, ERR_DUP_SLOT, Slot, TaskID); - continue; - } - - int acceptedtime = atoi(row[2]); - - state->ActiveTasks[Slot].TaskID = TaskID; - - state->ActiveTasks[Slot].CurrentStep = -1; - - state->ActiveTasks[Slot].AcceptedTime = acceptedtime; - - state->ActiveTasks[Slot].Updated = false; - - for(int i=0; iActiveTasks[Slot].Activity[i].ActivityID = -1; - } - - //LoadClientActivitiesForTask(CharacterID, &state->ActiveTasks[state->ActiveTaskCount]); - // Calculate which activities are active based on those completed. - //state->UnlockActivities(state->ActiveTaskCount); - - state->ActiveTaskCount++; - - _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, " - "Accepted Time: %8X", - CharacterID, TaskID,acceptedtime); - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR1, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT `taskid`, `slot`, `acceptedtime` " + "FROM `character_tasks` " + "WHERE `charid` = %i ORDER BY acceptedtime", characterID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load Tasks: %s", results.ErrorMessage().c_str()); return false; + } + + for(auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + int slot = atoi(row[1]); + + if((taskID<0) || (taskID>=MAXTASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading character tasks from database", taskID); + continue; + } + + if((slot<0) || (slot>=MAXACTIVETASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS] Slot %i out of range while loading character tasks from database", slot); + continue; + } + + if(state->ActiveTasks[slot].TaskID != TASKSLOTEMPTY) { + LogFile->write(EQEMuLog::Error, "[TASKS] Slot %i for Task %is is already occupied.", slot, taskID); + continue; + } + + int acceptedtime = atoi(row[2]); + + state->ActiveTasks[slot].TaskID = taskID; + state->ActiveTasks[slot].CurrentStep = -1; + state->ActiveTasks[slot].AcceptedTime = acceptedtime; + state->ActiveTasks[slot].Updated = false; + + for(int i=0; iActiveTasks[slot].Activity[i].ActivityID = -1; + + ++state->ActiveTaskCount; + + _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, Accepted Time: %8X", characterID, taskID, acceptedtime); } // Load Activities + _log(TASKS__CLIENTLOAD, "LoadClientState. Loading activities for character ID %d", characterID); - const char *ActivityQuery = "SELECT `taskid`, `activityid`, `donecount`, `completed` " - " from `character_activities` WHERE `charid` = %i " - "ORDER BY `taskid` ASC, `activityid` ASC"; - - const char *ERR_TASK_OOR2 = "[TASKS]Task ID %i out of range while loading character activities from database"; - - const char *ERR_ACTIVITY_OOR = "[TASKS]Activity ID %i out of range while loading character activities from database"; - - const char *ERR_NOTASK = "[TASKS]Activity %i found for task %i which client does not have."; - - const char *ERR_MYSQLERROR2 = "[TASKS]Error in TaskManager::LoadClientState load Activities: %s"; - - _log(TASKS__CLIENTLOAD, "LoadClientState. Loading activities for character ID %d", CharacterID); - - - - if(database.RunQuery(query,MakeAnyLenString(&query, ActivityQuery, - CharacterID), errbuf, &result)) { - - - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - if((TaskID<0) || (TaskID>=MAXTASKS)) { - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR2, TaskID); - continue; - } - int ActivityID = atoi(row[1]); - if((ActivityID<0) || (ActivityID>=MAXACTIVITIESPERTASK)) { - LogFile->write(EQEMuLog::Error, ERR_ACTIVITY_OOR, ActivityID); - continue; - } - - // Find Active Task Slot - int ActiveTaskIndex = -1; - - for(int i=0; iActiveTasks[i].TaskID == TaskID) { - ActiveTaskIndex = i; - break; - } - } - - if(ActiveTaskIndex == -1) { - LogFile->write(EQEMuLog::Error, ERR_NOTASK, ActivityID, TaskID); - continue; - } - - int DoneCount = atoi(row[2]); - bool Completed = atoi(row[3]); - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].ActivityID = ActivityID; - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].DoneCount = DoneCount; - if(Completed) state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].State = ActivityCompleted; - else - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].State = ActivityHidden; - - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].Updated = false; - - _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, ActivityID: %i, " - "DoneCount: %i, Completed: %i", - CharacterID, TaskID, ActivityID, DoneCount, Completed); - - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR2, errbuf); - safe_delete_array(query); + query = StringFormat("SELECT `taskid`, `activityid`, `donecount`, `completed` " + "FROM `character_activities` " + "WHERE `charid` = %i " + "ORDER BY `taskid` ASC, `activityid` ASC", characterID); + results = database.QueryDatabase(query); + if (!results.Success()){ + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load Activities: %s", results.ErrorMessage().c_str()); return false; } - const char *CompletedTaskQuery = "SELECT `taskid`, `activityid`, `completedtime` from `completed_tasks` " - "WHERE `charid` = %i ORDER BY completedtime, taskid, activityid"; + for (auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + if((taskID<0) || (taskID>=MAXTASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading character activities from database", taskID); + continue; + } - const char *ERR_TASK_OOR3 = "[TASKS]Task ID %i out of range while loading completed tasks from database"; + int activityID = atoi(row[1]); + if((activityID<0) || (activityID>=MAXACTIVITIESPERTASK)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity ID %i out of range while loading character activities from database", activityID); + continue; + } - const char *ERR_ACTIVITY_OOR2 = "[TASKS]Activity ID %i out of range while loading completed tasks from database"; + // Find Active Task Slot + int activeTaskIndex = -1; - const char *ERR_MYSQLERROR3 = "[TASKS]Error in TaskManager::LoadClientState load completed tasks: %s"; + for(int i=0; iActiveTasks[i].TaskID == taskID) { + activeTaskIndex = i; + break; + } + if(activeTaskIndex == -1) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity %i found for task %i which client does not have.", activityID, taskID); + continue; + } + int doneCount = atoi(row[2]); + bool completed = atoi(row[3]); + state->ActiveTasks[activeTaskIndex].Activity[activityID].ActivityID = activityID; + state->ActiveTasks[activeTaskIndex].Activity[activityID].DoneCount = doneCount; + if(completed) + state->ActiveTasks[activeTaskIndex].Activity[activityID].State = ActivityCompleted; + else + state->ActiveTasks[activeTaskIndex].Activity[activityID].State = ActivityHidden; + + state->ActiveTasks[activeTaskIndex].Activity[activityID].Updated = false; + + _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, ActivityID: %i, DoneCount: %i, Completed: %i", characterID, taskID, activityID, doneCount, completed); + + } if(RuleB(TaskSystem, RecordCompletedTasks)) { - if(database.RunQuery(query,MakeAnyLenString(&query, CompletedTaskQuery, - CharacterID), errbuf, &result)) { - - CompletedTaskInformation cti; - - for(int i=0; i=MAXTASKS)) { - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR3, TaskID); - continue; - } - int ActivityID = atoi(row[1]); - - // An ActivityID of -1 means mark all the none optional activities in the - // task as complete. If the Rule to record optional activities is enabled, - // subsequent records for this task will flag any optional tasks that were - // completed. - if((ActivityID<-1) || (ActivityID>=MAXACTIVITIESPERTASK)) { - LogFile->write(EQEMuLog::Error, ERR_ACTIVITY_OOR2, ActivityID); - continue; - } - int CompletedTime = atoi(row[2]); - - if((PreviousTaskID != -1) && ((TaskID != PreviousTaskID) || - (CompletedTime != PreviousCompletedTime))) { - - state->CompletedTasks.push_back(cti); - for(int i=0; iActivityCount; i++) - if(!Task->Activity[i].Optional) - cti.ActivityDone[i] = true; - } - else - cti.ActivityDone[ActivityID] = true; - - } - if(PreviousTaskID != -1) - state->CompletedTasks.push_back(cti); - - state->LastCompletedTaskLoaded = state->CompletedTasks.size(); - - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR3, errbuf); - safe_delete_array(query); + query = StringFormat("SELECT `taskid`, `activityid`, `completedtime` " + "FROM `completed_tasks` " + "WHERE `charid` = %i ORDER BY completedtime, taskid, activityid", + characterID); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load completed tasks: %s", results.ErrorMessage().c_str()); return false; + } + + CompletedTaskInformation cti; + + for(int i=0; i=MAXTASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading completed tasks from database", taskID); + continue; + } + + // An ActivityID of -1 means mark all the none optional activities in the + // task as complete. If the Rule to record optional activities is enabled, + // subsequent records for this task will flag any optional tasks that were + // completed. + int activityID = atoi(row[1]); + if((activityID<-1) || (activityID>=MAXACTIVITIESPERTASK)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity ID %i out of range while loading completed tasks from database", activityID); + continue; + } + + int completedTime = atoi(row[2]); + if((previousTaskID != -1) && ((taskID != previousTaskID) || (completedTime != previousCompletedTime))) { + state->CompletedTasks.push_back(cti); + for(int i=0; iActivityCount; i++) + if(!task->Activity[i].Optional) + cti.ActivityDone[i] = true; + } + else + cti.ActivityDone[activityID] = true; + + } + + if(previousTaskID != -1) + state->CompletedTasks.push_back(cti); + + state->LastCompletedTaskLoaded = state->CompletedTasks.size(); + + } + + query = StringFormat("SELECT `taskid` FROM character_enabledtasks " + "WHERE `charid` = %i AND `taskid` >0 AND `taskid` < %i " + "ORDER BY `taskid` ASC", + characterID, MAXTASKS); + results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load enabled tasks: %s", results.ErrorMessage().c_str()); + else + for (auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + state->EnabledTasks.push_back(taskID); + _log(TASKS__CLIENTLOAD, "Adding TaskID %i to enabled tasks", taskID); } - } - - const char *EnabledTaskQuery = "SELECT `taskid` FROM character_enabledtasks WHERE `charid` = %i " - "AND `taskid` >0 AND `taskid` < %i ORDER BY `taskid` ASC"; - - const char *ERR_MYSQLERROR4 = "[TASKS]Error in TaskManager::LoadClientState load enabled tasks: %s"; - - if(database.RunQuery(query,MakeAnyLenString(&query, EnabledTaskQuery, - CharacterID, MAXTASKS), errbuf, &result)) { - - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - state->EnabledTasks.push_back(TaskID); - _log(TASKS__CLIENTLOAD, "Adding TaskID %i to enabled tasks", TaskID); - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR4, errbuf); - safe_delete_array(query); - } // Check that there is an entry in the client task state for every activity in each task // This should only break if a ServerOP adds or deletes activites for a task that players already // have active, or due to a bug. - - - const char *ERR_NOTASK2 = "[TASKS]Character %i has task %i which does not exist."; - - const char *ERR_INCONSISTENT = "[TASKS]Fatal error in character %i task state. Activity %i for " - "Task %i either missing from client state or from task."; - for(int i=0; iActiveTasks[i].TaskID; - if(TaskID==TASKSLOTEMPTY) continue; - if(!Tasks[TaskID]) { + int taskID = state->ActiveTasks[i].TaskID; + if(taskID==TASKSLOTEMPTY) continue; + if(!Tasks[taskID]) { c->Message(13, "Active Task Slot %i, references a task (%i), that does not exist. " - "Removing from memory. Contact a GM to resolve this.",i, TaskID); + "Removing from memory. Contact a GM to resolve this.",i, taskID); - LogFile->write(EQEMuLog::Error, ERR_NOTASK2, CharacterID, TaskID); + LogFile->write(EQEMuLog::Error, "[TASKS]Character %i has task %i which does not exist.", characterID, taskID); state->ActiveTasks[i].TaskID=TASKSLOTEMPTY; continue; } - for(int j=0; jActivityCount; j++) { + for(int j=0; jActivityCount; j++) { if(state->ActiveTasks[i].Activity[j].ActivityID != j) { c->Message(13, "Active Task %i, %s. Activity count does not match expected value." "Removing from memory. Contact a GM to resolve this.", - TaskID, Tasks[TaskID]->Title); + taskID, Tasks[taskID]->Title); - LogFile->write(EQEMuLog::Error, ERR_INCONSISTENT, CharacterID, j, TaskID); + LogFile->write(EQEMuLog::Error, "[TASKS]Fatal error in character %i task state. Activity %i for " + "Task %i either missing from client state or from task.", characterID, j, taskID); state->ActiveTasks[i].TaskID=TASKSLOTEMPTY; break; } } } - for(int i=0; iActiveTasks[i].TaskID != TASKSLOTEMPTY) - state->UnlockActivities(CharacterID, i); + state->UnlockActivities(characterID, i); - _log(TASKS__CLIENTLOAD, "LoadClientState for Character ID %d DONE!", CharacterID); + _log(TASKS__CLIENTLOAD, "LoadClientState for Character ID %d DONE!", characterID); return true; } -void ClientTaskState::EnableTask(int CharID, int TaskCount, int *TaskList) { +void ClientTaskState::EnableTask(int characterID, int taskCount, int *tasks) { // Check if the Task is already enabled for this client // - std::vector TasksEnabled; - std::vector::iterator Iterator; + std::vector tasksEnabled; - for(int i=0; i TaskList[i]) break; - ++Iterator; + if((*iterator) > tasks[i]) + break; + ++iterator; } - if(AddTask) { - EnabledTasks.insert(Iterator, TaskList[i]); + + if(addTask) { + EnabledTasks.insert(iterator, tasks[i]); // Make a note of the task we enabled, for later SQL generation - TasksEnabled.push_back(TaskList[i]); + tasksEnabled.push_back(tasks[i]); } } @@ -835,59 +713,46 @@ void ClientTaskState::EnableTask(int CharID, int TaskCount, int *TaskList) { for(unsigned int i=0; iwrite(EQEMuLog::Error, "[TASKS]Error in ClientTaskState::EnableTask %s %s", query.c_str(), results.ErrorMessage().c_str()); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - char *buf = 0; - - for(unsigned int i=0; iwrite(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - } - - safe_delete_array(query); } -void ClientTaskState::DisableTask(int CharID, int TaskCount, int *TaskList) { +void ClientTaskState::DisableTask(int charID, int taskCount, int *taskList) { // Check if the Task is enabled for this client // - std::vector TasksDisabled; - std::vector::iterator Iterator; + std::vector tasksDisabled; - for(int i=0; i TaskList[i]) break; - ++Iterator; + + if((*iterator) > taskList[i]) + break; + + ++iterator; } - if(RemoveTask) { - EnabledTasks.erase(Iterator); - TasksDisabled.push_back(TaskList[i]); + + if(removeTask) { + EnabledTasks.erase(iterator); + tasksDisabled.push_back(taskList[i]); } } @@ -895,40 +760,20 @@ void ClientTaskState::DisableTask(int CharID, int TaskCount, int *TaskList) { for(unsigned int i=0; iwrite(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - } - - safe_delete_array(query); + queryStream << ")"; + std::string query = queryStream.str(); + _log(TASKS__UPDATE, "Executing query %s", query.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "[TASKS]Error in ClientTaskState::DisableTask %s %s", query.c_str(), results.ErrorMessage().c_str()); } bool ClientTaskState::IsTaskEnabled(int TaskID) { @@ -1152,7 +997,7 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task if(Tasks[TaskList[i]] != nullptr) break; } - // FIXME: The 10 and 5 values in this calculation are to account for the string "ABCD" we are putting in 3 times. + // FIXME: The 10 and 5 values in this calculation are to account for the string "ABCD" we are putting in 3 times. // // Calculate how big the packet needs to be pased on the number of tasks and the // size of the variable length strings. @@ -1230,9 +1075,9 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task // FIXME: In live packets, these two strings appear to be the same as the Text1 and Text2 // strings from the first activity in the task, however the task chooser/selector // does not appear to make use of them. - sprintf(Ptr, "ABCD"); + sprintf(Ptr, "ABCD"); Ptr = Ptr + strlen(Ptr) + 1; - sprintf(Ptr, "ABCD"); + sprintf(Ptr, "ABCD"); Ptr = Ptr + strlen(Ptr) + 1; AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)Ptr; @@ -1247,7 +1092,7 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task // In some packets, this next string looks like a short task summary, however it doesn't // appear anywhere in the client window. - sprintf(Ptr, "ABCD"); + sprintf(Ptr, "ABCD"); Ptr = Ptr + strlen(Ptr) + 1; } @@ -1427,24 +1272,18 @@ int ClientTaskState::GetActiveTaskID(int index) { return ActiveTasks[index].TaskID; } -static void DeleteCompletedTaskFromDatabase(int CharID, int TaskID) { +static void DeleteCompletedTaskFromDatabase(int charID, int taskID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; + _log(TASKS__UPDATE, "DeleteCompletedTasksFromDatabase. CharID = %i, TaskID = %i", charID, taskID); - const char *TaskQuery="DELETE FROM completed_tasks WHERE charid=%i AND taskid = %i"; - - _log(TASKS__UPDATE, "DeleteCompletedTasksFromDatabase. CharID = %i, TaskID = %i", - CharID, TaskID); - - if(!database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, CharID, TaskID), errbuf)) { - - LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s, %s", query, errbuf); - safe_delete_array(query); + const std::string query = StringFormat("DELETE FROM completed_tasks WHERE charid=%i AND taskid = %i", charID, taskID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s, %s", query.c_str(), results.ErrorMessage().c_str()); return; } - _log(TASKS__UPDATE, "Delete query %s", query); - safe_delete_array(query); + + _log(TASKS__UPDATE, "Delete query %s", query.c_str()); } bool ClientTaskState::UnlockActivities(int CharID, int TaskIndex) { @@ -1979,7 +1818,7 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T taskmanager->SendSingleActiveTaskToClient(c, TaskIndex, TaskComplete, false); // Inform the client the task has been updated, both by a chat message c->Message(0, "Your task '%s' has been updated.", Task->Title); - + if(Task->Activity[ActivityID].GoalMethod != METHODQUEST) { char buf[24]; snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID); @@ -1992,7 +1831,7 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T QServ->PlayerLogEvent(Player_Log_Task_Updates, c->CharacterID(), event_desc); } } - + // If this task is now complete, the Completed tasks will have been // updated in UnlockActivities. Send the completed task list to the // client. This is the same sequence the packets are sent on live. @@ -3142,40 +2981,29 @@ void ClientTaskState::CancelTask(Client *c, int SequenceNumber, bool RemoveFromD RemoveTask(c, SequenceNumber); } -void ClientTaskState::RemoveTask(Client *c, int SequenceNumber) { +void ClientTaskState::RemoveTask(Client *c, int sequenceNumber) { - int CharacterID = c->CharacterID(); + int characterID = c->CharacterID(); + _log(TASKS__UPDATE, "ClientTaskState Cancel Task %i ", sequenceNumber); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - const char *TaskQuery="DELETE FROM character_tasks WHERE charid=%i AND taskid = %i"; - - const char *ActivityQuery="DELETE FROM character_activities WHERE charid=%i AND taskid = %i"; - - _log(TASKS__UPDATE, "ClientTaskState Cancel Task %i ", SequenceNumber); - - if(!database.RunQuery(query,MakeAnyLenString(&query, ActivityQuery, - CharacterID, - ActiveTasks[SequenceNumber].TaskID), errbuf)) { - - LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", errbuf); - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM character_activities WHERE charid=%i AND taskid = %i", + characterID, ActiveTasks[sequenceNumber].TaskID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", results.ErrorMessage().c_str()); return; } - _log(TASKS__UPDATE, "CancelTask: %s", query); - safe_delete_array(query); - if(!database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, - CharacterID, - ActiveTasks[SequenceNumber].TaskID), errbuf)) { + _log(TASKS__UPDATE, "CancelTask: %s", query.c_str()); - LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", errbuf); - } + query = StringFormat("DELETE FROM character_tasks WHERE charid=%i AND taskid = %i", + characterID, ActiveTasks[sequenceNumber].TaskID); + results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", results.ErrorMessage().c_str()); - _log(TASKS__UPDATE, "CancelTask: %s", query); - safe_delete_array(query); + _log(TASKS__UPDATE, "CancelTask: %s", query.c_str()); - ActiveTasks[SequenceNumber].TaskID = TASKSLOTEMPTY; + ActiveTasks[sequenceNumber].TaskID = TASKSLOTEMPTY; ActiveTaskCount--; } @@ -3297,98 +3125,83 @@ TaskGoalListManager::~TaskGoalListManager() { bool TaskGoalListManager::LoadLists() { - - const char *CountQuery = "SELECT `listid`, COUNT(`entry`) FROM `goallists` GROUP by `listid` " - "ORDER BY `listid`"; - - const char *ListQuery = "SELECT `entry` from `goallists` WHERE `listid`=%i " - "ORDER BY `entry` ASC LIMIT %i"; - - const char *ERR_MYSQLERROR = "Error in TaskGoalListManager::LoadLists: %s %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - _log(TASKS__GLOBALLOAD, "TaskGoalListManager::LoadLists Called"); - for(int i=0; i< NumberOfLists; i++) { - + for(int i=0; i< NumberOfLists; i++) safe_delete_array(TaskGoalLists[i].GoalItemEntries); - - } safe_delete_array(TaskGoalLists); + const char *ERR_MYSQLERROR = "Error in TaskGoalListManager::LoadLists: %s %s"; + NumberOfLists = 0; - if(database.RunQuery(query,MakeAnyLenString(&query,CountQuery),errbuf,&result)) { - - NumberOfLists = mysql_num_rows(result); - _log(TASKS__GLOBALLOAD, "Database returned a count of %i lists", NumberOfLists); - - TaskGoalLists = new TaskGoalList_Struct[NumberOfLists]; - - int ListIndex = 0; - - while((row = mysql_fetch_row(result))) { - int ListID = atoi(row[0]); - int ListSize = atoi(row[1]); - - TaskGoalLists[ListIndex].ListID = ListID; - TaskGoalLists[ListIndex].Size = ListSize; - TaskGoalLists[ListIndex].Min = 0; - TaskGoalLists[ListIndex].Max = 0; - TaskGoalLists[ListIndex].GoalItemEntries = new int[ListSize]; - - ListIndex++; - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - safe_delete_array(query); + std::string query = "SELECT `listid`, COUNT(`entry`) " + "FROM `goallists` GROUP by `listid` " + "ORDER BY `listid`"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query.c_str(), results.ErrorMessage().c_str()); return false; + } + + NumberOfLists = results.RowCount(); + _log(TASKS__GLOBALLOAD, "Database returned a count of %i lists", NumberOfLists); + + TaskGoalLists = new TaskGoalList_Struct[NumberOfLists]; + + int listIndex = 0; + + for(auto row = results.begin(); row != results.end(); ++row) { + int listID = atoi(row[0]); + int listSize = atoi(row[1]); + + TaskGoalLists[listIndex].ListID = listID; + TaskGoalLists[listIndex].Size = listSize; + TaskGoalLists[listIndex].Min = 0; + TaskGoalLists[listIndex].Max = 0; + TaskGoalLists[listIndex].GoalItemEntries = new int[listSize]; + + listIndex++; + } + + for(int listIndex = 0; listIndex < NumberOfLists; listIndex++) { + + int listID = TaskGoalLists[listIndex].ListID; + unsigned int size = TaskGoalLists[listIndex].Size; + query = StringFormat("SELECT `entry` from `goallists` " + "WHERE `listid` = %i " + "ORDER BY `entry` ASC LIMIT %i", + listID, size); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query.c_str(), results.ErrorMessage().c_str()); + TaskGoalLists[listIndex].Size = 0; + continue; + } + + // This should only happen if a row is deleted in between us retrieving the counts + // at the start of this method and getting to here. It should not be possible for + // an INSERT to cause a problem, as the SELECT is used with a LIMIT + if(results.RowCount() < size) + TaskGoalLists[listIndex].Size = results.RowCount(); + + int entryIndex = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++entryIndex) { + + int entry = atoi(row[0]); + + if(entry < TaskGoalLists[listIndex].Min) + TaskGoalLists[listIndex].Min = entry; + + if(entry > TaskGoalLists[listIndex].Max) + TaskGoalLists[listIndex].Max = entry; + + TaskGoalLists[listIndex].GoalItemEntries[entryIndex] = entry; + + } + } - for(int ListIndex = 0; ListIndex < NumberOfLists; ListIndex++) { - - int ListID = TaskGoalLists[ListIndex].ListID; - unsigned int Size = TaskGoalLists[ListIndex].Size; - - if(database.RunQuery(query,MakeAnyLenString(&query,ListQuery,ListID,Size),errbuf,&result)) { - // This should only happen if a row is deleted in between us retrieving the counts - // at the start of this method and getting to here. It should not be possible for - // an INSERT to cause a problem, as the SELECT is used with a LIMIT - if(mysql_num_rows(result) < Size) - TaskGoalLists[ListIndex].Size = mysql_num_rows(result); - - int EntryIndex = 0; - - while((row = mysql_fetch_row(result))) { - - int Entry = atoi(row[0]); - - if(Entry < TaskGoalLists[ListIndex].Min) - TaskGoalLists[ListIndex].Min = Entry; - - if(Entry > TaskGoalLists[ListIndex].Max) - TaskGoalLists[ListIndex].Max = Entry; - - TaskGoalLists[ListIndex].GoalItemEntries[EntryIndex++] = Entry; - - } - - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - TaskGoalLists[ListIndex].Size = 0; - safe_delete_array(query); - } - } return true; } @@ -3486,47 +3299,33 @@ TaskProximityManager::~TaskProximityManager() { } -bool TaskProximityManager::LoadProximities(int ZoneID) { +bool TaskProximityManager::LoadProximities(int zoneID) { + TaskProximity proximity; - const char *ProximityQuery = "SELECT `exploreid`, `minx`, `maxx`, `miny`, `maxy`, " - "`minz`, `maxz` from `proximities` WHERE `zoneid`=%i " - "ORDER BY `zoneid` ASC"; - - const char *ERR_MYSQLERROR = "Error in TaskProximityManager::LoadProximities %s %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - - TaskProximity Proximity; - - _log(TASKS__GLOBALLOAD, "TaskProximityManager::LoadProximities Called for zone %i", ZoneID); + _log(TASKS__GLOBALLOAD, "TaskProximityManager::LoadProximities Called for zone %i", zoneID); TaskProximities.clear(); - if(database.RunQuery(query,MakeAnyLenString(&query,ProximityQuery, ZoneID),errbuf,&result)) { - - while((row = mysql_fetch_row(result))) { - Proximity.ExploreID = atoi(row[0]); - Proximity.MinX = atof(row[1]); - Proximity.MaxX = atof(row[2]); - Proximity.MinY = atof(row[3]); - Proximity.MaxY = atof(row[4]); - Proximity.MinZ = atof(row[5]); - Proximity.MaxZ = atof(row[6]); - - TaskProximities.push_back(Proximity); - - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT `exploreid`, `minx`, `maxx`, " + "`miny`, `maxy`, `minz`, `maxz` " + "FROM `proximities` WHERE `zoneid` = %i " + "ORDER BY `zoneid` ASC", zoneID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in TaskProximityManager::LoadProximities %s %s", query.c_str(), results.ErrorMessage().c_str()); return false; - } - safe_delete_array(query); + } + + for( auto row = results.begin(); row != results.end(); ++row) { + proximity.ExploreID = atoi(row[0]); + proximity.MinX = atof(row[1]); + proximity.MaxX = atof(row[2]); + proximity.MinY = atof(row[3]); + proximity.MaxY = atof(row[4]); + proximity.MinZ = atof(row[5]); + proximity.MaxZ = atof(row[6]); + + TaskProximities.push_back(proximity); + } return true;