summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/account-server/dalstorage.cpp56
-rw-r--r--src/account-server/dalstorage.hpp11
-rw-r--r--src/account-server/dalstoragesql.hpp24
-rw-r--r--src/account-server/serverhandler.cpp21
-rw-r--r--src/account-server/storage.hpp11
-rw-r--r--src/defines.h3
-rw-r--r--src/game-server/accountconnection.cpp31
-rw-r--r--src/game-server/accountconnection.hpp13
-rw-r--r--src/game-server/character.hpp7
-rw-r--r--src/game-server/quest.cpp138
-rw-r--r--src/game-server/quest.hpp63
-rw-r--r--src/scripting/lua.cpp71
13 files changed, 440 insertions, 11 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 6bc88be8..a2d22615 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -114,6 +114,8 @@ tmwserv_game_SOURCES = \
game-server/npc.hpp \
game-server/npc.cpp \
game-server/object.hpp \
+ game-server/quest.hpp \
+ game-server/quest.cpp \
game-server/spawnarea.hpp \
game-server/spawnarea.cpp \
game-server/state.hpp \
diff --git a/src/account-server/dalstorage.cpp b/src/account-server/dalstorage.cpp
index e9887fad..612d0565 100644
--- a/src/account-server/dalstorage.cpp
+++ b/src/account-server/dalstorage.cpp
@@ -184,6 +184,7 @@ void DALStorage::open()
createTable(CHANNELS_TBL_NAME, SQL_CHANNELS_TABLE);
createTable(GUILDS_TBL_NAME, SQL_GUILDS_TABLE);
createTable(GUILD_MEMBERS_TBL_NAME, SQL_GUILD_MEMBERS_TABLE);
+ createTable(QUESTS_TBL_NAME, SQL_QUESTS_TABLE);
}
catch (const DbConnectionFailure& e) {
LOG_ERROR("(DALStorage::open #1) Unable to connect to the database: "
@@ -1231,3 +1232,58 @@ std::list<Guild*> DALStorage::getGuildList()
return guilds;
}
+
+std::string DALStorage::getQuestVar(int id, std::string const &name)
+{
+ // connect to the database (if not connected yet).
+ open();
+
+ using namespace dal;
+
+ try
+ {
+ std::ostringstream query;
+ query << "select value from " << QUESTS_TBL_NAME
+ << " where owner_id = '" << id << "' and name = '"
+ << name << "';";
+ RecordSet const &info = mDb->execSql(query.str());
+
+ if (!info.isEmpty()) return info(0, 0);
+ }
+ catch (DbSqlQueryExecFailure const &e)
+ {
+ LOG_ERROR("(DALStorage::getQuestVar) SQL query failure: " << e.what());
+ }
+
+ return std::string();
+}
+
+void DALStorage::setQuestVar(int id, std::string const &name,
+ std::string const &value)
+{
+ // connect to the database (if not connected yet).
+ open();
+
+ using namespace dal;
+
+ try
+ {
+ std::ostringstream query1;
+ query1 << "delete from " << QUESTS_TBL_NAME
+ << " where owner_id = '" << id << "' and name = '"
+ << name << "';";
+ mDb->execSql(query1.str());
+
+ if (value.empty()) return;
+
+ std::ostringstream query2;
+ query2 << "insert into " << QUESTS_TBL_NAME
+ << " (owner_id, name, value) values ('"
+ << id << "', '" << name << "', '" << value << "');";
+ mDb->execSql(query2.str());
+ }
+ catch (DbSqlQueryExecFailure const &e)
+ {
+ LOG_ERROR("(DALStorage::setQuestVar) SQL query failure: " << e.what());
+ }
+}
diff --git a/src/account-server/dalstorage.hpp b/src/account-server/dalstorage.hpp
index 00a5b69b..c25b3fc2 100644
--- a/src/account-server/dalstorage.hpp
+++ b/src/account-server/dalstorage.hpp
@@ -203,6 +203,17 @@ class DALStorage: public Storage
void flushAll();
void flush(AccountPtr const &);
+ /**
+ * Gets the value of a quest variable.
+ */
+ std::string getQuestVar(int id, std::string const &);
+
+ /**
+ * Sets the value of a quest variable.
+ */
+ void setQuestVar(int id, std::string const &, std::string const &);
+
+
private:
/**
* Constructor.
diff --git a/src/account-server/dalstoragesql.hpp b/src/account-server/dalstoragesql.hpp
index 81ceaf46..c4d8ad7d 100644
--- a/src/account-server/dalstoragesql.hpp
+++ b/src/account-server/dalstoragesql.hpp
@@ -64,13 +64,6 @@
/**
* TABLE: tmw_accounts.
- *
- * Notes:
- * - the user levels are:
- * 0: normal user
- * 1: moderator (has medium level rights)
- * 2: administrator (i am god :))
- * - the 'banned' field contains the UNIX time of unban (default = 0)
*/
static char const *ACCOUNTS_TBL_NAME = "tmw_accounts";
static char const *SQL_ACCOUNTS_TABLE =
@@ -294,5 +287,22 @@ static char const *SQL_GUILD_MEMBERS_TABLE =
#endif
");";
+/**
+ * TABLE: tmw_quests.
+ */
+static char const *QUESTS_TBL_NAME = "tmw_quests";
+static char const *SQL_QUESTS_TABLE =
+ "CREATE TABLE tmw_quests ("
+#if defined (MYSQL_SUPPORT)
+#error "Missing definition. Please fill the blanks."
+#elif defined (SQLITE_SUPPORT)
+ "owner_id INTEGER NOT NULL,"
+ "name TEXT NOT NULL,"
+ "value TEXT NOT NULL,"
+ "FOREIGN KEY (owner_id) REFERENCES tmw_characters(id)"
+#elif defined (POSTGRESQL_SUPPORT)
+#error "Missing definition. Please fill the blanks."
+#endif
+ ");";
#endif // _TMWSERV_DALSTORAGE_SQL_H_
diff --git a/src/account-server/serverhandler.cpp b/src/account-server/serverhandler.cpp
index 9e53356f..6c428e4a 100644
--- a/src/account-server/serverhandler.cpp
+++ b/src/account-server/serverhandler.cpp
@@ -189,6 +189,27 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg)
} break;
+ case GAMSG_GET_QUEST:
+ {
+ int id = msg.readLong();
+ std::string name = msg.readString();
+ Storage &store = Storage::instance("tmw");
+ std::string value = store.getQuestVar(id, name);
+ result.writeShort(AGMSG_GET_QUEST_RESPONSE);
+ result.writeLong(id);
+ result.writeString(name);
+ result.writeString(value);
+ } break;
+
+ case GAMSG_SET_QUEST:
+ {
+ int id = msg.readLong();
+ std::string name = msg.readString();
+ std::string value = msg.readString();
+ Storage &store = Storage::instance("tmw");
+ store.setQuestVar(id, name, value);
+ } break;
+
#if 0
case GAMSG_GUILD_CREATE:
{
diff --git a/src/account-server/storage.hpp b/src/account-server/storage.hpp
index 55a82abf..d436fa1f 100644
--- a/src/account-server/storage.hpp
+++ b/src/account-server/storage.hpp
@@ -319,6 +319,17 @@ class Storage
*/
virtual void flush(AccountPtr const &account) = 0;
+ /**
+ * Gets the value of a quest variable.
+ */
+ virtual std::string getQuestVar(int id, std::string const &) = 0;
+
+ /**
+ * Sets the value of a quest variable.
+ */
+ virtual void setQuestVar(int id, std::string const &,
+ std::string const &) = 0;
+
protected:
/**
diff --git a/src/defines.h b/src/defines.h
index 03eb60d0..fff197c5 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -235,6 +235,9 @@ enum {
GAMSG_REDIRECT = 0x0530, // L id
AGMSG_REDIRECT_RESPONSE = 0x0531, // L id, B*32 token, S game address, W game port
GAMSG_PLAYER_RECONNECT = 0x0532, // L id, B*32 token
+ GAMSG_SET_QUEST = 0x0540, // L id, S name, S value
+ GAMSG_GET_QUEST = 0x0541, // L id, S name
+ AGMSG_GET_QUEST_RESPONSE = 0x0542, // L id S name, S value
#if 0
GAMSG_GUILD_CREATE = 0x0550, // S name
diff --git a/src/game-server/accountconnection.cpp b/src/game-server/accountconnection.cpp
index 8a03bb24..b351890b 100644
--- a/src/game-server/accountconnection.cpp
+++ b/src/game-server/accountconnection.cpp
@@ -29,6 +29,7 @@
#include "game-server/map.hpp"
#include "game-server/mapcomposite.hpp"
#include "game-server/mapmanager.hpp"
+#include "game-server/quest.hpp"
#include "game-server/state.hpp"
#include "net/messagein.hpp"
#include "net/messageout.hpp"
@@ -94,6 +95,14 @@ void AccountConnection::processMessage(MessageIn &msg)
gameHandler->completeServerChange(id, token, address, port);
} break;
+ case AGMSG_GET_QUEST_RESPONSE:
+ {
+ int id = msg.readLong();
+ std::string name = msg.readString();
+ std::string value = msg.readString();
+ recoveredQuestVar(id, name, value);
+ } break;
+
// The client should directly talk with the chat server and not go through the game server.
#if 0
case AGMSG_GUILD_CREATE_RESPONSE:
@@ -216,15 +225,33 @@ void AccountConnection::processMessage(MessageIn &msg)
}
}
-void AccountConnection::playerReconnectAccount(int id, const std::string magic_token)
+void AccountConnection::playerReconnectAccount(int id, std::string const &magic_token)
{
- LOG_INFO("Send GAMSG_PLAYER_RECONNECT.");
+ LOG_DEBUG("Send GAMSG_PLAYER_RECONNECT.");
MessageOut msg(GAMSG_PLAYER_RECONNECT);
msg.writeLong(id);
msg.writeString(magic_token, MAGIC_TOKEN_LENGTH);
send(msg);
}
+void AccountConnection::requestQuestVar(Character *ch, std::string const &name)
+{
+ MessageOut msg(GAMSG_GET_QUEST);
+ msg.writeLong(ch->getDatabaseID());
+ msg.writeString(name);
+ send(msg);
+}
+
+void AccountConnection::updateQuestVar(Character *ch, std::string const &name,
+ std::string const &value)
+{
+ MessageOut msg(GAMSG_SET_QUEST);
+ msg.writeLong(ch->getDatabaseID());
+ msg.writeString(name);
+ msg.writeString(value);
+ send(msg);
+}
+
#if 0
void AccountConnection::playerCreateGuild(int id, const std::string &guildName)
{
diff --git a/src/game-server/accountconnection.hpp b/src/game-server/accountconnection.hpp
index ba9abe06..9899db14 100644
--- a/src/game-server/accountconnection.hpp
+++ b/src/game-server/accountconnection.hpp
@@ -48,7 +48,18 @@ class AccountConnection : public Connection
/**
* Prepares the account server for a reconnecting player
*/
- void playerReconnectAccount(int id, const std::string magic_token);
+ void playerReconnectAccount(int id, std::string const &magic_token);
+
+ /**
+ * Requests the value of a quest variable from the database.
+ */
+ void requestQuestVar(Character *, std::string const &);
+
+ /**
+ * Pushes a new quest value to the database.
+ */
+ void updateQuestVar(Character *, std::string const &name,
+ std::string const &value);
#if 0
/**
diff --git a/src/game-server/character.hpp b/src/game-server/character.hpp
index 2cf4500c..eff99286 100644
--- a/src/game-server/character.hpp
+++ b/src/game-server/character.hpp
@@ -23,6 +23,7 @@
#ifndef _TMWSERV_CHARACTER_HPP_
#define _TMWSERV_CHARACTER_HPP_
+#include <map>
#include <string>
#include <vector>
@@ -207,6 +208,12 @@ class Character : public Being
*/
void modifiedAttribute(int);
+ /**
+ * Associative array containing all the quest variables known by the
+ * server.
+ */
+ std::map< std::string, std::string > questCache;
+
private:
Character(Character const &);
Character &operator=(Character const &);
diff --git a/src/game-server/quest.cpp b/src/game-server/quest.cpp
new file mode 100644
index 00000000..f6675127
--- /dev/null
+++ b/src/game-server/quest.cpp
@@ -0,0 +1,138 @@
+/*
+ * The Mana World Server
+ * Copyright 2007 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+#include <algorithm>
+#include <list>
+#include <map>
+#include <string>
+
+#include "game-server/quest.hpp"
+
+#include "defines.h"
+#include "game-server/accountconnection.hpp"
+#include "game-server/character.hpp"
+#include "game-server/deathlistener.hpp"
+#include "utils/logger.h"
+
+typedef std::list< QuestCallback > QuestCallbacks;
+typedef std::map< std::string, QuestCallbacks > PendingVariables;
+
+struct PendingQuest
+{
+ Character *character;
+ PendingVariables variables;
+};
+
+typedef std::map< int, PendingQuest > PendingQuests;
+
+static PendingQuests pendingQuests;
+
+bool getQuestVar(Character *ch, std::string const &name, std::string &value)
+{
+ std::map< std::string, std::string >::iterator
+ i = ch->questCache.find(name);
+ if (i == ch->questCache.end()) return false;
+ value = i->second;
+ return true;
+}
+
+void setQuestVar(Character *ch, std::string const &name,
+ std::string const &value)
+{
+ std::map< std::string, std::string >::iterator
+ i = ch->questCache.lower_bound(name);
+ if (i == ch->questCache.end() || i->first != name)
+ {
+ ch->questCache.insert(i, std::make_pair(name, value));
+ }
+ else if (i->second == value)
+ {
+ return;
+ }
+ else
+ {
+ i->second = value;
+ }
+ accountHandler->updateQuestVar(ch, name, value);
+}
+
+/**
+ * Listener for deleting related quests when a character disappears.
+ */
+struct QuestDeathListener: DeathListener
+{
+ void deleted(Being *b)
+ {
+ int id = static_cast< Character * >(b)->getDatabaseID();
+ pendingQuests.erase(id);
+ }
+};
+
+static QuestDeathListener questDeathListener;
+
+void recoverQuestVar(Character *ch, std::string const &name,
+ QuestCallback const &f)
+{
+ int id = ch->getDatabaseID();
+ PendingQuests::iterator i = pendingQuests.lower_bound(id);
+ if (i == pendingQuests.end() || i->first != id)
+ {
+ i = pendingQuests.insert(i, std::make_pair(id, PendingQuest()));
+ i->second.character = ch;
+ /* Register a listener, because we cannot afford to get invalid
+ pointers, when we finally recover the variable. */
+ ch->addDeathListener(&questDeathListener);
+ }
+ i->second.variables[name].push_back(f);
+ accountHandler->requestQuestVar(ch, name);
+}
+
+void recoveredQuestVar(int id, std::string const &name,
+ std::string const &value)
+{
+ PendingQuests::iterator i = pendingQuests.find(id);
+ if (i == pendingQuests.end()) return;
+ PendingVariables &variables = i->second.variables;
+ PendingVariables::iterator j = variables.find(name);
+ if (j == variables.end())
+ {
+ LOG_ERROR("Account server recovered an unexpected quest variable.");
+ return;
+ }
+
+ Character *ch = i->second.character;
+ ch->questCache[name] = value;
+
+ // Call the registered callbacks.
+ for (QuestCallbacks::const_iterator k = j->second.begin(),
+ k_end = j->second.end(); k != k_end; ++k)
+ {
+ k->handler(ch, name, value, k->data);
+ }
+
+ variables.erase(j);
+ if (variables.empty())
+ {
+ pendingQuests.erase(i);
+ }
+}
diff --git a/src/game-server/quest.hpp b/src/game-server/quest.hpp
new file mode 100644
index 00000000..720d79ae
--- /dev/null
+++ b/src/game-server/quest.hpp
@@ -0,0 +1,63 @@
+/*
+ * The Mana World Server
+ * Copyright 2007 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * The Mana World is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+#ifndef _TMWSERV_GAMESERVER_QUEST_
+#define _TMWSERV_GAMESERVER_QUEST_
+
+#include <string>
+
+class Character;
+
+struct QuestCallback
+{
+ void (*handler)(Character *, std::string const &name,
+ std::string const &value, void *data);
+ void *data;
+};
+
+/**
+ * Gets the value associated to a quest variable.
+ * @return false if no value was in cache.
+ */
+bool getQuestVar(Character *, std::string const &name, std::string &value);
+
+/**
+ * Sets the value associated to a quest variable.
+ */
+void setQuestVar(Character *, std::string const &name,
+ std::string const &value);
+
+/**
+ * Starts the recovery of a variable and returns immediatly. The callback will
+ * be called once the value has been recovered.
+ */
+void recoverQuestVar(Character *, std::string const &name,
+ QuestCallback const &);
+
+/**
+ * Called by the handler of the account server when a value is received.
+ */
+void recoveredQuestVar(int id, std::string const &name,
+ std::string const &value);
+
+#endif
diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp
index 50d36349..3eb3ecff 100644
--- a/src/scripting/lua.cpp
+++ b/src/scripting/lua.cpp
@@ -37,6 +37,7 @@ extern "C" {
#include "game-server/itemmanager.hpp"
#include "game-server/mapmanager.hpp"
#include "game-server/npc.hpp"
+#include "game-server/quest.hpp"
#include "game-server/state.hpp"
#include "net/messageout.hpp"
#include "scripting/script.hpp"
@@ -68,6 +69,9 @@ class LuaScript: public Script
int execute();
+ static void getQuestCallback(Character *, std::string const &,
+ std::string const &, void *);
+
private:
lua_State *mState;
@@ -320,7 +324,7 @@ static int LuaChr_InvCount(lua_State *s)
}
/**
- * Callback for trading between a a player and an NPC.
+ * Callback for trading between a player and an NPC.
* tmw.npc_trade(npc, character, bool sell, table items)
*/
static int LuaNpc_Trade(lua_State *s)
@@ -362,6 +366,69 @@ static int LuaNpc_Trade(lua_State *s)
return 0;
}
+/**
+ * Called when the server has recovered the value of a quest variable.
+ */
+void LuaScript::getQuestCallback(Character *q, std::string const &name,
+ std::string const &value, void *data)
+{
+ LuaScript *s = static_cast< LuaScript * >(data);
+ assert(s->nbArgs == -1);
+ lua_getglobal(s->mState, "quest_reply");
+ lua_pushlightuserdata(s->mState, q);
+ lua_pushstring(s->mState, name.c_str());
+ lua_pushstring(s->mState, value.c_str());
+ s->nbArgs = 3;
+ s->execute();
+}
+
+/**
+ * Callback for getting a quest variable. Starts a recovery and returns
+ * immediatly, if the variable is not known yet.
+ * tmw.chr_get_chest(character, string): nil or string
+ */
+static int LuaChr_GetQuest(lua_State *s)
+{
+ Character *q = getCharacter(s, 1);
+ char const *m = lua_tostring(s, 2);
+ if (!m || m[0] == 0)
+ {
+ LOG_WARN("LuaChr_GetQuest called with incorrect parameters.");
+ return 0;
+ }
+ std::string value, name = m;
+ bool res = getQuestVar(q, name, value);
+ if (res)
+ {
+ lua_pushstring(s, value.c_str());
+ return 1;
+ }
+ lua_pushlightuserdata(s, (void *)&registryKey);
+ lua_gettable(s, LUA_REGISTRYINDEX);
+ Script *t = static_cast<Script *>(lua_touserdata(s, -1));
+ QuestCallback f = { &LuaScript::getQuestCallback, t };
+ recoverQuestVar(q, name, f);
+ return 0;
+}
+
+/**
+ * Callback for setting a quest variable.
+ * tmw.chr_set_chest(character, string, string)
+ */
+static int LuaChr_SetQuest(lua_State *s)
+{
+ Character *q = getCharacter(s, 1);
+ char const *m = lua_tostring(s, 2);
+ char const *n = lua_tostring(s, 3);
+ if (!m || !n || m[0] == 0)
+ {
+ LOG_WARN("LuaChr_SetQuest called with incorrect parameters.");
+ return 0;
+ }
+ setQuestVar(q, m, n);
+ return 0;
+}
+
LuaScript::LuaScript():
nbArgs(-1)
{
@@ -377,6 +444,8 @@ LuaScript::LuaScript():
{ "chr_warp", &LuaChr_Warp },
{ "chr_inv_change", &LuaChr_InvChange },
{ "chr_inv_count", &LuaChr_InvCount },
+ { "chr_get_quest", &LuaChr_GetQuest },
+ { "chr_set_quest", &LuaChr_SetQuest },
{ NULL, NULL }
};
luaL_register(mState, "tmw", callbacks);