| @@ -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 |
| @@ -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 | + | } |
| @@ -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 |
| @@ -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 | + | } |
| @@ -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 |
| @@ -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 | + | } |
| @@ -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 |
| @@ -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 |
| @@ -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 | + | } |
| @@ -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 | + |
| @@ -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 | + | } |
| @@ -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 | + |
| @@ -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 ⁢ | |
| 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 | + | } |
| @@ -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 |
| @@ -0,0 +1,681 @@ | |||
| 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_ENUMS_H_003445999FEE4A67BCECBE918B0124CE | |
| 21 | + | #define FS_ENUMS_H_003445999FEE4A67BCECBE918B0124CE | |
| 22 | + | ||
| 23 | + | #include <cstdint> | |
| 24 | + | #include <list> | |
| 25 | + | #include <string> | |
| 26 | + | #include <utility> | |
| 27 | + | ||
| 28 | + | enum RuleViolationType_t : uint8_t { | |
| 29 | + | REPORT_TYPE_NAME = 0, | |
| 30 | + | REPORT_TYPE_STATEMENT = 1, | |
| 31 | + | REPORT_TYPE_BOT = 2 | |
| 32 | + | }; | |
| 33 | + | ||
| 34 | + | enum RuleViolationReasons_t : uint8_t { | |
| 35 | + | REPORT_REASON_NAMEINAPPROPRIATE = 0, | |
| 36 | + | REPORT_REASON_NAMEPOORFORMATTED = 1, | |
| 37 | + | REPORT_REASON_NAMEADVERTISING = 2, | |
| 38 | + | REPORT_REASON_NAMEUNFITTING = 3, | |
| 39 | + | REPORT_REASON_NAMERULEVIOLATION = 4, | |
| 40 | + | REPORT_REASON_INSULTINGSTATEMENT = 5, | |
| 41 | + | REPORT_REASON_SPAMMING = 6, | |
| 42 | + | REPORT_REASON_ADVERTISINGSTATEMENT = 7, | |
| 43 | + | REPORT_REASON_UNFITTINGSTATEMENT = 8, | |
| 44 | + | REPORT_REASON_LANGUAGESTATEMENT = 9, | |
| 45 | + | REPORT_REASON_DISCLOSURE = 10, | |
| 46 | + | REPORT_REASON_RULEVIOLATION = 11, | |
| 47 | + | REPORT_REASON_STATEMENT_BUGABUSE = 12, | |
| 48 | + | REPORT_REASON_UNOFFICIALSOFTWARE = 13, | |
| 49 | + | REPORT_REASON_PRETENDING = 14, | |
| 50 | + | REPORT_REASON_HARASSINGOWNERS = 15, | |
| 51 | + | REPORT_REASON_FALSEINFO = 16, | |
| 52 | + | REPORT_REASON_ACCOUNTSHARING = 17, | |
| 53 | + | REPORT_REASON_STEALINGDATA = 18, | |
| 54 | + | REPORT_REASON_SERVICEATTACKING = 19, | |
| 55 | + | REPORT_REASON_SERVICEAGREEMENT = 20 | |
| 56 | + | }; | |
| 57 | + | ||
| 58 | + | enum BugReportType_t : uint8_t { | |
| 59 | + | BUG_CATEGORY_MAP = 0, | |
| 60 | + | BUG_CATEGORY_TYPO = 1, | |
| 61 | + | BUG_CATEGORY_TECHNICAL = 2, | |
| 62 | + | BUG_CATEGORY_OTHER = 3 | |
| 63 | + | }; | |
| 64 | + | ||
| 65 | + | enum ThreadState { | |
| 66 | + | THREAD_STATE_RUNNING, | |
| 67 | + | THREAD_STATE_CLOSING, | |
| 68 | + | THREAD_STATE_TERMINATED, | |
| 69 | + | }; | |
| 70 | + | ||
| 71 | + | enum itemAttrTypes : uint32_t { | |
| 72 | + | ITEM_ATTRIBUTE_NONE, | |
| 73 | + | ||
| 74 | + | ITEM_ATTRIBUTE_ACTIONID = 1 << 0, | |
| 75 | + | ITEM_ATTRIBUTE_UNIQUEID = 1 << 1, | |
| 76 | + | ITEM_ATTRIBUTE_DESCRIPTION = 1 << 2, | |
| 77 | + | ITEM_ATTRIBUTE_TEXT = 1 << 3, | |
| 78 | + | ITEM_ATTRIBUTE_DATE = 1 << 4, | |
| 79 | + | ITEM_ATTRIBUTE_WRITER = 1 << 5, | |
| 80 | + | ITEM_ATTRIBUTE_NAME = 1 << 6, | |
| 81 | + | ITEM_ATTRIBUTE_ARTICLE = 1 << 7, | |
| 82 | + | ITEM_ATTRIBUTE_PLURALNAME = 1 << 8, | |
| 83 | + | ITEM_ATTRIBUTE_WEIGHT = 1 << 9, | |
| 84 | + | ITEM_ATTRIBUTE_ATTACK = 1 << 10, | |
| 85 | + | ITEM_ATTRIBUTE_DEFENSE = 1 << 11, | |
| 86 | + | ITEM_ATTRIBUTE_EXTRADEFENSE = 1 << 12, | |
| 87 | + | ITEM_ATTRIBUTE_ARMOR = 1 << 13, | |
| 88 | + | ITEM_ATTRIBUTE_HITCHANCE = 1 << 14, | |
| 89 | + | ITEM_ATTRIBUTE_SHOOTRANGE = 1 << 15, | |
| 90 | + | ITEM_ATTRIBUTE_OWNER = 1 << 16, | |
| 91 | + | ITEM_ATTRIBUTE_DURATION = 1 << 17, | |
| 92 | + | ITEM_ATTRIBUTE_DECAYSTATE = 1 << 18, | |
| 93 | + | ITEM_ATTRIBUTE_CORPSEOWNER = 1 << 19, | |
| 94 | + | ITEM_ATTRIBUTE_CHARGES = 1 << 20, | |
| 95 | + | ITEM_ATTRIBUTE_FLUIDTYPE = 1 << 21, | |
| 96 | + | ITEM_ATTRIBUTE_DOORID = 1 << 22, | |
| 97 | + | ITEM_ATTRIBUTE_DECAYTO = 1 << 23, | |
| 98 | + | ITEM_ATTRIBUTE_WRAPID = 1 << 24, | |
| 99 | + | ||
| 100 | + | ITEM_ATTRIBUTE_CUSTOM = 1U << 31 | |
| 101 | + | }; | |
| 102 | + | ||
| 103 | + | enum VipStatus_t : uint8_t { | |
| 104 | + | VIPSTATUS_OFFLINE = 0, | |
| 105 | + | VIPSTATUS_ONLINE = 1, | |
| 106 | + | VIPSTATUS_PENDING = 2 | |
| 107 | + | }; | |
| 108 | + | ||
| 109 | + | enum MarketAction_t { | |
| 110 | + | MARKETACTION_BUY = 0, | |
| 111 | + | MARKETACTION_SELL = 1, | |
| 112 | + | }; | |
| 113 | + | ||
| 114 | + | enum MarketRequest_t { | |
| 115 | + | MARKETREQUEST_OWN_OFFERS = 0xFFFE, | |
| 116 | + | MARKETREQUEST_OWN_HISTORY = 0xFFFF, | |
| 117 | + | }; | |
| 118 | + | ||
| 119 | + | enum MarketOfferState_t { | |
| 120 | + | OFFERSTATE_ACTIVE = 0, | |
| 121 | + | OFFERSTATE_CANCELLED = 1, | |
| 122 | + | OFFERSTATE_EXPIRED = 2, | |
| 123 | + | OFFERSTATE_ACCEPTED = 3, | |
| 124 | + | ||
| 125 | + | OFFERSTATE_ACCEPTEDEX = 255, | |
| 126 | + | }; | |
| 127 | + | ||
| 128 | + | enum ChannelEvent_t : uint8_t { | |
| 129 | + | CHANNELEVENT_JOIN = 0, | |
| 130 | + | CHANNELEVENT_LEAVE = 1, | |
| 131 | + | CHANNELEVENT_INVITE = 2, | |
| 132 | + | CHANNELEVENT_EXCLUDE = 3, | |
| 133 | + | }; | |
| 134 | + | ||
| 135 | + | enum CreatureType_t : uint8_t { | |
| 136 | + | CREATURETYPE_PLAYER = 0, | |
| 137 | + | CREATURETYPE_MONSTER = 1, | |
| 138 | + | CREATURETYPE_NPC = 2, | |
| 139 | + | CREATURETYPE_SUMMON_OWN = 3, | |
| 140 | + | CREATURETYPE_SUMMON_OTHERS = 4, | |
| 141 | + | }; | |
| 142 | + | ||
| 143 | + | enum OperatingSystem_t : uint8_t { | |
| 144 | + | CLIENTOS_NONE = 0, | |
| 145 | + | ||
| 146 | + | CLIENTOS_LINUX = 1, | |
| 147 | + | CLIENTOS_WINDOWS = 2, | |
| 148 | + | CLIENTOS_FLASH = 3, | |
| 149 | + | ||
| 150 | + | CLIENTOS_OTCLIENT_LINUX = 10, | |
| 151 | + | CLIENTOS_OTCLIENT_WINDOWS = 11, | |
| 152 | + | CLIENTOS_OTCLIENT_MAC = 12, | |
| 153 | + | ||
| 154 | + | // by default OTCv8 uses CLIENTOS_WINDOWS for backward compatibility | |
| 155 | + | // for correct value enable g_game.enableFeature(GameExtendedOpcode) | |
| 156 | + | // in modules/game_features/features.lua | |
| 157 | + | CLIENTOS_OTCLIENTV8_LINUX = 20, | |
| 158 | + | CLIENTOS_OTCLIENTV8_WINDOWS = 21, | |
| 159 | + | CLIENTOS_OTCLIENTV8_MAC = 22, | |
| 160 | + | CLIENTOS_OTCLIENTV8_ANDROID = 23, | |
| 161 | + | CLIENTOS_OTCLIENTV8_IOS = 24, | |
| 162 | + | CLIENTOS_OTCLIENTV8_WEB = 25 | |
| 163 | + | }; | |
| 164 | + | ||
| 165 | + | enum SpellGroup_t : uint8_t { | |
| 166 | + | SPELLGROUP_NONE = 0, | |
| 167 | + | SPELLGROUP_ATTACK = 1, | |
| 168 | + | SPELLGROUP_HEALING = 2, | |
| 169 | + | SPELLGROUP_SUPPORT = 3, | |
| 170 | + | SPELLGROUP_SPECIAL = 4, | |
| 171 | + | }; | |
| 172 | + | ||
| 173 | + | enum SpellType_t : uint8_t { | |
| 174 | + | SPELL_UNDEFINED = 0, | |
| 175 | + | SPELL_INSTANT = 1, | |
| 176 | + | SPELL_RUNE = 2, | |
| 177 | + | }; | |
| 178 | + | ||
| 179 | + | enum AccountType_t : uint8_t { | |
| 180 | + | ACCOUNT_TYPE_NORMAL = 1, | |
| 181 | + | ACCOUNT_TYPE_TUTOR = 2, | |
| 182 | + | ACCOUNT_TYPE_SENIORTUTOR = 3, | |
| 183 | + | ACCOUNT_TYPE_GAMEMASTER = 4, | |
| 184 | + | ACCOUNT_TYPE_GOD = 5 | |
| 185 | + | }; | |
| 186 | + | ||
| 187 | + | enum RaceType_t : uint8_t { | |
| 188 | + | RACE_NONE, | |
| 189 | + | RACE_VENOM, | |
| 190 | + | RACE_BLOOD, | |
| 191 | + | RACE_UNDEAD, | |
| 192 | + | RACE_FIRE, | |
| 193 | + | RACE_ENERGY, | |
| 194 | + | }; | |
| 195 | + | ||
| 196 | + | enum CombatType_t : uint16_t { | |
| 197 | + | COMBAT_NONE = 0, | |
| 198 | + | ||
| 199 | + | COMBAT_PHYSICALDAMAGE = 1 << 0, | |
| 200 | + | COMBAT_ENERGYDAMAGE = 1 << 1, | |
| 201 | + | COMBAT_EARTHDAMAGE = 1 << 2, | |
| 202 | + | COMBAT_FIREDAMAGE = 1 << 3, | |
| 203 | + | COMBAT_UNDEFINEDDAMAGE = 1 << 4, | |
| 204 | + | COMBAT_LIFEDRAIN = 1 << 5, | |
| 205 | + | COMBAT_MANADRAIN = 1 << 6, | |
| 206 | + | COMBAT_HEALING = 1 << 7, | |
| 207 | + | COMBAT_DROWNDAMAGE = 1 << 8, | |
| 208 | + | COMBAT_ICEDAMAGE = 1 << 9, | |
| 209 | + | COMBAT_HOLYDAMAGE = 1 << 10, | |
| 210 | + | COMBAT_DEATHDAMAGE = 1 << 11, | |
| 211 | + | ||
| 212 | + | COMBAT_COUNT = 12 | |
| 213 | + | }; | |
| 214 | + | ||
| 215 | + | enum CombatParam_t { | |
| 216 | + | COMBAT_PARAM_TYPE, | |
| 217 | + | COMBAT_PARAM_EFFECT, | |
| 218 | + | COMBAT_PARAM_DISTANCEEFFECT, | |
| 219 | + | COMBAT_PARAM_BLOCKSHIELD, | |
| 220 | + | COMBAT_PARAM_BLOCKARMOR, | |
| 221 | + | COMBAT_PARAM_TARGETCASTERORTOPMOST, | |
| 222 | + | COMBAT_PARAM_CREATEITEM, | |
| 223 | + | COMBAT_PARAM_AGGRESSIVE, | |
| 224 | + | COMBAT_PARAM_DISPEL, | |
| 225 | + | COMBAT_PARAM_USECHARGES, | |
| 226 | + | }; | |
| 227 | + | ||
| 228 | + | enum CallBackParam_t { | |
| 229 | + | CALLBACK_PARAM_LEVELMAGICVALUE, | |
| 230 | + | CALLBACK_PARAM_SKILLVALUE, | |
| 231 | + | CALLBACK_PARAM_TARGETTILE, | |
| 232 | + | CALLBACK_PARAM_TARGETCREATURE, | |
| 233 | + | }; | |
| 234 | + | ||
| 235 | + | enum ConditionParam_t { | |
| 236 | + | CONDITION_PARAM_OWNER = 1, | |
| 237 | + | CONDITION_PARAM_TICKS = 2, | |
| 238 | + | // CONDITION_PARAM_OUTFIT = 3, | |
| 239 | + | CONDITION_PARAM_HEALTHGAIN = 4, | |
| 240 | + | CONDITION_PARAM_HEALTHTICKS = 5, | |
| 241 | + | CONDITION_PARAM_MANAGAIN = 6, | |
| 242 | + | CONDITION_PARAM_MANATICKS = 7, | |
| 243 | + | CONDITION_PARAM_DELAYED = 8, | |
| 244 | + | CONDITION_PARAM_SPEED = 9, | |
| 245 | + | CONDITION_PARAM_LIGHT_LEVEL = 10, | |
| 246 | + | CONDITION_PARAM_LIGHT_COLOR = 11, | |
| 247 | + | CONDITION_PARAM_SOULGAIN = 12, | |
| 248 | + | CONDITION_PARAM_SOULTICKS = 13, | |
| 249 | + | CONDITION_PARAM_MINVALUE = 14, | |
| 250 | + | CONDITION_PARAM_MAXVALUE = 15, | |
| 251 | + | CONDITION_PARAM_STARTVALUE = 16, | |
| 252 | + | CONDITION_PARAM_TICKINTERVAL = 17, | |
| 253 | + | CONDITION_PARAM_FORCEUPDATE = 18, | |
| 254 | + | CONDITION_PARAM_SKILL_MELEE = 19, | |
| 255 | + | CONDITION_PARAM_SKILL_FIST = 20, | |
| 256 | + | CONDITION_PARAM_SKILL_CLUB = 21, | |
| 257 | + | CONDITION_PARAM_SKILL_SWORD = 22, | |
| 258 | + | CONDITION_PARAM_SKILL_AXE = 23, | |
| 259 | + | CONDITION_PARAM_SKILL_DISTANCE = 24, | |
| 260 | + | CONDITION_PARAM_SKILL_SHIELD = 25, | |
| 261 | + | CONDITION_PARAM_SKILL_FISHING = 26, | |
| 262 | + | CONDITION_PARAM_STAT_MAXHITPOINTS = 27, | |
| 263 | + | CONDITION_PARAM_STAT_MAXMANAPOINTS = 28, | |
| 264 | + | // CONDITION_PARAM_STAT_SOULPOINTS = 29, | |
| 265 | + | CONDITION_PARAM_STAT_MAGICPOINTS = 30, | |
| 266 | + | CONDITION_PARAM_STAT_MAXHITPOINTSPERCENT = 31, | |
| 267 | + | CONDITION_PARAM_STAT_MAXMANAPOINTSPERCENT = 32, | |
| 268 | + | // CONDITION_PARAM_STAT_SOULPOINTSPERCENT = 33, | |
| 269 | + | CONDITION_PARAM_STAT_MAGICPOINTSPERCENT = 34, | |
| 270 | + | CONDITION_PARAM_PERIODICDAMAGE = 35, | |
| 271 | + | CONDITION_PARAM_SKILL_MELEEPERCENT = 36, | |
| 272 | + | CONDITION_PARAM_SKILL_FISTPERCENT = 37, | |
| 273 | + | CONDITION_PARAM_SKILL_CLUBPERCENT = 38, | |
| 274 | + | CONDITION_PARAM_SKILL_SWORDPERCENT = 39, | |
| 275 | + | CONDITION_PARAM_SKILL_AXEPERCENT = 40, | |
| 276 | + | CONDITION_PARAM_SKILL_DISTANCEPERCENT = 41, | |
| 277 | + | CONDITION_PARAM_SKILL_SHIELDPERCENT = 42, | |
| 278 | + | CONDITION_PARAM_SKILL_FISHINGPERCENT = 43, | |
| 279 | + | CONDITION_PARAM_BUFF_SPELL = 44, | |
| 280 | + | CONDITION_PARAM_SUBID = 45, | |
| 281 | + | CONDITION_PARAM_FIELD = 46, | |
| 282 | + | CONDITION_PARAM_DISABLE_DEFENSE = 47, | |
| 283 | + | CONDITION_PARAM_SPECIALSKILL_CRITICALHITCHANCE = 48, | |
| 284 | + | CONDITION_PARAM_SPECIALSKILL_CRITICALHITAMOUNT = 49, | |
| 285 | + | CONDITION_PARAM_SPECIALSKILL_LIFELEECHCHANCE = 50, | |
| 286 | + | CONDITION_PARAM_SPECIALSKILL_LIFELEECHAMOUNT = 51, | |
| 287 | + | CONDITION_PARAM_SPECIALSKILL_MANALEECHCHANCE = 52, | |
| 288 | + | CONDITION_PARAM_SPECIALSKILL_MANALEECHAMOUNT = 53, | |
| 289 | + | CONDITION_PARAM_AGGRESSIVE = 54, | |
| 290 | + | }; | |
| 291 | + | ||
| 292 | + | enum BlockType_t : uint8_t { | |
| 293 | + | BLOCK_NONE, | |
| 294 | + | BLOCK_DEFENSE, | |
| 295 | + | BLOCK_ARMOR, | |
| 296 | + | BLOCK_IMMUNITY | |
| 297 | + | }; | |
| 298 | + | ||
| 299 | + | enum skills_t : uint8_t { | |
| 300 | + | SKILL_FIST = 0, | |
| 301 | + | SKILL_CLUB = 1, | |
| 302 | + | SKILL_SWORD = 2, | |
| 303 | + | SKILL_AXE = 3, | |
| 304 | + | SKILL_DISTANCE = 4, | |
| 305 | + | SKILL_SHIELD = 5, | |
| 306 | + | SKILL_FISHING = 6, | |
| 307 | + | ||
| 308 | + | SKILL_MAGLEVEL = 7, | |
| 309 | + | SKILL_LEVEL = 8, | |
| 310 | + | ||
| 311 | + | SKILL_FIRST = SKILL_FIST, | |
| 312 | + | SKILL_LAST = SKILL_FISHING | |
| 313 | + | }; | |
| 314 | + | ||
| 315 | + | enum stats_t { | |
| 316 | + | STAT_MAXHITPOINTS, | |
| 317 | + | STAT_MAXMANAPOINTS, | |
| 318 | + | STAT_SOULPOINTS, // unused | |
| 319 | + | STAT_MAGICPOINTS, | |
| 320 | + | ||
| 321 | + | STAT_FIRST = STAT_MAXHITPOINTS, | |
| 322 | + | STAT_LAST = STAT_MAGICPOINTS | |
| 323 | + | }; | |
| 324 | + | ||
| 325 | + | enum SpecialSkills_t { | |
| 326 | + | SPECIALSKILL_CRITICALHITCHANCE, | |
| 327 | + | SPECIALSKILL_CRITICALHITAMOUNT, | |
| 328 | + | SPECIALSKILL_LIFELEECHCHANCE, | |
| 329 | + | SPECIALSKILL_LIFELEECHAMOUNT, | |
| 330 | + | SPECIALSKILL_MANALEECHCHANCE, | |
| 331 | + | SPECIALSKILL_MANALEECHAMOUNT, | |
| 332 | + | ||
| 333 | + | SPECIALSKILL_FIRST = SPECIALSKILL_CRITICALHITCHANCE, | |
| 334 | + | SPECIALSKILL_LAST = SPECIALSKILL_MANALEECHAMOUNT | |
| 335 | + | }; | |
| 336 | + | ||
| 337 | + | enum Archetype_t : uint8_t { | |
| 338 | + | ARCHETYPE_NONE = 0, | |
| 339 | + | ARCHETYPE_WARFARE = 1, | |
| 340 | + | ARCHETYPE_ARCHERY = 2, | |
| 341 | + | ARCHETYPE_PROTECTION = 3, | |
| 342 | + | ARCHETYPE_SHADOW = 4, | |
| 343 | + | ARCHETYPE_WIZARDRY = 5, | |
| 344 | + | ARCHETYPE_HOLY = 6, | |
| 345 | + | ARCHETYPE_WITCHCRAFT = 7, | |
| 346 | + | ARCHETYPE_SPIRITUAL = 8, | |
| 347 | + | ||
| 348 | + | ARCHETYPE_FIRST = ARCHETYPE_WARFARE, | |
| 349 | + | ARCHETYPE_LAST = ARCHETYPE_SPIRITUAL | |
| 350 | + | }; | |
| 351 | + | ||
| 352 | + | enum ArchetypeResource_t : uint16_t { | |
| 353 | + | ARCHETYPE_RESOURCE_AETHER = 1, | |
| 354 | + | }; | |
| 355 | + | ||
| 356 | + | enum formulaType_t { | |
| 357 | + | COMBAT_FORMULA_UNDEFINED, | |
| 358 | + | COMBAT_FORMULA_LEVELMAGIC, | |
| 359 | + | COMBAT_FORMULA_SKILL, | |
| 360 | + | COMBAT_FORMULA_DAMAGE, | |
| 361 | + | }; | |
| 362 | + | ||
| 363 | + | enum ConditionType_t { | |
| 364 | + | CONDITION_NONE, | |
| 365 | + | ||
| 366 | + | CONDITION_POISON = 1 << 0, | |
| 367 | + | CONDITION_FIRE = 1 << 1, | |
| 368 | + | CONDITION_ENERGY = 1 << 2, | |
| 369 | + | CONDITION_BLEEDING = 1 << 3, | |
| 370 | + | CONDITION_HASTE = 1 << 4, | |
| 371 | + | CONDITION_PARALYZE = 1 << 5, | |
| 372 | + | CONDITION_OUTFIT = 1 << 6, | |
| 373 | + | CONDITION_INVISIBLE = 1 << 7, | |
| 374 | + | CONDITION_LIGHT = 1 << 8, | |
| 375 | + | CONDITION_MANASHIELD = 1 << 9, | |
| 376 | + | CONDITION_INFIGHT = 1 << 10, | |
| 377 | + | CONDITION_DRUNK = 1 << 11, | |
| 378 | + | CONDITION_EXHAUST_WEAPON = 1 << 12, // unused | |
| 379 | + | CONDITION_REGENERATION = 1 << 13, | |
| 380 | + | CONDITION_SOUL = 1 << 14, | |
| 381 | + | CONDITION_DROWN = 1 << 15, | |
| 382 | + | CONDITION_MUTED = 1 << 16, | |
| 383 | + | CONDITION_CHANNELMUTEDTICKS = 1 << 17, | |
| 384 | + | CONDITION_YELLTICKS = 1 << 18, | |
| 385 | + | CONDITION_ATTRIBUTES = 1 << 19, | |
| 386 | + | CONDITION_FREEZING = 1 << 20, | |
| 387 | + | CONDITION_DAZZLED = 1 << 21, | |
| 388 | + | CONDITION_CURSED = 1 << 22, | |
| 389 | + | CONDITION_EXHAUST_COMBAT = 1 << 23, // unused | |
| 390 | + | CONDITION_EXHAUST_HEAL = 1 << 24, // unused | |
| 391 | + | CONDITION_PACIFIED = 1 << 25, | |
| 392 | + | CONDITION_SPELLCOOLDOWN = 1 << 26, | |
| 393 | + | CONDITION_SPELLGROUPCOOLDOWN = 1 << 27, | |
| 394 | + | }; | |
| 395 | + | ||
| 396 | + | enum ConditionId_t : int8_t { | |
| 397 | + | CONDITIONID_DEFAULT = -1, | |
| 398 | + | CONDITIONID_COMBAT, | |
| 399 | + | CONDITIONID_HEAD, | |
| 400 | + | CONDITIONID_NECKLACE, | |
| 401 | + | CONDITIONID_BACKPACK, | |
| 402 | + | CONDITIONID_ARMOR, | |
| 403 | + | CONDITIONID_RIGHT, | |
| 404 | + | CONDITIONID_LEFT, | |
| 405 | + | CONDITIONID_LEGS, | |
| 406 | + | CONDITIONID_FEET, | |
| 407 | + | CONDITIONID_RING, | |
| 408 | + | CONDITIONID_AMMO, | |
| 409 | + | }; | |
| 410 | + | ||
| 411 | + | enum PlayerSex_t : uint8_t { | |
| 412 | + | PLAYERSEX_FEMALE = 0, | |
| 413 | + | PLAYERSEX_MALE = 1, | |
| 414 | + | ||
| 415 | + | PLAYERSEX_LAST = PLAYERSEX_MALE | |
| 416 | + | }; | |
| 417 | + | ||
| 418 | + | enum Vocation_t : uint16_t { VOCATION_NONE = 0 }; | |
| 419 | + | ||
| 420 | + | enum ReturnValue { | |
| 421 | + | RETURNVALUE_NOERROR, | |
| 422 | + | RETURNVALUE_NOTPOSSIBLE, | |
| 423 | + | RETURNVALUE_NOTENOUGHROOM, | |
| 424 | + | RETURNVALUE_PLAYERISPZLOCKED, | |
| 425 | + | RETURNVALUE_PLAYERISNOTINVITED, | |
| 426 | + | RETURNVALUE_CANNOTTHROW, | |
| 427 | + | RETURNVALUE_THEREISNOWAY, | |
| 428 | + | RETURNVALUE_DESTINATIONOUTOFREACH, | |
| 429 | + | RETURNVALUE_CREATUREBLOCK, | |
| 430 | + | RETURNVALUE_NOTMOVEABLE, | |
| 431 | + | RETURNVALUE_DROPTWOHANDEDITEM, | |
| 432 | + | RETURNVALUE_BOTHHANDSNEEDTOBEFREE, | |
| 433 | + | RETURNVALUE_CANONLYUSEONEWEAPON, | |
| 434 | + | RETURNVALUE_NEEDEXCHANGE, | |
| 435 | + | RETURNVALUE_CANNOTBEDRESSED, | |
| 436 | + | RETURNVALUE_PUTTHISOBJECTINYOURHAND, | |
| 437 | + | RETURNVALUE_PUTTHISOBJECTINBOTHHANDS, | |
| 438 | + | RETURNVALUE_TOOFARAWAY, | |
| 439 | + | RETURNVALUE_FIRSTGODOWNSTAIRS, | |
| 440 | + | RETURNVALUE_FIRSTGOUPSTAIRS, | |
| 441 | + | RETURNVALUE_CONTAINERNOTENOUGHROOM, | |
| 442 | + | RETURNVALUE_NOTENOUGHCAPACITY, | |
| 443 | + | RETURNVALUE_CANNOTPICKUP, | |
| 444 | + | RETURNVALUE_THISISIMPOSSIBLE, | |
| 445 | + | RETURNVALUE_DEPOTISFULL, | |
| 446 | + | RETURNVALUE_CREATUREDOESNOTEXIST, | |
| 447 | + | RETURNVALUE_CANNOTUSETHISOBJECT, | |
| 448 | + | RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE, | |
| 449 | + | RETURNVALUE_NOTREQUIREDLEVELTOUSERUNE, | |
| 450 | + | RETURNVALUE_YOUAREALREADYTRADING, | |
| 451 | + | RETURNVALUE_THISPLAYERISALREADYTRADING, | |
| 452 | + | RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT, | |
| 453 | + | RETURNVALUE_DIRECTPLAYERSHOOT, | |
| 454 | + | RETURNVALUE_NOTENOUGHLEVEL, | |
| 455 | + | RETURNVALUE_NOTENOUGHMAGICLEVEL, | |
| 456 | + | RETURNVALUE_NOTENOUGHMANA, | |
| 457 | + | RETURNVALUE_NOTENOUGHSOUL, | |
| 458 | + | RETURNVALUE_YOUAREEXHAUSTED, | |
| 459 | + | RETURNVALUE_YOUCANNOTUSEOBJECTSTHATFAST, | |
| 460 | + | RETURNVALUE_PLAYERISNOTREACHABLE, | |
| 461 | + | RETURNVALUE_CANONLYUSETHISRUNEONCREATURES, | |
| 462 | + | RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE, | |
| 463 | + | RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER, | |
| 464 | + | RETURNVALUE_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE, | |
| 465 | + | RETURNVALUE_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE, | |
| 466 | + | RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE, | |
| 467 | + | RETURNVALUE_YOUCANONLYUSEITONCREATURES, | |
| 468 | + | RETURNVALUE_CREATUREISNOTREACHABLE, | |
| 469 | + | RETURNVALUE_TURNSECUREMODETOATTACKUNMARKEDPLAYERS, | |
| 470 | + | RETURNVALUE_YOUNEEDPREMIUMACCOUNT, | |
| 471 | + | RETURNVALUE_YOUNEEDTOLEARNTHISSPELL, | |
| 472 | + | RETURNVALUE_YOURVOCATIONCANNOTUSETHISSPELL, | |
| 473 | + | RETURNVALUE_YOUNEEDAWEAPONTOUSETHISSPELL, | |
| 474 | + | RETURNVALUE_PLAYERISPZLOCKEDLEAVEPVPZONE, | |
| 475 | + | RETURNVALUE_PLAYERISPZLOCKEDENTERPVPZONE, | |
| 476 | + | RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE, | |
| 477 | + | RETURNVALUE_YOUCANNOTLOGOUTHERE, | |
| 478 | + | RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL, | |
| 479 | + | RETURNVALUE_CANNOTCONJUREITEMHERE, | |
| 480 | + | RETURNVALUE_YOUNEEDTOSPLITYOURSPEARS, | |
| 481 | + | RETURNVALUE_NAMEISTOOAMBIGUOUS, | |
| 482 | + | RETURNVALUE_CANONLYUSEONESHIELD, | |
| 483 | + | RETURNVALUE_NOPARTYMEMBERSINRANGE, | |
| 484 | + | RETURNVALUE_YOUARENOTTHEOWNER, | |
| 485 | + | RETURNVALUE_NOSUCHRAIDEXISTS, | |
| 486 | + | RETURNVALUE_ANOTHERRAIDISALREADYEXECUTING, | |
| 487 | + | RETURNVALUE_TRADEPLAYERFARAWAY, | |
| 488 | + | RETURNVALUE_YOUDONTOWNTHISHOUSE, | |
| 489 | + | RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE, | |
| 490 | + | RETURNVALUE_TRADEPLAYERHIGHESTBIDDER, | |
| 491 | + | RETURNVALUE_YOUCANNOTTRADETHISHOUSE, | |
| 492 | + | RETURNVALUE_YOUDONTHAVEREQUIREDPROFESSION, | |
| 493 | + | }; | |
| 494 | + | ||
| 495 | + | enum SpeechBubble_t { | |
| 496 | + | SPEECHBUBBLE_NONE = 0, | |
| 497 | + | SPEECHBUBBLE_NORMAL = 1, | |
| 498 | + | SPEECHBUBBLE_TRADE = 2, | |
| 499 | + | SPEECHBUBBLE_QUEST = 3, | |
| 500 | + | SPEECHBUBBLE_QUESTTRADER = 4, | |
| 501 | + | }; | |
| 502 | + | ||
| 503 | + | enum MapMark_t { | |
| 504 | + | MAPMARK_TICK = 0, | |
| 505 | + | MAPMARK_QUESTION = 1, | |
| 506 | + | MAPMARK_EXCLAMATION = 2, | |
| 507 | + | MAPMARK_STAR = 3, | |
| 508 | + | MAPMARK_CROSS = 4, | |
| 509 | + | MAPMARK_TEMPLE = 5, | |
| 510 | + | MAPMARK_KISS = 6, | |
| 511 | + | MAPMARK_SHOVEL = 7, | |
| 512 | + | MAPMARK_SWORD = 8, | |
| 513 | + | MAPMARK_FLAG = 9, | |
| 514 | + | MAPMARK_LOCK = 10, | |
| 515 | + | MAPMARK_BAG = 11, | |
| 516 | + | MAPMARK_SKULL = 12, | |
| 517 | + | MAPMARK_DOLLAR = 13, | |
| 518 | + | MAPMARK_REDNORTH = 14, | |
| 519 | + | MAPMARK_REDSOUTH = 15, | |
| 520 | + | MAPMARK_REDEAST = 16, | |
| 521 | + | MAPMARK_REDWEST = 17, | |
| 522 | + | MAPMARK_GREENNORTH = 18, | |
| 523 | + | MAPMARK_GREENSOUTH = 19, | |
| 524 | + | }; | |
| 525 | + | ||
| 526 | + | struct OutfitOffset { | |
| 527 | + | int16_t lookX = 0; | |
| 528 | + | int16_t lookY = 0; | |
| 529 | + | int16_t meleeX = 0; | |
| 530 | + | int16_t meleeY = 0; | |
| 531 | + | int16_t rangeX = 0; | |
| 532 | + | int16_t rangeY = 0; | |
| 533 | + | }; | |
| 534 | + | ||
| 535 | + | struct Outfit_t { | |
| 536 | + | uint16_t lookType = 0; | |
| 537 | + | uint16_t lookTypeEx = 0; | |
| 538 | + | uint16_t lookMount = 0; | |
| 539 | + | uint16_t lookWings = 0; | |
| 540 | + | uint16_t lookAura = 0; | |
| 541 | + | uint16_t lookShader = 0; | |
| 542 | + | uint16_t lookRuneEffect = 0; | |
| 543 | + | uint8_t lookHead = 0; | |
| 544 | + | uint8_t lookBody = 0; | |
| 545 | + | uint8_t lookLegs = 0; | |
| 546 | + | uint8_t lookFeet = 0; | |
| 547 | + | uint8_t lookAddons = 0; | |
| 548 | + | uint8_t attackRange = 1; | |
| 549 | + | OutfitOffset offsets[4]; | |
| 550 | + | }; | |
| 551 | + | ||
| 552 | + | struct LightInfo { | |
| 553 | + | uint8_t level = 0; | |
| 554 | + | uint8_t color = 0; | |
| 555 | + | constexpr LightInfo() = default; | |
| 556 | + | constexpr LightInfo(uint8_t level, uint8_t color) | |
| 557 | + | : level(level), color(color) {} | |
| 558 | + | }; | |
| 559 | + | ||
| 560 | + | struct ShopInfo { | |
| 561 | + | uint16_t itemId; | |
| 562 | + | int32_t subType; | |
| 563 | + | uint32_t buyPrice; | |
| 564 | + | uint32_t sellPrice; | |
| 565 | + | std::string realName; | |
| 566 | + | ||
| 567 | + | ShopInfo() { | |
| 568 | + | itemId = 0; | |
| 569 | + | subType = 1; | |
| 570 | + | buyPrice = 0; | |
| 571 | + | sellPrice = 0; | |
| 572 | + | } | |
| 573 | + | ||
| 574 | + | ShopInfo(uint16_t itemId, int32_t subType = 0, uint32_t buyPrice = 0, | |
| 575 | + | uint32_t sellPrice = 0, std::string realName = "") | |
| 576 | + | : itemId(itemId), subType(subType), buyPrice(buyPrice), | |
| 577 | + | sellPrice(sellPrice), realName(std::move(realName)) {} | |
| 578 | + | }; | |
| 579 | + | ||
| 580 | + | struct MarketOffer { | |
| 581 | + | uint32_t price; | |
| 582 | + | uint32_t timestamp; | |
| 583 | + | uint16_t amount; | |
| 584 | + | uint16_t counter; | |
| 585 | + | uint16_t itemId; | |
| 586 | + | std::string playerName; | |
| 587 | + | }; | |
| 588 | + | ||
| 589 | + | struct MarketOfferEx { | |
| 590 | + | MarketOfferEx() = default; | |
| 591 | + | MarketOfferEx(MarketOfferEx &&other) | |
| 592 | + | : id(other.id), playerId(other.playerId), timestamp(other.timestamp), | |
| 593 | + | price(other.price), amount(other.amount), counter(other.counter), | |
| 594 | + | itemId(other.itemId), type(other.type), | |
| 595 | + | playerName(std::move(other.playerName)) {} | |
| 596 | + | ||
| 597 | + | uint32_t id; | |
| 598 | + | uint32_t playerId; | |
| 599 | + | uint32_t timestamp; | |
| 600 | + | uint32_t price; | |
| 601 | + | uint16_t amount; | |
| 602 | + | uint16_t counter; | |
| 603 | + | uint16_t itemId; | |
| 604 | + | MarketAction_t type; | |
| 605 | + | std::string playerName; | |
| 606 | + | }; | |
| 607 | + | ||
| 608 | + | struct HistoryMarketOffer { | |
| 609 | + | uint32_t timestamp; | |
| 610 | + | uint32_t price; | |
| 611 | + | uint16_t itemId; | |
| 612 | + | uint16_t amount; | |
| 613 | + | MarketOfferState_t state; | |
| 614 | + | }; | |
| 615 | + | ||
| 616 | + | struct MarketStatistics { | |
| 617 | + | MarketStatistics() { | |
| 618 | + | numTransactions = 0; | |
| 619 | + | highestPrice = 0; | |
| 620 | + | totalPrice = 0; | |
| 621 | + | lowestPrice = 0; | |
| 622 | + | } | |
| 623 | + | ||
| 624 | + | uint32_t numTransactions; | |
| 625 | + | uint32_t highestPrice; | |
| 626 | + | uint64_t totalPrice; | |
| 627 | + | uint32_t lowestPrice; | |
| 628 | + | }; | |
| 629 | + | ||
| 630 | + | struct ModalWindow { | |
| 631 | + | std::list<std::pair<std::string, uint8_t>> buttons, choices; | |
| 632 | + | std::string title, message; | |
| 633 | + | uint32_t id; | |
| 634 | + | uint8_t defaultEnterButton, defaultEscapeButton; | |
| 635 | + | bool priority; | |
| 636 | + | ||
| 637 | + | ModalWindow(uint32_t id, std::string title, std::string message) | |
| 638 | + | : title(std::move(title)), message(std::move(message)), id(id), | |
| 639 | + | defaultEnterButton(0xFF), defaultEscapeButton(0xFF), priority(false) {} | |
| 640 | + | }; | |
| 641 | + | ||
| 642 | + | enum CombatOrigin { | |
| 643 | + | ORIGIN_NONE, | |
| 644 | + | ORIGIN_CONDITION, | |
| 645 | + | ORIGIN_SPELL, | |
| 646 | + | ORIGIN_MELEE, | |
| 647 | + | ORIGIN_RANGED, | |
| 648 | + | }; | |
| 649 | + | ||
| 650 | + | struct CombatDamage { | |
| 651 | + | struct { | |
| 652 | + | CombatType_t type; | |
| 653 | + | int32_t value; | |
| 654 | + | } primary, secondary; | |
| 655 | + | ||
| 656 | + | CombatOrigin origin; | |
| 657 | + | BlockType_t blockType; | |
| 658 | + | bool critical; | |
| 659 | + | CombatDamage() { | |
| 660 | + | origin = ORIGIN_NONE; | |
| 661 | + | blockType = BLOCK_NONE; | |
| 662 | + | primary.type = secondary.type = COMBAT_NONE; | |
| 663 | + | primary.value = secondary.value = 0; | |
| 664 | + | critical = false; | |
| 665 | + | } | |
| 666 | + | }; | |
| 667 | + | ||
| 668 | + | using MarketOfferList = std::list<MarketOffer>; | |
| 669 | + | using HistoryMarketOfferList = std::list<HistoryMarketOffer>; | |
| 670 | + | using ShopInfoList = std::list<ShopInfo>; | |
| 671 | + | ||
| 672 | + | enum MonstersEvent_t : uint8_t { | |
| 673 | + | MONSTERS_EVENT_NONE = 0, | |
| 674 | + | MONSTERS_EVENT_THINK = 1, | |
| 675 | + | MONSTERS_EVENT_APPEAR = 2, | |
| 676 | + | MONSTERS_EVENT_DISAPPEAR = 3, | |
| 677 | + | MONSTERS_EVENT_MOVE = 4, | |
| 678 | + | MONSTERS_EVENT_SAY = 5, | |
| 679 | + | }; | |
| 680 | + | ||
| 681 | + | #endif |