diff --git a/data/XML/groups.xml b/data/XML/groups.xml
index 935cb2c..bdd3c44 100644
--- a/data/XML/groups.xml
+++ b/data/XML/groups.xml
@@ -142,6 +142,7 @@
+
diff --git a/data/chatchannels/chatchannels.xml b/data/chatchannels/chatchannels.xml
index 66e0a41..877d47d 100644
--- a/data/chatchannels/chatchannels.xml
+++ b/data/chatchannels/chatchannels.xml
@@ -5,7 +5,7 @@
-
+
-
+
diff --git a/data/chatchannels/scripts/execute.lua b/data/chatchannels/scripts/execute.lua
index 47b18c9..fcbd312 100644
--- a/data/chatchannels/scripts/execute.lua
+++ b/data/chatchannels/scripts/execute.lua
@@ -1,4 +1,4 @@
-local CHAT_ID = 9
+local CHAT_ID = 7
function canJoin(player) return player:getAccountType() >= ACCOUNT_TYPE_GOD end
diff --git a/data/scripts/eventcallbacks/account_manager.lua b/data/scripts/eventcallbacks/account_manager.lua
index 47fb200..056df53 100644
--- a/data/scripts/eventcallbacks/account_manager.lua
+++ b/data/scripts/eventcallbacks/account_manager.lua
@@ -1,7 +1,12 @@
-- #region Configuration
local ACCOUNT_MANAGER_ACCOUNT_ID = 1
local ACCOUNT_MANAGER_STORAGE = {}
-local ACCOUNT_MANAGER_LOGOUT_DELAY = 60 -- 60 seconds
+local ACCOUNT_MANAGER_LOGOUT_DELAY = 1 * 60 -- 1 minute
+
+local ACCOUNT_MANAGER_LOGINS = {}
+local ACCOUNT_MANAGER_LOGIN_TRIES = 5
+local ACCOUNT_MANAGER_LOGIN_TIMEOUT = 30 -- 30 seconds
+local ACCOUNT_MANAGER_BAN_TIME = 1 * 60 -- 10 minutes
local CREATE_ACCOUNT_TABLE = {}
local CREATE_ACCOUNT_EXHAUST = 30 * 60 -- 30 minutes
@@ -449,13 +454,50 @@ local function logoutEvent(player, playerId)
if player then player:remove() end
end
+---@param player Player
+---@param days integer
+---@param reason string
+---@return boolean
+function banIp(player, days, reason)
+ local ip = player:getIp()
+ local resultId = db.storeQuery("SELECT 1 FROM `ip_bans` WHERE `ip` = " .. ip)
+ if resultId then
+ result.free(resultId)
+ return false
+ end
+
+ local timeNow = os.time()
+ db.query(string.format(
+ "INSERT INTO `ip_bans` (`ip`, `reason`, `banned_at`, `expires_at`, `banned_by`) VALUES (%d, %s, %d, %d, %d)",
+ ip, db.escapeString(reason), timeNow, timeNow + (days * 86400),
+ player:getGuid()))
+ return true
+end
+
local login = CreatureEvent("Account Manager Login")
function login.onLogin(player)
if player:isAccountManager() then
+ local playerIp = player:getIp()
+ -- Protect
+ local tries = ACCOUNT_MANAGER_LOGINS[playerIp]
+ if tries and tries >= ACCOUNT_MANAGER_LOGIN_TRIES then
+ banIp(player, ACCOUNT_MANAGER_BAN_TIME, "Too many login attempts.")
+ return false
+ end
+
+ ACCOUNT_MANAGER_LOGINS[playerIp] = tries and tries + 1 or 1
+ -- Decrease Tries
+ addEvent(function(playerIp)
+ ACCOUNT_MANAGER_LOGINS[playerIp] = ACCOUNT_MANAGER_LOGINS[playerIp] - 1
+ if ACCOUNT_MANAGER_LOGINS[playerIp] <= 0 then
+ ACCOUNT_MANAGER_LOGINS[playerIp] = nil
+ end
+ end, ACCOUNT_MANAGER_LOGIN_TIMEOUT * 1000, playerIp)
+
-- Logout Event
addEvent(logoutEvent, ACCOUNT_MANAGER_LOGOUT_DELAY * 1000, player,
- player:getId())
+ player:getIp())
-- Initial Message
local send = sender(player)
diff --git a/data/scripts/eventcallbacks/player/default_onReportBug.lua b/data/scripts/eventcallbacks/player/default_onReportBug.lua
index bb04c3b..0a64c2c 100644
--- a/data/scripts/eventcallbacks/player/default_onReportBug.lua
+++ b/data/scripts/eventcallbacks/player/default_onReportBug.lua
@@ -12,19 +12,18 @@ event.onReportBug = function(self, message, position, category)
return true
end
- io.output(file)
- io.write("------------------------------\n")
- io.write("Name: " .. name)
+ file:write("------------------------------\n")
+ file:write("Name: " .. name)
if category == BUG_CATEGORY_MAP then
- io.write(" [Map position: " .. position.x .. ", " .. position.y .. ", " ..
+ file:write(" [Map position: " .. position.x .. ", " .. position.y .. ", " ..
position.z .. "]")
end
local playerPosition = self:getPosition()
- io.write(
+ file:write(
" [Player Position: " .. playerPosition.x .. ", " .. playerPosition.y .. ", " ..
playerPosition.z .. "]\n")
- io.write("Comment: " .. message .. "\n")
- io.close(file)
+ file:write("Comment: " .. message .. "\n")
+ file:close()
self:sendTextMessage(MESSAGE_EVENT_DEFAULT, "Your report has been sent to " ..
configManager.getString(configKeys.SERVER_NAME) .. ".")
diff --git a/data/scripts/eventcallbacks/player/default_onReportRuleViolation.lua b/data/scripts/eventcallbacks/player/default_onReportRuleViolation.lua
index ab22e7c..d6fa7e5 100644
--- a/data/scripts/eventcallbacks/player/default_onReportRuleViolation.lua
+++ b/data/scripts/eventcallbacks/player/default_onReportRuleViolation.lua
@@ -2,7 +2,7 @@ local function hasPendingReport(name, targetName, reportType)
local f = io.open(string.format("data/reports/players/%s-%s-%d.txt", name,
targetName, reportType), "r")
if f then
- io.close(f)
+ f:close()
return true
else
return false
@@ -27,18 +27,17 @@ event.onReportRuleViolation = function(self, targetName, reportType,
return
end
- io.output(file)
- io.write("------------------------------\n")
- io.write("Reported by: " .. name .. "\n")
- io.write("Target: " .. targetName .. "\n")
- io.write("Type: " .. reportType .. "\n")
- io.write("Reason: " .. reportReason .. "\n")
- io.write("Comment: " .. comment .. "\n")
+ file:write("------------------------------\n")
+ file:write("Reported by: " .. name .. "\n")
+ file:write("Target: " .. targetName .. "\n")
+ file:write("Type: " .. reportType .. "\n")
+ file:write("Reason: " .. reportReason .. "\n")
+ file:write("Comment: " .. comment .. "\n")
if reportType ~= REPORT_TYPE_BOT then
- io.write("Translation: " .. translation .. "\n")
+ file:write("Translation: " .. translation .. "\n")
end
- io.write("------------------------------\n")
- io.close(file)
+ file:write("------------------------------\n")
+ file:close()
self:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format(
"Thank you for reporting %s. Your report will be processed by %s team as soon as possible.",
targetName,
diff --git a/src/const.h b/src/const.h
index cce3252..353686c 100644
--- a/src/const.h
+++ b/src/const.h
@@ -457,6 +457,7 @@ enum PlayerFlags : uint64_t
PlayerFlag_IsAlwaysPremium = static_cast(1) << 37,
PlayerFlag_IgnoreYellCheck = static_cast(1) << 38,
PlayerFlag_IgnoreSendPrivateCheck = static_cast(1) << 39,
+ PlayerFlag_CanThrowFar = static_cast(1) << 40,
};
enum ReloadTypes_t : uint8_t
diff --git a/src/game.cpp b/src/game.cpp
index 0cc6b67..166748c 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -546,7 +546,7 @@ bool Game::removeCreature(Creature* creature, bool isLogout /* = true*/)
for (Creature* spectator : spectators) {
if (Player* player = spectator->getPlayer()) {
if (player->canSeeCreature(creature)) {
- player->sendRemoveTileCreature(creature, tilePosition, oldStackPosVector[i++]);
+ player->sendRemoveTileThing(tilePosition, oldStackPosVector[i++]);
}
}
}
@@ -672,9 +672,11 @@ void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Po
return;
}
+ const bool canThrowFar = player->hasFlag(PlayerFlag_CanThrowFar);
+
player->setNextActionTask(nullptr);
- if (!Position::areInRange<1, 1, 0>(movingCreatureOrigPos, player->getPosition())) {
+ if (!canThrowFar && !Position::areInRange<1, 1, 0>(movingCreatureOrigPos, player->getPosition())) {
// need to walk to the creature first before moving it
std::vector listDir;
if (player->getPathTo(movingCreatureOrigPos, listDir, 0, 1, true, true)) {
@@ -693,31 +695,35 @@ void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Po
return;
}
- if ((!movingCreature->isPushable() && !player->hasFlag(PlayerFlag_CanPushAllCreatures)) ||
- (movingCreature->isInGhostMode() && !player->canSeeGhostMode(movingCreature))) {
- player->sendCancelMessage(RETURNVALUE_NOTMOVEABLE);
- return;
+ if (!canThrowFar) {
+ if ((!movingCreature->isPushable() && !player->hasFlag(PlayerFlag_CanPushAllCreatures)) ||
+ (movingCreature->isInGhostMode() && !player->canSeeGhostMode(movingCreature))) {
+ player->sendCancelMessage(RETURNVALUE_NOTMOVEABLE);
+ return;
+ }
}
// check throw distance
const Position& movingCreaturePos = movingCreature->getPosition();
const Position& toPos = toTile->getPosition();
- if ((Position::getDistanceX(movingCreaturePos, toPos) > movingCreature->getThrowRange()) ||
- (Position::getDistanceY(movingCreaturePos, toPos) > movingCreature->getThrowRange()) ||
- (Position::getDistanceZ(movingCreaturePos, toPos) * 4 > movingCreature->getThrowRange())) {
- player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
- return;
+ if (!canThrowFar) {
+ if ((Position::getDistanceX(movingCreaturePos, toPos) > movingCreature->getThrowRange()) ||
+ (Position::getDistanceY(movingCreaturePos, toPos) > movingCreature->getThrowRange()) ||
+ (Position::getDistanceZ(movingCreaturePos, toPos) * 4 > movingCreature->getThrowRange())) {
+ player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
+ return;
+ }
}
if (player != movingCreature) {
- if (toTile->hasFlag(TILESTATE_BLOCKPATH)) {
+ if (!canThrowFar && toTile->hasFlag(TILESTATE_BLOCKPATH)) {
player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
return;
} else if ((movingCreature->getZone() == ZONE_PROTECTION && !toTile->hasFlag(TILESTATE_PROTECTIONZONE)) ||
(movingCreature->getZone() == ZONE_NOPVP && !toTile->hasFlag(TILESTATE_NOPVPZONE))) {
player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
return;
- } else {
+ } else if (!canThrowFar) {
if (CreatureVector* tileCreatures = toTile->getCreatures()) {
for (Creature* tileCreature : *tileCreatures) {
if (!tileCreature->isInGhostMode()) {
@@ -739,7 +745,7 @@ void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Po
return;
}
- ReturnValue ret = internalMoveCreature(*movingCreature, *toTile);
+ ReturnValue ret = internalMoveCreature(*movingCreature, *toTile, canThrowFar ? FLAG_NOLIMIT : 0);
if (ret != RETURNVALUE_NOERROR) {
player->sendCancelMessage(ret);
}
@@ -913,15 +919,17 @@ void Game::playerMoveItem(Player* player, const Position& fromPos, uint16_t spri
return;
}
+ const bool canThrowFar = player->hasFlag(PlayerFlag_CanThrowFar);
+
const Position& playerPos = player->getPosition();
const Position& mapFromPos = fromCylinder->getTile()->getPosition();
- if (playerPos.z != mapFromPos.z) {
+ if (!canThrowFar && playerPos.z != mapFromPos.z) {
player->sendCancelMessage(playerPos.z > mapFromPos.z ? RETURNVALUE_FIRSTGOUPSTAIRS
: RETURNVALUE_FIRSTGODOWNSTAIRS);
return;
}
- if (!Position::areInRange<1, 1>(playerPos, mapFromPos)) {
+ if (!canThrowFar && !Position::areInRange<1, 1>(playerPos, mapFromPos)) {
// need to walk to the item first before using it
std::vector listDir;
if (player->getPathTo(item->getPosition(), listDir, 0, 1, true, true)) {
@@ -1004,21 +1012,23 @@ void Game::playerMoveItem(Player* player, const Position& fromPos, uint16_t spri
}
}
- if (!item->isPickupable() && playerPos.z != mapToPos.z) {
+ if (!canThrowFar && !item->isPickupable() && playerPos.z != mapToPos.z) {
player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
return;
}
- int32_t throwRange = item->getThrowRange();
- if ((Position::getDistanceX(playerPos, mapToPos) > throwRange) ||
- (Position::getDistanceY(playerPos, mapToPos) > throwRange)) {
- player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
- return;
- }
+ if (!canThrowFar) {
+ int32_t throwRange = item->getThrowRange();
+ if ((Position::getDistanceX(playerPos, mapToPos) > throwRange) ||
+ (Position::getDistanceY(playerPos, mapToPos) > throwRange)) {
+ player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
+ return;
+ }
- if (!canThrowObjectTo(mapFromPos, mapToPos, true, false, throwRange, throwRange)) {
- player->sendCancelMessage(RETURNVALUE_CANNOTTHROW);
- return;
+ if (!canThrowObjectTo(mapFromPos, mapToPos, true, false, throwRange, throwRange)) {
+ player->sendCancelMessage(RETURNVALUE_CANNOTTHROW);
+ return;
+ }
}
uint8_t toIndex = 0;
@@ -1030,8 +1040,8 @@ void Game::playerMoveItem(Player* player, const Position& fromPos, uint16_t spri
}
}
- ReturnValue ret =
- internalMoveItem(fromCylinder, toCylinder, toIndex, item, count, nullptr, 0, player, nullptr, &fromPos, &toPos);
+ ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, count, nullptr,
+ canThrowFar ? FLAG_NOLIMIT : 0, player, nullptr, &fromPos, &toPos);
if (ret != RETURNVALUE_NOERROR) {
player->sendCancelMessage(ret);
}
@@ -1105,7 +1115,7 @@ ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder,
fromCylinder->postAddNotification(toItem, toCylinder, newToItemIndex);
}
- ret = toCylinder->queryAdd(index, *item, count, flags);
+ ret = toCylinder->queryAdd(index, *item, count, flags, actor);
if (actorPlayer && fromPos && toPos && !toItem->isRemoved()) {
g_events->eventPlayerOnItemMoved(actorPlayer, toItem, static_cast(count), *toPos,
diff --git a/src/groups.cpp b/src/groups.cpp
index 33a4581..57570b9 100644
--- a/src/groups.cpp
+++ b/src/groups.cpp
@@ -48,7 +48,8 @@ const std::unordered_map ParsePlayerFlagMap = {
{"cannotbemuted", PlayerFlag_CannotBeMuted},
{"isalwayspremium", PlayerFlag_IsAlwaysPremium},
{"ignoreyellcheck", PlayerFlag_IgnoreYellCheck},
- {"ignoresendprivatecheck", PlayerFlag_IgnoreSendPrivateCheck}};
+ {"ignoresendprivatecheck", PlayerFlag_IgnoreSendPrivateCheck},
+ {"canthrowfar", PlayerFlag_CanThrowFar}};
bool Groups::load()
{
diff --git a/src/luascript.cpp b/src/luascript.cpp
index 6477ee8..cc40418 100644
--- a/src/luascript.cpp
+++ b/src/luascript.cpp
@@ -9737,7 +9737,7 @@ int LuaScriptInterface::luaPlayerSetGhostMode(lua_State* L)
Player* tmpPlayer = static_cast(spectator);
if (tmpPlayer != player && !tmpPlayer->isAccessPlayer()) {
if (enabled) {
- tmpPlayer->sendRemoveTileCreature(player, position, tile->getClientIndexOfCreature(tmpPlayer, player));
+ tmpPlayer->sendRemoveTileThing(position, tile->getClientIndexOfCreature(tmpPlayer, player));
} else {
tmpPlayer->sendCreatureAppear(player, position, showEffect);
}
diff --git a/src/map.cpp b/src/map.cpp
index ce540fd..0f12e9d 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -313,11 +313,8 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport /*
for (Creature* spectator : spectators) {
if (Player* tmpPlayer = spectator->getPlayer()) {
// Use the correct stackpos
- int32_t stackpos = oldStackPosVector[i++];
- if (stackpos != -1) {
- tmpPlayer->sendCreatureMove(&creature, newPos, newTile.getClientIndexOfCreature(tmpPlayer, &creature),
- oldPos, stackpos, teleport);
- }
+ tmpPlayer->sendCreatureMove(&creature, newPos, newTile.getClientIndexOfCreature(tmpPlayer, &creature),
+ oldPos, oldStackPosVector[i++], teleport);
}
}
diff --git a/src/player.h b/src/player.h
index 75cad9e..2b39308 100644
--- a/src/player.h
+++ b/src/player.h
@@ -533,12 +533,6 @@ class Player final : public Creature, public Cylinder
creature->getTile()->getClientIndexOfCreature(this, creature), creature);
}
}
- void sendRemoveTileCreature(const Creature* creature, const Position& pos, int32_t stackpos)
- {
- if (client) {
- client->sendRemoveTileCreature(creature, pos, stackpos);
- }
- }
void sendUpdateTile(const Tile* tile, const Position& pos)
{
if (client) {
@@ -624,7 +618,7 @@ class Player final : public Creature, public Cylinder
if (visible) {
client->sendAddCreature(creature, creature->getPosition(), stackpos, false);
} else {
- client->sendRemoveTileCreature(creature, creature->getPosition(), stackpos);
+ client->sendRemoveTileThing(creature->getPosition(), stackpos);
}
}
}
diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp
index c21ff76..1591fef 100644
--- a/src/protocolgame.cpp
+++ b/src/protocolgame.cpp
@@ -764,38 +764,48 @@ void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg)
count = 0;
}
+ const bool isStacked = player->getPosition() == tile->getPosition();
+
const TileItemVector* items = tile->getItemList();
if (items) {
for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
msg.addItem(*it);
- if (++count == 10) {
+ if (++count == 9 && isStacked) {
break;
+ } else if (count == MAX_STACKPOS_THINGS) {
+ return;
}
}
}
const CreatureVector* creatures = tile->getCreatures();
if (creatures) {
+ bool playerAdded = false;
for (auto it = creatures->rbegin(), end = creatures->rend(); it != end; ++it) {
const Creature* creature = (*it);
if (!player->canSeeCreature(creature)) {
continue;
}
- bool known;
- uint32_t removedKnown;
- checkCreatureAsKnown(creature->getID(), known, removedKnown);
+ if (count == 9 && isStacked && !playerAdded) {
+ playerAdded = true;
+ creature = player;
+ }
+
+ auto [known, removedKnown] = isKnownCreature(creature->getID());
AddCreature(msg, creature, known, removedKnown);
- ++count;
+ if (++count == MAX_STACKPOS_THINGS) {
+ return;
+ }
}
}
- if (items && count < 10) {
+ if (items) {
for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) {
msg.addItem(*it);
- if (++count == 10) {
+ if (++count == MAX_STACKPOS_THINGS) {
return;
}
}
@@ -854,38 +864,33 @@ void ProtocolGame::GetFloorDescription(NetworkMessage& msg, int32_t x, int32_t y
}
}
-void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool& known, uint32_t& removedKnown)
+std::pair ProtocolGame::isKnownCreature(uint32_t id)
{
auto result = knownCreatureSet.insert(id);
if (!result.second) {
- known = true;
- return;
+ return std::make_pair(true, 0);
}
- known = false;
-
if (knownCreatureSet.size() > 250) {
// Look for a creature to remove
- for (auto it = knownCreatureSet.begin(), end = knownCreatureSet.end(); it != end; ++it) {
- Creature* creature = g_game.getCreatureByID(*it);
+ for (const uint32_t& creatureId : knownCreatureSet) {
+ Creature* creature = g_game.getCreatureByID(creatureId);
if (!canSee(creature)) {
- removedKnown = *it;
- knownCreatureSet.erase(it);
- return;
+ knownCreatureSet.erase(creatureId);
+ return std::make_pair(false, creatureId);
}
}
// Bad situation. Let's just remove anyone.
- auto it = knownCreatureSet.begin();
- if (*it == id) {
- ++it;
+ auto creatureId = knownCreatureSet.begin();
+ if (*creatureId == id) {
+ ++creatureId;
}
- removedKnown = *it;
- knownCreatureSet.erase(it);
- } else {
- removedKnown = 0;
+ knownCreatureSet.erase(creatureId);
+ return std::make_pair(false, *creatureId);
}
+ return {};
}
bool ProtocolGame::canSee(const Creature* c) const
@@ -1775,7 +1780,11 @@ void ProtocolGame::sendCreatureSay(const Creature* creature, SpeakClasses type,
// Add level only for players
if (const Player* speaker = creature->getPlayer()) {
- msg.add(static_cast(speaker->getLevel()));
+ if (!speaker->isAccessPlayer() && !speaker->isAccountManager()) {
+ msg.add(static_cast(speaker->getLevel()));
+ } else {
+ msg.add(0x00);
+ }
} else {
msg.add(0x00);
}
@@ -1808,7 +1817,11 @@ void ProtocolGame::sendToChannel(const Creature* creature, SpeakClasses type, st
msg.addString(creature->getName());
// Add level only for players
if (const Player* speaker = creature->getPlayer()) {
- msg.add(static_cast(speaker->getLevel()));
+ if (!speaker->isAccessPlayer() && !speaker->isAccountManager()) {
+ msg.add(static_cast(speaker->getLevel()));
+ } else {
+ msg.add(0x00);
+ }
} else {
msg.add(0x00);
}
@@ -1828,7 +1841,11 @@ void ProtocolGame::sendPrivateMessage(const Player* speaker, SpeakClasses type,
msg.add(++statementId);
if (speaker) {
msg.addString(speaker->getName());
- msg.add(static_cast(speaker->getLevel()));
+ if (!speaker->isAccessPlayer() && !speaker->isAccountManager()) {
+ msg.add(static_cast(speaker->getLevel()));
+ } else {
+ msg.add(0x00);
+ }
} else {
msg.add(0x00);
}
@@ -1983,34 +2000,12 @@ void ProtocolGame::sendUpdateTileCreature(const Position& pos, uint32_t stackpos
msg.addPosition(pos);
msg.addByte(static_cast(stackpos));
- bool known;
- uint32_t removedKnown;
- checkCreatureAsKnown(creature->getID(), known, removedKnown);
+ auto [known, removedKnown] = isKnownCreature(creature->getID());
AddCreature(msg, creature, false, removedKnown);
writeToOutputBuffer(msg);
}
-void ProtocolGame::sendRemoveTileCreature(const Creature* creature, const Position& pos, uint32_t stackpos)
-{
- if (stackpos < 10) {
- if (!canSee(pos)) {
- return;
- }
-
- NetworkMessage msg;
- RemoveTileThing(msg, pos, stackpos);
- writeToOutputBuffer(msg);
- return;
- }
-
- NetworkMessage msg;
- msg.addByte(0x6C);
- msg.add(0xFFFF);
- msg.add(creature->getID());
- writeToOutputBuffer(msg);
-}
-
void ProtocolGame::sendUpdateTile(const Tile* tile, const Position& pos)
{
if (!canSee(pos)) {
@@ -2050,27 +2045,13 @@ void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos
}
if (creature != player) {
- // stack pos is always real index now, so it can exceed the limit
- // if stack pos exceeds the limit, we need to refresh the tile instead
- // 1. this is a rare case, and is only triggered by forcing summon in a position
- // 2. since no stackpos will be send to the client about that creature, removing
- // it must be done with its id if its stackpos remains >= 10. this is done to
- // add creatures to battle list instead of rendering on screen
- if (stackpos >= 10) {
- // @todo: should we avoid this check?
- if (const Tile* tile = creature->getTile()) {
- sendUpdateTile(tile, pos);
- }
- } else {
- // if stackpos is -1, the client will automatically detect it
+ if (stackpos != -1 && stackpos < 10) {
NetworkMessage msg;
msg.addByte(0x6A);
msg.addPosition(pos);
msg.addByte(static_cast(stackpos));
- bool known;
- uint32_t removedKnown;
- checkCreatureAsKnown(creature->getID(), known, removedKnown);
+ auto [known, removedKnown] = isKnownCreature(creature->getID());
AddCreature(msg, creature, known, removedKnown);
writeToOutputBuffer(msg);
}
@@ -2130,22 +2111,19 @@ void ProtocolGame::sendMoveCreature(const Creature* creature, const Position& ne
const Position& oldPos, int32_t oldStackPos, bool teleport)
{
if (creature == player) {
- if (teleport) {
- sendRemoveTileCreature(creature, oldPos, oldStackPos);
+ if (oldStackPos >= MAX_STACKPOS_THINGS) {
+ sendMapDescription(newPos);
+ } else if (teleport) {
+ sendRemoveTileThing(oldPos, oldStackPos);
sendMapDescription(newPos);
} else {
NetworkMessage msg;
if (oldPos.z == 7 && newPos.z >= 8) {
- RemoveTileCreature(msg, creature, oldPos, oldStackPos);
+ RemoveTileThing(msg, oldPos, oldStackPos);
} else {
msg.addByte(0x6D);
- if (oldStackPos < 10) {
- msg.addPosition(oldPos);
- msg.addByte(static_cast(oldStackPos));
- } else {
- msg.add(0xFFFF);
- msg.add(creature->getID());
- }
+ msg.addPosition(oldPos);
+ msg.addByte(static_cast(oldStackPos));
msg.addPosition(newPos);
}
@@ -2177,24 +2155,19 @@ void ProtocolGame::sendMoveCreature(const Creature* creature, const Position& ne
writeToOutputBuffer(msg);
}
} else if (canSee(oldPos) && canSee(creature->getPosition())) {
- if (teleport || (oldPos.z == 7 && newPos.z >= 8)) {
- sendRemoveTileCreature(creature, oldPos, oldStackPos);
+ if (teleport || (oldPos.z == 7 && newPos.z >= 8) || oldStackPos >= MAX_STACKPOS_THINGS) {
+ sendRemoveTileThing(oldPos, oldStackPos);
sendAddCreature(creature, newPos, newStackPos, false);
} else {
NetworkMessage msg;
msg.addByte(0x6D);
- if (oldStackPos < 10) {
- msg.addPosition(oldPos);
- msg.addByte(static_cast(oldStackPos));
- } else {
- msg.add(0xFFFF);
- msg.add(creature->getID());
- }
+ msg.addPosition(oldPos);
+ msg.addByte(static_cast(oldStackPos));
msg.addPosition(creature->getPosition());
writeToOutputBuffer(msg);
}
} else if (canSee(oldPos)) {
- sendRemoveTileCreature(creature, oldPos, oldStackPos);
+ sendRemoveTileThing(oldPos, oldStackPos);
} else if (canSee(creature->getPosition())) {
sendAddCreature(creature, newPos, newStackPos, false);
}
@@ -2517,19 +2490,6 @@ void ProtocolGame::RemoveTileThing(NetworkMessage& msg, const Position& pos, uin
msg.addByte(static_cast(stackpos));
}
-void ProtocolGame::RemoveTileCreature(NetworkMessage& msg, const Creature* creature, const Position& pos,
- uint32_t stackpos)
-{
- if (stackpos < 10) {
- RemoveTileThing(msg, pos, stackpos);
- return;
- }
-
- msg.addByte(0x6C);
- msg.add(0xFFFF);
- msg.add(creature->getID());
-}
-
void ProtocolGame::MoveUpCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos,
const Position& oldPos)
{
diff --git a/src/protocolgame.h b/src/protocolgame.h
index 24ce286..65c86ac 100644
--- a/src/protocolgame.h
+++ b/src/protocolgame.h
@@ -70,7 +70,7 @@ class ProtocolGame final : public Protocol
void release() override;
- void checkCreatureAsKnown(uint32_t id, bool& known, uint32_t& removedKnown);
+ std::pair isKnownCreature(uint32_t id);
bool canSee(int32_t x, int32_t y, int32_t z) const;
bool canSee(const Creature*) const;
@@ -198,7 +198,6 @@ class ProtocolGame final : public Protocol
void sendUpdateTileItem(const Position& pos, uint32_t stackpos, const Item* item);
void sendRemoveTileThing(const Position& pos, uint32_t stackpos);
void sendUpdateTileCreature(const Position& pos, uint32_t stackpos, const Creature* creature);
- void sendRemoveTileCreature(const Creature* creature, const Position& pos, uint32_t stackpos);
void sendUpdateTile(const Tile* tile, const Position& pos);
void sendAddCreature(const Creature* creature, const Position& pos, int32_t stackpos, bool isLogin);
@@ -237,8 +236,6 @@ class ProtocolGame final : public Protocol
// tiles
static void RemoveTileThing(NetworkMessage& msg, const Position& pos, uint32_t stackpos);
- static void RemoveTileCreature(NetworkMessage& msg, const Creature* creature, const Position& pos,
- uint32_t stackpos);
void MoveUpCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos);
void MoveDownCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos,
diff --git a/src/tile.cpp b/src/tile.cpp
index 86b23a6..396b72b 100644
--- a/src/tile.cpp
+++ b/src/tile.cpp
@@ -1204,13 +1204,13 @@ int32_t Tile::getStackposOfItem(const Player* player, const Item* item) const
for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
if (*it == item) {
return n;
- } else if (++n == 10) {
+ } else if (++n == MAX_STACKPOS_THINGS) {
return -1;
}
}
} else {
n += items->getTopItemCount();
- if (n >= 10) {
+ if (n >= MAX_STACKPOS_THINGS) {
return -1;
}
}
@@ -1219,7 +1219,7 @@ int32_t Tile::getStackposOfItem(const Player* player, const Item* item) const
if (const CreatureVector* creatures = getCreatures()) {
for (const Creature* creature : *creatures) {
if (player->canSeeCreature(creature)) {
- if (++n >= 10) {
+ if (++n >= MAX_STACKPOS_THINGS) {
return -1;
}
}
@@ -1230,7 +1230,7 @@ int32_t Tile::getStackposOfItem(const Player* player, const Item* item) const
for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) {
if (*it == item) {
return n;
- } else if (++n >= 10) {
+ } else if (++n >= MAX_STACKPOS_THINGS) {
return -1;
}
}
diff --git a/src/tile.h b/src/tile.h
index cc731ce..3673e03 100644
--- a/src/tile.h
+++ b/src/tile.h
@@ -20,6 +20,8 @@ class BedItem;
using CreatureVector = std::vector;
using ItemVector = std::vector- ;
+static constexpr int32_t MAX_STACKPOS_THINGS = 10;
+
enum tileflags_t : uint32_t
{
TILESTATE_NONE = 0,