From 2f59a0c840e9101c87e2d59fabf34116b3328121 Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Sat, 10 Dec 2016 10:31:17 -0800 Subject: Process ABMs in a spherical volume instead of cubic Increase active_block_range default to a 3 mapblock radius. --- src/environment.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/environment.cpp') diff --git a/src/environment.cpp b/src/environment.cpp index 13c64b37c..3d4a2efc1 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -397,8 +397,11 @@ void fillRadiusBlock(v3s16 p0, s16 r, std::set &list) 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++) { - // Set in list - list.insert(p); + // limit to a sphere + if (p.getDistanceFrom(p0) <= r) { + // Set in list + list.insert(p); + } } } -- cgit v1.2.3 From 4d4b8bb8a46b6472d86fa848954dbc26b4fadb50 Mon Sep 17 00:00:00 2001 From: Rogier Date: Tue, 13 Dec 2016 23:16:26 +0100 Subject: Move PP() and PP2() macros to basic_macros.h Instead of redefining them everywhere. --- src/clientmap.cpp | 3 +-- src/content_abm.cpp | 2 -- src/content_cao.cpp | 3 +-- src/database.h | 5 +--- src/environment.cpp | 3 +-- src/inventorymanager.cpp | 3 +-- src/map.cpp | 3 +-- src/mapblock.cpp | 3 +-- src/object_properties.cpp | 4 +-- src/pathfinder.cpp | 64 ++++++++++++++++++++++------------------------ src/rollback_interface.cpp | 3 +-- src/server.h | 3 +-- src/util/basic_macros.h | 9 +++++++ 13 files changed, 50 insertions(+), 58 deletions(-) (limited to 'src/environment.cpp') diff --git a/src/clientmap.cpp b/src/clientmap.cpp index faa1461f6..27f9dea38 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -30,10 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "camera.h" // CameraModes #include "util/mathconstants.h" +#include "util/basic_macros.h" #include -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - ClientMap::ClientMap( Client *client, IGameDef *gamedef, diff --git a/src/content_abm.cpp b/src/content_abm.cpp index 8694ef981..ee444ae77 100644 --- a/src/content_abm.cpp +++ b/src/content_abm.cpp @@ -29,8 +29,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_game.h" #include "log.h" -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) { } diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 6b35d5881..a02d5168e 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" // For IntervalLimiter #include "util/serialize.h" #include "util/mathconstants.h" +#include "util/basic_macros.h" #include "client/tile.h" #include "environment.h" #include "collision.h" @@ -49,8 +50,6 @@ with this program; if not, write to the Free Software Foundation, Inc., class Settings; struct ToolCapabilities; -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - UNORDERED_MAP ClientActiveObject::m_types; SmoothTranslator::SmoothTranslator(): diff --git a/src/database.h b/src/database.h index 0cf75232f..459789306 100644 --- a/src/database.h +++ b/src/database.h @@ -24,10 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irr_v3d.h" #include "irrlichttypes.h" - -#ifndef PP - #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" -#endif +#include "util/basic_macros.h" class Database { diff --git a/src/environment.cpp b/src/environment.cpp index 3d4a2efc1..707d89659 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -44,10 +44,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "emerge.h" #include "util/serialize.h" +#include "util/basic_macros.h" #include "threading/mutex_auto_lock.h" -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" // A number that is much smaller than the timeout for particle spawners should/could ever be diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 3d8513492..f36a2fa4e 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -26,8 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "craftdef.h" #include "rollback_interface.h" #include "util/strfnd.h" - -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" +#include "util/basic_macros.h" #define PLAYER_TO_SA(p) p->getEnv()->getScriptIface() diff --git a/src/map.cpp b/src/map.cpp index 7bb8c4a13..c3b62ded0 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "util/directiontables.h" #include "util/mathconstants.h" +#include "util/basic_macros.h" #include "rollback_interface.h" #include "environment.h" #include "reflowscan.h" @@ -56,8 +57,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "database-postgresql.h" #endif -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - /* Map diff --git a/src/mapblock.cpp b/src/mapblock.cpp index f8747f52b..f8c3197bc 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -35,8 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif #include "util/string.h" #include "util/serialize.h" - -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" +#include "util/basic_macros.h" static const char *modified_reason_strings[] = { "initial", diff --git a/src/object_properties.cpp b/src/object_properties.cpp index 89ca26274..f4e4953ba 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -21,11 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_bloated.h" #include "exceptions.h" #include "util/serialize.h" +#include "util/basic_macros.h" #include -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" -#define PP2(x) "("<<(x).X<<","<<(x).Y<<")" - ObjectProperties::ObjectProperties(): hp_max(1), physical(false), diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp index 57a008ead..073670c6d 100644 --- a/src/pathfinder.cpp +++ b/src/pathfinder.cpp @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "log.h" #include "irr_aabb3d.h" +#include "util/basic_macros.h" //#define PATHFINDER_DEBUG //#define PATHFINDER_CALC_TIME @@ -47,9 +48,6 @@ with this program; if not, write to the Free Software Foundation, Inc., /* Typedefs and macros */ /******************************************************************************/ -/** shortcut to print a 3d pos */ -#define PPOS(pos) "(" << pos.X << "," << pos.Y << "," << pos.Z << ")" - #define LVL "(" << level << ")" << #ifdef PATHFINDER_DEBUG @@ -531,25 +529,25 @@ void GridNodeContainer::initNode(v3s16 ipos, PathGridnode *p_node) if ((current.param0 == CONTENT_IGNORE) || (below.param0 == CONTENT_IGNORE)) { - DEBUG_OUT("Pathfinder: " << PPOS(realpos) << + DEBUG_OUT("Pathfinder: " << PP(realpos) << " current or below is invalid element" << std::endl); if (current.param0 == CONTENT_IGNORE) { elem.type = 'i'; - DEBUG_OUT(PPOS(ipos) << ": " << 'i' << std::endl); + DEBUG_OUT(PP(ipos) << ": " << 'i' << std::endl); } return; } //don't add anything if it isn't an air node if (ndef->get(current).walkable || !ndef->get(below).walkable) { - DEBUG_OUT("Pathfinder: " << PPOS(realpos) + DEBUG_OUT("Pathfinder: " << PP(realpos) << " not on surface" << std::endl); if (ndef->get(current).walkable) { elem.type = 's'; - DEBUG_OUT(PPOS(ipos) << ": " << 's' << std::endl); + DEBUG_OUT(PP(ipos) << ": " << 's' << std::endl); } else { elem.type = '-'; - DEBUG_OUT(PPOS(ipos) << ": " << '-' << std::endl); + DEBUG_OUT(PP(ipos) << ": " << '-' << std::endl); } return; } @@ -557,7 +555,7 @@ void GridNodeContainer::initNode(v3s16 ipos, PathGridnode *p_node) elem.valid = true; elem.pos = realpos; elem.type = 'g'; - DEBUG_OUT(PPOS(ipos) << ": " << 'a' << std::endl); + DEBUG_OUT(PP(ipos) << ": " << 'a' << std::endl); if (m_pathf->m_prefetch) { elem.directions[DIR_XP] = m_pathf->calcCost(realpos, v3s16( 1, 0, 0)); @@ -686,14 +684,14 @@ std::vector Pathfinder::getPath(ServerEnvironment *env, if (!startpos.valid) { VERBOSE_TARGET << "invalid startpos" << - "Index: " << PPOS(StartIndex) << - "Realpos: " << PPOS(getRealPos(StartIndex)) << std::endl; + "Index: " << PP(StartIndex) << + "Realpos: " << PP(getRealPos(StartIndex)) << std::endl; return retval; } if (!endpos.valid) { VERBOSE_TARGET << "invalid stoppos" << - "Index: " << PPOS(EndIndex) << - "Realpos: " << PPOS(getRealPos(EndIndex)) << std::endl; + "Index: " << PP(EndIndex) << + "Realpos: " << PP(getRealPos(EndIndex)) << std::endl; return retval; } @@ -809,7 +807,7 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) //check limits if (!m_limits.isPointInside(pos2)) { - DEBUG_OUT("Pathfinder: " << PPOS(pos2) << + DEBUG_OUT("Pathfinder: " << PP(pos2) << " no cost -> out of limits" << std::endl); return retval; } @@ -819,7 +817,7 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) //did we get information about node? if (node_at_pos2.param0 == CONTENT_IGNORE ) { VERBOSE_TARGET << "Pathfinder: (1) area at pos: " - << PPOS(pos2) << " not loaded"; + << PP(pos2) << " not loaded"; return retval; } @@ -830,7 +828,7 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) //did we get information about node? if (node_below_pos2.param0 == CONTENT_IGNORE ) { VERBOSE_TARGET << "Pathfinder: (2) area at pos: " - << PPOS((pos2 + v3s16(0, -1, 0))) << " not loaded"; + << PP((pos2 + v3s16(0, -1, 0))) << " not loaded"; return retval; } @@ -838,7 +836,7 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) retval.valid = true; retval.value = 1; retval.direction = 0; - DEBUG_OUT("Pathfinder: "<< PPOS(pos) + DEBUG_OUT("Pathfinder: "<< PP(pos) << " cost same height found" << std::endl); } else { @@ -991,8 +989,8 @@ bool Pathfinder::updateAllCosts(v3s16 ipos, v3s16 ipos2 = ipos + directions[i]; if (!isValidIndex(ipos2)) { - DEBUG_OUT(LVL " Pathfinder: " << PPOS(ipos2) << - " out of range, max=" << PPOS(m_limits.MaxEdge) << std::endl); + DEBUG_OUT(LVL " Pathfinder: " << PP(ipos2) << + " out of range, max=" << PP(m_limits.MaxEdge) << std::endl); continue; } @@ -1000,7 +998,7 @@ bool Pathfinder::updateAllCosts(v3s16 ipos, if (!g_pos2.valid) { VERBOSE_TARGET << LVL "Pathfinder: no data for new position: " - << PPOS(ipos2) << std::endl; + << PP(ipos2) << std::endl; continue; } @@ -1017,7 +1015,7 @@ bool Pathfinder::updateAllCosts(v3s16 ipos, if ((g_pos2.totalcost < 0) || (g_pos2.totalcost > new_cost)) { DEBUG_OUT(LVL "Pathfinder: updating path at: "<< - PPOS(ipos2) << " from: " << g_pos2.totalcost << " to "<< + PP(ipos2) << " from: " << g_pos2.totalcost << " to "<< new_cost << std::endl); if (updateAllCosts(ipos2, invert(directions[i]), new_cost, level)) { @@ -1027,13 +1025,13 @@ bool Pathfinder::updateAllCosts(v3s16 ipos, else { DEBUG_OUT(LVL "Pathfinder:" " already found shorter path to: " - << PPOS(ipos2) << std::endl); + << PP(ipos2) << std::endl); } } else { DEBUG_OUT(LVL "Pathfinder:" " not moving to invalid direction: " - << PPOS(directions[i]) << std::endl); + << PP(directions[i]) << std::endl); } } } @@ -1147,8 +1145,8 @@ bool Pathfinder::updateCostHeuristic( v3s16 ipos, v3s16 ipos2 = ipos + direction; if (!isValidIndex(ipos2)) { - DEBUG_OUT(LVL " Pathfinder: " << PPOS(ipos2) << - " out of range, max=" << PPOS(m_limits.MaxEdge) << std::endl); + DEBUG_OUT(LVL " Pathfinder: " << PP(ipos2) << + " out of range, max=" << PP(m_limits.MaxEdge) << std::endl); direction = getDirHeuristic(directions, g_pos); continue; } @@ -1157,7 +1155,7 @@ bool Pathfinder::updateCostHeuristic( v3s16 ipos, if (!g_pos2.valid) { VERBOSE_TARGET << LVL "Pathfinder: no data for new position: " - << PPOS(ipos2) << std::endl; + << PP(ipos2) << std::endl; direction = getDirHeuristic(directions, g_pos); continue; } @@ -1171,16 +1169,16 @@ bool Pathfinder::updateCostHeuristic( v3s16 ipos, (m_min_target_distance < new_cost)) { DEBUG_OUT(LVL "Pathfinder:" " already longer than best already found path " - << PPOS(ipos2) << std::endl); + << PP(ipos2) << std::endl); return false; } if ((g_pos2.totalcost < 0) || (g_pos2.totalcost > new_cost)) { DEBUG_OUT(LVL "Pathfinder: updating path at: "<< - PPOS(ipos2) << " from: " << g_pos2.totalcost << " to "<< + PP(ipos2) << " from: " << g_pos2.totalcost << " to "<< new_cost << " srcdir=" << - PPOS(invert(direction))<< std::endl); + PP(invert(direction))<< std::endl); if (updateCostHeuristic(ipos2, invert(direction), new_cost, level)) { retval = true; @@ -1189,19 +1187,19 @@ bool Pathfinder::updateCostHeuristic( v3s16 ipos, else { DEBUG_OUT(LVL "Pathfinder:" " already found shorter path to: " - << PPOS(ipos2) << std::endl); + << PP(ipos2) << std::endl); } } else { DEBUG_OUT(LVL "Pathfinder:" " not moving to invalid direction: " - << PPOS(direction) << std::endl); + << PP(direction) << std::endl); } } else { DEBUG_OUT(LVL "Pathfinder:" " skipping srcdir: " - << PPOS(direction) << std::endl); + << PP(direction) << std::endl); } direction = getDirHeuristic(directions, g_pos); } @@ -1409,7 +1407,7 @@ void Pathfinder::printPath(std::vector path) unsigned int current = 0; for (std::vector::iterator i = path.begin(); i != path.end(); ++i) { - std::cout << std::setw(3) << current << ":" << PPOS((*i)) << std::endl; + std::cout << std::setw(3) << current << ":" << PP((*i)) << std::endl; current++; } } diff --git a/src/rollback_interface.cpp b/src/rollback_interface.cpp index bffe0a82c..7345c4a7d 100644 --- a/src/rollback_interface.cpp +++ b/src/rollback_interface.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/serialize.h" #include "util/string.h" #include "util/numeric.h" +#include "util/basic_macros.h" #include "map.h" #include "gamedef.h" #include "nodedef.h" @@ -32,8 +33,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventory.h" #include "mapblock.h" -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef) { diff --git a/src/server.h b/src/server.h index 4425d139b..cab7e2445 100644 --- a/src/server.h +++ b/src/server.h @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "subgame.h" #include "util/numeric.h" #include "util/thread.h" +#include "util/basic_macros.h" #include "environment.h" #include "chat_interface.h" #include "clientiface.h" @@ -41,8 +42,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - class IWritableItemDefManager; class IWritableNodeDefManager; class IWritableCraftDefManager; diff --git a/src/util/basic_macros.h b/src/util/basic_macros.h index c100b4f25..bd4b890eb 100644 --- a/src/util/basic_macros.h +++ b/src/util/basic_macros.h @@ -50,4 +50,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #define STATIC_ASSERT(expr, msg) \ UNUSED_ATTRIBUTE typedef char msg[!!(expr) * 2 - 1] +// Macros to facilitate writing position vectors to a stream +// Usage: +// v3s16 pos(1,2,3); +// mystream << "message " << PP(pos) << std::endl; + +#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" + +#define PP2(x) "("<<(x).X<<","<<(x).Y<<")" + #endif -- cgit v1.2.3 From 52ba1f867e5edb579a59a44fbb8286d4f1e54931 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 1 Jan 2017 16:13:01 +0100 Subject: Breath cheat fix: server side Breath is now handled server side. Changing this behaviour required some modifications to core: * Ignore TOSERVER_BREATH package, marking it as obsolete * Clients doesn't send the breath to server anymore * Use PlayerSAO pointer instead of peer_id in Server::SendPlayerBreath to prevent a useless lookup (little perf gain) * drop a useless static_cast in emergePlayer --- src/client.cpp | 11 +++-- src/content_sao.cpp | 39 +++++++++++++++-- src/content_sao.h | 7 +++- src/environment.cpp | 84 ++++++++++++++++++------------------- src/network/clientopcodes.cpp | 2 +- src/network/networkprotocol.h | 6 ++- src/network/serveropcodes.cpp | 2 +- src/network/serverpackethandler.cpp | 40 ------------------ src/remoteplayer.cpp | 2 +- src/script/lua_api/l_object.cpp | 5 --- src/server.cpp | 15 +++---- src/server.h | 3 +- src/unittest/test_player.cpp | 4 +- 13 files changed, 107 insertions(+), 113 deletions(-) (limited to 'src/environment.cpp') diff --git a/src/client.cpp b/src/client.cpp index 5476aad0e..1446ebad8 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -499,9 +499,10 @@ void Client::step(float dtime) m_client_event_queue.push(event); } } - else if(event.type == CEE_PLAYER_BREATH) { - u16 breath = event.player_breath.amount; - sendBreath(breath); + // Protocol v29 or greater obsoleted this event + else if (event.type == CEE_PLAYER_BREATH && m_proto_ver < 29) { + u16 breath = event.player_breath.amount; + sendBreath(breath); } } @@ -1270,6 +1271,10 @@ void Client::sendBreath(u16 breath) { DSTACK(FUNCTION_NAME); + // Protocol v29 make this obsolete + if (m_proto_ver >= 29) + return; + NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16)); pkt << breath; Send(&pkt); diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 77ab51a02..f866d4372 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" // For compressZlib #include "tool.h" // For ToolCapabilities #include "gamedef.h" +#include "nodedef.h" #include "remoteplayer.h" #include "server.h" #include "scripting_game.h" @@ -940,8 +941,35 @@ bool PlayerSAO::isAttached() void PlayerSAO::step(float dtime, bool send_recommended) { - if(!m_properties_sent) - { + if (m_drowning_interval.step(dtime, 2.0)) { + // 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); + // If node generates drown + if (c.drowning > 0) { + if (m_hp > 0 && m_breath > 0) + setBreath(m_breath - 1); + + // No more breath, damage player + if (m_breath == 0) { + setHP(m_hp - c.drowning); + ((Server*) m_env->getGameDef())->SendPlayerHPOrDie(this); + } + } + } + + if (m_breathing_interval.step(dtime, 0.5)) { + // 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); + // If player is alive & no drowning, breath + if (m_hp > 0 && c.drowning == 0) + setBreath(m_breath + 1); + } + + if (!m_properties_sent) { m_properties_sent = true; std::string str = getPropertyPacket(); // create message and add to list @@ -1237,12 +1265,15 @@ void PlayerSAO::setHP(s16 hp) m_properties_sent = false; } -void PlayerSAO::setBreath(const u16 breath) +void PlayerSAO::setBreath(const u16 breath, bool send) { if (m_player && breath != m_breath) m_player->setDirty(true); - m_breath = breath; + m_breath = MYMIN(breath, PLAYER_MAX_BREATH); + + if (send) + ((Server *) m_env->getGameDef())->SendPlayerBreath(this); } void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups) diff --git a/src/content_sao.h b/src/content_sao.h index 86255183d..9c66068b3 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef CONTENT_SAO_HEADER #define CONTENT_SAO_HEADER +#include #include "serverobject.h" #include "itemgroup.h" #include "object_properties.h" @@ -232,7 +233,7 @@ public: void setHPRaw(s16 hp) { m_hp = hp; } s16 readDamage(); u16 getBreath() const { return m_breath; } - void setBreath(const u16 breath); + void setBreath(const u16 breath, bool send = true); void setArmorGroups(const ItemGroupList &armor_groups); ItemGroupList getArmorGroups(); void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); @@ -339,6 +340,10 @@ private: v3s16 m_nocheat_dig_pos; float m_nocheat_dig_time; + // Timers + IntervalLimiter m_breathing_interval; + IntervalLimiter m_drowning_interval; + int m_wield_index; bool m_position_not_sent; ItemGroupList m_armor_groups; diff --git a/src/environment.cpp b/src/environment.cpp index 707d89659..ac9b5b079 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -2511,51 +2511,51 @@ void ClientEnvironment::step(float dtime) } } - /* - Drowning - */ - if(m_drowning_interval.step(dtime, 2.0)) - { - v3f pf = lplayer->getPosition(); - - // 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); - u8 drowning_damage = c.drowning; - if(drowning_damage > 0 && lplayer->hp > 0){ - u16 breath = lplayer->getBreath(); - if(breath > 10){ - breath = 11; - } - if(breath > 0){ - breath -= 1; + // Protocol v29 make this behaviour obsolete + if (((Client*) getGameDef())->getProtoVersion() < 29) { + /* + Drowning + */ + if (m_drowning_interval.step(dtime, 2.0)) { + v3f pf = lplayer->getPosition(); + + // 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); + u8 drowning_damage = c.drowning; + if (drowning_damage > 0 && lplayer->hp > 0) { + u16 breath = lplayer->getBreath(); + if (breath > 10) { + breath = 11; + } + if (breath > 0) { + breath -= 1; + } + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); } - lplayer->setBreath(breath); - updateLocalPlayerBreath(breath); - } - if(lplayer->getBreath() == 0 && drowning_damage > 0){ - damageLocalPlayer(drowning_damage, true); + if (lplayer->getBreath() == 0 && drowning_damage > 0) { + damageLocalPlayer(drowning_damage, true); + } } - } - if(m_breathing_interval.step(dtime, 0.5)) - { - v3f pf = lplayer->getPosition(); - - // 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); - if (!lplayer->hp){ - lplayer->setBreath(11); - } - else if(c.drowning == 0){ - u16 breath = lplayer->getBreath(); - if(breath <= 10){ - breath += 1; - lplayer->setBreath(breath); - updateLocalPlayerBreath(breath); + if (m_breathing_interval.step(dtime, 0.5)) { + v3f pf = lplayer->getPosition(); + + // 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); + if (!lplayer->hp) { + lplayer->setBreath(11); + } else if (c.drowning == 0) { + u16 breath = lplayer->getBreath(); + if (breath <= 10) { + breath += 1; + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } } } } diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 3364de8c5..6defdcf1b 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -193,7 +193,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = null_command_factory, // 0x3f { "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40 { "TOSERVER_RECEIVED_MEDIA", 1, true }, // 0x41 - { "TOSERVER_BREATH", 0, true }, // 0x42 + null_command_factory, // 0x42 old TOSERVER_BREATH. Ignored by servers { "TOSERVER_CLIENT_READY", 0, true }, // 0x43 null_command_factory, // 0x44 null_command_factory, // 0x45 diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 018b392b6..f65167380 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -138,9 +138,11 @@ with this program; if not, write to the Free Software Foundation, Inc., Add nodedef v3 - connected nodeboxes PROTOCOL_VERSION 28: CPT2_MESHOPTIONS + PROTOCOL_VERSION 29: + Server doesn't accept TOSERVER_BREATH anymore */ -#define LATEST_PROTOCOL_VERSION 28 +#define LATEST_PROTOCOL_VERSION 29 // Server's supported network protocol range #define SERVER_PROTOCOL_VERSION_MIN 13 @@ -833,7 +835,7 @@ enum ToServerCommand */ - TOSERVER_BREATH = 0x42, + TOSERVER_BREATH = 0x42, // Obsolete /* u16 breath */ diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 9b14a1be3..642dd376a 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -90,7 +90,7 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] = null_command_handler, // 0x3f { "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40 { "TOSERVER_RECEIVED_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_ReceivedMedia }, // 0x41 - { "TOSERVER_BREATH", TOSERVER_STATE_INGAME, &Server::handleCommand_Breath }, // 0x42 + { "TOSERVER_BREATH", TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x42 Old breath model which is now deprecated for anticheating { "TOSERVER_CLIENT_READY", TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43 null_command_handler, // 0x44 null_command_handler, // 0x45 diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index d0f4d948d..eeabcca71 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -1136,46 +1136,6 @@ void Server::handleCommand_Damage(NetworkPacket* pkt) } } -void Server::handleCommand_Breath(NetworkPacket* pkt) -{ - u16 breath; - - *pkt >> breath; - - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); - - if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - m_con.DisconnectPeer(pkt->getPeerId()); - return; - } - - - PlayerSAO *playersao = player->getPlayerSAO(); - if (playersao == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player object for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - m_con.DisconnectPeer(pkt->getPeerId()); - return; - } - - /* - * If player is dead, we don't need to update the breath - * He is dead ! - */ - if (playersao->isDead()) { - verbosestream << "TOSERVER_BREATH: " << player->getName() - << " is dead. Ignoring packet"; - return; - } - - playersao->setBreath(breath); - SendPlayerBreath(pkt->getPeerId()); -} - void Server::handleCommand_Password(NetworkPacket* pkt) { if (pkt->getSize() != PASSWORD_SIZE * 2) diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 67ab89113..18bfa1030 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -148,7 +148,7 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername, } catch (SettingNotFoundException &e) {} try { - sao->setBreath(args.getS32("breath")); + sao->setBreath(args.getS32("breath"), false); } catch (SettingNotFoundException &e) {} } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 2a8b8a64e..cfdceb28e 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1152,13 +1152,8 @@ int ObjectRef::l_set_breath(lua_State *L) PlayerSAO* co = getplayersao(ref); if (co == NULL) return 0; u16 breath = luaL_checknumber(L, 2); - // Do it co->setBreath(breath); - // If the object is a player sent the breath to client - if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) - getServer(L)->SendPlayerBreath(((PlayerSAO*)co)->getPeerID()); - return 0; } diff --git a/src/server.cpp b/src/server.cpp index fa7a838d4..60dbef0d2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1076,8 +1076,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id) } m_clients.unlock(); - RemotePlayer *player = - static_cast(m_env->getPlayer(playername.c_str())); + RemotePlayer *player = m_env->getPlayer(playername.c_str()); // If failed, cancel if ((playersao == NULL) || (player == NULL)) { @@ -1113,7 +1112,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id) SendPlayerHPOrDie(playersao); // Send Breath - SendPlayerBreath(peer_id); + SendPlayerBreath(playersao); // Show death screen if necessary if (playersao->isDead()) @@ -1857,14 +1856,13 @@ void Server::SendPlayerHP(u16 peer_id) playersao->m_messages_out.push(aom); } -void Server::SendPlayerBreath(u16 peer_id) +void Server::SendPlayerBreath(PlayerSAO *sao) { DSTACK(FUNCTION_NAME); - PlayerSAO *playersao = getPlayerSAO(peer_id); - assert(playersao); + assert(sao); - m_script->player_event(playersao, "breath_changed"); - SendBreath(peer_id, playersao->getBreath()); + m_script->player_event(sao, "breath_changed"); + SendBreath(sao->getPeerID(), sao->getBreath()); } void Server::SendMovePlayer(u16 peer_id) @@ -2565,7 +2563,6 @@ void Server::RespawnPlayer(u16 peer_id) } SendPlayerHP(peer_id); - SendPlayerBreath(peer_id); } diff --git a/src/server.h b/src/server.h index cab7e2445..f0df0f9ec 100644 --- a/src/server.h +++ b/src/server.h @@ -180,7 +180,6 @@ public: void handleCommand_InventoryAction(NetworkPacket* pkt); void handleCommand_ChatMessage(NetworkPacket* pkt); void handleCommand_Damage(NetworkPacket* pkt); - void handleCommand_Breath(NetworkPacket* pkt); void handleCommand_Password(NetworkPacket* pkt); void handleCommand_PlayerItem(NetworkPacket* pkt); void handleCommand_Respawn(NetworkPacket* pkt); @@ -358,7 +357,7 @@ public: void printToConsoleOnly(const std::string &text); void SendPlayerHPOrDie(PlayerSAO *player); - void SendPlayerBreath(u16 peer_id); + void SendPlayerBreath(PlayerSAO *sao); void SendInventory(PlayerSAO* playerSAO); void SendMovePlayer(u16 peer_id); diff --git a/src/unittest/test_player.cpp b/src/unittest/test_player.cpp index 85fbc8b2d..655ee08fd 100644 --- a/src/unittest/test_player.cpp +++ b/src/unittest/test_player.cpp @@ -49,7 +49,7 @@ void TestPlayer::testSave(IGameDef *gamedef) PlayerSAO sao(NULL, 1, false); sao.initialize(&rplayer, std::set()); rplayer.setPlayerSAO(&sao); - sao.setBreath(10); + sao.setBreath(10, false); sao.setHPRaw(8); sao.setYaw(0.1f); sao.setPitch(0.6f); @@ -64,7 +64,7 @@ void TestPlayer::testLoad(IGameDef *gamedef) PlayerSAO sao(NULL, 1, false); sao.initialize(&rplayer, std::set()); rplayer.setPlayerSAO(&sao); - sao.setBreath(10); + sao.setBreath(10, false); sao.setHPRaw(8); sao.setYaw(0.1f); sao.setPitch(0.6f); -- cgit v1.2.3 From 3f8261830e0503cd59d8713d5c9aab12fc1491db Mon Sep 17 00:00:00 2001 From: Dániel Juhász Date: Wed, 4 Jan 2017 19:18:40 +0100 Subject: Improve getPointedThing() (#4346) * Improved getPointedThing() The new algorithm checks every node exactly once. Now the point and normal vector of the collision is also returned in the PointedThing (currently they are not used outside of the function). Now the CNodeDefManager keeps the union of all possible nodeboxes, so the raycast won't miss any nodes. Also if there are only small nodeboxes, getPointedThing() is exceptionally fast. Also adds unit test for VoxelLineIterator. * Cleanup, code move This commit moves getPointedThing() and Client::getSelectedActiveObject() to ClientEnvironment. The map nodes now can decide which neighbors they are connecting to (MapNode::getNeighbors()). --- src/CMakeLists.txt | 1 + src/client.cpp | 39 +--- src/client.h | 8 - src/clientmap.cpp | 33 ++- src/environment.cpp | 241 +++++++++++++++++++++ src/environment.h | 36 ++++ src/game.cpp | 392 ++++++++++------------------------ src/map.cpp | 91 ++++---- src/map.h | 5 + src/mapnode.cpp | 46 ++++ src/mapnode.h | 8 + src/nodedef.cpp | 141 ++++++++++++ src/nodedef.h | 6 + src/raycast.cpp | 89 ++++++++ src/raycast.h | 38 ++++ src/unittest/test_voxelalgorithms.cpp | 59 +++++ src/util/pointedthing.cpp | 75 +++---- src/util/pointedthing.h | 40 ++++ src/voxelalgorithms.cpp | 68 ++++++ src/voxelalgorithms.h | 61 ++++++ 20 files changed, 1045 insertions(+), 432 deletions(-) create mode 100644 src/raycast.cpp create mode 100644 src/raycast.h (limited to 'src/environment.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51cf88063..507624753 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -447,6 +447,7 @@ set(common_SRCS quicktune.cpp reflowscan.cpp remoteplayer.cpp + raycast.cpp rollback.cpp rollback_interface.cpp serialization.cpp diff --git a/src/client.cpp b/src/client.cpp index 1446ebad8..693a90604 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -53,6 +53,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "database-sqlite3.h" #include "serialization.h" #include "guiscalingfilter.h" +#include "raycast.h" extern gui::IGUIEnvironment* guienv; @@ -1500,44 +1501,6 @@ void Client::inventoryAction(InventoryAction *a) delete a; } -ClientActiveObject * Client::getSelectedActiveObject( - f32 max_d, - v3f from_pos_f_on_map, - core::line3d shootline_on_map - ) -{ - std::vector objects; - - m_env.getActiveObjects(from_pos_f_on_map, max_d, objects); - - // Sort them. - // After this, the closest object is the first in the array. - std::sort(objects.begin(), objects.end()); - - for(unsigned int i=0; igetSelectionBox(); - if(selection_box == NULL) - continue; - - v3f pos = obj->getPosition(); - - aabb3f offsetted_box( - selection_box->MinEdge + pos, - selection_box->MaxEdge + pos - ); - - if(offsetted_box.intersectsWithLine(shootline_on_map)) - { - return obj; - } - } - - return NULL; -} - float Client::getAnimationTime() { return m_animation_time; diff --git a/src/client.h b/src/client.h index 9f5bda059..891fe62f8 100644 --- a/src/client.h +++ b/src/client.h @@ -445,14 +445,6 @@ public: Inventory* getInventory(const InventoryLocation &loc); void inventoryAction(InventoryAction *a); - // Gets closest object pointed by the shootline - // Returns NULL if not found - ClientActiveObject * getSelectedActiveObject( - f32 max_d, - v3f from_pos_f_on_map, - core::line3d shootline_on_map - ); - const std::list &getConnectedPlayerNames() { return m_env.getPlayerNames(); diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 7d76e6e8b..542eb03e8 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -172,8 +172,6 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG); g_profiler->add("CM::updateDrawList() count", 1); - INodeDefManager *nodemgr = m_gamedef->ndef(); - for (std::map::iterator i = m_drawlist.begin(); i != m_drawlist.end(); ++i) { MapBlock *block = i->second; @@ -219,7 +217,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) if (g_settings->getBool("free_move")) { MapNode n = getNodeNoEx(cam_pos_nodes); if (n.getContent() == CONTENT_IGNORE || - nodemgr->get(n).solidness == 2) + m_nodedef->get(n).solidness == 2) occlusion_culling_enabled = false; } @@ -297,23 +295,23 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) if (occlusion_culling_enabled && // For the central point of the mapblock 'endoff' can be halved isOccluded(this, spn, cpn, - step, stepfac, startoff, endoff / 2.0f, needed_count, nodemgr) && + step, stepfac, startoff, endoff / 2.0f, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr)) { + step, stepfac, startoff, endoff, needed_count, m_nodedef)) { blocks_occlusion_culled++; continue; } @@ -656,7 +654,6 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, int oldvalue, bool *sunlight_seen_result) { const bool debugprint = false; - INodeDefManager *ndef = m_gamedef->ndef(); static v3f z_directions[50] = { v3f(-100, 0, 0) }; @@ -694,7 +691,7 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, float off = step * z_offsets[i]; bool sunlight_seen_now = false; bool ok = getVisibleBrightness(this, m_camera_position, dir, - step, 1.0, max_d*0.6+off, max_d, ndef, daylight_factor, + step, 1.0, max_d*0.6+off, max_d, m_nodedef, daylight_factor, sunlight_min_d, &br, &sunlight_seen_now); if(sunlight_seen_now) @@ -734,8 +731,8 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, int ret = 0; if(brightness_count == 0){ MapNode n = getNodeNoEx(floatToInt(m_camera_position, BS)); - if(ndef->get(n).param_type == CPT_LIGHT){ - ret = decode_light(n.getLightBlend(daylight_factor, ndef)); + if(m_nodedef->get(n).param_type == CPT_LIGHT){ + ret = decode_light(n.getLightBlend(daylight_factor, m_nodedef)); } else { ret = oldvalue; } @@ -758,8 +755,6 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, void ClientMap::renderPostFx(CameraMode cam_mode) { - INodeDefManager *nodemgr = m_gamedef->ndef(); - // Sadly ISceneManager has no "post effects" render pass, in that case we // could just register for that and handle it in renderMap(). @@ -768,7 +763,7 @@ void ClientMap::renderPostFx(CameraMode cam_mode) // - If the player is in a solid node, make everything black. // - If the player is in liquid, draw a semi-transparent overlay. // - Do not if player is in third person mode - const ContentFeatures& features = nodemgr->get(n); + 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")) && diff --git a/src/environment.cpp b/src/environment.cpp index ac9b5b079..8c0daf87b 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -43,8 +43,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "daynightratio.h" #include "map.h" #include "emerge.h" +#include "raycast.h" +#include "voxelalgorithms.h" #include "util/serialize.h" #include "util/basic_macros.h" +#include "util/pointedthing.h" #include "threading/mutex_auto_lock.h" #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" @@ -2848,4 +2851,242 @@ ClientEnvEvent ClientEnvironment::getClientEvent() return event; } +ClientActiveObject * ClientEnvironment::getSelectedActiveObject( + const core::line3d &shootline_on_map, v3f *intersection_point, + v3s16 *intersection_normal) +{ + std::vector objects; + getActiveObjects(shootline_on_map.start, + shootline_on_map.getLength() + 3, objects); + const v3f line_vector = shootline_on_map.getVector(); + + // Sort them. + // After this, the closest object is the first in the array. + std::sort(objects.begin(), objects.end()); + + /* Because objects can have different nodebox sizes, + * the object whose center is the nearest isn't necessarily + * the closest one. If an object is found, don't stop + * immediately. */ + + f32 d_min = shootline_on_map.getLength(); + ClientActiveObject *nearest_obj = NULL; + for (u32 i = 0; i < objects.size(); i++) { + ClientActiveObject *obj = objects[i].obj; + + aabb3f *selection_box = obj->getSelectionBox(); + if (selection_box == NULL) + continue; + + v3f pos = obj->getPosition(); + + aabb3f offsetted_box(selection_box->MinEdge + pos, + selection_box->MaxEdge + pos); + + if (offsetted_box.getCenter().getDistanceFrom( + shootline_on_map.start) > d_min + 9.6f*BS) { + // Probably there is no active object that has bigger nodebox than + // (-5.5,-5.5,-5.5,5.5,5.5,5.5) + // 9.6 > 5.5*sqrt(3) + break; + } + + v3f current_intersection; + v3s16 current_normal; + if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector, + ¤t_intersection, ¤t_normal)) { + f32 d_current = current_intersection.getDistanceFrom( + shootline_on_map.start); + if (d_current <= d_min) { + d_min = d_current; + nearest_obj = obj; + *intersection_point = current_intersection; + *intersection_normal = current_normal; + } + } + } + + return nearest_obj; +} + +/* + Check if a node is pointable +*/ +static inline bool isPointableNode(const MapNode &n, + INodeDefManager *ndef, bool liquids_pointable) +{ + const ContentFeatures &features = ndef->get(n); + return features.pointable || + (liquids_pointable && features.isLiquid()); +} + +PointedThing ClientEnvironment::getPointedThing( + core::line3d shootline, + bool liquids_pointable, + bool look_for_object) +{ + PointedThing result; + + INodeDefManager *nodedef = m_map->getNodeDefManager(); + + core::aabbox3d maximal_exceed = nodedef->getSelectionBoxIntUnion(); + // The code needs to search these nodes + core::aabbox3d search_range(-maximal_exceed.MaxEdge, + -maximal_exceed.MinEdge); + // If a node is found, there might be a larger node behind. + // To find it, we have to go further. + s16 maximal_overcheck = + std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X)) + + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y)) + + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z)); + + const v3f original_vector = shootline.getVector(); + const f32 original_length = original_vector.getLength(); + + f32 min_distance = original_length; + + // First try to find an active object + if (look_for_object) { + ClientActiveObject *selected_object = getSelectedActiveObject( + shootline, &result.intersection_point, + &result.intersection_normal); + + if (selected_object != NULL) { + min_distance = + (result.intersection_point - shootline.start).getLength(); + + result.type = POINTEDTHING_OBJECT; + result.object_id = selected_object->getId(); + } + } + + // Reduce shootline + if (original_length > 0) { + shootline.end = shootline.start + + shootline.getVector() / original_length * min_distance; + } + + // Try to find a node that is closer than the selected active + // object (if it exists). + + voxalgo::VoxelLineIterator iterator(shootline.start / BS, + shootline.getVector() / BS); + v3s16 oldnode = iterator.m_current_node_pos; + // Indicates that a node was found. + bool is_node_found = false; + // If a node is found, it is possible that there's a node + // behind it with a large nodebox, so continue the search. + u16 node_foundcounter = 0; + // If a node is found, this is the center of the + // first nodebox the shootline meets. + v3f found_boxcenter(0, 0, 0); + // The untested nodes are in this range. + core::aabbox3d new_nodes; + while (true) { + // Test the nodes around the current node in search_range. + new_nodes = search_range; + new_nodes.MinEdge += iterator.m_current_node_pos; + new_nodes.MaxEdge += iterator.m_current_node_pos; + + // Only check new nodes + v3s16 delta = iterator.m_current_node_pos - oldnode; + if (delta.X > 0) + new_nodes.MinEdge.X = new_nodes.MaxEdge.X; + else if (delta.X < 0) + new_nodes.MaxEdge.X = new_nodes.MinEdge.X; + else if (delta.Y > 0) + new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y; + else if (delta.Y < 0) + new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y; + else if (delta.Z > 0) + new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z; + else if (delta.Z < 0) + new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z; + + // For each untested node + for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) { + for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) { + for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) { + MapNode n; + v3s16 np(x, y, z); + bool is_valid_position; + + n = m_map->getNodeNoEx(np, &is_valid_position); + if (!(is_valid_position && + isPointableNode(n, nodedef, liquids_pointable))) { + continue; + } + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes, + n.getNeighbors(np, m_map)); + + v3f npf = intToFloat(np, BS); + for (std::vector::const_iterator i = boxes.begin(); + i != boxes.end(); ++i) { + aabb3f box = *i; + box.MinEdge += npf; + box.MaxEdge += npf; + v3f intersection_point; + v3s16 intersection_normal; + if (!boxLineCollision(box, shootline.start, shootline.getVector(), + &intersection_point, &intersection_normal)) { + continue; + } + f32 distance = (intersection_point - shootline.start).getLength(); + if (distance >= min_distance) { + continue; + } + result.type = POINTEDTHING_NODE; + result.node_undersurface = np; + result.intersection_point = intersection_point; + result.intersection_normal = intersection_normal; + found_boxcenter = box.getCenter(); + min_distance = distance; + is_node_found = true; + } + } + } + } + if (is_node_found) { + node_foundcounter++; + if (node_foundcounter > maximal_overcheck) { + break; + } + } + // Next node + if (iterator.hasNext()) { + oldnode = iterator.m_current_node_pos; + iterator.next(); + } else { + break; + } + } + + if (is_node_found) { + // Set undersurface and abovesurface nodes + f32 d = 0.002 * BS; + v3f fake_intersection = result.intersection_point; + // Move intersection towards its source block. + if (fake_intersection.X < found_boxcenter.X) + fake_intersection.X += d; + else + fake_intersection.X -= d; + + if (fake_intersection.Y < found_boxcenter.Y) + fake_intersection.Y += d; + else + fake_intersection.Y -= d; + + if (fake_intersection.Z < found_boxcenter.Z) + fake_intersection.Z += d; + else + fake_intersection.Z -= d; + + result.node_real_undersurface = floatToInt(fake_intersection, BS); + result.node_abovesurface = result.node_real_undersurface + + result.intersection_normal; + } + return result; +} + #endif // #ifndef SERVER diff --git a/src/environment.h b/src/environment.h index 4bee40e57..84805b462 100644 --- a/src/environment.h +++ b/src/environment.h @@ -55,6 +55,7 @@ class GameScripting; class Player; class RemotePlayer; class PlayerSAO; +class PointedThing; class Environment { @@ -627,6 +628,41 @@ public: // Get event from queue. CEE_NONE is returned if queue is empty. ClientEnvEvent getClientEvent(); + /*! + * Gets closest object pointed by the shootline. + * Returns NULL if not found. + * + * \param[in] shootline_on_map the shootline for + * the test in world coordinates + * \param[out] intersection_point the first point where + * the shootline meets the object. Valid only if + * not NULL is returned. + * \param[out] intersection_normal the normal vector of + * the intersection, pointing outwards. Zero vector if + * the shootline starts in an active object. + * Valid only if not NULL is returned. + */ + ClientActiveObject * getSelectedActiveObject( + const core::line3d &shootline_on_map, + v3f *intersection_point, + v3s16 *intersection_normal + ); + + /*! + * Performs a raycast on the world. + * Returns the first thing the shootline meets. + * + * @param[in] shootline the shootline, starting from + * the camera position. This also gives the maximal distance + * of the search. + * @param[in] liquids_pointable if false, liquids are ignored + * @param[in] look_for_object if false, objects are ignored + */ + PointedThing getPointedThing( + core::line3d shootline, + bool liquids_pointable, + bool look_for_object); + u16 attachement_parent_ids[USHRT_MAX + 1]; const std::list &getPlayerNames() { return m_player_names; } diff --git a/src/game.cpp b/src/game.cpp index 5bdbea617..cfa6234ff 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -265,271 +265,6 @@ public: Client *m_client; }; -/* - Check if a node is pointable -*/ -inline bool isPointableNode(const MapNode &n, - Client *client, bool liquids_pointable) -{ - const ContentFeatures &features = client->getNodeDefManager()->get(n); - return features.pointable || - (liquids_pointable && features.isLiquid()); -} - -static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, - ClientMap *map, MapNode n, u8 bitmask, u8 *neighbors) -{ - MapNode n2 = map->getNodeNoEx(p); - if (nodedef->nodeboxConnects(n, n2, bitmask)) - *neighbors |= bitmask; -} - -static inline u8 getNeighbors(v3s16 p, INodeDefManager *nodedef, ClientMap *map, MapNode n) -{ - u8 neighbors = 0; - const ContentFeatures &f = nodedef->get(n); - // locate possible neighboring nodes to connect to - if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) { - v3s16 p2 = p; - - p2.Y++; - getNeighborConnectingFace(p2, nodedef, map, n, 1, &neighbors); - - p2 = p; - p2.Y--; - getNeighborConnectingFace(p2, nodedef, map, n, 2, &neighbors); - - p2 = p; - p2.Z--; - getNeighborConnectingFace(p2, nodedef, map, n, 4, &neighbors); - - p2 = p; - p2.X--; - getNeighborConnectingFace(p2, nodedef, map, n, 8, &neighbors); - - p2 = p; - p2.Z++; - getNeighborConnectingFace(p2, nodedef, map, n, 16, &neighbors); - - p2 = p; - p2.X++; - getNeighborConnectingFace(p2, nodedef, map, n, 32, &neighbors); - } - - return neighbors; -} - -/* - Find what the player is pointing at -*/ -PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_position, - const v3f &camera_direction, const v3f &camera_position, - core::line3d shootline, f32 d, bool liquids_pointable, - bool look_for_object, const v3s16 &camera_offset, - ClientActiveObject *&selected_object) -{ - PointedThing result; - - std::vector *selectionboxes = hud->getSelectionBoxes(); - selectionboxes->clear(); - static const bool show_entity_selectionbox = g_settings->getBool("show_entity_selectionbox"); - - selected_object = NULL; - - INodeDefManager *nodedef = client->getNodeDefManager(); - ClientMap &map = client->getEnv().getClientMap(); - - f32 min_distance = BS * 1001; - - // First try to find a pointed at active object - if (look_for_object) { - selected_object = client->getSelectedActiveObject(d * BS, - camera_position, shootline); - - if (selected_object != NULL) { - if (show_entity_selectionbox && - selected_object->doShowSelectionBox()) { - aabb3f *selection_box = selected_object->getSelectionBox(); - // Box should exist because object was - // returned in the first place - assert(selection_box); - - v3f pos = selected_object->getPosition(); - selectionboxes->push_back(aabb3f( - selection_box->MinEdge, selection_box->MaxEdge)); - hud->setSelectionPos(pos, camera_offset); - } - - min_distance = (selected_object->getPosition() - camera_position).getLength(); - - hud->setSelectedFaceNormal(v3f(0.0, 0.0, 0.0)); - result.type = POINTEDTHING_OBJECT; - result.object_id = selected_object->getId(); - } - } - - // That didn't work, try to find a pointed at node - - v3s16 pos_i = floatToInt(player_position, BS); - - /*infostream<<"pos_i=("< 0 ? a : 1); - s16 zend = pos_i.Z + (camera_direction.Z > 0 ? a : 1); - s16 xend = pos_i.X + (camera_direction.X > 0 ? a : 1); - - // Prevent signed number overflow - if (yend == 32767) - yend = 32766; - - if (zend == 32767) - zend = 32766; - - if (xend == 32767) - xend = 32766; - - v3s16 pointed_pos(0, 0, 0); - - for (s16 y = ystart; y <= yend; y++) { - for (s16 z = zstart; z <= zend; z++) { - for (s16 x = xstart; x <= xend; x++) { - MapNode n; - bool is_valid_position; - v3s16 p(x, y, z); - - n = map.getNodeNoEx(p, &is_valid_position); - if (!is_valid_position) { - continue; - } - if (!isPointableNode(n, client, liquids_pointable)) { - continue; - } - - std::vector boxes; - n.getSelectionBoxes(nodedef, &boxes, getNeighbors(p, nodedef, &map, n)); - - v3s16 np(x, y, z); - v3f npf = intToFloat(np, BS); - for (std::vector::const_iterator - i = boxes.begin(); - i != boxes.end(); ++i) { - aabb3f box = *i; - box.MinEdge += npf; - box.MaxEdge += npf; - - v3f centerpoint = box.getCenter(); - f32 distance = (centerpoint - camera_position).getLength(); - if (distance >= min_distance) { - continue; - } - if (!box.intersectsWithLine(shootline)) { - continue; - } - result.type = POINTEDTHING_NODE; - min_distance = distance; - pointed_pos = np; - } - } - } - } - - if (result.type == POINTEDTHING_NODE) { - f32 d = 0.001 * BS; - MapNode n = map.getNodeNoEx(pointed_pos); - v3f npf = intToFloat(pointed_pos, BS); - std::vector boxes; - n.getSelectionBoxes(nodedef, &boxes, getNeighbors(pointed_pos, nodedef, &map, n)); - f32 face_min_distance = 1000 * BS; - for (std::vector::const_iterator - i = boxes.begin(); - i != boxes.end(); ++i) { - aabb3f box = *i; - box.MinEdge += npf; - box.MaxEdge += npf; - for (u16 j = 0; j < 6; j++) { - v3s16 facedir = g_6dirs[j]; - aabb3f facebox = box; - if (facedir.X > 0) { - facebox.MinEdge.X = facebox.MaxEdge.X - d; - } else if (facedir.X < 0) { - facebox.MaxEdge.X = facebox.MinEdge.X + d; - } else if (facedir.Y > 0) { - facebox.MinEdge.Y = facebox.MaxEdge.Y - d; - } else if (facedir.Y < 0) { - facebox.MaxEdge.Y = facebox.MinEdge.Y + d; - } else if (facedir.Z > 0) { - facebox.MinEdge.Z = facebox.MaxEdge.Z - d; - } else if (facedir.Z < 0) { - facebox.MaxEdge.Z = facebox.MinEdge.Z + d; - } - v3f centerpoint = facebox.getCenter(); - f32 distance = (centerpoint - camera_position).getLength(); - if (distance >= face_min_distance) - continue; - if (!facebox.intersectsWithLine(shootline)) - continue; - result.node_abovesurface = pointed_pos + facedir; - hud->setSelectedFaceNormal(v3f(facedir.X, facedir.Y, facedir.Z)); - face_min_distance = distance; - } - } - selectionboxes->clear(); - for (std::vector::const_iterator - i = boxes.begin(); - i != boxes.end(); ++i) { - aabb3f box = *i; - box.MinEdge += v3f(-d, -d, -d); - box.MaxEdge += v3f(d, d, d); - selectionboxes->push_back(box); - } - hud->setSelectionPos(intToFloat(pointed_pos, BS), camera_offset); - result.node_undersurface = pointed_pos; - } - - // Update selection mesh light level and vertex colors - if (selectionboxes->size() > 0) { - v3f pf = hud->getSelectionPos(); - v3s16 p = floatToInt(pf, BS); - - // Get selection mesh light level - MapNode n = map.getNodeNoEx(p); - u16 node_light = getInteriorLight(n, -1, nodedef); - u16 light_level = node_light; - - for (u8 i = 0; i < 6; i++) { - n = map.getNodeNoEx(p + g_6dirs[i]); - node_light = getInteriorLight(n, -1, nodedef); - if (node_light > light_level) - light_level = node_light; - } - - video::SColor c = MapBlock_LightColor(255, light_level, 0); - u8 day = c.getRed(); - u8 night = c.getGreen(); - u32 daynight_ratio = client->getEnv().getDayNightRatio(); - finalColorBlend(c, day, night, daynight_ratio); - - // Modify final color a bit with time - u32 timer = porting::getTimeMs() % 5000; - float timerf = (float)(irr::core::PI * ((timer / 2500.0) - 0.5)); - float sin_r = 0.08 * sin(timerf); - float sin_g = 0.08 * sin(timerf + irr::core::PI * 0.5); - float sin_b = 0.08 * sin(timerf + irr::core::PI); - c.setRed(core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255)); - c.setGreen(core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255)); - c.setBlue(core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255)); - - // Set mesh final color - hud->setSelectionMeshColor(c); - } - return result; -} - /* Profiler display */ void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe, @@ -1668,6 +1403,23 @@ protected: void updateSound(f32 dtime); void processPlayerInteraction(GameRunData *runData, f32 dtime, bool show_hud, bool show_debug); + /*! + * Returns the object or node the player is pointing at. + * Also updates the selected thing in the Hud. + * + * @param[in] shootline the shootline, starting from + * the camera position. This also gives the maximal distance + * of the search. + * @param[in] liquids_pointable if false, liquids are ignored + * @param[in] look_for_object if false, objects are ignored + * @param[in] camera_offset offset of the camera + * @param[out] selected_object the selected object or + * NULL if not found + */ + PointedThing updatePointedThing( + const core::line3d &shootline, bool liquids_pointable, + bool look_for_object, const v3s16 &camera_offset, + ClientActiveObject *&selected_object); void handlePointingAtNothing(GameRunData *runData, const ItemStack &playerItem); void handlePointingAtNode(GameRunData *runData, const PointedThing &pointed, const ItemDefinition &playeritem_def, @@ -3823,14 +3575,11 @@ void Game::processPlayerInteraction(GameRunData *runData, core::line3d shootline; if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) { - shootline = core::line3d(camera_position, - camera_position + camera_direction * BS * (d + 1)); - + camera_position + camera_direction * BS * d); } else { // prevent player pointing anything in front-view - if (camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT) - shootline = core::line3d(0, 0, 0, 0, 0, 0); + shootline = core::line3d(camera_position,camera_position); } #ifdef HAVE_TOUCHSCREENGUI @@ -3843,10 +3592,7 @@ void Game::processPlayerInteraction(GameRunData *runData, #endif - PointedThing pointed = getPointedThing( - // input - client, hud, player_position, camera_direction, - camera_position, shootline, d, + PointedThing pointed = updatePointedThing(shootline, playeritem_def.liquids_pointable, !runData->ldown_for_dig, camera_offset, @@ -3940,6 +3686,104 @@ void Game::processPlayerInteraction(GameRunData *runData, } +PointedThing Game::updatePointedThing( + const core::line3d &shootline, + bool liquids_pointable, + bool look_for_object, + const v3s16 &camera_offset, + ClientActiveObject *&selected_object) +{ + std::vector *selectionboxes = hud->getSelectionBoxes(); + selectionboxes->clear(); + static const bool show_entity_selectionbox = g_settings->getBool( + "show_entity_selectionbox"); + + ClientMap &map = client->getEnv().getClientMap(); + INodeDefManager *nodedef=client->getNodeDefManager(); + + selected_object = NULL; + + PointedThing result=client->getEnv().getPointedThing( + shootline, liquids_pointable, look_for_object); + if (result.type == POINTEDTHING_OBJECT) { + selected_object = client->getEnv().getActiveObject(result.object_id); + if (show_entity_selectionbox && selected_object->doShowSelectionBox()) { + aabb3f *selection_box = selected_object->getSelectionBox(); + + // Box should exist because object was + // returned in the first place + + assert(selection_box); + + v3f pos = selected_object->getPosition(); + selectionboxes->push_back(aabb3f( + selection_box->MinEdge, selection_box->MaxEdge)); + selectionboxes->push_back( + aabb3f(selection_box->MinEdge, selection_box->MaxEdge)); + hud->setSelectionPos(pos, camera_offset); + } + } else if (result.type == POINTEDTHING_NODE) { + // Update selection boxes + MapNode n = map.getNodeNoEx(result.node_undersurface); + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes, + n.getNeighbors(result.node_undersurface, &map)); + + f32 d = 0.002 * BS; + for (std::vector::const_iterator i = boxes.begin(); + i != boxes.end(); ++i) { + aabb3f box = *i; + box.MinEdge -= v3f(d, d, d); + box.MaxEdge += v3f(d, d, d); + selectionboxes->push_back(box); + } + hud->setSelectionPos(intToFloat(result.node_undersurface, BS), + camera_offset); + } + + // Update selection mesh light level and vertex colors + if (selectionboxes->size() > 0) { + v3f pf = hud->getSelectionPos(); + v3s16 p = floatToInt(pf, BS); + + // Get selection mesh light level + MapNode n = map.getNodeNoEx(p); + u16 node_light = getInteriorLight(n, -1, nodedef); + u16 light_level = node_light; + + for (u8 i = 0; i < 6; i++) { + n = map.getNodeNoEx(p + g_6dirs[i]); + node_light = getInteriorLight(n, -1, nodedef); + if (node_light > light_level) + light_level = node_light; + } + + video::SColor c = MapBlock_LightColor(255, light_level, 0); + u8 day = c.getRed(); + u8 night = c.getGreen(); + u32 daynight_ratio = client->getEnv().getDayNightRatio(); + finalColorBlend(c, day, night, daynight_ratio); + + // Modify final color a bit with time + u32 timer = porting::getTimeMs() % 5000; + float timerf = (float) (irr::core::PI * ((timer / 2500.0) - 0.5)); + float sin_r = 0.08 * sin(timerf); + float sin_g = 0.08 * sin(timerf + irr::core::PI * 0.5); + float sin_b = 0.08 * sin(timerf + irr::core::PI); + c.setRed( + core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255)); + c.setGreen( + core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255)); + c.setBlue( + core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255)); + + // Set mesh final color + hud->setSelectionMeshColor(c); + } + return result; +} + + void Game::handlePointingAtNothing(GameRunData *runData, const ItemStack &playerItem) { infostream << "Right Clicked in Air" << std::endl; diff --git a/src/map.cpp b/src/map.cpp index c3b62ded0..53657ce6b 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -66,6 +66,7 @@ Map::Map(std::ostream &dout, IGameDef *gamedef): m_dout(dout), m_gamedef(gamedef), m_sector_cache(NULL), + m_nodedef(gamedef->ndef()), m_transforming_liquid_loop_count_multiplier(1.0f), m_unprocessed_count(0), m_inc_trending_up_start_time(0), @@ -227,7 +228,7 @@ void Map::setNode(v3s16 p, MapNode & n) bool temp_bool; errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE" <<" while trying to replace \"" - <ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name + <get(block->getNodeNoCheck(relpos, &temp_bool)).name <<"\" at "< & light_sources, std::map & modified_blocks) { - INodeDefManager *nodemgr = m_gamedef->ndef(); - v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top @@ -353,20 +352,20 @@ void Map::unspreadLight(enum LightBank bank, If the neighbor is dimmer than what was specified as oldlight (the light of the previous node) */ - if(n2.getLight(bank, nodemgr) < oldlight) + if(n2.getLight(bank, m_nodedef) < oldlight) { /* And the neighbor is transparent and it has some light */ - if(nodemgr->get(n2).light_propagates - && n2.getLight(bank, nodemgr) != 0) + if(m_nodedef->get(n2).light_propagates + && n2.getLight(bank, m_nodedef) != 0) { /* Set light to 0 and add to queue */ - u8 current_light = n2.getLight(bank, nodemgr); - n2.setLight(bank, 0, nodemgr); + u8 current_light = n2.getLight(bank, m_nodedef); + n2.setLight(bank, 0, m_nodedef); block->setNode(relpos, n2); unlighted_nodes[n2pos] = current_light; @@ -421,8 +420,6 @@ void Map::spreadLight(enum LightBank bank, std::set & from_nodes, std::map & modified_blocks) { - INodeDefManager *nodemgr = m_gamedef->ndef(); - const v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top @@ -476,7 +473,7 @@ void Map::spreadLight(enum LightBank bank, bool is_valid_position; MapNode n = block->getNode(relpos, &is_valid_position); - u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0; + u8 oldlight = is_valid_position ? n.getLight(bank, m_nodedef) : 0; u8 newlight = diminish_light(oldlight); // Loop through 6 neighbors @@ -512,7 +509,7 @@ void Map::spreadLight(enum LightBank bank, If the neighbor is brighter than the current node, add to list (it will light up this node on its turn) */ - if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight)) + if(n2.getLight(bank, m_nodedef) > undiminish_light(oldlight)) { lighted_nodes.insert(n2pos); changed = true; @@ -521,11 +518,11 @@ void Map::spreadLight(enum LightBank bank, If the neighbor is dimmer than how much light this node would spread on it, add to list */ - if(n2.getLight(bank, nodemgr) < newlight) + if(n2.getLight(bank, m_nodedef) < newlight) { - if(nodemgr->get(n2).light_propagates) + if(m_nodedef->get(n2).light_propagates) { - n2.setLight(bank, newlight, nodemgr); + n2.setLight(bank, newlight, m_nodedef); block->setNode(relpos, n2); lighted_nodes.insert(n2pos); changed = true; @@ -558,8 +555,6 @@ void Map::updateLighting(enum LightBank bank, std::map & a_blocks, std::map & modified_blocks) { - INodeDefManager *nodemgr = m_gamedef->ndef(); - /*m_dout<<"Map::updateLighting(): " <setNode(p, n); // If node sources light, add to list - u8 source = nodemgr->get(n).light_source; + u8 source = m_nodedef->get(n).light_source; if(source != 0) light_sources.insert(p + posnodes); @@ -810,8 +805,6 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, std::map &modified_blocks, bool remove_metadata) { - INodeDefManager *ndef = m_gamedef->ndef(); - // Collect old node for rollback RollbackNode rollback_oldnode(this, p, m_gamedef); @@ -825,14 +818,14 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, // Set the node on the map // Ignore light (because calling voxalgo::update_lighting_nodes) - n.setLight(LIGHTBANK_DAY, 0, ndef); - n.setLight(LIGHTBANK_NIGHT, 0, ndef); + n.setLight(LIGHTBANK_DAY, 0, m_nodedef); + n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef); setNode(p, n); // Update lighting std::vector > oldnodes; oldnodes.push_back(std::pair(p, oldnode)); - voxalgo::update_lighting_nodes(this, ndef, oldnodes, modified_blocks); + voxalgo::update_lighting_nodes(this, m_nodedef, oldnodes, modified_blocks); for(std::map::iterator i = modified_blocks.begin(); @@ -869,11 +862,10 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, bool is_valid_position; MapNode n2 = getNodeNoEx(p2, &is_valid_position); - if(is_valid_position - && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)) - { + if(is_valid_position && + (m_nodedef->get(n2).isLiquid() || + n2.getContent() == CONTENT_AIR)) m_transforming_liquid.push_back(p2); - } } } @@ -1213,9 +1205,6 @@ s32 Map::transforming_liquid_size() { void Map::transformLiquids(std::map &modified_blocks) { - - INodeDefManager *nodemgr = m_gamedef->ndef(); - DSTACK(FUNCTION_NAME); //TimeTaker timer("transformLiquids()"); @@ -1275,12 +1264,12 @@ void Map::transformLiquids(std::map &modified_blocks) // The node which will be placed there if liquid // can't flow into this node. content_t floodable_node = CONTENT_AIR; - const ContentFeatures &cf = nodemgr->get(n0); + const ContentFeatures &cf = m_nodedef->get(n0); LiquidType liquid_type = cf.liquid_type; switch (liquid_type) { case LIQUID_SOURCE: liquid_level = LIQUID_LEVEL_SOURCE; - liquid_kind = nodemgr->getId(cf.liquid_alternative_flowing); + liquid_kind = m_nodedef->getId(cf.liquid_alternative_flowing); break; case LIQUID_FLOWING: liquid_level = (n0.param2 & LIQUID_LEVEL_MASK); @@ -1322,8 +1311,8 @@ void Map::transformLiquids(std::map &modified_blocks) } v3s16 npos = p0 + dirs[i]; NodeNeighbor nb(getNodeNoEx(npos), nt, npos); - const ContentFeatures &cfnb = nodemgr->get(nb.n); - switch (nodemgr->get(nb.n.getContent()).liquid_type) { + const ContentFeatures &cfnb = m_nodedef->get(nb.n); + switch (m_nodedef->get(nb.n.getContent()).liquid_type) { case LIQUID_NONE: if (cfnb.floodable) { airs[num_airs++] = nb; @@ -1351,8 +1340,8 @@ void Map::transformLiquids(std::map &modified_blocks) case LIQUID_SOURCE: // if this node is not (yet) of a liquid type, choose the first liquid type we encounter if (liquid_kind == CONTENT_AIR) - liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing); - if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { + liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing); + if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { neutrals[num_neutrals++] = nb; } else { // Do not count bottom source, it will screw things up @@ -1363,8 +1352,8 @@ void Map::transformLiquids(std::map &modified_blocks) case LIQUID_FLOWING: // if this node is not (yet) of a liquid type, choose the first liquid type we encounter if (liquid_kind == CONTENT_AIR) - liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing); - if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { + liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing); + if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { neutrals[num_neutrals++] = nb; } else { flows[num_flows++] = nb; @@ -1382,15 +1371,15 @@ void Map::transformLiquids(std::map &modified_blocks) s8 new_node_level = -1; s8 max_node_level = -1; - u8 range = nodemgr->get(liquid_kind).liquid_range; + u8 range = m_nodedef->get(liquid_kind).liquid_range; if (range > LIQUID_LEVEL_MAX + 1) range = LIQUID_LEVEL_MAX + 1; - if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) { + if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) { // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid) // or the flowing alternative of the first of the surrounding sources (if it's air), so // it's perfectly safe to use liquid_kind here to determine the new node content. - new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source); + new_node_content = m_nodedef->getId(m_nodedef->get(liquid_kind).liquid_alternative_source); } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) { // liquid_kind is set properly, see above max_node_level = new_node_level = LIQUID_LEVEL_MAX; @@ -1427,7 +1416,7 @@ void Map::transformLiquids(std::map &modified_blocks) } } - u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity; + u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity; if (viscosity > 1 && max_node_level != liquid_level) { // amount to gain, limited by viscosity // must be at least 1 in absolute value @@ -1455,7 +1444,7 @@ void Map::transformLiquids(std::map &modified_blocks) check if anything has changed. if not, just continue with the next node. */ if (new_node_content == n0.getContent() && - (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING || + (m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING || ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK) == flowing_down))) @@ -1467,7 +1456,7 @@ void Map::transformLiquids(std::map &modified_blocks) */ MapNode n00 = n0; //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK)); - if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) { + if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) { // set level to last 3 bits, flowing down bit to 4th bit n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK); } else { @@ -1477,8 +1466,8 @@ void Map::transformLiquids(std::map &modified_blocks) n0.setContent(new_node_content); // Ignore light (because calling voxalgo::update_lighting_nodes) - n0.setLight(LIGHTBANK_DAY, 0, nodemgr); - n0.setLight(LIGHTBANK_NIGHT, 0, nodemgr); + n0.setLight(LIGHTBANK_DAY, 0, m_nodedef); + n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef); // Find out whether there is a suspect for this action std::string suspect; @@ -1512,7 +1501,7 @@ void Map::transformLiquids(std::map &modified_blocks) /* enqueue neighbors for update if neccessary */ - switch (nodemgr->get(n0.getContent()).liquid_type) { + switch (m_nodedef->get(n0.getContent()).liquid_type) { case LIQUID_SOURCE: case LIQUID_FLOWING: // make sure source flows into all neighboring nodes @@ -1535,7 +1524,7 @@ void Map::transformLiquids(std::map &modified_blocks) for (std::deque::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter) m_transforming_liquid.push_back(*iter); - voxalgo::update_lighting_nodes(this, nodemgr, changed_nodes, modified_blocks); + voxalgo::update_lighting_nodes(this, m_nodedef, changed_nodes, modified_blocks); /* ---------------------------------------------------------------------- @@ -1900,7 +1889,7 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) data->blockpos_min = bpmin; data->blockpos_max = bpmax; data->blockpos_requested = blockpos; - data->nodedef = m_gamedef->ndef(); + data->nodedef = m_nodedef; /* Create the whole area of this and the neighboring blocks diff --git a/src/map.h b/src/map.h index e8d40e982..19c94ee80 100644 --- a/src/map.h +++ b/src/map.h @@ -193,6 +193,8 @@ public: virtual MapBlock * emergeBlock(v3s16 p, bool create_blank=true) { return getBlockNoCreateNoEx(p); } + inline INodeDefManager * getNodeDefManager() { return m_nodedef; } + // Returns InvalidPositionException if not found bool isNodeUnderground(v3s16 p); @@ -346,6 +348,9 @@ protected: // Queued transforming water nodes UniqueQueue m_transforming_liquid; + // This stores the properties of the nodes on the map. + INodeDefManager *m_nodedef; + private: f32 m_transforming_liquid_loop_count_multiplier; u32 m_unprocessed_count; diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 5efebf3d8..f1a7f3e61 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapnode.h" #include "porting.h" #include "nodedef.h" +#include "map.h" #include "content_mapnode.h" // For mapnode_translate_*_internal #include "serialization.h" // For ser_ver_supported #include "util/serialize.h" @@ -453,6 +454,51 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox, } } +static inline void getNeighborConnectingFace( + v3s16 p, INodeDefManager *nodedef, + Map *map, MapNode n, u8 bitmask, u8 *neighbors) +{ + MapNode n2 = map->getNodeNoEx(p); + if (nodedef->nodeboxConnects(n, n2, bitmask)) + *neighbors |= bitmask; +} + +u8 MapNode::getNeighbors(v3s16 p, Map *map) +{ + INodeDefManager *nodedef=map->getNodeDefManager(); + u8 neighbors = 0; + const ContentFeatures &f = nodedef->get(*this); + // locate possible neighboring nodes to connect to + if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) { + v3s16 p2 = p; + + p2.Y++; + getNeighborConnectingFace(p2, nodedef, map, *this, 1, &neighbors); + + p2 = p; + p2.Y--; + getNeighborConnectingFace(p2, nodedef, map, *this, 2, &neighbors); + + p2 = p; + p2.Z--; + getNeighborConnectingFace(p2, nodedef, map, *this, 4, &neighbors); + + p2 = p; + p2.X--; + getNeighborConnectingFace(p2, nodedef, map, *this, 8, &neighbors); + + p2 = p; + p2.Z++; + getNeighborConnectingFace(p2, nodedef, map, *this, 16, &neighbors); + + p2 = p; + p2.X++; + getNeighborConnectingFace(p2, nodedef, map, *this, 32, &neighbors); + } + + return neighbors; +} + void MapNode::getNodeBoxes(INodeDefManager *nodemgr, std::vector *boxes, u8 neighbors) { const ContentFeatures &f = nodemgr->get(*this); diff --git a/src/mapnode.h b/src/mapnode.h index 0bd61c554..a3c20e8ff 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include class INodeDefManager; +class Map; /* Naming scheme: @@ -246,6 +247,13 @@ struct MapNode void rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot); + /*! + * Checks which neighbors does this node connect to. + * + * \param p coordinates of the node + */ + u8 getNeighbors(v3s16 p, Map *map); + /* Gets list of node boxes (used for rendering (NDT_NODEBOX)) */ diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 92cdf738e..dbbdf95d2 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -790,9 +790,18 @@ public: virtual void resetNodeResolveState(); virtual void mapNodeboxConnections(); virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face); + virtual core::aabbox3d getSelectionBoxIntUnion() const + { + return m_selection_box_int_union; + } private: void addNameIdMapping(content_t i, std::string name); + /*! + * Recalculates m_selection_box_int_union based on + * m_selection_box_union. + */ + void fixSelectionBoxIntUnion(); // Features indexed by id std::vector m_content_features; @@ -819,6 +828,14 @@ private: // True when all nodes have been registered bool m_node_registration_complete; + + //! The union of all nodes' selection boxes. + aabb3f m_selection_box_union; + /*! + * The smallest box in node coordinates that + * contains all nodes' selection boxes. + */ + core::aabbox3d m_selection_box_int_union; }; @@ -849,6 +866,8 @@ void CNodeDefManager::clear() m_name_id_mapping_with_aliases.clear(); m_group_to_items.clear(); m_next_id = 0; + m_selection_box_union.reset(0,0,0); + m_selection_box_int_union.reset(0,0,0); resetNodeResolveState(); @@ -1007,6 +1026,123 @@ content_t CNodeDefManager::allocateId() } +/*! + * Returns the smallest box that contains all boxes + * in the vector. Box_union is expanded. + * @param[in] boxes the vector containing the boxes + * @param[in, out] box_union the union of the arguments + */ +void boxVectorUnion(const std::vector &boxes, aabb3f *box_union) +{ + for (std::vector::const_iterator it = boxes.begin(); + it != boxes.end(); ++it) { + box_union->addInternalBox(*it); + } +} + + +/*! + * Returns a box that contains the nodebox in every case. + * The argument node_union is expanded. + * @param[in] nodebox the nodebox to be measured + * @param[in] features used to decide whether the nodebox + * can be rotated + * @param[in, out] box_union the union of the arguments + */ +void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features, + aabb3f *box_union) +{ + switch(nodebox.type) { + case NODEBOX_FIXED: + case NODEBOX_LEVELED: { + // Raw union + aabb3f half_processed(0, 0, 0, 0, 0, 0); + boxVectorUnion(nodebox.fixed, &half_processed); + // Set leveled boxes to maximal + if (nodebox.type == NODEBOX_LEVELED) { + half_processed.MaxEdge.Y = +BS / 2; + } + if (features.param_type_2 == CPT2_FACEDIR) { + // Get maximal coordinate + f32 coords[] = { + fabsf(half_processed.MinEdge.X), + fabsf(half_processed.MinEdge.Y), + fabsf(half_processed.MinEdge.Z), + fabsf(half_processed.MaxEdge.X), + fabsf(half_processed.MaxEdge.Y), + fabsf(half_processed.MaxEdge.Z) }; + f32 max = 0; + for (int i = 0; i < 6; i++) { + if (max < coords[i]) { + max = coords[i]; + } + } + // Add the union of all possible rotated boxes + box_union->addInternalPoint(-max, -max, -max); + box_union->addInternalPoint(+max, +max, +max); + } else { + box_union->addInternalBox(half_processed); + } + break; + } + case NODEBOX_WALLMOUNTED: { + // Add fix boxes + box_union->addInternalBox(nodebox.wall_top); + box_union->addInternalBox(nodebox.wall_bottom); + // Find maximal coordinate in the X-Z plane + f32 coords[] = { + fabsf(nodebox.wall_side.MinEdge.X), + fabsf(nodebox.wall_side.MinEdge.Z), + fabsf(nodebox.wall_side.MaxEdge.X), + fabsf(nodebox.wall_side.MaxEdge.Z) }; + f32 max = 0; + for (int i = 0; i < 4; i++) { + if (max < coords[i]) { + max = coords[i]; + } + } + // Add the union of all possible rotated boxes + box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max); + box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max); + break; + } + case NODEBOX_CONNECTED: { + // Add all possible connected boxes + boxVectorUnion(nodebox.fixed, box_union); + boxVectorUnion(nodebox.connect_top, box_union); + boxVectorUnion(nodebox.connect_bottom, box_union); + boxVectorUnion(nodebox.connect_front, box_union); + boxVectorUnion(nodebox.connect_left, box_union); + boxVectorUnion(nodebox.connect_back, box_union); + boxVectorUnion(nodebox.connect_right, box_union); + break; + } + default: { + // NODEBOX_REGULAR + box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2); + box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2); + } + } +} + + +inline void CNodeDefManager::fixSelectionBoxIntUnion() +{ + m_selection_box_int_union.MinEdge.X = floorf( + m_selection_box_union.MinEdge.X / BS + 0.5f); + m_selection_box_int_union.MinEdge.Y = floorf( + m_selection_box_union.MinEdge.Y / BS + 0.5f); + m_selection_box_int_union.MinEdge.Z = floorf( + m_selection_box_union.MinEdge.Z / BS + 0.5f); + m_selection_box_int_union.MaxEdge.X = ceilf( + m_selection_box_union.MaxEdge.X / BS - 0.5f); + m_selection_box_int_union.MaxEdge.Y = ceilf( + m_selection_box_union.MaxEdge.Y / BS - 0.5f); + m_selection_box_int_union.MaxEdge.Z = ceilf( + m_selection_box_union.MaxEdge.Z / BS - 0.5f); +} + + // IWritableNodeDefManager content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def) { @@ -1037,6 +1173,8 @@ content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &d verbosestream << "NodeDefManager: registering content id \"" << id << "\": name=\"" << def.name << "\""< getSelectionBoxIntUnion() const=0; }; class IWritableNodeDefManager : public INodeDefManager { @@ -406,6 +411,7 @@ public: virtual void runNodeResolveCallbacks()=0; virtual void resetNodeResolveState()=0; virtual void mapNodeboxConnections()=0; + virtual core::aabbox3d getSelectionBoxIntUnion() const=0; }; IWritableNodeDefManager *createNodeDefManager(); diff --git a/src/raycast.cpp b/src/raycast.cpp new file mode 100644 index 000000000..58102c993 --- /dev/null +++ b/src/raycast.cpp @@ -0,0 +1,89 @@ +/* +Minetest +Copyright (C) 2016 juhdanad, Daniel Juhasz + +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 "irr_v3d.h" +#include "irr_aabb3d.h" + +bool boxLineCollision(const aabb3f &box, const v3f &start, + const v3f &dir, v3f *collision_point, v3s16 *collision_normal) { + if (box.isPointInside(start)) { + *collision_point = start; + collision_normal->set(0, 0, 0); + return true; + } + float m = 0; + + // Test X collision + if (dir.X != 0) { + if (dir.X > 0) + m = (box.MinEdge.X - start.X) / dir.X; + else + m = (box.MaxEdge.X - start.X) / dir.X; + + if (m >= 0 && m <= 1) { + *collision_point = start + dir * m; + if ((collision_point->Y >= box.MinEdge.Y) + && (collision_point->Y <= box.MaxEdge.Y) + && (collision_point->Z >= box.MinEdge.Z) + && (collision_point->Z <= box.MaxEdge.Z)) { + collision_normal->set((dir.X > 0) ? -1 : 1, 0, 0); + return true; + } + } + } + + // Test Y collision + if (dir.Y != 0) { + if (dir.Y > 0) + m = (box.MinEdge.Y - start.Y) / dir.Y; + else + m = (box.MaxEdge.Y - start.Y) / dir.Y; + + if (m >= 0 && m <= 1) { + *collision_point = start + dir * m; + if ((collision_point->X >= box.MinEdge.X) + && (collision_point->X <= box.MaxEdge.X) + && (collision_point->Z >= box.MinEdge.Z) + && (collision_point->Z <= box.MaxEdge.Z)) { + collision_normal->set(0, (dir.Y > 0) ? -1 : 1, 0); + return true; + } + } + } + + // Test Z collision + if (dir.Z != 0) { + if (dir.Z > 0) + m = (box.MinEdge.Z - start.Z) / dir.Z; + else + m = (box.MaxEdge.Z - start.Z) / dir.Z; + + if (m >= 0 && m <= 1) { + *collision_point = start + dir * m; + if ((collision_point->X >= box.MinEdge.X) + && (collision_point->X <= box.MaxEdge.X) + && (collision_point->Y >= box.MinEdge.Y) + && (collision_point->Y <= box.MaxEdge.Y)) { + collision_normal->set(0, 0, (dir.Z > 0) ? -1 : 1); + return true; + } + } + } + return false; +} diff --git a/src/raycast.h b/src/raycast.h new file mode 100644 index 000000000..d7ec8c843 --- /dev/null +++ b/src/raycast.h @@ -0,0 +1,38 @@ +/* +Minetest +Copyright (C) 2016 juhdanad, Daniel Juhasz + +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 SRC_RAYCAST_H_ +#define SRC_RAYCAST_H_ + + +/*! + * Checks if a line and a box intersects. + * @param[in] box box to test collision + * @param[in] start starting point of the line + * @param[in] dir direction and length of the line + * @param[out] collision_point first point of the collision + * @param[out] collision_normal normal vector at the collision, points + * outwards of the surface. If start is in the box, zero vector. + * @returns true if a collision point was found + */ +bool boxLineCollision(const aabb3f &box, const v3f &start, const v3f &dir, + v3f *collision_point, v3s16 *collision_normal); + + +#endif /* SRC_RAYCAST_H_ */ diff --git a/src/unittest/test_voxelalgorithms.cpp b/src/unittest/test_voxelalgorithms.cpp index 31b9cadd5..fd83844af 100644 --- a/src/unittest/test_voxelalgorithms.cpp +++ b/src/unittest/test_voxelalgorithms.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "voxelalgorithms.h" +#include "util/numeric.h" class TestVoxelAlgorithms : public TestBase { public: @@ -31,6 +32,7 @@ public: void testPropogateSunlight(INodeDefManager *ndef); void testClearLightAndCollectSources(INodeDefManager *ndef); + void testVoxelLineIterator(INodeDefManager *ndef); }; static TestVoxelAlgorithms g_test_instance; @@ -41,6 +43,7 @@ void TestVoxelAlgorithms::runTests(IGameDef *gamedef) TEST(testPropogateSunlight, ndef); TEST(testClearLightAndCollectSources, ndef); + TEST(testVoxelLineIterator, ndef); } //////////////////////////////////////////////////////////////////////////////// @@ -202,3 +205,59 @@ void TestVoxelAlgorithms::testClearLightAndCollectSources(INodeDefManager *ndef) UASSERT(unlight_from.size() == 1); } } + +void TestVoxelAlgorithms::testVoxelLineIterator(INodeDefManager *ndef) +{ + // Test some lines + // Do not test lines that start or end on the border of + // two voxels as rounding errors can make the test fail! + std::vector > lines; + for (f32 x = -9.1; x < 9; x += 3.124) { + for (f32 y = -9.2; y < 9; y += 3.123) { + for (f32 z = -9.3; z < 9; z += 3.122) { + lines.push_back(core::line3d(-x, -y, -z, x, y, z)); + } + } + } + lines.push_back(core::line3d(0, 0, 0, 0, 0, 0)); + // Test every line + std::vector >::iterator it = lines.begin(); + for (; it < lines.end(); it++) { + core::line3d l = *it; + + // Initialize test + voxalgo::VoxelLineIterator iterator(l.start, l.getVector()); + + //Test the first voxel + v3s16 start_voxel = floatToInt(l.start, 1); + UASSERT(iterator.m_current_node_pos == start_voxel); + + // Values for testing + v3s16 end_voxel = floatToInt(l.end, 1); + v3s16 voxel_vector = end_voxel - start_voxel; + int nodecount = abs(voxel_vector.X) + abs(voxel_vector.Y) + + abs(voxel_vector.Z); + int actual_nodecount = 0; + v3s16 old_voxel = iterator.m_current_node_pos; + + while (iterator.hasNext()) { + iterator.next(); + actual_nodecount++; + v3s16 new_voxel = iterator.m_current_node_pos; + // This must be a neighbor of the old voxel + UASSERTEQ(f32, (new_voxel - old_voxel).getLengthSQ(), 1); + // The line must intersect with the voxel + v3f voxel_center = intToFloat(iterator.m_current_node_pos, 1); + aabb3f box(voxel_center - v3f(0.5, 0.5, 0.5), + voxel_center + v3f(0.5, 0.5, 0.5)); + UASSERT(box.intersectsWithLine(l)); + // Update old voxel + old_voxel = new_voxel; + } + + // Test last node + UASSERT(iterator.m_current_node_pos == end_voxel); + // Test node count + UASSERTEQ(int, actual_nodecount, nodecount); + } +} diff --git a/src/util/pointedthing.cpp b/src/util/pointedthing.cpp index cd13000b5..ed3d4bb67 100644 --- a/src/util/pointedthing.cpp +++ b/src/util/pointedthing.cpp @@ -27,29 +27,25 @@ PointedThing::PointedThing(): type(POINTEDTHING_NOTHING), node_undersurface(0,0,0), node_abovesurface(0,0,0), + node_real_undersurface(0,0,0), + intersection_point(0,0,0), + intersection_normal(0,0,0), object_id(-1) {} std::string PointedThing::dump() const { std::ostringstream os(std::ios::binary); - if(type == POINTEDTHING_NOTHING) - { + if (type == POINTEDTHING_NOTHING) { os<<"[nothing]"; - } - else if(type == POINTEDTHING_NODE) - { + } else if (type == POINTEDTHING_NODE) { const v3s16 &u = node_undersurface; const v3s16 &a = node_abovesurface; os<<"[node under="< 0) { + m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5) + 1.5 + - m_start_position.X) / m_line_vector.X; + m_intersection_multi_inc.X = 1 / m_line_vector.X; + } else if (m_line_vector.X < 0) { + m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5) + - m_start_position.X + 0.5) / m_line_vector.X; + m_intersection_multi_inc.X = -1 / m_line_vector.X; + m_step_directions.X = -1; + } + + if (m_line_vector.Y > 0) { + m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5) + 1.5 + - m_start_position.Y) / m_line_vector.Y; + m_intersection_multi_inc.Y = 1 / m_line_vector.Y; + } else if (m_line_vector.Y < 0) { + m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5) + - m_start_position.Y + 0.5) / m_line_vector.Y; + m_intersection_multi_inc.Y = -1 / m_line_vector.Y; + m_step_directions.Y = -1; + } + + if (m_line_vector.Z > 0) { + m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5) + 1.5 + - m_start_position.Z) / m_line_vector.Z; + m_intersection_multi_inc.Z = 1 / m_line_vector.Z; + } else if (m_line_vector.Z < 0) { + m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5) + - m_start_position.Z + 0.5) / m_line_vector.Z; + m_intersection_multi_inc.Z = -1 / m_line_vector.Z; + m_step_directions.Z = -1; + } + + m_has_next = (m_next_intersection_multi.X <= 1) + || (m_next_intersection_multi.Y <= 1) + || (m_next_intersection_multi.Z <= 1); +} + +void VoxelLineIterator::next() +{ + if ((m_next_intersection_multi.X < m_next_intersection_multi.Y) + && (m_next_intersection_multi.X < m_next_intersection_multi.Z)) { + m_next_intersection_multi.X += m_intersection_multi_inc.X; + m_current_node_pos.X += m_step_directions.X; + } else if ((m_next_intersection_multi.Y < m_next_intersection_multi.Z)) { + m_next_intersection_multi.Y += m_intersection_multi_inc.Y; + m_current_node_pos.Y += m_step_directions.Y; + } else { + m_next_intersection_multi.Z += m_intersection_multi_inc.Z; + m_current_node_pos.Z += m_step_directions.Z; + } + + m_has_next = (m_next_intersection_multi.X <= 1) + || (m_next_intersection_multi.Y <= 1) + || (m_next_intersection_multi.Z <= 1); +} + } // namespace voxalgo diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h index 3632546dd..5eff8f7ac 100644 --- a/src/voxelalgorithms.h +++ b/src/voxelalgorithms.h @@ -73,7 +73,68 @@ void update_lighting_nodes( std::vector > &oldnodes, std::map &modified_blocks); +/*! + * This class iterates trough voxels that intersect with + * a line. The collision detection does not see nodeboxes, + * every voxel is a cube and is returned. + * This iterator steps to all nodes exactly once. + */ +struct VoxelLineIterator +{ +public: + //! Starting position of the line in world coordinates. + v3f m_start_position; + //! Direction and length of the line in world coordinates. + v3f m_line_vector; + /*! + * Each component stores the next smallest positive number, by + * which multiplying the line's vector gives a vector that ends + * on the intersection of two nodes. + */ + v3f m_next_intersection_multi; + /*! + * Each component stores the smallest positive number, by which + * m_next_intersection_multi's components can be increased. + */ + v3f m_intersection_multi_inc; + /*! + * Direction of the line. Each component can be -1 or 1 (if a + * component of the line's vector is 0, then there will be 1). + */ + v3s16 m_step_directions; + //! Position of the current node. + v3s16 m_current_node_pos; + //! If true, the next node will intersect the line, too. + bool m_has_next; + + /*! + * Creates a voxel line iterator with the given line. + * @param start_position starting point of the line + * in voxel coordinates + * @param line_vector length and direction of the + * line in voxel coordinates. start_position+line_vector + * is the end of the line + */ + VoxelLineIterator(const v3f &start_position,const v3f &line_vector); + + /*! + * Steps to the next voxel. + * Updates m_current_node_pos and + * m_previous_node_pos. + * Note that it works even if hasNext() is false, + * continuing the line as a ray. + */ + void next(); + + /*! + * Returns true if the next voxel intersects the given line. + */ + inline bool hasNext() const { return m_has_next; } +}; + } // namespace voxalgo + + #endif -- cgit v1.2.3 From ad10b8b762d8097092f307867a36e55049d69546 Mon Sep 17 00:00:00 2001 From: Rogier-5 Date: Tue, 3 Jan 2017 21:23:22 -0800 Subject: Use std::vector instead of std::map in class ABMHandler --- src/environment.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'src/environment.cpp') diff --git a/src/environment.cpp b/src/environment.cpp index 8c0daf87b..5eb9d9770 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -767,7 +767,7 @@ class ABMHandler { private: ServerEnvironment *m_env; - std::map > m_aabms; + std::vector *> m_aabms; public: ABMHandler(std::vector &abms, float dtime_s, ServerEnvironment *env, @@ -826,18 +826,22 @@ public: k != ids.end(); ++k) { content_t c = *k; - std::map >::iterator j; - j = m_aabms.find(c); - if(j == m_aabms.end()){ - std::vector aabmlist; - m_aabms[c] = aabmlist; - j = m_aabms.find(c); - } - j->second.push_back(aabm); + if (c >= m_aabms.size()) + m_aabms.resize(c + 256, (std::vector *) 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 @@ -886,13 +890,11 @@ public: content_t c = n.getContent(); v3s16 p = p0 + block->getPosRelative(); - std::map >::iterator j; - j = m_aabms.find(c); - if(j == m_aabms.end()) + if (!m_aabms[c]) continue; for(std::vector::iterator - i = j->second.begin(); i != j->second.end(); ++i) { + i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) { if(myrand() % i->chance != 0) continue; -- cgit v1.2.3 From ca3629637cb20cea318b8811e717e2caab579dc0 Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Wed, 4 Jan 2017 11:11:55 -0800 Subject: Fixes for using std:vector in ABMHander and further perf improvements --- src/environment.cpp | 10 +++++----- src/mapblock.h | 20 +++++++++++++++++--- src/mapnode.h | 5 ----- 3 files changed, 22 insertions(+), 13 deletions(-) (limited to 'src/environment.cpp') diff --git a/src/environment.cpp b/src/environment.cpp index 5eb9d9770..a0cf9dca5 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -827,7 +827,7 @@ public: { content_t c = *k; if (c >= m_aabms.size()) - m_aabms.resize(c + 256, (std::vector *) NULL); + m_aabms.resize(c + 256, NULL); if (!m_aabms[c]) m_aabms[c] = new std::vector; m_aabms[c]->push_back(aabm); @@ -872,7 +872,7 @@ public: } void apply(MapBlock *block) { - if(m_aabms.empty()) + if(m_aabms.empty() || block->isDummy()) return; ServerMap *map = &m_env->getServerMap(); @@ -886,13 +886,13 @@ public: for(p0.Y=0; p0.YgetNodeNoEx(p0); + const MapNode &n = block->getNodeUnsafe(p0); content_t c = n.getContent(); - v3s16 p = p0 + block->getPosRelative(); - if (!m_aabms[c]) + 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) diff --git a/src/mapblock.h b/src/mapblock.h index 5adfcf3fb..c737b4c37 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -305,8 +305,7 @@ public: inline MapNode getNodeNoEx(v3s16 p) { bool is_valid; - MapNode node = getNode(p.X, p.Y, p.Z, &is_valid); - return is_valid ? node : MapNode(CONTENT_IGNORE); + return getNode(p.X, p.Y, p.Z, &is_valid); } inline void setNode(s16 x, s16 y, s16 z, MapNode & n) @@ -341,6 +340,22 @@ public: return getNodeNoCheck(p.X, p.Y, p.Z, valid_position); } + //// + //// Non-checking, unsafe variants of the above + //// MapBlock must be loaded by another function in the same scope/function + //// Caller must ensure that this is not a dummy block (by calling isDummy()) + //// + + inline const MapNode &getNodeUnsafe(s16 x, s16 y, s16 z) + { + return data[z * zstride + y * ystride + x]; + } + + inline const MapNode &getNodeUnsafe(v3s16 &p) + { + return getNodeUnsafe(p.X, p.Y, p.Z); + } + inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n) { if (data == NULL) @@ -512,7 +527,6 @@ public: void serializeNetworkSpecific(std::ostream &os, u16 net_proto_version); void deSerializeNetworkSpecific(std::istream &is); - private: /* Private methods diff --git a/src/mapnode.h b/src/mapnode.h index a3c20e8ff..ae0245cfe 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -143,11 +143,6 @@ struct MapNode MapNode() { } - MapNode(const MapNode & n) - { - *this = n; - } - MapNode(content_t content, u8 a_param1=0, u8 a_param2=0) : param0(content), param1(a_param1), -- cgit v1.2.3 From b0746834cc44af1086c83715d0d6f749f653f29a Mon Sep 17 00:00:00 2001 From: lhofhansl Date: Sat, 7 Jan 2017 23:42:25 -0800 Subject: Get neighbor from same map block if possible in ABMHandler (#4998) --- src/environment.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'src/environment.cpp') diff --git a/src/environment.cpp b/src/environment.cpp index a0cf9dca5..50f5d58aa 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -902,14 +902,23 @@ public: if(!i->required_neighbors.empty()) { v3s16 p1; - for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++) - for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++) - for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++) + 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 == p) + if(p1 == p0) continue; - MapNode n = map->getNodeNoEx(p1); - content_t c = n.getContent(); + 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()){ -- cgit v1.2.3 From eb2c19bbedecb5f42c946b0c14466abc0b109825 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 8 Jan 2017 10:49:47 +0100 Subject: Move ClientEnvironment to dedicated cpp/header files --- src/CMakeLists.txt | 1 + src/client.h | 2 +- src/clientenvironment.cpp | 847 ++++++++++++++++++++++++++++++++++++++++++++++ src/clientenvironment.h | 191 +++++++++++ src/collision.cpp | 6 +- src/content_cso.cpp | 3 +- src/environment.cpp | 828 -------------------------------------------- src/environment.h | 167 --------- 8 files changed, 1042 insertions(+), 1003 deletions(-) create mode 100644 src/clientenvironment.cpp create mode 100644 src/clientenvironment.h (limited to 'src/environment.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 507624753..5228eb9ac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -508,6 +508,7 @@ set(client_SRCS ${client_irrlicht_changes_SRCS} camera.cpp client.cpp + clientenvironment.cpp clientmap.cpp clientmedia.cpp clientobject.cpp diff --git a/src/client.h b/src/client.h index 891fe62f8..9a09704a6 100644 --- a/src/client.h +++ b/src/client.h @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define CLIENT_HEADER #include "network/connection.h" -#include "environment.h" +#include "clientenvironment.h" #include "irrlichttypes_extrabloated.h" #include "threading/mutex.h" #include diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp new file mode 100644 index 000000000..e831de109 --- /dev/null +++ b/src/clientenvironment.cpp @@ -0,0 +1,847 @@ +/* +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 "util/serialize.h" +#include "util/pointedthing.h" +#include "clientenvironment.h" +#include "clientsimpleobject.h" +#include "clientmap.h" +#include "mapblock_mesh.h" +#include "event.h" +#include "collision.h" +#include "profiler.h" +#include "raycast.h" +#include "voxelalgorithms.h" + +/* + ClientEnvironment +*/ + +ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, + ITextureSource *texturesource, IGameDef *gamedef, + IrrlichtDevice *irr): + m_map(map), + m_local_player(NULL), + m_smgr(smgr), + m_texturesource(texturesource), + m_gamedef(gamedef), + m_irr(irr) +{ + char zero = 0; + memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids)); +} + +ClientEnvironment::~ClientEnvironment() +{ + // delete active objects + for (UNORDERED_MAP::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + delete i->second; + } + + for(std::vector::iterator + i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) { + delete *i; + } + + // Drop/delete map + m_map->drop(); +} + +Map & ClientEnvironment::getMap() +{ + return *m_map; +} + +ClientMap & ClientEnvironment::getClientMap() +{ + return *m_map; +} + +void ClientEnvironment::setLocalPlayer(LocalPlayer *player) +{ + DSTACK(FUNCTION_NAME); + /* + It is a failure if already is a local player + */ + FATAL_ERROR_IF(m_local_player != NULL, + "Local player already allocated"); + + m_local_player = player; +} + +void ClientEnvironment::step(float dtime) +{ + DSTACK(FUNCTION_NAME); + + /* Step time of day */ + stepTimeOfDay(dtime); + + // Get some settings + bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); + bool free_move = fly_allowed && g_settings->getBool("free_move"); + + // Get local player + LocalPlayer *lplayer = getLocalPlayer(); + assert(lplayer); + // collision info queue + std::vector player_collisions; + + /* + Get the speed the player is going + */ + bool is_climbing = lplayer->is_climbing; + + f32 player_speed = lplayer->getSpeed().getLength(); + + /* + Maximum position increment + */ + //f32 position_max_increment = 0.05*BS; + f32 position_max_increment = 0.1*BS; + + // Maximum time increment (for collision detection etc) + // time = distance / speed + f32 dtime_max_increment = 1; + if(player_speed > 0.001) + dtime_max_increment = position_max_increment / player_speed; + + // Maximum time increment is 10ms or lower + if(dtime_max_increment > 0.01) + dtime_max_increment = 0.01; + + // Don't allow overly huge dtime + if(dtime > 0.5) + dtime = 0.5; + + f32 dtime_downcount = dtime; + + /* + Stuff that has a maximum time increment + */ + + u32 loopcount = 0; + do + { + loopcount++; + + f32 dtime_part; + if(dtime_downcount > dtime_max_increment) + { + dtime_part = dtime_max_increment; + dtime_downcount -= dtime_part; + } + else + { + dtime_part = dtime_downcount; + /* + Setting this to 0 (no -=dtime_part) disables an infinite loop + when dtime_part is so small that dtime_downcount -= dtime_part + does nothing + */ + dtime_downcount = 0; + } + + /* + Handle local player + */ + + { + // Apply physics + if(!free_move && !is_climbing) + { + // Gravity + v3f speed = lplayer->getSpeed(); + if(!lplayer->in_liquid) + speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2; + + // Liquid floating / sinking + if(lplayer->in_liquid && !lplayer->swimming_vertical) + speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2; + + // Liquid resistance + if(lplayer->in_liquid_stable || lplayer->in_liquid) + { + // How much the node's viscosity blocks movement, ranges between 0 and 1 + // Should match the scale at which viscosity increase affects other liquid attributes + const f32 viscosity_factor = 0.3; + + v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; + f32 dl = d_wanted.getLength(); + if(dl > lplayer->movement_liquid_fluidity_smooth) + dl = lplayer->movement_liquid_fluidity_smooth; + dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor); + + v3f d = d_wanted.normalize() * dl; + speed += d; + } + + lplayer->setSpeed(speed); + } + + /* + Move the lplayer. + This also does collision detection. + */ + lplayer->move(dtime_part, this, position_max_increment, + &player_collisions); + } + } + while(dtime_downcount > 0.001); + + //std::cout<<"Looped "<::iterator i = player_collisions.begin(); + i != player_collisions.end(); ++i) { + CollisionInfo &info = *i; + v3f speed_diff = info.new_speed - info.old_speed;; + // Handle only fall damage + // (because otherwise walking against something in fast_move kills you) + if(speed_diff.Y < 0 || info.old_speed.Y >= 0) + continue; + // Get rid of other components + speed_diff.X = 0; + speed_diff.Z = 0; + f32 pre_factor = 1; // 1 hp per node/s + f32 tolerance = BS*14; // 5 without damage + f32 post_factor = 1; // 1 hp per node/s + if(info.type == COLLISION_NODE) + { + const ContentFeatures &f = m_gamedef->ndef()-> + get(m_map->getNodeNoEx(info.node_p)); + // Determine fall damage multiplier + int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); + pre_factor = 1.0 + (float)addp/100.0; + } + float speed = pre_factor * speed_diff.getLength(); + if(speed > tolerance) + { + f32 damage_f = (speed - tolerance)/BS * post_factor; + u16 damage = (u16)(damage_f+0.5); + if(damage != 0){ + damageLocalPlayer(damage, true); + MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage"); + m_gamedef->event()->put(e); + } + } + } + + /* + A quick draft of lava damage + */ + if(m_lava_hurt_interval.step(dtime, 1.0)) + { + v3f pf = lplayer->getPosition(); + + // Feet, middle and head + v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS); + MapNode n1 = m_map->getNodeNoEx(p1); + v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS); + MapNode n2 = m_map->getNodeNoEx(p2); + v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS); + MapNode n3 = m_map->getNodeNoEx(p3); + + u32 damage_per_second = 0; + damage_per_second = MYMAX(damage_per_second, + m_gamedef->ndef()->get(n1).damage_per_second); + damage_per_second = MYMAX(damage_per_second, + m_gamedef->ndef()->get(n2).damage_per_second); + damage_per_second = MYMAX(damage_per_second, + m_gamedef->ndef()->get(n3).damage_per_second); + + if(damage_per_second != 0) + { + damageLocalPlayer(damage_per_second, true); + } + } + + // Protocol v29 make this behaviour obsolete + if (((Client*) getGameDef())->getProtoVersion() < 29) { + /* + Drowning + */ + if (m_drowning_interval.step(dtime, 2.0)) { + v3f pf = lplayer->getPosition(); + + // 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); + u8 drowning_damage = c.drowning; + if (drowning_damage > 0 && lplayer->hp > 0) { + u16 breath = lplayer->getBreath(); + if (breath > 10) { + breath = 11; + } + if (breath > 0) { + breath -= 1; + } + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } + + if (lplayer->getBreath() == 0 && drowning_damage > 0) { + damageLocalPlayer(drowning_damage, true); + } + } + if (m_breathing_interval.step(dtime, 0.5)) { + v3f pf = lplayer->getPosition(); + + // 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); + if (!lplayer->hp) { + lplayer->setBreath(11); + } else if (c.drowning == 0) { + u16 breath = lplayer->getBreath(); + if (breath <= 10) { + breath += 1; + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } + } + } + } + + // Update lighting on local player (used for wield item) + u32 day_night_ratio = getDayNightRatio(); + { + // Get node at head + + // On InvalidPositionException, use this as default + // (day: LIGHT_SUN, night: 0) + MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0); + + v3s16 p = lplayer->getLightPosition(); + node_at_lplayer = m_map->getNodeNoEx(p); + + u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef()); + u8 day = light & 0xff; + u8 night = (light >> 8) & 0xff; + finalColorBlend(lplayer->light_color, day, night, day_night_ratio); + } + + /* + Step active objects and update lighting of them + */ + + g_profiler->avg("CEnv: num of objects", m_active_objects.size()); + bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21); + for (UNORDERED_MAP::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ClientActiveObject* obj = i->second; + // Step object + obj->step(dtime, this); + + if(update_lighting) + { + // Update lighting + u8 light = 0; + bool pos_ok; + + // Get node at head + v3s16 p = obj->getLightPosition(); + MapNode n = m_map->getNodeNoEx(p, &pos_ok); + if (pos_ok) + light = n.getLightBlend(day_night_ratio, m_gamedef->ndef()); + else + light = blend_light(day_night_ratio, LIGHT_SUN, 0); + + obj->updateLight(light); + } + } + + /* + Step and handle simple objects + */ + g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size()); + for(std::vector::iterator + i = m_simple_objects.begin(); i != m_simple_objects.end();) { + std::vector::iterator cur = i; + ClientSimpleObject *simple = *cur; + + simple->step(dtime); + if(simple->m_to_be_removed) { + delete simple; + i = m_simple_objects.erase(cur); + } + else { + ++i; + } + } +} + +void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple) +{ + m_simple_objects.push_back(simple); +} + +GenericCAO* ClientEnvironment::getGenericCAO(u16 id) +{ + ClientActiveObject *obj = getActiveObject(id); + if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC) + return (GenericCAO*) obj; + else + return NULL; +} + +ClientActiveObject* ClientEnvironment::getActiveObject(u16 id) +{ + UNORDERED_MAP::iterator n = m_active_objects.find(id); + if (n == m_active_objects.end()) + return NULL; + return n->second; +} + +bool isFreeClientActiveObjectId(const u16 id, + UNORDERED_MAP &objects) +{ + if(id == 0) + return false; + + return objects.find(id) == objects.end(); +} + +u16 getFreeClientActiveObjectId(UNORDERED_MAP &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 (isFreeClientActiveObjectId(last_used_id, objects)) + return last_used_id; + + if (last_used_id == startid) + return 0; + } +} + +u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) +{ + assert(object); // Pre-condition + if(object->getId() == 0) + { + u16 new_id = getFreeClientActiveObjectId(m_active_objects); + if(new_id == 0) + { + infostream<<"ClientEnvironment::addActiveObject(): " + <<"no free ids available"<setId(new_id); + } + if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) { + infostream<<"ClientEnvironment::addActiveObject(): " + <<"id is not free ("<getId()<<")"<getId()] = object; + object->addToScene(m_smgr, m_texturesource, m_irr); + { // Update lighting immediately + u8 light = 0; + bool pos_ok; + + // Get node at head + v3s16 p = object->getLightPosition(); + MapNode n = m_map->getNodeNoEx(p, &pos_ok); + if (pos_ok) + light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); + else + light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); + + object->updateLight(light); + } + return object->getId(); +} + +void ClientEnvironment::addActiveObject(u16 id, u8 type, + const std::string &init_data) +{ + ClientActiveObject* obj = + ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this); + if(obj == NULL) + { + infostream<<"ClientEnvironment::addActiveObject(): " + <<"id="<setId(id); + + try + { + obj->initialize(init_data); + } + catch(SerializationError &e) + { + errorstream<<"ClientEnvironment::addActiveObject():" + <<" id="<removeFromScene(true); + delete obj; + m_active_objects.erase(id); +} + +void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data) +{ + ClientActiveObject *obj = getActiveObject(id); + if (obj == NULL) { + infostream << "ClientEnvironment::processActiveObjectMessage():" + << " got message for id=" << id << ", which doesn't exist." + << std::endl; + return; + } + + try { + obj->processMessage(data); + } catch (SerializationError &e) { + errorstream<<"ClientEnvironment::processActiveObjectMessage():" + << " id=" << id << " type=" << obj->getType() + << " SerializationError in processMessage(): " << e.what() + << std::endl; + } +} + +/* + Callbacks for activeobjects +*/ + +void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp) +{ + LocalPlayer *lplayer = getLocalPlayer(); + assert(lplayer); + + if (handle_hp) { + if (lplayer->hp > damage) + lplayer->hp -= damage; + else + lplayer->hp = 0; + } + + ClientEnvEvent event; + event.type = CEE_PLAYER_DAMAGE; + event.player_damage.amount = damage; + event.player_damage.send_to_server = handle_hp; + m_client_event_queue.push(event); +} + +void ClientEnvironment::updateLocalPlayerBreath(u16 breath) +{ + ClientEnvEvent event; + event.type = CEE_PLAYER_BREATH; + event.player_breath.amount = breath; + m_client_event_queue.push(event); +} + +/* + Client likes to call these +*/ + +void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, + std::vector &dest) +{ + for (UNORDERED_MAP::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ClientActiveObject* obj = i->second; + + f32 d = (obj->getPosition() - origin).getLength(); + + if(d > max_d) + continue; + + DistanceSortedActiveObject dso(obj, d); + + dest.push_back(dso); + } +} + +ClientEnvEvent ClientEnvironment::getClientEvent() +{ + ClientEnvEvent event; + if(m_client_event_queue.empty()) + event.type = CEE_NONE; + else { + event = m_client_event_queue.front(); + m_client_event_queue.pop(); + } + return event; +} + +ClientActiveObject * ClientEnvironment::getSelectedActiveObject( + const core::line3d &shootline_on_map, v3f *intersection_point, + v3s16 *intersection_normal) +{ + std::vector objects; + getActiveObjects(shootline_on_map.start, + shootline_on_map.getLength() + 3, objects); + const v3f line_vector = shootline_on_map.getVector(); + + // Sort them. + // After this, the closest object is the first in the array. + std::sort(objects.begin(), objects.end()); + + /* Because objects can have different nodebox sizes, + * the object whose center is the nearest isn't necessarily + * the closest one. If an object is found, don't stop + * immediately. */ + + f32 d_min = shootline_on_map.getLength(); + ClientActiveObject *nearest_obj = NULL; + for (u32 i = 0; i < objects.size(); i++) { + ClientActiveObject *obj = objects[i].obj; + + aabb3f *selection_box = obj->getSelectionBox(); + if (selection_box == NULL) + continue; + + v3f pos = obj->getPosition(); + + aabb3f offsetted_box(selection_box->MinEdge + pos, + selection_box->MaxEdge + pos); + + if (offsetted_box.getCenter().getDistanceFrom( + shootline_on_map.start) > d_min + 9.6f*BS) { + // Probably there is no active object that has bigger nodebox than + // (-5.5,-5.5,-5.5,5.5,5.5,5.5) + // 9.6 > 5.5*sqrt(3) + break; + } + + v3f current_intersection; + v3s16 current_normal; + if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector, + ¤t_intersection, ¤t_normal)) { + f32 d_current = current_intersection.getDistanceFrom( + shootline_on_map.start); + if (d_current <= d_min) { + d_min = d_current; + nearest_obj = obj; + *intersection_point = current_intersection; + *intersection_normal = current_normal; + } + } + } + + return nearest_obj; +} + +/* + Check if a node is pointable +*/ +static inline bool isPointableNode(const MapNode &n, + INodeDefManager *ndef, bool liquids_pointable) +{ + const ContentFeatures &features = ndef->get(n); + return features.pointable || + (liquids_pointable && features.isLiquid()); +} + +PointedThing ClientEnvironment::getPointedThing( + core::line3d shootline, + bool liquids_pointable, + bool look_for_object) +{ + PointedThing result; + + INodeDefManager *nodedef = m_map->getNodeDefManager(); + + core::aabbox3d maximal_exceed = nodedef->getSelectionBoxIntUnion(); + // The code needs to search these nodes + core::aabbox3d search_range(-maximal_exceed.MaxEdge, + -maximal_exceed.MinEdge); + // If a node is found, there might be a larger node behind. + // To find it, we have to go further. + s16 maximal_overcheck = + std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X)) + + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y)) + + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z)); + + const v3f original_vector = shootline.getVector(); + const f32 original_length = original_vector.getLength(); + + f32 min_distance = original_length; + + // First try to find an active object + if (look_for_object) { + ClientActiveObject *selected_object = getSelectedActiveObject( + shootline, &result.intersection_point, + &result.intersection_normal); + + if (selected_object != NULL) { + min_distance = + (result.intersection_point - shootline.start).getLength(); + + result.type = POINTEDTHING_OBJECT; + result.object_id = selected_object->getId(); + } + } + + // Reduce shootline + if (original_length > 0) { + shootline.end = shootline.start + + shootline.getVector() / original_length * min_distance; + } + + // Try to find a node that is closer than the selected active + // object (if it exists). + + voxalgo::VoxelLineIterator iterator(shootline.start / BS, + shootline.getVector() / BS); + v3s16 oldnode = iterator.m_current_node_pos; + // Indicates that a node was found. + bool is_node_found = false; + // If a node is found, it is possible that there's a node + // behind it with a large nodebox, so continue the search. + u16 node_foundcounter = 0; + // If a node is found, this is the center of the + // first nodebox the shootline meets. + v3f found_boxcenter(0, 0, 0); + // The untested nodes are in this range. + core::aabbox3d new_nodes; + while (true) { + // Test the nodes around the current node in search_range. + new_nodes = search_range; + new_nodes.MinEdge += iterator.m_current_node_pos; + new_nodes.MaxEdge += iterator.m_current_node_pos; + + // Only check new nodes + v3s16 delta = iterator.m_current_node_pos - oldnode; + if (delta.X > 0) + new_nodes.MinEdge.X = new_nodes.MaxEdge.X; + else if (delta.X < 0) + new_nodes.MaxEdge.X = new_nodes.MinEdge.X; + else if (delta.Y > 0) + new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y; + else if (delta.Y < 0) + new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y; + else if (delta.Z > 0) + new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z; + else if (delta.Z < 0) + new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z; + + // For each untested node + for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) { + for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) { + for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) { + MapNode n; + v3s16 np(x, y, z); + bool is_valid_position; + + n = m_map->getNodeNoEx(np, &is_valid_position); + if (!(is_valid_position && + isPointableNode(n, nodedef, liquids_pointable))) { + continue; + } + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes, + n.getNeighbors(np, m_map)); + + v3f npf = intToFloat(np, BS); + for (std::vector::const_iterator i = boxes.begin(); + i != boxes.end(); ++i) { + aabb3f box = *i; + box.MinEdge += npf; + box.MaxEdge += npf; + v3f intersection_point; + v3s16 intersection_normal; + if (!boxLineCollision(box, shootline.start, shootline.getVector(), + &intersection_point, &intersection_normal)) { + continue; + } + f32 distance = (intersection_point - shootline.start).getLength(); + if (distance >= min_distance) { + continue; + } + result.type = POINTEDTHING_NODE; + result.node_undersurface = np; + result.intersection_point = intersection_point; + result.intersection_normal = intersection_normal; + found_boxcenter = box.getCenter(); + min_distance = distance; + is_node_found = true; + } + } + } + } + if (is_node_found) { + node_foundcounter++; + if (node_foundcounter > maximal_overcheck) { + break; + } + } + // Next node + if (iterator.hasNext()) { + oldnode = iterator.m_current_node_pos; + iterator.next(); + } else { + break; + } + } + + if (is_node_found) { + // Set undersurface and abovesurface nodes + f32 d = 0.002 * BS; + v3f fake_intersection = result.intersection_point; + // Move intersection towards its source block. + if (fake_intersection.X < found_boxcenter.X) + fake_intersection.X += d; + else + fake_intersection.X -= d; + + if (fake_intersection.Y < found_boxcenter.Y) + fake_intersection.Y += d; + else + fake_intersection.Y -= d; + + if (fake_intersection.Z < found_boxcenter.Z) + fake_intersection.Z += d; + else + fake_intersection.Z -= d; + + result.node_real_undersurface = floatToInt(fake_intersection, BS); + result.node_abovesurface = result.node_real_undersurface + + result.intersection_normal; + } + return result; +} diff --git a/src/clientenvironment.h b/src/clientenvironment.h new file mode 100644 index 000000000..e6292b5b7 --- /dev/null +++ b/src/clientenvironment.h @@ -0,0 +1,191 @@ +/* +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. +*/ + +#ifndef CLIENT_ENVIRONMENT_HEADER +#define CLIENT_ENVIRONMENT_HEADER + +#include +#include +#include "environment.h" +#include "clientobject.h" + +class ClientSimpleObject; +class ClientMap; +class ClientActiveObject; +class GenericCAO; +class LocalPlayer; + +/* + The client-side environment. + + This is not thread-safe. + Must be called from main (irrlicht) thread (uses the SceneManager) + Client uses an environment mutex. +*/ + +enum ClientEnvEventType +{ + CEE_NONE, + CEE_PLAYER_DAMAGE, + CEE_PLAYER_BREATH +}; + +struct ClientEnvEvent +{ + ClientEnvEventType type; + union { + //struct{ + //} none; + struct{ + u8 amount; + bool send_to_server; + } player_damage; + struct{ + u16 amount; + } player_breath; + }; +}; + +class ClientEnvironment : public Environment +{ +public: + ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, + ITextureSource *texturesource, IGameDef *gamedef, + IrrlichtDevice *device); + ~ClientEnvironment(); + + Map & getMap(); + ClientMap & getClientMap(); + + IGameDef *getGameDef() + { return m_gamedef; } + + void step(f32 dtime); + + virtual void setLocalPlayer(LocalPlayer *player); + LocalPlayer *getLocalPlayer() { return m_local_player; } + + /* + ClientSimpleObjects + */ + + void addSimpleObject(ClientSimpleObject *simple); + + /* + ActiveObjects + */ + + GenericCAO* getGenericCAO(u16 id); + ClientActiveObject* getActiveObject(u16 id); + + /* + Adds an active object to the environment. + Environment handles deletion of object. + Object may be deleted by environment immediately. + If id of object is 0, assigns a free id to it. + Returns the id of the object. + Returns 0 if not added and thus deleted. + */ + u16 addActiveObject(ClientActiveObject *object); + + void addActiveObject(u16 id, u8 type, const std::string &init_data); + void removeActiveObject(u16 id); + + void processActiveObjectMessage(u16 id, const std::string &data); + + /* + Callbacks for activeobjects + */ + + void damageLocalPlayer(u8 damage, bool handle_hp=true); + void updateLocalPlayerBreath(u16 breath); + + /* + Client likes to call these + */ + + // Get all nearby objects + void getActiveObjects(v3f origin, f32 max_d, + std::vector &dest); + + // Get event from queue. CEE_NONE is returned if queue is empty. + ClientEnvEvent getClientEvent(); + + /*! + * Gets closest object pointed by the shootline. + * Returns NULL if not found. + * + * \param[in] shootline_on_map the shootline for + * the test in world coordinates + * \param[out] intersection_point the first point where + * the shootline meets the object. Valid only if + * not NULL is returned. + * \param[out] intersection_normal the normal vector of + * the intersection, pointing outwards. Zero vector if + * the shootline starts in an active object. + * Valid only if not NULL is returned. + */ + ClientActiveObject * getSelectedActiveObject( + const core::line3d &shootline_on_map, + v3f *intersection_point, + v3s16 *intersection_normal + ); + + /*! + * Performs a raycast on the world. + * Returns the first thing the shootline meets. + * + * @param[in] shootline the shootline, starting from + * the camera position. This also gives the maximal distance + * of the search. + * @param[in] liquids_pointable if false, liquids are ignored + * @param[in] look_for_object if false, objects are ignored + */ + PointedThing getPointedThing( + core::line3d shootline, + bool liquids_pointable, + bool look_for_object); + + u16 attachement_parent_ids[USHRT_MAX + 1]; + + const std::list &getPlayerNames() { return m_player_names; } + void addPlayerName(const std::string &name) { m_player_names.push_back(name); } + void removePlayerName(const std::string &name) { m_player_names.remove(name); } + void updateCameraOffset(v3s16 camera_offset) + { m_camera_offset = camera_offset; } + v3s16 getCameraOffset() const { return m_camera_offset; } +private: + ClientMap *m_map; + LocalPlayer *m_local_player; + scene::ISceneManager *m_smgr; + ITextureSource *m_texturesource; + IGameDef *m_gamedef; + IrrlichtDevice *m_irr; + UNORDERED_MAP m_active_objects; + std::vector m_simple_objects; + std::queue m_client_event_queue; + IntervalLimiter m_active_object_light_update_interval; + IntervalLimiter m_lava_hurt_interval; + IntervalLimiter m_drowning_interval; + IntervalLimiter m_breathing_interval; + std::list m_player_names; + v3s16 m_camera_offset; +}; + +#endif diff --git a/src/collision.cpp b/src/collision.cpp index 8e5dbcc9b..21f14bec1 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -22,12 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "nodedef.h" #include "gamedef.h" -#include "log.h" -#include "environment.h" +#include "clientenvironment.h" #include "serverobject.h" -#include -#include -#include "util/timetaker.h" #include "profiler.h" // float error is 10 - 9.96875 = 0.03125 diff --git a/src/content_cso.cpp b/src/content_cso.cpp index 0790024fc..c0407f460 100644 --- a/src/content_cso.cpp +++ b/src/content_cso.cpp @@ -20,9 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_cso.h" #include #include "client/tile.h" -#include "environment.h" +#include "clientenvironment.h" #include "gamedef.h" -#include "log.h" #include "map.h" /* diff --git a/src/environment.cpp b/src/environment.cpp index 50f5d58aa..6f6e20238 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -33,12 +33,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "nodemetadata.h" #include "gamedef.h" -#ifndef SERVER -#include "clientmap.h" -#include "localplayer.h" -#include "mapblock_mesh.h" -#include "event.h" -#endif #include "server.h" #include "daynightratio.h" #include "map.h" @@ -2279,825 +2273,3 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) } } -#ifndef SERVER - -#include "clientsimpleobject.h" - -/* - ClientEnvironment -*/ - -ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, - ITextureSource *texturesource, IGameDef *gamedef, - IrrlichtDevice *irr): - m_map(map), - m_local_player(NULL), - m_smgr(smgr), - m_texturesource(texturesource), - m_gamedef(gamedef), - m_irr(irr) -{ - char zero = 0; - memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids)); -} - -ClientEnvironment::~ClientEnvironment() -{ - // delete active objects - for (UNORDERED_MAP::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - delete i->second; - } - - for(std::vector::iterator - i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) { - delete *i; - } - - // Drop/delete map - m_map->drop(); -} - -Map & ClientEnvironment::getMap() -{ - return *m_map; -} - -ClientMap & ClientEnvironment::getClientMap() -{ - return *m_map; -} - -void ClientEnvironment::setLocalPlayer(LocalPlayer *player) -{ - DSTACK(FUNCTION_NAME); - /* - It is a failure if already is a local player - */ - FATAL_ERROR_IF(m_local_player != NULL, - "Local player already allocated"); - - m_local_player = player; -} - -void ClientEnvironment::step(float dtime) -{ - DSTACK(FUNCTION_NAME); - - /* Step time of day */ - stepTimeOfDay(dtime); - - // Get some settings - bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); - bool free_move = fly_allowed && g_settings->getBool("free_move"); - - // Get local player - LocalPlayer *lplayer = getLocalPlayer(); - assert(lplayer); - // collision info queue - std::vector player_collisions; - - /* - Get the speed the player is going - */ - bool is_climbing = lplayer->is_climbing; - - f32 player_speed = lplayer->getSpeed().getLength(); - - /* - Maximum position increment - */ - //f32 position_max_increment = 0.05*BS; - f32 position_max_increment = 0.1*BS; - - // Maximum time increment (for collision detection etc) - // time = distance / speed - f32 dtime_max_increment = 1; - if(player_speed > 0.001) - dtime_max_increment = position_max_increment / player_speed; - - // Maximum time increment is 10ms or lower - if(dtime_max_increment > 0.01) - dtime_max_increment = 0.01; - - // Don't allow overly huge dtime - if(dtime > 0.5) - dtime = 0.5; - - f32 dtime_downcount = dtime; - - /* - Stuff that has a maximum time increment - */ - - u32 loopcount = 0; - do - { - loopcount++; - - f32 dtime_part; - if(dtime_downcount > dtime_max_increment) - { - dtime_part = dtime_max_increment; - dtime_downcount -= dtime_part; - } - else - { - dtime_part = dtime_downcount; - /* - Setting this to 0 (no -=dtime_part) disables an infinite loop - when dtime_part is so small that dtime_downcount -= dtime_part - does nothing - */ - dtime_downcount = 0; - } - - /* - Handle local player - */ - - { - // Apply physics - if(!free_move && !is_climbing) - { - // Gravity - v3f speed = lplayer->getSpeed(); - if(!lplayer->in_liquid) - speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2; - - // Liquid floating / sinking - if(lplayer->in_liquid && !lplayer->swimming_vertical) - speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2; - - // Liquid resistance - if(lplayer->in_liquid_stable || lplayer->in_liquid) - { - // How much the node's viscosity blocks movement, ranges between 0 and 1 - // Should match the scale at which viscosity increase affects other liquid attributes - const f32 viscosity_factor = 0.3; - - v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; - f32 dl = d_wanted.getLength(); - if(dl > lplayer->movement_liquid_fluidity_smooth) - dl = lplayer->movement_liquid_fluidity_smooth; - dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor); - - v3f d = d_wanted.normalize() * dl; - speed += d; - } - - lplayer->setSpeed(speed); - } - - /* - Move the lplayer. - This also does collision detection. - */ - lplayer->move(dtime_part, this, position_max_increment, - &player_collisions); - } - } - while(dtime_downcount > 0.001); - - //std::cout<<"Looped "<::iterator i = player_collisions.begin(); - i != player_collisions.end(); ++i) { - CollisionInfo &info = *i; - v3f speed_diff = info.new_speed - info.old_speed;; - // Handle only fall damage - // (because otherwise walking against something in fast_move kills you) - if(speed_diff.Y < 0 || info.old_speed.Y >= 0) - continue; - // Get rid of other components - speed_diff.X = 0; - speed_diff.Z = 0; - f32 pre_factor = 1; // 1 hp per node/s - f32 tolerance = BS*14; // 5 without damage - f32 post_factor = 1; // 1 hp per node/s - if(info.type == COLLISION_NODE) - { - const ContentFeatures &f = m_gamedef->ndef()-> - get(m_map->getNodeNoEx(info.node_p)); - // Determine fall damage multiplier - int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); - pre_factor = 1.0 + (float)addp/100.0; - } - float speed = pre_factor * speed_diff.getLength(); - if(speed > tolerance) - { - f32 damage_f = (speed - tolerance)/BS * post_factor; - u16 damage = (u16)(damage_f+0.5); - if(damage != 0){ - damageLocalPlayer(damage, true); - MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage"); - m_gamedef->event()->put(e); - } - } - } - - /* - A quick draft of lava damage - */ - if(m_lava_hurt_interval.step(dtime, 1.0)) - { - v3f pf = lplayer->getPosition(); - - // Feet, middle and head - v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS); - MapNode n1 = m_map->getNodeNoEx(p1); - v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS); - MapNode n2 = m_map->getNodeNoEx(p2); - v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS); - MapNode n3 = m_map->getNodeNoEx(p3); - - u32 damage_per_second = 0; - damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n1).damage_per_second); - damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n2).damage_per_second); - damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n3).damage_per_second); - - if(damage_per_second != 0) - { - damageLocalPlayer(damage_per_second, true); - } - } - - // Protocol v29 make this behaviour obsolete - if (((Client*) getGameDef())->getProtoVersion() < 29) { - /* - Drowning - */ - if (m_drowning_interval.step(dtime, 2.0)) { - v3f pf = lplayer->getPosition(); - - // 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); - u8 drowning_damage = c.drowning; - if (drowning_damage > 0 && lplayer->hp > 0) { - u16 breath = lplayer->getBreath(); - if (breath > 10) { - breath = 11; - } - if (breath > 0) { - breath -= 1; - } - lplayer->setBreath(breath); - updateLocalPlayerBreath(breath); - } - - if (lplayer->getBreath() == 0 && drowning_damage > 0) { - damageLocalPlayer(drowning_damage, true); - } - } - if (m_breathing_interval.step(dtime, 0.5)) { - v3f pf = lplayer->getPosition(); - - // 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); - if (!lplayer->hp) { - lplayer->setBreath(11); - } else if (c.drowning == 0) { - u16 breath = lplayer->getBreath(); - if (breath <= 10) { - breath += 1; - lplayer->setBreath(breath); - updateLocalPlayerBreath(breath); - } - } - } - } - - // Update lighting on local player (used for wield item) - u32 day_night_ratio = getDayNightRatio(); - { - // Get node at head - - // On InvalidPositionException, use this as default - // (day: LIGHT_SUN, night: 0) - MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0); - - v3s16 p = lplayer->getLightPosition(); - node_at_lplayer = m_map->getNodeNoEx(p); - - u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef()); - u8 day = light & 0xff; - u8 night = (light >> 8) & 0xff; - finalColorBlend(lplayer->light_color, day, night, day_night_ratio); - } - - /* - Step active objects and update lighting of them - */ - - g_profiler->avg("CEnv: num of objects", m_active_objects.size()); - bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21); - for (UNORDERED_MAP::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - ClientActiveObject* obj = i->second; - // Step object - obj->step(dtime, this); - - if(update_lighting) - { - // Update lighting - u8 light = 0; - bool pos_ok; - - // Get node at head - v3s16 p = obj->getLightPosition(); - MapNode n = m_map->getNodeNoEx(p, &pos_ok); - if (pos_ok) - light = n.getLightBlend(day_night_ratio, m_gamedef->ndef()); - else - light = blend_light(day_night_ratio, LIGHT_SUN, 0); - - obj->updateLight(light); - } - } - - /* - Step and handle simple objects - */ - g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size()); - for(std::vector::iterator - i = m_simple_objects.begin(); i != m_simple_objects.end();) { - std::vector::iterator cur = i; - ClientSimpleObject *simple = *cur; - - simple->step(dtime); - if(simple->m_to_be_removed) { - delete simple; - i = m_simple_objects.erase(cur); - } - else { - ++i; - } - } -} - -void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple) -{ - m_simple_objects.push_back(simple); -} - -GenericCAO* ClientEnvironment::getGenericCAO(u16 id) -{ - ClientActiveObject *obj = getActiveObject(id); - if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC) - return (GenericCAO*) obj; - else - return NULL; -} - -ClientActiveObject* ClientEnvironment::getActiveObject(u16 id) -{ - UNORDERED_MAP::iterator n = m_active_objects.find(id); - if (n == m_active_objects.end()) - return NULL; - return n->second; -} - -bool isFreeClientActiveObjectId(const u16 id, - UNORDERED_MAP &objects) -{ - if(id == 0) - return false; - - return objects.find(id) == objects.end(); -} - -u16 getFreeClientActiveObjectId(UNORDERED_MAP &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 (isFreeClientActiveObjectId(last_used_id, objects)) - return last_used_id; - - if (last_used_id == startid) - return 0; - } -} - -u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) -{ - assert(object); // Pre-condition - if(object->getId() == 0) - { - u16 new_id = getFreeClientActiveObjectId(m_active_objects); - if(new_id == 0) - { - infostream<<"ClientEnvironment::addActiveObject(): " - <<"no free ids available"<setId(new_id); - } - if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) { - infostream<<"ClientEnvironment::addActiveObject(): " - <<"id is not free ("<getId()<<")"<getId()] = object; - object->addToScene(m_smgr, m_texturesource, m_irr); - { // Update lighting immediately - u8 light = 0; - bool pos_ok; - - // Get node at head - v3s16 p = object->getLightPosition(); - MapNode n = m_map->getNodeNoEx(p, &pos_ok); - if (pos_ok) - light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); - else - light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); - - object->updateLight(light); - } - return object->getId(); -} - -void ClientEnvironment::addActiveObject(u16 id, u8 type, - const std::string &init_data) -{ - ClientActiveObject* obj = - ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this); - if(obj == NULL) - { - infostream<<"ClientEnvironment::addActiveObject(): " - <<"id="<setId(id); - - try - { - obj->initialize(init_data); - } - catch(SerializationError &e) - { - errorstream<<"ClientEnvironment::addActiveObject():" - <<" id="<removeFromScene(true); - delete obj; - m_active_objects.erase(id); -} - -void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data) -{ - ClientActiveObject *obj = getActiveObject(id); - if (obj == NULL) { - infostream << "ClientEnvironment::processActiveObjectMessage():" - << " got message for id=" << id << ", which doesn't exist." - << std::endl; - return; - } - - try { - obj->processMessage(data); - } catch (SerializationError &e) { - errorstream<<"ClientEnvironment::processActiveObjectMessage():" - << " id=" << id << " type=" << obj->getType() - << " SerializationError in processMessage(): " << e.what() - << std::endl; - } -} - -/* - Callbacks for activeobjects -*/ - -void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp) -{ - LocalPlayer *lplayer = getLocalPlayer(); - assert(lplayer); - - if (handle_hp) { - if (lplayer->hp > damage) - lplayer->hp -= damage; - else - lplayer->hp = 0; - } - - ClientEnvEvent event; - event.type = CEE_PLAYER_DAMAGE; - event.player_damage.amount = damage; - event.player_damage.send_to_server = handle_hp; - m_client_event_queue.push(event); -} - -void ClientEnvironment::updateLocalPlayerBreath(u16 breath) -{ - ClientEnvEvent event; - event.type = CEE_PLAYER_BREATH; - event.player_breath.amount = breath; - m_client_event_queue.push(event); -} - -/* - Client likes to call these -*/ - -void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, - std::vector &dest) -{ - for (UNORDERED_MAP::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - ClientActiveObject* obj = i->second; - - f32 d = (obj->getPosition() - origin).getLength(); - - if(d > max_d) - continue; - - DistanceSortedActiveObject dso(obj, d); - - dest.push_back(dso); - } -} - -ClientEnvEvent ClientEnvironment::getClientEvent() -{ - ClientEnvEvent event; - if(m_client_event_queue.empty()) - event.type = CEE_NONE; - else { - event = m_client_event_queue.front(); - m_client_event_queue.pop(); - } - return event; -} - -ClientActiveObject * ClientEnvironment::getSelectedActiveObject( - const core::line3d &shootline_on_map, v3f *intersection_point, - v3s16 *intersection_normal) -{ - std::vector objects; - getActiveObjects(shootline_on_map.start, - shootline_on_map.getLength() + 3, objects); - const v3f line_vector = shootline_on_map.getVector(); - - // Sort them. - // After this, the closest object is the first in the array. - std::sort(objects.begin(), objects.end()); - - /* Because objects can have different nodebox sizes, - * the object whose center is the nearest isn't necessarily - * the closest one. If an object is found, don't stop - * immediately. */ - - f32 d_min = shootline_on_map.getLength(); - ClientActiveObject *nearest_obj = NULL; - for (u32 i = 0; i < objects.size(); i++) { - ClientActiveObject *obj = objects[i].obj; - - aabb3f *selection_box = obj->getSelectionBox(); - if (selection_box == NULL) - continue; - - v3f pos = obj->getPosition(); - - aabb3f offsetted_box(selection_box->MinEdge + pos, - selection_box->MaxEdge + pos); - - if (offsetted_box.getCenter().getDistanceFrom( - shootline_on_map.start) > d_min + 9.6f*BS) { - // Probably there is no active object that has bigger nodebox than - // (-5.5,-5.5,-5.5,5.5,5.5,5.5) - // 9.6 > 5.5*sqrt(3) - break; - } - - v3f current_intersection; - v3s16 current_normal; - if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector, - ¤t_intersection, ¤t_normal)) { - f32 d_current = current_intersection.getDistanceFrom( - shootline_on_map.start); - if (d_current <= d_min) { - d_min = d_current; - nearest_obj = obj; - *intersection_point = current_intersection; - *intersection_normal = current_normal; - } - } - } - - return nearest_obj; -} - -/* - Check if a node is pointable -*/ -static inline bool isPointableNode(const MapNode &n, - INodeDefManager *ndef, bool liquids_pointable) -{ - const ContentFeatures &features = ndef->get(n); - return features.pointable || - (liquids_pointable && features.isLiquid()); -} - -PointedThing ClientEnvironment::getPointedThing( - core::line3d shootline, - bool liquids_pointable, - bool look_for_object) -{ - PointedThing result; - - INodeDefManager *nodedef = m_map->getNodeDefManager(); - - core::aabbox3d maximal_exceed = nodedef->getSelectionBoxIntUnion(); - // The code needs to search these nodes - core::aabbox3d search_range(-maximal_exceed.MaxEdge, - -maximal_exceed.MinEdge); - // If a node is found, there might be a larger node behind. - // To find it, we have to go further. - s16 maximal_overcheck = - std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X)) - + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y)) - + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z)); - - const v3f original_vector = shootline.getVector(); - const f32 original_length = original_vector.getLength(); - - f32 min_distance = original_length; - - // First try to find an active object - if (look_for_object) { - ClientActiveObject *selected_object = getSelectedActiveObject( - shootline, &result.intersection_point, - &result.intersection_normal); - - if (selected_object != NULL) { - min_distance = - (result.intersection_point - shootline.start).getLength(); - - result.type = POINTEDTHING_OBJECT; - result.object_id = selected_object->getId(); - } - } - - // Reduce shootline - if (original_length > 0) { - shootline.end = shootline.start - + shootline.getVector() / original_length * min_distance; - } - - // Try to find a node that is closer than the selected active - // object (if it exists). - - voxalgo::VoxelLineIterator iterator(shootline.start / BS, - shootline.getVector() / BS); - v3s16 oldnode = iterator.m_current_node_pos; - // Indicates that a node was found. - bool is_node_found = false; - // If a node is found, it is possible that there's a node - // behind it with a large nodebox, so continue the search. - u16 node_foundcounter = 0; - // If a node is found, this is the center of the - // first nodebox the shootline meets. - v3f found_boxcenter(0, 0, 0); - // The untested nodes are in this range. - core::aabbox3d new_nodes; - while (true) { - // Test the nodes around the current node in search_range. - new_nodes = search_range; - new_nodes.MinEdge += iterator.m_current_node_pos; - new_nodes.MaxEdge += iterator.m_current_node_pos; - - // Only check new nodes - v3s16 delta = iterator.m_current_node_pos - oldnode; - if (delta.X > 0) - new_nodes.MinEdge.X = new_nodes.MaxEdge.X; - else if (delta.X < 0) - new_nodes.MaxEdge.X = new_nodes.MinEdge.X; - else if (delta.Y > 0) - new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y; - else if (delta.Y < 0) - new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y; - else if (delta.Z > 0) - new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z; - else if (delta.Z < 0) - new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z; - - // For each untested node - for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) { - for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) { - for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) { - MapNode n; - v3s16 np(x, y, z); - bool is_valid_position; - - n = m_map->getNodeNoEx(np, &is_valid_position); - if (!(is_valid_position && - isPointableNode(n, nodedef, liquids_pointable))) { - continue; - } - std::vector boxes; - n.getSelectionBoxes(nodedef, &boxes, - n.getNeighbors(np, m_map)); - - v3f npf = intToFloat(np, BS); - for (std::vector::const_iterator i = boxes.begin(); - i != boxes.end(); ++i) { - aabb3f box = *i; - box.MinEdge += npf; - box.MaxEdge += npf; - v3f intersection_point; - v3s16 intersection_normal; - if (!boxLineCollision(box, shootline.start, shootline.getVector(), - &intersection_point, &intersection_normal)) { - continue; - } - f32 distance = (intersection_point - shootline.start).getLength(); - if (distance >= min_distance) { - continue; - } - result.type = POINTEDTHING_NODE; - result.node_undersurface = np; - result.intersection_point = intersection_point; - result.intersection_normal = intersection_normal; - found_boxcenter = box.getCenter(); - min_distance = distance; - is_node_found = true; - } - } - } - } - if (is_node_found) { - node_foundcounter++; - if (node_foundcounter > maximal_overcheck) { - break; - } - } - // Next node - if (iterator.hasNext()) { - oldnode = iterator.m_current_node_pos; - iterator.next(); - } else { - break; - } - } - - if (is_node_found) { - // Set undersurface and abovesurface nodes - f32 d = 0.002 * BS; - v3f fake_intersection = result.intersection_point; - // Move intersection towards its source block. - if (fake_intersection.X < found_boxcenter.X) - fake_intersection.X += d; - else - fake_intersection.X -= d; - - if (fake_intersection.Y < found_boxcenter.Y) - fake_intersection.Y += d; - else - fake_intersection.Y -= d; - - if (fake_intersection.Z < found_boxcenter.Z) - fake_intersection.Z += d; - else - fake_intersection.Z -= d; - - result.node_real_undersurface = floatToInt(fake_intersection, BS); - result.node_abovesurface = result.node_real_undersurface - + result.intersection_normal; - } - return result; -} - -#endif // #ifndef SERVER diff --git a/src/environment.h b/src/environment.h index 84805b462..209d795d8 100644 --- a/src/environment.h +++ b/src/environment.h @@ -50,7 +50,6 @@ class ITextureSource; class IGameDef; class Map; class ServerMap; -class ClientMap; class GameScripting; class Player; class RemotePlayer; @@ -525,171 +524,5 @@ private: UNORDERED_MAP m_particle_spawner_attachments; }; -#ifndef SERVER - -#include "clientobject.h" -#include "content_cao.h" - -class ClientSimpleObject; - -/* - The client-side environment. - - This is not thread-safe. - Must be called from main (irrlicht) thread (uses the SceneManager) - Client uses an environment mutex. -*/ - -enum ClientEnvEventType -{ - CEE_NONE, - CEE_PLAYER_DAMAGE, - CEE_PLAYER_BREATH -}; - -struct ClientEnvEvent -{ - ClientEnvEventType type; - union { - //struct{ - //} none; - struct{ - u8 amount; - bool send_to_server; - } player_damage; - struct{ - u16 amount; - } player_breath; - }; -}; - -class ClientEnvironment : public Environment -{ -public: - ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, - ITextureSource *texturesource, IGameDef *gamedef, - IrrlichtDevice *device); - ~ClientEnvironment(); - - Map & getMap(); - ClientMap & getClientMap(); - - IGameDef *getGameDef() - { return m_gamedef; } - - void step(f32 dtime); - - virtual void setLocalPlayer(LocalPlayer *player); - LocalPlayer *getLocalPlayer() { return m_local_player; } - - /* - ClientSimpleObjects - */ - - void addSimpleObject(ClientSimpleObject *simple); - - /* - ActiveObjects - */ - - GenericCAO* getGenericCAO(u16 id); - ClientActiveObject* getActiveObject(u16 id); - - /* - Adds an active object to the environment. - Environment handles deletion of object. - Object may be deleted by environment immediately. - If id of object is 0, assigns a free id to it. - Returns the id of the object. - Returns 0 if not added and thus deleted. - */ - u16 addActiveObject(ClientActiveObject *object); - - void addActiveObject(u16 id, u8 type, const std::string &init_data); - void removeActiveObject(u16 id); - - void processActiveObjectMessage(u16 id, const std::string &data); - - /* - Callbacks for activeobjects - */ - - void damageLocalPlayer(u8 damage, bool handle_hp=true); - void updateLocalPlayerBreath(u16 breath); - - /* - Client likes to call these - */ - - // Get all nearby objects - void getActiveObjects(v3f origin, f32 max_d, - std::vector &dest); - - // Get event from queue. CEE_NONE is returned if queue is empty. - ClientEnvEvent getClientEvent(); - - /*! - * Gets closest object pointed by the shootline. - * Returns NULL if not found. - * - * \param[in] shootline_on_map the shootline for - * the test in world coordinates - * \param[out] intersection_point the first point where - * the shootline meets the object. Valid only if - * not NULL is returned. - * \param[out] intersection_normal the normal vector of - * the intersection, pointing outwards. Zero vector if - * the shootline starts in an active object. - * Valid only if not NULL is returned. - */ - ClientActiveObject * getSelectedActiveObject( - const core::line3d &shootline_on_map, - v3f *intersection_point, - v3s16 *intersection_normal - ); - - /*! - * Performs a raycast on the world. - * Returns the first thing the shootline meets. - * - * @param[in] shootline the shootline, starting from - * the camera position. This also gives the maximal distance - * of the search. - * @param[in] liquids_pointable if false, liquids are ignored - * @param[in] look_for_object if false, objects are ignored - */ - PointedThing getPointedThing( - core::line3d shootline, - bool liquids_pointable, - bool look_for_object); - - u16 attachement_parent_ids[USHRT_MAX + 1]; - - const std::list &getPlayerNames() { return m_player_names; } - void addPlayerName(const std::string &name) { m_player_names.push_back(name); } - void removePlayerName(const std::string &name) { m_player_names.remove(name); } - void updateCameraOffset(v3s16 camera_offset) - { m_camera_offset = camera_offset; } - v3s16 getCameraOffset() const { return m_camera_offset; } -private: - ClientMap *m_map; - LocalPlayer *m_local_player; - scene::ISceneManager *m_smgr; - ITextureSource *m_texturesource; - IGameDef *m_gamedef; - IrrlichtDevice *m_irr; - UNORDERED_MAP m_active_objects; - std::vector m_simple_objects; - std::queue m_client_event_queue; - IntervalLimiter m_active_object_light_update_interval; - IntervalLimiter m_lava_hurt_interval; - IntervalLimiter m_drowning_interval; - IntervalLimiter m_breathing_interval; - std::list m_player_names; - v3s16 m_camera_offset; -}; - -#endif - #endif -- cgit v1.2.3 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/CMakeLists.txt | 1 + src/clientiface.cpp | 2 +- src/collision.cpp | 1 + src/environment.cpp | 2149 ----------------------------------- src/environment.h | 397 ------- src/inventorymanager.cpp | 2 +- src/pathfinder.cpp | 2 +- src/script/lua_api/l_env.h | 2 +- src/script/lua_api/l_nodemeta.cpp | 2 +- src/script/lua_api/l_nodetimer.cpp | 2 +- src/server.h | 2 +- src/serverenvironment.cpp | 2167 ++++++++++++++++++++++++++++++++++++ src/serverenvironment.h | 423 +++++++ src/treegen.cpp | 3 +- 14 files changed, 2600 insertions(+), 2555 deletions(-) create mode 100644 src/serverenvironment.cpp create mode 100644 src/serverenvironment.h (limited to 'src/environment.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5228eb9ac..f90542be9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -452,6 +452,7 @@ set(common_SRCS rollback_interface.cpp serialization.cpp server.cpp + serverenvironment.cpp serverlist.cpp serverobject.cpp settings.cpp diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 0390cf0ff..1610c21fd 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "mapblock.h" #include "network/connection.h" -#include "environment.h" +#include "serverenvironment.h" #include "map.h" #include "emerge.h" #include "content_sao.h" // TODO this is used for cleanup of only diff --git a/src/collision.cpp b/src/collision.cpp index 21f14bec1..595fa8059 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "gamedef.h" #include "clientenvironment.h" +#include "serverenvironment.h" #include "serverobject.h" #include "profiler.h" diff --git a/src/environment.cpp b/src/environment.cpp index 6f6e20238..8c1aad9d3 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -19,35 +19,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "environment.h" -#include "filesys.h" -#include "porting.h" #include "collision.h" -#include "content_mapnode.h" -#include "mapblock.h" #include "serverobject.h" -#include "content_sao.h" -#include "settings.h" -#include "log.h" -#include "profiler.h" #include "scripting_game.h" -#include "nodedef.h" -#include "nodemetadata.h" -#include "gamedef.h" #include "server.h" #include "daynightratio.h" -#include "map.h" #include "emerge.h" -#include "raycast.h" -#include "voxelalgorithms.h" -#include "util/serialize.h" -#include "util/basic_macros.h" -#include "util/pointedthing.h" -#include "threading/mutex_auto_lock.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 Environment::Environment(): m_time_of_day_speed(0), @@ -147,2129 +124,3 @@ u32 Environment::getDayCount() // Atomic counter return m_day_count; } - - -/* - 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); - } -} - diff --git a/src/environment.h b/src/environment.h index 209d795d8..14a18421b 100644 --- a/src/environment.h +++ b/src/environment.h @@ -30,7 +30,6 @@ with this program; if not, write to the Free Software Foundation, Inc., - etc. */ -#include #include #include #include @@ -43,17 +42,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "threading/atomic.h" #include "network/networkprotocol.h" // for AccessDeniedCode -class ServerEnvironment; -class ActiveBlockModifier; -class ServerActiveObject; class ITextureSource; class IGameDef; class Map; -class ServerMap; class GameScripting; class Player; -class RemotePlayer; -class PlayerSAO; class PointedThing; class Environment @@ -134,395 +127,5 @@ private: DISABLE_CLASS_COPY(Environment); }; -/* - {Active, Loading} block modifier interface. - - These are fed into ServerEnvironment at initialization time; - ServerEnvironment handles deleting them. -*/ - -class ActiveBlockModifier -{ -public: - ActiveBlockModifier(){}; - virtual ~ActiveBlockModifier(){}; - - // Set of contents to trigger on - virtual std::set getTriggerContents()=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(); } - // Trigger interval in seconds - virtual float getTriggerInterval() = 0; - // Random chance of (1 / return value), 0 is disallowed - virtual u32 getTriggerChance() = 0; - // Whether to modify chance to simulate time lost by an unnattended block - virtual bool getSimpleCatchUp() = 0; - // This is called usually at interval for 1/chance of the nodes - virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){}; - virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, - u32 active_object_count, u32 active_object_count_wider){}; -}; - -struct ABMWithState -{ - ActiveBlockModifier *abm; - float timer; - - ABMWithState(ActiveBlockModifier *abm_); -}; - -struct LoadingBlockModifierDef -{ - // Set of contents to trigger on - std::set trigger_contents; - std::string name; - bool run_at_every_load; - - virtual ~LoadingBlockModifierDef() {} - virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){}; -}; - -struct LBMContentMapping -{ - typedef std::map > container_map; - container_map map; - - std::vector lbm_list; - - // Needs to be separate method (not inside destructor), - // because the LBMContentMapping may be copied and destructed - // many times during operation in the lbm_lookup_map. - void deleteContents(); - void addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef); - const std::vector *lookup(content_t c) const; -}; - -class LBMManager -{ -public: - LBMManager(): - m_query_mode(false) - {} - - ~LBMManager(); - - // Don't call this after loadIntroductionTimes() ran. - void addLBMDef(LoadingBlockModifierDef *lbm_def); - - void loadIntroductionTimes(const std::string ×, - IGameDef *gamedef, u32 now); - - // Don't call this before loadIntroductionTimes() ran. - std::string createIntroductionTimesString(); - - // Don't call this before loadIntroductionTimes() ran. - void applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp); - - // Warning: do not make this std::unordered_map, order is relevant here - typedef std::map lbm_lookup_map; - -private: - // Once we set this to true, we can only query, - // not modify - bool m_query_mode; - - // For m_query_mode == false: - // The key of the map is the LBM def's name. - // TODO make this std::unordered_map - std::map m_lbm_defs; - - // For m_query_mode == true: - // The key of the map is the LBM def's first introduction time. - lbm_lookup_map m_lbm_lookup; - - // Returns an iterator to the LBMs that were introduced - // after the given time. This is guaranteed to return - // valid values for everything - lbm_lookup_map::const_iterator getLBMsIntroducedAfter(u32 time) - { return m_lbm_lookup.lower_bound(time); } -}; - -/* - List of active blocks, used by ServerEnvironment -*/ - -class ActiveBlockList -{ -public: - void update(std::vector &active_positions, - s16 radius, - std::set &blocks_removed, - std::set &blocks_added); - - bool contains(v3s16 p){ - return (m_list.find(p) != m_list.end()); - } - - void clear(){ - m_list.clear(); - } - - std::set m_list; - std::set m_forceloaded_list; - -private: -}; - -/* - Operation mode for ServerEnvironment::clearObjects() -*/ -enum ClearObjectsMode { - // Load and go through every mapblock, clearing objects - CLEAR_OBJECTS_MODE_FULL, - - // Clear objects immediately in loaded mapblocks; - // clear objects in unloaded mapblocks only when the mapblocks are next activated. - CLEAR_OBJECTS_MODE_QUICK, -}; - -/* - The server-side environment. - - This is not thread-safe. Server uses an environment mutex. -*/ - -typedef UNORDERED_MAP ActiveObjectMap; - -class ServerEnvironment : public Environment -{ -public: - ServerEnvironment(ServerMap *map, GameScripting *scriptIface, - IGameDef *gamedef, const std::string &path_world); - ~ServerEnvironment(); - - Map & getMap(); - - ServerMap & getServerMap(); - - //TODO find way to remove this fct! - GameScripting* getScriptIface() - { return m_script; } - - IGameDef *getGameDef() - { return m_gamedef; } - - float getSendRecommendedInterval() - { return m_recommended_send_interval; } - - void kickAllPlayers(AccessDeniedCode reason, - const std::string &str_reason, bool reconnect); - // Save players - void saveLoadedPlayers(); - void savePlayer(RemotePlayer *player); - RemotePlayer *loadPlayer(const std::string &playername, PlayerSAO *sao); - void addPlayer(RemotePlayer *player); - void removePlayer(RemotePlayer *player); - - /* - Save and load time of day and game timer - */ - void saveMeta(); - void loadMeta(); - // to be called instead of loadMeta if - // env_meta.txt doesn't exist (e.g. new world) - void loadDefaultMeta(); - - u32 addParticleSpawner(float exptime); - u32 addParticleSpawner(float exptime, u16 attached_id); - void deleteParticleSpawner(u32 id, bool remove_from_object = true); - - /* - External ActiveObject interface - ------------------------------------------- - */ - - ServerActiveObject* getActiveObject(u16 id); - - /* - Add an active object to the environment. - Environment handles deletion of object. - Object may be deleted by environment immediately. - If id of object is 0, assigns a free id to it. - Returns the id of the object. - Returns 0 if not added and thus deleted. - */ - u16 addActiveObject(ServerActiveObject *object); - - /* - Add an active object as a static object to the corresponding - MapBlock. - Caller allocates memory, ServerEnvironment frees memory. - Return value: true if succeeded, false if failed. - (note: not used, pending removal from engine) - */ - //bool addActiveObjectAsStatic(ServerActiveObject *object); - - /* - Find out what new objects have been added to - inside a radius around a position - */ - void getAddedActiveObjects(PlayerSAO *playersao, s16 radius, - s16 player_radius, - std::set ¤t_objects, - std::queue &added_objects); - - /* - Find out what new objects have been removed from - inside a radius around a position - */ - void getRemovedActiveObjects(PlayerSAO *playersao, s16 radius, - s16 player_radius, - std::set ¤t_objects, - std::queue &removed_objects); - - /* - Get the next message emitted by some active object. - Returns a message with id=0 if no messages are available. - */ - ActiveObjectMessage getActiveObjectMessage(); - - /* - Activate objects and dynamically modify for the dtime determined - from timestamp and additional_dtime - */ - void activateBlock(MapBlock *block, u32 additional_dtime=0); - - /* - {Active,Loading}BlockModifiers - ------------------------------------------- - */ - - void addActiveBlockModifier(ActiveBlockModifier *abm); - void addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm); - - /* - Other stuff - ------------------------------------------- - */ - - // Script-aware node setters - bool setNode(v3s16 p, const MapNode &n); - bool removeNode(v3s16 p); - bool swapNode(v3s16 p, const MapNode &n); - - // Find all active objects inside a radius around a point - void getObjectsInsideRadius(std::vector &objects, v3f pos, float radius); - - // Clear objects, loading and going through every MapBlock - void clearObjects(ClearObjectsMode mode); - - // This makes stuff happen - void step(f32 dtime); - - //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; } - - void reportMaxLagEstimate(float f) { m_max_lag_estimate = f; } - float getMaxLagEstimate() { return m_max_lag_estimate; } - - std::set* getForceloadedBlocks() { return &m_active_blocks.m_forceloaded_list; }; - - // Sets the static object status all the active objects in the specified block - // This is only really needed for deleting blocks from the map - void setStaticForActiveObjectsInBlock(v3s16 blockpos, - bool static_exists, v3s16 static_block=v3s16(0,0,0)); - - RemotePlayer *getPlayer(const u16 peer_id); - RemotePlayer *getPlayer(const char* name); -private: - - /* - Internal ActiveObject interface - ------------------------------------------- - */ - - /* - Add an active object to the environment. - - Called by addActiveObject. - - Object may be deleted by environment immediately. - If id of object is 0, assigns a free id to it. - Returns the id of the object. - Returns 0 if not added and thus deleted. - */ - u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed, u32 dtime_s); - - /* - Remove all objects that satisfy (m_removed && m_known_by_count==0) - */ - void removeRemovedObjects(); - - /* - Convert stored objects from block to active - */ - void activateObjects(MapBlock *block, u32 dtime_s); - - /* - Convert objects that are not in active blocks to static. - - If m_known_by_count != 0, active object is not deleted, but static - data is still updated. - - If force_delete is set, active object is deleted nevertheless. It - shall only be set so in the destructor of the environment. - */ - void deactivateFarObjects(bool force_delete); - - /* - Member variables - */ - - // The map - ServerMap *m_map; - // Lua state - GameScripting* m_script; - // Game definition - IGameDef *m_gamedef; - // World path - const std::string m_path_world; - // Active object list - ActiveObjectMap m_active_objects; - // Outgoing network message buffer for active objects - std::queue m_active_object_messages; - // Some timers - float m_send_recommended_timer; - IntervalLimiter m_object_management_interval; - // List of active blocks - ActiveBlockList m_active_blocks; - IntervalLimiter m_active_blocks_management_interval; - IntervalLimiter m_active_block_modifier_interval; - IntervalLimiter m_active_blocks_nodemetadata_interval; - int m_active_block_interval_overload_skip; - // Time from the beginning of the game in seconds. - // Incremented in step(). - u32 m_game_time; - // A helper variable for incrementing the latter - float m_game_time_fraction_counter; - // Time of last clearObjects call (game time). - // When a mapblock older than this is loaded, its objects are cleared. - u32 m_last_clear_objects_time; - // Active block modifiers - std::vector m_abms; - LBMManager m_lbm_mgr; - // An interval for generally sending object positions and stuff - float m_recommended_send_interval; - // Estimate for general maximum lag as determined by server. - // Can raise to high values like 15s with eg. map generation mods. - float m_max_lag_estimate; - - // peer_ids in here should be unique, except that there may be many 0s - std::vector m_players; - - // Particles - IntervalLimiter m_particle_management_interval; - UNORDERED_MAP m_particle_spawners; - UNORDERED_MAP m_particle_spawner_attachments; -}; - #endif diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index f36a2fa4e..469e7396b 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventorymanager.h" #include "log.h" -#include "environment.h" +#include "serverenvironment.h" #include "scripting_game.h" #include "serverobject.h" #include "settings.h" diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp index 073670c6d..84aa9252c 100644 --- a/src/pathfinder.cpp +++ b/src/pathfinder.cpp @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., /******************************************************************************/ #include "pathfinder.h" -#include "environment.h" +#include "serverenvironment.h" #include "gamedef.h" #include "nodedef.h" #include "map.h" diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 89dd7978f..21b235f84 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define L_ENV_H_ #include "lua_api/l_base.h" -#include "environment.h" +#include "serverenvironment.h" class ModApiEnvMod : public ModApiBase { private: diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index c8bc7d558..3cdd3cbfe 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_inventory.h" #include "common/c_converter.h" #include "common/c_content.h" -#include "environment.h" +#include "serverenvironment.h" #include "map.h" #include "gamedef.h" #include "nodemetadata.h" diff --git a/src/script/lua_api/l_nodetimer.cpp b/src/script/lua_api/l_nodetimer.cpp index 3242d6ea5..ed11cc58e 100644 --- a/src/script/lua_api/l_nodetimer.cpp +++ b/src/script/lua_api/l_nodetimer.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_nodetimer.h" #include "lua_api/l_internal.h" -#include "environment.h" +#include "serverenvironment.h" #include "map.h" diff --git a/src/server.h b/src/server.h index f0df0f9ec..fe7b50b77 100644 --- a/src/server.h +++ b/src/server.h @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "util/thread.h" #include "util/basic_macros.h" -#include "environment.h" +#include "serverenvironment.h" #include "chat_interface.h" #include "clientiface.h" #include "remoteplayer.h" 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); + } +} diff --git a/src/serverenvironment.h b/src/serverenvironment.h new file mode 100644 index 000000000..20a783ea5 --- /dev/null +++ b/src/serverenvironment.h @@ -0,0 +1,423 @@ +/* +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. +*/ + +#ifndef SERVER_ENVIRONMENT_HEADER +#define SERVER_ENVIRONMENT_HEADER + +#include "environment.h" + +class IGameDef; +class ServerMap; +class RemotePlayer; +class PlayerSAO; +class ServerEnvironment; +class ActiveBlockModifier; +class ServerActiveObject; + +/* + {Active, Loading} block modifier interface. + + These are fed into ServerEnvironment at initialization time; + ServerEnvironment handles deleting them. +*/ + +class ActiveBlockModifier +{ +public: + ActiveBlockModifier(){}; + virtual ~ActiveBlockModifier(){}; + + // Set of contents to trigger on + virtual std::set getTriggerContents()=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(); } + // Trigger interval in seconds + virtual float getTriggerInterval() = 0; + // Random chance of (1 / return value), 0 is disallowed + virtual u32 getTriggerChance() = 0; + // Whether to modify chance to simulate time lost by an unnattended block + virtual bool getSimpleCatchUp() = 0; + // This is called usually at interval for 1/chance of the nodes + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){}; + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, + u32 active_object_count, u32 active_object_count_wider){}; +}; + +struct ABMWithState +{ + ActiveBlockModifier *abm; + float timer; + + ABMWithState(ActiveBlockModifier *abm_); +}; + +struct LoadingBlockModifierDef +{ + // Set of contents to trigger on + std::set trigger_contents; + std::string name; + bool run_at_every_load; + + virtual ~LoadingBlockModifierDef() {} + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){}; +}; + +struct LBMContentMapping +{ + typedef std::map > container_map; + container_map map; + + std::vector lbm_list; + + // Needs to be separate method (not inside destructor), + // because the LBMContentMapping may be copied and destructed + // many times during operation in the lbm_lookup_map. + void deleteContents(); + void addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef); + const std::vector *lookup(content_t c) const; +}; + +class LBMManager +{ +public: + LBMManager(): + m_query_mode(false) + {} + + ~LBMManager(); + + // Don't call this after loadIntroductionTimes() ran. + void addLBMDef(LoadingBlockModifierDef *lbm_def); + + void loadIntroductionTimes(const std::string ×, + IGameDef *gamedef, u32 now); + + // Don't call this before loadIntroductionTimes() ran. + std::string createIntroductionTimesString(); + + // Don't call this before loadIntroductionTimes() ran. + void applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp); + + // Warning: do not make this std::unordered_map, order is relevant here + typedef std::map lbm_lookup_map; + +private: + // Once we set this to true, we can only query, + // not modify + bool m_query_mode; + + // For m_query_mode == false: + // The key of the map is the LBM def's name. + // TODO make this std::unordered_map + std::map m_lbm_defs; + + // For m_query_mode == true: + // The key of the map is the LBM def's first introduction time. + lbm_lookup_map m_lbm_lookup; + + // Returns an iterator to the LBMs that were introduced + // after the given time. This is guaranteed to return + // valid values for everything + lbm_lookup_map::const_iterator getLBMsIntroducedAfter(u32 time) + { return m_lbm_lookup.lower_bound(time); } +}; + +/* + List of active blocks, used by ServerEnvironment +*/ + +class ActiveBlockList +{ +public: + void update(std::vector &active_positions, + s16 radius, + std::set &blocks_removed, + std::set &blocks_added); + + bool contains(v3s16 p){ + return (m_list.find(p) != m_list.end()); + } + + void clear(){ + m_list.clear(); + } + + std::set m_list; + std::set m_forceloaded_list; + +private: +}; + +/* + Operation mode for ServerEnvironment::clearObjects() +*/ +enum ClearObjectsMode { + // Load and go through every mapblock, clearing objects + CLEAR_OBJECTS_MODE_FULL, + + // Clear objects immediately in loaded mapblocks; + // clear objects in unloaded mapblocks only when the mapblocks are next activated. + CLEAR_OBJECTS_MODE_QUICK, +}; + +/* + The server-side environment. + + This is not thread-safe. Server uses an environment mutex. +*/ + +typedef UNORDERED_MAP ActiveObjectMap; + +class ServerEnvironment : public Environment +{ +public: + ServerEnvironment(ServerMap *map, GameScripting *scriptIface, + IGameDef *gamedef, const std::string &path_world); + ~ServerEnvironment(); + + Map & getMap(); + + ServerMap & getServerMap(); + + //TODO find way to remove this fct! + GameScripting* getScriptIface() + { return m_script; } + + IGameDef *getGameDef() + { return m_gamedef; } + + float getSendRecommendedInterval() + { return m_recommended_send_interval; } + + void kickAllPlayers(AccessDeniedCode reason, + const std::string &str_reason, bool reconnect); + // Save players + void saveLoadedPlayers(); + void savePlayer(RemotePlayer *player); + RemotePlayer *loadPlayer(const std::string &playername, PlayerSAO *sao); + void addPlayer(RemotePlayer *player); + void removePlayer(RemotePlayer *player); + + /* + Save and load time of day and game timer + */ + void saveMeta(); + void loadMeta(); + // to be called instead of loadMeta if + // env_meta.txt doesn't exist (e.g. new world) + void loadDefaultMeta(); + + u32 addParticleSpawner(float exptime); + u32 addParticleSpawner(float exptime, u16 attached_id); + void deleteParticleSpawner(u32 id, bool remove_from_object = true); + + /* + External ActiveObject interface + ------------------------------------------- + */ + + ServerActiveObject* getActiveObject(u16 id); + + /* + Add an active object to the environment. + Environment handles deletion of object. + Object may be deleted by environment immediately. + If id of object is 0, assigns a free id to it. + Returns the id of the object. + Returns 0 if not added and thus deleted. + */ + u16 addActiveObject(ServerActiveObject *object); + + /* + Add an active object as a static object to the corresponding + MapBlock. + Caller allocates memory, ServerEnvironment frees memory. + Return value: true if succeeded, false if failed. + (note: not used, pending removal from engine) + */ + //bool addActiveObjectAsStatic(ServerActiveObject *object); + + /* + Find out what new objects have been added to + inside a radius around a position + */ + void getAddedActiveObjects(PlayerSAO *playersao, s16 radius, + s16 player_radius, + std::set ¤t_objects, + std::queue &added_objects); + + /* + Find out what new objects have been removed from + inside a radius around a position + */ + void getRemovedActiveObjects(PlayerSAO *playersao, s16 radius, + s16 player_radius, + std::set ¤t_objects, + std::queue &removed_objects); + + /* + Get the next message emitted by some active object. + Returns a message with id=0 if no messages are available. + */ + ActiveObjectMessage getActiveObjectMessage(); + + /* + Activate objects and dynamically modify for the dtime determined + from timestamp and additional_dtime + */ + void activateBlock(MapBlock *block, u32 additional_dtime=0); + + /* + {Active,Loading}BlockModifiers + ------------------------------------------- + */ + + void addActiveBlockModifier(ActiveBlockModifier *abm); + void addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm); + + /* + Other stuff + ------------------------------------------- + */ + + // Script-aware node setters + bool setNode(v3s16 p, const MapNode &n); + bool removeNode(v3s16 p); + bool swapNode(v3s16 p, const MapNode &n); + + // Find all active objects inside a radius around a point + void getObjectsInsideRadius(std::vector &objects, v3f pos, float radius); + + // Clear objects, loading and going through every MapBlock + void clearObjects(ClearObjectsMode mode); + + // This makes stuff happen + void step(f32 dtime); + + //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; } + + void reportMaxLagEstimate(float f) { m_max_lag_estimate = f; } + float getMaxLagEstimate() { return m_max_lag_estimate; } + + std::set* getForceloadedBlocks() { return &m_active_blocks.m_forceloaded_list; }; + + // Sets the static object status all the active objects in the specified block + // This is only really needed for deleting blocks from the map + void setStaticForActiveObjectsInBlock(v3s16 blockpos, + bool static_exists, v3s16 static_block=v3s16(0,0,0)); + + RemotePlayer *getPlayer(const u16 peer_id); + RemotePlayer *getPlayer(const char* name); +private: + + /* + Internal ActiveObject interface + ------------------------------------------- + */ + + /* + Add an active object to the environment. + + Called by addActiveObject. + + Object may be deleted by environment immediately. + If id of object is 0, assigns a free id to it. + Returns the id of the object. + Returns 0 if not added and thus deleted. + */ + u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed, u32 dtime_s); + + /* + Remove all objects that satisfy (m_removed && m_known_by_count==0) + */ + void removeRemovedObjects(); + + /* + Convert stored objects from block to active + */ + void activateObjects(MapBlock *block, u32 dtime_s); + + /* + Convert objects that are not in active blocks to static. + + If m_known_by_count != 0, active object is not deleted, but static + data is still updated. + + If force_delete is set, active object is deleted nevertheless. It + shall only be set so in the destructor of the environment. + */ + void deactivateFarObjects(bool force_delete); + + /* + Member variables + */ + + // The map + ServerMap *m_map; + // Lua state + GameScripting* m_script; + // Game definition + IGameDef *m_gamedef; + // World path + const std::string m_path_world; + // Active object list + ActiveObjectMap m_active_objects; + // Outgoing network message buffer for active objects + std::queue m_active_object_messages; + // Some timers + float m_send_recommended_timer; + IntervalLimiter m_object_management_interval; + // List of active blocks + ActiveBlockList m_active_blocks; + IntervalLimiter m_active_blocks_management_interval; + IntervalLimiter m_active_block_modifier_interval; + IntervalLimiter m_active_blocks_nodemetadata_interval; + int m_active_block_interval_overload_skip; + // Time from the beginning of the game in seconds. + // Incremented in step(). + u32 m_game_time; + // A helper variable for incrementing the latter + float m_game_time_fraction_counter; + // Time of last clearObjects call (game time). + // When a mapblock older than this is loaded, its objects are cleared. + u32 m_last_clear_objects_time; + // Active block modifiers + std::vector m_abms; + LBMManager m_lbm_mgr; + // An interval for generally sending object positions and stuff + float m_recommended_send_interval; + // Estimate for general maximum lag as determined by server. + // Can raise to high values like 15s with eg. map generation mods. + float m_max_lag_estimate; + + // peer_ids in here should be unique, except that there may be many 0s + std::vector m_players; + + // Particles + IntervalLimiter m_particle_management_interval; + UNORDERED_MAP m_particle_spawners; + UNORDERED_MAP m_particle_spawner_attachments; +}; + +#endif diff --git a/src/treegen.cpp b/src/treegen.cpp index f37bf0e86..4df574f34 100644 --- a/src/treegen.cpp +++ b/src/treegen.cpp @@ -21,9 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "util/pointer.h" #include "util/numeric.h" -#include "util/mathconstants.h" #include "map.h" -#include "environment.h" +#include "serverenvironment.h" #include "nodedef.h" #include "treegen.h" -- 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/environment.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/environment.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 3a90b78a037df3eb9098d4fddb1289ed8ee21329 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Thu, 6 Apr 2017 16:03:29 +0200 Subject: LINT: Switch whitelist check from egrep to awk Bonus: make CI happy with the last rules fix --- src/client/keys.h | 3 ++- src/environment.cpp | 1 + src/gameparams.h | 3 ++- src/gettime.h | 3 ++- src/script/lua_api/l_client.cpp | 4 ++-- util/travis/lint.sh | 5 +++-- 6 files changed, 12 insertions(+), 7 deletions(-) (limited to 'src/environment.cpp') diff --git a/src/client/keys.h b/src/client/keys.h index b446d3a1e..76ae38ff0 100644 --- a/src/client/keys.h +++ b/src/client/keys.h @@ -25,7 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., class KeyType { public: - enum T { + enum T + { // Player movement FORWARD, BACKWARD, diff --git a/src/environment.cpp b/src/environment.cpp index d1ea5f8bb..9c2ea8896 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "daynightratio.h" #include "emerge.h" + Environment::Environment(IGameDef *gamedef): m_time_of_day_speed(0), m_time_of_day(9000), diff --git a/src/gameparams.h b/src/gameparams.h index bf9953c39..4afc0fdce 100644 --- a/src/gameparams.h +++ b/src/gameparams.h @@ -24,7 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., struct SubgameSpec; -struct GameParams { +struct GameParams +{ u16 socket_port; std::string world_path; SubgameSpec game_spec; diff --git a/src/gettime.h b/src/gettime.h index 6e70b8533..b2f09a7bb 100644 --- a/src/gettime.h +++ b/src/gettime.h @@ -31,7 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc., Normal build: main.cpp Server build: servermain.cpp */ -enum TimePrecision { +enum TimePrecision +{ PRECISION_SECONDS = 0, PRECISION_MILLI, PRECISION_MICRO, diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index 0f4d7eaae..be3a749de 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -212,8 +212,8 @@ int ModApiClient::l_sound_play(lua_State *L) if (!lua_isnil(L, -1)) { v3f pos = read_v3f(L, -1) * BS; lua_pop(L, 1); - handle = - sound->playSoundAt(spec.name, looped, gain * spec.gain, pos); + handle = sound->playSoundAt( + spec.name, looped, gain * spec.gain, pos); lua_pushinteger(L, handle); return 1; } diff --git a/util/travis/lint.sh b/util/travis/lint.sh index c1df2d5fa..4e816bd94 100644 --- a/util/travis/lint.sh +++ b/util/travis/lint.sh @@ -6,6 +6,7 @@ function perform_lint() { else CLANG_FORMAT=clang-format fi + echo "LINT: Using binary $CLANG_FORMAT" CLANG_FORMAT_WHITELIST="util/travis/clang-format-whitelist.txt" if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then @@ -22,10 +23,10 @@ function perform_lint() { d=$(diff -u "$f" <(${CLANG_FORMAT} "$f") || true) if ! [ -z "$d" ]; then - whitelisted=$(egrep -c "^${f}" "${CLANG_FORMAT_WHITELIST}") + whitelisted=$(awk '$1 == "'$f'" { print 1 }' "$CLANG_FORMAT_WHITELIST") # If file is not whitelisted, mark a failure - if [ ${whitelisted} -eq 0 ]; then + if [ -z ${whitelisted} ]; then errorcount=$((errorcount+1)) printf "The file %s is not compliant with the coding style" "$f" -- cgit v1.2.3 From f3fe62a0bf9e775b3e6e838f104ab605a2238792 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Wed, 19 Apr 2017 23:02:07 +0200 Subject: Fix various copy instead of const ref reported by cppcheck (#5615) * Also remove InventoryList::peekItem unused function * Fix some post increment to preincrement reported by cppcheck --- src/chat.cpp | 2 +- src/chat.h | 6 +++--- src/craftdef.h | 11 +++++++---- src/environment.cpp | 4 ++-- src/exceptions.h | 9 +++------ src/game.cpp | 9 ++------- src/httpfetch.cpp | 5 +++-- src/inventory.cpp | 28 +++++++++------------------- src/inventory.h | 7 ++----- src/script/common/c_content.cpp | 19 +++++++------------ src/script/common/c_content.h | 4 ++-- src/script/lua_api/l_item.cpp | 2 +- src/tool.h | 8 ++++---- 13 files changed, 46 insertions(+), 68 deletions(-) (limited to 'src/environment.cpp') diff --git a/src/chat.cpp b/src/chat.cpp index 46555b3dc..de7483e22 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -386,7 +386,7 @@ s32 ChatBuffer::getBottomScrollPos() const -ChatPrompt::ChatPrompt(std::wstring prompt, u32 history_limit): +ChatPrompt::ChatPrompt(const std::wstring &prompt, u32 history_limit): m_prompt(prompt), m_line(L""), m_history(), diff --git a/src/chat.h b/src/chat.h index 11061fd39..5de676a2e 100644 --- a/src/chat.h +++ b/src/chat.h @@ -38,14 +38,14 @@ struct ChatLine // message text EnrichedString text; - ChatLine(std::wstring a_name, std::wstring a_text): + ChatLine(const std::wstring &a_name, const std::wstring &a_text): age(0.0), name(a_name), text(a_text) { } - ChatLine(EnrichedString a_name, EnrichedString a_text): + ChatLine(const EnrichedString &a_name, const EnrichedString &a_text): age(0.0), name(a_name), text(a_text) @@ -148,7 +148,7 @@ private: class ChatPrompt { public: - ChatPrompt(std::wstring prompt, u32 history_limit); + ChatPrompt(const std::wstring &prompt, u32 history_limit); ~ChatPrompt(); // Input character or string diff --git a/src/craftdef.h b/src/craftdef.h index 695ee0c2c..bdd741f7c 100644 --- a/src/craftdef.h +++ b/src/craftdef.h @@ -97,7 +97,7 @@ struct CraftOutput CraftOutput(): item(""), time(0) {} - CraftOutput(std::string item_, float time_): + CraftOutput(const std::string &item_, float time_): item(item_), time(time_) {} std::string dump() const; @@ -124,7 +124,7 @@ struct CraftReplacements CraftReplacements(): pairs() {} - CraftReplacements(std::vector > pairs_): + CraftReplacements(const std::vector > &pairs_): pairs(pairs_) {} std::string dump() const; @@ -359,10 +359,13 @@ public: CraftDefinitionFuel(): recipe(""), hash_inited(false), burntime() {} - CraftDefinitionFuel(std::string recipe_, + CraftDefinitionFuel(const std::string &recipe_, float burntime_, const CraftReplacements &replacements_): - recipe(recipe_), hash_inited(false), burntime(burntime_), replacements(replacements_) + recipe(recipe_), + hash_inited(false), + burntime(burntime_), + replacements(replacements_) {} virtual ~CraftDefinitionFuel(){} diff --git a/src/environment.cpp b/src/environment.cpp index 9c2ea8896..c1aeeec60 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -70,7 +70,7 @@ void Environment::setTimeOfDay(u32 time) { MutexAutoLock lock(this->m_time_lock); if (m_time_of_day > time) - m_day_count++; + ++m_day_count; m_time_of_day = time; m_time_of_day_f = (float)time / 24000.0; } @@ -103,7 +103,7 @@ void Environment::stepTimeOfDay(float dtime) // Sync at overflow if (m_time_of_day + units >= 24000) { sync_f = true; - m_day_count++; + ++m_day_count; } m_time_of_day = (m_time_of_day + units) % 24000; if (sync_f) diff --git a/src/exceptions.h b/src/exceptions.h index 67a2d0df6..1b39c6725 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -27,10 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class BaseException : public std::exception { public: - BaseException(const std::string &s) throw() - { - m_s = s; - } + BaseException(const std::string &s) throw(): m_s(s) {} ~BaseException() throw() {} virtual const char * what() const throw() { @@ -122,12 +119,12 @@ public: class ClientStateError : public BaseException { public: - ClientStateError(std::string s): BaseException(s) {} + ClientStateError(const std::string &s): BaseException(s) {} }; class PrngException : public BaseException { public: - PrngException(std::string s): BaseException(s) {} + PrngException(const std::string &s): BaseException(s) {} }; class ModError : public BaseException { diff --git a/src/game.cpp b/src/game.cpp index bcf378e4e..fbc0cf35f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -127,8 +127,8 @@ struct TextDestPlayerInventory : public TextDest struct LocalFormspecHandler : public TextDest { - LocalFormspecHandler(std::string formname): - m_client(0) + LocalFormspecHandler(const std::string &formname): + m_client(NULL) { m_formname = formname; } @@ -139,11 +139,6 @@ struct LocalFormspecHandler : public TextDest m_formname = formname; } - void gotText(const std::wstring &message) - { - errorstream << "LocalFormspecHandler::gotText old style message received" << std::endl; - } - void gotText(const StringMap &fields) { if (m_formname == "MT_PAUSE_MENU") { diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp index f64c9f717..21400355a 100644 --- a/src/httpfetch.cpp +++ b/src/httpfetch.cpp @@ -207,7 +207,7 @@ public: class HTTPFetchOngoing { public: - HTTPFetchOngoing(HTTPFetchRequest request, CurlHandlePool *pool); + HTTPFetchOngoing(const HTTPFetchRequest &request, CurlHandlePool *pool); ~HTTPFetchOngoing(); CURLcode start(CURLM *multi); @@ -228,7 +228,8 @@ private: }; -HTTPFetchOngoing::HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *pool_): +HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_, + CurlHandlePool *pool_): pool(pool_), curl(NULL), multi(NULL), diff --git a/src/inventory.cpp b/src/inventory.cpp index 6d5b49916..8617f7263 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -46,12 +46,11 @@ static content_t content_translate_from_19_to_internal(content_t c_from) } ItemStack::ItemStack(const std::string &name_, u16 count_, - u16 wear_, IItemDefManager *itemdef) + u16 wear_, IItemDefManager *itemdef) : + name(itemdef->getAlias(name_)), + count(count_), + wear(wear_) { - name = itemdef->getAlias(name_); - count = count_; - wear = wear_; - if (name.empty() || count == 0) clear(); else if (itemdef->get(name).type == ITEM_TOOL) @@ -370,14 +369,13 @@ ItemStack ItemStack::peekItem(u32 peekcount) const Inventory */ -InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemdef) +InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef): + m_name(name), + m_size(size), + m_width(0), + m_itemdef(itemdef) { - m_name = name; - m_size = size; - m_width = 0; - m_itemdef = itemdef; clearItems(); - //m_dirty = false; } InventoryList::~InventoryList() @@ -712,14 +710,6 @@ ItemStack InventoryList::takeItem(u32 i, u32 takecount) return taken; } -ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const -{ - if(i >= m_items.size()) - return ItemStack(); - - return m_items[i].peekItem(peekcount); -} - void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count) { // Take item from source list diff --git a/src/inventory.h b/src/inventory.h index fe1639728..a9fef3b05 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -173,7 +173,7 @@ struct ItemStack class InventoryList { public: - InventoryList(std::string name, u32 size, IItemDefManager *itemdef); + InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef); ~InventoryList(); void clearItems(); void setSize(u32 newsize); @@ -239,9 +239,6 @@ public: // Returns empty item if couldn't take any. ItemStack takeItem(u32 i, u32 takecount); - // Similar to takeItem, but keeps the slot intact. - ItemStack peekItem(u32 i, u32 peekcount) const; - // Move an item to a different list (or a different stack in the same list) // count is the maximum number of items to move (0 for everything) // returns number of moved items @@ -254,8 +251,8 @@ public: private: std::vector m_items; - u32 m_size, m_width; std::string m_name; + u32 m_size, m_width; IItemDefManager *m_itemdef; }; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index bcae874b9..8dfb851e6 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -43,15 +43,12 @@ struct EnumString es_TileAnimationType[] = }; /******************************************************************************/ -ItemDefinition read_item_definition(lua_State* L,int index, - ItemDefinition default_def) +void read_item_definition(lua_State* L, int index, + const ItemDefinition &default_def, ItemDefinition &def) { - if(index < 0) + if (index < 0) index = lua_gettop(L) + 1 + index; - // Read the item definition - ItemDefinition def = default_def; - def.type = (ItemType)getenumfield(L, index, "type", es_ItemType, ITEM_NONE); getstringfield(L, index, "name", def.name); @@ -118,8 +115,6 @@ ItemDefinition read_item_definition(lua_State* L,int index, // "" = no prediction getstringfield(L, index, "node_placement_prediction", def.node_placement_prediction); - - return def; } /******************************************************************************/ @@ -873,7 +868,7 @@ void push_tool_capabilities(lua_State *L, lua_newtable(L); // For each groupcap for (ToolGCMap::const_iterator i = toolcap.groupcaps.begin(); - i != toolcap.groupcaps.end(); i++) { + i != toolcap.groupcaps.end(); ++i) { // Create groupcap table lua_newtable(L); const std::string &name = i->first; @@ -881,7 +876,7 @@ void push_tool_capabilities(lua_State *L, // Create subtable "times" lua_newtable(L); for (UNORDERED_MAP::const_iterator - i = groupcap.times.begin(); i != groupcap.times.end(); i++) { + i = groupcap.times.begin(); i != groupcap.times.end(); ++i) { lua_pushinteger(L, i->first); lua_pushnumber(L, i->second); lua_settable(L, -3); @@ -900,7 +895,7 @@ void push_tool_capabilities(lua_State *L, lua_newtable(L); // For each damage group for (DamageGroup::const_iterator i = toolcap.damageGroups.begin(); - i != toolcap.damageGroups.end(); i++) { + i != toolcap.damageGroups.end(); ++i) { // Create damage group table lua_pushinteger(L, i->second); lua_setfield(L, -2, i->first.c_str()); @@ -939,7 +934,7 @@ void read_inventory_list(lua_State *L, int tableindex, InventoryList *invlist = inv->addList(name, listsize); int index = 0; for(std::vector::const_iterator - i = items.begin(); i != items.end(); i++){ + i = items.begin(); i != items.end(); ++i){ if(forcesize != -1 && index == forcesize) break; invlist->changeItem(index, *i); diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 949b136eb..c701c0384 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -86,8 +86,8 @@ ToolCapabilities read_tool_capabilities (lua_State *L, int table); void push_tool_capabilities (lua_State *L, const ToolCapabilities &prop); -ItemDefinition read_item_definition (lua_State *L, int index, - ItemDefinition default_def); +void read_item_definition (lua_State *L, int index, const ItemDefinition &default_def, + ItemDefinition &def); void read_object_properties (lua_State *L, int index, ObjectProperties *prop, IItemDefManager *idef); diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 2d32093a7..0e4fc4ef0 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -526,7 +526,7 @@ int ModApiItemMod::l_register_item_raw(lua_State *L) def.node_placement_prediction = "__default"; // Read the item definition - def = read_item_definition(L, table, def); + read_item_definition(L, table, def, def); // Default to having client-side placement prediction for nodes // ("" in item definition sets it off) diff --git a/src/tool.h b/src/tool.h index ebba5b749..f33152355 100644 --- a/src/tool.h +++ b/src/tool.h @@ -63,8 +63,8 @@ struct ToolCapabilities ToolCapabilities( float full_punch_interval_=1.4, int max_drop_level_=1, - ToolGCMap groupcaps_=ToolGCMap(), - DamageGroup damageGroups_=DamageGroup() + const ToolGCMap &groupcaps_ = ToolGCMap(), + const DamageGroup &damageGroups_ = DamageGroup() ): full_punch_interval(full_punch_interval_), max_drop_level(max_drop_level_), @@ -85,8 +85,8 @@ struct DigParams u16 wear; std::string main_group; - DigParams(bool a_diggable=false, float a_time=0, u16 a_wear=0, - std::string a_main_group=""): + DigParams(bool a_diggable = false, float a_time = 0.0f, u16 a_wear = 0, + const std::string &a_main_group = ""): diggable(a_diggable), time(a_time), wear(a_wear), -- 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/environment.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