From d6b30dd3a5df4b84a287305f807442064f4cf68d Mon Sep 17 00:00:00 2001 From: Kahrl Date: Tue, 29 Nov 2011 17:15:18 +0200 Subject: CraftItem rework and Lua interface --- src/server.cpp | 2051 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 1016 insertions(+), 1035 deletions(-) (limited to 'src/server.cpp') diff --git a/src/server.cpp b/src/server.cpp index 63172e955..70638a0a6 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -45,6 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "tooldef.h" #include "craftdef.h" +#include "craftitemdef.h" #include "mapgen.h" #include "content_abm.h" @@ -1033,6 +1034,7 @@ Server::Server( m_toolmgr(createToolDefManager()), m_nodedef(createNodeDefManager()), m_craftdef(createCraftDefManager()), + m_craftitemdef(createCraftItemDefManager()), m_thread(this), m_emergethread(this), m_time_counter(0), @@ -1206,6 +1208,8 @@ Server::~Server() delete m_toolmgr; delete m_nodedef; + delete m_craftdef; + delete m_craftitemdef; // Deinitialize scripting infostream<<"Server: Deinitializing scripting"<getValue(); ServerRemotePlayer *player = - (ServerRemotePlayer*)m_env->getPlayer(client->peer_id); + static_cast + (m_env->getPlayer(client->peer_id)); if(player==NULL) continue; player->m_last_good_position_age += dtime; @@ -2240,6 +2245,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Send node definitions SendNodeDef(m_con, peer_id, m_nodedef); + // Send CraftItem definitions + SendCraftItemDef(m_con, peer_id, m_craftitemdef); + // Send textures SendTextures(peer_id); @@ -2420,1190 +2428,1130 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } else if(command == TOSERVER_CLICK_ACTIVEOBJECT) { - if(datasize < 7) - return; + infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<getActiveObject(id); - - if(obj == NULL) + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + u8 buf[6]; + // Read stuff + is.read((char*)buf, 6); + v3s16 p = readV3S16(buf); + is.read((char*)buf, 2); + u16 textlen = readU16(buf); + std::string text; + for(u16 i=0; im_removed) + NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); + if(!meta) return; - - //TODO: Check that object is reasonably close - - // Get ServerRemotePlayer - ServerRemotePlayer *srp = (ServerRemotePlayer*)player; - // Update wielded item - srp->wieldItem(item_i); + meta->setText(text); - // Left click, pick/punch - if(button == 0) + actionstream<getName()<<" writes \""<getMap().getBlockNoCreateNoEx(blockpos); + if(block) { - actionstream<getName()<<" punches object " - <getId()<punch(srp); - -#if 0 + block->raiseModified(MOD_STATE_WRITE_NEEDED, + "sign node text"); + } + + setBlockNotSent(blockpos); + } + else if(command == TOSERVER_INVENTORY_ACTION) + { + /*// Ignore inventory changes if in creative mode + if(g_settings->getBool("creative_mode") == true) + { + infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode" + <inventory.getList("main"); - if(ilist != NULL) + IMoveAction *ma = (IMoveAction*)a; + if(ma->to_inv == "current_player" && + ma->from_inv == "current_player") { - actionstream<getName()<<" picked up " - <getName()<getBool("creative_mode") == false) + InventoryList *rlist = player->inventory.getList("craftresult"); + assert(rlist); + InventoryList *clist = player->inventory.getList("craft"); + assert(clist); + InventoryList *mlist = player->inventory.getList("main"); + assert(mlist); + /* + Craftresult is no longer preview if something + is moved into it + */ + if(ma->to_list == "craftresult" + && ma->from_list != "craftresult") { - // Skip if inventory has no free space - if(ilist->roomForItem(item) == false) + // If it currently is a preview, remove + // its contents + if(player->craftresult_is_preview) { - infostream<<"Player inventory has no free space"<deleteItem(0); } - - // Add to inventory and send inventory - ilist->addItem(item); - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); + player->craftresult_is_preview = false; + } + /* + Crafting takes place if this condition is true. + */ + if(player->craftresult_is_preview && + ma->from_list == "craftresult") + { + player->craftresult_is_preview = false; + clist->decrementMaterials(1); + + /* Print out action */ + InventoryList *list = + player->inventory.getList("craftresult"); + assert(list); + InventoryItem *item = list->getItem(0); + std::string itemname = "NULL"; + if(item) + itemname = item->getName(); + actionstream<getName()<<" crafts " + <to_list == "craftresult" + && ma->from_list == "craftresult") + { + disable_action = true; + + InventoryItem *item1 = rlist->changeItem(0, NULL); + mlist->addItem(item1); } - - // Remove object from environment - obj->m_removed = true; } - } - else - { - /* - Item cannot be picked up. Punch it instead. - */ - - actionstream<getName()<<" punches object " - <getId()<inventory.getList("main"); - if(mlist != NULL) + // Disallow moving items if not allowed to build + else if((getPlayerPrivs(player) & PRIV_BUILD) == 0) { - InventoryItem *item = mlist->getItem(item_i); - if(item && (std::string)item->getName() == "ToolItem") + disable_action = true; + } + // if it's a locking chest, only allow the owner or server admins to move items + else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + { + Strfnd fn(ma->from_inv); + std::string id0 = fn.next(":"); + if(id0 == "nodemeta") + { + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); + if(meta->getOwner() != ""){ + if(meta->getOwner() != player->getName()) + disable_action = true; + } + } + } + else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + { + Strfnd fn(ma->to_inv); + std::string id0 = fn.next(":"); + if(id0 == "nodemeta") { - titem = (ToolItem*)item; - toolname = titem->getToolName(); + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); + if(meta->getOwner() != ""){ + if(meta->getOwner() != player->getName()) + disable_action = true; + } } } + } - v3f playerpos = player->getPosition(); - v3f objpos = obj->getBasePosition(); - v3f dir = (objpos - playerpos).normalize(); - - u16 wear = obj->punch(toolname, dir, player->getName()); - - if(titem) + if(a->getType() == IACTION_DROP) + { + IDropAction *da = (IDropAction*)a; + // Disallow dropping items if not allowed to build + if((getPlayerPrivs(player) & PRIV_BUILD) == 0) + { + disable_action = true; + } + // if it's a locking chest, only allow the owner or server admins to drop items + else if (da->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) { - bool weared_out = titem->addWear(wear); - if(weared_out) - mlist->deleteItem(item_i); - SendInventory(player->peer_id); + Strfnd fn(da->from_inv); + std::string id0 = fn.next(":"); + if(id0 == "nodemeta") + { + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); + if(meta->getOwner() != ""){ + if(meta->getOwner() != player->getName()) + disable_action = true; + } + } } } -#endif + + if(disable_action == false) + { + // Feed action to player inventory + a->apply(&c, this, m_env); + } + + // Eat the action + delete a; } - // Right click, do something with object - if(button == 1) + else { - actionstream<getName()<<" right clicks object " - <getId()<rightClick(srp); + infostream<<"TOSERVER_INVENTORY_ACTION: " + <<"InventoryAction::deSerialize() returned NULL" + <peer_id); - SendInventory(player->peer_id); } - else if(command == TOSERVER_GROUND_ACTION) + else if(command == TOSERVER_CHAT_MESSAGE) { - if(datasize < 17) - return; - /* - length: 17 - [0] u16 command - [2] u8 action - [3] v3s16 nodepos_undersurface - [9] v3s16 nodepos_abovesurface - [15] u16 item - actions: - 0: start digging - 1: place block - 2: stop digging (all parameters ignored) - 3: digging completed - */ - u8 action = readU8(&data[2]); - v3s16 p_under; - p_under.X = readS16(&data[3]); - p_under.Y = readS16(&data[5]); - p_under.Z = readS16(&data[7]); - v3s16 p_over; - p_over.X = readS16(&data[9]); - p_over.Y = readS16(&data[11]); - p_over.Z = readS16(&data[13]); - u16 item_i = readU16(&data[15]); - - ServerRemotePlayer *srp = (ServerRemotePlayer*)player; - /* - Check that target is reasonably close + u16 command + u16 length + wstring message */ - if(action != 2) // action 2 has always position (0,0,0) - { - v3f np_f = intToFloat(p_under, BS); - float max_d = BS * 10; // Just some large enough value - float d = srp->m_last_good_position.getDistanceFrom(np_f); - if(d > max_d){ - actionstream<<"Player "<getName() - <<" tried to access node from too far: " - <<"d="<SetBlockNotSent(blockpos); - // Do nothing else - return; - } + u8 buf[6]; + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + // Read stuff + is.read((char*)buf, 2); + u16 len = readU16(buf); + + std::wstring message; + for(u16 i=0; igetName()); + + // Run script hook + bool ate = scriptapi_on_chat_message(m_lua, player->getName(), + wide_to_narrow(message)); + // If script ate the message, don't proceed + if(ate) + return; + + // Line to send to players + std::wstring line; + // Whether to send to the player that sent the line + bool send_to_sender = false; + // Whether to send to other players + bool send_to_others = false; + + // Local player gets all privileges regardless of + // what's set on their account. + u64 privs = getPlayerPrivs(player); + + // Parse commands + if(message[0] == L'/') { - /* - NOTE: This can be used in the future to check if - somebody is cheating, by checking the timing. - */ - bool cannot_punch_node = false; + size_t strip_size = 1; + if (message[1] == L'#') // support old-style commans + ++strip_size; + message = message.substr(strip_size); - MapNode n(CONTENT_IGNORE); + WStrfnd f1(message); + f1.next(L" "); // Skip over /#whatever + std::wstring paramstring = f1.next(L""); - try + ServerCommandContext *ctx = new ServerCommandContext( + str_split(message, L' '), + paramstring, + this, + m_env, + player, + privs); + + std::wstring reply(processServerCommand(ctx)); + send_to_sender = ctx->flags & SEND_TO_SENDER; + send_to_others = ctx->flags & SEND_TO_OTHERS; + + if (ctx->flags & SEND_NO_PREFIX) + line += reply; + else + line += L"Server: " + reply; + + delete ctx; + + } + else + { + if(privs & PRIV_SHOUT) { - n = m_env->getMap().getNode(p_under); + line += L"<"; + line += name; + line += L"> "; + line += message; + send_to_others = true; } - catch(InvalidPositionException &e) + else { - infostream<<"Server: Not punching: Node not found." - <<" Adding block to emerge queue." - <::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + // Get client and check that it is valid + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; - } // action == 0 + // Filter recipient + bool sender_selected = (peer_id == client->peer_id); + if(sender_selected == true && send_to_sender == false) + continue; + if(sender_selected == false && send_to_others == false) + continue; + + SendChatMessage(client->peer_id, line); + } + } + } + else if(command == TOSERVER_DAMAGE) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + u8 damage = readU8(is); + if(g_settings->getBool("enable_damage")) + { + actionstream<getName()<<" damaged by " + <<(int)damage<<" hp at "<getPosition()/BS) + <m_dig_mutex); - client->m_dig_tool_item = -1; -#endif + char c = data[2+i]; + if(c == 0) + break; + oldpwd += c; + } + std::string newpwd; + for(u32 i=0; igetName(); + + if(m_authmanager.exists(playername) == false) + { + infostream<<"Server: playername not found in authmanager"<getName()<<" changes password"<wieldItem(item); + SendWieldedItem(player); + } + else if(command == TOSERVER_RESPAWN) + { + if(player->hp != 0) + return; + + RespawnPlayer(player); + + actionstream<getName()<<" respawns at " + <getPosition()/BS)<getMap().getNode(p_under); - // Get mineral - mineral = n.getMineral(m_nodedef); - // Get material at position - material = n.getContent(); - // If not yet cancelled - if(cannot_remove_node == false) - { - // If it's not diggable, do nothing - if(m_nodedef->get(material).diggable == false) - { - infostream<<"Server: Not finishing digging: " - <<"Node not diggable" - <getMap().getNodeMetadata(p_under); - if(meta && meta->nodeRemovalDisabled() == true) - { - infostream<<"Server: Not finishing digging: " - <<"Node metadata disables removal" - <getBasePosition(); } - // Make sure the player is allowed to do it - if((getPlayerPrivs(player) & PRIV_BUILD) == 0) - { - infostream<<"Player "<getName()<<" cannot remove node" - <<" because privileges are "< max_d){ + actionstream<<"Player "<getName() + <<" tried to access "<SetBlockNotSent(blockpos); - - return; - } - - actionstream<getName()<<" digs "< far_players; - sendRemoveNode(p_under, peer_id, &far_players, 30); - - /* - Update and send inventory - */ - - if(g_settings->getBool("creative_mode") == false) - { - /* - Wear out tool - */ - InventoryList *mlist = player->inventory.getList("main"); - if(mlist != NULL) - { - InventoryItem *item = mlist->getItem(item_i); - if(item && (std::string)item->getName() == "ToolItem") - { - ToolItem *titem = (ToolItem*)item; - std::string toolname = titem->getToolName(); - - // Get digging properties for material and tool - ToolDiggingProperties tp = - m_toolmgr->getDiggingProperties(toolname); - DiggingProperties prop = - getDiggingProperties(material, &tp, m_nodedef); - - if(prop.diggable == false) - { - infostream<<"Server: WARNING: Player digged" - <<" with impossible material + tool" - <<" combination"<addWear(prop.wear); - - if(weared_out) - { - mlist->deleteItem(item_i); - } - } - } - - /* - Add dug item to inventory - */ - - InventoryItem *item = NULL; - - if(mineral != MINERAL_NONE) - item = getDiggedMineralItem(mineral, this); - - // If not mineral - if(item == NULL) - { - const std::string &dug_s = m_nodedef->get(material).dug_item; - if(dug_s != "") - { - std::istringstream is(dug_s, std::ios::binary); - item = InventoryItem::deSerialize(is, this); - } - } - - if(item != NULL) - { - // Add a item to inventory - player->inventory.addItem("main", item); - - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); - } - - item = NULL; - - if(mineral != MINERAL_NONE) - item = getDiggedMineralItem(mineral, this); - - // If not mineral - if(item == NULL) - { - const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item; - s32 extra_rarity = m_nodedef->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, this); - } - } - - if(item != NULL) - { - // Add a item to inventory - player->inventory.addItem("main", item); - - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); - } - } - - /* - Remove the node - (this takes some time so it is done after the quick stuff) - */ - { - MapEditEventIgnorer ign(&m_ignore_map_edit_events); - - m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks); - } - /* - Set blocks not sent to far players - */ - for(core::list::Iterator - i = far_players.begin(); - i != far_players.end(); i++) - { - u16 peer_id = *i; - RemoteClient *client = getClient(peer_id); - if(client==NULL) - continue; - client->SetBlocksNotSent(modified_blocks); - } - - /* - Run script hook - */ - scriptapi_environment_on_dignode(m_lua, p_under, n, srp); - } - - /* - 1: place block - */ - else if(action == 1) - { - - InventoryList *ilist = player->inventory.getList("main"); - if(ilist == NULL) - return; - - // Get item - InventoryItem *item = ilist->getItem(item_i); - - // If there is no item, it is not possible to add it anywhere - if(item == NULL) + // Do nothing else return; - - /* - Handle material items - */ - if(std::string("MaterialItem") == item->getName()) - { - try{ - // Don't add a node if this is not a free space - MapNode n2 = m_env->getMap().getNode(p_over); - bool no_enough_privs = - ((getPlayerPrivs(player) & PRIV_BUILD)==0); - if(no_enough_privs) - infostream<<"Player "<getName()<<" cannot add node" - <<" because privileges are "<get(n2).buildable_to == false - || no_enough_privs) - { - // Client probably has wrong data. - // Set block not sent, so that client will get - // a valid one. - infostream<<"Client "<SetBlockNotSent(blockpos); - return; - } - } - catch(InvalidPositionException &e) - { - infostream<<"Server: Ignoring ADDNODE: Node not found" - <<" Adding block to emerge queue." - <m_time_from_building = 0.0; - - // Create node data - MaterialItem *mitem = (MaterialItem*)item; - MapNode n; - n.setContent(mitem->getMaterial()); - - actionstream<getName()<<" places material " - <<(int)mitem->getMaterial() - <<" at "<get(n).wall_mounted) - n.param2 = packDir(p_under - p_over); - - // Calculate the direction for furnaces and chests and stuff - if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE) - { - v3f playerpos = player->getPosition(); - v3f blockpos = intToFloat(p_over, BS) - playerpos; - blockpos = blockpos.normalize(); - n.param1 = 0; - if (fabs(blockpos.X) > fabs(blockpos.Z)) { - if (blockpos.X < 0) - n.param1 = 3; - else - n.param1 = 1; - } else { - if (blockpos.Z < 0) - n.param1 = 2; - else - n.param1 = 0; - } - } - - /* - Send to all close-by players - */ - core::list far_players; - sendAddNode(p_over, n, 0, &far_players, 30); - - /* - Handle inventory - */ - InventoryList *ilist = player->inventory.getList("main"); - if(g_settings->getBool("creative_mode") == false && ilist) - { - // Remove from inventory and send inventory - if(mitem->getCount() == 1) - ilist->deleteItem(item_i); - else - mitem->remove(1); - // Send inventory - UpdateCrafting(peer_id); - SendInventory(peer_id); - } - - /* - Add node. - - This takes some time so it is done after the quick stuff - */ - core::map modified_blocks; - { - MapEditEventIgnorer ign(&m_ignore_map_edit_events); - - std::string p_name = std::string(player->getName()); - m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name); - } - /* - Set blocks not sent to far players - */ - for(core::list::Iterator - i = far_players.begin(); - i != far_players.end(); i++) - { - u16 peer_id = *i; - RemoteClient *client = getClient(peer_id); - if(client==NULL) - continue; - client->SetBlocksNotSent(modified_blocks); - } - - /* - Run script hook - */ - scriptapi_environment_on_placenode(m_lua, p_over, n, srp); - - /* - Calculate special events - */ - - /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE")) - { - u32 count = 0; - for(s16 z=-1; z<=1; z++) - for(s16 y=-1; y<=1; y++) - for(s16 x=-1; x<=1; x++) - { - - } - }*/ } - /* - Place other item (not a block) - */ - else + } + + /* + Make sure the player is allowed to do it + */ + bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0; + if(!build_priv) + { + infostream<<"Ignoring interaction from player "<getName() + <<" because privileges are "<getMap().getBlockNoCreateNoEx(blockpos); - if(block==NULL) + bool cannot_punch_node = !build_priv; + + MapNode n(CONTENT_IGNORE); + + try { - infostream<<"Error while placing object: " - "block not found"<getMap().getNode(p_under); } - - /* - If in creative mode, item dropping is disabled unless - player has build privileges - */ - if(g_settings->getBool("creative_mode") && - (getPlayerPrivs(player) & PRIV_BUILD) == 0) + catch(InvalidPositionException &e) { - infostream<<"Not allowing player to drop item: " - "creative mode and no build privs"<createSAO(m_env, pos); + scriptapi_environment_on_punchnode(m_lua, p_under, n, srp); + } + else if(pointed.type == POINTEDTHING_OBJECT) + { + if(!build_priv) + return; - if(obj == NULL) - { - infostream<<"WARNING: item resulted in NULL object, " - <<"not placing onto map" - <getName()<<" places "<getName() - <<" at "<addActiveObject(obj); - - infostream<<"Placed object"<m_removed) + return; - if(g_settings->getBool("creative_mode") == false) - { - // Delete the right amount of items from the slot - u16 dropcount = item->getDropCount(); - - // Delete item if all gone - if(item->getCount() <= dropcount) - { - if(item->getCount() < dropcount) - infostream<<"WARNING: Server: dropped more items" - <<" than the slot contains"<inventory.getList("main"); - if(ilist) - // Remove from inventory and send inventory - ilist->deleteItem(item_i); - } - // Else decrement it - else - item->remove(dropcount); - - // Send inventory - UpdateCrafting(peer_id); - SendInventory(peer_id); - } - } + actionstream<getName()<<" punches object " + <punch(srp); } - } // action == 1 + } // action == 0 /* - Catch invalid actions + 1: stop digging */ - else + else if(action == 1) { - infostream<<"WARNING: Server: Invalid action " - <getMap().getNodeMetadata(p); - if(!meta) - return; + // Mandatory parameter; actually used for nothing + core::map modified_blocks; - meta->setText(text); - - actionstream<getName()<<" writes \""<getMap().getBlockNoCreateNoEx(blockpos); - if(block) - { - block->raiseModified(MOD_STATE_WRITE_NEEDED, - "sign node text"); - } + content_t material = CONTENT_IGNORE; + u8 mineral = MINERAL_NONE; - setBlockNotSent(blockpos); - } - else if(command == TOSERVER_INVENTORY_ACTION) - { - /*// Ignore inventory changes if in creative mode - if(g_settings->getBool("creative_mode") == true) - { - infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode" - <getMap().getNodeMetadata(p_under); + if(meta && meta->nodeRemovalDisabled() == true) + { + infostream<<"Server: Not finishing digging: " + <<"Node metadata disables removal" + <getType() == IACTION_MOVE - && g_settings->getBool("creative_mode") == false) + if(cannot_remove_node) { - IMoveAction *ma = (IMoveAction*)a; - if(ma->to_inv == "current_player" && - ma->from_inv == "current_player") + infostream<<"Server: Not finishing digging."<SetBlockNotSent(blockpos); + + return; + } + + actionstream<getName()<<" digs "< far_players; + sendRemoveNode(p_under, peer_id, &far_players, 30); + + /* + Update and send inventory + */ + + if(g_settings->getBool("creative_mode") == false) + { + /* + Wear out tool + */ + InventoryList *mlist = player->inventory.getList("main"); + if(mlist != NULL) { - InventoryList *rlist = player->inventory.getList("craftresult"); - assert(rlist); - InventoryList *clist = player->inventory.getList("craft"); - assert(clist); - InventoryList *mlist = player->inventory.getList("main"); - assert(mlist); - /* - Craftresult is no longer preview if something - is moved into it - */ - if(ma->to_list == "craftresult" - && ma->from_list != "craftresult") + InventoryItem *item = mlist->getItem(item_i); + if(item && (std::string)item->getName() == "ToolItem") { - // If it currently is a preview, remove - // its contents - if(player->craftresult_is_preview) + ToolItem *titem = (ToolItem*)item; + std::string toolname = titem->getToolName(); + + // Get digging properties for material and tool + ToolDiggingProperties tp = + m_toolmgr->getDiggingProperties(toolname); + DiggingProperties prop = + getDiggingProperties(material, &tp, m_nodedef); + + if(prop.diggable == false) { - rlist->deleteItem(0); + infostream<<"Server: WARNING: Player digged" + <<" with impossible material + tool" + <<" combination"<craftresult_is_preview = false; - } - /* - Crafting takes place if this condition is true. - */ - if(player->craftresult_is_preview && - ma->from_list == "craftresult") - { - player->craftresult_is_preview = false; - clist->decrementMaterials(1); - - /* Print out action */ - InventoryList *list = - player->inventory.getList("craftresult"); - assert(list); - InventoryItem *item = list->getItem(0); - std::string itemname = "NULL"; - if(item) - itemname = item->getName(); - actionstream<getName()<<" crafts " - <to_list == "craftresult" - && ma->from_list == "craftresult") - { - disable_action = true; - InventoryItem *item1 = rlist->changeItem(0, NULL); - mlist->addItem(item1); - } - } - // Disallow moving items if not allowed to build - else if((getPlayerPrivs(player) & PRIV_BUILD) == 0) - { - disable_action = true; - } - // if it's a locking chest, only allow the owner or server admins to move items - else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) - { - Strfnd fn(ma->from_inv); - std::string id0 = fn.next(":"); - if(id0 == "nodemeta") - { - v3s16 p; - p.X = stoi(fn.next(",")); - p.Y = stoi(fn.next(",")); - p.Z = stoi(fn.next(",")); - NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); - if(meta->getOwner() != ""){ - if(meta->getOwner() != player->getName()) - disable_action = true; + bool weared_out = titem->addWear(prop.wear); + + if(weared_out) + { + mlist->deleteItem(item_i); } } } - else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + + /* + Add dug item to inventory + */ + + InventoryItem *item = NULL; + + if(mineral != MINERAL_NONE) + item = getDiggedMineralItem(mineral, this); + + // If not mineral + if(item == NULL) { - Strfnd fn(ma->to_inv); - std::string id0 = fn.next(":"); - if(id0 == "nodemeta") + const std::string &dug_s = m_nodedef->get(material).dug_item; + if(dug_s != "") { - v3s16 p; - p.X = stoi(fn.next(",")); - p.Y = stoi(fn.next(",")); - p.Z = stoi(fn.next(",")); - NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); - if(meta->getOwner() != ""){ - if(meta->getOwner() != player->getName()) - disable_action = true; - } + std::istringstream is(dug_s, std::ios::binary); + item = InventoryItem::deSerialize(is, this); } } - } - - if(a->getType() == IACTION_DROP) - { - IDropAction *da = (IDropAction*)a; - // Disallow dropping items if not allowed to build - if((getPlayerPrivs(player) & PRIV_BUILD) == 0) + + if(item != NULL) { - disable_action = true; + // Add a item to inventory + player->inventory.addItem("main", item); } - // if it's a locking chest, only allow the owner or server admins to drop items - else if (da->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + + item = NULL; + + if(mineral != MINERAL_NONE) + item = getDiggedMineralItem(mineral, this); + + // If not mineral + if(item == NULL) { - Strfnd fn(da->from_inv); - std::string id0 = fn.next(":"); - if(id0 == "nodemeta") + const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item; + s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity; + if(extra_dug_s != "" && extra_rarity != 0 + && myrand() % extra_rarity == 0) { - v3s16 p; - p.X = stoi(fn.next(",")); - p.Y = stoi(fn.next(",")); - p.Z = stoi(fn.next(",")); - NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); - if(meta->getOwner() != ""){ - if(meta->getOwner() != player->getName()) - disable_action = true; - } + std::istringstream is(extra_dug_s, std::ios::binary); + item = InventoryItem::deSerialize(is, this); } } - } - if(disable_action == false) + if(item != NULL) + { + // Add a item to inventory + player->inventory.addItem("main", item); + } + } + + /* + Remove the node + (this takes some time so it is done after the quick stuff) + */ { - // Feed action to player inventory - a->apply(&c, this, m_env); + MapEditEventIgnorer ign(&m_ignore_map_edit_events); + + m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks); } - else + /* + Set blocks not sent to far players + */ + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) { - // Send inventory - UpdateCrafting(player->peer_id); - SendInventory(player->peer_id); + u16 peer_id = *i; + RemoteClient *client = getClient(peer_id); + if(client==NULL) + continue; + client->SetBlocksNotSent(modified_blocks); } - // Eat the action - delete a; - } - else - { - infostream<<"TOSERVER_INVENTORY_ACTION: " - <<"InventoryAction::deSerialize() returned NULL" - <inventory.getList("main"); + if(ilist == NULL) + return; - // Get player name of this client - std::wstring name = narrow_to_wide(player->getName()); - - // Run script hook - bool ate = scriptapi_on_chat_message(m_lua, player->getName(), - wide_to_narrow(message)); - // If script ate the message, don't proceed - if(ate) - return; - - // Line to send to players - std::wstring line; - // Whether to send to the player that sent the line - bool send_to_sender = false; - // Whether to send to other players - bool send_to_others = false; - - // Local player gets all privileges regardless of - // what's set on their account. - u64 privs = getPlayerPrivs(player); + // Get item + InventoryItem *item = ilist->getItem(item_i); + + // If there is no item, it is not possible to add it anywhere + if(item == NULL) + return; + + /* + Handle material items + */ + if(std::string("MaterialItem") == item->getName()) + { + bool cannot_place_node = !build_priv; + + try{ + // Don't add a node if this is not a free space + MapNode n2 = m_env->getMap().getNode(p_above); + if(m_nodedef->get(n2).buildable_to == false) + { + infostream<<"Client "<SetBlockNotSent(blockpos); + return; + } + + // Reset build time counter + getClient(peer_id)->m_time_from_building = 0.0; + + // Create node data + MaterialItem *mitem = (MaterialItem*)item; + MapNode n; + n.setContent(mitem->getMaterial()); + + actionstream<getName()<<" places material " + <<(int)mitem->getMaterial() + <<" at "<get(n).wall_mounted) + n.param2 = packDir(p_under - p_above); + + // Calculate the direction for furnaces and chests and stuff + if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE) + { + v3f playerpos = player->getPosition(); + v3f blockpos = intToFloat(p_above, BS) - playerpos; + blockpos = blockpos.normalize(); + n.param1 = 0; + if (fabs(blockpos.X) > fabs(blockpos.Z)) { + if (blockpos.X < 0) + n.param1 = 3; + else + n.param1 = 1; + } else { + if (blockpos.Z < 0) + n.param1 = 2; + else + n.param1 = 0; + } + } + + /* + Send to all close-by players + */ + core::list far_players; + sendAddNode(p_above, n, 0, &far_players, 30); + + /* + Handle inventory + */ + InventoryList *ilist = player->inventory.getList("main"); + if(g_settings->getBool("creative_mode") == false && ilist) + { + // Remove from inventory and send inventory + if(mitem->getCount() == 1) + ilist->deleteItem(item_i); + else + mitem->remove(1); + } + + /* + Add node. - // Parse commands - if(message[0] == L'/') - { - size_t strip_size = 1; - if (message[1] == L'#') // support old-style commans - ++strip_size; - message = message.substr(strip_size); + This takes some time so it is done after the quick stuff + */ + core::map modified_blocks; + { + MapEditEventIgnorer ign(&m_ignore_map_edit_events); - WStrfnd f1(message); - f1.next(L" "); // Skip over /#whatever - std::wstring paramstring = f1.next(L""); + std::string p_name = std::string(player->getName()); + m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name); + } + /* + Set blocks not sent to far players + */ + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) + { + u16 peer_id = *i; + RemoteClient *client = getClient(peer_id); + if(client==NULL) + continue; + client->SetBlocksNotSent(modified_blocks); + } - ServerCommandContext *ctx = new ServerCommandContext( - str_split(message, L' '), - paramstring, - this, - m_env, - player, - privs); + /* + Run script hook + */ + scriptapi_environment_on_placenode(m_lua, p_above, n, srp); - std::wstring reply(processServerCommand(ctx)); - send_to_sender = ctx->flags & SEND_TO_SENDER; - send_to_others = ctx->flags & SEND_TO_OTHERS; + /* + Calculate special events + */ + + /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE")) + { + u32 count = 0; + for(s16 z=-1; z<=1; z++) + for(s16 y=-1; y<=1; y++) + for(s16 x=-1; x<=1; x++) + { + + } + }*/ + } + /* + Place other item (not a block) + */ + else + { + if(!build_priv) + { + infostream<<"Not allowing player to place item: " + "no build privileges"<flags & SEND_NO_PREFIX) - line += reply; - else - line += L"Server: " + reply; + // Calculate a position for it + v3f pos = player_pos; + if(pointed.type == POINTEDTHING_NOTHING) + { + infostream<<"Not allowing player to place item: " + "pointing to nothing"<getBasePosition(); - delete ctx; + // Randomize a bit + pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0; + pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0; + } - } - else - { - if(privs & PRIV_SHOUT) - { - line += L"<"; - line += name; - line += L"> "; - line += message; - send_to_others = true; + //pos.Y -= BS*0.45; + //pos.Y -= BS*0.25; // let it drop a bit + + /* + Check that the block is loaded so that the item + can properly be added to the static list too + */ + v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS)); + MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); + if(block==NULL) + { + infostream<<"Error while placing item: " + "block not found"<getName()<<" places "<getName() + <<" at "<dropOrPlace(m_env, srp, pos, true, -1); + if(remove && g_settings->getBool("creative_mode") == false) + { + InventoryList *ilist = player->inventory.getList("main"); + if(ilist) + // Remove from inventory and send inventory + ilist->deleteItem(item_i); + } + } } - else + else if(pointed.type == POINTEDTHING_OBJECT) { - line += L"Server: You are not allowed to shout"; - send_to_sender = true; - } - } - - if(line != L"") - { - if(send_to_others) - actionstream<<"CHAT: "<::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; + if(!build_priv) + return; - // Filter recipient - bool sender_selected = (peer_id == client->peer_id); - if(sender_selected == true && send_to_sender == false) - continue; - if(sender_selected == false && send_to_others == false) - continue; + // Skip if object has been removed + if(pointed_object->m_removed) + return; - SendChatMessage(client->peer_id, line); + actionstream<getName()<<" right-clicks object " + <rightClick(srp); } - } - } - else if(command == TOSERVER_DAMAGE) - { - std::string datastring((char*)&data[2], datasize-2); - std::istringstream is(datastring, std::ios_base::binary); - u8 damage = readU8(is); - if(g_settings->getBool("enable_damage")) - { - actionstream<getName()<<" damaged by " - <<(int)damage<<" hp at "<getPosition()/BS) - <inventory.getList("main"); + if(ilist == NULL) + return; - infostream<<"Server: Client requests a password change from " - <<"'"<getItem(item_i); + + // If there is no item, it is not possible to add it anywhere + if(item == NULL) + return; - std::string playername = player->getName(); + // Requires build privs + if(!build_priv) + { + infostream<<"Not allowing player to use item: " + "no build privileges"<getName()<<" uses "<getName() + <<", pointing at "<use(m_env, srp, pointed); + if(remove && g_settings->getBool("creative_mode") == false) + { + InventoryList *ilist = player->inventory.getList("main"); + if(ilist) + // Remove from inventory and send inventory + ilist->deleteItem(item_i); + } - if(oldpwd != checkpwd) + } // action == 4 + + /* + Catch invalid actions + */ + else { - infostream<<"Server: invalid old password"<getName()<<" changes password"<completeAddToInventoryLater(item_i); - u16 item = readU16(&data[2]); - player->wieldItem(item); - SendWieldedItem(player); - } - else if(command == TOSERVER_RESPAWN) - { - if(player->hp != 0) - return; - - RespawnPlayer(player); - - actionstream<getName()<<" respawns at " - <getPosition()/BS)<peer_id); + SendInventory(player->peer_id); } else { @@ -3865,6 +3813,31 @@ void Server::SendNodeDef(con::Connection &con, u16 peer_id, con.Send(peer_id, 0, data, true); } +void Server::SendCraftItemDef(con::Connection &con, u16 peer_id, + ICraftItemDefManager *craftitemdef) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + /* + u16 command + u32 length of the next item + serialized CraftItemDefManager + */ + writeU16(os, TOCLIENT_CRAFTITEMDEF); + std::ostringstream tmp_os(std::ios::binary); + craftitemdef->serialize(tmp_os); + os<