aboutsummaryrefslogtreecommitdiff
path: root/src/content_sao.cpp
Commit message (Collapse)AuthorAge
* Add offset to automatic_face_movement_dirPilzAdam2013-09-10
|
* Send player damage to all clients and apply [brightenPilzAdam2013-08-17
|
* Omnicleanup: header cleanup, add ModApiUtil shared between game and mainmenuKahrl2013-08-14
|
* Add support for entities to automatic face movement directionsapier2013-08-13
|
* Fix various memory access problems detected by valgrindKahrl2013-08-07
|
* Allow mods to listen to cheat detections using minetest.register_on_cheat()Perttu Ahola2013-08-04
|
* Fix anticheatPerttu Ahola2013-08-03
|
* Add support for setting stepheight for entitiessapier2013-07-30
|
* Add an option to disable object <-> object collision for Lua entitiesPilzAdam2013-07-20
|
* Add set_breath and get_breath to lua API.RealBadAngel2013-07-20
|
* Move scriptapi to separate folder (by sapier)sapier2013-05-25
| | | | | | | | | | | On the lua side, notably minetest.env:<function>(<args>) should now be replaced by minetest.<function>(<args>). The old way is and will stay supported for a long time. Also: Update and clean up lua_api.txt (by celeron55) Move EnvRef to lua and remove add_rat and add_firefly (by kahrl) Add separate src/util/CMakeLists.txt, other minor fixes (by kahrl)
* Allow nil as puncher e.g. to do damage by tntsapier2013-05-20
|
* Move player collisionbox to player classsapier2013-04-25
|
* fix objects colliding with its own collision boxessapier2013-04-09
|
* Allow modifying movement speed, jump height and gravity per-player via the ↵MirceaKitsune2013-04-05
| | | | Lua API.
* New damage system, add damageGroups to ToolCapabilities, bump protocol versionPilzAdam2013-03-29
|
* Closed add object <-> object collision handlingsapier2013-03-28
|
* lua methods set_look_pitch and set_look_yawRealBadAngel2013-03-17
|
* Migrate to STL containers/algorithms.Ilya Zhuravlev2013-03-11
|
* Update Copyright YearsSfan52013-02-24
|
* Change Minetest-c55 to MinetestPilzAdam2013-02-24
|
* Initialize all member variables of LuaEntitySAOPerttu Ahola2012-12-23
|
* Full protocol 13 compatibility on server sidePerttu Ahola2012-11-29
|
* Fix LuaEntitySAO::getClientInitializationData() and bump the related versionPerttu Ahola2012-11-26
|
* A bunch of fixesMirceaKitsune2012-11-25
| | | | | | | | | | | | No longer hide players who are dead. With models, a death animation should be used instead Some changes requested by celeron55 Rename a lot of things in the code, and use better lua api function names Minor code corrections Bump protocol version up, since the models / animations / attachments code creates new client<->server messages
* Allow Vanessa's texture filtering / mipmap patch to work on models, as well ↵MirceaKitsune2012-11-25
| | | | | | | | | | | | as all other node types Initialize m_bone_posrot too Update pos_translator for attached players, which should fix a bug noticed with carts where a detached player would travel from the detachment spot to where it was detached Don't make attachments possible to right-click either Properly apply object and text visibility for local attachments, and fix them showing when they shouldn't
* Update attachments at the ending of the addToScene function for parents. And ↵MirceaKitsune2012-11-25
| | | | | | | | | | | | | | | | | | | | with this... *drum roll* Client-side attachments are at last functional and stick visibly. Fix the last segmentation fault (apparently). So far attachments seem to be fully functional, although removing the parent causes children to go to origin 0,0,0 and possibly still cause such a fault (though this should already be addressed) Fix a bug in falling code where entities get stuck Also check if the parent has been removed server-side, and detach the child if so. Fixes children going to origin 0,0,0 when their parent is removed. Unset all attachment properties when permanently detaching (on both the client and server). Also store less data we don't need Create a separate function for detaching, and also update lua api documentation When a child is detached, update its position from the server to clients. This WILL cause it to get positioned slightly differently client side, as the server attachment system only copies parent origin and knows not about mesh / bone transformation. This prevents different clients seeing the object detached in different spots which is most correct Update the position of attached players to clients. An attached player will see himself move, but this is currently VERY ugly and laggy as it is done by the server (it probably must stay this way too) Use a different approach for locally attached players. This allows for smooth positio transitions to work, as well at the player turning around freely. Still buggy however
* Send animations, bone overrides and attachments in entity initialization. ↵MirceaKitsune2012-11-25
| | | | | | | | | | | | | | | | | | | | Clients no longer have to be near an object when an animation or attachment is set to see the changes, and newly connected clients (or a client that simply renders the object for the first time) will get all of those settings. Therefore, the lua script no longer needs to run every X seconds either, just once per entity. Finish fixing the material color code. But it won't work until MineTest has dynamic lighting... another day another feature. Extra checks for the bone positioning / rotation code Many checks and consistency improvements to the client attachment code Make a separate function for checking if a client object is attached. A more in-depth change will be needed here to fix reading of invalid pointers Use a different method of fetching the parent. Fixes the mass segmentation faults when rendering an attachment (some still happen though) Major change to how attachments are handled. Fix the last segmentaton fault, which was due to the parent becoming invalid while being refreshed / removed which would bause the child to remain attached to nothing. Parents remove their children when being deleted themselves and add them back when re-added. Attachments are stored inside a 2D a vector which easily allows both a child to find their parent and a parent to find its children. Remove attachment list entry when an object is being permanently removed. Also avoid duplicate entries in this list when re-attaching the same object The "big code comments" can now go away. Client attachments almost work properly, and I know what else needs to be done
* Enable client-side attachments, add detachment codeMirceaKitsune2012-11-25
|
* Fix some remaining issues with attachments, now they work. Tested with ↵MirceaKitsune2012-11-25
| | | | object->player and player->player attachments
* Complete the attachment framework.MirceaKitsune2012-11-25
| | | | | | | | | | | | The child ID can now be checked against the parent ID in content_cao.cpp so the parent can be detected. Actual attachment code to come Divide attachment system between server attachments and client attachments, neither coded right now. As explained in the code comment: // Attachments need to be handled on both the server and client. // If we attach only on the server, models (which are client-side) // can't be read so we don't know the origin and orientation of bones. // If we attach only on the client, the real position of attachments is // not updated and you can't click them for example.
* Framework for the attachment system, new object property which allows ↵MirceaKitsune2012-11-25
| | | | | | | | | | | | | | changing the color and alpha of mesh materials New object property which allows changing the color and alpha of mesh materials. Due to the current lighting systems it doesn't work yet, but the full implementation is there Framework for the attachment system, with no actual functionality yet Send bone and player object to the setAttachment function in content_sao.cpp, but we need a way to translate it there and send it to the client I will also want position and rotation offsets to be possible to apply to attachments Network object ID from server to client. This will be used to identify the parent client-side and know what to attach to
* Get the new animation framework properly workingMirceaKitsune2012-11-25
| | | | | | Store start and end frames as v2f Also move bone animations to their own function instead of object properties
* Joint positioning and rotation code, and fix a problem related to their lua APIMirceaKitsune2012-11-25
| | | | Attempt to move the animation system to a more correct implementation, rather than using object properties. Incomplete and breaks functionality
* Properly read the mesh from LUA.MirceaKitsune2012-11-25
| | | | | | | | | | | | | | | | | Players can now be set to meshes using the following test script: function switch_player_visual() prop = { mesh="player.obj", texture="player.png", visual="mesh", } for _, obj in pairs(minetest.get_connected_players()) do obj:set_properties(prop) end minetest.after(1.0, switch_player_visual) end minetest.after(1.0, switch_player_visual)
* 3D model support for players using Irrlicht. Also ready the basis for mesh ↵MirceaKitsune2012-11-25
| | | | support on nodes / items via LUA (to be done). Supports any mesh format compatible with Irrlicht, but animations are not set up yet.
* Add dtime_s to entity activationPerttu Ahola2012-09-09
|
* Remove special handling of creative modePerttu Ahola2012-07-25
|
* Server-side checking of digging; disable_anticheat settingPerttu Ahola2012-07-21
|
* Custom boxy nodes (stairs, slabs) and collision changesKahrl2012-06-17
|
* Properly and efficiently use split utility headersPerttu Ahola2012-06-17
|
* Add comment about ItemSAO being deprecatedPerttu Ahola2012-06-11
|
* Switch the license to be LGPLv2/later, with small parts still remaining as ↵Perttu Ahola2012-06-05
| | | | GPLv2/later, by agreement of major contributors
* Fix crash after 'LuaEntity name "particles:smoke" not defined' and similar ↵Perttu Ahola2012-04-08
| | | | errors
* Fix disabling of player movement cheat detection in singleplayerPerttu Ahola2012-04-01
|
* Don't apply player movement cheat detection in singleplayerPerttu Ahola2012-04-01
|
* Add server-side enforcement of the 'fast' privilege; also fix client ↵Perttu Ahola2012-03-31
| | | | checking 'fly' instead of 'fast'
* Fix the position of LuaEntityCAOs not getting updated at allPerttu Ahola2012-03-30
|
* ObjectRef:set_armor_groups() and ObjectRef:set_properties() - works on ↵Perttu Ahola2012-03-30
| | | | players too!
* ObjectPropertiesPerttu Ahola2012-03-30
|
void InventoryLocation::serialize(std::ostream &os) const { switch (type) { case InventoryLocation::UNDEFINED: os<<"undefined"; break; case InventoryLocation::CURRENT_PLAYER: os<<"current_player"; break; case InventoryLocation::PLAYER: os<<"player:"<<name; break; case InventoryLocation::NODEMETA: os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z; break; case InventoryLocation::DETACHED: os<<"detached:"<<name; break; default: FATAL_ERROR("Unhandled inventory location type"); } } void InventoryLocation::deSerialize(std::istream &is) { std::string tname; std::getline(is, tname, ':'); if (tname == "undefined") { type = InventoryLocation::UNDEFINED; } else if (tname == "current_player") { type = InventoryLocation::CURRENT_PLAYER; } else if (tname == "player") { type = InventoryLocation::PLAYER; std::getline(is, name, '\n'); } else if (tname == "nodemeta") { type = InventoryLocation::NODEMETA; std::string pos; std::getline(is, pos, '\n'); Strfnd fn(pos); p.X = stoi(fn.next(",")); p.Y = stoi(fn.next(",")); p.Z = stoi(fn.next(",")); } else if (tname == "detached") { type = InventoryLocation::DETACHED; std::getline(is, name, '\n'); } else { infostream<<"Unknown InventoryLocation type=\""<<tname<<"\""<<std::endl; throw SerializationError("Unknown InventoryLocation type"); } } void InventoryLocation::deSerialize(const std::string &s) { std::istringstream is(s, std::ios::binary); deSerialize(is); } /* InventoryAction */ InventoryAction *InventoryAction::deSerialize(std::istream &is) { std::string type; std::getline(is, type, ' '); InventoryAction *a = nullptr; if (type == "Move") { a = new IMoveAction(is, false); } else if (type == "MoveSomewhere") { a = new IMoveAction(is, true); } else if (type == "Drop") { a = new IDropAction(is); } else if (type == "Craft") { a = new ICraftAction(is); } return a; } /* IMoveAction */ IMoveAction::IMoveAction(std::istream &is, bool somewhere) : move_somewhere(somewhere) { std::string ts; std::getline(is, ts, ' '); count = stoi(ts); std::getline(is, ts, ' '); from_inv.deSerialize(ts); std::getline(is, from_list, ' '); std::getline(is, ts, ' '); from_i = stoi(ts); std::getline(is, ts, ' '); to_inv.deSerialize(ts); std::getline(is, to_list, ' '); if (!somewhere) { std::getline(is, ts, ' '); to_i = stoi(ts); } } void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) { Inventory *inv_from = mgr->getInventory(from_inv); Inventory *inv_to = mgr->getInventory(to_inv); if (!inv_from) { infostream << "IMoveAction::apply(): FAIL: source inventory not found: " << "from_inv=\""<<from_inv.dump() << "\"" << ", to_inv=\"" << to_inv.dump() << "\"" << std::endl; return; } if (!inv_to) { infostream << "IMoveAction::apply(): FAIL: destination inventory not found: " << "from_inv=\"" << from_inv.dump() << "\"" << ", to_inv=\"" << to_inv.dump() << "\"" << std::endl; return; } InventoryList *list_from = inv_from->getList(from_list); InventoryList *list_to = inv_to->getList(to_list); /* If a list doesn't exist or the source item doesn't exist */ if (!list_from) { infostream << "IMoveAction::apply(): FAIL: source list not found: " << "from_inv=\"" << from_inv.dump() << "\"" << ", from_list=\"" << from_list << "\"" << std::endl; return; } if (!list_to) { infostream << "IMoveAction::apply(): FAIL: destination list not found: " << "to_inv=\""<<to_inv.dump() << "\"" << ", to_list=\"" << to_list << "\"" << std::endl; return; } if (move_somewhere) { s16 old_to_i = to_i; u16 old_count = count; caused_by_move_somewhere = true; move_somewhere = false; infostream << "IMoveAction::apply(): moving item somewhere" << " msom=" << move_somewhere << " count=" << count << " from inv=\"" << from_inv.dump() << "\"" << " list=\"" << from_list << "\"" << " i=" << from_i << " to inv=\"" << to_inv.dump() << "\"" << " list=\"" << to_list << "\"" << std::endl; // Try to add the item to destination list s16 dest_size = list_to->getSize(); // First try all the non-empty slots for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) { if (!list_to->getItem(dest_i).empty()) { to_i = dest_i; apply(mgr, player, gamedef); count -= move_count; } } // Then try all the empty ones for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) { if (list_to->getItem(dest_i).empty()) { to_i = dest_i; apply(mgr, player, gamedef); count -= move_count; } } to_i = old_to_i; count = old_count; caused_by_move_somewhere = false; move_somewhere = true; return; } if ((u16)to_i > list_to->getSize()) { infostream << "IMoveAction::apply(): FAIL: destination index out of bounds: " << "to_i=" << to_i << ", size=" << list_to->getSize() << std::endl; return; } /* Do not handle rollback if both inventories are that of the same player */ bool ignore_rollback = ( from_inv.type == InventoryLocation::PLAYER && from_inv == to_inv); /* Collect information of endpoints */ int try_take_count = count; if (try_take_count == 0) try_take_count = list_from->getItem(from_i).count; int src_can_take_count = 0xffff; int dst_can_put_count = 0xffff; /* Query detached inventories */ // Move occurs in the same detached inventory if (from_inv.type == InventoryLocation::DETACHED && from_inv == to_inv) { src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowMove( *this, try_take_count, player); dst_can_put_count = src_can_take_count; } else { // Destination is detached if (to_inv.type == InventoryLocation::DETACHED) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; dst_can_put_count = PLAYER_TO_SA(player)->detached_inventory_AllowPut( *this, src_item, player); } // Source is detached if (from_inv.type == InventoryLocation::DETACHED) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake( *this, src_item, player); } } /* Query node metadata inventories */ // Both endpoints are nodemeta // Move occurs in the same nodemeta inventory if (from_inv.type == InventoryLocation::NODEMETA && from_inv == to_inv) { src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowMove( *this, try_take_count, player); dst_can_put_count = src_can_take_count; } else { // Destination is nodemeta if (to_inv.type == InventoryLocation::NODEMETA) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; dst_can_put_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowPut( *this, src_item, player); } // Source is nodemeta if (from_inv.type == InventoryLocation::NODEMETA) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake( *this, src_item, player); } } // Query player inventories // Move occurs in the same player inventory if (from_inv.type == InventoryLocation::PLAYER && from_inv == to_inv) { src_can_take_count = PLAYER_TO_SA(player)->player_inventory_AllowMove( *this, try_take_count, player); dst_can_put_count = src_can_take_count; } else { // Destination is a player if (to_inv.type == InventoryLocation::PLAYER) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; dst_can_put_count = PLAYER_TO_SA(player)->player_inventory_AllowPut( *this, src_item, player); } // Source is a player if (from_inv.type == InventoryLocation::PLAYER) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; src_can_take_count = PLAYER_TO_SA(player)->player_inventory_AllowTake( *this, src_item, player); } } int old_count = count; /* Modify count according to collected data */ count = try_take_count; if (src_can_take_count != -1 && count > src_can_take_count) count = src_can_take_count; if (dst_can_put_count != -1 && count > dst_can_put_count) count = dst_can_put_count; /* Limit according to source item count */ if (count > list_from->getItem(from_i).count) count = list_from->getItem(from_i).count; /* If no items will be moved, don't go further */ if (count == 0) { // Undo client prediction. See 'clientApply' if (from_inv.type == InventoryLocation::PLAYER) list_from->setModified(); if (to_inv.type == InventoryLocation::PLAYER) list_to->setModified(); infostream<<"IMoveAction::apply(): move was completely disallowed:" <<" count="<<old_count <<" from inv=\""<<from_inv.dump()<<"\"" <<" list=\""<<from_list<<"\"" <<" i="<<from_i <<" to inv=\""<<to_inv.dump()<<"\"" <<" list=\""<<to_list<<"\"" <<" i="<<to_i <<std::endl; return; } ItemStack src_item = list_from->getItem(from_i); src_item.count = count; ItemStack from_stack_was = list_from->getItem(from_i); ItemStack to_stack_was = list_to->getItem(to_i); /* Perform actual move If something is wrong (source item is empty, destination is the same as source), nothing happens */ bool did_swap = false; move_count = list_from->moveItem(from_i, list_to, to_i, count, !caused_by_move_somewhere, &did_swap); // If source is infinite, reset it's stack if (src_can_take_count == -1) { // For the caused_by_move_somewhere == true case we didn't force-put the item, // which guarantees there is no leftover, and code below would duplicate the // (not replaced) to_stack_was item. if (!caused_by_move_somewhere) { // If destination stack is of different type and there are leftover // items, attempt to put the leftover items to a different place in the // destination inventory. // The client-side GUI will try to guess if this happens. if (from_stack_was.name != to_stack_was.name) { for (u32 i = 0; i < list_to->getSize(); i++) { if (list_to->getItem(i).empty()) { list_to->changeItem(i, to_stack_was); break; } } } } if (move_count > 0 || did_swap) { list_from->deleteItem(from_i); list_from->addItem(from_i, from_stack_was); } } // If destination is infinite, reset it's stack and take count from source if (dst_can_put_count == -1) { list_to->deleteItem(to_i); list_to->addItem(to_i, to_stack_was); list_from->deleteItem(from_i); list_from->addItem(from_i, from_stack_was); list_from->takeItem(from_i, count); } infostream << "IMoveAction::apply(): moved" << " msom=" << move_somewhere << " caused=" << caused_by_move_somewhere << " count=" << count << " from inv=\"" << from_inv.dump() << "\"" << " list=\"" << from_list << "\"" << " i=" << from_i << " to inv=\"" << to_inv.dump() << "\"" << " list=\"" << to_list << "\"" << " i=" << to_i << std::endl; // If we are inside the move somewhere loop, we don't need to report // anything if nothing happened (perhaps we don't need to report // anything for caused_by_move_somewhere == true, but this way its safer) if (caused_by_move_somewhere && move_count == 0) return; /* Record rollback information */ if (!ignore_rollback && gamedef->rollback()) { IRollbackManager *rollback = gamedef->rollback(); // If source is not infinite, record item take if (src_can_take_count != -1) { RollbackAction action; std::string loc; { std::ostringstream os(std::ios::binary); from_inv.serialize(os); loc = os.str(); } action.setModifyInventoryStack(loc, from_list, from_i, false, src_item); rollback->reportAction(action); } // If destination is not infinite, record item put if (dst_can_put_count != -1) { RollbackAction action; std::string loc; { std::ostringstream os(std::ios::binary); to_inv.serialize(os); loc = os.str(); } action.setModifyInventoryStack(loc, to_list, to_i, true, src_item); rollback->reportAction(action); } } /* Report move to endpoints */ /* Detached inventories */ // Both endpoints are same detached if (from_inv.type == InventoryLocation::DETACHED && from_inv == to_inv) { PLAYER_TO_SA(player)->detached_inventory_OnMove( *this, count, player); } else { // Destination is detached if (to_inv.type == InventoryLocation::DETACHED) { PLAYER_TO_SA(player)->detached_inventory_OnPut( *this, src_item, player); } // Source is detached if (from_inv.type == InventoryLocation::DETACHED) { PLAYER_TO_SA(player)->detached_inventory_OnTake( *this, src_item, player); } } /* Node metadata inventories */ // Both endpoints are same nodemeta if (from_inv.type == InventoryLocation::NODEMETA && from_inv == to_inv) { PLAYER_TO_SA(player)->nodemeta_inventory_OnMove( *this, count, player); } else { // Destination is nodemeta if (to_inv.type == InventoryLocation::NODEMETA) { PLAYER_TO_SA(player)->nodemeta_inventory_OnPut( *this, src_item, player); } // Source is nodemeta if (from_inv.type == InventoryLocation::NODEMETA) { PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( *this, src_item, player); } } // Player inventories // Both endpoints are same player inventory if (from_inv.type == InventoryLocation::PLAYER && from_inv == to_inv) { PLAYER_TO_SA(player)->player_inventory_OnMove( *this, count, player); } else { // Destination is player inventory if (to_inv.type == InventoryLocation::PLAYER) { PLAYER_TO_SA(player)->player_inventory_OnPut( *this, src_item, player); } // Source is player inventory if (from_inv.type == InventoryLocation::PLAYER) { PLAYER_TO_SA(player)->player_inventory_OnTake( *this, src_item, player); } } mgr->setInventoryModified(from_inv); if (inv_from != inv_to) mgr->setInventoryModified(to_inv); } void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef) { // Optional InventoryAction operation that is run on the client // to make lag less apparent. Inventory *inv_from = mgr->getInventory(from_inv); Inventory *inv_to = mgr->getInventory(to_inv); if (!inv_from || !inv_to) return; InventoryLocation current_player; current_player.setCurrentPlayer(); Inventory *inv_player = mgr->getInventory(current_player); if (inv_from != inv_player || inv_to != inv_player) return; InventoryList *list_from = inv_from->getList(from_list); InventoryList *list_to = inv_to->getList(to_list); if (!list_from || !list_to) return; if (!move_somewhere) list_from->moveItem(from_i, list_to, to_i, count); else list_from->moveItemSomewhere(from_i, list_to, count); mgr->setInventoryModified(from_inv); if (inv_from != inv_to) mgr->setInventoryModified(to_inv); } /* IDropAction */ IDropAction::IDropAction(std::istream &is) { std::string ts; std::getline(is, ts, ' '); count = stoi(ts); std::getline(is, ts, ' '); from_inv.deSerialize(ts); std::getline(is, from_list, ' '); std::getline(is, ts, ' '); from_i = stoi(ts); } void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) { Inventory *inv_from = mgr->getInventory(from_inv); if (!inv_from) { infostream<<"IDropAction::apply(): FAIL: source inventory not found: " <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl; return; } InventoryList *list_from = inv_from->getList(from_list); /* If a list doesn't exist or the source item doesn't exist */ if (!list_from) { infostream<<"IDropAction::apply(): FAIL: source list not found: " <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl; return; } if (list_from->getItem(from_i).empty()) { infostream<<"IDropAction::apply(): FAIL: source item not found: " <<"from_inv=\""<<from_inv.dump()<<"\"" <<", from_list=\""<<from_list<<"\"" <<" from_i="<<from_i<<std::endl; return; } /* Do not handle rollback if inventory is player's */ bool ignore_src_rollback = (from_inv.type == InventoryLocation::PLAYER); /* Collect information of endpoints */ int take_count = list_from->getItem(from_i).count; if (count != 0 && count < take_count) take_count = count; int src_can_take_count = take_count; ItemStack src_item = list_from->getItem(from_i); src_item.count = take_count; // Run callbacks depending on source inventory switch (from_inv.type) { case InventoryLocation::DETACHED: src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake( *this, src_item, player); break; case InventoryLocation::NODEMETA: src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake( *this, src_item, player); break; case InventoryLocation::PLAYER: src_can_take_count = PLAYER_TO_SA(player)->player_inventory_AllowTake( *this, src_item, player); break; default: break; } if (src_can_take_count != -1 && src_can_take_count < take_count) take_count = src_can_take_count; // Update item due executed callbacks src_item = list_from->getItem(from_i); // Drop the item ItemStack item1 = list_from->getItem(from_i); item1.count = take_count; if(PLAYER_TO_SA(player)->item_OnDrop(item1, player, player->getBasePosition())) { int actually_dropped_count = take_count - item1.count; if (actually_dropped_count == 0) { infostream<<"Actually dropped no items"<<std::endl; // Revert client prediction. See 'clientApply' if (from_inv.type == InventoryLocation::PLAYER) list_from->setModified(); return; } // If source isn't infinite if (src_can_take_count != -1) { // Take item from source list ItemStack item2 = list_from->takeItem(from_i, actually_dropped_count); if (item2.count != actually_dropped_count) errorstream<<"Could not take dropped count of items"<<std::endl; } src_item.count = actually_dropped_count; mgr->setInventoryModified(from_inv); } infostream<<"IDropAction::apply(): dropped " <<" from inv=\""<<from_inv.dump()<<"\"" <<" list=\""<<from_list<<"\"" <<" i="<<from_i <<std::endl; /* Report drop to endpoints */ switch (from_inv.type) { case InventoryLocation::DETACHED: PLAYER_TO_SA(player)->detached_inventory_OnTake( *this, src_item, player); break; case InventoryLocation::NODEMETA: PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( *this, src_item, player); break; case InventoryLocation::PLAYER: PLAYER_TO_SA(player)->player_inventory_OnTake( *this, src_item, player); break; default: break; } /* Record rollback information */ if (!ignore_src_rollback && gamedef->rollback()) { IRollbackManager *rollback = gamedef->rollback(); // If source is not infinite, record item take if (src_can_take_count != -1) { RollbackAction action; std::string loc; { std::ostringstream os(std::ios::binary); from_inv.serialize(os); loc = os.str(); } action.setModifyInventoryStack(loc, from_list, from_i, false, src_item); rollback->reportAction(action); } } } void IDropAction::clientApply(InventoryManager *mgr, IGameDef *gamedef) { // Optional InventoryAction operation that is run on the client // to make lag less apparent. Inventory *inv_from = mgr->getInventory(from_inv); if (!inv_from) return; InventoryLocation current_player; current_player.setCurrentPlayer(); Inventory *inv_player = mgr->getInventory(current_player); if (inv_from != inv_player) return; InventoryList *list_from = inv_from->getList(from_list); if (!list_from) return; if (count == 0) list_from->changeItem(from_i, ItemStack()); else list_from->takeItem(from_i, count); mgr->setInventoryModified(from_inv); } /* ICraftAction */ ICraftAction::ICraftAction(std::istream &is) { std::string ts; std::getline(is, ts, ' '); count = stoi(ts); std::getline(is, ts, ' '); craft_inv.deSerialize(ts); } void ICraftAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) { Inventory *inv_craft = mgr->getInventory(craft_inv); if (!inv_craft) { infostream << "ICraftAction::apply(): FAIL: inventory not found: " << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl; return; } InventoryList *list_craft = inv_craft->getList("craft"); InventoryList *list_craftresult = inv_craft->getList("craftresult"); InventoryList *list_main = inv_craft->getList("main"); /* If a list doesn't exist or the source item doesn't exist */ if (!list_craft) { infostream << "ICraftAction::apply(): FAIL: craft list not found: " << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl; return; } if (!list_craftresult) { infostream << "ICraftAction::apply(): FAIL: craftresult list not found: " << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl; return; } if (list_craftresult->getSize() < 1) { infostream << "ICraftAction::apply(): FAIL: craftresult list too short: " << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl; return; } ItemStack crafted; ItemStack craftresultitem; int count_remaining = count; std::vector<ItemStack> output_replacements; getCraftingResult(inv_craft, crafted, output_replacements, false, gamedef); PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv); bool found = !crafted.empty(); while (found && list_craftresult->itemFits(0, crafted)) { InventoryList saved_craft_list = *list_craft; std::vector<ItemStack> temp; // Decrement input and add crafting output getCraftingResult(inv_craft, crafted, temp, true, gamedef); PLAYER_TO_SA(player)->item_OnCraft(crafted, player, &saved_craft_list, craft_inv); list_craftresult->addItem(0, crafted); mgr->setInventoryModified(craft_inv); // Add the new replacements to the list IItemDefManager *itemdef = gamedef->getItemDefManager(); for (auto &itemstack : temp) { for (auto &output_replacement : output_replacements) { if (itemstack.name == output_replacement.name) { itemstack = output_replacement.addItem(itemstack, itemdef); if (itemstack.empty()) continue; } } output_replacements.push_back(itemstack); } actionstream << player->getDescription() << " crafts " << crafted.getItemString() << std::endl; // Decrement counter if (count_remaining == 1) break; if (count_remaining > 1) count_remaining--; // Get next crafting result getCraftingResult(inv_craft, crafted, temp, false, gamedef); PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv); found = !crafted.empty(); } // Put the replacements in the inventory or drop them on the floor, if // the inventory is full for (auto &output_replacement : output_replacements) { if (list_main) output_replacement = list_main->addItem(output_replacement); if (output_replacement.empty()) continue; u16 count = output_replacement.count; do { PLAYER_TO_SA(player)->item_OnDrop(output_replacement, player, player->getBasePosition()); if (count >= output_replacement.count) { errorstream << "Couldn't drop replacement stack " << output_replacement.getItemString() << " because drop loop didn't " "decrease count." << std::endl; break; } } while (!output_replacement.empty()); } infostream<<"ICraftAction::apply(): crafted " <<" craft_inv=\""<<craft_inv.dump()<<"\"" <<std::endl; } void ICraftAction::clientApply(InventoryManager *mgr, IGameDef *gamedef) { // Optional InventoryAction operation that is run on the client // to make lag less apparent. } // Crafting helper bool getCraftingResult(Inventory *inv, ItemStack &result, std::vector<ItemStack> &output_replacements, bool decrementInput, IGameDef *gamedef) { result.clear(); // Get the InventoryList in which we will operate InventoryList *clist = inv->getList("craft"); if (!clist) return false; // Mangle crafting grid to an another format CraftInput ci; ci.method = CRAFT_METHOD_NORMAL; ci.width = clist->getWidth() ? clist->getWidth() : 3; for (u16 i=0; i < clist->getSize(); i++) ci.items.push_back(clist->getItem(i)); // Find out what is crafted and add it to result item slot CraftOutput co; bool found = gamedef->getCraftDefManager()->getCraftResult( ci, co, output_replacements, decrementInput, gamedef); if (found) result.deSerialize(co.item, gamedef->getItemDefManager()); if (found && decrementInput) { // CraftInput has been changed, apply changes in clist for (u16 i=0; i < clist->getSize(); i++) { clist->changeItem(i, ci.items[i]); } } return found; }