From 8a49d09b751ceb635537b3fa66ce1bc18632c0ac Mon Sep 17 00:00:00 2001 From: niczkx Date: Fri, 20 Jul 2012 20:56:08 +0200 Subject: [PATCH] stop using pairs instead of index loops, please, it's so much slower --- modules/game_market/market.lua | 5 +- src/framework/core/binarytree.cpp | 70 +++++++++++ src/framework/core/binarytree.h | 10 ++ src/framework/core/filestream.cpp | 7 ++ src/framework/core/filestream.h | 3 +- src/otclient/houses.cpp | 2 - src/otclient/item.cpp | 31 +++++ src/otclient/item.h | 5 +- src/otclient/luafunctions.cpp | 2 +- src/otclient/map.cpp | 185 +++++++++++++++++------------- src/otclient/map.h | 21 +++- src/otclient/tile.h | 7 +- 12 files changed, 253 insertions(+), 95 deletions(-) diff --git a/modules/game_market/market.lua b/modules/game_market/market.lua index e19fefcf..c28b729d 100644 --- a/modules/game_market/market.lua +++ b/modules/game_market/market.lua @@ -62,7 +62,8 @@ local function initMarketItems() -- populate all market items marketItems = {} local types = g_things.findThingTypeByAttr(ThingAttrMarket) - for k,t in pairs(types) do + for idx = 1, #types do + local t = types[idx] local newItem = Item.create(t:getId()) if newItem then local item = { @@ -352,4 +353,4 @@ function Market.onItemBoxChecked(widget) if widget:isChecked() then Market.updateSelectedItem(widget.item) end -end \ No newline at end of file +end diff --git a/src/framework/core/binarytree.cpp b/src/framework/core/binarytree.cpp index 10b85660..122ecc5c 100644 --- a/src/framework/core/binarytree.cpp +++ b/src/framework/core/binarytree.cpp @@ -162,3 +162,73 @@ std::string BinaryTree::getString() m_pos += len; return ret; } + +BinaryTreePtr BinaryTree::makeChild(uint8 type) +{ + BinaryTreePtr child(new BinaryTree(m_fin)); + child->setType(type); + children.append(child); + return child; +} + +void BinaryTree::setType(uint8 type) +{ + writeU8(0xFE); + writeU8(type); +} + +void BinaryTree::writeU8(uint8 u8) +{ + m_buffer.add(u8); +} + +void BinaryTree::writeU16(uint16 u16) +{ + stdext::writeLE16(m_buffer.data(), u16); +} + +void BinaryTree::writeU32(uint32 u32) +{ + stdext::writeLE32(m_buffer.data(), u32); +} + +void BinaryTree::writeString(const std::string& s) +{ + size_t len = s.length(); + writeU16(len); + m_buffer.grow(m_pos + len); + memcpy(&m_buffer[m_pos], s.c_str(), len); + m_pos += len; +} + +void BinaryTree::writePos(const Position& p) +{ + if(!p.isValid()) + stdext::throw_exception("invalid position passed to BinaryTree::writePos"); + + writeU16(p.x); + writeU16(p.y); + writeU8(p.z); +} + +void BinaryTree::writePoint(const Point& p) +{ + if(p.isNull()) + stdext::throw_exception("invalid point passed to BinaryTree::writePoint"); + + writeU8(p.x); + writeU8(p.y); +} + +void BinaryTree::writeToFile() +{ + if(!m_fin) + stdext::throw_exception("attempt to write binary node to closed file"); + + /// first write self data + m_fin->write(&m_buffer[0], m_buffer.size()); + + /// write children data + for(const BinaryTreePtr& child : m_children) + child->writeToFile(); +} diff --git a/src/framework/core/binarytree.h b/src/framework/core/binarytree.h index ca479e49..1e63c8ea 100644 --- a/src/framework/core/binarytree.h +++ b/src/framework/core/binarytree.h @@ -52,6 +52,16 @@ public: Position getPosition() { return Position(getU16(), getU16(), getU8()); } Point getPoint() { return Point(getU8(), getU8()); } + void setType(uint8 type); + void writeU8(uint8 u8); + void writeU16(uint16 u16); + void writeU32(uint32 u32); + void writeString(const std::string& s); + void writePos(const Position& p); + void writePoint(const Point& p); + BinaryTreePtr makeChild(uint8 type); + void writeToFile(); + BinaryTreeVec getChildren(); bool canRead() { unserialize(); return m_pos < m_buffer.size(); } diff --git a/src/framework/core/filestream.cpp b/src/framework/core/filestream.cpp index df40b576..2b8df06f 100644 --- a/src/framework/core/filestream.cpp +++ b/src/framework/core/filestream.cpp @@ -256,6 +256,13 @@ BinaryTreePtr FileStream::getBinaryTree() return BinaryTreePtr(new BinaryTree(asFileStream())); } +BinaryTreePtr FileStream::makeTree() +{ + BinaryTreePtr root(new BinaryTree(asFileStream())); + root->setType(0); + return root; +} + void FileStream::addU8(uint8 v) { if(!m_caching) { diff --git a/src/framework/core/filestream.h b/src/framework/core/filestream.h index bac90f10..367d9f41 100644 --- a/src/framework/core/filestream.h +++ b/src/framework/core/filestream.h @@ -59,8 +59,7 @@ public: void addU32(uint32 v); void addU64(uint64 v); void addString(const std::string& v); - void startNode(uint8 nodeType) { addU8(0xFE); addU8(nodeType); } - void endNode() { addU8(0xFF); } + BinaryTreePtr makeTree(); FileStreamPtr asFileStream() { return std::static_pointer_cast(shared_from_this()); } diff --git a/src/otclient/houses.cpp b/src/otclient/houses.cpp index 8d2096e3..6b5be5b1 100644 --- a/src/otclient/houses.cpp +++ b/src/otclient/houses.cpp @@ -92,8 +92,6 @@ void Houses::load(const std::string& fileName) house->load(elem); } - - g_logger.debug("Loaded houses.xml successfully."); } HouseList::iterator Houses::findHouse(uint32 houseId) diff --git a/src/otclient/item.cpp b/src/otclient/item.cpp index 21f64074..825fc650 100644 --- a/src/otclient/item.cpp +++ b/src/otclient/item.cpp @@ -263,6 +263,37 @@ void Item::unserializeItem(const BinaryTreePtr &in) } } +void Item::serializeItem(const BinaryTreePtr& out) +{ + out->writeU8(ATTR_COUNT); + out->writeU8(getCount()); + + out->writeU8(ATTR_CHARGES); + out->writeU16(getCountOrSubType()); + + Position dest = m_attribs.get(ATTR_TELE_DEST); + if(dest.isValid()) { + out->writeU8(ATTR_TELE_DEST); + out->writePos(dest); + } + + if(isDepot()) { + out->writeU8(ATTR_DEPOT_ID); + out->writeU16(getDepotId()); + } + + uint16 aid = m_attribs.get(ATTR_ACTION_ID), uid = m_attribs.get(ATTR_UNIQUE_ID); + if(aid) { + out->writeU8(ATTR_ACTION_ID); + out->writeU16(aid); + } + + if(uid) { + out->writeU8(ATTR_UNIQUE_ID); + out->writeU16(uid); + } +} + bool Item::isMoveable() { return !rawGetThingType()->isNotMoveable(); diff --git a/src/otclient/item.h b/src/otclient/item.h index 529e8fec..5b252792 100644 --- a/src/otclient/item.h +++ b/src/otclient/item.h @@ -97,8 +97,8 @@ public: ItemPtr clone(); - void unserializeItem(const BinaryTreePtr &in); - bool isMoveable(); + void unserializeItem(const BinaryTreePtr& in); + void serializeItem(const BinaryTreePtr& out); void setDepotId(uint16 depotId) { m_attribs.set(ATTR_DEPOT_ID, depotId); } uint16 getDepotId() { return m_attribs.get(ATTR_DEPOT_ID); } @@ -113,6 +113,7 @@ public: bool isContainer() { return m_attribs.has(ATTR_CONTAINER_ITEMS); } bool isDoor() { return m_attribs.has(ATTR_HOUSEDOORID); } bool isTeleport() { return m_attribs.has(ATTR_TELE_DEST); } + bool isMoveable(); ItemPtr asItem() { return std::static_pointer_cast(shared_from_this()); } bool isItem() { return true; } diff --git a/src/otclient/luafunctions.cpp b/src/otclient/luafunctions.cpp index 03b08229..abae76e7 100644 --- a/src/otclient/luafunctions.cpp +++ b/src/otclient/luafunctions.cpp @@ -98,7 +98,7 @@ void OTClient::registerLuaFunctions() g_lua.bindSingletonFunction("g_map", "getSpectators", &Map::getSpectators, &g_map); g_lua.bindSingletonFunction("g_map", "findPath", &Map::findPath, &g_map); g_lua.bindSingletonFunction("g_map", "loadOtbm", &Map::loadOtbm, &g_map); - //g_lua.bindSingletonFunction("g_map", "saveOtbm", &Map::saveOtbm, &g_map); + g_lua.bindSingletonFunction("g_map", "saveOtbm", &Map::saveOtbm, &g_map); g_lua.bindSingletonFunction("g_map", "loadOtcm", &Map::loadOtcm, &g_map); g_lua.bindSingletonFunction("g_map", "saveOtcm", &Map::saveOtcm, &g_map); g_lua.bindSingletonFunction("g_map", "loadMonsters", &Map::loadMonsters, &g_map); diff --git a/src/otclient/map.cpp b/src/otclient/map.cpp index 06bfe730..36565148 100644 --- a/src/otclient/map.cpp +++ b/src/otclient/map.cpp @@ -36,6 +36,9 @@ #include #include +/// TODO: Move it to Position class if needed +static inline Position operator&(const Position& pos, int a) { return Position(pos.x & a, pos.y & a, pos.z); } + Map g_map; void Map::terminate() @@ -82,8 +85,7 @@ void Map::loadOtbm(const std::string& fileName) if(!headerVersion || headerVersion > 3) stdext::throw_exception(stdext::format("Unknown OTBM version detected: %u.", headerVersion)); - m_width = root->getU16(); - m_height = root->getU16(); + setSize(root->getU16(), root->getU16()); dump << "Map size: " << m_width << "x" << m_height; uint32 headerMajorItems = root->getU8(); @@ -188,17 +190,14 @@ void Map::loadOtbm(const std::string& fileName) item->unserializeItem(nodeItem); if(item->isContainer()) { - // This is a temporary way for reading container items. - MapContainerPtr mapContainer(new MapContainer); - for(const BinaryTreePtr &insideItem : nodeItem->getChildren()) { - if(insideItem->getU8() != OTBM_ITEM) + for(const BinaryTreePtr& containerItem : nodeItem->getChildren()) { + if(containerItem->getU8() != OTBM_ITEM) stdext::throw_exception("invalid container item node"); - ItemPtr newItem = Item::createFromOtb(insideItem->getU16()); - newItem->unserializeItem(insideItem); - mapContainer->add(newItem); + ItemPtr cItem = Item::createFromOtb(containerItem->getU16()); + cItem->unserializeItem(containerItem); + item->addContainerItem(cItem); } - m_containers.push_back(mapContainer); } if(house && item->isMoveable()) { @@ -255,111 +254,139 @@ void Map::loadOtbm(const std::string& fileName) void Map::saveOtbm(const std::string &fileName) { - // TODO: Continue sleepy work. -#if 0 - /// TODO: Use binary trees for this - FileStreamPtr fin = g_resources.openFile(fileName); + /// FIXME: Untested code + FileStreamPtr fin = g_resources.appendFile(fileName); if(!fin) - stdext::throw_exception(stdext::format("failed to open file '%s'", fileName)); + stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName)); std::string dir; - if(fileName.find_last_of('/') <= 0) + if(fileName.find_last_of('/') == std::string::npos) dir = g_resources.getWorkDir(); else dir = fileName.substr(0, fileName.find_last_of('/')); - if(m_houseFile.empty()) - m_houseFile = "houses.xml"; - if(m_spawnFile.empty()) - m_spawnFile = "spawns.xml"; + uint32 version = 0; + /// Support old versions (< 810 or 860 IIRC) + /// TODO: Use constants? + if(g_things.getOtbMajorVersion() < 10) + version = 1; + else + version = 2; + + /// Usually when a map has empty house/spawn file it means the map is new. + /// TODO: Ask the user for a map name instead of those ugly uses of substr + std::string houseFile = getHousefile(), spawnFile = getSpawnFile(); + if(houseFile.empty() && version > 1) + houseFile = fileName.substr(fileName.find_last_of('/')) + "-houses.xml"; + if(spawnFile.empty()) + spawnFile = fileName.substr(fileName.find_last_of('/')) + "-spawns.xml"; #if 0 - if(!m_houses->save(dir + "/" + m_houseFile)) - ; - if(!m_spawns->save(dir + "/" + m_spawnFile)) - ; -#endif + if(version > 1) + m_houses->save(dir + "/" + houseFile); - uint32 ver; - if(g_things.getOtbMajorVersion() < 2) - ver =0; - else if(g_things.getOtbMajorVersion() < 10) - ver = 1; - else - ver = 2; + saveSpawns(dir + "/" + spawnFile); +#endif - fin->addU32(0x00); // file version + time_t start = time(0); + fin->addU32(0); // file version + BinaryTreePtr root = fin->makeTree(); { - fin->startNode(0x00); // root - fin->addU32(ver); - fin->addU16(m_width); // some random width. - fin->addU16(m_height); // some random height. + root->writeU32(version); + + Size mapSize = getSize(); + root->writeU16(mapSize.width()); + root->writeU16(mapSize.height()); - fin->addU32(g_things.getOtbMajorVersion()); - fin->addU32(g_things.getOtbMinorVersion()); + root->writeU32(g_things.getOtbMajorVersion()); + root->writeU32(g_things.getOtbMinorVersion()); - fin->startNode(OTBM_MAP_DATA); // map data node + BinaryTreePtr mapData = root->makeChild(OTBM_MAP_DATA); { // own description. - fin->addU8(OTBM_ATTR_DESCRIPTION); - fin->addString(m_description); + for(const auto& desc : getDescriptions()) { + mapData->writeU8(OTBM_ATTR_DESCRIPTION); + mapData->writeString(desc); + } // special one - fin->addU8(OTBM_ATTR_DESCRIPTION); - fin->addString(stdext::format("Saved with %s v%d", g_app.getName(), stdext::unsafe_cast(g_app.getVersion()))); + mapData->writeU8(OTBM_ATTR_DESCRIPTION); + mapData->writeString(stdext::format("Saved with %s v%d", g_app.getName(), stdext::unsafe_cast(g_app.getVersion()))); // spawn file. - fin->addU8(OTBM_ATTR_SPAWN_FILE); - fin->addString(m_spawnFile); + mapData->addU8(OTBM_ATTR_SPAWN_FILE); + mapData->addString(spawnFile); // house file. if(ver > 1) { - fin->addU8(OTBM_ATTR_HOUSE_FILE); - fin->addString(m_houseFile); + mapData->addU8(OTBM_ATTR_HOUSE_FILE); + mapData->addString(houseFile); } - Position pos(-1, -1, -1); - Boolean first; - for (const auto& pair : m_tiles) { + /// write tiles first + BinaryTreePtr tileArea = mapData->makeChild(OTBM_TILE_AREA); + Position base(-1, -1, -1); + for(const auto& pair : m_tiles) { + Position pos = pair.first; TilePtr tile = pair.second; - if(!tile || tile->isEmpty()) - continue; - Position tilePos = pair.first; - if(tilePos.x < pos.x || tilePos.x >= pos.x + 256 || - tilePos.y < pos.y || tilePos.y >= pos.y + 256 || - tilePos.z != pos.z) { - if(!first) - fin->endNode(); - - pos.x = tilePos.x & 0xFF00; - pos.y = tilePos.y & 0xFF00; - pos.z = tilePos.z; - fin->addU16(pos.x); - fin->addU16(pos.y); - fin->addU8(pos.z); + /// base position + if(pos.x < base.x || pos.x >= base.x + 256 || pos.y < base.y|| pos.y >= base.y + 256 || + pos.z != base.z) { + tileArea->writePos(base = pos & 0xFF00); } - // TODO: hOUSES. - fin->startNode(OTBM_TILE); - fin->addU8(tilePos.x); - fin->addU8(tilePos.y); + BinaryTreePtr tileNode(nullptr); + tileflags_t flags = tile->getFlags(); + if((flags & TILESTATE_HOUSE) == TILESTATE_HOUSE) + tileNode = tileArea->makeChild(OTBM_HOUSETILE); + else + tileNode = tileArea->makeChild(OTBM_TILE); + + tileNode->writePoint(Point(pos.x, pos.y)); + if(tileNode->getType() == OTBM_HOUSETILE) + tileNode->writeU32(tile->getHouseId()); -#if 0 - // TODO: hOUSES again. - if(is house tile) - add u32 house id...; -#endif - if(tile->flags()) { - fin->addU8(OTBM_ATTR_TILE_FLAGS); - fin->addU32(tile->flags()); + /// Tile flags + if(flags != 0) { + tileNode->writeU8(OTBM_ATTR_TILE_FLAGS); + tileNode->writeU32(flags); } + /// start writing tile items + for(const ItemPtr& item : tile->getItems()) { + BinaryTreePtr itemNode = tileNode->makeChild(OTBM_ATTR_ITEM); + item->serializeItem(itemNode); + } + } + + /// write towns + BinaryTreePtr townNode = mapData->makeChild(OTBM_TOWNS); + for(const TownPtr& town : m_towns.getTowns()) { + BinaryTreePtr newTown = townNode->makeChild(OTBM_TOWN); + { + newTown->writeU32(town->getId()); + newTown->writeString(town->getName()); + newTown->writePos(town->getPos()); + } + } + /// write waypoints + if(version > 1) { + BinaryTreePtr waypointNode = mapData->makeChild(OTBM_WAYPOINTS); + for(const auto& it : m_waypoints) { + BinaryTreePtr newWaypoint = waypointNode->makeChild(OTBM_WAYPOINT); + { + newWaypoint->writeString(it.second); + newWaypoint->writePos(it.first); + } + } } } } -#endif + + root->writeToFile(); + g_logger.debug(stdext::format("OTBM saving took %ld", time(0) - start); } void Map::loadSpawns(const std::string &fileName) diff --git a/src/otclient/map.h b/src/otclient/map.h index 315f83dc..6cc8b5e2 100644 --- a/src/otclient/map.h +++ b/src/otclient/map.h @@ -57,7 +57,10 @@ enum OTBM_ItemAttr OTBM_ATTR_SLEEPSTART = 21, OTBM_ATTR_CHARGES = 22, OTBM_ATTR_CONTAINER_ITEMS = 23, - OTBM_ATTR_ATTRIBUTE_MAP = 128 + OTBM_ATTR_ATTRIBUTE_MAP = 128, + /// just random numbers, they're not actually used by the binary reader... + OTBM_ATTR_WIDTH = 129, + OTBM_ATTR_HEIGHT = 130 }; enum OTBM_NodeTypes_t @@ -121,6 +124,18 @@ public: void loadSpawns(const std::string& fileName); void saveSpawns(const std::string&) { } + // otbm attributes (description, size, etc.) + void setHouseFile(const std::string& file) { m_attribs.set(OTBM_ATTR_HOUSE_FILE, file); } + void setSpawnFile(const std::string& file) { m_attribs.set(OTBM_ATTR_SPAWN_FILE, file); } + void setDescription(const std::string& desc) { m_attribs.set(OTBM_ATTR_DESCRIPTION, desc); } + void setWidth(uint16 w) { m_attribs.set(OTBM_ATTR_WIDTH, w); } + void setHeight(uint16 h) { m_attribs.set(OTBM_ATTR_HEIGHT, h); } + + std::string getHouseFile() { return m_attribs.get(OTBM_ATTR_HOUSE_FILE); } + std::string getSpawnFile() { return m_attribs.get(OTBM_ATTR_SPAWN_FILE); } + Size getSize() { return Size(m_attribs.get(OTBM_ATTR_WIDTH), m_attribs.get(OTBM_ATTR_HEIGHT)); } + std::vector getDescriptions() { return stdext::split(m_attribs.get(), "\n"; } + void loadMonsters(const std::string& fileName) { m_creatures.loadMonsters(fileName); } void loadSingleCreature(const std::string& file) { m_creatures.loadSingleCreature(file); } void loadNpcs(const std::string& folder) { m_creatures.loadNpcs(folder); } @@ -190,13 +205,9 @@ private: Rect m_tilesRect; AttribStorage m_attribs; - std::string m_description, m_spawnFile, m_houseFile; - Houses m_houses; Towns m_towns; Creatures m_creatures; - - uint16 m_width, m_height; }; extern Map g_map; diff --git a/src/otclient/tile.h b/src/otclient/tile.h index 4b3131a9..f2412fce 100644 --- a/src/otclient/tile.h +++ b/src/otclient/tile.h @@ -101,7 +101,10 @@ public: bool canErase(); void setFlags(tileflags_t flags) { m_flags |= (uint32)flags; } - uint32 flags() { return m_flags; } + uint32 getFlags() { return m_flags; } + + void setHouseId(uint32 hid) { if(m_flags & TILESTATE_HOUSE) m_houseId = hid; } + uint32 getHouseId() { return m_houseId; } TilePtr asTile() { return std::static_pointer_cast(shared_from_this()); } @@ -113,7 +116,7 @@ private: std::vector m_things; Position m_position; uint8 m_drawElevation; - uint32 m_flags; + uint32 m_flags, m_houseId; uint8 m_minimapColorByte; }; #pragma pack(pop)