Update inventory texture too
[oweals/minetest.git] / src / server.cpp
index 330ce21c2c912cdeccd8e6e8c78d74f7df6c3538..44c66447c3eda6a35bde5f5e283759ca59bbe10d 100644 (file)
@@ -41,6 +41,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "script.h"
 #include "scriptapi.h"
+#include "nodedef.h"
+#include "tooldef.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -942,6 +944,35 @@ u32 PIChecksum(core::list<PlayerInfo> &l)
        return checksum;
 }
 
+struct ModSpec
+{
+       std::string name;
+       std::string path;
+
+       ModSpec(const std::string &name_="", const std::string path_=""):
+               name(name_),
+               path(path_)
+       {}
+};
+
+static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
+{
+       core::list<ModSpec> mods;
+       for(core::list<std::string>::Iterator i = modspaths.begin();
+                       i != modspaths.end(); i++){
+               std::string modspath = *i;
+               std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
+               for(u32 j=0; j<dirlist.size(); j++){
+                       if(!dirlist[j].dir)
+                               continue;
+                       std::string modname = dirlist[j].name;
+                       std::string modpath = modspath + DIR_DELIM + modname;
+                       mods.push_back(ModSpec(modname, modpath));
+               }
+       }
+       return mods;
+}
+
 /*
        Server
 */
@@ -955,7 +986,8 @@ Server::Server(
        m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
        m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
        m_lua(NULL),
-       //m_scriptapi(NULL),
+       m_toolmgr(createToolDefManager()),
+       m_nodemgr(createNodeDefManager(NULL)),
        m_thread(this),
        m_emergethread(this),
        m_time_counter(0),
@@ -980,7 +1012,15 @@ Server::Server(
 
        JMutexAutoLock envlock(m_env_mutex);
        JMutexAutoLock conlock(m_con_mutex);
+
+       infostream<<"m_nodemgr="<<m_nodemgr<<std::endl;
+       
+       // Initialize default node definitions
+       content_mapnode_init(NULL, m_nodemgr);
        
+       // Add default global mod path
+       m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
+
        // Initialize scripting
        
        infostream<<"Server: Initializing scripting"<<std::endl;
@@ -989,18 +1029,23 @@ Server::Server(
        // Export API
        scriptapi_export(m_lua, this);
        // Load and run scripts
-       std::string defaultscript = porting::path_data + DIR_DELIM
-                       + "scripts" + DIR_DELIM + "default.lua";
-       bool success = script_load(m_lua, defaultscript.c_str());
-       if(!success){
-               errorstream<<"Server: Failed to load and run "
-                               <<defaultscript<<std::endl;
-               assert(0);
+       core::list<ModSpec> mods = getMods(m_modspaths);
+       for(core::list<ModSpec>::Iterator i = mods.begin();
+                       i != mods.end(); i++){
+               ModSpec mod = *i;
+               infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
+               std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
+               bool success = script_load(m_lua, scriptpath.c_str());
+               if(!success){
+                       errorstream<<"Server: Failed to load and run "
+                                       <<scriptpath<<std::endl;
+                       assert(0);
+               }
        }
        
        // Initialize Environment
        
-       m_env = new ServerEnvironment(new ServerMap(mapsavedir), m_lua);
+       m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this);
 
        // Give environment reference to scripting api
        scriptapi_add_environment(m_lua, m_env);
@@ -1099,6 +1144,9 @@ Server::~Server()
 
        // Delete Environment
        delete m_env;
+
+       delete m_toolmgr;
+       delete m_nodemgr;
        
        // Deinitialize scripting
        infostream<<"Server: Deinitializing scripting"<<std::endl;
@@ -2090,6 +2138,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                /*
                        Send some initialization data
                */
+
+               // Send textures
+               SendTextures(peer_id);
+               
+               // Send tool definitions
+               SendToolDef(m_con, peer_id, m_toolmgr);
                
                // Send player info to all players
                SendPlayerInfos();
@@ -2299,10 +2353,23 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        return;
                
                //TODO: Check that object is reasonably close
+       
+               // Get ServerRemotePlayer
+               ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
+
+               // Update wielded item
+               srp->wieldItem(item_i);
                
-               // Left click, pick object up (usually)
+               // Left click, pick/punch
                if(button == 0)
                {
+                       actionstream<<player->getName()<<" punches object "
+                                       <<obj->getId()<<std::endl;
+                       
+                       // Do stuff
+                       obj->punch(srp);
+                       
+#if 0
                        /*
                                Try creating inventory item
                        */
@@ -2371,6 +2438,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        SendInventory(player->peer_id);
                                }
                        }
+#endif
                }
                // Right click, do something with object
                if(button == 1)
@@ -2378,18 +2446,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        actionstream<<player->getName()<<" right clicks object "
                                        <<obj->getId()<<std::endl;
 
-                       // Track hp changes super-crappily
-                       u16 oldhp = player->hp;
-                       
                        // Do stuff
-                       obj->rightClick(player);
-                       
-                       // Send back stuff
-                       if(player->hp != oldhp)
-                       {
-                               SendPlayerHP(player);
-                       }
+                       obj->rightClick(srp);
                }
