From 73f1933f94bcadb8c3b3f9763e6afad5f02891f4 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Mon, 23 Sep 2013 22:37:15 +0200 Subject: Store questlog values in the database --- src/account-server/character.cpp | 22 ++++++ src/account-server/character.h | 10 +++ src/account-server/storage.cpp | 61 ++++++++++++++++ src/common/manaserv_protocol.h | 4 +- src/game-server/charactercomponent.cpp | 112 ++++++++++++++++++++++++++++- src/game-server/charactercomponent.h | 41 ++++++++++- src/scripting/lua.cpp | 57 +++++---------- src/sql/mysql/createTables.sql | 17 ++++- src/sql/mysql/updates/update_25_to_26.sql | 24 +++++++ src/sql/sqlite/createTables.sql | 19 ++++- src/sql/sqlite/updates/update_25_to_26.sql | 22 ++++++ 11 files changed, 341 insertions(+), 48 deletions(-) create mode 100644 src/sql/mysql/updates/update_25_to_26.sql create mode 100644 src/sql/sqlite/updates/update_25_to_26.sql diff --git a/src/account-server/character.cpp b/src/account-server/character.cpp index cafc256b..35a8b079 100644 --- a/src/account-server/character.cpp +++ b/src/account-server/character.cpp @@ -91,6 +91,16 @@ void CharacterData::serialize(MessageOut &msg) msg.writeInt32(abilityId); } + + // questlog + msg.writeInt16(mQuests.size()); + for (QuestInfo &quest : mQuests) { + msg.writeInt16(quest.id); + msg.writeInt8(quest.state); + msg.writeString(quest.title); + msg.writeString(quest.description); + } + // inventory - must be last because size isn't transmitted const Possessions &poss = getPossessions(); const EquipData &equipData = poss.getEquipment(); @@ -166,6 +176,18 @@ void CharacterData::deserialize(MessageIn &msg) giveAbility(id); } + // questlog + int questlogSize = msg.readInt16(); + mQuests.clear(); + for (int i = 0; i < questlogSize; ++i) { + QuestInfo quest; + quest.id = msg.readInt16(); + quest.state = msg.readInt8(); + quest.title = msg.readString(); + quest.description = msg.readString(); + mQuests.push_back(quest); + } + // inventory - must be last because size isn't transmitted Possessions &poss = getPossessions(); diff --git a/src/account-server/character.h b/src/account-server/character.h index f61b8380..05fce77b 100644 --- a/src/account-server/character.h +++ b/src/account-server/character.h @@ -64,6 +64,14 @@ struct Status unsigned time; }; +struct QuestInfo +{ + int id; + int state; + std::string title; + std::string description; +}; + /** * Stores attributes by their id. */ @@ -263,6 +271,8 @@ class CharacterData std::vector mGuilds; //!< All the guilds the player //!< belongs to. + std::vector mQuests; + friend class AccountHandler; friend class Storage; }; diff --git a/src/account-server/storage.cpp b/src/account-server/storage.cpp index 91ceec94..b6268179 100644 --- a/src/account-server/storage.cpp +++ b/src/account-server/storage.cpp @@ -78,6 +78,7 @@ static const char *CHAR_ATTR_TBL_NAME = "mana_char_attr"; static const char *CHAR_STATUS_EFFECTS_TBL_NAME = "mana_char_status_effects"; static const char *CHAR_KILL_COUNT_TBL_NAME = "mana_char_kill_stats"; static const char *CHAR_ABILITIES_TBL_NAME = "mana_char_abilities"; +static const char *QUESTLOG_TBL_NAME = "mana_questlog"; static const char *INVENTORIES_TBL_NAME = "mana_inventories"; static const char *ITEMS_TBL_NAME = "mana_items"; static const char *GUILDS_TBL_NAME = "mana_guilds"; @@ -467,6 +468,27 @@ CharacterData *Storage::getCharacterBySQL(Account *owner) character->giveAbility(toUint(abilitiesInfo(row, 0))); } } + + // Load the questlog + s.clear(); + s.str(""); + s << "SELECT quest_id, quest_state, quest_title, quest_description " + << "FROM " << QUESTLOG_TBL_NAME + << " WHERE char_id = " << character->getDatabaseID(); + const dal::RecordSet &quests = mDb->execSql(s.str()); + if (!quests.isEmpty()) + { + const unsigned nRows = quests.rows(); + for (unsigned row = 0; row < nRows; row++) + { + QuestInfo quest; + quest.id = toUint(quests(row, 0)); + quest.state = toUint(quests(row, 1)); + quest.title = quests(row, 2); + quest.description = quests(row, 3); + character->mQuests.push_back(quest); + } + } } catch (const dal::DbSqlQueryExecFailure &e) { @@ -760,6 +782,45 @@ bool Storage::updateCharacter(CharacterData *character) "SQL query failure: ", e);; } + // Character's questlog + try + { + // Out with the old + std::ostringstream deleteSql(""); + std::ostringstream insertSql; + deleteSql << "DELETE FROM " << QUESTLOG_TBL_NAME + << " WHERE char_id='" + << character->getDatabaseID() << "';"; + mDb->execSql(deleteSql.str()); + // In with the new + for (QuestInfo &quest : character->mQuests) + { + insertSql.str(""); + insertSql << "INSERT INTO " << QUESTLOG_TBL_NAME + << " (char_id, quest_id, quest_state, " + << "quest_title, quest_description)" + << " VALUES (" + << character->getDatabaseID() << "," + << " " << quest.id << "," + << " " << quest.state << "," + << " ?," + << " ?" + << ")"; + if (mDb->prepareSql(insertSql.str())) + { + mDb->bindValue(1, quest.title); + mDb->bindValue(2, quest.description); + + mDb->processSql(); + } + } + } + catch (const dal::DbSqlQueryExecFailure& e) + { + utils::throwError("(DALStorage::updateCharacter #5) " + "SQL query failure: ", e);; + } + // Character's inventory // Delete the old inventory and equipment table first try diff --git a/src/common/manaserv_protocol.h b/src/common/manaserv_protocol.h index 80863273..26db32c6 100644 --- a/src/common/manaserv_protocol.h +++ b/src/common/manaserv_protocol.h @@ -30,7 +30,7 @@ namespace ManaServ { enum { PROTOCOL_VERSION = 8, - SUPPORTED_DB_VERSION = 25 + SUPPORTED_DB_VERSION = 26 }; /** @@ -255,7 +255,7 @@ enum { PCMSG_KICK_USER = 0x0466, // W channel id, S name // -- Questlog - GPMSG_QUESTLOG_STATUS = 0x0470, // W quest id, B flags, [B status], [S questname], [S questdescription] + GPMSG_QUESTLOG_STATUS = 0x0470, // {W quest id, B flags, [B status], [S questname], [S questdescription]}* // Inter-server GAMSG_REGISTER = 0x0500, // S address, W port, S password, D items db revision diff --git a/src/game-server/charactercomponent.cpp b/src/game-server/charactercomponent.cpp index 11e90a65..344a8959 100644 --- a/src/game-server/charactercomponent.cpp +++ b/src/game-server/charactercomponent.cpp @@ -176,6 +176,16 @@ void CharacterComponent::deserialize(Entity &entity, MessageIn &msg) entity.getComponent()->giveAbility(id); } + // questlog + int questlogSize = msg.readInt16(); + for (int i = 0; i < questlogSize; ++i) { + unsigned id = msg.readInt16(); + QuestState state = (QuestState) msg.readInt8(); + std::string title = msg.readString(); + std::string description = msg.readString(); + + setQuestlog(id, state, title, description); + } Possessions &poss = getPossessions(); @@ -256,6 +266,16 @@ void CharacterComponent::serialize(Entity &entity, MessageOut &msg) msg.writeInt32(abilityIt.first); } + // questlog + msg.writeInt16(mQuestlog.size()); + for (auto questlogIt : mQuestlog) { + QuestInfo &quest = questlogIt.second; + msg.writeInt16(quest.id); + msg.writeInt8(quest.state); + msg.writeString(quest.title); + msg.writeString(quest.description); + } + // inventory - must be last because size isn't transmitted const Possessions &poss = getPossessions(); const EquipData &equipData = poss.getEquipment(); @@ -359,6 +379,44 @@ void CharacterComponent::sendAttributePointsStatus(Entity &entity) mSendAttributePointsStatus = false; } +void CharacterComponent::sendQuestUpdate() +{ + MessageOut msg(GPMSG_QUESTLOG_STATUS); + for (auto &questIt : mModifiedQuests) { + const QuestInfo *quest = questIt.first; + bool notify = questIt.second; + msg.writeInt16(quest->id); + int flags = QUESTLOG_UPDATE_STATE | + QUESTLOG_UPDATE_TITLE | + QUESTLOG_UPDATE_DESCRIPTION; + if (notify) + flags |= QUESTLOG_SHOW_NOTIFICATION; + msg.writeInt8(flags); + msg.writeInt8(quest->state); + msg.writeString(quest->title); + msg.writeString(quest->description); + } + mModifiedQuests.clear(); + gameHandler->sendTo(mClient, msg); +} + +void CharacterComponent::markQuestAsModified(const QuestInfo *quest, + bool sendNotification) +{ + const auto &it = mModifiedQuests.find(quest); + if (it == mModifiedQuests.end()) { + mModifiedQuests.insert(std::make_pair(quest, sendNotification)); + return; + } + it->second = sendNotification; +} +void CharacterComponent::markAllQuestsAsModified() +{ + for (auto &questIt : mQuestlog) { + mModifiedQuests[&questIt.second] = false; + } +} + void CharacterComponent::cancelTransaction() { TransactionType t = mTransaction; @@ -428,7 +486,8 @@ void CharacterComponent::sendStatus(Entity &entity) attribMsg.writeInt32(beingComponent->getAttributeBase(attribute) * 256); attribMsg.writeInt32(beingComponent->getModifiedAttribute(attribute) * 256); } - if (attribMsg.getLength() > 2) gameHandler->sendTo(mClient, attribMsg); + if (attribMsg.getLength() > 2) + gameHandler->sendTo(mClient, attribMsg); mModifiedAttributes.clear(); if (!mModifiedAbilities.empty()) @@ -439,6 +498,9 @@ void CharacterComponent::sendStatus(Entity &entity) if (mSendAttributePointsStatus) sendAttributePointsStatus(entity); + + if (!mModifiedQuests.empty()) + sendQuestUpdate(); } void CharacterComponent::modifiedAllAbilities(Entity &entity) @@ -581,4 +643,52 @@ void CharacterComponent::markAllInfoAsChanged(Entity &entity) { modifiedAllAbilities(entity); modifiedAllAttributes(entity); + markAllQuestsAsModified(); +} + +void CharacterComponent::setQuestlog(unsigned id, QuestState state, + const std::string &title, + const std::string &description, + bool sendNotification) +{ + auto &quest = mQuestlog[id]; + quest.id = id; + quest.state = state; + quest.title = title; + quest.description = description; + + markQuestAsModified(&quest, sendNotification); +} + +void CharacterComponent::setQuestlogState(unsigned id, + QuestState state, + bool sendNotification) +{ + auto &quest = mQuestlog[id]; + quest.id = id; + quest.state = state; + + markQuestAsModified(&quest, sendNotification); +} + +void CharacterComponent::setQuestlogTitle(unsigned id, + const std::string &title, + bool sendNotification) +{ + auto &quest = mQuestlog[id]; + quest.id = id; + quest.title = title; + + markQuestAsModified(&quest, sendNotification); +} + +void CharacterComponent::setQuestlogDescription(unsigned id, + const std::string &description, + bool sendNotification) +{ + auto &quest = mQuestlog[id]; + quest.id = id; + quest.description = description; + + markQuestAsModified(&quest, sendNotification); } diff --git a/src/game-server/charactercomponent.h b/src/game-server/charactercomponent.h index fe3f028d..d43f0fe4 100644 --- a/src/game-server/charactercomponent.h +++ b/src/game-server/charactercomponent.h @@ -47,6 +47,26 @@ class MessageOut; class Point; class Trade; +enum QuestState +{ + QUEST_OPEN = 0, + QUEST_FINISHED, + QUEST_FAILED, +}; + +struct QuestInfo +{ + QuestInfo() + : id(0) + , state(QUEST_OPEN) + {} + + unsigned id; + QuestState state; + std::string title; + std::string description; +}; + /** * The representation of a player's character in the game world. */ @@ -278,6 +298,19 @@ class CharacterComponent : public Component void markAllInfoAsChanged(Entity &entity); + void setQuestlog(unsigned id, QuestState state, + const std::string &title, + const std::string &description, + bool sendNotification = false); + void setQuestlogState(unsigned id, QuestState state, + bool sendNotification = false); + void setQuestlogTitle(unsigned id, + const std::string &title, + bool sendNotification = false); + void setQuestlogDescription(unsigned id, + const std::string &description, + bool sendNotification = false); + sigc::signal signal_disconnected; void serialize(Entity &entity, MessageOut &msg); @@ -294,6 +327,10 @@ class CharacterComponent : public Component void sendAbilityUpdate(Entity &entity); void sendAbilityCooldownUpdate(Entity &entity); void sendAttributePointsStatus(Entity &entity); + void sendQuestUpdate(); + + void markQuestAsModified(const QuestInfo *quest, bool sendNotification); + void markAllQuestsAsModified(); enum TransactionType { TRANS_NONE, TRANS_TRADE, TRANS_BUYSELL }; @@ -313,8 +350,8 @@ class CharacterComponent : public Component /** Attributes modified since last update. */ std::set mModifiedAttributes; - std::set mModifiedAbilities; + std::map mModifiedQuests; int mDatabaseID; /**< Character's database ID. */ unsigned char mHairStyle; /**< Hair Style of the character. */ @@ -335,6 +372,8 @@ class CharacterComponent : public Component Timeout mMuteTimeout; /**< Time until the character is no longer muted */ + std::map mQuestlog; + Entity *mBaseEntity; /**< The entity this component is part of this is ONLY required to allow using the serialization routine without many diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index af666bb8..60b3e89c 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -2345,9 +2345,9 @@ static int entity_use_ability(lua_State *s) return 1; } -/** LUA set_questlog_status (being) - * entity:set_questlog_status(int id, int state, string name, - * string description[, bool notify = true]) +/** LUA set_questlog (being) + * entity:set_questlog(int id, int state, string name, + * string description[, bool notify = true]) ** * Sets the questlog status. * @@ -2358,28 +2358,18 @@ static int entity_use_ability(lua_State *s) * * `QUEST_FINISHED` * * `QUEST_FAILED` */ -static int set_questlog_status(lua_State *s) +static int set_questlog(lua_State *s) { const Entity *character = checkCharacter(s, 1); const int questId = luaL_checkinteger(s, 2); - const int questState = luaL_checkinteger(s, 3); + const QuestState questState = (QuestState)luaL_checkinteger(s, 3); const char *questTitle = luaL_checkstring(s, 4); const char *questDescription = luaL_checkstring(s, 5); const bool questNotification = checkOptionalBool(s, 6, true); - MessageOut out(GPMSG_QUESTLOG_STATUS); - out.writeInt16(questId); - int flags = QUESTLOG_UPDATE_STATE | - QUESTLOG_UPDATE_TITLE | - QUESTLOG_UPDATE_DESCRIPTION; - if (questNotification) - flags |= QUESTLOG_SHOW_NOTIFICATION; - out.writeInt8(flags); - out.writeInt8(questState); - out.writeString(questTitle); - out.writeString(questDescription); - - character->getComponent()->getClient()->send(out); + auto *characterComponent = character->getComponent(); + characterComponent->setQuestlog(questId, questState, questTitle, + questDescription, questNotification); return 0; } @@ -2399,16 +2389,11 @@ static int set_questlog_state(lua_State *s) { const Entity *character = checkCharacter(s, 1); const int questId = luaL_checkinteger(s, 2); - const int questState = luaL_checkinteger(s, 3); + const QuestState questState = (QuestState)luaL_checkinteger(s, 3); const bool questNotification = checkOptionalBool(s, 4, true); - MessageOut out(GPMSG_QUESTLOG_STATUS); - out.writeInt16(questId); - out.writeInt8(QUESTLOG_UPDATE_STATE | - questNotification ? QUESTLOG_SHOW_NOTIFICATION : 0); - out.writeInt8(questState); - - character->getComponent()->getClient()->send(out); + auto *characterComponent = character->getComponent(); + characterComponent->setQuestlogState(questId, questState, questNotification); return 0; } @@ -2426,13 +2411,8 @@ static int set_questlog_title(lua_State *s) const char *questTitle = luaL_checkstring(s, 3); const bool questNotification = checkOptionalBool(s, 4, true); - MessageOut out(GPMSG_QUESTLOG_STATUS); - out.writeInt16(questId); - out.writeInt8(QUESTLOG_UPDATE_TITLE | - questNotification ? QUESTLOG_SHOW_NOTIFICATION : 0); - out.writeString(questTitle); - - character->getComponent()->getClient()->send(out); + auto *characterComponent = character->getComponent(); + characterComponent->setQuestlogTitle(questId, questTitle, questNotification); return 0; } @@ -2450,13 +2430,8 @@ static int set_questlog_description(lua_State *s) const char *questDescription = luaL_checkstring(s, 3); const bool questNotification = checkOptionalBool(s, 4, true); - MessageOut out(GPMSG_QUESTLOG_STATUS); - out.writeInt16(questId); - out.writeInt8(QUESTLOG_UPDATE_DESCRIPTION | - questNotification ? QUESTLOG_SHOW_NOTIFICATION : 0); - out.writeString(questDescription); - - character->getComponent()->getClient()->send(out); + auto *characterComponent = character->getComponent(); + characterComponent->setQuestlogDescription(questId, questDescription, questNotification); return 0; } @@ -3553,7 +3528,7 @@ LuaScript::LuaScript(): { "status_time", entity_get_status_time }, { "set_status_time", entity_set_status_time }, { "add_hit_taken", entity_add_hit_taken }, - { "set_questlog_status", set_questlog_status }, + { "set_questlog", set_questlog }, { "set_questlog_state", set_questlog_state }, { "set_questlog_title", set_questlog_title }, { "set_questlog_description", set_questlog_description }, diff --git a/src/sql/mysql/createTables.sql b/src/sql/mysql/createTables.sql index 763a7191..54594bd6 100644 --- a/src/sql/mysql/createTables.sql +++ b/src/sql/mysql/createTables.sql @@ -402,11 +402,26 @@ CREATE TABLE IF NOT EXISTS `mana_transactions` ( ) AUTO_INCREMENT=0 ; +CREATE TABLE IF NOT EXISTS `mana_questlog` ( + `char_id` int(10) unsigned NOT NULL, + `quest_id` INTEGER NOT NULL, + `quest_state` INTEGER NOT NULL, + `quest_title` TEXT NOT NULL, + `quest_description` TEXT NOT NULL, + -- + + PRIMARY KEY (`char_id`, `quest_id`) + FOREIGN KEY (`char_id`) + REFERENCES `mana_characters` (`id`) + ON DELETE CASCADE +) ENGINE=InnoDB +DEFAULT CHARSET=utf8 ; + -- initial world states and database version INSERT INTO mana_world_states VALUES('accountserver_startup',-1,'0', NOW()); INSERT INTO mana_world_states VALUES('accountserver_version',-1,'0', NOW()); -INSERT INTO mana_world_states VALUES('database_version', -1,'25', NOW()); +INSERT INTO mana_world_states VALUES('database_version', -1,'26', NOW()); -- all known transaction codes diff --git a/src/sql/mysql/updates/update_25_to_26.sql b/src/sql/mysql/updates/update_25_to_26.sql new file mode 100644 index 00000000..6bed2e6b --- /dev/null +++ b/src/sql/mysql/updates/update_25_to_26.sql @@ -0,0 +1,24 @@ +START TRANSACTION; + +CREATE TABLE IF NOT EXISTS `mana_questlog` ( + `char_id` int(10) unsigned NOT NULL, + `quest_id` INTEGER NOT NULL, + `quest_state` INTEGER NOT NULL, + `quest_title` TEXT NOT NULL, + `quest_description` TEXT NOT NULL, + -- + + PRIMARY KEY (`char_id`, `quest_id`) + FOREIGN KEY (`char_id`) + REFERENCES `mana_characters` (`id`) + ON DELETE CASCADE +) ENGINE=InnoDB +DEFAULT CHARSET=utf8 ; + +-- Update database version. +UPDATE mana_world_states + SET value = '26', + moddate = UNIX_TIMESTAMP() + WHERE state_name = 'database_version'; + +COMMIT; diff --git a/src/sql/sqlite/createTables.sql b/src/sql/sqlite/createTables.sql index 95dc5a23..437a0546 100644 --- a/src/sql/sqlite/createTables.sql +++ b/src/sql/sqlite/createTables.sql @@ -110,7 +110,7 @@ CREATE TABLE mana_char_abilities ( char_id INTEGER NOT NULL, ability_id INTEGER NOT NULL, - PRIMARY KEY (char_id, ability_id), + PRIMARY KEY (char_id, ability_id), FOREIGN KEY (char_id) REFERENCES mana_characters(id) ); @@ -347,6 +347,21 @@ CREATE TABLE mana_online_list FOREIGN KEY (char_id) REFERENCES mana_characters(id) ); +----------------------------------------------------------------------------- + +CREATE TABLE mana_questlog +( + char_id INTEGER NOT NULL, + quest_id INTEGER NOT NULL, + quest_state INTEGER NOT NULL, + quest_title TEXT NOT NULL, + quest_description TEXT NOT NULL, + -- + FOREIGN KEY (char_id) REFERENCES mana_characters(id) +); +CREATE INDEX mana_questlog_char_id ON mana_questlog ( char_id ); +CREATE INDEX mana_questlog_quest_id ON mana_questlog ( quest_id ); + ----------------------------------------------------------------------------- -- Views ----------------------------------------------------------------------------- @@ -396,7 +411,7 @@ AS INSERT INTO mana_world_states VALUES('accountserver_startup',-1,'0', strftime('%s','now')); INSERT INTO mana_world_states VALUES('accountserver_version',-1,'0', strftime('%s','now')); -INSERT INTO mana_world_states VALUES('database_version', -1,'25', strftime('%s','now')); +INSERT INTO mana_world_states VALUES('database_version', -1,'26', strftime('%s','now')); -- all known transaction codes diff --git a/src/sql/sqlite/updates/update_25_to_26.sql b/src/sql/sqlite/updates/update_25_to_26.sql new file mode 100644 index 00000000..fa60031e --- /dev/null +++ b/src/sql/sqlite/updates/update_25_to_26.sql @@ -0,0 +1,22 @@ +BEGIN; + +CREATE TABLE mana_questlog +( + char_id INTEGER NOT NULL, + quest_id INTEGER NOT NULL, + quest_state INTEGER NOT NULL, + quest_title TEXT NOT NULL, + quest_description TEXT NOT NULL, + -- + FOREIGN KEY (char_id) REFERENCES mana_characters(id) +); +CREATE INDEX mana_questlog_char_id ON mana_questlog ( char_id ); +CREATE INDEX mana_questlog_quest_id ON mana_questlog ( quest_id ); + +-- Update the database version, and set date of update +UPDATE mana_world_states + SET value = '26', + moddate = strftime('%s','now') + WHERE state_name = 'database_version'; + +END; -- cgit v1.2.3-60-g2f50