Skip to content

Commit

Permalink
feat: add function for loading/unloading OTBM files per offset (#436)
Browse files Browse the repository at this point in the history
Add a new function called 'loadMapChunk' which allows loading or unloading an OTBM file live on your server by using an offset. Note that the file must be mapped starting from x:0 y:0 coordinates for the offset to work properly.
  • Loading branch information
Glatharth authored Jan 20, 2023
1 parent a3777d7 commit 298ca1a
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 43 deletions.
4 changes: 2 additions & 2 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,9 @@ bool Game::loadCustomMap(const std::string& filename)
return map.loadMapCustom(g_configManager().getString(DATA_DIRECTORY) + "/world/custom/" + filename + ".otbm", true, true, true);
}

void Game::loadMap(const std::string& path)
void Game::loadMap(const std::string& path, const Position& pos, bool unload)
{
map.loadMap(path);
map.loadMap(path, false, false, false, false, pos, unload);
}

Cylinder* Game::internalGetCylinder(Player* player, const Position& pos) const
Expand Down
2 changes: 1 addition & 1 deletion src/game/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class Game
* \returns true if the custom map was loaded successfully
*/
bool loadCustomMap(const std::string& filename);
void loadMap(const std::string& path);
void loadMap(const std::string& path, const Position& pos = Position(), bool unload = false);

