Server upload

RD Core / Commit #2

Commit Server upload

Alex Alex committed 18/10/2026 23:38 main Patch
1,106 files +223,404 -0
A src/cylinder.h +265-0 View file
@@ -0,0 +1,265 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_CYLINDER_H_54BBCEB2A5B7415DAD837E4D58115150
21+#define FS_CYLINDER_H_54BBCEB2A5B7415DAD837E4D58115150
22+
23+#include "enums.h"
24+#include "thing.h"
25+
26+class Item;
27+class Creature;
28+
29+static constexpr int32_t INDEX_WHEREEVER = -1;
30+
31+enum cylinderflags_t {
32+ FLAG_NOLIMIT = 1 << 0, // Bypass limits like capacity/container limits,
33+ // blocking items/creatures etc.
34+ FLAG_IGNOREBLOCKITEM = 1 << 1, // Bypass movable blocking item checks
35+ FLAG_IGNOREBLOCKCREATURE = 1 << 2, // Bypass creature checks
36+ FLAG_CHILDISOWNER =
37+ 1 << 3, // Used by containers to query capacity of the carrier (player)
38+ FLAG_PATHFINDING =
39+ 1 << 4, // An additional check is done for floor changing/teleport items
40+ FLAG_IGNOREFIELDDAMAGE = 1 << 5, // Bypass field damage checks
41+ FLAG_IGNORENOTMOVEABLE = 1 << 6, // Bypass check for mobility
42+ FLAG_IGNOREAUTOSTACK =
43+ 1 << 7, // queryDestination will not try to stack items together
44+};
45+
46+enum cylinderlink_t {
47+ LINK_OWNER,
48+ LINK_PARENT,
49+ LINK_TOPPARENT,
50+ LINK_NEAR,
51+};
52+
53+class Cylinder : virtual public Thing {
54+public:
55+ /**
56+ * Query if the cylinder can add an object
57+ * \param index points to the destination index (inventory slot/container
58+ * position) -1 is a internal value and means add to a empty position, with no
59+ * destItem
60+ * \param thing the object to move/add
61+ * \param count is the amount that we want to move/add
62+ * \param flags if FLAG_CHILDISOWNER if set the query is from a child-cylinder
63+ * (check cap etc.) if FLAG_NOLIMIT is set blocking items/container limits is
64+ * ignored
65+ * \param actor the creature trying to add the thing
66+ * \returns ReturnValue holds the return value
67+ */
68+ virtual ReturnValue queryAdd(int32_t index, const Thing &thing,
69+ uint32_t count, uint32_t flags,
70+ Creature *actor = nullptr) const = 0;
71+
72+ /**
73+ * Query the cylinder how much it can accept
74+ * \param index points to the destination index (inventory slot/container
75+ * position) -1 is a internal value and means add to a empty position, with no
76+ * destItem
77+ * \param thing the object to move/add
78+ * \param count is the amount that we want to move/add
79+ * \param maxQueryCount is the max amount that the cylinder can accept
80+ * \param flags optional flags to modify the default behaviour
81+ * \returns ReturnValue holds the return value
82+ */
83+ virtual ReturnValue queryMaxCount(int32_t index, const Thing &thing,
84+ uint32_t count, uint32_t &maxQueryCount,
85+ uint32_t flags) const = 0;
86+
87+ /**
88+ * Query if the cylinder can remove an object
89+ * \param thing the object to move/remove
90+ * \param count is the amount that we want to remove
91+ * \param flags optional flags to modify the default behaviour
92+ * \returns ReturnValue holds the return value
93+ */
94+ virtual ReturnValue queryRemove(const Thing &thing, uint32_t count,
95+ uint32_t flags) const = 0;
96+
97+ /**
98+ * Query the destination cylinder
99+ * \param index points to the destination index (inventory slot/container
100+ * position), -1 is a internal value and means add to a empty position, with
101+ * no destItem this method can change the index to point to the new cylinder
102+ * index
103+ * \param destItem is the destination object
104+ * \param flags optional flags to modify the default behaviour
105+ * this method can modify the flags
106+ * \returns Cylinder returns the destination cylinder
107+ */
108+ virtual Cylinder *queryDestination(int32_t &index, const Thing &thing,
109+ Item **destItem, uint32_t &flags) = 0;
110+
111+ /**
112+ * Add the object to the cylinder
113+ * \param thing is the object to add
114+ */
115+ virtual void addThing(Thing *thing) = 0;
116+
117+ /**
118+ * Add the object to the cylinder
119+ * \param index points to the destination index (inventory slot/container
120+ * position)
121+ * \param thing is the object to add
122+ */
123+ virtual void addThing(int32_t index, Thing *thing) = 0;
124+
125+ /**
126+ * Update the item count or type for an object
127+ * \param thing is the object to update
128+ * \param itemId is the new item id
129+ * \param count is the new count value
130+ */
131+ virtual void updateThing(Thing *thing, uint16_t itemId, uint32_t count) = 0;
132+
133+ /**
134+ * Replace an object with a new
135+ * \param index is the position to change (inventory slot/container position)
136+ * \param thing is the object to update
137+ */
138+ virtual void replaceThing(uint32_t index, Thing *thing) = 0;
139+
140+ /**
141+ * Remove an object
142+ * \param thing is the object to delete
143+ * \param count is the new count value
144+ */
145+ virtual void removeThing(Thing *thing, uint32_t count) = 0;
146+
147+ /**
148+ * Is sent after an operation (move/add) to update internal values
149+ * \param thing is the object that has been added
150+ * \param index is the objects new index value
151+ * \param link holds the relation the object has to the cylinder
152+ */
153+ virtual void postAddNotification(Thing *thing, const Cylinder *oldParent,
154+ int32_t index,
155+ cylinderlink_t link = LINK_OWNER) = 0;
156+
157+ /**
158+ * Is sent after an operation (move/remove) to update internal values
159+ * \param thing is the object that has been removed
160+ * \param index is the previous index of the removed object
161+ * \param link holds the relation the object has to the cylinder
162+ */
163+ virtual void postRemoveNotification(Thing *thing, const Cylinder *newParent,
164+ int32_t index,
165+ cylinderlink_t link = LINK_OWNER) = 0;
166+
167+ /**
168+ * Gets the index of an object
169+ * \param thing the object to get the index value from
170+ * \returns the index of the object, returns -1 if not found
171+ */
172+ virtual int32_t getThingIndex(const Thing *thing) const;
173+
174+ /**
175+ * Returns the first index
176+ * \returns the first index, if not implemented 0 is returned
177+ */
178+ virtual size_t getFirstIndex() const;
179+
180+ /**
181+ * Returns the last index
182+ * \returns the last index, if not implemented 0 is returned
183+ */
184+ virtual size_t getLastIndex() const;
185+
186+ /**
187+ * Gets the object based on index
188+ * \returns the object, returns nullptr if not found
189+ */
190+ virtual Thing *getThing(size_t index) const;
191+
192+ /**
193+ * Get the amount of items of a certain type
194+ * \param itemId is the item type to the get the count of
195+ * \param subType is the extra type an item can have such as
196+ * charges/fluidtype, -1 means not used
197+ * \returns the amount of items of the asked item type
198+ */
199+ virtual uint32_t getItemTypeCount(uint16_t itemId,
200+ int32_t subType = -1) const;
201+
202+ /**
203+ * Get the amount of items of a all types
204+ * \param countMap a map to put the itemID:count mapping in
205+ * \returns a map mapping item id to count (same as first argument)
206+ */
207+ virtual std::map<uint32_t, uint32_t> &
208+ getAllItemTypeCount(std::map<uint32_t, uint32_t> &countMap) const;
209+
210+ /**
211+ * Adds an object to the cylinder without sending to the client(s)
212+ * \param thing is the object to add
213+ */
214+ virtual void internalAddThing(Thing *thing);
215+
216+ /**
217+ * Adds an object to the cylinder without sending to the client(s)
218+ * \param thing is the object to add
219+ * \param index points to the destination index (inventory slot/container
220+ * position)
221+ */
222+ virtual void internalAddThing(uint32_t index, Thing *thing);
223+
224+ virtual void startDecaying();
225+};
226+
227+class VirtualCylinder final : public Cylinder {
228+public:
229+ static VirtualCylinder *virtualCylinder;
230+
231+ virtual ReturnValue queryAdd(int32_t, const Thing &, uint32_t, uint32_t,
232+ Creature * = nullptr) const override {
233+ return RETURNVALUE_NOTPOSSIBLE;
234+ }
235+ virtual ReturnValue queryMaxCount(int32_t, const Thing &, uint32_t,
236+ uint32_t &, uint32_t) const override {
237+ return RETURNVALUE_NOTPOSSIBLE;
238+ }
239+ virtual ReturnValue queryRemove(const Thing &, uint32_t,
240+ uint32_t) const override {
241+ return RETURNVALUE_NOTPOSSIBLE;
242+ }
243+ virtual Cylinder *queryDestination(int32_t &, const Thing &, Item **,
244+ uint32_t &) override {
245+ return nullptr;
246+ }
247+
248+ virtual void addThing(Thing *) override {}
249+ virtual void addThing(int32_t, Thing *) override {}
250+ virtual void updateThing(Thing *, uint16_t, uint32_t) override {}
251+ virtual void replaceThing(uint32_t, Thing *) override {}
252+ virtual void removeThing(Thing *, uint32_t) override {}
253+
254+ virtual void postAddNotification(Thing *, const Cylinder *, int32_t,
255+ cylinderlink_t = LINK_OWNER) override {}
256+ virtual void postRemoveNotification(Thing *, const Cylinder *, int32_t,
257+ cylinderlink_t = LINK_OWNER) override {}
258+
259+ bool isPushable() const override { return false; }
260+ int32_t getThrowRange() const override { return 1; }
261+ std::string getDescription(int32_t) const override { return {}; }
262+ bool isRemoved() const override { return false; }
263+};
264+
265+#endif
A src/database.cpp +295-0 View file
@@ -0,0 +1,295 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#include "otpch.h"
21+
22+#include "configmanager.h"
23+#include "database.h"
24+
25+#include <mysql/errmsg.h>
26+
27+extern ConfigManager g_config;
28+
29+Database::~Database()
30+{
31+ if (handle != nullptr) {
32+ mysql_close(handle);
33+ }
34+}
35+
36+bool Database::connect()
37+{
38+ // connection handle initialization
39+ handle = mysql_init(nullptr);
40+ if (!handle) {
41+ std::cout << std::endl << "Failed to initialize MySQL connection handle." << std::endl;
42+ return false;
43+ }
44+
45+ // automatic reconnect
46+ bool reconnect = true;
47+ mysql_options(handle, MYSQL_OPT_RECONNECT, &reconnect);
48+
49+ // connects to database
50+ if (!mysql_real_connect(handle, g_config.getString(ConfigManager::MYSQL_HOST).c_str(), g_config.getString(ConfigManager::MYSQL_USER).c_str(), g_config.getString(ConfigManager::MYSQL_PASS).c_str(), g_config.getString(ConfigManager::MYSQL_DB).c_str(), g_config.getNumber(ConfigManager::SQL_PORT), g_config.getString(ConfigManager::MYSQL_SOCK).c_str(), 0)) {
51+ std::cout << std::endl << "MySQL Error Message: " << mysql_error(handle) << std::endl;
52+ return false;
53+ }
54+
55+ DBResult_ptr result = storeQuery("SHOW VARIABLES LIKE 'max_allowed_packet'");
56+ if (result) {
57+ maxPacketSize = result->getNumber<uint64_t>("Value");
58+ }
59+ return true;
60+}
61+
62+bool Database::beginTransaction()
63+{
64+ if (!executeQuery("BEGIN")) {
65+ return false;
66+ }
67+
68+ databaseLock.lock();
69+ return true;
70+}
71+
72+bool Database::rollback()
73+{
74+ if (mysql_rollback(handle) != 0) {
75+ std::cout << "[Error - mysql_rollback] Message: " << mysql_error(handle) << std::endl;
76+ databaseLock.unlock();
77+ return false;
78+ }
79+
80+ databaseLock.unlock();
81+ return true;
82+}
83+
84+bool Database::commit()
85+{
86+ if (mysql_commit(handle) != 0) {
87+ std::cout << "[Error - mysql_commit] Message: " << mysql_error(handle) << std::endl;
88+ databaseLock.unlock();
89+ return false;
90+ }
91+
92+ databaseLock.unlock();
93+ return true;
94+}
95+
96+bool Database::executeQuery(const std::string& query)
97+{
98+ bool success = true;
99+
100+ // executes the query
101+ databaseLock.lock();
102+
103+ while (mysql_real_query(handle, query.c_str(), query.length()) != 0) {
104+ std::cout << "[Error - mysql_real_query] Query: " << query.substr(0, 256) << std::endl << "Message: " << mysql_error(handle) << std::endl;
105+ auto error = mysql_errno(handle);
106+ if (error != CR_SERVER_LOST && error != CR_SERVER_GONE_ERROR && error != CR_CONN_HOST_ERROR && error != 1053/*ER_SERVER_SHUTDOWN*/ && error != CR_CONNECTION_ERROR) {
107+ success = false;
108+ break;
109+ }
110+ std::this_thread::sleep_for(std::chrono::seconds(1));
111+ }
112+
113+ MYSQL_RES* m_res = mysql_store_result(handle);
114+ databaseLock.unlock();
115+
116+ if (m_res) {
117+ mysql_free_result(m_res);
118+ }
119+
120+ return success;
121+}
122+
123+DBResult_ptr Database::storeQuery(const std::string& query)
124+{
125+ databaseLock.lock();
126+
127+ retry:
128+ while (mysql_real_query(handle, query.c_str(), query.length()) != 0) {
129+ std::cout << "[Error - mysql_real_query] Query: " << query << std::endl << "Message: " << mysql_error(handle) << std::endl;
130+ auto error = mysql_errno(handle);
131+ if (error != CR_SERVER_LOST && error != CR_SERVER_GONE_ERROR && error != CR_CONN_HOST_ERROR && error != 1053/*ER_SERVER_SHUTDOWN*/ && error != CR_CONNECTION_ERROR) {
132+ break;
133+ }
134+ std::this_thread::sleep_for(std::chrono::seconds(1));
135+ }
136+
137+ // we should call that every time as someone would call executeQuery('SELECT...')
138+ // as it is described in MySQL manual: "it doesn't hurt" :P
139+ MYSQL_RES* res = mysql_store_result(handle);
140+ if (res == nullptr) {
141+ std::cout << "[Error - mysql_store_result] Query: " << query << std::endl << "Message: " << mysql_error(handle) << std::endl;
142+ auto error = mysql_errno(handle);
143+ if (error != CR_SERVER_LOST && error != CR_SERVER_GONE_ERROR && error != CR_CONN_HOST_ERROR && error != 1053/*ER_SERVER_SHUTDOWN*/ && error != CR_CONNECTION_ERROR) {
144+ databaseLock.unlock();
145+ return nullptr;
146+ }
147+ goto retry;
148+ }
149+ databaseLock.unlock();
150+
151+ // retrieving results of query
152+ DBResult_ptr result = std::make_shared<DBResult>(res);
153+ if (!result->hasNext()) {
154+ return nullptr;
155+ }
156+ return result;
157+}
158+
159+std::string Database::escapeString(const std::string& s) const
160+{
161+ return escapeBlob(s.c_str(), s.length());
162+}
163+
164+std::string Database::escapeBlob(const char* s, uint32_t length) const
165+{
166+ // the worst case is 2n + 1
167+ size_t maxLength = (length * 2) + 1;
168+
169+ std::string escaped;
170+ escaped.reserve(maxLength + 2);
171+ escaped.push_back('\'');
172+
173+ if (length != 0) {
174+ char* output = new char[maxLength];
175+ mysql_real_escape_string(handle, output, s, length);
176+ escaped.append(output);
177+ delete[] output;
178+ }
179+
180+ escaped.push_back('\'');
181+ return escaped;
182+}
183+
184+DBResult::DBResult(MYSQL_RES* res)
185+{
186+ handle = res;
187+
188+ size_t i = 0;
189+
190+ MYSQL_FIELD* field = mysql_fetch_field(handle);
191+ while (field) {
192+ listNames[field->name] = i++;
193+ field = mysql_fetch_field(handle);
194+ }
195+
196+ row = mysql_fetch_row(handle);
197+}
198+
199+DBResult::~DBResult()
200+{
201+ mysql_free_result(handle);
202+}
203+
204+std::string DBResult::getString(const std::string& s) const
205+{
206+ auto it = listNames.find(s);
207+ if (it == listNames.end()) {
208+ std::cout << "[Error - DBResult::getString] Column '" << s << "' does not exist in result set." << std::endl;
209+ return std::string();
210+ }
211+
212+ if (row[it->second] == nullptr) {
213+ return std::string();
214+ }
215+
216+ return std::string(row[it->second]);
217+}
218+
219+const char* DBResult::getStream(const std::string& s, unsigned long& size) const
220+{
221+ auto it = listNames.find(s);
222+ if (it == listNames.end()) {
223+ std::cout << "[Error - DBResult::getStream] Column '" << s << "' doesn't exist in the result set" << std::endl;
224+ size = 0;
225+ return nullptr;
226+ }
227+
228+ if (row[it->second] == nullptr) {
229+ size = 0;
230+ return nullptr;
231+ }
232+
233+ size = mysql_fetch_lengths(handle)[it->second];
234+ return row[it->second];
235+}
236+
237+bool DBResult::hasNext() const
238+{
239+ return row != nullptr;
240+}
241+
242+bool DBResult::next()
243+{
244+ row = mysql_fetch_row(handle);
245+ return row != nullptr;
246+}
247+
248+DBInsert::DBInsert(std::string query) : query(std::move(query))
249+{
250+ this->length = this->query.length();
251+}
252+
253+bool DBInsert::addRow(const std::string& row)
254+{
255+ // adds new row to buffer
256+ const size_t rowLength = row.length();
257+ length += rowLength;
258+ if (length > Database::getInstance().getMaxPacketSize() && !execute()) {
259+ return false;
260+ }
261+
262+ if (values.empty()) {
263+ values.reserve(rowLength + 2);
264+ values.push_back('(');
265+ values.append(row);
266+ values.push_back(')');
267+ } else {
268+ values.reserve(values.length() + rowLength + 3);
269+ values.push_back(',');
270+ values.push_back('(');
271+ values.append(row);
272+ values.push_back(')');
273+ }
274+ return true;
275+}
276+
277+bool DBInsert::addRow(std::ostringstream& row)
278+{
279+ bool ret = addRow(row.str());
280+ row.str(std::string());
281+ return ret;
282+}
283+
284+bool DBInsert::execute()
285+{
286+ if (values.empty()) {
287+ return true;
288+ }
289+
290+ // executes buffer
291+ bool res = Database::getInstance().executeQuery(query + values);
292+ values.clear();
293+ length = query.length();
294+ return res;
295+}
A src/database.h +242-0 View file
@@ -0,0 +1,242 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_DATABASE_H_A484B0CDFDE542838F506DCE3D40C693
21+#define FS_DATABASE_H_A484B0CDFDE542838F506DCE3D40C693
22+
23+#include <boost/lexical_cast.hpp>
24+
25+#include <mysql/mysql.h>
26+
27+class DBResult;
28+using DBResult_ptr = std::shared_ptr<DBResult>;
29+
30+class Database
31+{
32+ public:
33+ Database() = default;
34+ ~Database();
35+
36+ // non-copyable
37+ Database(const Database&) = delete;
38+ Database& operator=(const Database&) = delete;
39+
40+ /**
41+ * Singleton implementation.
42+ *
43+ * @return database connection handler singleton
44+ */
45+ static Database& getInstance()
46+ {
47+ static Database instance;
48+ return instance;
49+ }
50+
51+ /**
52+ * Connects to the database
53+ *
54+ * @return true on successful connection, false on error
55+ */
56+ bool connect();
57+
58+ /**
59+ * Executes command.
60+ *
61+ * Executes query which doesn't generates results (eg. INSERT, UPDATE, DELETE...).
62+ *
63+ * @param query command
64+ * @return true on success, false on error
65+ */
66+ bool executeQuery(const std::string& query);
67+
68+ /**
69+ * Queries database.
70+ *
71+ * Executes query which generates results (mostly SELECT).
72+ *
73+ * @return results object (nullptr on error)
74+ */
75+ DBResult_ptr storeQuery(const std::string& query);
76+
77+ /**
78+ * Escapes string for query.
79+ *
80+ * Prepares string to fit SQL queries including quoting it.
81+ *
82+ * @param s string to be escaped
83+ * @return quoted string
84+ */
85+ std::string escapeString(const std::string& s) const;
86+
87+ /**
88+ * Escapes binary stream for query.
89+ *
90+ * Prepares binary stream to fit SQL queries.
91+ *
92+ * @param s binary stream
93+ * @param length stream length
94+ * @return quoted string
95+ */
96+ std::string escapeBlob(const char* s, uint32_t length) const;
97+
98+ /**
99+ * Retrieve id of last inserted row
100+ *
101+ * @return id on success, 0 if last query did not result on any rows with auto_increment keys
102+ */
103+ uint64_t getLastInsertId() const {
104+ return static_cast<uint64_t>(mysql_insert_id(handle));
105+ }
106+
107+ /**
108+ * Get database engine version
109+ *
110+ * @return the database engine version
111+ */
112+ static const char* getClientVersion() {
113+ return mysql_get_client_info();
114+ }
115+
116+ uint64_t getMaxPacketSize() const {
117+ return maxPacketSize;
118+ }
119+
120+ private:
121+ /**
122+ * Transaction related methods.
123+ *
124+ * Methods for starting, commiting and rolling back transaction. Each of the returns boolean value.
125+ *
126+ * @return true on success, false on error
127+ */
128+ bool beginTransaction();
129+ bool rollback();
130+ bool commit();
131+
132+ MYSQL* handle = nullptr;
133+ std::recursive_mutex databaseLock;
134+ uint64_t maxPacketSize = 1048576;
135+
136+ friend class DBTransaction;
137+};
138+
139+class DBResult
140+{
141+ public:
142+ explicit DBResult(MYSQL_RES* res);
143+ ~DBResult();
144+
145+ // non-copyable
146+ DBResult(const DBResult&) = delete;
147+ DBResult& operator=(const DBResult&) = delete;
148+
149+ template<typename T>
150+ T getNumber(const std::string& s) const
151+ {
152+ auto it = listNames.find(s);
153+ if (it == listNames.end()) {
154+ std::cout << "[Error - DBResult::getNumber] Column '" << s << "' doesn't exist in the result set" << std::endl;
155+ return static_cast<T>(0);
156+ }
157+
158+ if (row[it->second] == nullptr) {
159+ return static_cast<T>(0);
160+ }
161+
162+ T data;
163+ try {
164+ data = boost::lexical_cast<T>(row[it->second]);
165+ } catch (boost::bad_lexical_cast&) {
166+ data = 0;
167+ }
168+ return data;
169+ }
170+
171+ std::string getString(const std::string& s) const;
172+ const char* getStream(const std::string& s, unsigned long& size) const;
173+
174+ bool hasNext() const;
175+ bool next();
176+
177+ private:
178+ MYSQL_RES* handle;
179+ MYSQL_ROW row;
180+
181+ std::map<std::string, size_t> listNames;
182+
183+ friend class Database;
184+};
185+
186+/**
187+ * INSERT statement.
188+ */
189+class DBInsert
190+{
191+ public:
192+ explicit DBInsert(std::string query);
193+ bool addRow(const std::string& row);
194+ bool addRow(std::ostringstream& row);
195+ bool execute();
196+
197+ private:
198+ std::string query;
199+ std::string values;
200+ size_t length;
201+};
202+
203+class DBTransaction
204+{
205+ public:
206+ constexpr DBTransaction() = default;
207+
208+ ~DBTransaction() {
209+ if (state == STATE_START) {
210+ Database::getInstance().rollback();
211+ }
212+ }
213+
214+ // non-copyable
215+ DBTransaction(const DBTransaction&) = delete;
216+ DBTransaction& operator=(const DBTransaction&) = delete;
217+
218+ bool begin() {
219+ state = STATE_START;
220+ return Database::getInstance().beginTransaction();
221+ }
222+
223+ bool commit() {
224+ if (state != STATE_START) {
225+ return false;
226+ }
227+
228+ state = STATE_COMMIT;
229+ return Database::getInstance().commit();
230+ }
231+
232+ private:
233+ enum TransactionStates_t {
234+ STATE_NO_START,
235+ STATE_START,
236+ STATE_COMMIT,
237+ };
238+
239+ TransactionStates_t state = STATE_NO_START;
240+};
241+
242+#endif
A src/databasemanager.cpp +172-0 View file
@@ -0,0 +1,172 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#include "otpch.h"
21+
22+#include "configmanager.h"
23+#include "databasemanager.h"
24+#include "luascript.h"
25+
26+extern ConfigManager g_config;
27+
28+bool DatabaseManager::optimizeTables()
29+{
30+ Database& db = Database::getInstance();
31+ std::ostringstream query;
32+
33+ query << "SELECT `TABLE_NAME` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `DATA_FREE` > 0";
34+ DBResult_ptr result = db.storeQuery(query.str());
35+ if (!result) {
36+ return false;
37+ }
38+
39+ do {
40+ std::string tableName = result->getString("TABLE_NAME");
41+ std::cout << "> Optimizing table " << tableName << "..." << std::flush;
42+
43+ query.str(std::string());
44+ query << "OPTIMIZE TABLE `" << tableName << '`';
45+
46+ if (db.executeQuery(query.str())) {
47+ std::cout << " [success]" << std::endl;
48+ } else {
49+ std::cout << " [failed]" << std::endl;
50+ }
51+ } while (result->next());
52+ return true;
53+}
54+
55+bool DatabaseManager::tableExists(const std::string& tableName)
56+{
57+ Database& db = Database::getInstance();
58+
59+ std::ostringstream query;
60+ query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `TABLE_NAME` = " << db.escapeString(tableName) << " LIMIT 1";
61+ return db.storeQuery(query.str()).get() != nullptr;
62+}
63+
64+bool DatabaseManager::isDatabaseSetup()
65+{
66+ Database& db = Database::getInstance();
67+ std::ostringstream query;
68+ query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB));
69+ return db.storeQuery(query.str()).get() != nullptr;
70+}
71+
72+int32_t DatabaseManager::getDatabaseVersion()
73+{
74+ if (!tableExists("server_config")) {
75+ Database& db = Database::getInstance();
76+ db.executeQuery("CREATE TABLE `server_config` (`config` VARCHAR(50) NOT NULL, `value` VARCHAR(256) NOT NULL DEFAULT '', UNIQUE(`config`)) ENGINE = InnoDB");
77+ db.executeQuery("INSERT INTO `server_config` VALUES ('db_version', 0)");
78+ return 0;
79+ }
80+
81+ int32_t version = 0;
82+ if (getDatabaseConfig("db_version", version)) {
83+ return version;
84+ }
85+ return -1;
86+}
87+
88+void DatabaseManager::updateDatabase()
89+{
90+ lua_State* L = luaL_newstate();
91+ if (!L) {
92+ return;
93+ }
94+
95+ luaL_openlibs(L);
96+
97+#ifndef LUAJIT_VERSION
98+ //bit operations for Lua, based on bitlib project release 24
99+ //bit.bnot, bit.band, bit.bor, bit.bxor, bit.lshift, bit.rshift
100+ luaL_register(L, "bit", LuaScriptInterface::luaBitReg);
101+#endif
102+
103+ //db table
104+ luaL_register(L, "db", LuaScriptInterface::luaDatabaseTable);
105+
106+ //result table
107+ luaL_register(L, "result", LuaScriptInterface::luaResultTable);
108+
109+ int32_t version = getDatabaseVersion();
110+ do {
111+ std::ostringstream ss;
112+ ss << "data/migrations/" << version << ".lua";
113+ if (luaL_dofile(L, ss.str().c_str()) != 0) {
114+ std::cout << "[Error - DatabaseManager::updateDatabase - Version: " << version << "] " << lua_tostring(L, -1) << std::endl;
115+ break;
116+ }
117+
118+ if (!LuaScriptInterface::reserveScriptEnv()) {
119+ break;
120+ }
121+
122+ lua_getglobal(L, "onUpdateDatabase");
123+ if (lua_pcall(L, 0, 1, 0) != 0) {
124+ LuaScriptInterface::resetScriptEnv();
125+ std::cout << "[Error - DatabaseManager::updateDatabase - Version: " << version << "] " << lua_tostring(L, -1) << std::endl;
126+ break;
127+ }
128+
129+ if (!LuaScriptInterface::getBoolean(L, -1, false)) {
130+ LuaScriptInterface::resetScriptEnv();
131+ break;
132+ }
133+
134+ version++;
135+ std::cout << "> Database has been updated to version " << version << '.' << std::endl;
136+ registerDatabaseConfig("db_version", version);
137+
138+ LuaScriptInterface::resetScriptEnv();
139+ } while (true);
140+ lua_close(L);
141+}
142+
143+bool DatabaseManager::getDatabaseConfig(const std::string& config, int32_t& value)
144+{
145+ Database& db = Database::getInstance();
146+ std::ostringstream query;
147+ query << "SELECT `value` FROM `server_config` WHERE `config` = " << db.escapeString(config);
148+
149+ DBResult_ptr result = db.storeQuery(query.str());
150+ if (!result) {
151+ return false;
152+ }
153+
154+ value = result->getNumber<int32_t>("value");
155+ return true;
156+}
157+
158+void DatabaseManager::registerDatabaseConfig(const std::string& config, int32_t value)
159+{
160+ Database& db = Database::getInstance();
161+ std::ostringstream query;
162+
163+ int32_t tmp;
164+
165+ if (!getDatabaseConfig(config, tmp)) {
166+ query << "INSERT INTO `server_config` VALUES (" << db.escapeString(config) << ", '" << value << "')";
167+ } else {
168+ query << "UPDATE `server_config` SET `value` = '" << value << "' WHERE `config` = " << db.escapeString(config);
169+ }
170+
171+ db.executeQuery(query.str());
172+}
A src/databasemanager.h +38-0 View file
@@ -0,0 +1,38 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_DATABASEMANAGER_H_2B75821C555E4D1D83E32B20D683217C
21+#define FS_DATABASEMANAGER_H_2B75821C555E4D1D83E32B20D683217C
22+#include "database.h"
23+
24+class DatabaseManager
25+{
26+ public:
27+ static bool tableExists(const std::string& tableName);
28+
29+ static int32_t getDatabaseVersion();
30+ static bool isDatabaseSetup();
31+
32+ static bool optimizeTables();
33+ static void updateDatabase();
34+
35+ static bool getDatabaseConfig(const std::string& config, int32_t& value);
36+ static void registerDatabaseConfig(const std::string& config, int32_t value);
37+};
38+#endif
A src/databasetasks.cpp +105-0 View file
@@ -0,0 +1,105 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#include "otpch.h"
21+
22+#include "databasetasks.h"
23+#include "tasks.h"
24+
25+extern Dispatcher g_dispatcher;
26+
27+
28+void DatabaseTasks::start()
29+{
30+ db.connect();
31+ ThreadHolder::start();
32+}
33+
34+void DatabaseTasks::threadMain()
35+{
36+ std::unique_lock<std::mutex> taskLockUnique(taskLock, std::defer_lock);
37+ while (getState() != THREAD_STATE_TERMINATED) {
38+ taskLockUnique.lock();
39+ if (tasks.empty()) {
40+ taskSignal.wait(taskLockUnique);
41+ }
42+
43+ if (!tasks.empty()) {
44+ DatabaseTask task = std::move(tasks.front());
45+ tasks.pop_front();
46+ taskLockUnique.unlock();
47+ runTask(task);
48+ } else {
49+ taskLockUnique.unlock();
50+ }
51+ }
52+}
53+
54+void DatabaseTasks::addTask(std::string query, std::function<void(DBResult_ptr, bool)> callback/* = nullptr*/, bool store/* = false*/)
55+{
56+ bool signal = false;
57+ taskLock.lock();
58+ if (getState() == THREAD_STATE_RUNNING) {
59+ signal = tasks.empty();
60+ tasks.emplace_back(std::move(query), std::move(callback), store);
61+ }
62+ taskLock.unlock();
63+
64+ if (signal) {
65+ taskSignal.notify_one();
66+ }
67+}
68+
69+void DatabaseTasks::runTask(const DatabaseTask& task)
70+{
71+ bool success;
72+ DBResult_ptr result;
73+ if (task.store) {
74+ result = db.storeQuery(task.query);
75+ success = true;
76+ } else {
77+ result = nullptr;
78+ success = db.executeQuery(task.query);
79+ }
80+
81+ if (task.callback) {
82+ g_dispatcher.addTask(createTask(std::bind(task.callback, result, success)));
83+ }
84+}
85+
86+void DatabaseTasks::flush()
87+{
88+ std::unique_lock<std::mutex> guard{ taskLock };
89+ while (!tasks.empty()) {
90+ auto task = std::move(tasks.front());
91+ tasks.pop_front();
92+ guard.unlock();
93+ runTask(task);
94+ guard.lock();
95+ }
96+}
97+
98+void DatabaseTasks::shutdown()
99+{
100+ taskLock.lock();
101+ setState(THREAD_STATE_TERMINATED);
102+ taskLock.unlock();
103+ flush();
104+ taskSignal.notify_one();
105+}
A src/databasetasks.h +60-0 View file
@@ -0,0 +1,60 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_DATABASETASKS_H_9CBA08E9F5FEBA7275CCEE6560059576
21+#define FS_DATABASETASKS_H_9CBA08E9F5FEBA7275CCEE6560059576
22+
23+#include <condition_variable>
24+#include "thread_holder_base.h"
25+#include "database.h"
26+#include "enums.h"
27+
28+struct DatabaseTask {
29+ DatabaseTask(std::string&& query, std::function<void(DBResult_ptr, bool)>&& callback, bool store) :
30+ query(std::move(query)), callback(std::move(callback)), store(store) {}
31+
32+ std::string query;
33+ std::function<void(DBResult_ptr, bool)> callback;
34+ bool store;
35+};
36+
37+class DatabaseTasks : public ThreadHolder<DatabaseTasks>
38+{
39+ public:
40+ DatabaseTasks() = default;
41+ void start();
42+ void flush();
43+ void shutdown();
44+
45+ void addTask(std::string query, std::function<void(DBResult_ptr, bool)> callback = nullptr, bool store = false);
46+
47+ void threadMain();
48+ private:
49+ void runTask(const DatabaseTask& task);
50+
51+ Database db;
52+ std::thread thread;
53+ std::list<DatabaseTask> tasks;
54+ std::mutex taskLock;
55+ std::condition_variable taskSignal;
56+};
57+
58+extern DatabaseTasks g_databaseTasks;
59+
60+#endif
A src/definitions.h +83-0 View file
@@ -0,0 +1,83 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963
21+#define FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963
22+
23+static constexpr auto STATUS_SERVER_NAME = "RD Core";
24+static constexpr auto STATUS_SERVER_VERSION = "1.0";
25+static constexpr auto STATUS_SERVER_DEVELOPERS = "";
26+
27+static constexpr auto CLIENT_VERSION_MIN = 1097;
28+static constexpr auto CLIENT_VERSION_MAX = 1099;
29+static constexpr auto CLIENT_VERSION_STR = "10.99";
30+
31+static constexpr auto AUTHENTICATOR_DIGITS = 6U;
32+static constexpr auto AUTHENTICATOR_PERIOD = 30U;
33+
34+#ifndef __FUNCTION__
35+#define __FUNCTION__ __func__
36+#endif
37+
38+#ifndef _CRT_SECURE_NO_WARNINGS
39+#define _CRT_SECURE_NO_WARNINGS
40+#endif
41+
42+#ifndef _USE_MATH_DEFINES
43+#define _USE_MATH_DEFINES
44+#endif
45+
46+#include <cmath>
47+
48+#ifdef _WIN32
49+#ifndef NOMINMAX
50+#define NOMINMAX
51+#endif
52+
53+#define WIN32_LEAN_AND_MEAN
54+
55+#ifdef _MSC_VER
56+#ifdef NDEBUG
57+#define _SECURE_SCL 0
58+#define HAS_ITERATOR_DEBUGGING 0
59+#endif
60+
61+#pragma warning(disable:4127) // conditional expression is constant
62+#pragma warning(disable:4244) // 'argument' : conversion from 'type1' to 'type2', possible loss of data
63+#pragma warning(disable:4250) // 'class1' : inherits 'class2::member' via dominance
64+#pragma warning(disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
65+#pragma warning(disable:4319) // '~': zero extending 'unsigned int' to 'lua_Number' of greater size
66+#pragma warning(disable:4351) // new behavior: elements of array will be default initialized
67+#pragma warning(disable:4458) // declaration hides class member
68+#endif
69+
70+#define strcasecmp _stricmp
71+#define strncasecmp _strnicmp
72+
73+#ifndef _WIN32_WINNT
74+// 0x0602: Windows 7
75+#define _WIN32_WINNT 0x0602
76+#endif
77+#endif
78+
79+#ifndef M_PI
80+#define M_PI 3.14159265358979323846
81+#endif
82+
83+#endif
A src/depotchest.cpp +82-0 View file
@@ -0,0 +1,82 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#include "otpch.h"
21+
22+#include "depotchest.h"
23+#include "tools.h"
24+
25+DepotChest::DepotChest(uint16_t type) :
26+ Container(type), maxDepotItems(1500) {}
27+
28+ReturnValue DepotChest::queryAdd(int32_t index, const Thing& thing, uint32_t count,
29+ uint32_t flags, Creature* actor/* = nullptr*/) const
30+{
31+ const Item* item = thing.getItem();
32+ if (item == nullptr) {
33+ return RETURNVALUE_NOTPOSSIBLE;
34+ }
35+
36+ bool skipLimit = hasBitSet(FLAG_NOLIMIT, flags);
37+ if (!skipLimit) {
38+ int32_t addCount = 0;
39+
40+ if ((item->isStackable() && item->getItemCount() != count)) {
41+ addCount = 1;
42+ }
43+
44+ if (item->getTopParent() != this) {
45+ if (const Container* container = item->getContainer()) {
46+ addCount = container->getItemHoldingCount() + 1;
47+ } else {
48+ addCount = 1;
49+ }
50+ }
51+
52+ if (getItemHoldingCount() + addCount > maxDepotItems) {
53+ return RETURNVALUE_DEPOTISFULL;
54+ }
55+ }
56+
57+ return Container::queryAdd(index, thing, count, flags, actor);
58+}
59+
60+void DepotChest::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t)
61+{
62+ Cylinder* parent = getParent();
63+ if (parent != nullptr) {
64+ parent->postAddNotification(thing, oldParent, index, LINK_PARENT);
65+ }
66+}
67+
68+void DepotChest::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t)
69+{
70+ Cylinder* parent = getParent();
71+ if (parent != nullptr) {
72+ parent->postRemoveNotification(thing, newParent, index, LINK_PARENT);
73+ }
74+}
75+
76+Cylinder* DepotChest::getParent() const
77+{
78+ if (parent) {
79+ return parent->getParent();
80+ }
81+ return nullptr;
82+}
A src/depotchest.h +57-0 View file
@@ -0,0 +1,57 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_DEPOTCHEST_H_6538526014684E3DBC92CC12815B6766
21+#define FS_DEPOTCHEST_H_6538526014684E3DBC92CC12815B6766
22+
23+#include "container.h"
24+
25+class DepotChest final : public Container
26+{
27+ public:
28+ explicit DepotChest(uint16_t type);
29+
30+ //serialization
31+ void setMaxDepotItems(uint32_t maxitems) {
32+ maxDepotItems = maxitems;
33+ }
34+
35+ //cylinder implementations
36+ ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count,
37+ uint32_t flags, Creature* actor = nullptr) const override;
38+
39+ void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
40+ void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
41+
42+ //overrides
43+ bool canRemove() const override {
44+ return false;
45+ }
46+
47+ Cylinder* getParent() const override;
48+ Cylinder* getRealParent() const override {
49+ return parent;
50+ }
51+
52+ private:
53+ uint32_t maxDepotItems;
54+};
55+
56+#endif
57+
A src/depotlocker.cpp +64-0 View file
@@ -0,0 +1,64 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#include "otpch.h"
21+
22+#include "depotlocker.h"
23+
24+DepotLocker::DepotLocker(uint16_t type) :
25+ Container(type, 3), depotId(0) {}
26+
27+Attr_ReadValue DepotLocker::readAttr(AttrTypes_t attr, PropStream& propStream)
28+{
29+ if (attr == ATTR_DEPOT_ID) {
30+ if (!propStream.read<uint16_t>(depotId)) {
31+ return ATTR_READ_ERROR;
32+ }
33+ return ATTR_READ_CONTINUE;
34+ }
35+ return Item::readAttr(attr, propStream);
36+}
37+
38+ReturnValue DepotLocker::queryAdd(int32_t, const Thing&, uint32_t, uint32_t, Creature*) const
39+{
40+ return RETURNVALUE_NOTENOUGHROOM;
41+}
42+
43+void DepotLocker::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t)
44+{
45+ if (parent != nullptr) {
46+ parent->postAddNotification(thing, oldParent, index, LINK_PARENT);
47+ }
48+}
49+
50+void DepotLocker::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t)
51+{
52+ if (parent != nullptr) {
53+ parent->postRemoveNotification(thing, newParent, index, LINK_PARENT);
54+ }
55+}
56+
57+void DepotLocker::removeInbox(Inbox* inbox)
58+{
59+ auto cit = std::find(itemlist.begin(), itemlist.end(), inbox);
60+ if (cit == itemlist.end()) {
61+ return;
62+ }
63+ itemlist.erase(cit);
64+}
A src/depotlocker.h +66-0 View file
@@ -0,0 +1,66 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_DEPOTLOCKER_H_53AD8E0606A34070B87F792611F4F3F8
21+#define FS_DEPOTLOCKER_H_53AD8E0606A34070B87F792611F4F3F8
22+
23+#include "container.h"
24+#include "inbox.h"
25+
26+class DepotLocker final : public Container
27+{
28+ public:
29+ explicit DepotLocker(uint16_t type);
30+
31+ DepotLocker* getDepotLocker() override {
32+ return this;
33+ }
34+ const DepotLocker* getDepotLocker() const override {
35+ return this;
36+ }
37+
38+ void removeInbox(Inbox* inbox);
39+
40+ //serialization
41+ Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override;
42+
43+ uint16_t getDepotId() const {
44+ return depotId;
45+ }
46+ void setDepotId(uint16_t depotId) {
47+ this->depotId = depotId;
48+ }
49+
50+ //cylinder implementations
51+ ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count,
52+ uint32_t flags, Creature* actor = nullptr) const override;
53+
54+ void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
55+ void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
56+
57+ bool canRemove() const override {
58+ return false;
59+ }
60+
61+ private:
62+ uint16_t depotId;
63+};
64+
65+#endif
66+
A src/effects.cpp +63-0 View file
@@ -0,0 +1,63 @@
1+#include "otpch.h"
2+
3+#include "effects.h"
4+
5+#include "pugicast.h"
6+#include "tools.h"
7+
8+bool Effects::reload()
9+{
10+ effects.clear();
11+ return loadFromXml();
12+}
13+
14+bool Effects::loadFromXml()
15+{
16+ pugi::xml_document doc;
17+ pugi::xml_parse_result result = doc.load_file("data/XML/effects.xml");
18+ if (!result) {
19+ printXMLError("Error - Effects::loadFromXml", "data/XML/effects.xml", result);
20+ return false;
21+ }
22+
23+ for (auto effectNode : doc.child("effects").children()) {
24+ effects.emplace_back(
25+ static_cast<uint16_t>(pugi::cast<uint16_t>(effectNode.attribute("id").value())), // Changed from uint8_t to uint16_t
26+ pugi::cast<uint16_t>(effectNode.attribute("clientid").value()),
27+ effectNode.attribute("name").as_string(),
28+ pugi::cast<int32_t>(effectNode.attribute("speed").value()),
29+ effectNode.attribute("premium").as_bool()
30+ );
31+ }
32+ effects.shrink_to_fit();
33+ return true;
34+}
35+
36+Effect* Effects::getEffectByID(uint16_t id) // Changed from uint8_t to uint16_t
37+{
38+ auto it = std::find_if(effects.begin(), effects.end(), [id](const Effect& effect) {
39+ return effect.id == id;
40+ });
41+
42+ return it != effects.end() ? &*it : nullptr;
43+}
44+
45+Effect* Effects::getEffectByName(const std::string& name) {
46+ auto effectName = name.c_str();
47+ for (auto& it : effects) {
48+ if (strcasecmp(effectName, it.name.c_str()) == 0) {
49+ return &it;
50+ }
51+ }
52+
53+ return nullptr;
54+}
55+
56+Effect* Effects::getEffectByClientID(uint16_t clientId)
57+{
58+ auto it = std::find_if(effects.begin(), effects.end(), [clientId](const Effect& effect) {
59+ return effect.clientId == clientId;
60+ });
61+
62+ return it != effects.end() ? &*it : nullptr;
63+}
A src/effects.h +33-0 View file
@@ -0,0 +1,33 @@
1+#ifndef FS_EFFECTS_H
2+#define FS_EFFECTS_H
3+
4+struct Effect
5+{
6+ Effect(uint16_t id, uint16_t clientId, std::string name, int32_t speed, bool premium) :
7+ name(std::move(name)), speed(speed), clientId(clientId), id(id), premium(premium) {}
8+
9+ std::string name;
10+ int32_t speed;
11+ uint16_t clientId;
12+ uint16_t id; // Changed from uint8_t to uint16_t
13+ bool premium;
14+};
15+
16+class Effects
17+{
18+ public:
19+ bool reload();
20+ bool loadFromXml();
21+ Effect* getEffectByID(uint16_t id); // Changed from uint8_t to uint16_t
22+ Effect* getEffectByName(const std::string& name);
23+ Effect* getEffectByClientID(uint16_t clientId);
24+
25+ const std::vector<Effect>& getEffects() const {
26+ return effects;
27+ }
28+
29+ private:
30+ std::vector<Effect> effects;
31+};
32+
33+#endif
Top