+
+               /*
+                       Update player state to client
+               */
+               SendPlayerHP(player);
+               UpdateCrafting(player->peer_id);
+               SendInventory(player->peer_id);
        }
        else if(command == TOSERVER_GROUND_ACTION)
        {
@@ -2461,14 +2527,14 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        {
                                MapNode n = m_env->getMap().getNode(p_under);
                                // Get mineral
-                               mineral = n.getMineral();
+                               mineral = n.getMineral(m_nodemgr);
                                // Get material at position
                                material = n.getContent();
                                // If not yet cancelled
                                if(cannot_remove_node == false)
                                {
                                        // If it's not diggable, do nothing
-                                       if(content_diggable(material) == false)
+                                       if(m_nodemgr->get(material).diggable == false)
                                        {
                                                infostream<<"Server: Not finishing digging: "
                                                                <<"Node not diggable"
@@ -2561,8 +2627,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                std::string toolname = titem->getToolName();
 
                                                // Get digging properties for material and tool
+                                               ToolDiggingProperties tp =
+                                                               m_toolmgr->getDiggingProperties(toolname);
                                                DiggingProperties prop =
-                                                               getDiggingProperties(material, toolname);
+                                                               getDiggingProperties(material, &tp, m_nodemgr);
 
                                                if(prop.diggable == false)
                                                {
@@ -2587,16 +2655,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                InventoryItem *item = NULL;
 
                                if(mineral != MINERAL_NONE)
-                                       item = getDiggedMineralItem(mineral);
+                                       item = getDiggedMineralItem(mineral, this);
                                
                                // If not mineral
                                if(item == NULL)
                                {
-                                       std::string &dug_s = content_features(material).dug_item;
+                                       const std::string &dug_s = m_nodemgr->get(material).dug_item;
                                        if(dug_s != "")
                                        {
                                                std::istringstream is(dug_s, std::ios::binary);
-                                               item = InventoryItem::deSerialize(is);
+                                               item = InventoryItem::deSerialize(is, this);
                                        }
                                }
                                
@@ -2613,25 +2681,25 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                item = NULL;
 
                                if(mineral != MINERAL_NONE)
-                                 item = getDiggedMineralItem(mineral);
+                                 item = getDiggedMineralItem(mineral, this);
                        
                                // If not mineral
                                if(item == NULL)
                                {
-                                       std::string &extra_dug_s = content_features(material).extra_dug_item;
-                                       s32 extra_rarity = content_features(material).extra_dug_item_rarity;
+                                       const std::string &extra_dug_s = m_nodemgr->get(material).extra_dug_item;
+                                       s32 extra_rarity = m_nodemgr->get(material).extra_dug_item_rarity;
                                        if(extra_dug_s != "" && extra_rarity != 0
                                           && myrand() % extra_rarity == 0)
                                        {
-                                               std::istringstream is(extra_dug_s, std::ios::binary);
-                                               item = InventoryItem::deSerialize(is);
+                                               std::istringstream is(extra_dug_s, std::ios::binary);
+                                               item = InventoryItem::deSerialize(is, this);
                                        }
                                }
                        
                                if(item != NULL)
                                {
-                                       // Add a item to inventory
-                                       player->inventory.addItem("main", item);
+                                       // Add a item to inventory
+                                       player->inventory.addItem("main", item);
 
                                        // Send inventory
                                        UpdateCrafting(player->peer_id);
@@ -2695,7 +2763,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                        <<" because privileges are "<<getPlayerPrivs(player)
                                                        <<std::endl;
 
-                                       if(content_features(n2).buildable_to == false
+                                       if(m_nodemgr->get(n2).buildable_to == false
                                                || no_enough_privs)
                                        {
                                                // Client probably has wrong data.
@@ -2733,11 +2801,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                <<" at "<<PP(p_under)<<std::endl;
                        
                                // Calculate direction for wall mounted stuff
-                               if(content_features(n).wall_mounted)
+                               if(m_nodemgr->get(n).wall_mounted)
                                        n.param2 = packDir(p_under - p_over);
 
                                // Calculate the direction for furnaces and chests and stuff
-                               if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
+                               if(m_nodemgr->get(n).param_type == CPT_FACEDIR_SIMPLE)
                                {
                                        v3f playerpos = player->getPosition();
                                        v3f blockpos = intToFloat(p_over, BS) - playerpos;
@@ -3565,6 +3633,29 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
        con.Send(peer_id, 0, data, true);
 }
 
+void Server::SendToolDef(con::Connection &con, u16 peer_id,
+               IToolDefManager *tooldef)
+{
+       DSTACK(__FUNCTION_NAME);
+       std::ostringstream os(std::ios_base::binary);
+
+       /*
+               u16 command
+               u32 length of the next item
+               serialized ToolDefManager
+       */
+       writeU16(os, TOCLIENT_TOOLDEF);
+       std::ostringstream tmp_os(std::ios::binary);
+       tooldef->serialize(tmp_os);
+       os<<serializeLongString(tmp_os.str());
+
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       con.Send(peer_id, 0, data, true);
+}
+
 /*
        Non-static send methods
 */
@@ -4015,6 +4106,105 @@ void Server::SendBlocks(float dtime)
        }
 }
 
+struct SendableTexture
+{
+       std::string name;
+       std::string path;
+       std::string data;
+
+       SendableTexture(const std::string &name_="", const std::string path_="",
+                       const std::string &data_=""):
+               name(name_),
+               path(path_),
+               data(data_)
+       {}
+};
+
+void Server::SendTextures(u16 peer_id)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
+       
+       /* Read textures */
+       
+       core::list<SendableTexture> textures;
+       core::list<ModSpec> mods = getMods(m_modspaths);
+       for(core::list<ModSpec>::Iterator i = mods.begin();
+                       i != mods.end(); i++){
+               ModSpec mod = *i;
+               std::string texturepath = mod.path + DIR_DELIM + "textures";
+               std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
+               for(u32 j=0; j<dirlist.size(); j++){
+                       if(dirlist[j].dir) // Ignode dirs
+                               continue;
+                       std::string tname = dirlist[j].name;
+                       std::string tpath = texturepath + DIR_DELIM + tname;
+                       // Read data
+                       std::ifstream fis(tpath.c_str(), std::ios_base::binary);
+                       if(fis.good() == false){
+                               errorstream<<"Server::SendTextures(): Could not open \""
+                                               <<tname<<"\" for reading"<<std::endl;
+                               continue;
+                       }
+                       std::ostringstream tmp_os(std::ios_base::binary);
+                       bool bad = false;
+                       for(;;){
+                               char buf[1024];
+                               fis.read(buf, 1024);
+                               std::streamsize len = fis.gcount();
+                               tmp_os.write(buf, len);
+                               if(fis.eof())
+                                       break;
+                               if(!fis.good()){
+                                       bad = true;
+                                       break;
+                               }
+                       }
+                       if(bad){
+                               errorstream<<"Server::SendTextures(): Failed to read \""
+                                               <<tname<<"\""<<std::endl;
+                               continue;
+                       }
+                       errorstream<<"Server::SendTextures(): Loaded \""
+                                       <<tname<<"\""<<std::endl;
+                       // Put in list
+                       textures.push_back(SendableTexture(tname, tpath, tmp_os.str()));
+               }
+       }
+
+       /* Create and send packet */
+
+       /*
+               u16 command
+               u32 number of textures
+               for each texture {
+                       u16 length of name
+                       string name
+                       u32 length of data
+                       data
+               }
+       */
+       std::ostringstream os(std::ios_base::binary);
+
+       writeU16(os, TOCLIENT_TEXTURES);
+       writeU32(os, textures.size());
+       
+       for(core::list<SendableTexture>::Iterator i = textures.begin();
+                       i != textures.end(); i++){
+               os<<serializeString(i->name);
+               os<<serializeLongString(i->data);
+       }
+       
+       // Make data buffer
+       std::string s = os.str();
+       infostream<<"Server::SendTextures(): number of textures: "
+                       <<textures.size()<<", data size: "<<s.size()<<std::endl;
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       m_con.Send(peer_id, 0, data, true);
+}
+
 /*
        Something random
 */
@@ -4095,7 +4285,7 @@ void Server::UpdateCrafting(u16 peer_id)
                        }
                        
                        // Get result of crafting grid
-                       InventoryItem *result = craft_get_result(items);
+                       InventoryItem *result = craft_get_result(items, this);
                        if(result)
                                rlist->addItem(result);
                }
@@ -4170,6 +4360,30 @@ void Server::notifyPlayers(const std::wstring msg)
        BroadcastChatMessage(msg);
 }
 
+// IGameDef interface
+// Under envlock
+IToolDefManager* Server::getToolDefManager()
+{
+       return m_toolmgr;
+}
+INodeDefManager* Server::getNodeDefManager()
+{
+       return m_nodemgr;
+}
+ITextureSource* Server::getTextureSource()
+{
+       return NULL;
+}
+
+IWritableToolDefManager* Server::getWritableToolDefManager()
+{
+       return m_toolmgr;
+}
+IWritableNodeDefManager* Server::getWritableNodeDefManager()
+{
+       return m_nodemgr;
+}
+
 v3f findSpawnPos(ServerMap &map)
 {
        //return v3f(50,50,50)*BS;
@@ -4259,7 +4473,7 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                        player->inventory_backup = new Inventory();
                        *(player->inventory_backup) = player->inventory;
                        // Set creative inventory
-                       craft_set_creative_inventory(player);
+                       craft_set_creative_inventory(player, this);
                }
 
                return player;
@@ -4313,11 +4527,11 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                        player->inventory_backup = new Inventory();
                        *(player->inventory_backup) = player->inventory;
                        // Set creative inventory
-                       craft_set_creative_inventory(player);
+                       craft_set_creative_inventory(player, this);
                }
                else if(g_settings->getBool("give_initial_stuff"))
                {
-                       craft_give_initial_stuff(player);
+                       craft_give_initial_stuff(player, this);
                }
 
                return player;