aboutsummaryrefslogtreecommitdiff
path: root/src/script/lua_api
Commit message (Collapse)AuthorAge
* Expose getPointedThing to LuaDániel Juhász2017-07-07
| | | | | | This commit introduces Raycast, a Lua user object, which can be used to perform a raycast on the map. The ray is continuable, so one can also get hidden nodes (for example to see trough glass).
* Main Menu: Allow copying directories from non-Minetest locations (#6095)Elijah Duffy2017-07-04
| | | | | Allow `core.copy_dir` (main menu API) to copy directories from a non-Minetest location. The check to disallow copying to non-Minetest locations is retained.
* Create a filesystem abstraction layer for CSM and only allow accessing files ↵red-0012017-06-30
| | | | | | | | | | | | | | | | | | that are scanned into it. (#5965) * Load client-side mods into memory before executing them. This removes the remaining filesystem access that client-sided mods had and it will hopefully make then more secure. * Lua Virtual filesystem: don't load the files into memory just scan the filenames into memory. * Fix the issues with backtrace * fix most of the issues * fix code style. * add a comment
* Isolate irrlicht references and use a singleton (#6041)Loïc Blot2017-06-26
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * Add Device3D class which will contain IrrlichtDevice interface move getSupportedVideoDrivers to Device3D Add Device3D singleton & use it in various places Rename Device3D to Rendering engine & add helper functions to various device pointers More singleton work RenderingEngine owns draw_load_screen move draw functions to RenderingEngine Reduce IrrlichtDevice exposure and guienvironment RenderingEngine: Expose get_timer_time() to remove device from guiEngine Make irrlichtdevice & scene manager less exposed * Code style fixes * Move porting::getVideoDriverName, getVideoDriverFriendlyName, getDisplayDensity, getDisplaySize to RenderingEngine Fix XORG_USED macro -> RenderingEngine + create_engine_device from RenderingEngine constructor directly * enum paralax => enum parallax
* Fix CSM crash caused by move to C++11. (#6027)red-0012017-06-22
|
* Automatic item and node colorization (#5640)Dániel Juhász2017-06-20
| | | | | | | | | | | | | | | | | * Automatic item and node colorization Now nodes with a palette yield colored item stacks, and colored items place colored nodes by default. The client predicts the colorization. * Backwards compatibility * Use nil * Style fixes * Fix code style * Document changes
* Fix 2 warnings reported by GCCLoic Blot2017-06-20
| | | | | * ClientEnvironment::m_irr is not used anymore since a recent cleanup * l_vmanip constructor ordering
* C++11 cleanup on constructors (#6000)Vincent Glize2017-06-19
| | | | * C++11 cleanup on constructors dir script
* find_nodes_in_area: Extend maximal count to U32_MAX (#5277)SmallJoker2017-06-19
| | | | | | | Extend documentation, limit area volume Remove u16 count limitation * Prevent integer overflow, replace minp/maxp with pos1/pos2
* Client::makeScreenshot: remove device paramLoic Blot2017-06-19
| | | | We already have the device param as class member
* Sound: Add pitch option (#5960)Rui2017-06-11
| | | | * Sound: Add pitch option
* Improve the path select GUI (#5852)red-0012017-06-11
| | | | | | | | | | | - Allow lua to chose whatever directories or files can be selected - Fix selecting directories - Rename dialog to `guiPathSelectMenu` from `guiFileSelectMenu` - Rename lua function for opening the menu from `show_file_open_dialog` to `show_path_select_dialog` - Remove duplicate code and fix code style. Related changes - fix `clang-format` whitelist. - Regenerate minetest.conf.example
* [CSM] Add function to get player privileges (#5933)red-0012017-06-07
| | | | | | * [CSM] Add function to get player privileges + move related help functions to common * Added @Zeno- const
* Main Menu: Add get_clientmodpath API (#5912)Elijah Duffy2017-06-06
| | | Add `core.get_clientmodpath` to main menu API (also possible in async calls).
* C++11 patchset 2: remove util/cpp11.h and util/cpp11_container.h (#5821)Loïc Blot2017-06-04
|
* Minetest for C++11 (CMakeLists + Travis)Loic Blot2017-06-04
| | | | | | | | | | | * Move GCC to GCC 6 & GCC 7 * Move Clang to Clang 3.6 & Clang 4.0 * LINT moves from Clang 3.9 to Clang 4.0 * Move XCode 7.3 to 8.0 * Use more travis tricks to install compilers instead of adding complexity to our build script * Clang format fixes on checked files (compat Cpp11 instead of Cpp03) * Mingw GCC update from 4.8.4 to 5.3 (Ubuntu Xenial) * Drop mingw cmake generated files and add them to gitignore
* Enhance ABM performance a little bit by removing two std::set copy (#5815)Loïc Blot2017-05-25
| | | | | | | | | | * Enhance ABM performance a little bit by removing two std::set copy * ActiveBlockModifier::getTriggerContents now returns a const ref * ActiveBlockModifier::getRequiredNeighbors now returns a const ref * ActiveBlockModifier::getRequiredNeighbors is now purely virtual * Little code style fix
* Fix wrong return value in get_sky Lua call since ↵Loic Blot2017-05-23
| | | | | | ad9fcf859ec2347325830e09504ae96968b51ea8 Fix #5803
* LINT fix & check all files with clang-formatLoic Blot2017-05-22
| | | | Seems the diff mode doesn't work well, PR are detected as working whereas in master it's shown it's problematic (and really problematic). Use same check everywhere
* [CSM] Add send_chat_message and run_server_chatcommand API functions (#5747)Pierre-Adrien Langrognet2017-05-21
| | | | | | | | | | | | * [CSM] Add send_chat_message and run_server_chatcommand API functions * Add client-side chat message rate limiting * Limit out chat queue size * [CSM] Add minetest.clear_out_chat_queue API function and .clear_chat_queue chatcommand * Last fixes/cleanups before merge
* Fix LINT broken by dfa0c15ce045705f05487d623dc7beca6c945b4bLoic Blot2017-05-21
|
* [CSM] Add function to get the definition of items (#5732)bigfoot5472017-05-21
| | | | | Add node def and item def documentation. Please be ready for merge!
* [CSM] Correct the log destination of print() (#5784)SmallJoker2017-05-20
|
* Private nodemeta (#5702)sfan52017-05-10
| | | | * Private node metadata that isn't sent to the client
* read_schematic_replacements: ensure fields are strings (#5726)Loïc Blot2017-05-08
| | | | | | | | * read_schematic_replacements: ensure fields are strings add a type check before reading strings on read_schematic_replacements deserializer * throw LuaError instead of asserting the whole client
* Player attrs: permits to remove an attribute by setting value to nil (#5716)Loïc Blot2017-05-07
| | | | | | | | | | * Player attrs: permits to remove an attribute by setting value to nil When doing player:set_attribute("attr", nil) remove attribute Also remove a useless check on C++ API part (already done by checkplayer) Fix #5709
* LINT fix since d0678948165768472fc940c03e78cba787f49ea5Loic Blot2017-05-07
|
* Replace occurrence of luaL_reg in l_camera.cppT0ny22017-05-07
| | | | | Related to commit 41c5483. Replace an occurrence of luaL_reg in src/script/lua_api/l_camera.cpp (added by commit de028fc).
* Clean up numeric.h and split FacePositionCache from itShadowNinja2017-05-06
| | | | | I also optiized FacePositionCache a bit: I removed a map lookup and vector copy from both branches of getFacePosition.
* Use a settings object for the main settingsShadowNinja2017-05-06
| | | | | | | This unifies the settings APIs. This also unifies the sync and async registration APIs, since the async registration API did not support adding non-functions to the API table.
* Fix codestyle since CSM Camera APILoic Blot2017-05-06
|
* [CSM] Add camera API (#5609)bigfoot5472017-05-05
| | | | | | | * [CSM] Add camera API roper rebase & squash * Address nerzhul's review
* Add option to also check the center to `find_node_near` (#5255)red-0012017-05-04
| | | | | * Add option to also check the center to `find_node_near`
* Add function to get server info.red-0012017-05-04
|
* Set sky API: Add bool for clouds in front of custom skyboxparamat2017-05-02
| | | | | | | Default true. Add 'm_clouds_enabled' bool to sky.h, set from new bool in 'set sky' API. Make 'getCloudsVisible()' depend on 'm_clouds_enabled' instead of 'm_visible' (whether normal sky is visible).
* Sneak: Improve and fix various thingsSmallJoker2017-05-03
| | | | | | Remove useless `got_teleported`. Fix jitter when walking against the sneak limits. Fix damage evading on sneak ladders.
* Sound API: Add fading soundsBrandon2017-05-03
|
* Add clouds APIBen Deutsch2017-04-30
|
* Fix visual slide issue with set_detach, fixes #5620shivajiva1012017-04-29
|
* [CSM] add screenshot api lua (#5674)Vincent Glize2017-04-29
| | | | * [CSM] add screenshot api lua
* Allow scripts to get the client protocol version in non-debug builds. (#5649)red-0012017-04-27
|
* Rename Scripting API files for consistencyShadowNinja2017-04-25
|
* Fix various points reported by cppcheck (#5656)Loïc Blot2017-04-25
| | | | | | | | | | | | * Fix various performance issues reported by cppcheck + code style (CI) * Make CI happy with code style on master * guiFileSelectMenu: remove useless includes * some performance fixes pointed by cppcheck * remove some useless casts * TextDest: remove unused setFormSpec function * Fix various iterator post-increment reported by cppcheck
* Player data to Database (#5475)Loïc Blot2017-04-23
| | | | | | | | | | | | * Player data to Database Add player data into databases (SQLite3 & PG only) PostgreSQL & SQLite: better POO Design for databases Add --migrate-players argument to server + deprecation warning * Remove players directory if empty
* lua: remove core.cause_error call (#5637)Loïc Blot2017-04-22
| | | it was used in minimal to trigger core crash, not very useful
* Light update for map blocksDániel Juhász2017-04-20
| | | | | | | | | This is not really different from the light update of a voxel manipulator. This update does not assume that the lighting was correct before, therefore it is useful for correction. Also expose this function to the Lua API for light correction, and allow voxel manipulators not to update the light.
* Fix various copy instead of const ref reported by cppcheck (#5615)Loïc Blot2017-04-19
| | | | * Also remove InventoryList::peekItem unused function * Fix some post increment to preincrement reported by cppcheck
* Sneak: Add option for old move codeparamat2017-04-17
| | | | | | Temporary option for the old move code for specific old sneak behaviour. Enabled by setting the added 'new move' physics override to false. By default 'new move' is true.
* Implement delayed server shutdown with cancelation (#4664)Loïc Blot2017-04-15
|
* Minimap: Do a double-typecast to fix compiling with MSVCSmallJoker2017-04-15
|
span class="hl opt">< BUSY_FATAL_TRESHOLD; } Database_SQLite3::Database_SQLite3(const std::string &savedir, const std::string &dbname) : m_savedir(savedir), m_dbname(dbname) { } void Database_SQLite3::beginSave() { verifyDatabase(); SQLRES(sqlite3_step(m_stmt_begin), SQLITE_DONE, "Failed to start SQLite3 transaction"); sqlite3_reset(m_stmt_begin); } void Database_SQLite3::endSave() { verifyDatabase(); SQLRES(sqlite3_step(m_stmt_end), SQLITE_DONE, "Failed to commit SQLite3 transaction"); sqlite3_reset(m_stmt_end); } void Database_SQLite3::openDatabase() { if (m_database) return; std::string dbp = m_savedir + DIR_DELIM + m_dbname + ".sqlite"; // Open the database connection if (!fs::CreateAllDirs(m_savedir)) { infostream << "Database_SQLite3: Failed to create directory \"" << m_savedir << "\"" << std::endl; throw FileNotGoodException("Failed to create database " "save directory"); } bool needs_create = !fs::PathExists(dbp); SQLOK(sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL), std::string("Failed to open SQLite3 database file ") + dbp); SQLOK(sqlite3_busy_handler(m_database, Database_SQLite3::busyHandler, m_busy_handler_data), "Failed to set SQLite3 busy handler"); if (needs_create) { createDatabase(); } std::string query_str = std::string("PRAGMA synchronous = ") + itos(g_settings->getU16("sqlite_synchronous")); SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL), "Failed to modify sqlite3 synchronous mode"); SQLOK(sqlite3_exec(m_database, "PRAGMA foreign_keys = ON", NULL, NULL, NULL), "Failed to enable sqlite3 foreign key support"); } void Database_SQLite3::verifyDatabase() { if (m_initialized) return; openDatabase(); PREPARE_STATEMENT(begin, "BEGIN;"); PREPARE_STATEMENT(end, "COMMIT;"); initStatements(); m_initialized = true; } Database_SQLite3::~Database_SQLite3() { FINALIZE_STATEMENT(m_stmt_begin) FINALIZE_STATEMENT(m_stmt_end) SQLOK_ERRSTREAM(sqlite3_close(m_database), "Failed to close database"); } /* * Map database */ MapDatabaseSQLite3::MapDatabaseSQLite3(const std::string &savedir): Database_SQLite3(savedir, "map"), MapDatabase() { } MapDatabaseSQLite3::~MapDatabaseSQLite3() { FINALIZE_STATEMENT(m_stmt_read) FINALIZE_STATEMENT(m_stmt_write) FINALIZE_STATEMENT(m_stmt_list) FINALIZE_STATEMENT(m_stmt_delete) } void MapDatabaseSQLite3::createDatabase() { assert(m_database); // Pre-condition SQLOK(sqlite3_exec(m_database, "CREATE TABLE IF NOT EXISTS `blocks` (\n" " `pos` INT PRIMARY KEY,\n" " `data` BLOB\n" ");\n", NULL, NULL, NULL), "Failed to create database table"); } void MapDatabaseSQLite3::initStatements() { PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1"); #ifdef __ANDROID__ PREPARE_STATEMENT(write, "INSERT INTO `blocks` (`pos`, `data`) VALUES (?, ?)"); #else PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`pos`, `data`) VALUES (?, ?)"); #endif PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?"); PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`"); verbosestream << "ServerMap: SQLite3 database opened." << std::endl; } inline void MapDatabaseSQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index) { SQLOK(sqlite3_bind_int64(stmt, index, getBlockAsInteger(pos)), "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__)); } bool MapDatabaseSQLite3::deleteBlock(const v3s16 &pos) { verifyDatabase(); bindPos(m_stmt_delete, pos); bool good = sqlite3_step(m_stmt_delete) == SQLITE_DONE; sqlite3_reset(m_stmt_delete); if (!good) { warningstream << "deleteBlock: Block failed to delete " << PP(pos) << ": " << sqlite3_errmsg(m_database) << std::endl; } return good; } bool MapDatabaseSQLite3::saveBlock(const v3s16 &pos, const std::string &data) { verifyDatabase(); #ifdef __ANDROID__ /** * Note: For some unknown reason SQLite3 fails to REPLACE blocks on Android, * deleting them and then inserting works. */ bindPos(m_stmt_read, pos); if (sqlite3_step(m_stmt_read) == SQLITE_ROW) { deleteBlock(pos); } sqlite3_reset(m_stmt_read); #endif bindPos(m_stmt_write, pos); SQLOK(sqlite3_bind_blob(m_stmt_write, 2, data.data(), data.size(), NULL), "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__)); SQLRES(sqlite3_step(m_stmt_write), SQLITE_DONE, "Failed to save block") sqlite3_reset(m_stmt_write); return true; } void MapDatabaseSQLite3::loadBlock(const v3s16 &pos, std::string *block) { verifyDatabase(); bindPos(m_stmt_read, pos); if (sqlite3_step(m_stmt_read) != SQLITE_ROW) { sqlite3_reset(m_stmt_read); return; } const char *data = (const char *) sqlite3_column_blob(m_stmt_read, 0); size_t len = sqlite3_column_bytes(m_stmt_read, 0); if (data) block->assign(data, len); else block->clear(); sqlite3_step(m_stmt_read); // We should never get more than 1 row, so ok to reset sqlite3_reset(m_stmt_read); } void MapDatabaseSQLite3::listAllLoadableBlocks(std::vector<v3s16> &dst) { verifyDatabase(); while (sqlite3_step(m_stmt_list) == SQLITE_ROW) dst.push_back(getIntegerAsBlock(sqlite3_column_int64(m_stmt_list, 0))); sqlite3_reset(m_stmt_list); } /* * Player Database */ PlayerDatabaseSQLite3::PlayerDatabaseSQLite3(const std::string &savedir): Database_SQLite3(savedir, "players"), PlayerDatabase() { } PlayerDatabaseSQLite3::~PlayerDatabaseSQLite3() { FINALIZE_STATEMENT(m_stmt_player_load) FINALIZE_STATEMENT(m_stmt_player_add) FINALIZE_STATEMENT(m_stmt_player_update) FINALIZE_STATEMENT(m_stmt_player_remove) FINALIZE_STATEMENT(m_stmt_player_list) FINALIZE_STATEMENT(m_stmt_player_add_inventory) FINALIZE_STATEMENT(m_stmt_player_add_inventory_items) FINALIZE_STATEMENT(m_stmt_player_remove_inventory) FINALIZE_STATEMENT(m_stmt_player_remove_inventory_items) FINALIZE_STATEMENT(m_stmt_player_load_inventory) FINALIZE_STATEMENT(m_stmt_player_load_inventory_items) FINALIZE_STATEMENT(m_stmt_player_metadata_load) FINALIZE_STATEMENT(m_stmt_player_metadata_add) FINALIZE_STATEMENT(m_stmt_player_metadata_remove) }; void PlayerDatabaseSQLite3::createDatabase() { assert(m_database); // Pre-condition SQLOK(sqlite3_exec(m_database, "CREATE TABLE IF NOT EXISTS `player` (" "`name` VARCHAR(50) NOT NULL," "`pitch` NUMERIC(11, 4) NOT NULL," "`yaw` NUMERIC(11, 4) NOT NULL," "`posX` NUMERIC(11, 4) NOT NULL," "`posY` NUMERIC(11, 4) NOT NULL," "`posZ` NUMERIC(11, 4) NOT NULL," "`hp` INT NOT NULL," "`breath` INT NOT NULL," "`creation_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP," "`modification_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP," "PRIMARY KEY (`name`));", NULL, NULL, NULL), "Failed to create player table"); SQLOK(sqlite3_exec(m_database, "CREATE TABLE IF NOT EXISTS `player_metadata` (" " `player` VARCHAR(50) NOT NULL," " `metadata` VARCHAR(256) NOT NULL," " `value` TEXT," " PRIMARY KEY(`player`, `metadata`)," " FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );", NULL, NULL, NULL), "Failed to create player metadata table"); SQLOK(sqlite3_exec(m_database, "CREATE TABLE IF NOT EXISTS `player_inventories` (" " `player` VARCHAR(50) NOT NULL," " `inv_id` INT NOT NULL," " `inv_width` INT NOT NULL," " `inv_name` TEXT NOT NULL DEFAULT ''," " `inv_size` INT NOT NULL," " PRIMARY KEY(player, inv_id)," " FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );", NULL, NULL, NULL), "Failed to create player inventory table"); SQLOK(sqlite3_exec(m_database, "CREATE TABLE `player_inventory_items` (" " `player` VARCHAR(50) NOT NULL," " `inv_id` INT NOT NULL," " `slot_id` INT NOT NULL," " `item` TEXT NOT NULL DEFAULT ''," " PRIMARY KEY(player, inv_id, slot_id)," " FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );", NULL, NULL, NULL), "Failed to create player inventory items table"); } void PlayerDatabaseSQLite3::initStatements() { PREPARE_STATEMENT(player_load, "SELECT `pitch`, `yaw`, `posX`, `posY`, `posZ`, `hp`, " "`breath`" "FROM `player` WHERE `name` = ?") PREPARE_STATEMENT(player_add, "INSERT INTO `player` (`name`, `pitch`, `yaw`, `posX`, " "`posY`, `posZ`, `hp`, `breath`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)") PREPARE_STATEMENT(player_update, "UPDATE `player` SET `pitch` = ?, `yaw` = ?, " "`posX` = ?, `posY` = ?, `posZ` = ?, `hp` = ?, `breath` = ?, " "`modification_date` = CURRENT_TIMESTAMP WHERE `name` = ?") PREPARE_STATEMENT(player_remove, "DELETE FROM `player` WHERE `name` = ?") PREPARE_STATEMENT(player_list, "SELECT `name` FROM `player`") PREPARE_STATEMENT(player_add_inventory, "INSERT INTO `player_inventories` " "(`player`, `inv_id`, `inv_width`, `inv_name`, `inv_size`) VALUES (?, ?, ?, ?, ?)") PREPARE_STATEMENT(player_add_inventory_items, "INSERT INTO `player_inventory_items` " "(`player`, `inv_id`, `slot_id`, `item`) VALUES (?, ?, ?, ?)") PREPARE_STATEMENT(player_remove_inventory, "DELETE FROM `player_inventories` " "WHERE `player` = ?") PREPARE_STATEMENT(player_remove_inventory_items, "DELETE FROM `player_inventory_items` " "WHERE `player` = ?") PREPARE_STATEMENT(player_load_inventory, "SELECT `inv_id`, `inv_width`, `inv_name`, " "`inv_size` FROM `player_inventories` WHERE `player` = ? ORDER BY inv_id") PREPARE_STATEMENT(player_load_inventory_items, "SELECT `slot_id`, `item` " "FROM `player_inventory_items` WHERE `player` = ? AND `inv_id` = ?") PREPARE_STATEMENT(player_metadata_load, "SELECT `metadata`, `value` FROM " "`player_metadata` WHERE `player` = ?") PREPARE_STATEMENT(player_metadata_add, "INSERT INTO `player_metadata` " "(`player`, `metadata`, `value`) VALUES (?, ?, ?)") PREPARE_STATEMENT(player_metadata_remove, "DELETE FROM `player_metadata` " "WHERE `player` = ?") verbosestream << "ServerEnvironment: SQLite3 database opened (players)." << std::endl; } bool PlayerDatabaseSQLite3::playerDataExists(const std::string &name) { verifyDatabase(); str_to_sqlite(m_stmt_player_load, 1, name); bool res = (sqlite3_step(m_stmt_player_load) == SQLITE_ROW); sqlite3_reset(m_stmt_player_load); return res; } void PlayerDatabaseSQLite3::savePlayer(RemotePlayer *player) { PlayerSAO* sao = player->getPlayerSAO(); sanity_check(sao); const v3f &pos = sao->getBasePosition(); // Begin save in brace is mandatory if (!playerDataExists(player->getName())) { beginSave(); str_to_sqlite(m_stmt_player_add, 1, player->getName()); double_to_sqlite(m_stmt_player_add, 2, sao->getLookPitch()); double_to_sqlite(m_stmt_player_add, 3, sao->getRotation().Y); double_to_sqlite(m_stmt_player_add, 4, pos.X); double_to_sqlite(m_stmt_player_add, 5, pos.Y); double_to_sqlite(m_stmt_player_add, 6, pos.Z); int64_to_sqlite(m_stmt_player_add, 7, sao->getHP()); int64_to_sqlite(m_stmt_player_add, 8, sao->getBreath()); sqlite3_vrfy(sqlite3_step(m_stmt_player_add), SQLITE_DONE); sqlite3_reset(m_stmt_player_add); } else { beginSave(); double_to_sqlite(m_stmt_player_update, 1, sao->getLookPitch()); double_to_sqlite(m_stmt_player_update, 2, sao->getRotation().Y); double_to_sqlite(m_stmt_player_update, 3, pos.X); double_to_sqlite(m_stmt_player_update, 4, pos.Y); double_to_sqlite(m_stmt_player_update, 5, pos.Z); int64_to_sqlite(m_stmt_player_update, 6, sao->getHP()); int64_to_sqlite(m_stmt_player_update, 7, sao->getBreath()); str_to_sqlite(m_stmt_player_update, 8, player->getName()); sqlite3_vrfy(sqlite3_step(m_stmt_player_update), SQLITE_DONE); sqlite3_reset(m_stmt_player_update); } // Write player inventories str_to_sqlite(m_stmt_player_remove_inventory, 1, player->getName()); sqlite3_vrfy(sqlite3_step(m_stmt_player_remove_inventory), SQLITE_DONE); sqlite3_reset(m_stmt_player_remove_inventory); str_to_sqlite(m_stmt_player_remove_inventory_items, 1, player->getName()); sqlite3_vrfy(sqlite3_step(m_stmt_player_remove_inventory_items), SQLITE_DONE); sqlite3_reset(m_stmt_player_remove_inventory_items); std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists(); std::ostringstream oss; for (u16 i = 0; i < inventory_lists.size(); i++) { const InventoryList* list = inventory_lists[i]; str_to_sqlite(m_stmt_player_add_inventory, 1, player->getName()); int_to_sqlite(m_stmt_player_add_inventory, 2, i); int_to_sqlite(m_stmt_player_add_inventory, 3, list->getWidth()); str_to_sqlite(m_stmt_player_add_inventory, 4, list->getName()); int_to_sqlite(m_stmt_player_add_inventory, 5, list->getSize()); sqlite3_vrfy(sqlite3_step(m_stmt_player_add_inventory), SQLITE_DONE); sqlite3_reset(m_stmt_player_add_inventory); for (u32 j = 0; j < list->getSize(); j++) { oss.str(""); oss.clear(); list->getItem(j).serialize(oss); std::string itemStr = oss.str(); str_to_sqlite(m_stmt_player_add_inventory_items, 1, player->getName()); int_to_sqlite(m_stmt_player_add_inventory_items, 2, i); int_to_sqlite(m_stmt_player_add_inventory_items, 3, j); str_to_sqlite(m_stmt_player_add_inventory_items, 4, itemStr); sqlite3_vrfy(sqlite3_step(m_stmt_player_add_inventory_items), SQLITE_DONE); sqlite3_reset(m_stmt_player_add_inventory_items); } } str_to_sqlite(m_stmt_player_metadata_remove, 1, player->getName()); sqlite3_vrfy(sqlite3_step(m_stmt_player_metadata_remove), SQLITE_DONE); sqlite3_reset(m_stmt_player_metadata_remove); const StringMap &attrs = sao->getMeta().getStrings(); for (const auto &attr : attrs) { str_to_sqlite(m_stmt_player_metadata_add, 1, player->getName()); str_to_sqlite(m_stmt_player_metadata_add, 2, attr.first); str_to_sqlite(m_stmt_player_metadata_add, 3, attr.second); sqlite3_vrfy(sqlite3_step(m_stmt_player_metadata_add), SQLITE_DONE); sqlite3_reset(m_stmt_player_metadata_add); } endSave(); player->onSuccessfulSave(); } bool PlayerDatabaseSQLite3::loadPlayer(RemotePlayer *player, PlayerSAO *sao) { verifyDatabase(); str_to_sqlite(m_stmt_player_load, 1, player->getName()); if (sqlite3_step(m_stmt_player_load) != SQLITE_ROW) { sqlite3_reset(m_stmt_player_load); return false; } sao->setLookPitch(sqlite_to_float(m_stmt_player_load, 0)); sao->setPlayerYaw(sqlite_to_float(m_stmt_player_load, 1)); sao->setBasePosition(sqlite_to_v3f(m_stmt_player_load, 2)); sao->setHPRaw((u16) MYMIN(sqlite_to_int(m_stmt_player_load, 5), U16_MAX)); sao->setBreath((u16) MYMIN(sqlite_to_int(m_stmt_player_load, 6), U16_MAX), false); sqlite3_reset(m_stmt_player_load); // Load inventory str_to_sqlite(m_stmt_player_load_inventory, 1, player->getName()); while (sqlite3_step(m_stmt_player_load_inventory) == SQLITE_ROW) { InventoryList *invList = player->inventory.addList( sqlite_to_string(m_stmt_player_load_inventory, 2), sqlite_to_uint(m_stmt_player_load_inventory, 3)); invList->setWidth(sqlite_to_uint(m_stmt_player_load_inventory, 1)); u32 invId = sqlite_to_uint(m_stmt_player_load_inventory, 0); str_to_sqlite(m_stmt_player_load_inventory_items, 1, player->getName()); int_to_sqlite(m_stmt_player_load_inventory_items, 2, invId); while (sqlite3_step(m_stmt_player_load_inventory_items) == SQLITE_ROW) { const std::string itemStr = sqlite_to_string(m_stmt_player_load_inventory_items, 1); if (itemStr.length() > 0) { ItemStack stack; stack.deSerialize(itemStr); invList->changeItem(sqlite_to_uint(m_stmt_player_load_inventory_items, 0), stack); } } sqlite3_reset(m_stmt_player_load_inventory_items); } sqlite3_reset(m_stmt_player_load_inventory); str_to_sqlite(m_stmt_player_metadata_load, 1, sao->getPlayer()->getName()); while (sqlite3_step(m_stmt_player_metadata_load) == SQLITE_ROW) { std::string attr = sqlite_to_string(m_stmt_player_metadata_load, 0); std::string value = sqlite_to_string(m_stmt_player_metadata_load, 1); sao->getMeta().setString(attr, value); } sao->getMeta().setModified(false); sqlite3_reset(m_stmt_player_metadata_load); return true; } bool PlayerDatabaseSQLite3::removePlayer(const std::string &name) { if (!playerDataExists(name)) return false; str_to_sqlite(m_stmt_player_remove, 1, name); sqlite3_vrfy(sqlite3_step(m_stmt_player_remove), SQLITE_DONE); sqlite3_reset(m_stmt_player_remove); return true; } void PlayerDatabaseSQLite3::listPlayers(std::vector<std::string> &res) { verifyDatabase(); while (sqlite3_step(m_stmt_player_list) == SQLITE_ROW) res.push_back(sqlite_to_string(m_stmt_player_list, 0)); sqlite3_reset(m_stmt_player_list); } /* * Auth database */ AuthDatabaseSQLite3::AuthDatabaseSQLite3(const std::string &savedir) : Database_SQLite3(savedir, "auth"), AuthDatabase() { } AuthDatabaseSQLite3::~AuthDatabaseSQLite3() { FINALIZE_STATEMENT(m_stmt_read) FINALIZE_STATEMENT(m_stmt_write) FINALIZE_STATEMENT(m_stmt_create) FINALIZE_STATEMENT(m_stmt_delete) FINALIZE_STATEMENT(m_stmt_list_names) FINALIZE_STATEMENT(m_stmt_read_privs) FINALIZE_STATEMENT(m_stmt_write_privs) FINALIZE_STATEMENT(m_stmt_delete_privs) FINALIZE_STATEMENT(m_stmt_last_insert_rowid) } void AuthDatabaseSQLite3::createDatabase() { assert(m_database); // Pre-condition SQLOK(sqlite3_exec(m_database, "CREATE TABLE IF NOT EXISTS `auth` (" "`id` INTEGER PRIMARY KEY AUTOINCREMENT," "`name` VARCHAR(32) UNIQUE," "`password` VARCHAR(512)," "`last_login` INTEGER" ");", NULL, NULL, NULL), "Failed to create auth table"); SQLOK(sqlite3_exec(m_database, "CREATE TABLE IF NOT EXISTS `user_privileges` (" "`id` INTEGER," "`privilege` VARCHAR(32)," "PRIMARY KEY (id, privilege)" "CONSTRAINT fk_id FOREIGN KEY (id) REFERENCES auth (id) ON DELETE CASCADE" ");", NULL, NULL, NULL), "Failed to create auth privileges table"); } void AuthDatabaseSQLite3::initStatements() { PREPARE_STATEMENT(read, "SELECT id, name, password, last_login FROM auth WHERE name = ?"); PREPARE_STATEMENT(write, "UPDATE auth set name = ?, password = ?, last_login = ? WHERE id = ?"); PREPARE_STATEMENT(create, "INSERT INTO auth (name, password, last_login) VALUES (?, ?, ?)"); PREPARE_STATEMENT(delete, "DELETE FROM auth WHERE name = ?"); PREPARE_STATEMENT(list_names, "SELECT name FROM auth ORDER BY name DESC"); PREPARE_STATEMENT(read_privs, "SELECT privilege FROM user_privileges WHERE id = ?"); PREPARE_STATEMENT(write_privs, "INSERT OR IGNORE INTO user_privileges (id, privilege) VALUES (?, ?)"); PREPARE_STATEMENT(delete_privs, "DELETE FROM user_privileges WHERE id = ?"); PREPARE_STATEMENT(last_insert_rowid, "SELECT last_insert_rowid()"); } bool AuthDatabaseSQLite3::getAuth(const std::string &name, AuthEntry &res) { verifyDatabase(); str_to_sqlite(m_stmt_read, 1, name); if (sqlite3_step(m_stmt_read) != SQLITE_ROW) { sqlite3_reset(m_stmt_read); return false; } res.id = sqlite_to_uint(m_stmt_read, 0); res.name = sqlite_to_string(m_stmt_read, 1); res.password = sqlite_to_string(m_stmt_read, 2); res.last_login = sqlite_to_int64(m_stmt_read, 3); sqlite3_reset(m_stmt_read); int64_to_sqlite(m_stmt_read_privs, 1, res.id); while (sqlite3_step(m_stmt_read_privs) == SQLITE_ROW) { res.privileges.emplace_back(sqlite_to_string(m_stmt_read_privs, 0)); } sqlite3_reset(m_stmt_read_privs); return true; } bool AuthDatabaseSQLite3::saveAuth(const AuthEntry &authEntry) { beginSave(); str_to_sqlite(m_stmt_write, 1, authEntry.name); str_to_sqlite(m_stmt_write, 2, authEntry.password); int64_to_sqlite(m_stmt_write, 3, authEntry.last_login); int64_to_sqlite(m_stmt_write, 4, authEntry.id); sqlite3_vrfy(sqlite3_step(m_stmt_write), SQLITE_DONE); sqlite3_reset(m_stmt_write); writePrivileges(authEntry); endSave(); return true; } bool AuthDatabaseSQLite3::createAuth(AuthEntry &authEntry) { beginSave(); // id autoincrements str_to_sqlite(m_stmt_create, 1, authEntry.name); str_to_sqlite(m_stmt_create, 2, authEntry.password); int64_to_sqlite(m_stmt_create, 3, authEntry.last_login); sqlite3_vrfy(sqlite3_step(m_stmt_create), SQLITE_DONE); sqlite3_reset(m_stmt_create); // obtain id and write back to original authEntry sqlite3_step(m_stmt_last_insert_rowid); authEntry.id = sqlite_to_uint(m_stmt_last_insert_rowid, 0); sqlite3_reset(m_stmt_last_insert_rowid); writePrivileges(authEntry); endSave(); return true; } bool AuthDatabaseSQLite3::deleteAuth(const std::string &name) { verifyDatabase(); str_to_sqlite(m_stmt_delete, 1, name); sqlite3_vrfy(sqlite3_step(m_stmt_delete), SQLITE_DONE); int changes = sqlite3_changes(m_database); sqlite3_reset(m_stmt_delete); // privileges deleted by foreign key on delete cascade return changes > 0; } void AuthDatabaseSQLite3::listNames(std::vector<std::string> &res) { verifyDatabase(); while (sqlite3_step(m_stmt_list_names) == SQLITE_ROW) { res.push_back(sqlite_to_string(m_stmt_list_names, 0)); } sqlite3_reset(m_stmt_list_names); } void AuthDatabaseSQLite3::reload() { // noop for SQLite } void AuthDatabaseSQLite3::writePrivileges(const AuthEntry &authEntry) { int64_to_sqlite(m_stmt_delete_privs, 1, authEntry.id); sqlite3_vrfy(sqlite3_step(m_stmt_delete_privs), SQLITE_DONE); sqlite3_reset(m_stmt_delete_privs); for (const std::string &privilege : authEntry.privileges) { int64_to_sqlite(m_stmt_write_privs, 1, authEntry.id); str_to_sqlite(m_stmt_write_privs, 2, privilege); sqlite3_vrfy(sqlite3_step(m_stmt_write_privs), SQLITE_DONE); sqlite3_reset(m_stmt_write_privs); } }