void getMapDimensions(uint32_t& width, uint32_t& height) const {
width = map.width;
Expand Down
94 changes: 62 additions & 32 deletions src/io/iomap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "io/iomap.h"
#include "game/movement/teleport.h"
#include "game/game.h"

/*
OTBM_ROOTV1
Expand Down Expand Up @@ -55,7 +56,7 @@ Tile* IOMap::createTile(Item*& ground, Item* item, uint16_t x, uint16_t y, uint8
return tile;
}

bool IOMap::loadMap(Map* map, const std::string& fileName)
bool IOMap::loadMap(Map* map, const std::string& fileName, const Position& pos, bool unload)
{
int64_t start = OTSYS_TIME();
OTB::Loader loader{fileName, OTB::Identifier{{'O', 'T', 'B', 'M'}}};
Expand Down Expand Up @@ -108,7 +109,7 @@ bool IOMap::loadMap(Map* map, const std::string& fileName)

for (auto& mapDataNode : mapNode.children) {
if (mapDataNode.type == OTBM_TILE_AREA) {
if (!parseTileArea(loader, mapDataNode, *map)) {
if (!parseTileArea(loader, mapDataNode, *map, pos, unload)) {
return false;
}
} else if (mapDataNode.type == OTBM_TOWNS) {
Expand Down Expand Up @@ -188,7 +189,7 @@ bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode
return true;
}

bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map)
bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map, const Position& pos, bool unload)
{
PropStream propStream;
if (!loader.getProps(tileAreaNode, propStream)) {
Expand All @@ -204,7 +205,7 @@ bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Ma

uint16_t base_x = area_coord.x;
uint16_t base_y = area_coord.y;
uint16_t z = area_coord.z;
uint16_t base_z = area_coord.z;

static std::map<uint64_t, uint64_t> teleportMap;

Expand All @@ -225,8 +226,31 @@ bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Ma
return false;
}

uint16_t x = base_x + tile_coord.x;
uint16_t y = base_y + tile_coord.y;
uint16_t x = base_x + tile_coord.x + pos.x;
uint16_t y = base_y + tile_coord.y + pos.y;
uint8_t z = static_cast<uint8_t>(base_z + pos.z);

if (unload) {
Tile* tile = map.getTile(Position(x, y, z));

if (const TileItemVector* items = tile->getItemList();
items) {
TileItemVector item_list = *items;
if (!item_list.size() == 0) {
for (Item* item : item_list) {
if (item) {
g_game().internalRemoveItem(item);
}
}
}
}

if (Item* ground = tile->getGround();
ground) {
g_game().internalRemoveItem(ground);
}
continue;
}

bool isHouseTile = false;
House* house = nullptr;
Expand All @@ -243,42 +267,48 @@ bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Ma
return false;
}

house = map.houses.addHouse(houseId);
if (!house) {
std::ostringstream ss;
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId;
setLastErrorString(ss.str());
return false;
}
if (!unload) {
house = map.houses.addHouse(houseId);
if (!house) {
std::ostringstream ss;
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId;
setLastErrorString(ss.str());
return false;
}

tile = new HouseTile(x, y, z, house);
house->addTile(static_cast<HouseTile*>(tile));
isHouseTile = true;
tile = new HouseTile(x, y, z, house);
house->addTile(static_cast<HouseTile*>(tile));
isHouseTile = true;
}
}

uint8_t attribute;
//read tile attributes
while (propStream.read<uint8_t>(attribute)) {
switch (attribute) {
case OTBM_ATTR_TILE_FLAGS: {
uint32_t flags;
if (!propStream.read<uint32_t>(flags)) {
std::ostringstream ss;
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to read tile flags.";
setLastErrorString(ss.str());
return false;
}
if (!unload) {
uint32_t flags;
if (!propStream.read<uint32_t>(flags)) {
std::ostringstream ss;
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to read tile flags.";
setLastErrorString(ss.str());
return false;
}

if ((flags & OTBM_TILEFLAG_PROTECTIONZONE) != 0) {
tileflags |= TILESTATE_PROTECTIONZONE;
} else if ((flags & OTBM_TILEFLAG_NOPVPZONE) != 0) {
tileflags |= TILESTATE_NOPVPZONE;
} else if ((flags & OTBM_TILEFLAG_PVPZONE) != 0) {
tileflags |= TILESTATE_PVPZONE;
}
if ((flags & OTBM_TILEFLAG_PROTECTIONZONE) != 0) {
tileflags |= TILESTATE_PROTECTIONZONE;
}
else if ((flags & OTBM_TILEFLAG_NOPVPZONE) != 0) {
tileflags |= TILESTATE_NOPVPZONE;
}
else if ((flags & OTBM_TILEFLAG_PVPZONE) != 0) {
tileflags |= TILESTATE_PVPZONE;
}

if ((flags & OTBM_TILEFLAG_NOLOGOUT) != 0) {
tileflags |= TILESTATE_NOLOGOUT;
if ((flags & OTBM_TILEFLAG_NOLOGOUT) != 0) {
tileflags |= TILESTATE_NOLOGOUT;
}
}
break;
}
Expand Down
4 changes: 2 additions & 2 deletions src/io/iomap.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class IOMap
static Tile* createTile(Item*& ground, Item* item, uint16_t x, uint16_t y, uint8_t z);

public:
bool loadMap(Map* map, const std::string& identifier);
bool loadMap(Map* map, const std::string& identifier, const Position& pos = Position(), bool unload = false);

/**
* Load main map monsters
Expand Down Expand Up @@ -157,7 +157,7 @@ class IOMap
bool parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode, Map& map, const std::string& fileName);
bool parseWaypoints(OTB::Loader& loader, const OTB::Node& waypointsNode, Map& map);
bool parseTowns(OTB::Loader& loader, const OTB::Node& townsNode, Map& map);
bool parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map);
bool parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map, const Position& pos, bool unload);
std::string errorString;
};

Expand Down
9 changes: 9 additions & 0 deletions src/lua/functions/core/game/game_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ int GameFunctions::luaGameLoadMap(lua_State* L) {
return 0;
}

int GameFunctions::luaGameloadMapChunk(lua_State* L) {
// Game.loadMapChunk(path, position, remove)
const std::string& path = getString(L, 1);
const Position& position = getPosition(L, 2);
bool unload = getBoolean(L, 3);
g_dispatcher().addTask(createTask([path, position, unload]() {g_game().loadMap(path, position, unload); }));
return 0;
}

int GameFunctions::luaGameGetMonsterCount(lua_State* L) {
// Game.getMonsterCount()
lua_pushnumber(L, g_game().getMonstersOnline());
Expand Down
2 changes: 2 additions & 0 deletions src/lua/functions/core/game/game_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class GameFunctions final : LuaScriptInterface {

registerMethod(L, "Game", "getPlayers", GameFunctions::luaGameGetPlayers);
registerMethod(L, "Game", "loadMap", GameFunctions::luaGameLoadMap);
registerMethod(L, "Game", "loadMapChunk", GameFunctions::luaGameloadMapChunk);

registerMethod(L, "Game", "getMonsterCount", GameFunctions::luaGameGetMonsterCount);
registerMethod(L, "Game", "getPlayerCount", GameFunctions::luaGameGetPlayerCount);
Expand Down Expand Up @@ -85,6 +86,7 @@ class GameFunctions final : LuaScriptInterface {

static int luaGameGetPlayers(lua_State* L);
static int luaGameLoadMap(lua_State* L);
static int luaGameloadMapChunk(lua_State* L);

static int luaGameGetMonsterCount(lua_State* L);
static int luaGameGetPlayerCount(lua_State* L);
Expand Down
9 changes: 5 additions & 4 deletions src/map/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
#include "game/game.h"
#include "creatures/monsters/monster.h"

bool Map::load(const std::string& identifier) {
bool Map::load(const std::string& identifier, const Position& pos, bool unload) {
try {
IOMap loader;
if (!loader.loadMap(this, identifier)) {
if (!loader.loadMap(this, identifier, pos, unload)) {
SPDLOG_ERROR("[Map::load] - {}", loader.getLastErrorString());
return false;
}
Expand All @@ -33,7 +33,8 @@ bool Map::load(const std::string& identifier) {

bool Map::loadMap(const std::string& identifier,
bool mainMap /*= false*/,bool loadHouses /*= false*/,
bool loadMonsters /*= false*/, bool loadNpcs /*= false*/)
bool loadMonsters /*= false*/, bool loadNpcs /*= false*/,
const Position& pos /*= Position()*/, bool unload /*= false*/)
{
// Only download map if is loading the main map and it is not already downloaded
if (mainMap && g_configManager().getBoolean(TOGGLE_DOWNLOAD_MAP) && !std::filesystem::exists(identifier)) {
Expand All @@ -56,7 +57,7 @@ bool Map::loadMap(const std::string& identifier,
}

// Load the map
this->load(identifier);
this->load(identifier, pos, unload);

// Only create items from lua functions if is loading main map
// It needs to be after the load map to ensure the map already exists before creating the items
Expand Down
4 changes: 2 additions & 2 deletions src/map/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ class Map
* Load a map.
* \returns true if the map was loaded successfully
*/
bool load(const std::string& identifier);
bool load(const std::string& identifier, const Position& pos = Position(), bool unload = false);
/**
* Load the main map
* \param identifier Is the main map name (name of file .otbm)
Expand All @@ -184,7 +184,7 @@ class Map
* \param loadNpcs if true, the main map npcs is loaded
* \returns true if the main map was loaded successfully
*/
bool loadMap(const std::string& identifier, bool mainMap = false, bool loadHouses = false, bool loadMonsters = false, bool loadNpcs = false);
bool loadMap(const std::string& identifier, bool mainMap = false, bool loadHouses = false, bool loadMonsters = false, bool loadNpcs = false, const Position& pos = Position(), bool unload = false);
/**
* Load the custom map
* \param identifier Is the map custom folder
Expand Down

0 comments on commit 298ca1a

Please sign in to comment.