From 98e36d7d681abc508aa81b6f9df0c8c87c7cfe17 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 8 Jan 2017 11:01:35 +0100 Subject: Move ServerEnvironment to dedicated cpp/header files * also cleanup some unneeded inclusions --- src/serverenvironment.cpp | 2167 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2167 insertions(+) create mode 100644 src/serverenvironment.cpp (limited to 'src/serverenvironment.cpp') diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp new file mode 100644 index 000000000..6229e4cf1 --- /dev/null +++ b/src/serverenvironment.cpp @@ -0,0 +1,2167 @@ +/* +Minetest +Copyright (C) 2010-2017 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "serverenvironment.h" +#include "content_sao.h" +#include "settings.h" +#include "log.h" +#include "nodedef.h" +#include "nodemetadata.h" +#include "gamedef.h" +#include "map.h" +#include "profiler.h" +#include "raycast.h" +#include "remoteplayer.h" +#include "scripting_game.h" +#include "server.h" +#include "voxelalgorithms.h" +#include "util/serialize.h" +#include "util/basic_macros.h" +#include "util/pointedthing.h" +#include "threading/mutex_auto_lock.h" +#include "filesys.h" + +#define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" + +// A number that is much smaller than the timeout for particle spawners should/could ever be +#define PARTICLE_SPAWNER_NO_EXPIRY -1024.f + +/* + ABMWithState +*/ + +ABMWithState::ABMWithState(ActiveBlockModifier *abm_): + abm(abm_), + timer(0) +{ + // Initialize timer to random value to spread processing + float itv = abm->getTriggerInterval(); + itv = MYMAX(0.001, itv); // No less than 1ms + int minval = MYMAX(-0.51*itv, -60); // Clamp to + int maxval = MYMIN(0.51*itv, 60); // +-60 seconds + timer = myrand_range(minval, maxval); +} + +/* + LBMManager +*/ + +void LBMContentMapping::deleteContents() +{ + for (std::vector::iterator it = lbm_list.begin(); + it != lbm_list.end(); ++it) { + delete *it; + } +} + +void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef) +{ + // Add the lbm_def to the LBMContentMapping. + // Unknown names get added to the global NameIdMapping. + INodeDefManager *nodedef = gamedef->ndef(); + + lbm_list.push_back(lbm_def); + + for (std::set::const_iterator it = lbm_def->trigger_contents.begin(); + it != lbm_def->trigger_contents.end(); ++it) { + std::set c_ids; + bool found = nodedef->getIds(*it, c_ids); + if (!found) { + content_t c_id = gamedef->allocateUnknownNodeId(*it); + if (c_id == CONTENT_IGNORE) { + // Seems it can't be allocated. + warningstream << "Could not internalize node name \"" << *it + << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl; + continue; + } + c_ids.insert(c_id); + } + + for (std::set::const_iterator iit = + c_ids.begin(); iit != c_ids.end(); ++iit) { + content_t c_id = *iit; + map[c_id].push_back(lbm_def); + } + } +} + +const std::vector * +LBMContentMapping::lookup(content_t c) const +{ + container_map::const_iterator it = map.find(c); + if (it == map.end()) + return NULL; + // This first dereferences the iterator, returning + // a std::vector + // reference, then we convert it to a pointer. + return &(it->second); +} + +LBMManager::~LBMManager() +{ + for (std::map::iterator it = + m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { + delete it->second; + } + for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); + it != m_lbm_lookup.end(); ++it) { + (it->second).deleteContents(); + } +} + +void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def) +{ + // Precondition, in query mode the map isn't used anymore + FATAL_ERROR_IF(m_query_mode == true, + "attempted to modify LBMManager in query mode"); + + if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) { + throw ModError("Error adding LBM \"" + lbm_def->name + + "\": Does not follow naming conventions: " + "Only chararacters [a-z0-9_:] are allowed."); + } + + m_lbm_defs[lbm_def->name] = lbm_def; +} + +void LBMManager::loadIntroductionTimes(const std::string ×, + IGameDef *gamedef, u32 now) +{ + m_query_mode = true; + + // name -> time map. + // Storing it in a map first instead of + // handling the stuff directly in the loop + // removes all duplicate entries. + // TODO make this std::unordered_map + std::map introduction_times; + + /* + The introduction times string consists of name~time entries, + with each entry terminated by a semicolon. The time is decimal. + */ + + size_t idx = 0; + size_t idx_new; + while ((idx_new = times.find(";", idx)) != std::string::npos) { + std::string entry = times.substr(idx, idx_new - idx); + std::vector components = str_split(entry, '~'); + if (components.size() != 2) + throw SerializationError("Introduction times entry \"" + + entry + "\" requires exactly one '~'!"); + const std::string &name = components[0]; + u32 time = from_string(components[1]); + introduction_times[name] = time; + idx = idx_new + 1; + } + + // Put stuff from introduction_times into m_lbm_lookup + for (std::map::const_iterator it = introduction_times.begin(); + it != introduction_times.end(); ++it) { + const std::string &name = it->first; + u32 time = it->second; + + std::map::iterator def_it = + m_lbm_defs.find(name); + if (def_it == m_lbm_defs.end()) { + // This seems to be an LBM entry for + // an LBM we haven't loaded. Discard it. + continue; + } + LoadingBlockModifierDef *lbm_def = def_it->second; + if (lbm_def->run_at_every_load) { + // This seems to be an LBM entry for + // an LBM that runs at every load. + // Don't add it just yet. + continue; + } + + m_lbm_lookup[time].addLBM(lbm_def, gamedef); + + // Erase the entry so that we know later + // what elements didn't get put into m_lbm_lookup + m_lbm_defs.erase(name); + } + + // Now also add the elements from m_lbm_defs to m_lbm_lookup + // that weren't added in the previous step. + // They are introduced first time to this world, + // or are run at every load (introducement time hardcoded to U32_MAX). + + LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now]; + LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX]; + + for (std::map::iterator it = + m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { + if (it->second->run_at_every_load) { + lbms_running_always.addLBM(it->second, gamedef); + } else { + lbms_we_introduce_now.addLBM(it->second, gamedef); + } + } + + // Clear the list, so that we don't delete remaining elements + // twice in the destructor + m_lbm_defs.clear(); +} + +std::string LBMManager::createIntroductionTimesString() +{ + // Precondition, we must be in query mode + FATAL_ERROR_IF(m_query_mode == false, + "attempted to query on non fully set up LBMManager"); + + std::ostringstream oss; + for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); + it != m_lbm_lookup.end(); ++it) { + u32 time = it->first; + std::vector &lbm_list = it->second.lbm_list; + for (std::vector::iterator iit = lbm_list.begin(); + iit != lbm_list.end(); ++iit) { + // Don't add if the LBM runs at every load, + // then introducement time is hardcoded + // and doesn't need to be stored + if ((*iit)->run_at_every_load) + continue; + oss << (*iit)->name << "~" << time << ";"; + } + } + return oss.str(); +} + +void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp) +{ + // Precondition, we need m_lbm_lookup to be initialized + FATAL_ERROR_IF(m_query_mode == false, + "attempted to query on non fully set up LBMManager"); + v3s16 pos_of_block = block->getPosRelative(); + v3s16 pos; + MapNode n; + content_t c; + lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp); + for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) + for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) + for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) + { + n = block->getNodeNoEx(pos); + c = n.getContent(); + for (LBMManager::lbm_lookup_map::const_iterator iit = it; + iit != m_lbm_lookup.end(); ++iit) { + const std::vector *lbm_list = + iit->second.lookup(c); + if (!lbm_list) + continue; + for (std::vector::const_iterator iit = + lbm_list->begin(); iit != lbm_list->end(); ++iit) { + (*iit)->trigger(env, pos + pos_of_block, n); + } + } + } +} + +/* + ActiveBlockList +*/ + +void fillRadiusBlock(v3s16 p0, s16 r, std::set &list) +{ + v3s16 p; + for(p.X=p0.X-r; p.X<=p0.X+r; p.X++) + for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++) + for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++) + { + // limit to a sphere + if (p.getDistanceFrom(p0) <= r) { + // Set in list + list.insert(p); + } + } +} + +void ActiveBlockList::update(std::vector &active_positions, + s16 radius, + std::set &blocks_removed, + std::set &blocks_added) +{ + /* + Create the new list + */ + std::set newlist = m_forceloaded_list; + for(std::vector::iterator i = active_positions.begin(); + i != active_positions.end(); ++i) + { + fillRadiusBlock(*i, radius, newlist); + } + + /* + Find out which blocks on the old list are not on the new list + */ + // Go through old list + for(std::set::iterator i = m_list.begin(); + i != m_list.end(); ++i) + { + v3s16 p = *i; + // If not on new list, it's been removed + if(newlist.find(p) == newlist.end()) + blocks_removed.insert(p); + } + + /* + Find out which blocks on the new list are not on the old list + */ + // Go through new list + for(std::set::iterator i = newlist.begin(); + i != newlist.end(); ++i) + { + v3s16 p = *i; + // If not on old list, it's been added + if(m_list.find(p) == m_list.end()) + blocks_added.insert(p); + } + + /* + Update m_list + */ + m_list.clear(); + for(std::set::iterator i = newlist.begin(); + i != newlist.end(); ++i) + { + v3s16 p = *i; + m_list.insert(p); + } +} + +/* + ServerEnvironment +*/ + +ServerEnvironment::ServerEnvironment(ServerMap *map, + GameScripting *scriptIface, IGameDef *gamedef, + const std::string &path_world) : + m_map(map), + m_script(scriptIface), + m_gamedef(gamedef), + m_path_world(path_world), + m_send_recommended_timer(0), + m_active_block_interval_overload_skip(0), + m_game_time(0), + m_game_time_fraction_counter(0), + m_last_clear_objects_time(0), + m_recommended_send_interval(0.1), + m_max_lag_estimate(0.1) +{ +} + +ServerEnvironment::~ServerEnvironment() +{ + // Clear active block list. + // This makes the next one delete all active objects. + m_active_blocks.clear(); + + // Convert all objects to static and delete the active objects + deactivateFarObjects(true); + + // Drop/delete map + m_map->drop(); + + // Delete ActiveBlockModifiers + for (std::vector::iterator + i = m_abms.begin(); i != m_abms.end(); ++i){ + delete i->abm; + } + + // Deallocate players + for (std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { + delete (*i); + } +} + +Map & ServerEnvironment::getMap() +{ + return *m_map; +} + +ServerMap & ServerEnvironment::getServerMap() +{ + return *m_map; +} + +RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id) +{ + for (std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { + RemotePlayer *player = *i; + if (player->peer_id == peer_id) + return player; + } + return NULL; +} + +RemotePlayer *ServerEnvironment::getPlayer(const char* name) +{ + for (std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { + RemotePlayer *player = *i; + if (strcmp(player->getName(), name) == 0) + return player; + } + return NULL; +} + +void ServerEnvironment::addPlayer(RemotePlayer *player) +{ + DSTACK(FUNCTION_NAME); + /* + Check that peer_ids are unique. + Also check that names are unique. + Exception: there can be multiple players with peer_id=0 + */ + // If peer id is non-zero, it has to be unique. + if (player->peer_id != 0) + FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique"); + // Name has to be unique. + FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique"); + // Add. + m_players.push_back(player); +} + +void ServerEnvironment::removePlayer(RemotePlayer *player) +{ + for (std::vector::iterator it = m_players.begin(); + it != m_players.end(); ++it) { + if ((*it) == player) { + delete *it; + m_players.erase(it); + return; + } + } +} + +bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p) +{ + float distance = pos1.getDistanceFrom(pos2); + + //calculate normalized direction vector + v3f normalized_vector = v3f((pos2.X - pos1.X)/distance, + (pos2.Y - pos1.Y)/distance, + (pos2.Z - pos1.Z)/distance); + + //find out if there's a node on path between pos1 and pos2 + for (float i = 1; i < distance; i += stepsize) { + v3s16 pos = floatToInt(v3f(normalized_vector.X * i, + normalized_vector.Y * i, + normalized_vector.Z * i) +pos1,BS); + + MapNode n = getMap().getNodeNoEx(pos); + + if(n.param0 != CONTENT_AIR) { + if (p) { + *p = pos; + } + return false; + } + } + return true; +} + +void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason, + const std::string &str_reason, bool reconnect) +{ + for (std::vector::iterator it = m_players.begin(); + it != m_players.end(); ++it) { + RemotePlayer *player = dynamic_cast(*it); + ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id, + player->protocol_version, reason, str_reason, reconnect); + } +} + +void ServerEnvironment::saveLoadedPlayers() +{ + std::string players_path = m_path_world + DIR_DELIM "players"; + fs::CreateDir(players_path); + + for (std::vector::iterator it = m_players.begin(); + it != m_players.end(); + ++it) { + if ((*it)->checkModified()) { + (*it)->save(players_path, m_gamedef); + } + } +} + +void ServerEnvironment::savePlayer(RemotePlayer *player) +{ + std::string players_path = m_path_world + DIR_DELIM "players"; + fs::CreateDir(players_path); + + player->save(players_path, m_gamedef); +} + +RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao) +{ + bool newplayer = false; + bool found = false; + std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM; + std::string path = players_path + playername; + + RemotePlayer *player = getPlayer(playername.c_str()); + if (!player) { + player = new RemotePlayer("", m_gamedef->idef()); + newplayer = true; + } + + for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) { + //// Open file and deserialize + std::ifstream is(path.c_str(), std::ios_base::binary); + if (!is.good()) + continue; + + player->deSerialize(is, path, sao); + is.close(); + + if (player->getName() == playername) { + found = true; + break; + } + + path = players_path + playername + itos(i); + } + + if (!found) { + infostream << "Player file for player " << playername + << " not found" << std::endl; + if (newplayer) + delete player; + + return NULL; + } + + if (newplayer) { + addPlayer(player); + } + player->setModified(false); + return player; +} + +void ServerEnvironment::saveMeta() +{ + std::string path = m_path_world + DIR_DELIM "env_meta.txt"; + + // Open file and serialize + std::ostringstream ss(std::ios_base::binary); + + Settings args; + args.setU64("game_time", m_game_time); + args.setU64("time_of_day", getTimeOfDay()); + args.setU64("last_clear_objects_time", m_last_clear_objects_time); + args.setU64("lbm_introduction_times_version", 1); + args.set("lbm_introduction_times", + m_lbm_mgr.createIntroductionTimesString()); + args.setU64("day_count", m_day_count); + args.writeLines(ss); + ss<<"EnvArgsEnd\n"; + + if(!fs::safeWriteToFile(path, ss.str())) + { + infostream<<"ServerEnvironment::saveMeta(): Failed to write " + < required_neighbors; +}; + +class ABMHandler +{ +private: + ServerEnvironment *m_env; + std::vector *> m_aabms; +public: + ABMHandler(std::vector &abms, + float dtime_s, ServerEnvironment *env, + bool use_timers): + m_env(env) + { + if(dtime_s < 0.001) + return; + INodeDefManager *ndef = env->getGameDef()->ndef(); + for(std::vector::iterator + i = abms.begin(); i != abms.end(); ++i) { + ActiveBlockModifier *abm = i->abm; + float trigger_interval = abm->getTriggerInterval(); + if(trigger_interval < 0.001) + trigger_interval = 0.001; + float actual_interval = dtime_s; + if(use_timers){ + i->timer += dtime_s; + if(i->timer < trigger_interval) + continue; + i->timer -= trigger_interval; + actual_interval = trigger_interval; + } + float chance = abm->getTriggerChance(); + if(chance == 0) + chance = 1; + ActiveABM aabm; + aabm.abm = abm; + if(abm->getSimpleCatchUp()) { + float intervals = actual_interval / trigger_interval; + if(intervals == 0) + continue; + aabm.chance = chance / intervals; + if(aabm.chance == 0) + aabm.chance = 1; + } else { + aabm.chance = chance; + } + // Trigger neighbors + std::set required_neighbors_s + = abm->getRequiredNeighbors(); + for(std::set::iterator + i = required_neighbors_s.begin(); + i != required_neighbors_s.end(); ++i) + { + ndef->getIds(*i, aabm.required_neighbors); + } + // Trigger contents + std::set contents_s = abm->getTriggerContents(); + for(std::set::iterator + i = contents_s.begin(); i != contents_s.end(); ++i) + { + std::set ids; + ndef->getIds(*i, ids); + for(std::set::const_iterator k = ids.begin(); + k != ids.end(); ++k) + { + content_t c = *k; + if (c >= m_aabms.size()) + m_aabms.resize(c + 256, NULL); + if (!m_aabms[c]) + m_aabms[c] = new std::vector; + m_aabms[c]->push_back(aabm); + } + } + } + } + + ~ABMHandler() + { + for (size_t i = 0; i < m_aabms.size(); i++) + delete m_aabms[i]; + } + + // Find out how many objects the given block and its neighbours contain. + // Returns the number of objects in the block, and also in 'wider' the + // number of objects in the block and all its neighbours. The latter + // may an estimate if any neighbours are unloaded. + u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider) + { + wider = 0; + u32 wider_unknown_count = 0; + for(s16 x=-1; x<=1; x++) + for(s16 y=-1; y<=1; y++) + for(s16 z=-1; z<=1; z++) + { + MapBlock *block2 = map->getBlockNoCreateNoEx( + block->getPos() + v3s16(x,y,z)); + if(block2==NULL){ + wider_unknown_count++; + continue; + } + wider += block2->m_static_objects.m_active.size() + + block2->m_static_objects.m_stored.size(); + } + // Extrapolate + u32 active_object_count = block->m_static_objects.m_active.size(); + u32 wider_known_count = 3*3*3 - wider_unknown_count; + wider += wider_unknown_count * wider / wider_known_count; + return active_object_count; + + } + void apply(MapBlock *block) + { + if(m_aabms.empty() || block->isDummy()) + return; + + ServerMap *map = &m_env->getServerMap(); + + u32 active_object_count_wider; + u32 active_object_count = this->countObjects(block, map, active_object_count_wider); + m_env->m_added_objects = 0; + + v3s16 p0; + for(p0.X=0; p0.XgetNodeUnsafe(p0); + content_t c = n.getContent(); + + if (c >= m_aabms.size() || !m_aabms[c]) + continue; + + v3s16 p = p0 + block->getPosRelative(); + for(std::vector::iterator + i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) { + if(myrand() % i->chance != 0) + continue; + + // Check neighbors + if(!i->required_neighbors.empty()) + { + v3s16 p1; + for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++) + for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++) + for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++) + { + if(p1 == p0) + continue; + content_t c; + if (block->isValidPosition(p1)) { + // if the neighbor is found on the same map block + // get it straight from there + const MapNode &n = block->getNodeUnsafe(p1); + c = n.getContent(); + } else { + // otherwise consult the map + MapNode n = map->getNodeNoEx(p1 + block->getPosRelative()); + c = n.getContent(); + } + std::set::const_iterator k; + k = i->required_neighbors.find(c); + if(k != i->required_neighbors.end()){ + goto neighbor_found; + } + } + // No required neighbor found + continue; + } + neighbor_found: + + // Call all the trigger variations + i->abm->trigger(m_env, p, n); + i->abm->trigger(m_env, p, n, + active_object_count, active_object_count_wider); + + // Count surrounding objects again if the abms added any + if(m_env->m_added_objects > 0) { + active_object_count = countObjects(block, map, active_object_count_wider); + m_env->m_added_objects = 0; + } + } + } + } +}; + +void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) +{ + // Reset usage timer immediately, otherwise a block that becomes active + // again at around the same time as it would normally be unloaded will + // get unloaded incorrectly. (I think this still leaves a small possibility + // of a race condition between this and server::AsyncRunStep, which only + // some kind of synchronisation will fix, but it at least reduces the window + // of opportunity for it to break from seconds to nanoseconds) + block->resetUsageTimer(); + + // Get time difference + u32 dtime_s = 0; + u32 stamp = block->getTimestamp(); + if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED) + dtime_s = m_game_time - stamp; + dtime_s += additional_dtime; + + /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: " + <m_static_objects.m_stored.clear(); + // do not set changed flag to avoid unnecessary mapblock writes + } + + // Set current time as timestamp + block->setTimestampNoChangedFlag(m_game_time); + + /*infostream<<"ServerEnvironment::activateBlock(): block is " + < elapsed_timers = + block->m_node_timers.step((float)dtime_s); + if (!elapsed_timers.empty()) { + MapNode n; + for (std::vector::iterator + i = elapsed_timers.begin(); + i != elapsed_timers.end(); ++i){ + n = block->getNodeNoEx(i->position); + v3s16 p = i->position + block->getPosRelative(); + if (m_script->node_on_timer(p, n, i->elapsed)) + block->setNodeTimer(NodeTimer(i->timeout, 0, i->position)); + } + } + + /* Handle ActiveBlockModifiers */ + ABMHandler abmhandler(m_abms, dtime_s, this, false); + abmhandler.apply(block); +} + +void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm) +{ + m_abms.push_back(ABMWithState(abm)); +} + +void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm) +{ + m_lbm_mgr.addLBMDef(lbm); +} + +bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) +{ + INodeDefManager *ndef = m_gamedef->ndef(); + MapNode n_old = m_map->getNodeNoEx(p); + + // Call destructor + if (ndef->get(n_old).has_on_destruct) + m_script->node_on_destruct(p, n_old); + + // Replace node + if (!m_map->addNodeWithEvent(p, n)) + return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + + // Call post-destructor + if (ndef->get(n_old).has_after_destruct) + m_script->node_after_destruct(p, n_old); + + // Call constructor + if (ndef->get(n).has_on_construct) + m_script->node_on_construct(p, n); + + return true; +} + +bool ServerEnvironment::removeNode(v3s16 p) +{ + INodeDefManager *ndef = m_gamedef->ndef(); + MapNode n_old = m_map->getNodeNoEx(p); + + // Call destructor + if (ndef->get(n_old).has_on_destruct) + m_script->node_on_destruct(p, n_old); + + // Replace with air + // This is slightly optimized compared to addNodeWithEvent(air) + if (!m_map->removeNodeWithEvent(p)) + return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + + // Call post-destructor + if (ndef->get(n_old).has_after_destruct) + m_script->node_after_destruct(p, n_old); + + // Air doesn't require constructor + return true; +} + +bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n) +{ + if (!m_map->addNodeWithEvent(p, n, false)) + return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + + return true; +} + +void ServerEnvironment::getObjectsInsideRadius(std::vector &objects, v3f pos, float radius) +{ + for (ActiveObjectMap::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ServerActiveObject* obj = i->second; + u16 id = i->first; + v3f objectpos = obj->getBasePosition(); + if (objectpos.getDistanceFrom(pos) > radius) + continue; + objects.push_back(id); + } +} + +void ServerEnvironment::clearObjects(ClearObjectsMode mode) +{ + infostream << "ServerEnvironment::clearObjects(): " + << "Removing all active objects" << std::endl; + std::vector objects_to_remove; + for (ActiveObjectMap::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ServerActiveObject* obj = i->second; + if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) + continue; + u16 id = i->first; + // Delete static object if block is loaded + if (obj->m_static_exists) { + MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); + if (block) { + block->m_static_objects.remove(id); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_CLEAR_ALL_OBJECTS); + obj->m_static_exists = false; + } + } + // If known by some client, don't delete immediately + if (obj->m_known_by_count > 0) { + obj->m_pending_deactivation = true; + obj->m_removed = true; + continue; + } + + // Tell the object about removal + obj->removingFromEnvironment(); + // Deregister in scripting api + m_script->removeObjectReference(obj); + + // Delete active object + if (obj->environmentDeletes()) + delete obj; + // Id to be removed from m_active_objects + objects_to_remove.push_back(id); + } + + // Remove references from m_active_objects + for (std::vector::iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); ++i) { + m_active_objects.erase(*i); + } + + // Get list of loaded blocks + std::vector loaded_blocks; + infostream << "ServerEnvironment::clearObjects(): " + << "Listing all loaded blocks" << std::endl; + m_map->listAllLoadedBlocks(loaded_blocks); + infostream << "ServerEnvironment::clearObjects(): " + << "Done listing all loaded blocks: " + << loaded_blocks.size()< loadable_blocks; + if (mode == CLEAR_OBJECTS_MODE_FULL) { + infostream << "ServerEnvironment::clearObjects(): " + << "Listing all loadable blocks" << std::endl; + m_map->listAllLoadableBlocks(loadable_blocks); + infostream << "ServerEnvironment::clearObjects(): " + << "Done listing all loadable blocks: " + << loadable_blocks.size() << std::endl; + } else { + loadable_blocks = loaded_blocks; + } + + infostream << "ServerEnvironment::clearObjects(): " + << "Now clearing objects in " << loadable_blocks.size() + << " blocks" << std::endl; + + // Grab a reference on each loaded block to avoid unloading it + for (std::vector::iterator i = loaded_blocks.begin(); + i != loaded_blocks.end(); ++i) { + v3s16 p = *i; + MapBlock *block = m_map->getBlockNoCreateNoEx(p); + assert(block != NULL); + block->refGrab(); + } + + // Remove objects in all loadable blocks + u32 unload_interval = U32_MAX; + if (mode == CLEAR_OBJECTS_MODE_FULL) { + unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks"); + unload_interval = MYMAX(unload_interval, 1); + } + u32 report_interval = loadable_blocks.size() / 10; + u32 num_blocks_checked = 0; + u32 num_blocks_cleared = 0; + u32 num_objs_cleared = 0; + for (std::vector::iterator i = loadable_blocks.begin(); + i != loadable_blocks.end(); ++i) { + v3s16 p = *i; + MapBlock *block = m_map->emergeBlock(p, false); + if (!block) { + errorstream << "ServerEnvironment::clearObjects(): " + << "Failed to emerge block " << PP(p) << std::endl; + continue; + } + u32 num_stored = block->m_static_objects.m_stored.size(); + u32 num_active = block->m_static_objects.m_active.size(); + if (num_stored != 0 || num_active != 0) { + block->m_static_objects.m_stored.clear(); + block->m_static_objects.m_active.clear(); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_CLEAR_ALL_OBJECTS); + num_objs_cleared += num_stored + num_active; + num_blocks_cleared++; + } + num_blocks_checked++; + + if (report_interval != 0 && + num_blocks_checked % report_interval == 0) { + float percent = 100.0 * (float)num_blocks_checked / + loadable_blocks.size(); + infostream << "ServerEnvironment::clearObjects(): " + << "Cleared " << num_objs_cleared << " objects" + << " in " << num_blocks_cleared << " blocks (" + << percent << "%)" << std::endl; + } + if (num_blocks_checked % unload_interval == 0) { + m_map->unloadUnreferencedBlocks(); + } + } + m_map->unloadUnreferencedBlocks(); + + // Drop references that were added above + for (std::vector::iterator i = loaded_blocks.begin(); + i != loaded_blocks.end(); ++i) { + v3s16 p = *i; + MapBlock *block = m_map->getBlockNoCreateNoEx(p); + assert(block); + block->refDrop(); + } + + m_last_clear_objects_time = m_game_time; + + infostream << "ServerEnvironment::clearObjects(): " + << "Finished: Cleared " << num_objs_cleared << " objects" + << " in " << num_blocks_cleared << " blocks" << std::endl; +} + +void ServerEnvironment::step(float dtime) +{ + DSTACK(FUNCTION_NAME); + + //TimeTaker timer("ServerEnv step"); + + /* Step time of day */ + stepTimeOfDay(dtime); + + // Update this one + // NOTE: This is kind of funny on a singleplayer game, but doesn't + // really matter that much. + static const float server_step = g_settings->getFloat("dedicated_server_step"); + m_recommended_send_interval = server_step; + + /* + Increment game time + */ + { + m_game_time_fraction_counter += dtime; + u32 inc_i = (u32)m_game_time_fraction_counter; + m_game_time += inc_i; + m_game_time_fraction_counter -= (float)inc_i; + } + + /* + Handle players + */ + { + ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG); + for (std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { + RemotePlayer *player = dynamic_cast(*i); + assert(player); + + // Ignore disconnected players + if(player->peer_id == 0) + continue; + + // Move + player->move(dtime, this, 100*BS); + } + } + + /* + Manage active block list + */ + if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) { + ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG); + /* + Get player block positions + */ + std::vector players_blockpos; + for (std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { + RemotePlayer *player = dynamic_cast(*i); + assert(player); + + // Ignore disconnected players + if (player->peer_id == 0) + continue; + + PlayerSAO *playersao = player->getPlayerSAO(); + assert(playersao); + + v3s16 blockpos = getNodeBlockPos( + floatToInt(playersao->getBasePosition(), BS)); + players_blockpos.push_back(blockpos); + } + + /* + Update list of active blocks, collecting changes + */ + static const s16 active_block_range = g_settings->getS16("active_block_range"); + std::set blocks_removed; + std::set blocks_added; + m_active_blocks.update(players_blockpos, active_block_range, + blocks_removed, blocks_added); + + /* + Handle removed blocks + */ + + // Convert active objects that are no more in active blocks to static + deactivateFarObjects(false); + + for(std::set::iterator + i = blocks_removed.begin(); + i != blocks_removed.end(); ++i) { + v3s16 p = *i; + + /* infostream<<"Server: Block " << PP(p) + << " became inactive"<getBlockNoCreateNoEx(p); + if(block==NULL) + continue; + + // Set current time as timestamp (and let it set ChangedFlag) + block->setTimestamp(m_game_time); + } + + /* + Handle added blocks + */ + + for(std::set::iterator + i = blocks_added.begin(); + i != blocks_added.end(); ++i) + { + v3s16 p = *i; + + MapBlock *block = m_map->getBlockOrEmerge(p); + if(block==NULL){ + m_active_blocks.m_list.erase(p); + continue; + } + + activateBlock(block); + /* infostream<<"Server: Block " << PP(p) + << " became active"<::iterator + i = m_active_blocks.m_list.begin(); + i != m_active_blocks.m_list.end(); ++i) + { + v3s16 p = *i; + + /*infostream<<"Server: Block ("<getBlockNoCreateNoEx(p); + if(block==NULL) + continue; + + // Reset block usage timer + block->resetUsageTimer(); + + // Set current time as timestamp + block->setTimestampNoChangedFlag(m_game_time); + // If time has changed much from the one on disk, + // set block to be saved when it is unloaded + if(block->getTimestamp() > block->getDiskTimestamp() + 60) + block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD, + MOD_REASON_BLOCK_EXPIRED); + + // Run node timers + std::vector elapsed_timers = + block->m_node_timers.step((float)dtime); + if (!elapsed_timers.empty()) { + MapNode n; + for (std::vector::iterator i = elapsed_timers.begin(); + i != elapsed_timers.end(); ++i) { + n = block->getNodeNoEx(i->position); + p = i->position + block->getPosRelative(); + if (m_script->node_on_timer(p, n, i->elapsed)) { + block->setNodeTimer(NodeTimer( + i->timeout, 0, i->position)); + } + } + } + } + } + + if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) + do{ // breakable + if(m_active_block_interval_overload_skip > 0){ + ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips"); + m_active_block_interval_overload_skip--; + break; + } + ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG); + TimeTaker timer("modify in active blocks per interval"); + + // Initialize handling of ActiveBlockModifiers + ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true); + + for(std::set::iterator + i = m_active_blocks.m_list.begin(); + i != m_active_blocks.m_list.end(); ++i) + { + v3s16 p = *i; + + /*infostream<<"Server: Block ("<getBlockNoCreateNoEx(p); + if(block == NULL) + continue; + + // Set current time as timestamp + block->setTimestampNoChangedFlag(m_game_time); + + /* Handle ActiveBlockModifiers */ + abmhandler.apply(block); + } + + u32 time_ms = timer.stop(true); + u32 max_time_ms = 200; + if(time_ms > max_time_ms){ + warningstream<<"active block modifiers took " + <environment_Step(dtime); + + /* + Step active objects + */ + { + ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG); + //TimeTaker timer("Step active objects"); + + g_profiler->avg("SEnv: num of objects", m_active_objects.size()); + + // This helps the objects to send data at the same time + bool send_recommended = false; + m_send_recommended_timer += dtime; + if(m_send_recommended_timer > getSendRecommendedInterval()) + { + m_send_recommended_timer -= getSendRecommendedInterval(); + send_recommended = true; + } + + for(ActiveObjectMap::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ServerActiveObject* obj = i->second; + // Don't step if is to be removed or stored statically + if(obj->m_removed || obj->m_pending_deactivation) + continue; + // Step object + obj->step(dtime, send_recommended); + // Read messages from object + while(!obj->m_messages_out.empty()) + { + m_active_object_messages.push( + obj->m_messages_out.front()); + obj->m_messages_out.pop(); + } + } + } + + /* + Manage active objects + */ + if(m_object_management_interval.step(dtime, 0.5)) + { + ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG); + /* + Remove objects that satisfy (m_removed && m_known_by_count==0) + */ + removeRemovedObjects(); + } + + /* + Manage particle spawner expiration + */ + if (m_particle_management_interval.step(dtime, 1.0)) { + for (UNORDERED_MAP::iterator i = m_particle_spawners.begin(); + i != m_particle_spawners.end(); ) { + //non expiring spawners + if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) { + ++i; + continue; + } + + i->second -= 1.0f; + if (i->second <= 0.f) + m_particle_spawners.erase(i++); + else + ++i; + } + } +} + +u32 ServerEnvironment::addParticleSpawner(float exptime) +{ + // Timers with lifetime 0 do not expire + float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY; + + u32 id = 0; + for (;;) { // look for unused particlespawner id + id++; + UNORDERED_MAP::iterator f = m_particle_spawners.find(id); + if (f == m_particle_spawners.end()) { + m_particle_spawners[id] = time; + break; + } + } + return id; +} + +u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id) +{ + u32 id = addParticleSpawner(exptime); + m_particle_spawner_attachments[id] = attached_id; + if (ServerActiveObject *obj = getActiveObject(attached_id)) { + obj->attachParticleSpawner(id); + } + return id; +} + +void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object) +{ + m_particle_spawners.erase(id); + UNORDERED_MAP::iterator it = m_particle_spawner_attachments.find(id); + if (it != m_particle_spawner_attachments.end()) { + u16 obj_id = (*it).second; + ServerActiveObject *sao = getActiveObject(obj_id); + if (sao != NULL && remove_from_object) { + sao->detachParticleSpawner(id); + } + m_particle_spawner_attachments.erase(id); + } +} + +ServerActiveObject* ServerEnvironment::getActiveObject(u16 id) +{ + ActiveObjectMap::iterator n = m_active_objects.find(id); + return (n != m_active_objects.end() ? n->second : NULL); +} + +bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects) +{ + if (id == 0) + return false; + + return objects.find(id) == objects.end(); +} + +u16 getFreeServerActiveObjectId(ActiveObjectMap &objects) +{ + //try to reuse id's as late as possible + static u16 last_used_id = 0; + u16 startid = last_used_id; + for(;;) + { + last_used_id ++; + if(isFreeServerActiveObjectId(last_used_id, objects)) + return last_used_id; + + if(last_used_id == startid) + return 0; + } +} + +u16 ServerEnvironment::addActiveObject(ServerActiveObject *object) +{ + assert(object); // Pre-condition + m_added_objects++; + u16 id = addActiveObjectRaw(object, true, 0); + return id; +} + +/* + Finds out what new objects have been added to + inside a radius around a position +*/ +void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius, + s16 player_radius, + std::set ¤t_objects, + std::queue &added_objects) +{ + f32 radius_f = radius * BS; + f32 player_radius_f = player_radius * BS; + + if (player_radius_f < 0) + player_radius_f = 0; + /* + Go through the object list, + - discard m_removed objects, + - discard objects that are too far away, + - discard objects that are found in current_objects. + - add remaining objects to added_objects + */ + for (ActiveObjectMap::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + u16 id = i->first; + + // Get object + ServerActiveObject *object = i->second; + if (object == NULL) + continue; + + // Discard if removed or deactivating + if(object->m_removed || object->m_pending_deactivation) + continue; + + f32 distance_f = object->getBasePosition(). + getDistanceFrom(playersao->getBasePosition()); + if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + // Discard if too far + if (distance_f > player_radius_f && player_radius_f != 0) + continue; + } else if (distance_f > radius_f) + continue; + + // Discard if already on current_objects + std::set::iterator n; + n = current_objects.find(id); + if(n != current_objects.end()) + continue; + // Add to added_objects + added_objects.push(id); + } +} + +/* + Finds out what objects have been removed from + inside a radius around a position +*/ +void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius, + s16 player_radius, + std::set ¤t_objects, + std::queue &removed_objects) +{ + f32 radius_f = radius * BS; + f32 player_radius_f = player_radius * BS; + + if (player_radius_f < 0) + player_radius_f = 0; + /* + Go through current_objects; object is removed if: + - object is not found in m_active_objects (this is actually an + error condition; objects should be set m_removed=true and removed + only after all clients have been informed about removal), or + - object has m_removed=true, or + - object is too far away + */ + for(std::set::iterator + i = current_objects.begin(); + i != current_objects.end(); ++i) + { + u16 id = *i; + ServerActiveObject *object = getActiveObject(id); + + if (object == NULL) { + infostream << "ServerEnvironment::getRemovedActiveObjects():" + << " object in current_objects is NULL" << std::endl; + removed_objects.push(id); + continue; + } + + if (object->m_removed || object->m_pending_deactivation) { + removed_objects.push(id); + continue; + } + + f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition()); + if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + if (distance_f <= player_radius_f || player_radius_f == 0) + continue; + } else if (distance_f <= radius_f) + continue; + + // Object is no longer visible + removed_objects.push(id); + } +} + +void ServerEnvironment::setStaticForActiveObjectsInBlock( + v3s16 blockpos, bool static_exists, v3s16 static_block) +{ + MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); + if (!block) + return; + + for (std::map::iterator + so_it = block->m_static_objects.m_active.begin(); + so_it != block->m_static_objects.m_active.end(); ++so_it) { + // Get the ServerActiveObject counterpart to this StaticObject + ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first); + if (ao_it == m_active_objects.end()) { + // If this ever happens, there must be some kind of nasty bug. + errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): " + "Object from MapBlock::m_static_objects::m_active not found " + "in m_active_objects"; + continue; + } + + ServerActiveObject *sao = ao_it->second; + sao->m_static_exists = static_exists; + sao->m_static_block = static_block; + } +} + +ActiveObjectMessage ServerEnvironment::getActiveObjectMessage() +{ + if(m_active_object_messages.empty()) + return ActiveObjectMessage(0); + + ActiveObjectMessage message = m_active_object_messages.front(); + m_active_object_messages.pop(); + return message; +} + +/* + ************ Private methods ************* +*/ + +u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, + bool set_changed, u32 dtime_s) +{ + assert(object); // Pre-condition + if(object->getId() == 0){ + u16 new_id = getFreeServerActiveObjectId(m_active_objects); + if(new_id == 0) + { + errorstream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"no free ids available"<environmentDeletes()) + delete object; + return 0; + } + object->setId(new_id); + } + else{ + verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"supplied with id "<getId()<getId(), m_active_objects)) { + errorstream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"id is not free ("<getId()<<")"<environmentDeletes()) + delete object; + return 0; + } + + if (objectpos_over_limit(object->getBasePosition())) { + v3f p = object->getBasePosition(); + errorstream << "ServerEnvironment::addActiveObjectRaw(): " + << "object position (" << p.X << "," << p.Y << "," << p.Z + << ") outside maximum range" << std::endl; + if (object->environmentDeletes()) + delete object; + return 0; + } + + /*infostream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"added (id="<getId()<<")"<getId()] = object; + + verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"Added id="<getId()<<"; there are now " + <addObjectReference(object); + // Post-initialize object + object->addedToEnvironment(dtime_s); + + // Add static data to block + if(object->isStaticAllowed()) + { + // Add static object to active static list of the block + v3f objectpos = object->getBasePosition(); + std::string staticdata = object->getStaticData(); + StaticObject s_obj(object->getType(), objectpos, staticdata); + // Add to the block where the object is located in + v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); + MapBlock *block = m_map->emergeBlock(blockpos); + if(block){ + block->m_static_objects.m_active[object->getId()] = s_obj; + object->m_static_exists = true; + object->m_static_block = blockpos; + + if(set_changed) + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_ADD_ACTIVE_OBJECT_RAW); + } else { + v3s16 p = floatToInt(objectpos, BS); + errorstream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"could not emerge block for storing id="<getId() + <<" statically (pos="<getId(); +} + +/* + Remove objects that satisfy (m_removed && m_known_by_count==0) +*/ +void ServerEnvironment::removeRemovedObjects() +{ + std::vector objects_to_remove; + for(ActiveObjectMap::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + u16 id = i->first; + ServerActiveObject* obj = i->second; + // This shouldn't happen but check it + if(obj == NULL) + { + infostream<<"NULL object found in ServerEnvironment" + <<" while finding removed objects. id="<m_removed && !obj->m_pending_deactivation) + continue; + + /* + Delete static data from block if is marked as removed + */ + if(obj->m_static_exists && obj->m_removed) + { + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); + if (block) { + block->m_static_objects.remove(id); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_REMOVE_OBJECTS_REMOVE); + obj->m_static_exists = false; + } else { + infostream<<"Failed to emerge block from which an object to " + <<"be removed was loaded from. id="< 0, don't actually remove. On some future + // invocation this will be 0, which is when removal will continue. + if(obj->m_known_by_count > 0) + continue; + + /* + Move static data from active to stored if not marked as removed + */ + if(obj->m_static_exists && !obj->m_removed){ + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); + if (block) { + std::map::iterator i = + block->m_static_objects.m_active.find(id); + if(i != block->m_static_objects.m_active.end()){ + block->m_static_objects.m_stored.push_back(i->second); + block->m_static_objects.m_active.erase(id); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_REMOVE_OBJECTS_DEACTIVATE); + } + } else { + infostream<<"Failed to emerge block from which an object to " + <<"be deactivated was loaded from. id="<removingFromEnvironment(); + // Deregister in scripting api + m_script->removeObjectReference(obj); + + // Delete + if(obj->environmentDeletes()) + delete obj; + + // Id to be removed from m_active_objects + objects_to_remove.push_back(id); + } + // Remove references from m_active_objects + for(std::vector::iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); ++i) { + m_active_objects.erase(*i); + } +} + +static void print_hexdump(std::ostream &o, const std::string &data) +{ + const int linelength = 16; + for(int l=0; ; l++){ + int i0 = linelength * l; + bool at_end = false; + int thislinelength = linelength; + if(i0 + thislinelength > (int)data.size()){ + thislinelength = data.size() - i0; + at_end = true; + } + for(int di=0; di= 32) + o<m_static_objects.m_stored.empty()) + return; + + verbosestream<<"ServerEnvironment::activateObjects(): " + <<"activating objects of block "<getPos()) + <<" ("<m_static_objects.m_stored.size() + <<" objects)"<m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block")); + if (large_amount) { + errorstream<<"suspiciously large amount of objects detected: " + <m_static_objects.m_stored.size()<<" in " + <getPos()) + <<"; removing all of them."<m_static_objects.m_stored.clear(); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_TOO_MANY_OBJECTS); + return; + } + + // Activate stored objects + std::vector new_stored; + for (std::vector::iterator + i = block->m_static_objects.m_stored.begin(); + i != block->m_static_objects.m_stored.end(); ++i) { + StaticObject &s_obj = *i; + + // Create an active object from the data + ServerActiveObject *obj = ServerActiveObject::create + ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data); + // If couldn't create object, store static data back. + if(obj == NULL) { + errorstream<<"ServerEnvironment::activateObjects(): " + <<"failed to create active object from static object " + <<"in block "<getStaticData(); + StaticObject s_obj(obj->getType(), objectpos, staticdata_new); + block->m_static_objects.insert(id, s_obj); + obj->m_static_block = blockpos_o; + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_STATIC_DATA_ADDED); + + // Delete from block where object was located + block = m_map->emergeBlock(old_static_block, false); + if(!block){ + errorstream<<"ServerEnvironment::deactivateFarObjects(): " + <<"Could not delete object id="<m_static_objects.remove(id); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_STATIC_DATA_REMOVED); + continue; + } + + // If block is active, don't remove + if(!force_delete && m_active_blocks.contains(blockpos_o)) + continue; + + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " + <<"deactivating object id="<m_known_by_count > 0 && !force_delete); + + /* + Update the static data + */ + + if(obj->isStaticAllowed()) + { + // Create new static object + std::string staticdata_new = obj->getStaticData(); + StaticObject s_obj(obj->getType(), objectpos, staticdata_new); + + bool stays_in_same_block = false; + bool data_changed = true; + + if (obj->m_static_exists) { + if (obj->m_static_block == blockpos_o) + stays_in_same_block = true; + + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); + + if (block) { + std::map::iterator n = + block->m_static_objects.m_active.find(id); + if (n != block->m_static_objects.m_active.end()) { + StaticObject static_old = n->second; + + float save_movem = obj->getMinimumSavedMovement(); + + if (static_old.data == staticdata_new && + (static_old.pos - objectpos).getLength() < save_movem) + data_changed = false; + } else { + errorstream<<"ServerEnvironment::deactivateFarObjects(): " + <<"id="<m_static_block)<m_static_exists) + { + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); + if(block) + { + block->m_static_objects.remove(id); + obj->m_static_exists = false; + // Only mark block as modified if data changed considerably + if(shall_be_written) + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_STATIC_DATA_CHANGED); + } + } + + // Add to the block where the object is located in + v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); + // Get or generate the block + MapBlock *block = NULL; + try{ + block = m_map->emergeBlock(blockpos); + } catch(InvalidPositionException &e){ + // Handled via NULL pointer + // NOTE: emergeBlock's failure is usually determined by it + // actually returning NULL + } + + if(block) + { + if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { + warningstream << "ServerEnv: Trying to store id = " << obj->getId() + << " statically but block " << PP(blockpos) + << " already contains " + << block->m_static_objects.m_stored.size() + << " objects." + << " Forcing delete." << std::endl; + force_delete = true; + } else { + // If static counterpart already exists in target block, + // remove it first. + // This shouldn't happen because the object is removed from + // the previous block before this according to + // obj->m_static_block, but happens rarely for some unknown + // reason. Unsuccessful attempts have been made to find + // said reason. + if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){ + warningstream<<"ServerEnv: Performing hack #83274" + <m_static_objects.remove(id); + } + // Store static data + u16 store_id = pending_delete ? id : 0; + block->m_static_objects.insert(store_id, s_obj); + + // Only mark block as modified if data changed considerably + if(shall_be_written) + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_STATIC_DATA_CHANGED); + + obj->m_static_exists = true; + obj->m_static_block = block->getPos(); + } + } + else{ + if(!force_delete){ + v3s16 p = floatToInt(objectpos, BS); + errorstream<<"ServerEnv: Could not find or generate " + <<"a block for storing id="<getId() + <<" statically (pos="<m_pending_deactivation = true; + continue; + } + + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " + <<"object id="<removingFromEnvironment(); + // Deregister in scripting api + m_script->removeObjectReference(obj); + + // Delete active object + if(obj->environmentDeletes()) + delete obj; + // Id to be removed from m_active_objects + objects_to_remove.push_back(id); + } + + // Remove references from m_active_objects + for(std::vector::iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); ++i) { + m_active_objects.erase(*i); + } +} -- cgit v1.2.3 From 1fee649f1586c39510169f4dbc84b6c7aed12cfd Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Sun, 8 Jan 2017 09:32:16 -0800 Subject: Minor: Fix indentation in serverenvironment.cpp --- src/serverenvironment.cpp | 102 +++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 51 deletions(-) (limited to 'src/serverenvironment.cpp') diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 6229e4cf1..c9fa64ec5 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -770,65 +770,65 @@ public: v3s16 p0; for(p0.X=0; p0.XgetNodeUnsafe(p0); - content_t c = n.getContent(); + for(p0.Y=0; p0.YgetNodeUnsafe(p0); + content_t c = n.getContent(); - if (c >= m_aabms.size() || !m_aabms[c]) - continue; + if (c >= m_aabms.size() || !m_aabms[c]) + continue; - v3s16 p = p0 + block->getPosRelative(); - for(std::vector::iterator - i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) { - if(myrand() % i->chance != 0) - continue; + v3s16 p = p0 + block->getPosRelative(); + for(std::vector::iterator + i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) { + if(myrand() % i->chance != 0) + continue; - // Check neighbors - if(!i->required_neighbors.empty()) - { - v3s16 p1; - for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++) - for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++) - for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++) - { - if(p1 == p0) - continue; - content_t c; - if (block->isValidPosition(p1)) { - // if the neighbor is found on the same map block - // get it straight from there - const MapNode &n = block->getNodeUnsafe(p1); - c = n.getContent(); - } else { - // otherwise consult the map - MapNode n = map->getNodeNoEx(p1 + block->getPosRelative()); - c = n.getContent(); - } - std::set::const_iterator k; - k = i->required_neighbors.find(c); - if(k != i->required_neighbors.end()){ - goto neighbor_found; - } - } - // No required neighbor found + // Check neighbors + if(!i->required_neighbors.empty()) + { + v3s16 p1; + for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++) + for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++) + for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++) + { + if(p1 == p0) continue; + content_t c; + if (block->isValidPosition(p1)) { + // if the neighbor is found on the same map block + // get it straight from there + const MapNode &n = block->getNodeUnsafe(p1); + c = n.getContent(); + } else { + // otherwise consult the map + MapNode n = map->getNodeNoEx(p1 + block->getPosRelative()); + c = n.getContent(); } - neighbor_found: - - // Call all the trigger variations - i->abm->trigger(m_env, p, n); - i->abm->trigger(m_env, p, n, - active_object_count, active_object_count_wider); - - // Count surrounding objects again if the abms added any - if(m_env->m_added_objects > 0) { - active_object_count = countObjects(block, map, active_object_count_wider); - m_env->m_added_objects = 0; + std::set::const_iterator k; + k = i->required_neighbors.find(c); + if(k != i->required_neighbors.end()){ + goto neighbor_found; } } + // No required neighbor found + continue; } + neighbor_found: + + // Call all the trigger variations + i->abm->trigger(m_env, p, n); + i->abm->trigger(m_env, p, n, + active_object_count, active_object_count_wider); + + // Count surrounding objects again if the abms added any + if(m_env->m_added_objects > 0) { + active_object_count = countObjects(block, map, active_object_count_wider); + m_env->m_added_objects = 0; + } + } + } } }; -- cgit v1.2.3 From 8e7449e09253e138716d8dbad6a2ab5c6e089e28 Mon Sep 17 00:00:00 2001 From: Ner'zhul Date: Mon, 9 Jan 2017 20:39:22 +0100 Subject: Environment & IGameDef code refactoring (#4985) * Environment code refactoring * Cleanup includes & class declarations in client & server environment to improve build speed * ServerEnvironment::m_gamedef is now a pointer to Server instead of IGameDef, permitting to cleanup many casts. * Cleanup IGameDef * Move ITextureSource* IGameDef::getTextureSource() to Client only. * Also move ITextureSource *IGameDef::tsrc() helper * drop getShaderSource, getSceneManager, getSoundManager & getCamera abstract call * drop unused emerge() call * cleanup server unused functions (mentionned before) * Drop one unused parameter from ContentFeatures::updateTextures * move checkLocalPrivilege to Client * Remove some unnecessary casts * create_formspec_menu: remove IWritableTextureSource pointer, as client already knows it * Fix some comments * Change required IGameDef to Server/Client pointers * Previous change that game.cpp sometimes calls functions with Client + InventoryManager + IGameDef in same functions but it's the same objects * Remove duplicate Client pointer in GUIFormSpecMenu::GUIFormSpecMenu * drop ClientMap::sectorWasDrawn which is unused --- build/android/jni/Android.mk | 2 ++ src/camera.cpp | 21 ++++++------ src/camera.h | 6 ++-- src/client.cpp | 2 +- src/client.h | 17 +++++----- src/clientenvironment.cpp | 30 +++++++++--------- src/clientenvironment.h | 8 ++--- src/clientmap.cpp | 7 ++-- src/clientmap.h | 21 ++++-------- src/clientobject.cpp | 9 +++--- src/clientobject.h | 21 +++--------- src/collision.cpp | 3 ++ src/content_cao.cpp | 67 +++++++++++++++++++-------------------- src/content_cao.h | 9 +++--- src/content_cso.cpp | 13 +------- src/content_mapblock.cpp | 6 ++-- src/content_sao.cpp | 20 ++++++------ src/emerge.cpp | 14 ++++---- src/emerge.h | 3 +- src/environment.h | 7 ---- src/game.cpp | 59 +++++++++++++++------------------- src/gamedef.h | 29 ++--------------- src/guiEngine.cpp | 2 -- src/guiFormSpecMenu.cpp | 39 +++++++++++------------ src/guiFormSpecMenu.h | 6 +--- src/hud.cpp | 20 ++++++------ src/hud.h | 8 ++--- src/itemdef.cpp | 22 ++++++------- src/itemdef.h | 9 +++--- src/localplayer.cpp | 23 +++++++------- src/localplayer.h | 4 +-- src/mapblock_mesh.cpp | 25 +++++++-------- src/mapblock_mesh.h | 8 ++--- src/mg_biome.cpp | 11 +++---- src/mg_biome.h | 5 +-- src/mg_schematic.cpp | 10 +++--- src/mg_schematic.h | 6 ++-- src/nodedef.cpp | 16 ++++++---- src/nodedef.h | 4 +-- src/particles.cpp | 16 ++++------ src/particles.h | 2 +- src/pathfinder.cpp | 6 +--- src/script/lua_api/l_nodemeta.cpp | 6 +--- src/server.cpp | 18 ----------- src/server.h | 6 +--- src/serverenvironment.cpp | 20 ++++++------ src/serverenvironment.h | 12 ++++--- src/wieldmesh.cpp | 26 +++++++-------- src/wieldmesh.h | 6 ++-- 49 files changed, 301 insertions(+), 409 deletions(-) (limited to 'src/serverenvironment.cpp') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 2567457b9..3be856770 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -117,6 +117,7 @@ LOCAL_SRC_FILES := \ jni/src/cavegen.cpp \ jni/src/chat.cpp \ jni/src/client.cpp \ + jni/src/clientenvironment.cpp \ jni/src/clientiface.cpp \ jni/src/clientmap.cpp \ jni/src/clientmedia.cpp \ @@ -210,6 +211,7 @@ LOCAL_SRC_FILES := \ jni/src/rollback_interface.cpp \ jni/src/serialization.cpp \ jni/src/server.cpp \ + jni/src/serverenvironment.cpp \ jni/src/serverlist.cpp \ jni/src/serverobject.cpp \ jni/src/shader.cpp \ diff --git a/src/camera.cpp b/src/camera.cpp index 43980db1c..4768e8778 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "wieldmesh.h" #include "noise.h" // easeCurve -#include "gamedef.h" #include "sound.h" #include "event.h" #include "profiler.h" @@ -41,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, - IGameDef *gamedef): + Client *client): m_playernode(NULL), m_headnode(NULL), m_cameranode(NULL), @@ -50,7 +49,7 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, m_wieldnode(NULL), m_draw_control(draw_control), - m_gamedef(gamedef), + m_client(client), m_camera_position(0,0,0), m_camera_direction(0,0,0), @@ -88,7 +87,7 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, m_wieldmgr = smgr->createNewSceneManager(); m_wieldmgr->addCameraSceneNode(); m_wieldnode = new WieldMeshSceneNode(m_wieldmgr->getRootSceneNode(), m_wieldmgr, -1, false); - m_wieldnode->setItem(ItemStack(), m_gamedef); + m_wieldnode->setItem(ItemStack(), m_client); m_wieldnode->drop(); // m_wieldmgr grabbed it /* TODO: Add a callback function so these can be updated when a setting @@ -151,7 +150,7 @@ void Camera::step(f32 dtime) m_wield_change_timer = MYMIN(m_wield_change_timer + dtime, 0.125); if (m_wield_change_timer >= 0 && was_under_zero) - m_wieldnode->setItem(m_wield_item_next, m_gamedef); + m_wieldnode->setItem(m_wield_item_next, m_client); if (m_view_bobbing_state != 0) { @@ -189,7 +188,7 @@ void Camera::step(f32 dtime) (was > 0.5f && m_view_bobbing_anim <= 0.5f)); if(step) { MtEvent *e = new SimpleTriggerEvent("ViewBobbingStep"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } } @@ -210,10 +209,10 @@ void Camera::step(f32 dtime) if(m_digging_button == 0) { MtEvent *e = new SimpleTriggerEvent("CameraPunchLeft"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } else if(m_digging_button == 1) { MtEvent *e = new SimpleTriggerEvent("CameraPunchRight"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } } @@ -352,7 +351,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, my_cp.Y = m_camera_position.Y + (m_camera_direction.Y*-i); // Prevent camera positioned inside nodes - INodeDefManager *nodemgr = m_gamedef->ndef(); + INodeDefManager *nodemgr = m_client->ndef(); MapNode n = c_env.getClientMap().getNodeNoEx(floatToInt(my_cp, BS)); const ContentFeatures& features = nodemgr->get(n); if(features.walkable) @@ -390,7 +389,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, // Get FOV f32 fov_degrees; - if (player->getPlayerControl().zoom && m_gamedef->checkLocalPrivilege("zoom")) { + if (player->getPlayerControl().zoom && m_client->checkLocalPrivilege("zoom")) { fov_degrees = m_cache_zoom_fov; } else { fov_degrees = m_cache_fov; @@ -468,7 +467,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, const bool climbing = movement_Y && player->is_climbing; if ((walking || swimming || climbing) && m_cache_view_bobbing && - (!g_settings->getBool("free_move") || !m_gamedef->checkLocalPrivilege("fly"))) + (!g_settings->getBool("free_move") || !m_client->checkLocalPrivilege("fly"))) { // Start animation m_view_bobbing_state = 1; diff --git a/src/camera.h b/src/camera.h index cb0e9686d..b5be26718 100644 --- a/src/camera.h +++ b/src/camera.h @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class LocalPlayer; struct MapDrawControl; -class IGameDef; +class Client; class WieldMeshSceneNode; struct Nametag { @@ -61,7 +61,7 @@ class Camera { public: Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, - IGameDef *gamedef); + Client *client); ~Camera(); // Get player scene node. @@ -189,7 +189,7 @@ private: // draw control MapDrawControl& m_draw_control; - IGameDef *m_gamedef; + Client *m_client; video::IVideoDriver *m_driver; // Absolute camera position diff --git a/src/client.cpp b/src/client.cpp index 693a90604..c2471dbd7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -221,7 +221,7 @@ Client::Client( m_event(event), m_mesh_update_thread(), m_env( - new ClientMap(this, this, control, + new ClientMap(this, control, device->getSceneManager()->getRootSceneNode(), device->getSceneManager(), 666), device->getSceneManager(), diff --git a/src/client.h b/src/client.h index 9a09704a6..df3e7e605 100644 --- a/src/client.h +++ b/src/client.h @@ -34,7 +34,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "localplayer.h" #include "hud.h" #include "particles.h" -#include "network/networkpacket.h" struct MeshMakeData; class MapBlockMesh; @@ -51,6 +50,7 @@ class Database; class Mapper; struct MinimapMapblock; class Camera; +class NetworkPacket; struct QueuedMeshUpdate { @@ -402,9 +402,6 @@ public: void ProcessData(NetworkPacket *pkt); - // Returns true if something was received - bool AsyncProcessPacket(); - bool AsyncProcessData(); void Send(NetworkPacket* pkt); void interact(u8 action, const PointedThing& pointed); @@ -422,8 +419,9 @@ public: void sendRespawn(); void sendReady(); - ClientEnvironment& getEnv() - { return m_env; } + ClientEnvironment& getEnv() { return m_env; } + ITextureSource *tsrc() { return getTextureSource(); } + ISoundManager *sound() { return getSoundManager(); } // Causes urgent mesh updates (unlike Map::add/removeNodeWithEvent) void removeNode(v3s16 p); @@ -521,14 +519,15 @@ public: virtual IItemDefManager* getItemDefManager(); virtual INodeDefManager* getNodeDefManager(); virtual ICraftDefManager* getCraftDefManager(); - virtual ITextureSource* getTextureSource(); + ITextureSource* getTextureSource(); virtual IShaderSource* getShaderSource(); - virtual scene::ISceneManager* getSceneManager(); + IShaderSource *shsrc() { return getShaderSource(); } + scene::ISceneManager* getSceneManager(); virtual u16 allocateUnknownNodeId(const std::string &name); virtual ISoundManager* getSoundManager(); virtual MtEventManager* getEventManager(); virtual ParticleManager* getParticleManager(); - virtual bool checkLocalPrivilege(const std::string &priv) + bool checkLocalPrivilege(const std::string &priv) { return checkPrivilege(priv); } virtual scene::IAnimatedMesh* getMesh(const std::string &filename); diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp index e831de109..65646c6b4 100644 --- a/src/clientenvironment.cpp +++ b/src/clientenvironment.cpp @@ -34,13 +34,13 @@ with this program; if not, write to the Free Software Foundation, Inc., */ ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, - ITextureSource *texturesource, IGameDef *gamedef, + ITextureSource *texturesource, Client *client, IrrlichtDevice *irr): m_map(map), m_local_player(NULL), m_smgr(smgr), m_texturesource(texturesource), - m_gamedef(gamedef), + m_client(client), m_irr(irr) { char zero = 0; @@ -94,7 +94,7 @@ void ClientEnvironment::step(float dtime) stepTimeOfDay(dtime); // Get some settings - bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); + bool fly_allowed = m_client->checkLocalPrivilege("fly"); bool free_move = fly_allowed && g_settings->getBool("free_move"); // Get local player @@ -223,7 +223,7 @@ void ClientEnvironment::step(float dtime) f32 post_factor = 1; // 1 hp per node/s if(info.type == COLLISION_NODE) { - const ContentFeatures &f = m_gamedef->ndef()-> + const ContentFeatures &f = m_client->ndef()-> get(m_map->getNodeNoEx(info.node_p)); // Determine fall damage multiplier int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); @@ -237,7 +237,7 @@ void ClientEnvironment::step(float dtime) if(damage != 0){ damageLocalPlayer(damage, true); MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } } @@ -259,11 +259,11 @@ void ClientEnvironment::step(float dtime) u32 damage_per_second = 0; damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n1).damage_per_second); + m_client->ndef()->get(n1).damage_per_second); damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n2).damage_per_second); + m_client->ndef()->get(n2).damage_per_second); damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n3).damage_per_second); + m_client->ndef()->get(n3).damage_per_second); if(damage_per_second != 0) { @@ -272,7 +272,7 @@ void ClientEnvironment::step(float dtime) } // Protocol v29 make this behaviour obsolete - if (((Client*) getGameDef())->getProtoVersion() < 29) { + if (getGameDef()->getProtoVersion() < 29) { /* Drowning */ @@ -282,7 +282,7 @@ void ClientEnvironment::step(float dtime) // head v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); - ContentFeatures c = m_gamedef->ndef()->get(n); + ContentFeatures c = m_client->ndef()->get(n); u8 drowning_damage = c.drowning; if (drowning_damage > 0 && lplayer->hp > 0) { u16 breath = lplayer->getBreath(); @@ -306,7 +306,7 @@ void ClientEnvironment::step(float dtime) // head v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); - ContentFeatures c = m_gamedef->ndef()->get(n); + ContentFeatures c = m_client->ndef()->get(n); if (!lplayer->hp) { lplayer->setBreath(11); } else if (c.drowning == 0) { @@ -332,7 +332,7 @@ void ClientEnvironment::step(float dtime) v3s16 p = lplayer->getLightPosition(); node_at_lplayer = m_map->getNodeNoEx(p); - u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef()); + u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef()); u8 day = light & 0xff; u8 night = (light >> 8) & 0xff; finalColorBlend(lplayer->light_color, day, night, day_night_ratio); @@ -360,7 +360,7 @@ void ClientEnvironment::step(float dtime) v3s16 p = obj->getLightPosition(); MapNode n = m_map->getNodeNoEx(p, &pos_ok); if (pos_ok) - light = n.getLightBlend(day_night_ratio, m_gamedef->ndef()); + light = n.getLightBlend(day_night_ratio, m_client->ndef()); else light = blend_light(day_night_ratio, LIGHT_SUN, 0); @@ -467,7 +467,7 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) v3s16 p = object->getLightPosition(); MapNode n = m_map->getNodeNoEx(p, &pos_ok); if (pos_ok) - light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); + light = n.getLightBlend(getDayNightRatio(), m_client->ndef()); else light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); @@ -480,7 +480,7 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type, const std::string &init_data) { ClientActiveObject* obj = - ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this); + ClientActiveObject::create((ActiveObjectType) type, m_client, this); if(obj == NULL) { infostream<<"ClientEnvironment::addActiveObject(): " diff --git a/src/clientenvironment.h b/src/clientenvironment.h index e6292b5b7..b30a7a6d7 100644 --- a/src/clientenvironment.h +++ b/src/clientenvironment.h @@ -30,6 +30,7 @@ class ClientMap; class ClientActiveObject; class GenericCAO; class LocalPlayer; +struct PointedThing; /* The client-side environment. @@ -66,15 +67,14 @@ class ClientEnvironment : public Environment { public: ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, - ITextureSource *texturesource, IGameDef *gamedef, + ITextureSource *texturesource, Client *client, IrrlichtDevice *device); ~ClientEnvironment(); Map & getMap(); ClientMap & getClientMap(); - IGameDef *getGameDef() - { return m_gamedef; } + Client *getGameDef() { return m_client; } void step(f32 dtime); @@ -175,7 +175,7 @@ private: LocalPlayer *m_local_player; scene::ISceneManager *m_smgr; ITextureSource *m_texturesource; - IGameDef *m_gamedef; + Client *m_client; IrrlichtDevice *m_irr; UNORDERED_MAP m_active_objects; std::vector m_simple_objects; diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 542eb03e8..7e688daad 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -35,13 +35,12 @@ with this program; if not, write to the Free Software Foundation, Inc., ClientMap::ClientMap( Client *client, - IGameDef *gamedef, MapDrawControl &control, scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id ): - Map(dout_client, gamedef), + Map(dout_client, client), scene::ISceneNode(parent, mgr, id), m_client(client), m_control(control), @@ -140,7 +139,7 @@ static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac, return false; } -void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes, +void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes, v3s16 *p_blocks_min, v3s16 *p_blocks_max) { v3s16 box_nodes_d = m_control.wanted_range * v3s16(1, 1, 1); @@ -766,7 +765,7 @@ void ClientMap::renderPostFx(CameraMode cam_mode) const ContentFeatures& features = m_nodedef->get(n); video::SColor post_effect_color = features.post_effect_color; if(features.solidness == 2 && !(g_settings->getBool("noclip") && - m_gamedef->checkLocalPrivilege("noclip")) && + m_client->checkLocalPrivilege("noclip")) && cam_mode == CAMERA_MODE_FIRST) { post_effect_color = video::SColor(255, 0, 0, 0); diff --git a/src/clientmap.h b/src/clientmap.h index cb686ff33..84228f4ca 100644 --- a/src/clientmap.h +++ b/src/clientmap.h @@ -59,7 +59,7 @@ class ITextureSource; /* ClientMap - + This is the only map class that is able to render itself on screen. */ @@ -68,7 +68,6 @@ class ClientMap : public Map, public scene::ISceneNode public: ClientMap( Client *client, - IGameDef *gamedef, MapDrawControl &control, scene::ISceneNode* parent, scene::ISceneManager* mgr, @@ -114,13 +113,13 @@ public: driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); renderMap(driver, SceneManager->getSceneNodeRenderPass()); } - + virtual const aabb3f &getBoundingBox() const { return m_box; } - - void getBlocksInViewRange(v3s16 cam_pos_nodes, + + void getBlocksInViewRange(v3s16 cam_pos_nodes, v3s16 *p_blocks_min, v3s16 *p_blocks_max); void updateDrawList(video::IVideoDriver* driver); void renderMap(video::IVideoDriver* driver, s32 pass); @@ -132,20 +131,14 @@ public: // For debug printing virtual void PrintInfo(std::ostream &out); - - // Check if sector was drawn on last render() - bool sectorWasDrawn(v2s16 p) - { - return (m_last_drawn_sectors.find(p) != m_last_drawn_sectors.end()); - } const MapDrawControl & getControl() const { return m_control; } f32 getCameraFov() const { return m_camera_fov; } private: Client *m_client; - + aabb3f m_box; - + MapDrawControl &m_control; v3f m_camera_position; @@ -154,7 +147,7 @@ private: v3s16 m_camera_offset; std::map m_drawlist; - + std::set m_last_drawn_sectors; bool m_cache_trilinear_filter; diff --git a/src/clientobject.cpp b/src/clientobject.cpp index ff3f47187..89a0474a4 100644 --- a/src/clientobject.cpp +++ b/src/clientobject.cpp @@ -20,16 +20,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientobject.h" #include "debug.h" #include "porting.h" -#include "constants.h" /* ClientActiveObject */ -ClientActiveObject::ClientActiveObject(u16 id, IGameDef *gamedef, +ClientActiveObject::ClientActiveObject(u16 id, Client *client, ClientEnvironment *env): ActiveObject(id), - m_gamedef(gamedef), + m_client(client), m_env(env) { } @@ -40,7 +39,7 @@ ClientActiveObject::~ClientActiveObject() } ClientActiveObject* ClientActiveObject::create(ActiveObjectType type, - IGameDef *gamedef, ClientEnvironment *env) + Client *client, ClientEnvironment *env) { // Find factory function UNORDERED_MAP::iterator n = m_types.find(type); @@ -52,7 +51,7 @@ ClientActiveObject* ClientActiveObject::create(ActiveObjectType type, } Factory f = n->second; - ClientActiveObject *object = (*f)(gamedef, env); + ClientActiveObject *object = (*f)(client, env); return object; } diff --git a/src/clientobject.h b/src/clientobject.h index 83931e438..f0bde0adc 100644 --- a/src/clientobject.h +++ b/src/clientobject.h @@ -25,20 +25,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "util/cpp11_container.h" -/* - -Some planning -------------- - -* Client receives a network packet with information of added objects - in it -* Client supplies the information to its ClientEnvironment -* The environment adds the specified objects to itself - -*/ - class ClientEnvironment; class ITextureSource; +class Client; class IGameDef; class LocalPlayer; struct ItemStack; @@ -47,7 +36,7 @@ class WieldMeshSceneNode; class ClientActiveObject : public ActiveObject { public: - ClientActiveObject(u16 id, IGameDef *gamedef, ClientEnvironment *env); + ClientActiveObject(u16 id, Client *client, ClientEnvironment *env); virtual ~ClientActiveObject(); virtual void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, @@ -89,7 +78,7 @@ public: virtual void initialize(const std::string &data){} // Create a certain type of ClientActiveObject - static ClientActiveObject* create(ActiveObjectType type, IGameDef *gamedef, + static ClientActiveObject* create(ActiveObjectType type, Client *client, ClientEnvironment *env); // If returns true, punch will not be sent to the server @@ -99,9 +88,9 @@ public: protected: // Used for creating objects based on type - typedef ClientActiveObject* (*Factory)(IGameDef *gamedef, ClientEnvironment *env); + typedef ClientActiveObject* (*Factory)(Client *client, ClientEnvironment *env); static void registerType(u16 type, Factory f); - IGameDef *m_gamedef; + Client *m_client; ClientEnvironment *m_env; private: // Used for creating objects based on type diff --git a/src/collision.cpp b/src/collision.cpp index 595fa8059..c0891c152 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -22,9 +22,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "nodedef.h" #include "gamedef.h" +#ifndef SERVER #include "clientenvironment.h" +#endif #include "serverenvironment.h" #include "serverobject.h" +#include "util/timetaker.h" #include "profiler.h" // float error is 10 - 9.96875 = 0.03125 diff --git a/src/content_cao.cpp b/src/content_cao.cpp index a02d5168e..a4c0bf14d 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -33,7 +33,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "collision.h" #include "settings.h" #include "serialization.h" // For decompressZlib -#include "gamedef.h" #include "clientobject.h" #include "mesh.h" #include "itemdef.h" @@ -139,7 +138,7 @@ static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill, class TestCAO : public ClientActiveObject { public: - TestCAO(IGameDef *gamedef, ClientEnvironment *env); + TestCAO(Client *client, ClientEnvironment *env); virtual ~TestCAO(); ActiveObjectType getType() const @@ -147,7 +146,7 @@ public: return ACTIVEOBJECT_TYPE_TEST; } - static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env); + static ClientActiveObject* create(Client *client, ClientEnvironment *env); void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, IrrlichtDevice *irr); @@ -169,8 +168,8 @@ private: // Prototype TestCAO proto_TestCAO(NULL, NULL); -TestCAO::TestCAO(IGameDef *gamedef, ClientEnvironment *env): - ClientActiveObject(0, gamedef, env), +TestCAO::TestCAO(Client *client, ClientEnvironment *env): + ClientActiveObject(0, client, env), m_node(NULL), m_position(v3f(0,10*BS,0)) { @@ -181,9 +180,9 @@ TestCAO::~TestCAO() { } -ClientActiveObject* TestCAO::create(IGameDef *gamedef, ClientEnvironment *env) +ClientActiveObject* TestCAO::create(Client *client, ClientEnvironment *env) { - return new TestCAO(gamedef, env); + return new TestCAO(client, env); } void TestCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, @@ -283,7 +282,7 @@ void TestCAO::processMessage(const std::string &data) class ItemCAO : public ClientActiveObject { public: - ItemCAO(IGameDef *gamedef, ClientEnvironment *env); + ItemCAO(Client *client, ClientEnvironment *env); virtual ~ItemCAO(); ActiveObjectType getType() const @@ -291,7 +290,7 @@ public: return ACTIVEOBJECT_TYPE_ITEM; } - static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env); + static ClientActiveObject* create(Client *client, ClientEnvironment *env); void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, IrrlichtDevice *irr); @@ -331,13 +330,13 @@ private: // Prototype ItemCAO proto_ItemCAO(NULL, NULL); -ItemCAO::ItemCAO(IGameDef *gamedef, ClientEnvironment *env): - ClientActiveObject(0, gamedef, env), +ItemCAO::ItemCAO(Client *client, ClientEnvironment *env): + ClientActiveObject(0, client, env), m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.), m_node(NULL), m_position(v3f(0,10*BS,0)) { - if(!gamedef && !env) + if(!client && !env) { ClientActiveObject::registerType(getType(), create); } @@ -347,9 +346,9 @@ ItemCAO::~ItemCAO() { } -ClientActiveObject* ItemCAO::create(IGameDef *gamedef, ClientEnvironment *env) +ClientActiveObject* ItemCAO::create(Client *client, ClientEnvironment *env) { - return new ItemCAO(gamedef, env); + return new ItemCAO(client, env); } void ItemCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, @@ -433,7 +432,7 @@ void ItemCAO::updateNodePos() void ItemCAO::updateInfoText() { try{ - IItemDefManager *idef = m_gamedef->idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(m_itemstring, idef); if(item.isKnown(idef)) @@ -458,10 +457,10 @@ void ItemCAO::updateTexture() std::istringstream is(m_itemstring, std::ios_base::binary); video::ITexture *texture = NULL; try{ - IItemDefManager *idef = m_gamedef->idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(is, idef); - texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef); + texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_client); } catch(SerializationError &e) { @@ -538,15 +537,15 @@ void ItemCAO::initialize(const std::string &data) #include "genericobject.h" -GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env): - ClientActiveObject(0, gamedef, env), +GenericCAO::GenericCAO(Client *client, ClientEnvironment *env): + ClientActiveObject(0, client, env), // m_is_player(false), m_is_local_player(false), // m_smgr(NULL), m_irr(NULL), - m_gamedef(NULL), + m_client(NULL), m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.), m_meshnode(NULL), m_animated_meshnode(NULL), @@ -581,10 +580,10 @@ GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env): m_last_light(255), m_is_visible(false) { - if (gamedef == NULL) { + if (client == NULL) { ClientActiveObject::registerType(getType(), create); } else { - m_gamedef = gamedef; + m_client = client; } } @@ -793,7 +792,7 @@ void GenericCAO::removeFromScene(bool permanent) } if (m_nametag) { - m_gamedef->getCamera()->removeNametag(m_nametag); + m_client->getCamera()->removeNametag(m_nametag); m_nametag = NULL; } } @@ -906,7 +905,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, } else if(m_prop.visual == "mesh") { infostream<<"GenericCAO::addToScene(): mesh"<getMesh(m_prop.mesh); + scene::IAnimatedMesh *mesh = m_client->getMesh(m_prop.mesh); if(mesh) { m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, NULL); @@ -937,12 +936,12 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, infostream<<"textures: "<= 1){ infostream<<"textures[0]: "<idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item(m_prop.textures[0], 1, 0, "", idef); m_wield_meshnode = new WieldMeshSceneNode( smgr->getRootSceneNode(), smgr, -1); - m_wield_meshnode->setItem(item, m_gamedef); + m_wield_meshnode->setItem(item, m_client); m_wield_meshnode->setScale(v3f(m_prop.visual_size.X/2, m_prop.visual_size.Y/2, @@ -959,7 +958,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, scene::ISceneNode *node = getSceneNode(); if (node && m_prop.nametag != "" && !m_is_local_player) { // Add nametag - m_nametag = m_gamedef->getCamera()->addNametag(node, + m_nametag = m_client->getCamera()->addNametag(node, m_prop.nametag, m_prop.nametag_color); } @@ -1058,11 +1057,11 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) // increase speed if using fast or flying fast if((g_settings->getBool("fast_move") && - m_gamedef->checkLocalPrivilege("fast")) && + m_client->checkLocalPrivilege("fast")) && (controls.aux1 || (!player->touching_ground && g_settings->getBool("free_move") && - m_gamedef->checkLocalPrivilege("fly")))) + m_client->checkLocalPrivilege("fly")))) new_speed *= 1.5; // slowdown speed if sneeking if(controls.sneak && walking) @@ -1129,7 +1128,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) } removeFromScene(false); - addToScene(m_smgr, m_gamedef->tsrc(), m_irr); + addToScene(m_smgr, m_client->tsrc(), m_irr); // Attachments, part 2: Now that the parent has been refreshed, put its attachments back for (std::vector::size_type i = 0; i < m_children.size(); i++) { @@ -1199,12 +1198,12 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) m_step_distance_counter = 0; if(!m_is_local_player && m_prop.makes_footstep_sound) { - INodeDefManager *ndef = m_gamedef->ndef(); + INodeDefManager *ndef = m_client->ndef(); v3s16 p = floatToInt(getPosition() + v3f(0, (m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS); MapNode n = m_env->getMap().getNodeNoEx(p); SimpleSoundSpec spec = ndef->get(n).sound_footstep; - m_gamedef->sound()->playSoundAt(spec, false, getPosition()); + m_client->sound()->playSoundAt(spec, false, getPosition()); } } } @@ -1305,7 +1304,7 @@ void GenericCAO::updateTexturePos() void GenericCAO::updateTextures(const std::string &mod) { - ITextureSource *tsrc = m_gamedef->tsrc(); + ITextureSource *tsrc = m_client->tsrc(); bool use_trilinear_filter = g_settings->getBool("trilinear_filter"); bool use_bilinear_filter = g_settings->getBool("bilinear_filter"); @@ -1778,7 +1777,7 @@ bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem, { assert(punchitem); // pre-condition const ToolCapabilities *toolcap = - &punchitem->getToolCapabilities(m_gamedef->idef()); + &punchitem->getToolCapabilities(m_client->idef()); PunchDamageResult result = getPunchDamage( m_armor_groups, toolcap, diff --git a/src/content_cao.h b/src/content_cao.h index a158e8296..96a160055 100644 --- a/src/content_cao.h +++ b/src/content_cao.h @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemgroup.h" class Camera; +class Client; struct Nametag; /* @@ -68,7 +69,7 @@ private: // scene::ISceneManager *m_smgr; IrrlichtDevice *m_irr; - IGameDef *m_gamedef; + Client *m_client; aabb3f m_selection_box; scene::IMeshSceneNode *m_meshnode; scene::IAnimatedMeshSceneNode *m_animated_meshnode; @@ -109,13 +110,13 @@ private: std::vector m_children; public: - GenericCAO(IGameDef *gamedef, ClientEnvironment *env); + GenericCAO(Client *client, ClientEnvironment *env); ~GenericCAO(); - static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env) + static ClientActiveObject* create(Client *client, ClientEnvironment *env) { - return new GenericCAO(gamedef, env); + return new GenericCAO(client, env); } inline ActiveObjectType getType() const diff --git a/src/content_cso.cpp b/src/content_cso.cpp index c0407f460..aca71212b 100644 --- a/src/content_cso.cpp +++ b/src/content_cso.cpp @@ -21,20 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "client/tile.h" #include "clientenvironment.h" -#include "gamedef.h" +#include "client.h" #include "map.h" -/* -static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill, - float txs, float tys, int col, int row) -{ - video::SMaterial& material = bill->getMaterial(0); - core::matrix4& matrix = material.getTextureMatrix(0); - matrix.setTextureTranslate(txs*col, tys*row); - matrix.setTextureScale(txs, tys); -} -*/ - class SmokePuffCSO: public ClientSimpleObject { float m_age; diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 8ce0f1e0a..a7134590b 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/tile.h" #include "mesh.h" #include -#include "gamedef.h" +#include "client.h" #include "log.h" #include "noise.h" @@ -188,8 +188,8 @@ static inline int NeighborToIndex(const v3s16 &pos) void mapblock_mesh_generate_special(MeshMakeData *data, MeshCollector &collector) { - INodeDefManager *nodedef = data->m_gamedef->ndef(); - scene::ISceneManager* smgr = data->m_gamedef->getSceneManager(); + INodeDefManager *nodedef = data->m_client->ndef(); + scene::ISceneManager* smgr = data->m_client->getSceneManager(); scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator(); // 0ms diff --git a/src/content_sao.cpp b/src/content_sao.cpp index f866d4372..dd8bdc592 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -273,7 +273,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) v3f p_pos = m_base_position; v3f p_velocity = m_velocity; v3f p_acceleration = m_acceleration; - moveresult = collisionMoveSimple(m_env,m_env->getGameDef(), + moveresult = collisionMoveSimple(m_env, m_env->getGameDef(), pos_max_d, box, m_prop.stepheight, dtime, &p_pos, &p_velocity, p_acceleration, this, m_prop.collideWithObjects); @@ -945,7 +945,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) // get head position v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS); MapNode n = m_env->getMap().getNodeNoEx(p); - const ContentFeatures &c = ((Server*) m_env->getGameDef())->ndef()->get(n); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); // If node generates drown if (c.drowning > 0) { if (m_hp > 0 && m_breath > 0) @@ -954,7 +954,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) // No more breath, damage player if (m_breath == 0) { setHP(m_hp - c.drowning); - ((Server*) m_env->getGameDef())->SendPlayerHPOrDie(this); + m_env->getGameDef()->SendPlayerHPOrDie(this); } } } @@ -963,7 +963,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) // get head position v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS); MapNode n = m_env->getMap().getNodeNoEx(p); - const ContentFeatures &c = ((Server*) m_env->getGameDef())->ndef()->get(n); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); // If player is alive & no drowning, breath if (m_hp > 0 && c.drowning == 0) setBreath(m_breath + 1); @@ -985,7 +985,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) m_attachment_position = v3f(0,0,0); m_attachment_rotation = v3f(0,0,0); setBasePosition(m_last_good_position); - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } //dstream<<"PlayerSAO::step: dtime: "<getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } void PlayerSAO::moveTo(v3f pos, bool continuous) @@ -1118,7 +1118,7 @@ void PlayerSAO::moveTo(v3f pos, bool continuous) setBasePosition(pos); // Movement caused by this command is always valid m_last_good_position = pos; - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } void PlayerSAO::setYaw(const float yaw) @@ -1148,7 +1148,7 @@ void PlayerSAO::setWantedRange(const s16 range) void PlayerSAO::setYawAndSend(const float yaw) { setYaw(yaw); - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } void PlayerSAO::setPitch(const float pitch) @@ -1162,7 +1162,7 @@ void PlayerSAO::setPitch(const float pitch) void PlayerSAO::setPitchAndSend(const float pitch) { setPitch(pitch); - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } int PlayerSAO::punch(v3f dir, @@ -1273,7 +1273,7 @@ void PlayerSAO::setBreath(const u16 breath, bool send) m_breath = MYMIN(breath, PLAYER_MAX_BREATH); if (send) - ((Server *) m_env->getGameDef())->SendPlayerBreath(this); + m_env->getGameDef()->SendPlayerBreath(this); } void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups) diff --git a/src/emerge.cpp b/src/emerge.cpp index 25b2e924b..3f0a46010 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -89,13 +89,13 @@ private: //// EmergeManager //// -EmergeManager::EmergeManager(IGameDef *gamedef) +EmergeManager::EmergeManager(Server *server) { - this->ndef = gamedef->getNodeDefManager(); - this->biomemgr = new BiomeManager(gamedef); - this->oremgr = new OreManager(gamedef); - this->decomgr = new DecorationManager(gamedef); - this->schemmgr = new SchematicManager(gamedef); + this->ndef = server->getNodeDefManager(); + this->biomemgr = new BiomeManager(server); + this->oremgr = new OreManager(server); + this->decomgr = new DecorationManager(server); + this->schemmgr = new SchematicManager(server); this->gen_notify_on = 0; // Note that accesses to this variable are not synchronized. @@ -128,7 +128,7 @@ EmergeManager::EmergeManager(IGameDef *gamedef) m_qlimit_generate = 1; for (s16 i = 0; i < nthreads; i++) - m_threads.push_back(new EmergeThread((Server *)gamedef, i)); + m_threads.push_back(new EmergeThread(server, i)); infostream << "EmergeManager: using " << nthreads << " threads" << std::endl; } diff --git a/src/emerge.h b/src/emerge.h index 71ad97da3..76653e6cd 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -42,6 +42,7 @@ class BiomeManager; class OreManager; class DecorationManager; class SchematicManager; +class Server; // Structure containing inputs/outputs for chunk generation struct BlockMakeData { @@ -115,7 +116,7 @@ public: SchematicManager *schemmgr; // Methods - EmergeManager(IGameDef *gamedef); + EmergeManager(Server *server); ~EmergeManager(); bool initMapgens(MapgenParams *mgparams); diff --git a/src/environment.h b/src/environment.h index 14a18421b..0cc3222f9 100644 --- a/src/environment.h +++ b/src/environment.h @@ -42,13 +42,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "threading/atomic.h" #include "network/networkprotocol.h" // for AccessDeniedCode -class ITextureSource; -class IGameDef; -class Map; -class GameScripting; -class Player; -class PointedThing; - class Environment { public: diff --git a/src/game.cpp b/src/game.cpp index cfa6234ff..1070cb1b2 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -916,16 +916,14 @@ bool nodePlacementPrediction(Client &client, } static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, - InventoryManager *invmgr, IGameDef *gamedef, - IWritableTextureSource *tsrc, IrrlichtDevice *device, - JoystickController *joystick, - IFormSource *fs_src, TextDest *txt_dest, Client *client) + Client *client, IrrlichtDevice *device, JoystickController *joystick, + IFormSource *fs_src, TextDest *txt_dest) { if (*cur_formspec == 0) { *cur_formspec = new GUIFormSpecMenu(device, joystick, - guiroot, -1, &g_menumgr, invmgr, gamedef, tsrc, - fs_src, txt_dest, client); + guiroot, -1, &g_menumgr, client, client->getTextureSource(), + fs_src, txt_dest); (*cur_formspec)->doPause = false; /* @@ -950,9 +948,9 @@ static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, #endif static void show_deathscreen(GUIFormSpecMenu **cur_formspec, - InventoryManager *invmgr, IGameDef *gamedef, + Client *client, IWritableTextureSource *tsrc, IrrlichtDevice *device, - JoystickController *joystick, Client *client) + JoystickController *joystick) { std::string formspec = std::string(FORMSPEC_VERSION_STRING) + @@ -968,13 +966,12 @@ static void show_deathscreen(GUIFormSpecMenu **cur_formspec, FormspecFormSource *fs_src = new FormspecFormSource(formspec); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client); - create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, - joystick, fs_src, txt_dst, NULL); + create_formspec_menu(cur_formspec, client, device, joystick, fs_src, txt_dst); } /******************************************************************************/ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, - InventoryManager *invmgr, IGameDef *gamedef, + Client *client, IWritableTextureSource *tsrc, IrrlichtDevice *device, JoystickController *joystick, bool singleplayermode) { @@ -1041,8 +1038,7 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, FormspecFormSource *fs_src = new FormspecFormSource(os.str()); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); - create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, - joystick, fs_src, txt_dst, NULL); + create_formspec_menu(cur_formspec, client, device, joystick, fs_src, txt_dst); std::string con("btn_continue"); (*cur_formspec)->setFocus(con); (*cur_formspec)->doPause = true; @@ -1534,7 +1530,6 @@ private: bool *kill; std::string *error_message; bool *reconnect_requested; - IGameDef *gamedef; // Convenience (same as *client) scene::ISceneNode *skybox; bool random_input; @@ -2011,7 +2006,7 @@ bool Game::createClient(const std::string &playername, /* Camera */ - camera = new Camera(smgr, *draw_control, gamedef); + camera = new Camera(smgr, *draw_control, client); if (!camera || !camera->successfullyCreated(*error_message)) return false; client->setCamera(camera); @@ -2068,7 +2063,7 @@ bool Game::createClient(const std::string &playername, player->hurt_tilt_timer = 0; player->hurt_tilt_strength = 0; - hud = new Hud(driver, smgr, guienv, gamedef, player, local_inventory); + hud = new Hud(driver, smgr, guienv, client, player, local_inventory); if (!hud) { *error_message = "Memory error: could not create HUD"; @@ -2198,8 +2193,6 @@ bool Game::connectToServer(const std::string &playername, if (!client) return false; - gamedef = client; // Client acts as our GameDef - infostream << "Connecting to server at "; connect_address.print(&infostream); infostream << std::endl; @@ -2445,7 +2438,7 @@ inline bool Game::handleCallbacks() void Game::processQueues() { texture_src->processQueue(); - itemdef_manager->processQueue(gamedef); + itemdef_manager->processQueue(client); shader_src->processQueue(); } @@ -2617,7 +2610,7 @@ void Game::processKeyInput(VolatileRunFlags *flags, openInventory(); } else if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) { if (!gui_chat_console->isOpenInhibited()) { - show_pause_menu(¤t_formspec, client, gamedef, + show_pause_menu(¤t_formspec, client, texture_src, device, &input->joystick, simple_singleplayer_mode); } @@ -2769,8 +2762,7 @@ void Game::openInventory() PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client); TextDest *txt_dst = new TextDestPlayerInventory(client); - create_formspec_menu(¤t_formspec, client, gamedef, texture_src, - device, &input->joystick, fs_src, txt_dst, client); + create_formspec_menu(¤t_formspec, client, device, &input->joystick, fs_src, txt_dst); cur_formname = ""; InventoryLocation inventoryloc; @@ -3245,13 +3237,13 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) rangelim(event.player_damage.amount / 4, 1.0, 4.0); MtEvent *e = new SimpleTriggerEvent("PlayerDamage"); - gamedef->event()->put(e); + client->event()->put(e); } else if (event.type == CE_PLAYER_FORCE_MOVE) { cam->camera_yaw = event.player_force_move.yaw; cam->camera_pitch = event.player_force_move.pitch; } else if (event.type == CE_DEATHSCREEN) { - show_deathscreen(¤t_formspec, client, gamedef, texture_src, - device, &input->joystick, client); + show_deathscreen(¤t_formspec, client, texture_src, + device, &input->joystick); chat_backend->addMessage(L"", L"You died."); @@ -3271,9 +3263,8 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) TextDestPlayerInventory *txt_dst = new TextDestPlayerInventory(client, *(event.show_formspec.formname)); - create_formspec_menu(¤t_formspec, client, gamedef, - texture_src, device, &input->joystick, - fs_src, txt_dst, client); + create_formspec_menu(¤t_formspec, client, device, &input->joystick, + fs_src, txt_dst); cur_formname = *(event.show_formspec.formname); } @@ -3282,7 +3273,7 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) } else if ((event.type == CE_SPAWN_PARTICLE) || (event.type == CE_ADD_PARTICLESPAWNER) || (event.type == CE_DELETE_PARTICLESPAWNER)) { - client->getParticleManager()->handleParticleEvent(&event, gamedef, + client->getParticleManager()->handleParticleEvent(&event, client, smgr, player); } else if (event.type == CE_HUDADD) { u32 id = event.hudadd.id; @@ -3840,8 +3831,8 @@ void Game::handlePointingAtNode(GameRunData *runData, &client->getEnv().getClientMap(), nodepos); TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); - create_formspec_menu(¤t_formspec, client, gamedef, - texture_src, device, &input->joystick, fs_src, txt_dst, client); + create_formspec_menu(¤t_formspec, client, + device, &input->joystick, fs_src, txt_dst); cur_formname = ""; current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc); @@ -3972,7 +3963,7 @@ void Game::handleDigging(GameRunData *runData, if (m_cache_enable_particles) { const ContentFeatures &features = client->getNodeDefManager()->get(n); - client->getParticleManager()->addPunchingParticles(gamedef, smgr, + client->getParticleManager()->addPunchingParticles(client, smgr, player, nodepos, features.tiles); } } @@ -4019,7 +4010,7 @@ void Game::handleDigging(GameRunData *runData, if (m_cache_enable_particles) { const ContentFeatures &features = client->getNodeDefManager()->get(wasnode); - client->getParticleManager()->addDiggingParticles(gamedef, smgr, + client->getParticleManager()->addDiggingParticles(client, smgr, player, nodepos, features.tiles); } @@ -4043,7 +4034,7 @@ void Game::handleDigging(GameRunData *runData, // Send event to trigger sound MtEvent *e = new NodeDugEvent(nodepos, wasnode); - gamedef->event()->put(e); + client->event()->put(e); } if (runData->dig_time_complete < 100000.0) { diff --git a/src/gamedef.h b/src/gamedef.h index 7e3da4cac..cb624bd6a 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -53,47 +53,22 @@ public: virtual INodeDefManager* getNodeDefManager()=0; virtual ICraftDefManager* getCraftDefManager()=0; - // This is always thread-safe, but referencing the irrlicht texture - // pointers in other threads than main thread will make things explode. - virtual ITextureSource* getTextureSource()=0; - - virtual IShaderSource* getShaderSource()=0; - // Used for keeping track of names/ids of unknown nodes virtual u16 allocateUnknownNodeId(const std::string &name)=0; - // Only usable on the client - virtual ISoundManager* getSoundManager()=0; virtual MtEventManager* getEventManager()=0; - virtual scene::IAnimatedMesh* getMesh(const std::string &filename) - { return NULL; } - virtual scene::ISceneManager* getSceneManager()=0; - - virtual Camera* getCamera() - { return NULL; } - virtual void setCamera(Camera *camera) {} // Only usable on the server, and NOT thread-safe. It is usable from the // environment thread. - virtual IRollbackManager* getRollbackManager(){return NULL;} - - // Only usable on the server. Thread safe if not written while running threads. - virtual EmergeManager *getEmergeManager() { return NULL; } - - // Used on the client - virtual bool checkLocalPrivilege(const std::string &priv) - { return false; } + virtual IRollbackManager* getRollbackManager() { return NULL; } // Shorthands IItemDefManager *idef() { return getItemDefManager(); } INodeDefManager *ndef() { return getNodeDefManager(); } ICraftDefManager *cdef() { return getCraftDefManager(); } - ITextureSource *tsrc() { return getTextureSource(); } - ISoundManager *sound() { return getSoundManager(); } - IShaderSource *shsrc() { return getShaderSource(); } + MtEventManager *event() { return getEventManager(); } IRollbackManager *rollback() { return getRollbackManager();} - EmergeManager *emerge() { return getEmergeManager(); } }; #endif diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index a3c35f68d..6d66ed08d 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -194,11 +194,9 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev, -1, m_menumanager, NULL /* &client */, - NULL /* gamedef */, m_texture_source, m_formspecgui, m_buttonhandler, - NULL, false); m_menu->allowClose(false); diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index bfc7a9b79..45b0e9c11 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -81,13 +81,12 @@ static unsigned int font_line_height(gui::IGUIFont *font) GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, JoystickController *joystick, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, - InventoryManager *invmgr, IGameDef *gamedef, + Client *client, ISimpleTextureSource *tsrc, IFormSource* fsrc, TextDest* tdst, - Client* client, bool remap_dbl_click) : + bool remap_dbl_click) : GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr), m_device(dev), - m_invmgr(invmgr), - m_gamedef(gamedef), + m_invmgr(client), m_tsrc(tsrc), m_client(client), m_selected_item(NULL), @@ -307,8 +306,8 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data) void GUIFormSpecMenu::parseList(parserData* data,std::string element) { - if (m_gamedef == 0) { - warningstream<<"invalid use of 'list' with m_gamedef==0"<explicit_size) warningstream<<"invalid use of item_image_button without a size[] element"<idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(item_name, idef); @@ -2297,14 +2296,14 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase, if(!item.empty()) { drawItemStack(driver, m_font, item, - rect, &AbsoluteClippingRect, m_gamedef, + rect, &AbsoluteClippingRect, m_client, rotation_kind); } // Draw tooltip std::wstring tooltip_text = L""; if (hovering && !m_selected_item) { - tooltip_text = utf8_to_wide(item.getDefinition(m_gamedef->idef()).description); + tooltip_text = utf8_to_wide(item.getDefinition(m_client->idef()).description); } if (tooltip_text != L"") { std::vector tt_rows = str_split(tooltip_text, L'\n'); @@ -2349,7 +2348,7 @@ void GUIFormSpecMenu::drawSelectedItem() if (!m_selected_item) { drawItemStack(driver, m_font, ItemStack(), core::rect(v2s32(0, 0), v2s32(0, 0)), - NULL, m_gamedef, IT_ROT_DRAGGED); + NULL, m_client, IT_ROT_DRAGGED); return; } @@ -2363,7 +2362,7 @@ void GUIFormSpecMenu::drawSelectedItem() core::rect imgrect(0,0,imgsize.X,imgsize.Y); core::rect rect = imgrect + (m_pointer - imgrect.getCenter()); rect.constrainTo(driver->getViewPort()); - drawItemStack(driver, m_font, stack, rect, NULL, m_gamedef, IT_ROT_DRAGGED); + drawItemStack(driver, m_font, stack, rect, NULL, m_client, IT_ROT_DRAGGED); } void GUIFormSpecMenu::drawMenu() @@ -2488,11 +2487,11 @@ void GUIFormSpecMenu::drawMenu() */ for(u32 i=0; iidef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(spec.item_name, idef); core::rect imgrect(0, 0, spec.geom.X, spec.geom.Y); @@ -2509,7 +2508,7 @@ void GUIFormSpecMenu::drawMenu() #endif } drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect, - m_gamedef, IT_ROT_NONE); + m_client, IT_ROT_NONE); } /* @@ -2527,7 +2526,7 @@ void GUIFormSpecMenu::drawMenu() if (!item_hovered) { drawItemStack(driver, m_font, ItemStack(), core::rect(v2s32(0, 0), v2s32(0, 0)), - NULL, m_gamedef, IT_ROT_HOVERED); + NULL, m_client, IT_ROT_HOVERED); } /* TODO find way to show tooltips on touchscreen */ @@ -3470,7 +3469,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) // Check how many items can be moved move_amount = stack_from.count = MYMIN(move_amount, stack_from.count); - ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef()); + ItemStack leftover = stack_to.addItem(stack_from, m_client->idef()); // If source stack cannot be added to destination stack at all, // they are swapped if ((leftover.count == stack_from.count) && diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 95df11e6a..94b52e6f0 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -34,7 +34,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include "util/enriched_string.h" -class IGameDef; class InventoryManager; class ISimpleTextureSource; class Client; @@ -289,12 +288,10 @@ public: JoystickController *joystick, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, - InventoryManager *invmgr, - IGameDef *gamedef, + Client *client, ISimpleTextureSource *tsrc, IFormSource* fs_src, TextDest* txt_dst, - Client* client, bool remap_dbl_click = true); ~GUIFormSpecMenu(); @@ -384,7 +381,6 @@ protected: irr::IrrlichtDevice* m_device; InventoryManager *m_invmgr; - IGameDef *m_gamedef; ISimpleTextureSource *m_tsrc; Client *m_client; diff --git a/src/hud.cpp b/src/hud.cpp index 43d957380..a602125e3 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -22,10 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "hud.h" #include "settings.h" #include "util/numeric.h" -#include "util/string.h" #include "log.h" -#include "gamedef.h" -#include "itemdef.h" +#include "client.h" #include "inventory.h" #include "client/tile.h" #include "localplayer.h" @@ -41,13 +39,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr, - gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player, + gui::IGUIEnvironment* guienv, Client *client, LocalPlayer *player, Inventory *inventory) { this->driver = driver; this->smgr = smgr; this->guienv = guienv; - this->gamedef = gamedef; + this->client = client; this->player = player; this->inventory = inventory; @@ -61,7 +59,7 @@ Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr, for (unsigned int i = 0; i < 4; i++) hbar_colors[i] = video::SColor(255, 255, 255, 255); - tsrc = gamedef->getTextureSource(); + tsrc = client->getTextureSource(); v3f crosshair_color = g_settings->getV3F("crosshair_color"); u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255); @@ -92,7 +90,7 @@ Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr, m_selection_material.Lighting = false; if (g_settings->getBool("enable_shaders")) { - IShaderSource *shdrsrc = gamedef->getShaderSource(); + IShaderSource *shdrsrc = client->getShaderSource(); u16 shader_id = shdrsrc->getShader( mode == "halo" ? "selection_shader" : "default_shader", 1, 1); m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material; @@ -193,7 +191,7 @@ void Hud::drawItem(const ItemStack &item, const core::rect& rect, if (!use_hotbar_image) driver->draw2DRectangle(bgcolor2, rect, NULL); drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL, - gamedef, selected ? IT_ROT_SELECTED : IT_ROT_NONE); + client, selected ? IT_ROT_SELECTED : IT_ROT_NONE); } //NOTE: selectitem = 0 -> no selected; selectitem 1-based @@ -629,7 +627,7 @@ void drawItemStack(video::IVideoDriver *driver, const ItemStack &item, const core::rect &rect, const core::rect *clip, - IGameDef *gamedef, + Client *client, ItemRotationKind rotation_kind) { static MeshTimeInfo rotation_time_infos[IT_ROT_NONE]; @@ -643,8 +641,8 @@ void drawItemStack(video::IVideoDriver *driver, return; } - const ItemDefinition &def = item.getDefinition(gamedef->idef()); - scene::IMesh* mesh = gamedef->idef()->getWieldMesh(def.name, gamedef); + const ItemDefinition &def = item.getDefinition(client->idef()); + scene::IMesh* mesh = client->idef()->getWieldMesh(def.name, client); if (mesh) { driver->clearZBuffer(); diff --git a/src/hud.h b/src/hud.h index a4d7990e9..efa0c3648 100644 --- a/src/hud.h +++ b/src/hud.h @@ -95,7 +95,7 @@ struct HudElement { #include #include "irr_aabb3d.h" -class IGameDef; +class Client; class ITextureSource; class Inventory; class InventoryList; @@ -107,7 +107,7 @@ public: video::IVideoDriver *driver; scene::ISceneManager* smgr; gui::IGUIEnvironment *guienv; - IGameDef *gamedef; + Client *client; LocalPlayer *player; Inventory *inventory; ITextureSource *tsrc; @@ -121,7 +121,7 @@ public: bool use_hotbar_selected_image; Hud(video::IVideoDriver *driver,scene::ISceneManager* smgr, - gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player, + gui::IGUIEnvironment* guienv, Client *client, LocalPlayer *player, Inventory *inventory); ~Hud(); @@ -190,7 +190,7 @@ void drawItemStack(video::IVideoDriver *driver, const ItemStack &item, const core::rect &rect, const core::rect *clip, - IGameDef *gamedef, + Client *client, ItemRotationKind rotation_kind); #endif diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 1aa6331dc..5ba9d8f9a 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemdef.h" -#include "gamedef.h" #include "nodedef.h" #include "tool.h" #include "inventory.h" @@ -29,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mesh.h" #include "wieldmesh.h" #include "client/tile.h" +#include "client.h" #endif #include "log.h" #include "settings.h" @@ -317,7 +317,7 @@ public: #ifndef SERVER public: ClientCached* createClientCachedDirect(const std::string &name, - IGameDef *gamedef) const + Client *client) const { infostream<<"Lazily creating item texture and mesh for \"" <getTextureSource(); + ITextureSource *tsrc = client->getTextureSource(); const ItemDefinition &def = get(name); // Create new ClientCached @@ -345,7 +345,7 @@ public: ItemStack item = ItemStack(); item.name = def.name; - scene::IMesh *mesh = getItemMesh(gamedef, item); + scene::IMesh *mesh = getItemMesh(client, item); cc->wield_mesh = mesh; // Put in cache @@ -354,7 +354,7 @@ public: return cc; } ClientCached* getClientCached(const std::string &name, - IGameDef *gamedef) const + Client *client) const { ClientCached *cc = NULL; m_clientcached.get(name, &cc); @@ -363,7 +363,7 @@ public: if(thr_is_current_thread(m_main_thread)) { - return createClientCachedDirect(name, gamedef); + return createClientCachedDirect(name, client); } else { @@ -392,18 +392,18 @@ public: } // Get item inventory texture virtual video::ITexture* getInventoryTexture(const std::string &name, - IGameDef *gamedef) const + Client *client) const { - ClientCached *cc = getClientCached(name, gamedef); + ClientCached *cc = getClientCached(name, client); if(!cc) return NULL; return cc->inventory_texture; } // Get item wield mesh virtual scene::IMesh* getWieldMesh(const std::string &name, - IGameDef *gamedef) const + Client *client) const { - ClientCached *cc = getClientCached(name, gamedef); + ClientCached *cc = getClientCached(name, client); if(!cc) return NULL; return cc->wield_mesh; @@ -543,7 +543,7 @@ public: request = m_get_clientcached_queue.pop(); m_get_clientcached_queue.pushResult(request, - createClientCachedDirect(request.key, gamedef)); + createClientCachedDirect(request.key, (Client *)gamedef)); } #endif } diff --git a/src/itemdef.h b/src/itemdef.h index dcb98e8a9..2ade6116a 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemgroup.h" #include "sound.h" class IGameDef; +class Client; struct ToolCapabilities; /* @@ -107,10 +108,10 @@ public: #ifndef SERVER // Get item inventory texture virtual video::ITexture* getInventoryTexture(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; // Get item wield mesh virtual scene::IMesh* getWieldMesh(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; #endif virtual void serialize(std::ostream &os, u16 protocol_version)=0; @@ -133,10 +134,10 @@ public: #ifndef SERVER // Get item inventory texture virtual video::ITexture* getInventoryTexture(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; // Get item wield mesh virtual scene::IMesh* getWieldMesh(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; #endif // Remove all registered item and node definitions and aliases diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 4d0ca0600..b859c6455 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "event.h" #include "collision.h" -#include "gamedef.h" #include "nodedef.h" #include "settings.h" #include "environment.h" @@ -32,8 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc., LocalPlayer */ -LocalPlayer::LocalPlayer(Client *gamedef, const char *name): - Player(name, gamedef->idef()), +LocalPlayer::LocalPlayer(Client *client, const char *name): + Player(name, client->idef()), parent(0), hp(PLAYER_MAX_HP), got_teleported(false), @@ -79,7 +78,7 @@ LocalPlayer::LocalPlayer(Client *gamedef, const char *name): camera_barely_in_ceiling(false), m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30), m_cao(NULL), - m_gamedef(gamedef) + m_client(client) { // Initialize hp to 0, so that no hearts will be shown if server // doesn't support health points @@ -96,7 +95,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, std::vector *collision_info) { Map *map = &env->getMap(); - INodeDefManager *nodemgr = m_gamedef->ndef(); + INodeDefManager *nodemgr = m_client->ndef(); v3f position = getPosition(); @@ -109,8 +108,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, } // Skip collision detection if noclip mode is used - bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); - bool noclip = m_gamedef->checkLocalPrivilege("noclip") && + bool fly_allowed = m_client->checkLocalPrivilege("fly"); + bool noclip = m_client->checkLocalPrivilege("noclip") && g_settings->getBool("noclip"); bool free_move = noclip && fly_allowed && g_settings->getBool("free_move"); if (free_move) { @@ -241,7 +240,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, v3f accel_f = v3f(0,0,0); - collisionMoveResult result = collisionMoveSimple(env, m_gamedef, + collisionMoveResult result = collisionMoveSimple(env, m_client, pos_max_d, m_collisionbox, player_stepheight, dtime, &position, &m_speed, accel_f); @@ -376,7 +375,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, if(!result.standing_on_object && !touching_ground_was && touching_ground) { MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround"); - m_gamedef->event()->put(e); + m_client->event()->put(e); // Set camera impact value to be used for view bobbing camera_impact = getSpeed().Y * -1; @@ -448,8 +447,8 @@ void LocalPlayer::applyControl(float dtime) v3f speedH = v3f(0,0,0); // Horizontal (X, Z) v3f speedV = v3f(0,0,0); // Vertical (Y) - bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); - bool fast_allowed = m_gamedef->checkLocalPrivilege("fast"); + bool fly_allowed = m_client->checkLocalPrivilege("fly"); + bool fast_allowed = m_client->checkLocalPrivilege("fast"); bool free_move = fly_allowed && g_settings->getBool("free_move"); bool fast_move = fast_allowed && g_settings->getBool("fast_move"); @@ -599,7 +598,7 @@ void LocalPlayer::applyControl(float dtime) setSpeed(speedJ); MtEvent *e = new SimpleTriggerEvent("PlayerJump"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } else if(in_liquid) diff --git a/src/localplayer.h b/src/localplayer.h index 7a1cb7466..cbdcb9867 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -35,7 +35,7 @@ enum LocalPlayerAnimations {NO_ANIM, WALK_ANIM, DIG_ANIM, WD_ANIM}; // no local class LocalPlayer : public Player { public: - LocalPlayer(Client *gamedef, const char *name); + LocalPlayer(Client *client, const char *name); virtual ~LocalPlayer(); ClientActiveObject *parent; @@ -162,7 +162,7 @@ private: aabb3f m_collisionbox; GenericCAO* m_cao; - Client *m_gamedef; + Client *m_client; }; #endif diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 977eabb6e..143adb410 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -23,7 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "profiler.h" #include "nodedef.h" -#include "gamedef.h" #include "mesh.h" #include "minimap.h" #include "content_mapblock.h" @@ -43,14 +42,14 @@ static void applyFacesShading(video::SColor &color, const float factor) MeshMakeData */ -MeshMakeData::MeshMakeData(IGameDef *gamedef, bool use_shaders, +MeshMakeData::MeshMakeData(Client *client, bool use_shaders, bool use_tangent_vertices): m_vmanip(), m_blockpos(-1337,-1337,-1337), m_crack_pos_relative(-1337, -1337, -1337), m_smooth_lighting(false), m_show_hud(false), - m_gamedef(gamedef), + m_client(client), m_use_shaders(use_shaders), m_use_tangent_vertices(use_tangent_vertices) {} @@ -233,7 +232,7 @@ static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data) v3s16(1,1,1), }; - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); u16 ambient_occlusion = 0; u16 light_count = 0; @@ -664,7 +663,7 @@ static u8 face_contents(content_t m1, content_t m2, bool *equivalent, */ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data) { - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); TileSpec spec = ndef->get(mn).tiles[tileindex]; // Apply temporary crack if (p == data->m_crack_pos_relative) @@ -677,7 +676,7 @@ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data) */ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data) { - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0), // (0,0,1), (0,0,-1) or (0,0,0) @@ -734,7 +733,7 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data) u16 tile_index=facedir*16 + dir_i; TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data); spec.rotation=dir_to_tile[tile_index + 1]; - spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id); + spec.texture = data->m_client->tsrc()->getTexture(spec.texture_id); return spec; } @@ -753,7 +752,7 @@ static void getTileInfo( ) { VoxelManipulator &vmanip = data->m_vmanip; - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE; MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p); @@ -1020,10 +1019,10 @@ static void updateAllFastFaceRows(MeshMakeData *data, MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): m_mesh(new scene::SMesh()), m_minimap_mapblock(NULL), - m_gamedef(data->m_gamedef), - m_driver(m_gamedef->tsrc()->getDevice()->getVideoDriver()), - m_tsrc(m_gamedef->getTextureSource()), - m_shdrsrc(m_gamedef->getShaderSource()), + m_client(data->m_client), + m_driver(m_client->tsrc()->getDevice()->getVideoDriver()), + m_tsrc(m_client->getTextureSource()), + m_shdrsrc(m_client->getShaderSource()), m_animation_force_timer(0), // force initial animation m_last_crack(-1), m_crack_materials(), @@ -1243,7 +1242,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): if (m_use_tangent_vertices) { scene::IMeshManipulator* meshmanip = - m_gamedef->getSceneManager()->getMeshManipulator(); + m_client->getSceneManager()->getMeshManipulator(); meshmanip->recalculateTangents(m_mesh, true, false, false); } diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 8376468da..5adb7df3f 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/cpp11_container.h" #include -class IGameDef; +class Client; class IShaderSource; /* @@ -45,11 +45,11 @@ struct MeshMakeData bool m_smooth_lighting; bool m_show_hud; - IGameDef *m_gamedef; + Client *m_client; bool m_use_shaders; bool m_use_tangent_vertices; - MeshMakeData(IGameDef *gamedef, bool use_shaders, + MeshMakeData(Client *client, bool use_shaders, bool use_tangent_vertices = false); /* @@ -128,7 +128,7 @@ public: private: scene::IMesh *m_mesh; MinimapMapblock *m_minimap_mapblock; - IGameDef *m_gamedef; + Client *m_client; video::IVideoDriver *m_driver; ITextureSource *m_tsrc; IShaderSource *m_shdrsrc; diff --git a/src/mg_biome.cpp b/src/mg_biome.cpp index 78034bf6c..d564e9415 100644 --- a/src/mg_biome.cpp +++ b/src/mg_biome.cpp @@ -20,10 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_biome.h" #include "mg_decoration.h" #include "emerge.h" -#include "gamedef.h" +#include "server.h" #include "nodedef.h" #include "map.h" //for MMVManip -#include "log.h" #include "util/numeric.h" #include "util/mathconstants.h" #include "porting.h" @@ -33,10 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc., /////////////////////////////////////////////////////////////////////////////// -BiomeManager::BiomeManager(IGameDef *gamedef) : - ObjDefManager(gamedef, OBJDEF_BIOME) +BiomeManager::BiomeManager(Server *server) : + ObjDefManager(server, OBJDEF_BIOME) { - m_gamedef = gamedef; + m_server = server; // Create default biome to be used in case none exist Biome *b = new Biome; @@ -73,7 +72,7 @@ BiomeManager::~BiomeManager() void BiomeManager::clear() { - EmergeManager *emerge = m_gamedef->getEmergeManager(); + EmergeManager *emerge = m_server->getEmergeManager(); // Remove all dangling references in Decorations DecorationManager *decomgr = emerge->decomgr; diff --git a/src/mg_biome.h b/src/mg_biome.h index a10193bc3..15088f7dd 100644 --- a/src/mg_biome.h +++ b/src/mg_biome.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "noise.h" +class Server; class Settings; class BiomeManager; @@ -186,7 +187,7 @@ private: class BiomeManager : public ObjDefManager { public: - BiomeManager(IGameDef *gamedef); + BiomeManager(Server *server); virtual ~BiomeManager(); const char *getObjectTitle() const @@ -223,7 +224,7 @@ public: virtual void clear(); private: - IGameDef *m_gamedef; + Server *m_server; }; diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp index e028215dc..3d08d86fa 100644 --- a/src/mg_schematic.cpp +++ b/src/mg_schematic.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "mg_schematic.h" -#include "gamedef.h" +#include "server.h" #include "mapgen.h" #include "emerge.h" #include "map.h" @@ -34,16 +34,16 @@ with this program; if not, write to the Free Software Foundation, Inc., /////////////////////////////////////////////////////////////////////////////// -SchematicManager::SchematicManager(IGameDef *gamedef) : - ObjDefManager(gamedef, OBJDEF_SCHEMATIC) +SchematicManager::SchematicManager(Server *server) : + ObjDefManager(server, OBJDEF_SCHEMATIC) { - m_gamedef = gamedef; + m_server = server; } void SchematicManager::clear() { - EmergeManager *emerge = m_gamedef->getEmergeManager(); + EmergeManager *emerge = m_server->getEmergeManager(); // Remove all dangling references in Decorations DecorationManager *decomgr = emerge->decomgr; diff --git a/src/mg_schematic.h b/src/mg_schematic.h index da8859540..1d46e6ac4 100644 --- a/src/mg_schematic.h +++ b/src/mg_schematic.h @@ -29,7 +29,7 @@ class Mapgen; class MMVManip; class PseudoRandom; class NodeResolver; -class IGameDef; +class Server; /* Minetest Schematic File Format @@ -123,7 +123,7 @@ public: class SchematicManager : public ObjDefManager { public: - SchematicManager(IGameDef *gamedef); + SchematicManager(Server *server); virtual ~SchematicManager() {} virtual void clear(); @@ -139,7 +139,7 @@ public: } private: - IGameDef *m_gamedef; + Server *m_server; }; void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount, diff --git a/src/nodedef.cpp b/src/nodedef.cpp index dbbdf95d2..b7d023897 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef SERVER #include "client/tile.h" #include "mesh.h" +#include "client.h" #include #endif #include "log.h" @@ -572,8 +573,7 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, #ifndef SERVER void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, - scene::ISceneManager *smgr, scene::IMeshManipulator *meshmanip, - IGameDef *gamedef, const TextureSettings &tsettings) + scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings) { // minimap pixel color - the average color of a texture if (tsettings.enable_minimap && tiledef[0].name != "") @@ -709,7 +709,7 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc if ((drawtype == NDT_MESH) && (mesh != "")) { // Meshnode drawtype // Read the mesh and apply scale - mesh_ptr[0] = gamedef->getMesh(mesh); + mesh_ptr[0] = client->getMesh(mesh); if (mesh_ptr[0]){ v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale; scaleMesh(mesh_ptr[0], scale); @@ -1316,9 +1316,11 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef, #ifndef SERVER infostream << "CNodeDefManager::updateTextures(): Updating " "textures in node definitions" << std::endl; - ITextureSource *tsrc = gamedef->tsrc(); - IShaderSource *shdsrc = gamedef->getShaderSource(); - scene::ISceneManager* smgr = gamedef->getSceneManager(); + + Client *client = (Client *)gamedef; + ITextureSource *tsrc = client->tsrc(); + IShaderSource *shdsrc = client->getShaderSource(); + scene::ISceneManager* smgr = client->getSceneManager(); scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator(); TextureSettings tsettings; tsettings.readSettings(); @@ -1326,7 +1328,7 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef, u32 size = m_content_features.size(); for (u32 i = 0; i < size; i++) { - m_content_features[i].updateTextures(tsrc, shdsrc, smgr, meshmanip, gamedef, tsettings); + m_content_features[i].updateTextures(tsrc, shdsrc, meshmanip, client, tsettings); progress_callback(progress_callback_args, i, size); } #endif diff --git a/src/nodedef.h b/src/nodedef.h index 284c4a198..183b95d87 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef SERVER #include "client/tile.h" #include "shader.h" +class Client; #endif #include "itemgroup.h" #include "sound.h" // SimpleSoundSpec @@ -322,8 +323,7 @@ struct ContentFeatures u32 shader_id, bool use_normal_texture, bool backface_culling, u8 alpha, u8 material_type); void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, - scene::ISceneManager *smgr, scene::IMeshManipulator *meshmanip, - IGameDef *gamedef, const TextureSettings &tsettings); + scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings); #endif }; diff --git a/src/particles.cpp b/src/particles.cpp index 97f42e2c4..d9eb3cfa5 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -18,11 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "particles.h" -#include "constants.h" -#include "debug.h" -#include "settings.h" -#include "client/tile.h" -#include "gamedef.h" +#include "client.h" #include "collision.h" #include #include "util/numeric.h" @@ -452,7 +448,7 @@ void ParticleManager::clearAll () } } -void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, +void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, scene::ISceneManager* smgr, LocalPlayer *player) { switch (event->type) { @@ -477,9 +473,9 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, } video::ITexture *texture = - gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture)); + client->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture)); - ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player, + ParticleSpawner* toadd = new ParticleSpawner(client, smgr, player, event->add_particlespawner.amount, event->add_particlespawner.spawntime, *event->add_particlespawner.minpos, @@ -520,9 +516,9 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, } case CE_SPAWN_PARTICLE: { video::ITexture *texture = - gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture)); + client->tsrc()->getTextureForMesh(*(event->spawn_particle.texture)); - Particle* toadd = new Particle(gamedef, smgr, player, m_env, + Particle* toadd = new Particle(client, smgr, player, m_env, *event->spawn_particle.pos, *event->spawn_particle.vel, *event->spawn_particle.acc, diff --git a/src/particles.h b/src/particles.h index eb8c6665d..00cb2c08e 100644 --- a/src/particles.h +++ b/src/particles.h @@ -170,7 +170,7 @@ public: void step (float dtime); - void handleParticleEvent(ClientEvent *event,IGameDef *gamedef, + void handleParticleEvent(ClientEvent *event, Client *client, scene::ISceneManager* smgr, LocalPlayer *player); void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp index 84aa9252c..b240ec21f 100644 --- a/src/pathfinder.cpp +++ b/src/pathfinder.cpp @@ -24,12 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "pathfinder.h" #include "serverenvironment.h" -#include "gamedef.h" +#include "server.h" #include "nodedef.h" -#include "map.h" -#include "log.h" -#include "irr_aabb3d.h" -#include "util/basic_macros.h" //#define PATHFINDER_DEBUG //#define PATHFINDER_CALC_TIME diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 3cdd3cbfe..3d03c0c41 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -20,14 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_nodemeta.h" #include "lua_api/l_internal.h" #include "lua_api/l_inventory.h" -#include "common/c_converter.h" #include "common/c_content.h" #include "serverenvironment.h" #include "map.h" -#include "gamedef.h" -#include "nodemetadata.h" - - +#include "server.h" /* NodeMetaRef diff --git a/src/server.cpp b/src/server.cpp index 60dbef0d2..7380d37c2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -50,7 +50,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_abm.h" #include "content_sao.h" #include "mods.h" -#include "sound.h" // dummySoundManager #include "event_manager.h" #include "serverlist.h" #include "util/string.h" @@ -3310,29 +3309,12 @@ ICraftDefManager *Server::getCraftDefManager() { return m_craftdef; } -ITextureSource *Server::getTextureSource() -{ - return NULL; -} -IShaderSource *Server::getShaderSource() -{ - return NULL; -} -scene::ISceneManager *Server::getSceneManager() -{ - return NULL; -} u16 Server::allocateUnknownNodeId(const std::string &name) { return m_nodedef->allocateDummy(name); } -ISoundManager *Server::getSoundManager() -{ - return &dummySoundManager; -} - MtEventManager *Server::getEventManager() { return m_event; diff --git a/src/server.h b/src/server.h index fe7b50b77..a86f75f1d 100644 --- a/src/server.h +++ b/src/server.h @@ -283,13 +283,9 @@ public: virtual IItemDefManager* getItemDefManager(); virtual INodeDefManager* getNodeDefManager(); virtual ICraftDefManager* getCraftDefManager(); - virtual ITextureSource* getTextureSource(); - virtual IShaderSource* getShaderSource(); virtual u16 allocateUnknownNodeId(const std::string &name); - virtual ISoundManager* getSoundManager(); virtual MtEventManager* getEventManager(); - virtual scene::ISceneManager* getSceneManager(); - virtual IRollbackManager *getRollbackManager() { return m_rollback; } + IRollbackManager *getRollbackManager() { return m_rollback; } virtual EmergeManager *getEmergeManager() { return m_emerge; } IWritableItemDefManager* getWritableItemDefManager(); diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index c9fa64ec5..e1962bcff 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -352,11 +352,11 @@ void ActiveBlockList::update(std::vector &active_positions, */ ServerEnvironment::ServerEnvironment(ServerMap *map, - GameScripting *scriptIface, IGameDef *gamedef, + GameScripting *scriptIface, Server *server, const std::string &path_world) : m_map(map), m_script(scriptIface), - m_gamedef(gamedef), + m_server(server), m_path_world(path_world), m_send_recommended_timer(0), m_active_block_interval_overload_skip(0), @@ -487,7 +487,7 @@ void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason, for (std::vector::iterator it = m_players.begin(); it != m_players.end(); ++it) { RemotePlayer *player = dynamic_cast(*it); - ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id, + m_server->DenyAccessVerCompliant(player->peer_id, player->protocol_version, reason, str_reason, reconnect); } } @@ -501,7 +501,7 @@ void ServerEnvironment::saveLoadedPlayers() it != m_players.end(); ++it) { if ((*it)->checkModified()) { - (*it)->save(players_path, m_gamedef); + (*it)->save(players_path, m_server); } } } @@ -511,7 +511,7 @@ void ServerEnvironment::savePlayer(RemotePlayer *player) std::string players_path = m_path_world + DIR_DELIM "players"; fs::CreateDir(players_path); - player->save(players_path, m_gamedef); + player->save(players_path, m_server); } RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao) @@ -523,7 +523,7 @@ RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, Playe RemotePlayer *player = getPlayer(playername.c_str()); if (!player) { - player = new RemotePlayer("", m_gamedef->idef()); + player = new RemotePlayer("", m_server->idef()); newplayer = true; } @@ -632,7 +632,7 @@ void ServerEnvironment::loadMeta() } catch (SettingNotFoundException &e) { // No problem, this is expected. Just continue with an empty string } - m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time); + m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time); m_day_count = args.exists("day_count") ? args.getU64("day_count") : 0; @@ -640,7 +640,7 @@ void ServerEnvironment::loadMeta() void ServerEnvironment::loadDefaultMeta() { - m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time); + m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time); } struct ActiveABM @@ -902,7 +902,7 @@ void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm) bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) { - INodeDefManager *ndef = m_gamedef->ndef(); + INodeDefManager *ndef = m_server->ndef(); MapNode n_old = m_map->getNodeNoEx(p); // Call destructor @@ -929,7 +929,7 @@ bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) bool ServerEnvironment::removeNode(v3s16 p) { - INodeDefManager *ndef = m_gamedef->ndef(); + INodeDefManager *ndef = m_server->ndef(); MapNode n_old = m_map->getNodeNoEx(p); // Call destructor diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 20a783ea5..d71d29a9c 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -29,6 +29,8 @@ class PlayerSAO; class ServerEnvironment; class ActiveBlockModifier; class ServerActiveObject; +class Server; +class GameScripting; /* {Active, Loading} block modifier interface. @@ -190,7 +192,7 @@ class ServerEnvironment : public Environment { public: ServerEnvironment(ServerMap *map, GameScripting *scriptIface, - IGameDef *gamedef, const std::string &path_world); + Server *server, const std::string &path_world); ~ServerEnvironment(); Map & getMap(); @@ -201,8 +203,8 @@ public: GameScripting* getScriptIface() { return m_script; } - IGameDef *getGameDef() - { return m_gamedef; } + Server *getGameDef() + { return m_server; } float getSendRecommendedInterval() { return m_recommended_send_interval; } @@ -377,8 +379,8 @@ private: ServerMap *m_map; // Lua state GameScripting* m_script; - // Game definition - IGameDef *m_gamedef; + // Server definition + Server *m_server; // World path const std::string m_path_world; // Active object list diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index 9c4d5b642..c305238fe 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "wieldmesh.h" #include "inventory.h" -#include "gamedef.h" +#include "client.h" #include "itemdef.h" #include "nodedef.h" #include "mesh.h" @@ -283,7 +283,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, video::SMaterial &material = m_meshnode->getMaterial(0); material.setTexture(0, tsrc->getTextureForMesh(imagename)); material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; - material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; material.MaterialType = m_material_type; material.setFlag(video::EMF_BACK_FACE_CULLING, true); // Enable bi/trilinear filtering only for high resolution textures @@ -304,12 +304,12 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, } } -void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef) +void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) { - ITextureSource *tsrc = gamedef->getTextureSource(); - IItemDefManager *idef = gamedef->getItemDefManager(); - IShaderSource *shdrsrc = gamedef->getShaderSource(); - INodeDefManager *ndef = gamedef->getNodeDefManager(); + ITextureSource *tsrc = client->getTextureSource(); + IItemDefManager *idef = client->getItemDefManager(); + IShaderSource *shdrsrc = client->getShaderSource(); + INodeDefManager *ndef = client->getNodeDefManager(); const ItemDefinition &def = item.getDefinition(idef); const ContentFeatures &f = ndef->get(def.name); content_t id = ndef->getId(def.name); @@ -341,7 +341,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef) } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) { setCube(f.tiles, def.wield_scale, tsrc); } else { - MeshMakeData mesh_make_data(gamedef, false); + MeshMakeData mesh_make_data(client, false); MapNode mesh_make_node(id, 255, 0); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); @@ -435,11 +435,11 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh) m_meshnode->setVisible(true); } -scene::IMesh *getItemMesh(IGameDef *gamedef, const ItemStack &item) +scene::IMesh *getItemMesh(Client *client, const ItemStack &item) { - ITextureSource *tsrc = gamedef->getTextureSource(); - IItemDefManager *idef = gamedef->getItemDefManager(); - INodeDefManager *ndef = gamedef->getNodeDefManager(); + ITextureSource *tsrc = client->getTextureSource(); + IItemDefManager *idef = client->getItemDefManager(); + INodeDefManager *ndef = client->getNodeDefManager(); const ItemDefinition &def = item.getDefinition(idef); const ContentFeatures &f = ndef->get(def.name); content_t id = ndef->getId(def.name); @@ -470,7 +470,7 @@ scene::IMesh *getItemMesh(IGameDef *gamedef, const ItemStack &item) mesh = cloneMesh(g_extrusion_mesh_cache->createCube()); scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); } else { - MeshMakeData mesh_make_data(gamedef, false); + MeshMakeData mesh_make_data(client, false); MapNode mesh_make_node(id, 255, 0); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); diff --git a/src/wieldmesh.h b/src/wieldmesh.h index 0b3136bc1..0162c5e5a 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include struct ItemStack; -class IGameDef; +class Client; class ITextureSource; struct TileSpec; @@ -42,7 +42,7 @@ public: v3f wield_scale, ITextureSource *tsrc); void setExtruded(const std::string &imagename, v3f wield_scale, ITextureSource *tsrc, u8 num_frames); - void setItem(const ItemStack &item, IGameDef *gamedef); + void setItem(const ItemStack &item, Client *client); // Sets the vertex color of the wield mesh. // Must only be used if the constructor was called with lighting = false @@ -77,7 +77,7 @@ private: aabb3f m_bounding_box; }; -scene::IMesh *getItemMesh(IGameDef *gamedef, const ItemStack &item); +scene::IMesh *getItemMesh(Client *client, const ItemStack &item); scene::IMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename); -- cgit v1.2.3 From ef0aa7d5b543b6561e1b7292b2d0a0ac43add55d Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Wed, 11 Jan 2017 22:48:14 +0100 Subject: Optimize SAO getStaticData by using std::string pointer instead of return copy Signed-off-by: Loic Blot --- src/content_sao.cpp | 9 ++++----- src/content_sao.h | 4 ++-- src/serverenvironment.cpp | 9 ++++++--- src/serverobject.h | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) (limited to 'src/serverenvironment.cpp') diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 852e2d788..bf8282af4 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -521,7 +521,7 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) return os.str(); } -std::string LuaEntitySAO::getStaticData() const +void LuaEntitySAO::getStaticData(std::string *result) const { verbosestream<getBasePosition(); - std::string staticdata = object->getStaticData(); + std::string staticdata = ""; + object->getStaticData(&staticdata); StaticObject s_obj(object->getType(), objectpos, staticdata); // Add to the block where the object is located in v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); @@ -1980,7 +1981,8 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) <getStaticData(); + std::string staticdata_new = ""; + obj->getStaticData(&staticdata_new); StaticObject s_obj(obj->getType(), objectpos, staticdata_new); block->m_static_objects.insert(id, s_obj); obj->m_static_block = blockpos_o; @@ -2020,7 +2022,8 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) if(obj->isStaticAllowed()) { // Create new static object - std::string staticdata_new = obj->getStaticData(); + std::string staticdata_new = ""; + obj->getStaticData(&staticdata_new); StaticObject s_obj(obj->getType(), objectpos, staticdata_new); bool stays_in_same_block = false; diff --git a/src/serverobject.h b/src/serverobject.h index 9e8b5a779..26c8b062d 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -119,10 +119,10 @@ public: when it is created (converted from static to active - actually the data is the static form) */ - virtual std::string getStaticData() + virtual void getStaticData(std::string *result) { assert(isStaticAllowed()); - return ""; + *result = ""; } /* Return false in here to never save and instead remove object -- cgit v1.2.3 From 2ea60156437962d7d29d20606bf5d9189059f76b Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Sat, 21 Jan 2017 10:41:00 +0100 Subject: Do not force deletion of players when mapblock is full (#5081) This fixes #4067 --- src/serverenvironment.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/serverenvironment.cpp') diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index d3b85a430..41cd63684 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -2146,6 +2146,12 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) continue; } + // If it's a forced delete, there are too many objects in mapblock + // Ignore players, they should not be removed on force delete + if (force_delete && obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + continue; + } + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " <<"object id="< Date: Sat, 21 Jan 2017 11:29:18 +0100 Subject: Warning fix for 2ea60156437962d7d29d20606bf5d9189059f76b (#5082) Neither flag as force delete nor show the warning when mapblock is full and object is a player --- src/serverenvironment.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'src/serverenvironment.cpp') diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 41cd63684..01dc3ff10 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -2086,7 +2086,9 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) if(block) { - if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { + // Force delete object if mapblock is full, but ignore players + if (obj->getType() != ACTIVEOBJECT_TYPE_PLAYER && + block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { warningstream << "ServerEnv: Trying to store id = " << obj->getId() << " statically but block " << PP(blockpos) << " already contains " @@ -2146,12 +2148,6 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) continue; } - // If it's a forced delete, there are too many objects in mapblock - // Ignore players, they should not be removed on force delete - if (force_delete && obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - continue; - } - verbosestream<<"ServerEnvironment::deactivateFarObjects(): " <<"object id="< Date: Fri, 27 Jan 2017 08:59:30 +0100 Subject: Implement player attribute backend (#4155) * This backend permit mods to store extra players attributes to a common interface. * Add the obj:set_attribute(attr, value) Lua call * Add the obj:get_attribute(attr) Lua call Examples: * player:set_attribute("home:home", "10,25,-78") * player:get_attribute("default:mana") Attributes are saved as a json in the player file in extended_attributes key They are saved only if a modification on the attributes occurs and loaded when emergePlayer is called (they are attached to PlayerSAO). --- doc/lua_api.txt | 2 ++ src/content_sao.cpp | 1 + src/content_sao.h | 37 +++++++++++++++++++++++++++++++++++++ src/remoteplayer.cpp | 38 +++++++++++++++++++++++++++++++++++--- src/remoteplayer.h | 5 ++++- src/script/lua_api/l_object.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ src/script/lua_api/l_object.h | 6 ++++++ src/server.h | 1 - src/serverenvironment.cpp | 3 ++- 9 files changed, 128 insertions(+), 6 deletions(-) (limited to 'src/serverenvironment.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 62a7b81f7..ee7d57c2f 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2899,6 +2899,8 @@ This is basically a reference to a C++ `ServerActiveObject` * `0`: player is drowning, * `1`-`10`: remaining number of bubbles * `11`: bubbles bar is not shown +* `set_attribute(attribute, value)`: sets an extra attribute with value on player +* `get_attribute(attribute)`: returns value for extra attribute. Returns nil if no attribute found. * `set_inventory_formspec(formspec)` * Redefine player's inventory form * Should usually be called in on_joinplayer diff --git a/src/content_sao.cpp b/src/content_sao.cpp index bb62aea7d..35133490e 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -791,6 +791,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer m_pitch(0), m_fov(0), m_wanted_range(0), + m_extended_attributes_modified(false), // public m_physics_override_speed(1), m_physics_override_jump(1), diff --git a/src/content_sao.h b/src/content_sao.h index c3674fa2d..bbf244742 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -180,6 +180,7 @@ public: } }; +typedef UNORDERED_MAP PlayerAttributes; class RemotePlayer; class PlayerSAO : public UnitSAO @@ -249,6 +250,39 @@ public: int getWieldIndex() const; void setWieldIndex(int i); + /* + Modding interface + */ + inline void setExtendedAttribute(const std::string &attr, const std::string &value) + { + m_extra_attributes[attr] = value; + m_extended_attributes_modified = true; + } + + inline bool getExtendedAttribute(const std::string &attr, std::string *value) + { + if (m_extra_attributes.find(attr) == m_extra_attributes.end()) + return false; + + *value = m_extra_attributes[attr]; + return true; + } + + inline const PlayerAttributes &getExtendedAttributes() + { + return m_extra_attributes; + } + + inline bool extendedAttributesModified() const + { + return m_extended_attributes_modified; + } + + inline void setExtendedAttributeModified(bool v) + { + m_extended_attributes_modified = v; + } + /* PlayerSAO-specific */ @@ -343,6 +377,9 @@ private: f32 m_pitch; f32 m_fov; s16 m_wanted_range; + + PlayerAttributes m_extra_attributes; + bool m_extended_attributes_modified; public: float m_physics_override_speed; float m_physics_override_jump; diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 18bfa1030..6853ad6d9 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -19,13 +19,14 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "remoteplayer.h" +#include #include "content_sao.h" #include "filesys.h" #include "gamedef.h" #include "porting.h" // strlcpy +#include "server.h" #include "settings.h" - /* RemotePlayer */ @@ -112,9 +113,23 @@ void RemotePlayer::save(std::string savedir, IGameDef *gamedef) } infostream << "Didn't find free file for player " << m_name << std::endl; - return; } +void RemotePlayer::serializeExtraAttributes(std::string &output) +{ + assert(m_sao); + Json::Value json_root; + const PlayerAttributes &attrs = m_sao->getExtendedAttributes(); + for (PlayerAttributes::const_iterator it = attrs.begin(); it != attrs.end(); ++it) { + json_root[(*it).first] = (*it).second; + } + + Json::FastWriter writer; + output = writer.write(json_root); + m_sao->setExtendedAttributeModified(false); +} + + void RemotePlayer::deSerialize(std::istream &is, const std::string &playername, PlayerSAO *sao) { @@ -150,6 +165,20 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername, try { sao->setBreath(args.getS32("breath"), false); } catch (SettingNotFoundException &e) {} + + try { + std::string extended_attributes = args.get("extended_attributes"); + Json::Reader reader; + Json::Value attr_root; + reader.parse(extended_attributes, attr_root); + + const Json::Value::Members attr_list = attr_root.getMemberNames(); + for (Json::Value::Members::const_iterator it = attr_list.begin(); + it != attr_list.end(); ++it) { + Json::Value attr_value = attr_root[*it]; + sao->setExtendedAttribute(*it, attr_value.asString()); + } + } catch (SettingNotFoundException &e) {} } inventory.deSerialize(is); @@ -175,7 +204,6 @@ void RemotePlayer::serialize(std::ostream &os) Settings args; args.setS32("version", 1); args.set("name", m_name); - //args.set("password", m_password); // This should not happen assert(m_sao); @@ -185,6 +213,10 @@ void RemotePlayer::serialize(std::ostream &os) args.setFloat("yaw", m_sao->getYaw()); args.setS32("breath", m_sao->getBreath()); + std::string extended_attrs = ""; + serializeExtraAttributes(extended_attrs); + args.set("extended_attributes", extended_attrs); + args.writeLines(os); os<<"PlayerArgsEnd\n"; diff --git a/src/remoteplayer.h b/src/remoteplayer.h index 61b5a23de..f44fb9332 100644 --- a/src/remoteplayer.h +++ b/src/remoteplayer.h @@ -25,11 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc., class PlayerSAO; -enum RemotePlayerChatResult { +enum RemotePlayerChatResult +{ RPLAYER_CHATRESULT_OK, RPLAYER_CHATRESULT_FLOODING, RPLAYER_CHATRESULT_KICK, }; + /* Player on the server */ @@ -135,6 +137,7 @@ private: deSerialize stops reading exactly at the right point. */ void serialize(std::ostream &os); + void serializeExtraAttributes(std::string &output); PlayerSAO *m_sao; bool m_dirty; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index be4451704..9352812ab 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1181,6 +1181,45 @@ int ObjectRef::l_get_breath(lua_State *L) return 1; } +// set_attribute(self, attribute, value) +int ObjectRef::l_set_attribute(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if (co == NULL) { + return 0; + } + + std::string attr = luaL_checkstring(L, 2); + std::string value = luaL_checkstring(L, 3); + + if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + co->setExtendedAttribute(attr, value); + } + return 1; +} + +// get_attribute(self, attribute) +int ObjectRef::l_get_attribute(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if (co == NULL) { + return 0; + } + + std::string attr = luaL_checkstring(L, 2); + + std::string value = ""; + if (co->getExtendedAttribute(attr, &value)) { + lua_pushstring(L, value.c_str()); + return 1; + } + + return 0; +} + + // set_inventory_formspec(self, formspec) int ObjectRef::l_set_inventory_formspec(lua_State *L) { @@ -1839,6 +1878,8 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, set_look_pitch), luamethod(ObjectRef, get_breath), luamethod(ObjectRef, set_breath), + luamethod(ObjectRef, get_attribute), + luamethod(ObjectRef, set_attribute), luamethod(ObjectRef, set_inventory_formspec), luamethod(ObjectRef, get_inventory_formspec), luamethod(ObjectRef, get_player_control), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 96d0abae8..2c9aa559a 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -226,6 +226,12 @@ private: // get_breath(self, breath) static int l_get_breath(lua_State *L); + // set_attribute(self, attribute, value) + static int l_set_attribute(lua_State *L); + + // get_attribute(self, attribute) + static int l_get_attribute(lua_State *L); + // set_inventory_formspec(self, formspec) static int l_set_inventory_formspec(lua_State *L); diff --git a/src/server.h b/src/server.h index e5121bdc3..8f553ce38 100644 --- a/src/server.h +++ b/src/server.h @@ -576,7 +576,6 @@ private: float m_time_of_day_send_timer; // Uptime of server in seconds MutexedVariable m_uptime; - /* Client interface */ diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 01dc3ff10..7a5cfafd6 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -500,7 +500,8 @@ void ServerEnvironment::saveLoadedPlayers() for (std::vector::iterator it = m_players.begin(); it != m_players.end(); ++it) { - if ((*it)->checkModified()) { + if ((*it)->checkModified() || + ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) { (*it)->save(players_path, m_server); } } -- cgit v1.2.3 From de664b1c6d4b2bca47f918a6a865a920434bf664 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 4 Feb 2017 13:31:21 +0100 Subject: Fix PlayerSAO deletion warning (0eede97af2927dcda3545192403b0a44f30bcd1f) --- src/serverenvironment.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/serverenvironment.cpp') diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 7a5cfafd6..8d86a4e0a 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -2087,9 +2087,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) if(block) { - // Force delete object if mapblock is full, but ignore players - if (obj->getType() != ACTIVEOBJECT_TYPE_PLAYER && - block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { + if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { warningstream << "ServerEnv: Trying to store id = " << obj->getId() << " statically but block " << PP(blockpos) << " already contains " @@ -2149,6 +2147,13 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) continue; } + if (!force_delete && obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + warningstream << "ServerEnvironment::deactivateFarObjects(): " + << "Trying to delete player object, THIS SHOULD NEVER HAPPEN!" + << std::endl; + continue; + } + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " <<"object id="< Date: Wed, 15 Feb 2017 17:36:47 +0100 Subject: Fix >5 year old PlayerSAO deletion bug force_delete=true is usually set at shutdown in order to also remove PlayerSAOs, however when too many objects per block are detected force_delete is also set to true. This was intended only for the current loop iteration but obviously persisted to the next iterations thereby deleting all other remaining SAOs. --- src/serverenvironment.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'src/serverenvironment.cpp') diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 8d86a4e0a..61faaace7 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1941,11 +1941,14 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s) If block wasn't generated (not in memory or on disk), */ -void ServerEnvironment::deactivateFarObjects(bool force_delete) +void ServerEnvironment::deactivateFarObjects(bool _force_delete) { std::vector objects_to_remove; for(ActiveObjectMap::iterator i = m_active_objects.begin(); i != m_active_objects.end(); ++i) { + // force_delete might be overriden per object + bool force_delete = _force_delete; + ServerActiveObject* obj = i->second; assert(obj); @@ -2147,13 +2150,6 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) continue; } - if (!force_delete && obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - warningstream << "ServerEnvironment::deactivateFarObjects(): " - << "Trying to delete player object, THIS SHOULD NEVER HAPPEN!" - << std::endl; - continue; - } - verbosestream<<"ServerEnvironment::deactivateFarObjects(): " <<"object id="< Date: Mon, 13 Feb 2017 04:37:25 +0000 Subject: Objectpos over limit: Avoid crash caused by sector over limit Reduce the object limit by mapblock size, to avoid objects being added just inside the map generation limit but in a block and sector that extend beyond the map generation limit. Change notification of 'objectpos over limit' from red in-chat ERROR to in-terminal only WARNING, since this will happen often using mob mods near the world's edge. --- src/mapblock.h | 20 ++++++++++++-------- src/serverenvironment.cpp | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) (limited to 'src/serverenvironment.cpp') diff --git a/src/mapblock.h b/src/mapblock.h index 5a0ec937a..bec3b6f56 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -669,14 +669,18 @@ typedef std::vector MapBlockVect; inline bool objectpos_over_limit(v3f p) { - const float map_gen_limit_bs = MYMIN(MAX_MAP_GENERATION_LIMIT, - g_settings->getU16("map_generation_limit")) * BS; - return (p.X < -map_gen_limit_bs - || p.X > map_gen_limit_bs - || p.Y < -map_gen_limit_bs - || p.Y > map_gen_limit_bs - || p.Z < -map_gen_limit_bs - || p.Z > map_gen_limit_bs); + // MAP_BLOCKSIZE must be subtracted to avoid an object being spawned just + // within the map generation limit but in a block and sector that extend + // beyond the map generation limit. + // This avoids crashes caused by sector over limit in createSector(). + const float object_limit = (MYMIN(MAX_MAP_GENERATION_LIMIT, + g_settings->getU16("map_generation_limit")) - MAP_BLOCKSIZE) * BS; + return (p.X < -object_limit + || p.X > object_limit + || p.Y < -object_limit + || p.Y > object_limit + || p.Z < -object_limit + || p.Z > object_limit); } /* diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 61faaace7..f3f489092 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1667,7 +1667,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, if (objectpos_over_limit(object->getBasePosition())) { v3f p = object->getBasePosition(); - errorstream << "ServerEnvironment::addActiveObjectRaw(): " + warningstream << "ServerEnvironment::addActiveObjectRaw(): " << "object position (" << p.X << "," << p.Y << "," << p.Z << ") outside maximum range" << std::endl; if (object->environmentDeletes()) -- cgit v1.2.3 From 2efae3ffd720095222c800e016286a45c9fe1e5c Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 21 Jan 2017 15:02:08 +0100 Subject: [CSM] Client side modding * rename GameScripting to ServerScripting * Make getBuiltinLuaPath static serverside * Add on_shutdown callback * Add on_receiving_chat_message & on_sending_chat_message callbacks * ScriptApiBase: use IGameDef instead of Server This permits to share common attribute between client & server * Enable mod security in client side modding without conditions --- builtin/client/init.lua | 22 +++++++ builtin/client/register.lua | 62 +++++++++++++++++++ builtin/init.lua | 3 + src/client.cpp | 45 +++++++++++--- src/client.h | 10 +++ src/content_abm.cpp | 2 +- src/content_sao.cpp | 2 +- src/emerge.cpp | 2 +- src/environment.cpp | 2 +- src/game.cpp | 2 + src/gamedef.h | 7 ++- src/guiFormSpecMenu.cpp | 2 +- src/inventorymanager.cpp | 2 +- src/network/clientpackethandler.cpp | 6 +- src/network/serverpackethandler.cpp | 2 +- src/script/CMakeLists.txt | 7 ++- src/script/clientscripting.cpp | 54 ++++++++++++++++ src/script/clientscripting.h | 40 ++++++++++++ src/script/cpp_api/CMakeLists.txt | 1 + src/script/cpp_api/s_base.cpp | 21 +++++-- src/script/cpp_api/s_base.h | 14 ++++- src/script/cpp_api/s_client.cpp | 61 ++++++++++++++++++ src/script/cpp_api/s_client.h | 36 +++++++++++ src/script/cpp_api/s_security.cpp | 12 ++-- src/script/lua_api/CMakeLists.txt | 1 + src/script/lua_api/l_client.cpp | 33 ++++++++++ src/script/lua_api/l_client.h | 36 +++++++++++ src/script/lua_api/l_env.cpp | 6 +- src/script/lua_api/l_env.h | 2 +- src/script/lua_api/l_object.cpp | 2 +- src/script/scripting_game.cpp | 119 ------------------------------------ src/script/scripting_game.h | 57 ----------------- src/script/serverscripting.cpp | 119 ++++++++++++++++++++++++++++++++++++ src/script/serverscripting.h | 57 +++++++++++++++++ src/server.cpp | 6 +- src/server.h | 10 +-- src/serverenvironment.cpp | 4 +- src/serverenvironment.h | 8 +-- src/unittest/test.cpp | 9 ++- 39 files changed, 655 insertions(+), 231 deletions(-) create mode 100644 builtin/client/init.lua create mode 100644 builtin/client/register.lua create mode 100644 src/script/clientscripting.cpp create mode 100644 src/script/clientscripting.h create mode 100644 src/script/cpp_api/s_client.cpp create mode 100644 src/script/cpp_api/s_client.h create mode 100644 src/script/lua_api/l_client.cpp create mode 100644 src/script/lua_api/l_client.h delete mode 100644 src/script/scripting_game.cpp delete mode 100644 src/script/scripting_game.h create mode 100644 src/script/serverscripting.cpp create mode 100644 src/script/serverscripting.h (limited to 'src/serverenvironment.cpp') diff --git a/builtin/client/init.lua b/builtin/client/init.lua new file mode 100644 index 000000000..d14301ade --- /dev/null +++ b/builtin/client/init.lua @@ -0,0 +1,22 @@ +-- Minetest: builtin/client/init.lua +local scriptpath = core.get_builtin_path()..DIR_DELIM +local clientpath = scriptpath.."client"..DIR_DELIM + +dofile(clientpath .. "register.lua") + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_shutdown(function() + print("shutdown client") +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_receiving_chat_messages(function(message) + print("Received message " .. message) + return false +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_sending_chat_messages(function(message) + print("Sending message " .. message) + return false +end) diff --git a/builtin/client/register.lua b/builtin/client/register.lua new file mode 100644 index 000000000..c793195a1 --- /dev/null +++ b/builtin/client/register.lua @@ -0,0 +1,62 @@ + +core.callback_origins = {} + +function core.run_callbacks(callbacks, mode, ...) + assert(type(callbacks) == "table") + local cb_len = #callbacks + if cb_len == 0 then + if mode == 2 or mode == 3 then + return true + elseif mode == 4 or mode == 5 then + return false + end + end + local ret + for i = 1, cb_len do + local cb_ret = callbacks[i](...) + + if mode == 0 and i == 1 or mode == 1 and i == cb_len then + ret = cb_ret + elseif mode == 2 then + if not cb_ret or i == 1 then + ret = cb_ret + end + elseif mode == 3 then + if cb_ret then + return cb_ret + end + ret = cb_ret + elseif mode == 4 then + if (cb_ret and not ret) or i == 1 then + ret = cb_ret + end + elseif mode == 5 and cb_ret then + return cb_ret + end + end + return ret +end + +-- +-- Callback registration +-- + +local function make_registration() + local t = {} + local registerfunc = function(func) + t[#t + 1] = func + core.callback_origins[func] = { + mod = core.get_current_modname() or "??", + name = debug.getinfo(1, "n").name or "??" + } + --local origin = core.callback_origins[func] + --print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func)) + end + return t, registerfunc +end + +core.registered_on_shutdown, core.register_on_shutdown = make_registration() +core.registered_on_receiving_chat_messages, core.register_on_receiving_chat_messages = make_registration() +core.registered_on_sending_chat_messages, core.register_on_sending_chat_messages = make_registration() + + diff --git a/builtin/init.lua b/builtin/init.lua index b34ad14a0..590f7fa8c 100644 --- a/builtin/init.lua +++ b/builtin/init.lua @@ -27,6 +27,7 @@ minetest = core -- Load other files local scriptdir = core.get_builtin_path() .. DIR_DELIM local gamepath = scriptdir .. "game" .. DIR_DELIM +local clientpath = scriptdir .. "client" .. DIR_DELIM local commonpath = scriptdir .. "common" .. DIR_DELIM local asyncpath = scriptdir .. "async" .. DIR_DELIM @@ -45,6 +46,8 @@ elseif INIT == "mainmenu" then end elseif INIT == "async" then dofile(asyncpath .. "init.lua") +elseif INIT == "client" then + dofile(clientpath .. "init.lua") else error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT))) end diff --git a/src/client.cpp b/src/client.cpp index 30058a2b0..faf454b35 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -32,28 +32,20 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client.h" #include "network/clientopcodes.h" #include "filesys.h" -#include "porting.h" #include "mapblock_mesh.h" #include "mapblock.h" #include "minimap.h" -#include "settings.h" +#include "mods.h" #include "profiler.h" #include "gettext.h" -#include "log.h" -#include "nodemetadata.h" -#include "itemdef.h" -#include "shader.h" #include "clientmap.h" #include "clientmedia.h" -#include "sound.h" -#include "IMeshCache.h" -#include "config.h" #include "version.h" #include "drawscene.h" #include "database-sqlite3.h" #include "serialization.h" #include "guiscalingfilter.h" -#include "raycast.h" +#include "script/clientscripting.h" extern gui::IGUIEnvironment* guienv; @@ -269,10 +261,36 @@ Client::Client( m_cache_use_tangent_vertices = m_cache_enable_shaders && ( g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion")); + + m_script = new ClientScripting(this); +} + +void Client::initMods() +{ + std::string script_path = getBuiltinLuaPath() + DIR_DELIM "init.lua"; + + m_script->loadMod(script_path, BUILTIN_MOD_NAME); +} + +const std::string Client::getBuiltinLuaPath() +{ + return porting::path_share + DIR_DELIM + "builtin"; +} + +const std::vector& Client::getMods() const +{ + static std::vector client_modspec_temp; + return client_modspec_temp; +} + +const ModSpec* Client::getModSpec(const std::string &modname) const +{ + return NULL; } void Client::Stop() { + m_script->on_shutdown(); //request all client managed threads to stop m_mesh_update_thread.stop(); // Save local server map @@ -280,6 +298,8 @@ void Client::Stop() infostream << "Local map saving ended." << std::endl; m_localdb->endSave(); } + + delete m_script; } bool Client::isShutdown() @@ -1553,6 +1573,11 @@ void Client::typeChatMessage(const std::wstring &message) if(message == L"") return; + // If message was ate by script API, don't send it to server + if (m_script->on_sending_message(wide_to_utf8(message))) { + return; + } + // Send to others sendChatMessage(message); diff --git a/src/client.h b/src/client.h index b33358d94..2fdade61a 100644 --- a/src/client.h +++ b/src/client.h @@ -305,6 +305,8 @@ private: std::map m_packets; }; +class ClientScripting; + class Client : public con::PeerHandler, public InventoryManager, public IGameDef { public: @@ -328,6 +330,8 @@ public: ~Client(); + void initMods(); + /* request all threads managed by client to be stopped */ @@ -428,6 +432,10 @@ public: ClientEnvironment& getEnv() { return m_env; } ITextureSource *tsrc() { return getTextureSource(); } ISoundManager *sound() { return getSoundManager(); } + static const std::string getBuiltinLuaPath(); + + virtual const std::vector &getMods() const; + virtual const ModSpec* getModSpec(const std::string &modname) const; // Causes urgent mesh updates (unlike Map::add/removeNodeWithEvent) void removeNode(v3s16 p); @@ -692,6 +700,8 @@ private: bool m_cache_enable_shaders; bool m_cache_use_tangent_vertices; + ClientScripting *m_script; + DISABLE_CLASS_COPY(Client); }; diff --git a/src/content_abm.cpp b/src/content_abm.cpp index ee444ae77..2ab3a968c 100644 --- a/src/content_abm.cpp +++ b/src/content_abm.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "mapblock.h" // For getNodeBlockPos #include "map.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "log.h" void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) { diff --git a/src/content_sao.cpp b/src/content_sao.cpp index d4a218505..93662b035 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "remoteplayer.h" #include "server.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "genericobject.h" std::map ServerActiveObject::m_types; diff --git a/src/emerge.cpp b/src/emerge.cpp index 1c9719c48..8719a9eb3 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_schematic.h" #include "nodedef.h" #include "profiler.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "server.h" #include "serverobject.h" #include "settings.h" diff --git a/src/environment.cpp b/src/environment.cpp index 8c1aad9d3..737d93ecd 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "environment.h" #include "collision.h" #include "serverobject.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "server.h" #include "daynightratio.h" #include "emerge.h" diff --git a/src/game.cpp b/src/game.cpp index 55b2ccec9..9868142f7 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2222,6 +2222,8 @@ bool Game::connectToServer(const std::string &playername, fps_control.last_time = device->getTimer()->getTime(); + client->initMods(); + while (device->run()) { limitFps(&fps_control, &dtime); diff --git a/src/gamedef.h b/src/gamedef.h index cb624bd6a..16b53e24f 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -39,6 +39,7 @@ namespace irr { namespace scene { class ISceneManager; }} +struct ModSpec; /* An interface for fetching game-global definitions like tool and mapnode properties @@ -68,7 +69,11 @@ public: ICraftDefManager *cdef() { return getCraftDefManager(); } MtEventManager *event() { return getEventManager(); } - IRollbackManager *rollback() { return getRollbackManager();} + IRollbackManager *rollback() { return getRollbackManager(); } + + virtual const std::vector &getMods() const = 0; + virtual const ModSpec* getModSpec(const std::string &modname) const = 0; + virtual std::string getWorldPath() const { return ""; } }; #endif diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index ae3fad7c6..19cac6241 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "gettime.h" #include "gettext.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "porting.h" #include "settings.h" #include "client.h" diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 469e7396b..6ebc2994b 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventorymanager.h" #include "log.h" #include "serverenvironment.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "serverobject.h" #include "settings.h" #include "craftdef.h" diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index b11f73e86..f1c44c7d8 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "util/strfnd.h" #include "network/clientopcodes.h" +#include "script/clientscripting.h" #include "util/serialize.h" #include "util/srp.h" #include "tileanimation.h" @@ -411,7 +412,10 @@ void Client::handleCommand_ChatMessage(NetworkPacket* pkt) message += (wchar_t)read_wchar; } - m_chat_queue.push(message); + // If chat message not consummed by client lua API + if (!m_script->on_receiving_message(wide_to_utf8(message))) { + m_chat_queue.push(message); + } } void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt) diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index ac428e8ed..b707c6fad 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "player.h" #include "rollback_interface.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "settings.h" #include "tool.h" #include "version.h" diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index 5ef672ca9..c96ccc816 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -3,16 +3,17 @@ add_subdirectory(cpp_api) add_subdirectory(lua_api) # Used by server and client -set(common_SCRIPT_SRCS - ${CMAKE_CURRENT_SOURCE_DIR}/scripting_game.cpp +set(common_SCRIPT_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/serverscripting.cpp ${common_SCRIPT_COMMON_SRCS} ${common_SCRIPT_CPP_API_SRCS} ${common_SCRIPT_LUA_API_SRCS} PARENT_SCOPE) # Used by client only -set(client_SCRIPT_SRCS +set(client_SCRIPT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/scripting_mainmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/clientscripting.cpp ${client_SCRIPT_COMMON_SRCS} ${client_SCRIPT_CPP_API_SRCS} ${client_SCRIPT_LUA_API_SRCS} diff --git a/src/script/clientscripting.cpp b/src/script/clientscripting.cpp new file mode 100644 index 000000000..43bc6f94e --- /dev/null +++ b/src/script/clientscripting.cpp @@ -0,0 +1,54 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "clientscripting.h" +#include "client.h" +#include "cpp_api/s_internal.h" +#include "lua_api/l_client.h" +#include "lua_api/l_util.h" + +ClientScripting::ClientScripting(Client *client): + ScriptApiBase() +{ + setGameDef(client); + + SCRIPTAPI_PRECHECKHEADER + + // Security is mandatory client side + initializeSecurity(); + + lua_getglobal(L, "core"); + int top = lua_gettop(L); + + InitializeModApi(L, top); + lua_pop(L, 1); + + // Push builtin initialization type + lua_pushstring(L, "client"); + lua_setglobal(L, "INIT"); + + infostream << "SCRIPTAPI: Initialized client game modules" << std::endl; +} + +void ClientScripting::InitializeModApi(lua_State *L, int top) +{ + ModApiUtil::Initialize(L, top); + ModApiClient::Initialize(L, top); +} diff --git a/src/script/clientscripting.h b/src/script/clientscripting.h new file mode 100644 index 000000000..e2a91f695 --- /dev/null +++ b/src/script/clientscripting.h @@ -0,0 +1,40 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef CLIENT_SCRIPTING_H_ +#define CLIENT_SCRIPTING_H_ + +#include "cpp_api/s_base.h" +#include "cpp_api/s_client.h" +#include "cpp_api/s_security.h" + +class Client; +class ClientScripting: + virtual public ScriptApiBase, + public ScriptApiSecurity, + public ScriptApiClient +{ +public: + ClientScripting(Client *client); + +private: + virtual void InitializeModApi(lua_State *L, int top); +}; +#endif diff --git a/src/script/cpp_api/CMakeLists.txt b/src/script/cpp_api/CMakeLists.txt index be4d0131e..4b13356a8 100644 --- a/src/script/cpp_api/CMakeLists.txt +++ b/src/script/cpp_api/CMakeLists.txt @@ -13,6 +13,7 @@ set(common_SCRIPT_CPP_API_SRCS PARENT_SCOPE) set(client_SCRIPT_CPP_API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/s_client.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_mainmenu.cpp PARENT_SCOPE) diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index cbe5735a7..6a843810f 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -23,12 +23,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_object.h" #include "common/c_converter.h" #include "serverobject.h" -#include "debug.h" #include "filesys.h" -#include "log.h" #include "mods.h" #include "porting.h" #include "util/string.h" +#include "server.h" +#ifndef SERVER +#include "client.h" +#endif extern "C" { @@ -69,7 +71,8 @@ public: */ ScriptApiBase::ScriptApiBase() : - m_luastackmutex() + m_luastackmutex(), + m_gamedef(NULL) { #ifdef SCRIPTAPI_LOCK_DEBUG m_lock_recursion_count = 0; @@ -113,7 +116,6 @@ ScriptApiBase::ScriptApiBase() : // Default to false otherwise m_secure = false; - m_server = NULL; m_environment = NULL; m_guiengine = NULL; } @@ -333,3 +335,14 @@ void ScriptApiBase::objectrefGet(lua_State *L, u16 id) lua_remove(L, -2); // object_refs lua_remove(L, -2); // core } + +Server* ScriptApiBase::getServer() +{ + return dynamic_cast(m_gamedef); +} +#ifndef SERVER +Client* ScriptApiBase::getClient() +{ + return dynamic_cast(m_gamedef); +} +#endif diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h index c27235255..19d71df65 100644 --- a/src/script/cpp_api/s_base.h +++ b/src/script/cpp_api/s_base.h @@ -55,6 +55,10 @@ extern "C" { setOriginFromTableRaw(index, __FUNCTION__) class Server; +#ifndef SERVER +class Client; +#endif +class IGameDef; class Environment; class GUIEngine; class ServerActiveObject; @@ -75,7 +79,11 @@ public: void addObjectReference(ServerActiveObject *cobj); void removeObjectReference(ServerActiveObject *cobj); - Server* getServer() { return m_server; } + IGameDef *getGameDef() { return m_gamedef; } + Server* getServer(); +#ifndef SERVER + Client* getClient(); +#endif std::string getOrigin() { return m_last_run_mod; } void setOriginDirect(const char *origin); @@ -98,7 +106,7 @@ protected: void scriptError(int result, const char *fxn); void stackDump(std::ostream &o); - void setServer(Server* server) { m_server = server; } + void setGameDef(IGameDef* gamedef) { m_gamedef = gamedef; } Environment* getEnv() { return m_environment; } void setEnv(Environment* env) { m_environment = env; } @@ -122,7 +130,7 @@ private: lua_State* m_luastack; - Server* m_server; + IGameDef* m_gamedef; Environment* m_environment; GUIEngine* m_guiengine; }; diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp new file mode 100644 index 000000000..08af8ebdc --- /dev/null +++ b/src/script/cpp_api/s_client.cpp @@ -0,0 +1,61 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "s_client.h" +#include "s_internal.h" + +void ScriptApiClient::on_shutdown() +{ + SCRIPTAPI_PRECHECKHEADER + + // Get registered shutdown hooks + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_shutdown"); + // Call callbacks + runCallbacks(0, RUN_CALLBACKS_MODE_FIRST); +} + +bool ScriptApiClient::on_sending_message(const std::string &message) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_chat_messages + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_sending_chat_messages"); + // Call callbacks + lua_pushstring(L, message.c_str()); + runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC); + bool ate = lua_toboolean(L, -1); + return ate; +} + +bool ScriptApiClient::on_receiving_message(const std::string &message) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_chat_messages + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_receiving_chat_messages"); + // Call callbacks + lua_pushstring(L, message.c_str()); + runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC); + bool ate = lua_toboolean(L, -1); + return ate; +} diff --git a/src/script/cpp_api/s_client.h b/src/script/cpp_api/s_client.h new file mode 100644 index 000000000..08fdd8fc0 --- /dev/null +++ b/src/script/cpp_api/s_client.h @@ -0,0 +1,36 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef S_CLIENT_H_ +#define S_CLIENT_H_ + +#include "cpp_api/s_base.h" + +class ScriptApiClient: virtual public ScriptApiBase +{ +public: + // Calls on_shutdown handlers + void on_shutdown(); + + // Chat message handlers + bool on_sending_message(const std::string &message); + bool on_receiving_message(const std::string &message); +}; +#endif diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index be2b884cc..f85cd0c9c 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -382,9 +382,9 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1); lua_pop(L, 1); - const Server *server = script->getServer(); - - if (!server) return false; + const IGameDef *gamedef = script->getGameDef(); + if (!gamedef) + return false; // Get mod name lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); @@ -400,7 +400,7 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, // Allow paths in mod path // Don't bother if write access isn't important, since it will be handled later if (write_required || write_allowed != NULL) { - const ModSpec *mod = server->getModSpec(mod_name); + const ModSpec *mod = gamedef->getModSpec(mod_name); if (mod) { str = fs::AbsolutePath(mod->path); if (!str.empty() && fs::PathStartsWith(abs_path, str)) { @@ -414,7 +414,7 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, // Allow read-only access to all mod directories if (!write_required) { - const std::vector mods = server->getMods(); + const std::vector mods = gamedef->getMods(); for (size_t i = 0; i < mods.size(); ++i) { str = fs::AbsolutePath(mods[i].path); if (!str.empty() && fs::PathStartsWith(abs_path, str)) { @@ -423,7 +423,7 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, } } - str = fs::AbsolutePath(server->getWorldPath()); + str = fs::AbsolutePath(gamedef->getWorldPath()); if (!str.empty()) { // Don't allow access to other paths in the world mod/game path. // These have to be blocked so you can't override a trusted mod diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index e82560696..ea3d75ffa 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -23,5 +23,6 @@ set(common_SCRIPT_LUA_API_SRCS PARENT_SCOPE) set(client_SCRIPT_LUA_API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/l_client.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_mainmenu.cpp PARENT_SCOPE) diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp new file mode 100644 index 000000000..9c478602a --- /dev/null +++ b/src/script/lua_api/l_client.cpp @@ -0,0 +1,33 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "l_client.h" +#include "l_internal.h" + +int ModApiClient::l_get_current_modname(lua_State *L) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + return 1; +} + +void ModApiClient::Initialize(lua_State *L, int top) +{ + API_FCT(get_current_modname); +} diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h new file mode 100644 index 000000000..332f00132 --- /dev/null +++ b/src/script/lua_api/l_client.h @@ -0,0 +1,36 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef L_CLIENT_H_ +#define L_CLIENT_H_ + +#include "lua_api/l_base.h" + +class ModApiClient : public ModApiBase +{ +private: + // get_current_modname() + static int l_get_current_modname(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); +}; + +#endif diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 2722e35a4..442c4b99a 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_vmanip.h" #include "common/c_converter.h" #include "common/c_content.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "environment.h" #include "server.h" #include "nodedef.h" @@ -49,7 +49,7 @@ struct EnumString ModApiEnvMod::es_ClearObjectsMode[] = void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, u32 active_object_count, u32 active_object_count_wider) { - GameScripting *scriptIface = env->getScriptIface(); + ServerScripting *scriptIface = env->getScriptIface(); scriptIface->realityCheck(); lua_State *L = scriptIface->getStack(); @@ -92,7 +92,7 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n) { - GameScripting *scriptIface = env->getScriptIface(); + ServerScripting *scriptIface = env->getScriptIface(); scriptIface->realityCheck(); lua_State *L = scriptIface->getStack(); diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 21b235f84..322959411 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -242,7 +242,7 @@ public: }; struct ScriptCallbackState { - GameScripting *script; + ServerScripting *script; int callback_ref; int args_ref; unsigned int refcount; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 9352812ab..be454ad45 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_sao.h" #include "server.h" #include "hud.h" -#include "scripting_game.h" +#include "serverscripting.h" struct EnumString es_HudElementType[] = { diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp deleted file mode 100644 index 4da752263..000000000 --- a/src/script/scripting_game.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "scripting_game.h" -#include "server.h" -#include "log.h" -#include "settings.h" -#include "cpp_api/s_internal.h" -#include "lua_api/l_areastore.h" -#include "lua_api/l_base.h" -#include "lua_api/l_craft.h" -#include "lua_api/l_env.h" -#include "lua_api/l_inventory.h" -#include "lua_api/l_item.h" -#include "lua_api/l_itemstackmeta.h" -#include "lua_api/l_mapgen.h" -#include "lua_api/l_nodemeta.h" -#include "lua_api/l_nodetimer.h" -#include "lua_api/l_noise.h" -#include "lua_api/l_object.h" -#include "lua_api/l_particles.h" -#include "lua_api/l_rollback.h" -#include "lua_api/l_server.h" -#include "lua_api/l_util.h" -#include "lua_api/l_vmanip.h" -#include "lua_api/l_settings.h" -#include "lua_api/l_http.h" -#include "lua_api/l_storage.h" - -extern "C" { -#include "lualib.h" -} - -GameScripting::GameScripting(Server* server) -{ - setServer(server); - - // setEnv(env) is called by ScriptApiEnv::initializeEnvironment() - // once the environment has been created - - SCRIPTAPI_PRECHECKHEADER - - if (g_settings->getBool("secure.enable_security")) { - initializeSecurity(); - } - - lua_getglobal(L, "core"); - int top = lua_gettop(L); - - lua_newtable(L); - lua_setfield(L, -2, "object_refs"); - - lua_newtable(L); - lua_setfield(L, -2, "luaentities"); - - // Initialize our lua_api modules - InitializeModApi(L, top); - lua_pop(L, 1); - - // Push builtin initialization type - lua_pushstring(L, "game"); - lua_setglobal(L, "INIT"); - - infostream << "SCRIPTAPI: Initialized game modules" << std::endl; -} - -void GameScripting::InitializeModApi(lua_State *L, int top) -{ - // Initialize mod api modules - ModApiCraft::Initialize(L, top); - ModApiEnvMod::Initialize(L, top); - ModApiInventory::Initialize(L, top); - ModApiItemMod::Initialize(L, top); - ModApiMapgen::Initialize(L, top); - ModApiParticles::Initialize(L, top); - ModApiRollback::Initialize(L, top); - ModApiServer::Initialize(L, top); - ModApiUtil::Initialize(L, top); - ModApiHttp::Initialize(L, top); - ModApiStorage::Initialize(L, top); - - // Register reference classes (userdata) - InvRef::Register(L); - ItemStackMetaRef::Register(L); - LuaAreaStore::Register(L); - LuaItemStack::Register(L); - LuaPerlinNoise::Register(L); - LuaPerlinNoiseMap::Register(L); - LuaPseudoRandom::Register(L); - LuaPcgRandom::Register(L); - LuaSecureRandom::Register(L); - LuaVoxelManip::Register(L); - NodeMetaRef::Register(L); - NodeTimerRef::Register(L); - ObjectRef::Register(L); - LuaSettings::Register(L); - StorageRef::Register(L); -} - -void log_deprecated(const std::string &message) -{ - log_deprecated(NULL, message); -} diff --git a/src/script/scripting_game.h b/src/script/scripting_game.h deleted file mode 100644 index 970b3e80d..000000000 --- a/src/script/scripting_game.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef SCRIPTING_GAME_H_ -#define SCRIPTING_GAME_H_ - -#include "cpp_api/s_base.h" -#include "cpp_api/s_entity.h" -#include "cpp_api/s_env.h" -#include "cpp_api/s_inventory.h" -#include "cpp_api/s_node.h" -#include "cpp_api/s_player.h" -#include "cpp_api/s_server.h" -#include "cpp_api/s_security.h" - -/*****************************************************************************/ -/* Scripting <-> Game Interface */ -/*****************************************************************************/ - -class GameScripting : - virtual public ScriptApiBase, - public ScriptApiDetached, - public ScriptApiEntity, - public ScriptApiEnv, - public ScriptApiNode, - public ScriptApiPlayer, - public ScriptApiServer, - public ScriptApiSecurity -{ -public: - GameScripting(Server* server); - - // use ScriptApiBase::loadMod() to load mods - -private: - void InitializeModApi(lua_State *L, int top); -}; - -void log_deprecated(const std::string &message); - -#endif /* SCRIPTING_GAME_H_ */ diff --git a/src/script/serverscripting.cpp b/src/script/serverscripting.cpp new file mode 100644 index 000000000..215b2cfd7 --- /dev/null +++ b/src/script/serverscripting.cpp @@ -0,0 +1,119 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "serverscripting.h" +#include "server.h" +#include "log.h" +#include "settings.h" +#include "cpp_api/s_internal.h" +#include "lua_api/l_areastore.h" +#include "lua_api/l_base.h" +#include "lua_api/l_craft.h" +#include "lua_api/l_env.h" +#include "lua_api/l_inventory.h" +#include "lua_api/l_item.h" +#include "lua_api/l_itemstackmeta.h" +#include "lua_api/l_mapgen.h" +#include "lua_api/l_nodemeta.h" +#include "lua_api/l_nodetimer.h" +#include "lua_api/l_noise.h" +#include "lua_api/l_object.h" +#include "lua_api/l_particles.h" +#include "lua_api/l_rollback.h" +#include "lua_api/l_server.h" +#include "lua_api/l_util.h" +#include "lua_api/l_vmanip.h" +#include "lua_api/l_settings.h" +#include "lua_api/l_http.h" +#include "lua_api/l_storage.h" + +extern "C" { +#include "lualib.h" +} + +ServerScripting::ServerScripting(Server* server) +{ + setGameDef(server); + + // setEnv(env) is called by ScriptApiEnv::initializeEnvironment() + // once the environment has been created + + SCRIPTAPI_PRECHECKHEADER + + if (g_settings->getBool("secure.enable_security")) { + initializeSecurity(); + } + + lua_getglobal(L, "core"); + int top = lua_gettop(L); + + lua_newtable(L); + lua_setfield(L, -2, "object_refs"); + + lua_newtable(L); + lua_setfield(L, -2, "luaentities"); + + // Initialize our lua_api modules + InitializeModApi(L, top); + lua_pop(L, 1); + + // Push builtin initialization type + lua_pushstring(L, "game"); + lua_setglobal(L, "INIT"); + + infostream << "SCRIPTAPI: Initialized game modules" << std::endl; +} + +void ServerScripting::InitializeModApi(lua_State *L, int top) +{ + // Initialize mod api modules + ModApiCraft::Initialize(L, top); + ModApiEnvMod::Initialize(L, top); + ModApiInventory::Initialize(L, top); + ModApiItemMod::Initialize(L, top); + ModApiMapgen::Initialize(L, top); + ModApiParticles::Initialize(L, top); + ModApiRollback::Initialize(L, top); + ModApiServer::Initialize(L, top); + ModApiUtil::Initialize(L, top); + ModApiHttp::Initialize(L, top); + ModApiStorage::Initialize(L, top); + + // Register reference classes (userdata) + InvRef::Register(L); + ItemStackMetaRef::Register(L); + LuaAreaStore::Register(L); + LuaItemStack::Register(L); + LuaPerlinNoise::Register(L); + LuaPerlinNoiseMap::Register(L); + LuaPseudoRandom::Register(L); + LuaPcgRandom::Register(L); + LuaSecureRandom::Register(L); + LuaVoxelManip::Register(L); + NodeMetaRef::Register(L); + NodeTimerRef::Register(L); + ObjectRef::Register(L); + LuaSettings::Register(L); + StorageRef::Register(L); +} + +void log_deprecated(const std::string &message) +{ + log_deprecated(NULL, message); +} diff --git a/src/script/serverscripting.h b/src/script/serverscripting.h new file mode 100644 index 000000000..fd97ea40b --- /dev/null +++ b/src/script/serverscripting.h @@ -0,0 +1,57 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef SERVER_SCRIPTING_H_ +#define SERVER_SCRIPTING_H_ + +#include "cpp_api/s_base.h" +#include "cpp_api/s_entity.h" +#include "cpp_api/s_env.h" +#include "cpp_api/s_inventory.h" +#include "cpp_api/s_node.h" +#include "cpp_api/s_player.h" +#include "cpp_api/s_server.h" +#include "cpp_api/s_security.h" + +/*****************************************************************************/ +/* Scripting <-> Server Game Interface */ +/*****************************************************************************/ + +class ServerScripting: + virtual public ScriptApiBase, + public ScriptApiDetached, + public ScriptApiEntity, + public ScriptApiEnv, + public ScriptApiNode, + public ScriptApiPlayer, + public ScriptApiServer, + public ScriptApiSecurity +{ +public: + ServerScripting(Server* server); + + // use ScriptApiBase::loadMod() to load mods + +private: + void InitializeModApi(lua_State *L, int top); +}; + +void log_deprecated(const std::string &message); + +#endif /* SCRIPTING_GAME_H_ */ diff --git a/src/server.cpp b/src/server.cpp index 8b9f46f85..3adbf40cc 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "profiler.h" #include "log.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "nodedef.h" #include "itemdef.h" #include "craftdef.h" @@ -269,7 +269,7 @@ Server::Server( // Initialize scripting infostream<<"Server: Initializing Lua"< &modlist) modlist.push_back(it->name); } -std::string Server::getBuiltinLuaPath() +const std::string Server::getBuiltinLuaPath() { return porting::path_share + DIR_DELIM + "builtin"; } diff --git a/src/server.h b/src/server.h index 3eee67b78..417d31bd8 100644 --- a/src/server.h +++ b/src/server.h @@ -53,7 +53,7 @@ class PlayerSAO; class IRollbackManager; struct RollbackAction; class EmergeManager; -class GameScripting; +class ServerScripting; class ServerEnvironment; struct SimpleSoundSpec; class ServerThread; @@ -274,7 +274,7 @@ public: Inventory* createDetachedInventory(const std::string &name, const std::string &player=""); // Envlock and conlock should be locked when using scriptapi - GameScripting *getScriptIface(){ return m_script; } + ServerScripting *getScriptIface(){ return m_script; } // actions: time-reversed list // Return value: success/failure @@ -295,8 +295,8 @@ public: IWritableNodeDefManager* getWritableNodeDefManager(); IWritableCraftDefManager* getWritableCraftDefManager(); - const std::vector &getMods() const { return m_mods; } - const ModSpec* getModSpec(const std::string &modname) const; + virtual const std::vector &getMods() const { return m_mods; } + virtual const ModSpec* getModSpec(const std::string &modname) const; void getModNames(std::vector &modlist); std::string getBuiltinLuaPath(); inline const std::string &getWorldPath() const { return m_path_world; } @@ -540,7 +540,7 @@ private: // Scripting // Envlock and conlock should be locked when using Lua - GameScripting *m_script; + ServerScripting *m_script; // Item definition manager IWritableItemDefManager *m_itemdef; diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index f3f489092..ecc7c3150 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "raycast.h" #include "remoteplayer.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "server.h" #include "voxelalgorithms.h" #include "util/serialize.h" @@ -352,7 +352,7 @@ void ActiveBlockList::update(std::vector &active_positions, */ ServerEnvironment::ServerEnvironment(ServerMap *map, - GameScripting *scriptIface, Server *server, + ServerScripting *scriptIface, Server *server, const std::string &path_world) : m_map(map), m_script(scriptIface), diff --git a/src/serverenvironment.h b/src/serverenvironment.h index b7056c00c..b7796b5f1 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -33,7 +33,7 @@ class ServerEnvironment; class ActiveBlockModifier; class ServerActiveObject; class Server; -class GameScripting; +class ServerScripting; /* {Active, Loading} block modifier interface. @@ -194,7 +194,7 @@ typedef UNORDERED_MAP ActiveObjectMap; class ServerEnvironment : public Environment { public: - ServerEnvironment(ServerMap *map, GameScripting *scriptIface, + ServerEnvironment(ServerMap *map, ServerScripting *scriptIface, Server *server, const std::string &path_world); ~ServerEnvironment(); @@ -203,7 +203,7 @@ public: ServerMap & getServerMap(); //TODO find way to remove this fct! - GameScripting* getScriptIface() + ServerScripting* getScriptIface() { return m_script; } Server *getGameDef() @@ -381,7 +381,7 @@ private: // The map ServerMap *m_map; // Lua state - GameScripting* m_script; + ServerScripting* m_script; // Server definition Server *m_server; // World path diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index 41ccf0d2d..9beb0afa6 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -19,10 +19,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "test.h" -#include "log.h" #include "nodedef.h" #include "itemdef.h" #include "gamedef.h" +#include "mods.h" content_t t_CONTENT_STONE; content_t t_CONTENT_GRASS; @@ -59,6 +59,13 @@ public: void defineSomeNodes(); + virtual const std::vector &getMods() const + { + static std::vector testmodspec; + return testmodspec; + } + virtual const ModSpec* getModSpec(const std::string &modname) const { return NULL; } + private: IItemDefManager *m_itemdef; INodeDefManager *m_nodedef; -- cgit v1.2.3 From 0891975ad6c8d6d3e15b20f33b22cf5baca7eb62 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Fri, 17 Mar 2017 07:48:29 +0100 Subject: [CSM] Add core.get_timeofday & core.get_day_count env calls (#5401) * [CSM] Add core.get_timeofday & core.get_day_count env calls * [CSM] Add core.get_node_level, core.get_node_max_level, core.find_node_near --- clientmods/preview/init.lua | 9 +++++++++ src/client.cpp | 1 + src/clientenvironment.cpp | 1 + src/environment.cpp | 5 +++-- src/environment.h | 5 ++++- src/script/clientscripting.cpp | 2 ++ src/script/cpp_api/s_client.cpp | 5 +++++ src/script/cpp_api/s_client.h | 4 ++++ src/script/lua_api/l_env.cpp | 44 +++++++++++++++++++++++++++++++---------- src/script/lua_api/l_env.h | 1 + src/script/lua_api/l_internal.h | 10 ---------- src/serverenvironment.cpp | 3 ++- src/serverenvironment.h | 2 +- 13 files changed, 67 insertions(+), 25 deletions(-) (limited to 'src/serverenvironment.cpp') diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua index bdda7fe4e..3f85d576d 100644 --- a/clientmods/preview/init.lua +++ b/clientmods/preview/init.lua @@ -64,6 +64,15 @@ core.after(2, function() preview_minimap() modstorage:set_string("current_mod", modname) print(modstorage:get_string("current_mod")) + + print("[PREVIEW] Day count: " .. core.get_day_count() .. + " time of day " .. core.get_timeofday()) + + print("[PREVIEW] Node level: " .. core.get_node_level({x=0, y=20, z=0}) .. + " max level " .. core.get_node_max_level({x=0, y=20, z=0})) + + print("[PREVIEW] Find node near: " .. dump(core.find_node_near({x=0, y=20, z=0}, 10, + {"group:tree", "default:dirt", "default:stone"}))) end) core.register_on_dignode(function(pos, node) diff --git a/src/client.cpp b/src/client.cpp index 2491db704..b355fa617 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -268,6 +268,7 @@ Client::Client( m_modding_enabled = g_settings->getBool("enable_client_modding"); m_script = new ClientScripting(this); m_env.setScript(m_script); + m_script->setEnv(&m_env); } void Client::initMods() diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp index 7a74c897c..29ecd2dfe 100644 --- a/src/clientenvironment.cpp +++ b/src/clientenvironment.cpp @@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, ITextureSource *texturesource, Client *client, IrrlichtDevice *irr): + Environment(client), m_map(map), m_local_player(NULL), m_smgr(smgr), diff --git a/src/environment.cpp b/src/environment.cpp index 737d93ecd..d1ea5f8bb 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -26,13 +26,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "daynightratio.h" #include "emerge.h" -Environment::Environment(): +Environment::Environment(IGameDef *gamedef): m_time_of_day_speed(0), m_time_of_day(9000), m_time_of_day_f(9000./24000), m_time_conversion_skew(0.0f), m_enable_day_night_ratio_override(false), - m_day_night_ratio_override(0.0f) + m_day_night_ratio_override(0.0f), + m_gamedef(gamedef) { m_cache_enable_shaders = g_settings->getBool("enable_shaders"); m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval"); diff --git a/src/environment.h b/src/environment.h index 5154bbdcb..52f369817 100644 --- a/src/environment.h +++ b/src/environment.h @@ -40,13 +40,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "threading/atomic.h" #include "network/networkprotocol.h" // for AccessDeniedCode +class IGameDef; class Map; class Environment { public: // Environment will delete the map passed to the constructor - Environment(); + Environment(IGameDef *gamedef); virtual ~Environment(); /* @@ -77,6 +78,7 @@ public: // counter used internally when triggering ABMs u32 m_added_objects; + IGameDef* getGameDef() { return m_gamedef; } protected: GenericAtomic m_time_of_day_speed; @@ -114,6 +116,7 @@ protected: float m_cache_abm_interval; float m_cache_nodetimer_interval; + IGameDef *m_gamedef; private: Mutex m_time_lock; diff --git a/src/script/clientscripting.cpp b/src/script/clientscripting.cpp index 8bf1b68b1..df30a7253 100644 --- a/src/script/clientscripting.cpp +++ b/src/script/clientscripting.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client.h" #include "cpp_api/s_internal.h" #include "lua_api/l_client.h" +#include "lua_api/l_env.h" #include "lua_api/l_minimap.h" #include "lua_api/l_storage.h" #include "lua_api/l_sound.h" @@ -62,6 +63,7 @@ void ClientScripting::InitializeModApi(lua_State *L, int top) ModApiClient::Initialize(L, top); ModApiSound::Initialize(L, top); ModApiStorage::Initialize(L, top); + ModApiEnvMod::InitializeClient(L, top); LuaItemStack::Register(L); StorageRef::Register(L); diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp index 154dd6194..666fd693d 100644 --- a/src/script/cpp_api/s_client.cpp +++ b/src/script/cpp_api/s_client.cpp @@ -177,3 +177,8 @@ bool ScriptApiClient::on_punchnode(v3s16 p, MapNode node) bool blocked = lua_toboolean(L, -1); return blocked; } + +void ScriptApiClient::setEnv(ClientEnvironment *env) +{ + ScriptApiBase::setEnv(env); +} diff --git a/src/script/cpp_api/s_client.h b/src/script/cpp_api/s_client.h index 93e9558f2..2369efe3e 100644 --- a/src/script/cpp_api/s_client.h +++ b/src/script/cpp_api/s_client.h @@ -29,6 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #endif +class ClientEnvironment; + class ScriptApiClient: virtual public ScriptApiBase { public: @@ -47,5 +49,7 @@ public: bool on_dignode(v3s16 p, MapNode node); bool on_punchnode(v3s16 p, MapNode node); + + void setEnv(ClientEnvironment *env); }; #endif diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 14df558d3..4fad7b37c 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -347,7 +347,10 @@ int ModApiEnvMod::l_punch_node(lua_State *L) // pos = {x=num, y=num, z=num} int ModApiEnvMod::l_get_node_max_level(lua_State *L) { - GET_ENV_PTR; + Environment *env = getEnv(L); + if (!env) { + return 0; + } v3s16 pos = read_v3s16(L, 1); MapNode n = env->getMap().getNodeNoEx(pos); @@ -359,7 +362,10 @@ int ModApiEnvMod::l_get_node_max_level(lua_State *L) // pos = {x=num, y=num, z=num} int ModApiEnvMod::l_get_node_level(lua_State *L) { - GET_ENV_PTR; + Environment *env = getEnv(L); + if (!env) { + return 0; + } v3s16 pos = read_v3s16(L, 1); MapNode n = env->getMap().getNodeNoEx(pos); @@ -558,11 +564,14 @@ int ModApiEnvMod::l_set_timeofday(lua_State *L) // get_timeofday() -> 0...1 int ModApiEnvMod::l_get_timeofday(lua_State *L) { - GET_ENV_PTR; + Environment *env = getEnv(L); + if (!env) { + return 0; + } // Do it int timeofday_mh = env->getTimeOfDay(); - float timeofday_f = (float)timeofday_mh / 24000.0; + float timeofday_f = (float)timeofday_mh / 24000.0f; lua_pushnumber(L, timeofday_f); return 1; } @@ -570,7 +579,10 @@ int ModApiEnvMod::l_get_timeofday(lua_State *L) // get_day_count() -> int int ModApiEnvMod::l_get_day_count(lua_State *L) { - GET_ENV_PTR; + Environment *env = getEnv(L); + if (!env) { + return 0; + } lua_pushnumber(L, env->getDayCount()); return 1; @@ -591,9 +603,12 @@ int ModApiEnvMod::l_get_gametime(lua_State *L) // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" int ModApiEnvMod::l_find_node_near(lua_State *L) { - GET_ENV_PTR; + Environment *env = getEnv(L); + if (!env) { + return 0; + } - INodeDefManager *ndef = getServer(L)->ndef(); + INodeDefManager *ndef = getGameDef(L)->ndef(); v3s16 pos = read_v3s16(L, 1); int radius = luaL_checkinteger(L, 2); std::set filter; @@ -611,13 +626,13 @@ int ModApiEnvMod::l_find_node_near(lua_State *L) ndef->getIds(lua_tostring(L, 3), filter); } - for(int d=1; d<=radius; d++){ + for (int d=1; d<=radius; d++){ std::vector list = FacePositionCache::getFacePositions(d); - for(std::vector::iterator i = list.begin(); + for (std::vector::iterator i = list.begin(); i != list.end(); ++i){ v3s16 p = pos + (*i); content_t c = env->getMap().getNodeNoEx(p).getContent(); - if(filter.count(c) != 0){ + if (filter.count(c) != 0){ push_v3s16(L, p); return 1; } @@ -1087,3 +1102,12 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) API_FCT(forceload_block); API_FCT(forceload_free_block); } + +void ModApiEnvMod::InitializeClient(lua_State *L, int top) +{ + API_FCT(get_timeofday); + API_FCT(get_day_count); + API_FCT(get_node_max_level); + API_FCT(get_node_level); + API_FCT(find_node_near); +} diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 322959411..38b2282d7 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -173,6 +173,7 @@ private: public: static void Initialize(lua_State *L, int top); + static void InitializeClient(lua_State *L, int top); static struct EnumString es_ClearObjectsMode[]; }; diff --git a/src/script/lua_api/l_internal.h b/src/script/lua_api/l_internal.h index c610dc5a3..b7627619e 100644 --- a/src/script/lua_api/l_internal.h +++ b/src/script/lua_api/l_internal.h @@ -37,16 +37,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MAP_LOCK_REQUIRED #define NO_MAP_LOCK_REQUIRED -/* -#if (defined(WIN32) || defined(_WIN32_WCE)) - #define NO_MAP_LOCK_REQUIRED -#else - #include "profiler.h" - #define NO_MAP_LOCK_REQUIRED \ - ScopeProfiler nolocktime(g_profiler,"Scriptapi: unlockable time",SPT_ADD) -#endif -*/ - #define GET_ENV_PTR_NO_MAP_LOCK \ ServerEnvironment *env = (ServerEnvironment *)getEnv(L); \ if (env == NULL) \ diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index ecc7c3150..e09c7da16 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -353,7 +353,8 @@ void ActiveBlockList::update(std::vector &active_positions, ServerEnvironment::ServerEnvironment(ServerMap *map, ServerScripting *scriptIface, Server *server, - const std::string &path_world) : + const std::string &path_world): + Environment(server), m_map(map), m_script(scriptIface), m_server(server), diff --git a/src/serverenvironment.h b/src/serverenvironment.h index b7796b5f1..99110542a 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -320,7 +320,7 @@ public: //check if there's a line of sight between two positions bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0, v3s16 *p=NULL); - u32 getGameTime() { return m_game_time; } + u32 getGameTime() const { return m_game_time; } void reportMaxLagEstimate(float f) { m_max_lag_estimate = f; } float getMaxLagEstimate() { return m_max_lag_estimate; } -- cgit v1.2.3 From 29ab20c27229672c24a7699afbcd54caad903331 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Sun, 23 Apr 2017 14:35:08 +0200 Subject: Player data to Database (#5475) * 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 --- build/android/jni/Android.mk | 1 + builtin/game/chatcommands.lua | 25 +++ doc/lua_api.txt | 2 + src/CMakeLists.txt | 1 + src/client.cpp | 2 +- src/client.h | 4 +- src/content_sao.cpp | 7 +- src/content_sao.h | 4 +- src/database-dummy.h | 9 +- src/database-files.cpp | 179 +++++++++++++++ src/database-files.h | 46 ++++ src/database-leveldb.h | 4 +- src/database-postgresql.cpp | 470 ++++++++++++++++++++++++++++++++++------ src/database-postgresql.h | 115 +++++++--- src/database-redis.h | 2 +- src/database-sqlite3.cpp | 399 +++++++++++++++++++++++++++++++--- src/database-sqlite3.h | 158 ++++++++++++-- src/database.cpp | 4 +- src/database.h | 24 +- src/main.cpp | 21 +- src/map.cpp | 13 +- src/map.h | 8 +- src/remoteplayer.cpp | 48 ---- src/remoteplayer.h | 2 +- src/script/lua_api/l_server.cpp | 17 ++ src/script/lua_api/l_server.h | 3 + src/server.cpp | 50 +---- src/server.h | 3 +- src/serverenvironment.cpp | 234 ++++++++++++++++---- src/serverenvironment.h | 13 +- src/unittest/test_player.cpp | 49 ----- 31 files changed, 1547 insertions(+), 370 deletions(-) create mode 100644 src/database-files.cpp create mode 100644 src/database-files.h (limited to 'src/serverenvironment.cpp') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 2929eaba1..b652c6b5e 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -134,6 +134,7 @@ LOCAL_SRC_FILES := \ jni/src/convert_json.cpp \ jni/src/craftdef.cpp \ jni/src/database-dummy.cpp \ + jni/src/database-files.cpp \ jni/src/database-sqlite3.cpp \ jni/src/database.cpp \ jni/src/debug.cpp \ diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 84f2c3fed..cbf75c1bc 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -279,6 +279,31 @@ core.register_chatcommand("auth_reload", { end, }) +core.register_chatcommand("remove_player", { + params = "", + description = "Remove player data", + privs = {server=true}, + func = function(name, param) + local toname = param + if toname == "" then + return false, "Name field required" + end + + local rc = core.remove_player(toname) + + if rc == 0 then + core.log("action", name .. " removed player data of " .. toname .. ".") + return true, "Player \"" .. toname .. "\" removed." + elseif rc == 1 then + return true, "No such player \"" .. toname .. "\" to remove." + elseif rc == 2 then + return true, "Player \"" .. toname .. "\" is connected, cannot remove." + end + + return false, "Unhandled remove_player return code " .. rc .. "" + end, +}) + core.register_chatcommand("teleport", { params = ",, | | ,, | ", description = "Teleport to player or position", diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 33254fb2a..c3d8d2bf6 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2599,6 +2599,8 @@ These functions return the leftover itemstack. * `minetest.cancel_shutdown_requests()`: cancel current delayed shutdown * `minetest.get_server_status()`: returns server status string * `minetest.get_server_uptime()`: returns the server uptime in seconds +* `minetest.remove_player(name)`: remove player from database (if he is not connected). + * Returns a code (0: successful, 1: no such player, 2: player is connected) ### Bans * `minetest.get_ban_list()`: returns the ban list (same as `minetest.get_ban_description("")`) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37f72a44d..7f779db10 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -377,6 +377,7 @@ set(common_SRCS convert_json.cpp craftdef.cpp database-dummy.cpp + database-files.cpp database-leveldb.cpp database-postgresql.cpp database-redis.cpp diff --git a/src/client.cpp b/src/client.cpp index ce42d025e..94c808a57 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -770,7 +770,7 @@ void Client::initLocalMapSaving(const Address &address, fs::CreateAllDirs(world_path); - m_localdb = new Database_SQLite3(world_path); + m_localdb = new MapDatabaseSQLite3(world_path); m_localdb->beginSave(); actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl; } diff --git a/src/client.h b/src/client.h index 5dc3f9bc8..328a24f90 100644 --- a/src/client.h +++ b/src/client.h @@ -49,7 +49,7 @@ class ClientMediaDownloader; struct MapDrawControl; class MtEventManager; struct PointedThing; -class Database; +class MapDatabase; class Minimap; struct MinimapMapblock; class Camera; @@ -645,7 +645,7 @@ private: LocalClientState m_state; // Used for saving server map to disk client-side - Database *m_localdb; + MapDatabase *m_localdb; IntervalLimiter m_localdb_save_interval; u16 m_cache_save_interval; diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 355453fc9..caf6dcbab 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -764,9 +764,10 @@ bool LuaEntitySAO::collideWithObjects() const // No prototype, PlayerSAO does not need to be deserialized -PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer): +PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_, + bool is_singleplayer): UnitSAO(env_, v3f(0,0,0)), - m_player(NULL), + m_player(player_), m_peer_id(peer_id_), m_inventory(NULL), m_damage(0), @@ -819,7 +820,7 @@ PlayerSAO::~PlayerSAO() delete m_inventory; } -void PlayerSAO::initialize(RemotePlayer *player, const std::set &privs) +void PlayerSAO::finalize(RemotePlayer *player, const std::set &privs) { assert(player); m_player = player; diff --git a/src/content_sao.h b/src/content_sao.h index e53e8ecce..e08795579 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -194,7 +194,7 @@ class RemotePlayer; class PlayerSAO : public UnitSAO { public: - PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer); + PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_, bool is_singleplayer); ~PlayerSAO(); ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_PLAYER; } @@ -349,7 +349,7 @@ public: bool getCollisionBox(aabb3f *toset) const; bool collideWithObjects() const { return true; } - void initialize(RemotePlayer *player, const std::set &privs); + void finalize(RemotePlayer *player, const std::set &privs); v3f getEyePosition() const { return m_base_position + getEyeOffset(); } v3f getEyeOffset() const; diff --git a/src/database-dummy.h b/src/database-dummy.h index 9083850cb..7d1cb2279 100644 --- a/src/database-dummy.h +++ b/src/database-dummy.h @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "database.h" #include "irrlichttypes.h" -class Database_Dummy : public Database +class Database_Dummy : public MapDatabase, public PlayerDatabase { public: bool saveBlock(const v3s16 &pos, const std::string &data); @@ -33,6 +33,13 @@ public: bool deleteBlock(const v3s16 &pos); void listAllLoadableBlocks(std::vector &dst); + void savePlayer(RemotePlayer *player) {} + bool loadPlayer(RemotePlayer *player, PlayerSAO *sao) { return true; } + bool removePlayer(const std::string &name) { return true; } + void listPlayers(std::vector &) {} + + void beginSave() {} + void endSave() {} private: std::map m_database; }; diff --git a/src/database-files.cpp b/src/database-files.cpp new file mode 100644 index 000000000..08a1f2d03 --- /dev/null +++ b/src/database-files.cpp @@ -0,0 +1,179 @@ +/* +Minetest +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include "database-files.h" +#include "content_sao.h" +#include "remoteplayer.h" +#include "settings.h" +#include "porting.h" +#include "filesys.h" + +// !!! WARNING !!! +// This backend is intended to be used on Minetest 0.4.16 only for the transition backend for +// player files + +void PlayerDatabaseFiles::serialize(std::ostringstream &os, RemotePlayer *player) +{ + // Utilize a Settings object for storing values + Settings args; + args.setS32("version", 1); + args.set("name", player->getName()); + + sanity_check(player->getPlayerSAO()); + args.setS32("hp", player->getPlayerSAO()->getHP()); + args.setV3F("position", player->getPlayerSAO()->getBasePosition()); + args.setFloat("pitch", player->getPlayerSAO()->getPitch()); + args.setFloat("yaw", player->getPlayerSAO()->getYaw()); + args.setS32("breath", player->getPlayerSAO()->getBreath()); + + std::string extended_attrs = ""; + player->serializeExtraAttributes(extended_attrs); + args.set("extended_attributes", extended_attrs); + + args.writeLines(os); + + os << "PlayerArgsEnd\n"; + + player->inventory.serialize(os); +} + +void PlayerDatabaseFiles::savePlayer(RemotePlayer *player) +{ + std::string savedir = m_savedir + DIR_DELIM; + std::string path = savedir + player->getName(); + bool path_found = false; + RemotePlayer testplayer("", NULL); + + for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES && !path_found; i++) { + if (!fs::PathExists(path)) { + path_found = true; + continue; + } + + // Open and deserialize file to check player name + std::ifstream is(path.c_str(), std::ios_base::binary); + if (!is.good()) { + errorstream << "Failed to open " << path << std::endl; + return; + } + + testplayer.deSerialize(is, path, NULL); + is.close(); + if (strcmp(testplayer.getName(), player->getName()) == 0) { + path_found = true; + continue; + } + + path = savedir + player->getName() + itos(i); + } + + if (!path_found) { + errorstream << "Didn't find free file for player " << player->getName() + << std::endl; + return; + } + + // Open and serialize file + std::ostringstream ss(std::ios_base::binary); + serialize(ss, player); + if (!fs::safeWriteToFile(path, ss.str())) { + infostream << "Failed to write " << path << std::endl; + } + player->setModified(false); +} + +bool PlayerDatabaseFiles::removePlayer(const std::string &name) +{ + std::string players_path = m_savedir + DIR_DELIM; + std::string path = players_path + name; + + RemotePlayer temp_player("", NULL); + for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) { + // Open file and deserialize + std::ifstream is(path.c_str(), std::ios_base::binary); + if (!is.good()) + continue; + + temp_player.deSerialize(is, path, NULL); + is.close(); + + if (temp_player.getName() == name) { + fs::DeleteSingleFileOrEmptyDirectory(path); + return true; + } + + path = players_path + name + itos(i); + } + + return false; +} + +bool PlayerDatabaseFiles::loadPlayer(RemotePlayer *player, PlayerSAO *sao) +{ + std::string players_path = m_savedir + DIR_DELIM; + std::string path = players_path + player->getName(); + + const std::string player_to_load = player->getName(); + for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) { + // Open file and deserialize + std::ifstream is(path.c_str(), std::ios_base::binary); + if (!is.good()) + continue; + + player->deSerialize(is, path, sao); + is.close(); + + if (player->getName() == player_to_load) + return true; + + path = players_path + player_to_load + itos(i); + } + + infostream << "Player file for player " << player_to_load << " not found" << std::endl; + return false; +} + +void PlayerDatabaseFiles::listPlayers(std::vector &res) +{ + std::vector files = fs::GetDirListing(m_savedir); + // list files into players directory + for (std::vector::const_iterator it = files.begin(); it != + files.end(); ++it) { + // Ignore directories + if (it->dir) + continue; + + const std::string &filename = it->name; + std::string full_path = m_savedir + DIR_DELIM + filename; + std::ifstream is(full_path.c_str(), std::ios_base::binary); + if (!is.good()) + continue; + + RemotePlayer player(filename.c_str(), NULL); + // Null env & dummy peer_id + PlayerSAO playerSAO(NULL, &player, 15789, false); + + player.deSerialize(is, "", &playerSAO); + is.close(); + + res.push_back(player.getName()); + } +} diff --git a/src/database-files.h b/src/database-files.h new file mode 100644 index 000000000..d23069c2a --- /dev/null +++ b/src/database-files.h @@ -0,0 +1,46 @@ +/* +Minetest +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef DATABASE_FILES_HEADER +#define DATABASE_FILES_HEADER + +// !!! WARNING !!! +// This backend is intended to be used on Minetest 0.4.16 only for the transition backend for +// player files + +#include "database.h" + +class PlayerDatabaseFiles : public PlayerDatabase +{ +public: + PlayerDatabaseFiles(const std::string &savedir) : m_savedir(savedir) {} + virtual ~PlayerDatabaseFiles() {} + + void savePlayer(RemotePlayer *player); + bool loadPlayer(RemotePlayer *player, PlayerSAO *sao); + bool removePlayer(const std::string &name); + void listPlayers(std::vector &res); + +private: + void serialize(std::ostringstream &os, RemotePlayer *player); + + std::string m_savedir; +}; + +#endif diff --git a/src/database-leveldb.h b/src/database-leveldb.h index 171946741..52ccebe70 100644 --- a/src/database-leveldb.h +++ b/src/database-leveldb.h @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "database.h" #include "leveldb/db.h" -class Database_LevelDB : public Database +class Database_LevelDB : public MapDatabase { public: Database_LevelDB(const std::string &savedir); @@ -39,6 +39,8 @@ public: bool deleteBlock(const v3s16 &pos); void listAllLoadableBlocks(std::vector &dst); + void beginSave() {} + void endSave() {} private: leveldb::DB *m_database; }; diff --git a/src/database-postgresql.cpp b/src/database-postgresql.cpp index 83678fd52..a6b62bad5 100644 --- a/src/database-postgresql.cpp +++ b/src/database-postgresql.cpp @@ -39,13 +39,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "exceptions.h" #include "settings.h" +#include "content_sao.h" +#include "remoteplayer.h" -Database_PostgreSQL::Database_PostgreSQL(const Settings &conf) : - m_connect_string(""), +Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) : + m_connect_string(connect_string), m_conn(NULL), m_pgversion(0) { - if (!conf.getNoEx("pgsql_connection", m_connect_string)) { + if (m_connect_string.empty()) { throw SettingNotFoundException( "Set pgsql_connection string in world.mt to " "use the postgresql backend\n" @@ -57,8 +59,6 @@ Database_PostgreSQL::Database_PostgreSQL(const Settings &conf) : "DELETE rights on the database.\n" "Don't create mt_user as a SUPERUSER!"); } - - connectToDatabase(); } Database_PostgreSQL::~Database_PostgreSQL() @@ -118,40 +118,6 @@ bool Database_PostgreSQL::initialized() const return (PQstatus(m_conn) == CONNECTION_OK); } -void Database_PostgreSQL::initStatements() -{ - prepareStatement("read_block", - "SELECT data FROM blocks " - "WHERE posX = $1::int4 AND posY = $2::int4 AND " - "posZ = $3::int4"); - - if (m_pgversion < 90500) { - prepareStatement("write_block_insert", - "INSERT INTO blocks (posX, posY, posZ, data) SELECT " - "$1::int4, $2::int4, $3::int4, $4::bytea " - "WHERE NOT EXISTS (SELECT true FROM blocks " - "WHERE posX = $1::int4 AND posY = $2::int4 AND " - "posZ = $3::int4)"); - - prepareStatement("write_block_update", - "UPDATE blocks SET data = $4::bytea " - "WHERE posX = $1::int4 AND posY = $2::int4 AND " - "posZ = $3::int4"); - } else { - prepareStatement("write_block", - "INSERT INTO blocks (posX, posY, posZ, data) VALUES " - "($1::int4, $2::int4, $3::int4, $4::bytea) " - "ON CONFLICT ON CONSTRAINT blocks_pkey DO " - "UPDATE SET data = $4::bytea"); - } - - prepareStatement("delete_block", "DELETE FROM blocks WHERE " - "posX = $1::int4 AND posY = $2::int4 AND posZ = $3::int4"); - - prepareStatement("list_all_loadable_blocks", - "SELECT posX, posY, posZ FROM blocks"); -} - PGresult *Database_PostgreSQL::checkResults(PGresult *result, bool clear) { ExecStatusType statusType = PQresultStatus(result); @@ -173,30 +139,21 @@ PGresult *Database_PostgreSQL::checkResults(PGresult *result, bool clear) return result; } -void Database_PostgreSQL::createDatabase() +void Database_PostgreSQL::createTableIfNotExists(const std::string &table_name, + const std::string &definition) { - PGresult *result = checkResults(PQexec(m_conn, - "SELECT relname FROM pg_class WHERE relname='blocks';"), - false); + std::string sql_check_table = "SELECT relname FROM pg_class WHERE relname='" + + table_name + "';"; + PGresult *result = checkResults(PQexec(m_conn, sql_check_table.c_str()), false); // If table doesn't exist, create it if (!PQntuples(result)) { - static const char* dbcreate_sql = "CREATE TABLE blocks (" - "posX INT NOT NULL," - "posY INT NOT NULL," - "posZ INT NOT NULL," - "data BYTEA," - "PRIMARY KEY (posX,posY,posZ)" - ");"; - checkResults(PQexec(m_conn, dbcreate_sql)); + checkResults(PQexec(m_conn, definition.c_str())); } PQclear(result); - - infostream << "PostgreSQL: Game Database was inited." << std::endl; } - void Database_PostgreSQL::beginSave() { verifyDatabase(); @@ -208,14 +165,70 @@ void Database_PostgreSQL::endSave() checkResults(PQexec(m_conn, "COMMIT;")); } -bool Database_PostgreSQL::saveBlock(const v3s16 &pos, - const std::string &data) +MapDatabasePostgreSQL::MapDatabasePostgreSQL(const std::string &connect_string): + Database_PostgreSQL(connect_string), + MapDatabase() +{ + connectToDatabase(); +} + + +void MapDatabasePostgreSQL::createDatabase() +{ + createTableIfNotExists("blocks", + "CREATE TABLE blocks (" + "posX INT NOT NULL," + "posY INT NOT NULL," + "posZ INT NOT NULL," + "data BYTEA," + "PRIMARY KEY (posX,posY,posZ)" + ");" + ); + + infostream << "PostgreSQL: Map Database was initialized." << std::endl; +} + +void MapDatabasePostgreSQL::initStatements() +{ + prepareStatement("read_block", + "SELECT data FROM blocks " + "WHERE posX = $1::int4 AND posY = $2::int4 AND " + "posZ = $3::int4"); + + if (getPGVersion() < 90500) { + prepareStatement("write_block_insert", + "INSERT INTO blocks (posX, posY, posZ, data) SELECT " + "$1::int4, $2::int4, $3::int4, $4::bytea " + "WHERE NOT EXISTS (SELECT true FROM blocks " + "WHERE posX = $1::int4 AND posY = $2::int4 AND " + "posZ = $3::int4)"); + + prepareStatement("write_block_update", + "UPDATE blocks SET data = $4::bytea " + "WHERE posX = $1::int4 AND posY = $2::int4 AND " + "posZ = $3::int4"); + } else { + prepareStatement("write_block", + "INSERT INTO blocks (posX, posY, posZ, data) VALUES " + "($1::int4, $2::int4, $3::int4, $4::bytea) " + "ON CONFLICT ON CONSTRAINT blocks_pkey DO " + "UPDATE SET data = $4::bytea"); + } + + prepareStatement("delete_block", "DELETE FROM blocks WHERE " + "posX = $1::int4 AND posY = $2::int4 AND posZ = $3::int4"); + + prepareStatement("list_all_loadable_blocks", + "SELECT posX, posY, posZ FROM blocks"); +} + +bool MapDatabasePostgreSQL::saveBlock(const v3s16 &pos, const std::string &data) { // Verify if we don't overflow the platform integer with the mapblock size if (data.size() > INT_MAX) { errorstream << "Database_PostgreSQL::saveBlock: Data truncation! " - << "data.size() over 0xFFFF (== " << data.size() - << ")" << std::endl; + << "data.size() over 0xFFFFFFFF (== " << data.size() + << ")" << std::endl; return false; } @@ -232,7 +245,7 @@ bool Database_PostgreSQL::saveBlock(const v3s16 &pos, }; const int argFmt[] = { 1, 1, 1, 1 }; - if (m_pgversion < 90500) { + if (getPGVersion() < 90500) { execPrepared("write_block_update", ARRLEN(args), args, argLen, argFmt); execPrepared("write_block_insert", ARRLEN(args), args, argLen, argFmt); } else { @@ -241,8 +254,7 @@ bool Database_PostgreSQL::saveBlock(const v3s16 &pos, return true; } -void Database_PostgreSQL::loadBlock(const v3s16 &pos, - std::string *block) +void MapDatabasePostgreSQL::loadBlock(const v3s16 &pos, std::string *block) { verifyDatabase(); @@ -256,19 +268,17 @@ void Database_PostgreSQL::loadBlock(const v3s16 &pos, const int argFmt[] = { 1, 1, 1 }; PGresult *results = execPrepared("read_block", ARRLEN(args), args, - argLen, argFmt, false); + argLen, argFmt, false); *block = ""; - if (PQntuples(results)) { - *block = std::string(PQgetvalue(results, 0, 0), - PQgetlength(results, 0, 0)); - } + if (PQntuples(results)) + *block = std::string(PQgetvalue(results, 0, 0), PQgetlength(results, 0, 0)); PQclear(results); } -bool Database_PostgreSQL::deleteBlock(const v3s16 &pos) +bool MapDatabasePostgreSQL::deleteBlock(const v3s16 &pos) { verifyDatabase(); @@ -286,18 +296,338 @@ bool Database_PostgreSQL::deleteBlock(const v3s16 &pos) return true; } -void Database_PostgreSQL::listAllLoadableBlocks(std::vector &dst) +void MapDatabasePostgreSQL::listAllLoadableBlocks(std::vector &dst) { verifyDatabase(); PGresult *results = execPrepared("list_all_loadable_blocks", 0, - NULL, NULL, NULL, false, false); + NULL, NULL, NULL, false, false); int numrows = PQntuples(results); - for (int row = 0; row < numrows; ++row) { + for (int row = 0; row < numrows; ++row) dst.push_back(pg_to_v3s16(results, 0, 0)); + + PQclear(results); +} + +/* + * Player Database + */ +PlayerDatabasePostgreSQL::PlayerDatabasePostgreSQL(const std::string &connect_string): + Database_PostgreSQL(connect_string), + PlayerDatabase() +{ + connectToDatabase(); +} + + +void PlayerDatabasePostgreSQL::createDatabase() +{ + createTableIfNotExists("player", + "CREATE TABLE player (" + "name VARCHAR(60) NOT NULL," + "pitch NUMERIC(15, 7) NOT NULL," + "yaw NUMERIC(15, 7) NOT NULL," + "posX NUMERIC(15, 7) NOT NULL," + "posY NUMERIC(15, 7) NOT NULL," + "posZ NUMERIC(15, 7) NOT NULL," + "hp INT NOT NULL," + "breath INT NOT NULL," + "creation_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW()," + "modification_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW()," + "PRIMARY KEY (name)" + ");" + ); + + createTableIfNotExists("player_inventories", + "CREATE TABLE player_inventories (" + "player VARCHAR(60) 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)," + "CONSTRAINT player_inventories_fkey FOREIGN KEY (player) REFERENCES " + "player (name) ON DELETE CASCADE" + ");" + ); + + createTableIfNotExists("player_inventory_items", + "CREATE TABLE player_inventory_items (" + "player VARCHAR(60) NOT NULL," + "inv_id INT NOT NULL," + "slot_id INT NOT NULL," + "item TEXT NOT NULL DEFAULT ''," + "PRIMARY KEY(player, inv_id, slot_id)," + "CONSTRAINT player_inventory_items_fkey FOREIGN KEY (player) REFERENCES " + "player (name) ON DELETE CASCADE" + ");" + ); + + createTableIfNotExists("player_metadata", + "CREATE TABLE player_metadata (" + "player VARCHAR(60) NOT NULL," + "attr VARCHAR(256) NOT NULL," + "value TEXT," + "PRIMARY KEY(player, attr)," + "CONSTRAINT player_metadata_fkey FOREIGN KEY (player) REFERENCES " + "player (name) ON DELETE CASCADE" + ");" + ); + + infostream << "PostgreSQL: Player Database was inited." << std::endl; +} + +void PlayerDatabasePostgreSQL::initStatements() +{ + if (getPGVersion() < 90500) { + prepareStatement("create_player", + "INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES " + "($1, $2, $3, $4, $5, $6, $7::int, $8::int)"); + + prepareStatement("update_player", + "UPDATE SET pitch = $2, yaw = $3, posX = $4, posY = $5, posZ = $6, hp = $7::int, " + "breath = $8::int, modification_date = NOW() WHERE name = $1"); + } else { + prepareStatement("save_player", + "INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES " + "($1, $2, $3, $4, $5, $6, $7::int, $8::int)" + "ON CONFLICT ON CONSTRAINT player_pkey DO UPDATE SET pitch = $2, yaw = $3, " + "posX = $4, posY = $5, posZ = $6, hp = $7::int, breath = $8::int, " + "modification_date = NOW()"); + } + + prepareStatement("remove_player", "DELETE FROM player WHERE name = $1"); + + prepareStatement("load_player_list", "SELECT name FROM player"); + + prepareStatement("remove_player_inventories", + "DELETE FROM player_inventories WHERE player = $1"); + + prepareStatement("remove_player_inventory_items", + "DELETE FROM player_inventory_items WHERE player = $1"); + + prepareStatement("add_player_inventory", + "INSERT INTO player_inventories (player, inv_id, inv_width, inv_name, inv_size) VALUES " + "($1, $2::int, $3::int, $4, $5::int)"); + + prepareStatement("add_player_inventory_item", + "INSERT INTO player_inventory_items (player, inv_id, slot_id, item) VALUES " + "($1, $2::int, $3::int, $4)"); + + prepareStatement("load_player_inventories", + "SELECT inv_id, inv_width, inv_name, inv_size FROM player_inventories " + "WHERE player = $1 ORDER BY inv_id"); + + prepareStatement("load_player_inventory_items", + "SELECT slot_id, item FROM player_inventory_items WHERE " + "player = $1 AND inv_id = $2::int"); + + prepareStatement("load_player", + "SELECT pitch, yaw, posX, posY, posZ, hp, breath FROM player WHERE name = $1"); + + prepareStatement("remove_player_metadata", + "DELETE FROM player_metadata WHERE player = $1"); + + prepareStatement("save_player_metadata", + "INSERT INTO player_metadata (player, attr, value) VALUES ($1, $2, $3)"); + + prepareStatement("load_player_metadata", + "SELECT attr, value FROM player_metadata WHERE player = $1"); + +} + +bool PlayerDatabasePostgreSQL::playerDataExists(const std::string &playername) +{ + verifyDatabase(); + + const char *values[] = { playername.c_str() }; + PGresult *results = execPrepared("load_player", 1, values, false); + + bool res = (PQntuples(results) > 0); + PQclear(results); + return res; +} + +void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player) +{ + PlayerSAO* sao = player->getPlayerSAO(); + if (!sao) + return; + + verifyDatabase(); + + v3f pos = sao->getBasePosition(); + std::string pitch = ftos(sao->getPitch()); + std::string yaw = ftos(sao->getYaw()); + std::string posx = ftos(pos.X); + std::string posy = ftos(pos.Y); + std::string posz = ftos(pos.Z); + std::string hp = itos(sao->getHP()); + std::string breath = itos(sao->getBreath()); + const char *values[] = { + player->getName(), + pitch.c_str(), + yaw.c_str(), + posx.c_str(), posy.c_str(), posz.c_str(), + hp.c_str(), + breath.c_str() + }; + + const char* rmvalues[] = { player->getName() }; + beginSave(); + + if (getPGVersion() < 90500) { + if (!playerDataExists(player->getName())) + execPrepared("create_player", 8, values, true, false); + else + execPrepared("update_player", 8, values, true, false); + } + else + execPrepared("save_player", 8, values, true, false); + + // Write player inventories + execPrepared("remove_player_inventories", 1, rmvalues); + execPrepared("remove_player_inventory_items", 1, rmvalues); + + std::vector inventory_lists = sao->getInventory()->getLists(); + for (u16 i = 0; i < inventory_lists.size(); i++) { + const InventoryList* list = inventory_lists[i]; + std::string name = list->getName(), width = itos(list->getWidth()), + inv_id = itos(i), lsize = itos(list->getSize()); + + const char* inv_values[] = { + player->getName(), + inv_id.c_str(), + width.c_str(), + name.c_str(), + lsize.c_str() + }; + execPrepared("add_player_inventory", 5, inv_values); + + for (u32 j = 0; j < list->getSize(); j++) { + std::ostringstream os; + list->getItem(j).serialize(os); + std::string itemStr = os.str(), slotId = itos(j); + + const char* invitem_values[] = { + player->getName(), + inv_id.c_str(), + slotId.c_str(), + itemStr.c_str() + }; + execPrepared("add_player_inventory_item", 4, invitem_values); + } + } + + execPrepared("remove_player_metadata", 1, rmvalues); + const PlayerAttributes &attrs = sao->getExtendedAttributes(); + for (PlayerAttributes::const_iterator it = attrs.begin(); it != attrs.end(); ++it) { + const char *meta_values[] = { + player->getName(), + it->first.c_str(), + it->second.c_str() + }; + execPrepared("save_player_metadata", 3, meta_values); } + endSave(); +} + +bool PlayerDatabasePostgreSQL::loadPlayer(RemotePlayer *player, PlayerSAO *sao) +{ + sanity_check(sao); + verifyDatabase(); + + const char *values[] = { player->getName() }; + PGresult *results = execPrepared("load_player", 1, values, false, false); + + // Player not found, return not found + if (!PQntuples(results)) { + PQclear(results); + return false; + } + + sao->setPitch(pg_to_float(results, 0, 0)); + sao->setYaw(pg_to_float(results, 0, 1)); + sao->setBasePosition(v3f( + pg_to_float(results, 0, 2), + pg_to_float(results, 0, 3), + pg_to_float(results, 0, 4)) + ); + sao->setHPRaw((s16) pg_to_int(results, 0, 5)); + sao->setBreath((u16) pg_to_int(results, 0, 6), false); + + PQclear(results); + + // Load inventory + results = execPrepared("load_player_inventories", 1, values, false, false); + + int resultCount = PQntuples(results); + + for (int row = 0; row < resultCount; ++row) { + InventoryList* invList = player->inventory. + addList(PQgetvalue(results, row, 2), pg_to_uint(results, row, 3)); + invList->setWidth(pg_to_uint(results, row, 1)); + + u32 invId = pg_to_uint(results, row, 0); + std::string invIdStr = itos(invId); + + const char* values2[] = { + player->getName(), + invIdStr.c_str() + }; + PGresult *results2 = execPrepared("load_player_inventory_items", 2, + values2, false, false); + + int resultCount2 = PQntuples(results2); + for (int row2 = 0; row2 < resultCount2; row2++) { + const std::string itemStr = PQgetvalue(results2, row2, 1); + if (itemStr.length() > 0) { + ItemStack stack; + stack.deSerialize(itemStr); + invList->addItem(pg_to_uint(results2, row2, 0), stack); + } + } + PQclear(results2); + } + + PQclear(results); + + results = execPrepared("load_player_metadata", 1, values, false); + + int numrows = PQntuples(results); + for (int row = 0; row < numrows; row++) { + sao->setExtendedAttribute(PQgetvalue(results, row, 0),PQgetvalue(results, row, 1)); + } + + PQclear(results); + + return true; +} + +bool PlayerDatabasePostgreSQL::removePlayer(const std::string &name) +{ + if (!playerDataExists(name)) + return false; + + verifyDatabase(); + + const char *values[] = { name.c_str() }; + execPrepared("remove_player", 1, values); + + return true; +} + +void PlayerDatabasePostgreSQL::listPlayers(std::vector &res) +{ + verifyDatabase(); + + PGresult *results = execPrepared("load_player_list", 0, NULL, false); + + int numrows = PQntuples(results); + for (int row = 0; row < numrows; row++) + res.push_back(PQgetvalue(results, row, 0)); PQclear(results); } diff --git a/src/database-postgresql.h b/src/database-postgresql.h index 1cfa544e3..d6f208fd9 100644 --- a/src/database-postgresql.h +++ b/src/database-postgresql.h @@ -27,53 +27,33 @@ with this program; if not, write to the Free Software Foundation, Inc., class Settings; -class Database_PostgreSQL : public Database +class Database_PostgreSQL: public Database { public: - Database_PostgreSQL(const Settings &conf); + Database_PostgreSQL(const std::string &connect_string); ~Database_PostgreSQL(); void beginSave(); void endSave(); - bool saveBlock(const v3s16 &pos, const std::string &data); - void loadBlock(const v3s16 &pos, std::string *block); - bool deleteBlock(const v3s16 &pos); - void listAllLoadableBlocks(std::vector &dst); bool initialized() const; -private: - // Database initialization - void connectToDatabase(); - void initStatements(); - void createDatabase(); - inline void prepareStatement(const std::string &name, const std::string &sql) +protected: + // Conversion helpers + inline int pg_to_int(PGresult *res, int row, int col) { - checkResults(PQprepare(m_conn, name.c_str(), sql.c_str(), 0, NULL)); + return atoi(PQgetvalue(res, row, col)); } - // Database connectivity checks - void ping(); - void verifyDatabase(); - - // Database usage - PGresult *checkResults(PGresult *res, bool clear = true); - - inline PGresult *execPrepared(const char *stmtName, const int paramsNumber, - const void **params, - const int *paramsLengths = NULL, const int *paramsFormats = NULL, - bool clear = true, bool nobinary = true) + inline u32 pg_to_uint(PGresult *res, int row, int col) { - return checkResults(PQexecPrepared(m_conn, stmtName, paramsNumber, - (const char* const*) params, paramsLengths, paramsFormats, - nobinary ? 1 : 0), clear); + return (u32) atoi(PQgetvalue(res, row, col)); } - // Conversion helpers - inline int pg_to_int(PGresult *res, int row, int col) + inline float pg_to_float(PGresult *res, int row, int col) { - return atoi(PQgetvalue(res, row, col)); + return (float) atof(PQgetvalue(res, row, col)); } inline v3s16 pg_to_v3s16(PGresult *res, int row, int col) @@ -85,11 +65,86 @@ private: ); } + inline PGresult *execPrepared(const char *stmtName, const int paramsNumber, + const void **params, + const int *paramsLengths = NULL, const int *paramsFormats = NULL, + bool clear = true, bool nobinary = true) + { + return checkResults(PQexecPrepared(m_conn, stmtName, paramsNumber, + (const char* const*) params, paramsLengths, paramsFormats, + nobinary ? 1 : 0), clear); + } + + inline PGresult *execPrepared(const char *stmtName, const int paramsNumber, + const char **params, bool clear = true, bool nobinary = true) + { + return execPrepared(stmtName, paramsNumber, + (const void **)params, NULL, NULL, clear, nobinary); + } + + void createTableIfNotExists(const std::string &table_name, const std::string &definition); + void verifyDatabase(); + + // Database initialization + void connectToDatabase(); + virtual void createDatabase() = 0; + virtual void initStatements() = 0; + inline void prepareStatement(const std::string &name, const std::string &sql) + { + checkResults(PQprepare(m_conn, name.c_str(), sql.c_str(), 0, NULL)); + } + + const int getPGVersion() const { return m_pgversion; } +private: + // Database connectivity checks + void ping(); + + // Database usage + PGresult *checkResults(PGresult *res, bool clear = true); + // Attributes std::string m_connect_string; PGconn *m_conn; int m_pgversion; }; +class MapDatabasePostgreSQL : private Database_PostgreSQL, public MapDatabase +{ +public: + MapDatabasePostgreSQL(const std::string &connect_string); + virtual ~MapDatabasePostgreSQL() {} + + bool saveBlock(const v3s16 &pos, const std::string &data); + void loadBlock(const v3s16 &pos, std::string *block); + bool deleteBlock(const v3s16 &pos); + void listAllLoadableBlocks(std::vector &dst); + + void beginSave() { Database_PostgreSQL::beginSave(); } + void endSave() { Database_PostgreSQL::endSave(); } + +protected: + virtual void createDatabase(); + virtual void initStatements(); +}; + +class PlayerDatabasePostgreSQL : private Database_PostgreSQL, public PlayerDatabase +{ +public: + PlayerDatabasePostgreSQL(const std::string &connect_string); + virtual ~PlayerDatabasePostgreSQL() {} + + void savePlayer(RemotePlayer *player); + bool loadPlayer(RemotePlayer *player, PlayerSAO *sao); + bool removePlayer(const std::string &name); + void listPlayers(std::vector &res); + +protected: + virtual void createDatabase(); + virtual void initStatements(); + +private: + bool playerDataExists(const std::string &playername); +}; + #endif diff --git a/src/database-redis.h b/src/database-redis.h index 214bc8dd6..fa15dd8a7 100644 --- a/src/database-redis.h +++ b/src/database-redis.h @@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class Settings; -class Database_Redis : public Database +class Database_Redis : public MapDatabase { public: Database_Redis(Settings &conf); diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp index 095d485c0..714f56c39 100644 --- a/src/database-sqlite3.cpp +++ b/src/database-sqlite3.cpp @@ -33,6 +33,8 @@ SQLite format specification: #include "settings.h" #include "porting.h" #include "util/string.h" +#include "content_sao.h" +#include "remoteplayer.h" #include @@ -111,27 +113,26 @@ int Database_SQLite3::busyHandler(void *data, int count) } -Database_SQLite3::Database_SQLite3(const std::string &savedir) : +Database_SQLite3::Database_SQLite3(const std::string &savedir, const std::string &dbname) : + m_database(NULL), m_initialized(false), m_savedir(savedir), - m_database(NULL), - m_stmt_read(NULL), - m_stmt_write(NULL), - m_stmt_list(NULL), - m_stmt_delete(NULL), + m_dbname(dbname), m_stmt_begin(NULL), m_stmt_end(NULL) { } -void Database_SQLite3::beginSave() { +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() { +void Database_SQLite3::endSave() +{ verifyDatabase(); SQLRES(sqlite3_step(m_stmt_end), SQLITE_DONE, "Failed to commit SQLite3 transaction"); @@ -142,7 +143,7 @@ void Database_SQLite3::openDatabase() { if (m_database) return; - std::string dbp = m_savedir + DIR_DELIM + "map.sqlite"; + std::string dbp = m_savedir + DIR_DELIM + m_dbname + ".sqlite"; // Open the database connection @@ -170,6 +171,8 @@ void Database_SQLite3::openDatabase() + 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() @@ -178,8 +181,61 @@ void Database_SQLite3::verifyDatabase() openDatabase(); - PREPARE_STATEMENT(begin, "BEGIN"); - PREPARE_STATEMENT(end, "COMMIT"); + 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(), + m_stmt_read(NULL), + m_stmt_write(NULL), + m_stmt_list(NULL), + m_stmt_delete(NULL) +{ + +} + +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 (?, ?)"); @@ -189,18 +245,16 @@ void Database_SQLite3::verifyDatabase() PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?"); PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`"); - m_initialized = true; - verbosestream << "ServerMap: SQLite3 database opened." << std::endl; } -inline void Database_SQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index) +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 Database_SQLite3::deleteBlock(const v3s16 &pos) +bool MapDatabaseSQLite3::deleteBlock(const v3s16 &pos) { verifyDatabase(); @@ -216,7 +270,7 @@ bool Database_SQLite3::deleteBlock(const v3s16 &pos) return good; } -bool Database_SQLite3::saveBlock(const v3s16 &pos, const std::string &data) +bool MapDatabaseSQLite3::saveBlock(const v3s16 &pos, const std::string &data) { verifyDatabase(); @@ -243,7 +297,7 @@ bool Database_SQLite3::saveBlock(const v3s16 &pos, const std::string &data) return true; } -void Database_SQLite3::loadBlock(const v3s16 &pos, std::string *block) +void MapDatabaseSQLite3::loadBlock(const v3s16 &pos, std::string *block) { verifyDatabase(); @@ -264,37 +318,312 @@ void Database_SQLite3::loadBlock(const v3s16 &pos, std::string *block) sqlite3_reset(m_stmt_read); } -void Database_SQLite3::createDatabase() +void MapDatabaseSQLite3::listAllLoadableBlocks(std::vector &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(), + m_stmt_player_load(NULL), + m_stmt_player_add(NULL), + m_stmt_player_update(NULL), + m_stmt_player_remove(NULL), + m_stmt_player_list(NULL), + m_stmt_player_load_inventory(NULL), + m_stmt_player_load_inventory_items(NULL), + m_stmt_player_add_inventory(NULL), + m_stmt_player_add_inventory_items(NULL), + m_stmt_player_remove_inventory(NULL), + m_stmt_player_remove_inventory_items(NULL), + m_stmt_player_metadata_load(NULL), + m_stmt_player_metadata_remove(NULL), + m_stmt_player_metadata_add(NULL) +{ + +} +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 `blocks` (\n" - " `pos` INT PRIMARY KEY,\n" - " `data` BLOB\n" - ");\n", + "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 database table"); + "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 Database_SQLite3::listAllLoadableBlocks(std::vector &dst) +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; +} - while (sqlite3_step(m_stmt_list) == SQLITE_ROW) { - dst.push_back(getIntegerAsBlock(sqlite3_column_int64(m_stmt_list, 0))); +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->getPitch()); + double_to_sqlite(m_stmt_player_add, 3, sao->getYaw()); + 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->getPitch()); + double_to_sqlite(m_stmt_player_update, 2, sao->getYaw()); + 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); } - sqlite3_reset(m_stmt_list); + + // 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 inventory_lists = sao->getInventory()->getLists(); + 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++) { + std::ostringstream os; + list->getItem(j).serialize(os); + std::string itemStr = os.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 PlayerAttributes &attrs = sao->getExtendedAttributes(); + for (PlayerAttributes::const_iterator it = attrs.begin(); it != attrs.end(); ++it) { + str_to_sqlite(m_stmt_player_metadata_add, 1, player->getName()); + str_to_sqlite(m_stmt_player_metadata_add, 2, it->first); + str_to_sqlite(m_stmt_player_metadata_add, 3, it->second); + sqlite3_vrfy(sqlite3_step(m_stmt_player_metadata_add), SQLITE_DONE); + sqlite3_reset(m_stmt_player_metadata_add); + } + + endSave(); } -Database_SQLite3::~Database_SQLite3() +bool PlayerDatabaseSQLite3::loadPlayer(RemotePlayer *player, PlayerSAO *sao) { - FINALIZE_STATEMENT(m_stmt_read) - FINALIZE_STATEMENT(m_stmt_write) - FINALIZE_STATEMENT(m_stmt_list) - FINALIZE_STATEMENT(m_stmt_begin) - FINALIZE_STATEMENT(m_stmt_end) - FINALIZE_STATEMENT(m_stmt_delete) + verifyDatabase(); - SQLOK_ERRSTREAM(sqlite3_close(m_database), "Failed to close database"); + 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->setPitch(sqlite_to_float(m_stmt_player_load, 0)); + sao->setYaw(sqlite_to_float(m_stmt_player_load, 1)); + sao->setBasePosition(sqlite_to_v3f(m_stmt_player_load, 2)); + sao->setHPRaw((s16) MYMIN(sqlite_to_int(m_stmt_player_load, 5), S16_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->addItem(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->setExtendedAttribute(attr, value); + } + 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 &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); +} diff --git a/src/database-sqlite3.h b/src/database-sqlite3.h index 2ab4c8ee9..3244facc9 100644 --- a/src/database-sqlite3.h +++ b/src/database-sqlite3.h @@ -20,8 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef DATABASE_SQLITE3_HEADER #define DATABASE_SQLITE3_HEADER +#include #include #include "database.h" +#include "exceptions.h" extern "C" { #include "sqlite3.h" @@ -30,37 +32,97 @@ extern "C" { class Database_SQLite3 : public Database { public: - Database_SQLite3(const std::string &savedir); - ~Database_SQLite3(); + virtual ~Database_SQLite3(); void beginSave(); void endSave(); - bool saveBlock(const v3s16 &pos, const std::string &data); - void loadBlock(const v3s16 &pos, std::string *block); - bool deleteBlock(const v3s16 &pos); - void listAllLoadableBlocks(std::vector &dst); bool initialized() const { return m_initialized; } +protected: + Database_SQLite3(const std::string &savedir, const std::string &dbname); -private: - // Open the database - void openDatabase(); - // Create the database structure - void createDatabase(); // Open and initialize the database if needed void verifyDatabase(); - void bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index = 1); + // Convertors + inline void str_to_sqlite(sqlite3_stmt *s, int iCol, const std::string &str) const + { + sqlite3_vrfy(sqlite3_bind_text(s, iCol, str.c_str(), str.size(), NULL)); + } + + inline void str_to_sqlite(sqlite3_stmt *s, int iCol, const char *str) const + { + sqlite3_vrfy(sqlite3_bind_text(s, iCol, str, strlen(str), NULL)); + } + + inline void int_to_sqlite(sqlite3_stmt *s, int iCol, int val) const + { + sqlite3_vrfy(sqlite3_bind_int(s, iCol, val)); + } + + inline void int64_to_sqlite(sqlite3_stmt *s, int iCol, s64 val) const + { + sqlite3_vrfy(sqlite3_bind_int64(s, iCol, (sqlite3_int64) val)); + } + + inline void double_to_sqlite(sqlite3_stmt *s, int iCol, double val) const + { + sqlite3_vrfy(sqlite3_bind_double(s, iCol, val)); + } + + inline std::string sqlite_to_string(sqlite3_stmt *s, int iCol) + { + const char* text = reinterpret_cast(sqlite3_column_text(s, iCol)); + return std::string(text ? text : ""); + } + + inline s32 sqlite_to_int(sqlite3_stmt *s, int iCol) + { + return sqlite3_column_int(s, iCol); + } + + inline u32 sqlite_to_uint(sqlite3_stmt *s, int iCol) + { + return (u32) sqlite3_column_int(s, iCol); + } + + inline float sqlite_to_float(sqlite3_stmt *s, int iCol) + { + return (float) sqlite3_column_double(s, iCol); + } + + inline const v3f sqlite_to_v3f(sqlite3_stmt *s, int iCol) + { + return v3f(sqlite_to_float(s, iCol), sqlite_to_float(s, iCol + 1), + sqlite_to_float(s, iCol + 2)); + } + + // Query verifiers helpers + inline void sqlite3_vrfy(int s, const std::string &m = "", int r = SQLITE_OK) const + { + if (s != r) + throw DatabaseException(m + ": " + sqlite3_errmsg(m_database)); + } + + inline void sqlite3_vrfy(const int s, const int r, const std::string &m = "") const + { + sqlite3_vrfy(s, m, r); + } + + // Create the database structure + virtual void createDatabase() = 0; + virtual void initStatements() = 0; + + sqlite3 *m_database; +private: + // Open the database + void openDatabase(); bool m_initialized; std::string m_savedir; + std::string m_dbname; - sqlite3 *m_database; - sqlite3_stmt *m_stmt_read; - sqlite3_stmt *m_stmt_write; - sqlite3_stmt *m_stmt_list; - sqlite3_stmt *m_stmt_delete; sqlite3_stmt *m_stmt_begin; sqlite3_stmt *m_stmt_end; @@ -69,4 +131,66 @@ private: static int busyHandler(void *data, int count); }; +class MapDatabaseSQLite3 : private Database_SQLite3, public MapDatabase +{ +public: + MapDatabaseSQLite3(const std::string &savedir); + virtual ~MapDatabaseSQLite3(); + + bool saveBlock(const v3s16 &pos, const std::string &data); + void loadBlock(const v3s16 &pos, std::string *block); + bool deleteBlock(const v3s16 &pos); + void listAllLoadableBlocks(std::vector &dst); + + void beginSave() { Database_SQLite3::beginSave(); } + void endSave() { Database_SQLite3::endSave(); } +protected: + virtual void createDatabase(); + virtual void initStatements(); + +private: + void bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index = 1); + + // Map + sqlite3_stmt *m_stmt_read; + sqlite3_stmt *m_stmt_write; + sqlite3_stmt *m_stmt_list; + sqlite3_stmt *m_stmt_delete; +}; + +class PlayerDatabaseSQLite3 : private Database_SQLite3, public PlayerDatabase +{ +public: + PlayerDatabaseSQLite3(const std::string &savedir); + virtual ~PlayerDatabaseSQLite3(); + + void savePlayer(RemotePlayer *player); + bool loadPlayer(RemotePlayer *player, PlayerSAO *sao); + bool removePlayer(const std::string &name); + void listPlayers(std::vector &res); + +protected: + virtual void createDatabase(); + virtual void initStatements(); + +private: + bool playerDataExists(const std::string &name); + + // Players + sqlite3_stmt *m_stmt_player_load; + sqlite3_stmt *m_stmt_player_add; + sqlite3_stmt *m_stmt_player_update; + sqlite3_stmt *m_stmt_player_remove; + sqlite3_stmt *m_stmt_player_list; + sqlite3_stmt *m_stmt_player_load_inventory; + sqlite3_stmt *m_stmt_player_load_inventory_items; + sqlite3_stmt *m_stmt_player_add_inventory; + sqlite3_stmt *m_stmt_player_add_inventory_items; + sqlite3_stmt *m_stmt_player_remove_inventory; + sqlite3_stmt *m_stmt_player_remove_inventory_items; + sqlite3_stmt *m_stmt_player_metadata_load; + sqlite3_stmt *m_stmt_player_metadata_remove; + sqlite3_stmt *m_stmt_player_metadata_add; +}; + #endif diff --git a/src/database.cpp b/src/database.cpp index 262d475ec..8e1483893 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -48,7 +48,7 @@ static inline s64 pythonmodulo(s64 i, s16 mod) } -s64 Database::getBlockAsInteger(const v3s16 &pos) +s64 MapDatabase::getBlockAsInteger(const v3s16 &pos) { return (u64) pos.Z * 0x1000000 + (u64) pos.Y * 0x1000 + @@ -56,7 +56,7 @@ s64 Database::getBlockAsInteger(const v3s16 &pos) } -v3s16 Database::getIntegerAsBlock(s64 i) +v3s16 MapDatabase::getIntegerAsBlock(s64 i) { v3s16 pos; pos.X = unsigned_to_signed(pythonmodulo(i, 4096), 2048); diff --git a/src/database.h b/src/database.h index 7213f088a..5a2b844fd 100644 --- a/src/database.h +++ b/src/database.h @@ -29,10 +29,15 @@ with this program; if not, write to the Free Software Foundation, Inc., class Database { public: - virtual ~Database() {} + virtual void beginSave() = 0; + virtual void endSave() = 0; + virtual bool initialized() const { return true; } +}; - virtual void beginSave() {} - virtual void endSave() {} +class MapDatabase : public Database +{ +public: + virtual ~MapDatabase() {} virtual bool saveBlock(const v3s16 &pos, const std::string &data) = 0; virtual void loadBlock(const v3s16 &pos, std::string *block) = 0; @@ -42,8 +47,19 @@ public: static v3s16 getIntegerAsBlock(s64 i); virtual void listAllLoadableBlocks(std::vector &dst) = 0; +}; - virtual bool initialized() const { return true; } +class PlayerSAO; +class RemotePlayer; + +class PlayerDatabase +{ +public: + virtual ~PlayerDatabase() {} + virtual void savePlayer(RemotePlayer *player) = 0; + virtual bool loadPlayer(RemotePlayer *player, PlayerSAO *sao) = 0; + virtual bool removePlayer(const std::string &name) = 0; + virtual void listPlayers(std::vector &res) = 0; }; #endif diff --git a/src/main.cpp b/src/main.cpp index 1ec278981..2ad4e2780 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif #ifndef SERVER #include "client/clientlauncher.h" + #endif #ifdef HAVE_TOUCHSCREENGUI @@ -102,7 +103,7 @@ static bool get_game_from_cmdline(GameParams *game_params, const Settings &cmd_a static bool determine_subgame(GameParams *game_params); static bool run_dedicated_server(const GameParams &game_params, const Settings &cmd_args); -static bool migrate_database(const GameParams &game_params, const Settings &cmd_args); +static bool migrate_map_database(const GameParams &game_params, const Settings &cmd_args); /**********************************************************************/ @@ -292,6 +293,8 @@ static void set_allowed_options(OptionList *allowed_options) _("Set gameid (\"--gameid list\" prints available ones)")))); allowed_options->insert(std::make_pair("migrate", ValueSpec(VALUETYPE_STRING, _("Migrate from current map backend to another (Only works when using minetestserver or with --server)")))); + allowed_options->insert(std::make_pair("migrate-players", ValueSpec(VALUETYPE_STRING, + _("Migrate from current players backend to another (Only works when using minetestserver or with --server)")))); allowed_options->insert(std::make_pair("terminal", ValueSpec(VALUETYPE_FLAG, _("Feature an interactive terminal (Only works when using minetestserver or with --server)")))); #ifndef SERVER @@ -332,7 +335,7 @@ static void print_allowed_options(const OptionList &allowed_options) if (i->second.type != VALUETYPE_FLAG) os1 << _(" "); - std::cout << padStringRight(os1.str(), 24); + std::cout << padStringRight(os1.str(), 30); if (i->second.help != NULL) std::cout << i->second.help; @@ -828,7 +831,9 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & // Database migration if (cmd_args.exists("migrate")) - return migrate_database(game_params, cmd_args); + return migrate_map_database(game_params, cmd_args); + else if (cmd_args.exists("migrate-players")) + return ServerEnvironment::migratePlayersDatabase(game_params, cmd_args); if (cmd_args.exists("terminal")) { #if USE_CURSES @@ -912,7 +917,7 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & return true; } -static bool migrate_database(const GameParams &game_params, const Settings &cmd_args) +static bool migrate_map_database(const GameParams &game_params, const Settings &cmd_args) { std::string migrate_to = cmd_args.get("migrate"); Settings world_mt; @@ -921,20 +926,23 @@ static bool migrate_database(const GameParams &game_params, const Settings &cmd_ errorstream << "Cannot read world.mt!" << std::endl; return false; } + if (!world_mt.exists("backend")) { errorstream << "Please specify your current backend in world.mt:" << std::endl - << " backend = {sqlite3|leveldb|redis|dummy}" + << " backend = {sqlite3|leveldb|redis|dummy|postgresql}" << std::endl; return false; } + std::string backend = world_mt.get("backend"); if (backend == migrate_to) { errorstream << "Cannot migrate: new backend is same" << " as the old one" << std::endl; return false; } - Database *old_db = ServerMap::createDatabase(backend, game_params.world_path, world_mt), + + MapDatabase *old_db = ServerMap::createDatabase(backend, game_params.world_path, world_mt), *new_db = ServerMap::createDatabase(migrate_to, game_params.world_path, world_mt); u32 count = 0; @@ -976,4 +984,3 @@ static bool migrate_database(const GameParams &game_params, const Settings &cmd_ return true; } - diff --git a/src/map.cpp b/src/map.cpp index 75dcee350..c148c51f1 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2286,13 +2286,13 @@ bool ServerMap::loadSectorFull(v2s16 p2d) } #endif -Database *ServerMap::createDatabase( +MapDatabase *ServerMap::createDatabase( const std::string &name, const std::string &savedir, Settings &conf) { if (name == "sqlite3") - return new Database_SQLite3(savedir); + return new MapDatabaseSQLite3(savedir); if (name == "dummy") return new Database_Dummy(); #if USE_LEVELDB @@ -2304,8 +2304,11 @@ Database *ServerMap::createDatabase( return new Database_Redis(conf); #endif #if USE_POSTGRESQL - else if (name == "postgresql") - return new Database_PostgreSQL(conf); + else if (name == "postgresql") { + std::string connect_string = ""; + conf.getNoEx("pgsql_connection", connect_string); + return new MapDatabasePostgreSQL(connect_string); + } #endif else throw BaseException(std::string("Database backend ") + name + " not supported."); @@ -2326,7 +2329,7 @@ bool ServerMap::saveBlock(MapBlock *block) return saveBlock(block, dbase); } -bool ServerMap::saveBlock(MapBlock *block, Database *db) +bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db) { v3s16 p3d = block->getPos(); diff --git a/src/map.h b/src/map.h index 4d7079823..7e597bef6 100644 --- a/src/map.h +++ b/src/map.h @@ -37,7 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map_settings_manager.h" class Settings; -class Database; +class MapDatabase; class ClientMap; class MapSector; class ServerMapSector; @@ -430,7 +430,7 @@ public: /* Database functions */ - static Database *createDatabase(const std::string &name, const std::string &savedir, Settings &conf); + static MapDatabase *createDatabase(const std::string &name, const std::string &savedir, Settings &conf); // Returns true if the database file does not exist bool loadFromFolders(); @@ -458,7 +458,7 @@ public: bool loadSectorMeta(v2s16 p2d); bool saveBlock(MapBlock *block); - static bool saveBlock(MapBlock *block, Database *db); + static bool saveBlock(MapBlock *block, MapDatabase *db); // This will generate a sector with getSector if not found. void loadBlock(const std::string §ordir, const std::string &blockfile, MapSector *sector, bool save_after_load=false); @@ -510,7 +510,7 @@ private: This is reset to false when written on disk. */ bool m_map_metadata_changed; - Database *dbase; + MapDatabase *dbase; }; diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index c8e5b9132..2dbfe9d9d 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -67,54 +67,6 @@ RemotePlayer::RemotePlayer(const char *name, IItemDefManager *idef): movement_gravity = g_settings->getFloat("movement_gravity") * BS; } -void RemotePlayer::save(std::string savedir, IGameDef *gamedef) -{ - /* - * We have to open all possible player files in the players directory - * and check their player names because some file systems are not - * case-sensitive and player names are case-sensitive. - */ - - // A player to deserialize files into to check their names - RemotePlayer testplayer("", gamedef->idef()); - - savedir += DIR_DELIM; - std::string path = savedir + m_name; - for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) { - if (!fs::PathExists(path)) { - // Open file and serialize - std::ostringstream ss(std::ios_base::binary); - serialize(ss); - if (!fs::safeWriteToFile(path, ss.str())) { - infostream << "Failed to write " << path << std::endl; - } - setModified(false); - return; - } - // Open file and deserialize - std::ifstream is(path.c_str(), std::ios_base::binary); - if (!is.good()) { - infostream << "Failed to open " << path << std::endl; - return; - } - testplayer.deSerialize(is, path, NULL); - is.close(); - if (strcmp(testplayer.getName(), m_name) == 0) { - // Open file and serialize - std::ostringstream ss(std::ios_base::binary); - serialize(ss); - if (!fs::safeWriteToFile(path, ss.str())) { - infostream << "Failed to write " << path << std::endl; - } - setModified(false); - return; - } - path = savedir + m_name + itos(i); - } - - infostream << "Didn't find free file for player " << m_name << std::endl; -} - void RemotePlayer::serializeExtraAttributes(std::string &output) { assert(m_sao); diff --git a/src/remoteplayer.h b/src/remoteplayer.h index 9d123393f..ce7db5608 100644 --- a/src/remoteplayer.h +++ b/src/remoteplayer.h @@ -37,11 +37,11 @@ enum RemotePlayerChatResult */ class RemotePlayer : public Player { + friend class PlayerDatabaseFiles; public: RemotePlayer(const char *name, IItemDefManager *idef); virtual ~RemotePlayer() {} - void save(std::string savedir, IGameDef *gamedef); void deSerialize(std::istream &is, const std::string &playername, PlayerSAO *sao); PlayerSAO *getPlayerSAO() { return m_sao; } diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 6ac4bb653..d94f3e31d 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -334,6 +334,22 @@ int ModApiServer::l_kick_player(lua_State *L) return 1; } +int ModApiServer::l_remove_player(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string name = luaL_checkstring(L, 1); + ServerEnvironment *s_env = dynamic_cast(getEnv(L)); + assert(s_env); + + RemotePlayer *player = s_env->getPlayer(name.c_str()); + if (!player) + lua_pushinteger(L, s_env->removePlayerFromDatabase(name) ? 0 : 1); + else + lua_pushinteger(L, 2); + + return 1; +} + // unban_player_or_ip() int ModApiServer::l_unban_player_or_ip(lua_State *L) { @@ -510,6 +526,7 @@ void ModApiServer::Initialize(lua_State *L, int top) API_FCT(get_ban_description); API_FCT(ban_player); API_FCT(kick_player); + API_FCT(remove_player); API_FCT(unban_player_or_ip); API_FCT(notify_authentication_modified); diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h index 008810784..e6c0df978 100644 --- a/src/script/lua_api/l_server.h +++ b/src/script/lua_api/l_server.h @@ -92,6 +92,9 @@ private: // kick_player(name, [message]) -> success static int l_kick_player(lua_State *L); + // remove_player(name) + static int l_remove_player(lua_State *L); + // notify_authentication_modified(name) static int l_notify_authentication_modified(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index ac6265d09..4c7e60286 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -60,6 +60,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/base64.h" #include "util/sha1.h" #include "util/hex.h" +#include "database.h" class ClientNotFoundException : public BaseException { @@ -2618,9 +2619,8 @@ void Server::RespawnPlayer(u16 peer_id) bool repositioned = m_script->on_respawnplayer(playersao); if (!repositioned) { - v3f pos = findSpawnPos(); // setPos will send the new position to client - playersao->setPos(pos); + playersao->setPos(findSpawnPos()); } SendPlayerHP(peer_id); @@ -3442,8 +3442,8 @@ v3f Server::findSpawnPos() s32 range = 1 + i; // We're going to try to throw the player to this position v2s16 nodepos2d = v2s16( - -range + (myrand() % (range * 2)), - -range + (myrand() % (range * 2))); + -range + (myrand() % (range * 2)), + -range + (myrand() % (range * 2))); // Get spawn level at point s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d); @@ -3516,8 +3516,6 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version) { - bool newplayer = false; - /* Try to get an existing player */ @@ -3538,44 +3536,18 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version return NULL; } - // Create a new player active object - PlayerSAO *playersao = new PlayerSAO(m_env, peer_id, isSingleplayer()); - player = m_env->loadPlayer(name, playersao); - - // Create player if it doesn't exist if (!player) { - newplayer = true; - player = new RemotePlayer(name, this->idef()); - // Set player position - infostream<<"Server: Finding spawn place for player \"" - <setBasePosition(findSpawnPos()); - - // Make sure the player is saved - player->setModified(true); - - // Add player to environment - m_env->addPlayer(player); - } else { - // If the player exists, ensure that they respawn inside legal bounds - // This fixes an assert crash when the player can't be added - // to the environment - if (objectpos_over_limit(playersao->getBasePosition())) { - actionstream << "Respawn position for player \"" - << name << "\" outside limits, resetting" << std::endl; - playersao->setBasePosition(findSpawnPos()); - } + player = new RemotePlayer(name, idef()); } - playersao->initialize(player, getPlayerEffectivePrivs(player->getName())); - - player->protocol_version = proto_version; + bool newplayer = false; - /* Clean up old HUD elements from previous sessions */ - player->clearHud(); + // Load player + PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer()); - /* Add object to environment */ - m_env->addActiveObject(playersao); + // Complete init with server parts + playersao->finalize(player, getPlayerEffectivePrivs(player->getName())); + player->protocol_version = proto_version; /* Run scripts */ if (newplayer) { diff --git a/src/server.h b/src/server.h index 4183bcda1..948fb8fc2 100644 --- a/src/server.h +++ b/src/server.h @@ -306,6 +306,7 @@ public: bool showFormspec(const char *name, const std::string &formspec, const std::string &formname); Map & getMap() { return m_env->getMap(); } ServerEnvironment & getEnv() { return *m_env; } + v3f findSpawnPos(); u32 hudAdd(RemotePlayer *player, HudElement *element); bool hudRemove(RemotePlayer *player, u32 id); @@ -472,8 +473,6 @@ private: RemotePlayer *player = NULL); void handleAdminChat(const ChatEventChat *evt); - v3f findSpawnPos(); - // When called, connection mutex should be locked RemoteClient* getClient(u16 peer_id,ClientState state_min=CS_Active); RemoteClient* getClientNoEx(u16 peer_id,ClientState state_min=CS_Active); diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index e09c7da16..c0dc0e0ea 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -36,6 +36,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/pointedthing.h" #include "threading/mutex_auto_lock.h" #include "filesys.h" +#include "gameparams.h" +#include "database-dummy.h" +#include "database-files.h" +#include "database-sqlite3.h" +#if USE_POSTGRESQL +#include "database-postgresql.h" +#endif #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" @@ -365,8 +372,30 @@ ServerEnvironment::ServerEnvironment(ServerMap *map, m_game_time_fraction_counter(0), m_last_clear_objects_time(0), m_recommended_send_interval(0.1), - m_max_lag_estimate(0.1) + m_max_lag_estimate(0.1), + m_player_database(NULL) { + // Determine which database backend to use + std::string conf_path = path_world + DIR_DELIM + "world.mt"; + Settings conf; + bool succeeded = conf.readConfigFile(conf_path.c_str()); + if (!succeeded || !conf.exists("player_backend")) { + // fall back to files + conf.set("player_backend", "files"); + warningstream << "/!\\ You are using old player file backend. " + << "This backend is deprecated and will be removed in next release /!\\" + << std::endl << "Switching to SQLite3 or PostgreSQL is advised, " + << "please read http://wiki.minetest.net/Database_backends." << std::endl; + + if (!conf.updateConfigFile(conf_path.c_str())) { + errorstream << "ServerEnvironment::ServerEnvironment(): " + << "Failed to update world.mt!" << std::endl; + } + } + + std::string name = ""; + conf.getNoEx("player_backend", name); + m_player_database = openPlayerDatabase(name, path_world, conf); } ServerEnvironment::~ServerEnvironment() @@ -392,6 +421,8 @@ ServerEnvironment::~ServerEnvironment() i != m_players.end(); ++i) { delete (*i); } + + delete m_player_database; } Map & ServerEnvironment::getMap() @@ -455,6 +486,11 @@ void ServerEnvironment::removePlayer(RemotePlayer *player) } } +bool ServerEnvironment::removePlayerFromDatabase(const std::string &name) +{ + return m_player_database->removePlayer(name); +} + bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p) { float distance = pos1.getDistanceFrom(pos2); @@ -495,7 +531,7 @@ void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason, void ServerEnvironment::saveLoadedPlayers() { - std::string players_path = m_path_world + DIR_DELIM "players"; + std::string players_path = m_path_world + DIR_DELIM + "players"; fs::CreateDir(players_path); for (std::vector::iterator it = m_players.begin(); @@ -503,63 +539,63 @@ void ServerEnvironment::saveLoadedPlayers() ++it) { if ((*it)->checkModified() || ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) { - (*it)->save(players_path, m_server); + try { + m_player_database->savePlayer(*it); + } catch (DatabaseException &e) { + errorstream << "Failed to save player " << (*it)->getName() << " exception: " + << e.what() << std::endl; + throw; + } } } } void ServerEnvironment::savePlayer(RemotePlayer *player) { - std::string players_path = m_path_world + DIR_DELIM "players"; - fs::CreateDir(players_path); - - player->save(players_path, m_server); + try { + m_player_database->savePlayer(player); + } catch (DatabaseException &e) { + errorstream << "Failed to save player " << player->getName() << " exception: " + << e.what() << std::endl; + throw; + } } -RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao) +PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player, + u16 peer_id, bool is_singleplayer) { - bool newplayer = false; - bool found = false; - std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM; - std::string path = players_path + playername; - - RemotePlayer *player = getPlayer(playername.c_str()); - if (!player) { - player = new RemotePlayer("", m_server->idef()); - newplayer = true; - } - - for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) { - //// Open file and deserialize - std::ifstream is(path.c_str(), std::ios_base::binary); - if (!is.good()) - continue; - - player->deSerialize(is, path, sao); - is.close(); - - if (player->getName() == playername) { - found = true; - break; + PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer); + // Create player if it doesn't exist + if (!m_player_database->loadPlayer(player, playersao)) { + *new_player = true; + // Set player position + infostream << "Server: Finding spawn place for player \"" + << player->getName() << "\"" << std::endl; + playersao->setBasePosition(m_server->findSpawnPos()); + + // Make sure the player is saved + player->setModified(true); + } else { + // If the player exists, ensure that they respawn inside legal bounds + // This fixes an assert crash when the player can't be added + // to the environment + if (objectpos_over_limit(playersao->getBasePosition())) { + actionstream << "Respawn position for player \"" + << player->getName() << "\" outside limits, resetting" << std::endl; + playersao->setBasePosition(m_server->findSpawnPos()); } - - path = players_path + playername + itos(i); } - if (!found) { - infostream << "Player file for player " << playername - << " not found" << std::endl; - if (newplayer) - delete player; + // Add player to environment + addPlayer(player); - return NULL; - } + /* Clean up old HUD elements from previous sessions */ + player->clearHud(); - if (newplayer) { - addPlayer(player); - } - player->setModified(false); - return player; + /* Add object to environment */ + addActiveObject(playersao); + + return playersao; } void ServerEnvironment::saveMeta() @@ -2173,3 +2209,111 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete) m_active_objects.erase(*i); } } + +PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name, + const std::string &savedir, const Settings &conf) +{ + + if (name == "sqlite3") + return new PlayerDatabaseSQLite3(savedir); + else if (name == "dummy") + return new Database_Dummy(); +#if USE_POSTGRESQL + else if (name == "postgresql") { + std::string connect_string = ""; + conf.getNoEx("pgsql_player_connection", connect_string); + return new PlayerDatabasePostgreSQL(connect_string); + } +#endif + else if (name == "files") + return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players"); + else + throw BaseException(std::string("Database backend ") + name + " not supported."); +} + +bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params, + const Settings &cmd_args) +{ + std::string migrate_to = cmd_args.get("migrate-players"); + Settings world_mt; + std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt"; + if (!world_mt.readConfigFile(world_mt_path.c_str())) { + errorstream << "Cannot read world.mt!" << std::endl; + return false; + } + + if (!world_mt.exists("player_backend")) { + errorstream << "Please specify your current backend in world.mt:" + << std::endl + << " player_backend = {files|sqlite3|postgresql}" + << std::endl; + return false; + } + + std::string backend = world_mt.get("player_backend"); + if (backend == migrate_to) { + errorstream << "Cannot migrate: new backend is same" + << " as the old one" << std::endl; + return false; + } + + const std::string players_backup_path = game_params.world_path + DIR_DELIM + + "players.bak"; + + if (backend == "files") { + // Create backup directory + fs::CreateDir(players_backup_path); + } + + try { + PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend, + game_params.world_path, world_mt); + PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to, + game_params.world_path, world_mt); + + std::vector player_list; + srcdb->listPlayers(player_list); + for (std::vector::const_iterator it = player_list.begin(); + it != player_list.end(); ++it) { + actionstream << "Migrating player " << it->c_str() << std::endl; + RemotePlayer player(it->c_str(), NULL); + PlayerSAO playerSAO(NULL, &player, 15000, false); + + srcdb->loadPlayer(&player, &playerSAO); + + playerSAO.finalize(&player, std::set()); + player.setPlayerSAO(&playerSAO); + + dstdb->savePlayer(&player); + + // For files source, move player files to backup dir + if (backend == "files") { + fs::Rename( + game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it), + players_backup_path + DIR_DELIM + (*it)); + } + } + + actionstream << "Successfully migrated " << player_list.size() << " players" + << std::endl; + world_mt.set("player_backend", migrate_to); + if (!world_mt.updateConfigFile(world_mt_path.c_str())) + errorstream << "Failed to update world.mt!" << std::endl; + else + actionstream << "world.mt updated" << std::endl; + + // When migration is finished from file backend, remove players directory if empty + if (backend == "files") { + fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM + + "players"); + } + + delete srcdb; + delete dstdb; + + } catch (BaseException &e) { + errorstream << "An error occured during migration: " << e.what() << std::endl; + return false; + } + return true; +} diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 99110542a..0e31aa41a 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -27,7 +27,9 @@ with this program; if not, write to the Free Software Foundation, Inc., class IGameDef; class ServerMap; +struct GameParams; class RemotePlayer; +class PlayerDatabase; class PlayerSAO; class ServerEnvironment; class ActiveBlockModifier; @@ -217,9 +219,11 @@ public: // Save players void saveLoadedPlayers(); void savePlayer(RemotePlayer *player); - RemotePlayer *loadPlayer(const std::string &playername, PlayerSAO *sao); + PlayerSAO *loadPlayer(RemotePlayer *player, bool *new_player, u16 peer_id, + bool is_singleplayer); void addPlayer(RemotePlayer *player); void removePlayer(RemotePlayer *player); + bool removePlayerFromDatabase(const std::string &name); /* Save and load time of day and game timer @@ -334,8 +338,13 @@ public: RemotePlayer *getPlayer(const u16 peer_id); RemotePlayer *getPlayer(const char* name); + + static bool migratePlayersDatabase(const GameParams &game_params, + const Settings &cmd_args); private: + static PlayerDatabase *openPlayerDatabase(const std::string &name, + const std::string &savedir, const Settings &conf); /* Internal ActiveObject interface ------------------------------------------- @@ -419,6 +428,8 @@ private: // peer_ids in here should be unique, except that there may be many 0s std::vector m_players; + PlayerDatabase *m_player_database; + // Particles IntervalLimiter m_particle_management_interval; UNORDERED_MAP m_particle_spawners; diff --git a/src/unittest/test_player.cpp b/src/unittest/test_player.cpp index b639878b2..e2b1cd855 100644 --- a/src/unittest/test_player.cpp +++ b/src/unittest/test_player.cpp @@ -31,59 +31,10 @@ public: const char *getName() { return "TestPlayer"; } void runTests(IGameDef *gamedef); - - void testSave(IGameDef *gamedef); - void testLoad(IGameDef *gamedef); }; static TestPlayer g_test_instance; void TestPlayer::runTests(IGameDef *gamedef) { - TEST(testSave, gamedef); - TEST(testLoad, gamedef); -} - -void TestPlayer::testSave(IGameDef *gamedef) -{ - RemotePlayer rplayer("testplayer_save", gamedef->idef()); - PlayerSAO sao(NULL, 1, false); - sao.initialize(&rplayer, std::set()); - rplayer.setPlayerSAO(&sao); - sao.setBreath(10, false); - sao.setHPRaw(8); - sao.setYaw(0.1f); - sao.setPitch(0.6f); - sao.setBasePosition(v3f(450.2f, -15.7f, 68.1f)); - rplayer.save(".", gamedef); - UASSERT(fs::PathExists("testplayer_save")); -} - -void TestPlayer::testLoad(IGameDef *gamedef) -{ - RemotePlayer rplayer("testplayer_load", gamedef->idef()); - PlayerSAO sao(NULL, 1, false); - sao.initialize(&rplayer, std::set()); - rplayer.setPlayerSAO(&sao); - sao.setBreath(10, false); - sao.setHPRaw(8); - sao.setYaw(0.1f); - sao.setPitch(0.6f); - sao.setBasePosition(v3f(450.2f, -15.7f, 68.1f)); - rplayer.save(".", gamedef); - UASSERT(fs::PathExists("testplayer_load")); - - RemotePlayer rplayer_load("testplayer_load", gamedef->idef()); - PlayerSAO sao_load(NULL, 2, false); - std::ifstream is("testplayer_load", std::ios_base::binary); - UASSERT(is.good()); - rplayer_load.deSerialize(is, "testplayer_load", &sao_load); - is.close(); - - UASSERT(strcmp(rplayer_load.getName(), "testplayer_load") == 0); - UASSERT(sao_load.getBreath() == 10); - UASSERT(sao_load.getHP() == 8); - UASSERT(sao_load.getYaw() == 0.1f); - UASSERT(sao_load.getPitch() == 0.6f); - UASSERT(sao_load.getBasePosition() == v3f(450.2f, -15.7f, 68.1f)); } -- cgit v1.2.3 From 2818d3f2244d2146a5cdb61cd41f6561c514f97c Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Tue, 25 Apr 2017 13:38:08 -0400 Subject: Rename Scripting API files for consistency --- src/client.cpp | 2 +- src/clientenvironment.cpp | 2 +- src/content_abm.cpp | 2 +- src/content_sao.cpp | 2 +- src/emerge.cpp | 2 +- src/environment.cpp | 2 +- src/game.cpp | 2 +- src/guiFormSpecMenu.cpp | 2 +- src/inventorymanager.cpp | 2 +- src/map.cpp | 2 +- src/network/clientpackethandler.cpp | 2 +- src/network/serverpackethandler.cpp | 2 +- src/script/CMakeLists.txt | 4 +- src/script/clientscripting.cpp | 80 ------------------------ src/script/clientscripting.h | 42 ------------- src/script/lua_api/l_env.cpp | 2 +- src/script/lua_api/l_object.cpp | 2 +- src/script/scripting_client.cpp | 80 ++++++++++++++++++++++++ src/script/scripting_client.h | 42 +++++++++++++ src/script/scripting_server.cpp | 119 ++++++++++++++++++++++++++++++++++++ src/script/scripting_server.h | 57 +++++++++++++++++ src/script/serverscripting.cpp | 119 ------------------------------------ src/script/serverscripting.h | 57 ----------------- src/server.cpp | 2 +- src/serverenvironment.cpp | 2 +- 25 files changed, 316 insertions(+), 316 deletions(-) delete mode 100644 src/script/clientscripting.cpp delete mode 100644 src/script/clientscripting.h create mode 100644 src/script/scripting_client.cpp create mode 100644 src/script/scripting_client.h create mode 100644 src/script/scripting_server.cpp create mode 100644 src/script/scripting_server.h delete mode 100644 src/script/serverscripting.cpp delete mode 100644 src/script/serverscripting.h (limited to 'src/serverenvironment.cpp') diff --git a/src/client.cpp b/src/client.cpp index 94c808a57..19fe9b0ba 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -45,7 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "database-sqlite3.h" #include "serialization.h" #include "guiscalingfilter.h" -#include "script/clientscripting.h" +#include "script/scripting_client.h" #include "game.h" extern gui::IGUIEnvironment* guienv; diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp index 4a8bbb066..cc75fd2d6 100644 --- a/src/clientenvironment.cpp +++ b/src/clientenvironment.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientenvironment.h" #include "clientsimpleobject.h" #include "clientmap.h" -#include "clientscripting.h" +#include "scripting_client.h" #include "mapblock_mesh.h" #include "event.h" #include "collision.h" diff --git a/src/content_abm.cpp b/src/content_abm.cpp index 1e175c64f..162f93364 100644 --- a/src/content_abm.cpp +++ b/src/content_abm.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "mapblock.h" // For getNodeBlockPos #include "map.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "log.h" void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index caf6dcbab..81c6902f5 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "remoteplayer.h" #include "server.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "genericobject.h" std::map ServerActiveObject::m_types; diff --git a/src/emerge.cpp b/src/emerge.cpp index 4c3a83f7e..d24971e44 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_schematic.h" #include "nodedef.h" #include "profiler.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "server.h" #include "serverobject.h" #include "settings.h" diff --git a/src/environment.cpp b/src/environment.cpp index c1aeeec60..4e782db81 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "environment.h" #include "collision.h" #include "serverobject.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "server.h" #include "daynightratio.h" #include "emerge.h" diff --git a/src/game.cpp b/src/game.cpp index 31a48531c..97dc8c064 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -60,7 +60,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "version.h" #include "minimap.h" #include "mapblock_mesh.h" -#include "script/clientscripting.h" +#include "script/scripting_client.h" #include "sound.h" diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 5861e9a81..14d576f8f 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "gettime.h" #include "gettext.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "porting.h" #include "settings.h" #include "client.h" diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 6ebc2994b..c976bd037 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventorymanager.h" #include "log.h" #include "serverenvironment.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "serverobject.h" #include "settings.h" #include "craftdef.h" diff --git a/src/map.cpp b/src/map.cpp index c148c51f1..9e8823f84 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -44,7 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "database.h" #include "database-dummy.h" #include "database-sqlite3.h" -#include "script/serverscripting.h" +#include "script/scripting_server.h" #include #include #if USE_LEVELDB diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 42c49be3c..772ffe905 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "util/strfnd.h" #include "network/clientopcodes.h" -#include "script/clientscripting.h" +#include "script/scripting_client.h" #include "util/serialize.h" #include "util/srp.h" #include "tileanimation.h" diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index c284cb6c8..5b026bbdb 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "player.h" #include "rollback_interface.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "settings.h" #include "tool.h" #include "version.h" diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index c96ccc816..bebe2f037 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -4,7 +4,7 @@ add_subdirectory(lua_api) # Used by server and client set(common_SCRIPT_SRCS - ${CMAKE_CURRENT_SOURCE_DIR}/serverscripting.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/scripting_server.cpp ${common_SCRIPT_COMMON_SRCS} ${common_SCRIPT_CPP_API_SRCS} ${common_SCRIPT_LUA_API_SRCS} @@ -13,7 +13,7 @@ set(common_SCRIPT_SRCS # Used by client only set(client_SCRIPT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/scripting_mainmenu.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/clientscripting.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/scripting_client.cpp ${client_SCRIPT_COMMON_SRCS} ${client_SCRIPT_CPP_API_SRCS} ${client_SCRIPT_LUA_API_SRCS} diff --git a/src/script/clientscripting.cpp b/src/script/clientscripting.cpp deleted file mode 100644 index ba3f910c0..000000000 --- a/src/script/clientscripting.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola -Copyright (C) 2017 nerzhul, Loic Blot - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "clientscripting.h" -#include "client.h" -#include "cpp_api/s_internal.h" -#include "lua_api/l_client.h" -#include "lua_api/l_env.h" -#include "lua_api/l_minimap.h" -#include "lua_api/l_storage.h" -#include "lua_api/l_sound.h" -#include "lua_api/l_util.h" -#include "lua_api/l_item.h" -#include "lua_api/l_nodemeta.h" -#include "lua_api/l_localplayer.h" - -ClientScripting::ClientScripting(Client *client): - ScriptApiBase() -{ - setGameDef(client); - - SCRIPTAPI_PRECHECKHEADER - - // Security is mandatory client side - initializeSecurityClient(); - - lua_getglobal(L, "core"); - int top = lua_gettop(L); - - lua_newtable(L); - lua_setfield(L, -2, "ui"); - - InitializeModApi(L, top); - lua_pop(L, 1); - - LuaMinimap::create(L, client->getMinimap()); - - // Push builtin initialization type - lua_pushstring(L, "client"); - lua_setglobal(L, "INIT"); - - infostream << "SCRIPTAPI: Initialized client game modules" << std::endl; -} - -void ClientScripting::InitializeModApi(lua_State *L, int top) -{ - ModApiUtil::InitializeClient(L, top); - ModApiClient::Initialize(L, top); - ModApiStorage::Initialize(L, top); - ModApiEnvMod::InitializeClient(L, top); - - LuaItemStack::Register(L); - StorageRef::Register(L); - LuaMinimap::Register(L); - NodeMetaRef::RegisterClient(L); - LuaLocalPlayer::Register(L); -} - -void ClientScripting::on_client_ready(LocalPlayer *localplayer) -{ - lua_State *L = getStack(); - LuaLocalPlayer::create(L, localplayer); -} diff --git a/src/script/clientscripting.h b/src/script/clientscripting.h deleted file mode 100644 index df94e8b71..000000000 --- a/src/script/clientscripting.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola -Copyright (C) 2017 nerzhul, Loic Blot - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef CLIENT_SCRIPTING_H_ -#define CLIENT_SCRIPTING_H_ - -#include "cpp_api/s_base.h" -#include "cpp_api/s_client.h" -#include "cpp_api/s_security.h" - -class Client; -class LocalPlayer; -class ClientScripting: - virtual public ScriptApiBase, - public ScriptApiSecurity, - public ScriptApiClient -{ -public: - ClientScripting(Client *client); - void on_client_ready(LocalPlayer *localplayer); - -private: - virtual void InitializeModApi(lua_State *L, int top); -}; -#endif diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 75b07fa37..85791f90b 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_vmanip.h" #include "common/c_converter.h" #include "common/c_content.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "environment.h" #include "server.h" #include "nodedef.h" diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 95e977f9e..9668f76f7 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_sao.h" #include "server.h" #include "hud.h" -#include "serverscripting.h" +#include "scripting_server.h" struct EnumString es_HudElementType[] = { diff --git a/src/script/scripting_client.cpp b/src/script/scripting_client.cpp new file mode 100644 index 000000000..8ff5abcc4 --- /dev/null +++ b/src/script/scripting_client.cpp @@ -0,0 +1,80 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scripting_client.h" +#include "client.h" +#include "cpp_api/s_internal.h" +#include "lua_api/l_client.h" +#include "lua_api/l_env.h" +#include "lua_api/l_minimap.h" +#include "lua_api/l_storage.h" +#include "lua_api/l_sound.h" +#include "lua_api/l_util.h" +#include "lua_api/l_item.h" +#include "lua_api/l_nodemeta.h" +#include "lua_api/l_localplayer.h" + +ClientScripting::ClientScripting(Client *client): + ScriptApiBase() +{ + setGameDef(client); + + SCRIPTAPI_PRECHECKHEADER + + // Security is mandatory client side + initializeSecurityClient(); + + lua_getglobal(L, "core"); + int top = lua_gettop(L); + + lua_newtable(L); + lua_setfield(L, -2, "ui"); + + InitializeModApi(L, top); + lua_pop(L, 1); + + LuaMinimap::create(L, client->getMinimap()); + + // Push builtin initialization type + lua_pushstring(L, "client"); + lua_setglobal(L, "INIT"); + + infostream << "SCRIPTAPI: Initialized client game modules" << std::endl; +} + +void ClientScripting::InitializeModApi(lua_State *L, int top) +{ + ModApiUtil::InitializeClient(L, top); + ModApiClient::Initialize(L, top); + ModApiStorage::Initialize(L, top); + ModApiEnvMod::InitializeClient(L, top); + + LuaItemStack::Register(L); + StorageRef::Register(L); + LuaMinimap::Register(L); + NodeMetaRef::RegisterClient(L); + LuaLocalPlayer::Register(L); +} + +void ClientScripting::on_client_ready(LocalPlayer *localplayer) +{ + lua_State *L = getStack(); + LuaLocalPlayer::create(L, localplayer); +} diff --git a/src/script/scripting_client.h b/src/script/scripting_client.h new file mode 100644 index 000000000..df94e8b71 --- /dev/null +++ b/src/script/scripting_client.h @@ -0,0 +1,42 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef CLIENT_SCRIPTING_H_ +#define CLIENT_SCRIPTING_H_ + +#include "cpp_api/s_base.h" +#include "cpp_api/s_client.h" +#include "cpp_api/s_security.h" + +class Client; +class LocalPlayer; +class ClientScripting: + virtual public ScriptApiBase, + public ScriptApiSecurity, + public ScriptApiClient +{ +public: + ClientScripting(Client *client); + void on_client_ready(LocalPlayer *localplayer); + +private: + virtual void InitializeModApi(lua_State *L, int top); +}; +#endif diff --git a/src/script/scripting_server.cpp b/src/script/scripting_server.cpp new file mode 100644 index 000000000..ce56fcf19 --- /dev/null +++ b/src/script/scripting_server.cpp @@ -0,0 +1,119 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scripting_server.h" +#include "server.h" +#include "log.h" +#include "settings.h" +#include "cpp_api/s_internal.h" +#include "lua_api/l_areastore.h" +#include "lua_api/l_base.h" +#include "lua_api/l_craft.h" +#include "lua_api/l_env.h" +#include "lua_api/l_inventory.h" +#include "lua_api/l_item.h" +#include "lua_api/l_itemstackmeta.h" +#include "lua_api/l_mapgen.h" +#include "lua_api/l_nodemeta.h" +#include "lua_api/l_nodetimer.h" +#include "lua_api/l_noise.h" +#include "lua_api/l_object.h" +#include "lua_api/l_particles.h" +#include "lua_api/l_rollback.h" +#include "lua_api/l_server.h" +#include "lua_api/l_util.h" +#include "lua_api/l_vmanip.h" +#include "lua_api/l_settings.h" +#include "lua_api/l_http.h" +#include "lua_api/l_storage.h" + +extern "C" { +#include "lualib.h" +} + +ServerScripting::ServerScripting(Server* server) +{ + setGameDef(server); + + // setEnv(env) is called by ScriptApiEnv::initializeEnvironment() + // once the environment has been created + + SCRIPTAPI_PRECHECKHEADER + + if (g_settings->getBool("secure.enable_security")) { + initializeSecurity(); + } + + lua_getglobal(L, "core"); + int top = lua_gettop(L); + + lua_newtable(L); + lua_setfield(L, -2, "object_refs"); + + lua_newtable(L); + lua_setfield(L, -2, "luaentities"); + + // Initialize our lua_api modules + InitializeModApi(L, top); + lua_pop(L, 1); + + // Push builtin initialization type + lua_pushstring(L, "game"); + lua_setglobal(L, "INIT"); + + infostream << "SCRIPTAPI: Initialized game modules" << std::endl; +} + +void ServerScripting::InitializeModApi(lua_State *L, int top) +{ + // Initialize mod api modules + ModApiCraft::Initialize(L, top); + ModApiEnvMod::Initialize(L, top); + ModApiInventory::Initialize(L, top); + ModApiItemMod::Initialize(L, top); + ModApiMapgen::Initialize(L, top); + ModApiParticles::Initialize(L, top); + ModApiRollback::Initialize(L, top); + ModApiServer::Initialize(L, top); + ModApiUtil::Initialize(L, top); + ModApiHttp::Initialize(L, top); + ModApiStorage::Initialize(L, top); + + // Register reference classes (userdata) + InvRef::Register(L); + ItemStackMetaRef::Register(L); + LuaAreaStore::Register(L); + LuaItemStack::Register(L); + LuaPerlinNoise::Register(L); + LuaPerlinNoiseMap::Register(L); + LuaPseudoRandom::Register(L); + LuaPcgRandom::Register(L); + LuaSecureRandom::Register(L); + LuaVoxelManip::Register(L); + NodeMetaRef::Register(L); + NodeTimerRef::Register(L); + ObjectRef::Register(L); + LuaSettings::Register(L); + StorageRef::Register(L); +} + +void log_deprecated(const std::string &message) +{ + log_deprecated(NULL, message); +} diff --git a/src/script/scripting_server.h b/src/script/scripting_server.h new file mode 100644 index 000000000..fd97ea40b --- /dev/null +++ b/src/script/scripting_server.h @@ -0,0 +1,57 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef SERVER_SCRIPTING_H_ +#define SERVER_SCRIPTING_H_ + +#include "cpp_api/s_base.h" +#include "cpp_api/s_entity.h" +#include "cpp_api/s_env.h" +#include "cpp_api/s_inventory.h" +#include "cpp_api/s_node.h" +#include "cpp_api/s_player.h" +#include "cpp_api/s_server.h" +#include "cpp_api/s_security.h" + +/*****************************************************************************/ +/* Scripting <-> Server Game Interface */ +/*****************************************************************************/ + +class ServerScripting: + virtual public ScriptApiBase, + public ScriptApiDetached, + public ScriptApiEntity, + public ScriptApiEnv, + public ScriptApiNode, + public ScriptApiPlayer, + public ScriptApiServer, + public ScriptApiSecurity +{ +public: + ServerScripting(Server* server); + + // use ScriptApiBase::loadMod() to load mods + +private: + void InitializeModApi(lua_State *L, int top); +}; + +void log_deprecated(const std::string &message); + +#endif /* SCRIPTING_GAME_H_ */ diff --git a/src/script/serverscripting.cpp b/src/script/serverscripting.cpp deleted file mode 100644 index 215b2cfd7..000000000 --- a/src/script/serverscripting.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "serverscripting.h" -#include "server.h" -#include "log.h" -#include "settings.h" -#include "cpp_api/s_internal.h" -#include "lua_api/l_areastore.h" -#include "lua_api/l_base.h" -#include "lua_api/l_craft.h" -#include "lua_api/l_env.h" -#include "lua_api/l_inventory.h" -#include "lua_api/l_item.h" -#include "lua_api/l_itemstackmeta.h" -#include "lua_api/l_mapgen.h" -#include "lua_api/l_nodemeta.h" -#include "lua_api/l_nodetimer.h" -#include "lua_api/l_noise.h" -#include "lua_api/l_object.h" -#include "lua_api/l_particles.h" -#include "lua_api/l_rollback.h" -#include "lua_api/l_server.h" -#include "lua_api/l_util.h" -#include "lua_api/l_vmanip.h" -#include "lua_api/l_settings.h" -#include "lua_api/l_http.h" -#include "lua_api/l_storage.h" - -extern "C" { -#include "lualib.h" -} - -ServerScripting::ServerScripting(Server* server) -{ - setGameDef(server); - - // setEnv(env) is called by ScriptApiEnv::initializeEnvironment() - // once the environment has been created - - SCRIPTAPI_PRECHECKHEADER - - if (g_settings->getBool("secure.enable_security")) { - initializeSecurity(); - } - - lua_getglobal(L, "core"); - int top = lua_gettop(L); - - lua_newtable(L); - lua_setfield(L, -2, "object_refs"); - - lua_newtable(L); - lua_setfield(L, -2, "luaentities"); - - // Initialize our lua_api modules - InitializeModApi(L, top); - lua_pop(L, 1); - - // Push builtin initialization type - lua_pushstring(L, "game"); - lua_setglobal(L, "INIT"); - - infostream << "SCRIPTAPI: Initialized game modules" << std::endl; -} - -void ServerScripting::InitializeModApi(lua_State *L, int top) -{ - // Initialize mod api modules - ModApiCraft::Initialize(L, top); - ModApiEnvMod::Initialize(L, top); - ModApiInventory::Initialize(L, top); - ModApiItemMod::Initialize(L, top); - ModApiMapgen::Initialize(L, top); - ModApiParticles::Initialize(L, top); - ModApiRollback::Initialize(L, top); - ModApiServer::Initialize(L, top); - ModApiUtil::Initialize(L, top); - ModApiHttp::Initialize(L, top); - ModApiStorage::Initialize(L, top); - - // Register reference classes (userdata) - InvRef::Register(L); - ItemStackMetaRef::Register(L); - LuaAreaStore::Register(L); - LuaItemStack::Register(L); - LuaPerlinNoise::Register(L); - LuaPerlinNoiseMap::Register(L); - LuaPseudoRandom::Register(L); - LuaPcgRandom::Register(L); - LuaSecureRandom::Register(L); - LuaVoxelManip::Register(L); - NodeMetaRef::Register(L); - NodeTimerRef::Register(L); - ObjectRef::Register(L); - LuaSettings::Register(L); - StorageRef::Register(L); -} - -void log_deprecated(const std::string &message) -{ - log_deprecated(NULL, message); -} diff --git a/src/script/serverscripting.h b/src/script/serverscripting.h deleted file mode 100644 index fd97ea40b..000000000 --- a/src/script/serverscripting.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef SERVER_SCRIPTING_H_ -#define SERVER_SCRIPTING_H_ - -#include "cpp_api/s_base.h" -#include "cpp_api/s_entity.h" -#include "cpp_api/s_env.h" -#include "cpp_api/s_inventory.h" -#include "cpp_api/s_node.h" -#include "cpp_api/s_player.h" -#include "cpp_api/s_server.h" -#include "cpp_api/s_security.h" - -/*****************************************************************************/ -/* Scripting <-> Server Game Interface */ -/*****************************************************************************/ - -class ServerScripting: - virtual public ScriptApiBase, - public ScriptApiDetached, - public ScriptApiEntity, - public ScriptApiEnv, - public ScriptApiNode, - public ScriptApiPlayer, - public ScriptApiServer, - public ScriptApiSecurity -{ -public: - ServerScripting(Server* server); - - // use ScriptApiBase::loadMod() to load mods - -private: - void InitializeModApi(lua_State *L, int top); -}; - -void log_deprecated(const std::string &message); - -#endif /* SCRIPTING_GAME_H_ */ diff --git a/src/server.cpp b/src/server.cpp index 4c7e60286..2edf83947 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "profiler.h" #include "log.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "nodedef.h" #include "itemdef.h" #include "craftdef.h" diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index c0dc0e0ea..4d2c87a4d 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "raycast.h" #include "remoteplayer.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "server.h" #include "voxelalgorithms.h" #include "util/serialize.h" -- cgit v1.2.3 From 674400523e10624ae8320b0d18a05dbfd6dcac42 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Fri, 19 May 2017 07:45:47 +0200 Subject: Mainmenu: Fix issues while trying to enable all mods (#5770) --- builtin/mainmenu/dlg_config_world.lua | 24 +++++++++++++----------- builtin/mainmenu/modmgr.lua | 4 +++- src/client.cpp | 2 +- src/server.cpp | 2 +- src/serverenvironment.cpp | 2 +- 5 files changed, 19 insertions(+), 15 deletions(-) (limited to 'src/serverenvironment.cpp') diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/dlg_config_world.lua index 3e5ba16eb..fcedadea8 100644 --- a/builtin/mainmenu/dlg_config_world.lua +++ b/builtin/mainmenu/dlg_config_world.lua @@ -49,7 +49,7 @@ local function get_formspec(data) "button[3.25,7;2.5,0.5;btn_config_world_save;" .. fgettext("Save") .. "]" .. "button[5.75,7;2.5,0.5;btn_config_world_cancel;" .. fgettext("Cancel") .. "]" - if mod and mod.name ~= "" and mod.typ ~= "game_mod" then + if mod and mod.name ~= "" and not mod.is_game_content then if mod.is_modpack then local rawlist = data.list:get_raw_list() @@ -97,7 +97,7 @@ end local function enable_mod(this, toset) local mod = this.data.list:get_list()[this.data.selected_mod] - if mod.typ == "game_mod" then + if mod.is_game_content then -- game mods can't be enabled or disabled elseif not mod.is_modpack then if toset == nil then @@ -162,7 +162,7 @@ local function handle_buttons(this, fields) local i,mod for i,mod in ipairs(rawlist) do if not mod.is_modpack and - mod.typ ~= "game_mod" then + not mod.is_game_content then if modname_valid(mod.name) then worldfile:set("load_mod_"..mod.name, tostring(mod.enabled)) else @@ -198,7 +198,8 @@ local function handle_buttons(this, fields) local list = this.data.list:get_raw_list() for i = 1, #list do - if list[i].typ ~= "game_mod" and not list[i].is_modpack then + if not list[i].is_game_content + and not list[i].is_modpack then list[i].enabled = true end end @@ -210,7 +211,8 @@ local function handle_buttons(this, fields) local list = this.data.list:get_raw_list() for i = 1, #list do - if list[i].typ ~= "game_mod" and not list[i].is_modpack then + if not list[i].is_game_content + and not list[i].is_modpack then list[i].enabled = false end end @@ -252,16 +254,16 @@ function create_configure_world_dlg(worldidx) return true end end, - function(element,criteria) + function(element, criteria) if criteria.hide_game and - element.typ == "game_mod" then - return false + element.is_game_content then + return false end if criteria.hide_modpackcontents and - element.modpack ~= nil then - return false - end + element.modpack ~= nil then + return false + end return true end, --filter { worldpath= dlg.data.worldspec.path, diff --git a/builtin/mainmenu/modmgr.lua b/builtin/mainmenu/modmgr.lua index 14d29272e..dee048982 100644 --- a/builtin/mainmenu/modmgr.lua +++ b/builtin/mainmenu/modmgr.lua @@ -252,7 +252,7 @@ function modmgr.render_modlist(render_list) break end end - elseif v.typ == "game_mod" or v.typ == "game" then + elseif v.is_game_content then color = mt_color_blue elseif v.enabled then color = mt_color_green @@ -417,12 +417,14 @@ function modmgr.preparemodlist(data) -- Add title retval[#retval + 1] = { typ = "game", + is_game_content = true, name = fgettext("Subgame Mods") } end for i=1,#game_mods,1 do game_mods[i].typ = "game_mod" + game_mods[i].is_game_content = true retval[#retval + 1] = game_mods[i] end diff --git a/src/client.cpp b/src/client.cpp index 6ab4002a5..5f2d2d9d2 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -171,7 +171,7 @@ void Client::initMods() if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) { throw ModError("Error loading mod \"" + mod.name + "\": Mod name does not follow naming conventions: " - "Only chararacters [a-z0-9_] are allowed."); + "Only characters [a-z0-9_] are allowed."); } std::string script_path = mod.path + DIR_DELIM + "init.lua"; infostream << " [" << padStringRight(mod.name, 12) << "] [\"" diff --git a/src/server.cpp b/src/server.cpp index 5400dd595..6889451d9 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -283,7 +283,7 @@ Server::Server( if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) { throw ModError("Error loading mod \"" + mod.name + "\": Mod name does not follow naming conventions: " - "Only chararacters [a-z0-9_] are allowed."); + "Only characters [a-z0-9_] are allowed."); } std::string script_path = mod.path + DIR_DELIM + "init.lua"; infostream << " [" << padStringRight(mod.name, 12) << "] [\"" diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 4d2c87a4d..892c6c55b 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -141,7 +141,7 @@ void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def) if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) { throw ModError("Error adding LBM \"" + lbm_def->name + "\": Does not follow naming conventions: " - "Only chararacters [a-z0-9_:] are allowed."); + "Only characters [a-z0-9_:] are allowed."); } m_lbm_defs[lbm_def->name] = lbm_def; -- cgit v1.2.3 From 4d5ce8478c959a4ee5b13ef9ba7e46b28d089a21 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Thu, 25 May 2017 16:43:55 +0200 Subject: Enhance ABM performance a little bit by removing two std::set copy (#5815) * 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 --- src/script/lua_api/l_env.h | 4 ++-- src/serverenvironment.cpp | 30 ++++++++++++++---------------- src/serverenvironment.h | 5 ++--- 3 files changed, 18 insertions(+), 21 deletions(-) (limited to 'src/serverenvironment.cpp') diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 629c6bc39..7ce19b085 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -203,11 +203,11 @@ public: m_simple_catch_up(simple_catch_up) { } - virtual std::set getTriggerContents() + virtual const std::set &getTriggerContents() const { return m_trigger_contents; } - virtual std::set getRequiredNeighbors() + virtual const std::set &getRequiredNeighbors() const { return m_required_neighbors; } diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 892c6c55b..cbdc747d1 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -721,7 +721,7 @@ public: chance = 1; ActiveABM aabm; aabm.abm = abm; - if(abm->getSimpleCatchUp()) { + if (abm->getSimpleCatchUp()) { float intervals = actual_interval / trigger_interval; if(intervals == 0) continue; @@ -731,25 +731,23 @@ public: } else { aabm.chance = chance; } + // Trigger neighbors - std::set required_neighbors_s - = abm->getRequiredNeighbors(); - for(std::set::iterator - i = required_neighbors_s.begin(); - i != required_neighbors_s.end(); ++i) - { - ndef->getIds(*i, aabm.required_neighbors); + const std::set &required_neighbors_s = + abm->getRequiredNeighbors(); + for (std::set::iterator rn = required_neighbors_s.begin(); + rn != required_neighbors_s.end(); ++rn) { + ndef->getIds(*rn, aabm.required_neighbors); } + // Trigger contents - std::set contents_s = abm->getTriggerContents(); - for(std::set::iterator - i = contents_s.begin(); i != contents_s.end(); ++i) - { + const std::set &contents_s = abm->getTriggerContents(); + for (std::set::iterator cs = contents_s.begin(); + cs != contents_s.end(); ++cs) { std::set ids; - ndef->getIds(*i, ids); - for(std::set::const_iterator k = ids.begin(); - k != ids.end(); ++k) - { + ndef->getIds(*cs, ids); + for (std::set::const_iterator k = ids.begin(); + k != ids.end(); ++k) { content_t c = *k; if (c >= m_aabms.size()) m_aabms.resize(c + 256, NULL); diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 0e31aa41a..7c370fd54 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -51,11 +51,10 @@ public: virtual ~ActiveBlockModifier(){}; // Set of contents to trigger on - virtual std::set getTriggerContents()=0; + virtual const std::set &getTriggerContents() const = 0; // Set of required neighbors (trigger doesn't happen if none are found) // Empty = do not check neighbors - virtual std::set getRequiredNeighbors() - { return std::set(); } + virtual const std::set &getRequiredNeighbors() const = 0; // Trigger interval in seconds virtual float getTriggerInterval() = 0; // Random chance of (1 / return value), 0 is disallowed -- cgit v1.2.3