Ir para conteúdo

GaspaR1

Membro
  • Registro em

  • Última visita

  1. najatheus reagiu a uma resposta no tópico: [OPEN-SOURCE] Base Poke Naja - removido
  2. Huuum, obrigado tem a src, talvez seja bom, você chegou a abrir o game ? Nunca ouvi falar.
  3. samlecter reagiu a uma resposta no tópico: Projeto GTATibia Mundo aberto (Inovador)
  4. Vaga remunerada, quando o projeto estiver online.
  5. Bom primeiramente, quero agradecer a todos que estão lendo essa publicação... e desde já informar que esse é um projeto sério, onde iremos seguir o GTA história por história, em um mundo aberto... para todos os fãs de um Dayz, Samp, então vocês irão gostar do que irei mostrar a vocês. ? Primeiro de tudo, qual a finalidade do projeto ? Fácil, hoje em dia é muito comum ver um Poketibia, um NTO, DBZ E ETC, mas cadê a inovação? pra mim já deu, poketibia existe vários outros grandes, NTO, DBZ, então hoje em dia não compensa mais, a não ser é claro que você faça algo inovador e diferente senão, não irá para frente. então eu já vi alguns OTS, de GTA... mas nenhum que se mantém seguindo a risca o próprio jogo, e então vamos mudar isso, transformando o Tibia em um GTA, uma versão leve para quem não tem um PC totalmente bom, porque sim existe pessoas que ainda não tem condição para um PC Gamer extra luxuoso, então a minha primeira meta é deixar o GTA TIBIA jogável para todos. ? Quais sistemas já temos? ( Alguns irão precisar de modificações claro, para uma melhor jogabilidade ) --> Sistema de armas. --> Sistema de avião. --> Sistema de carros, roubos e etc. --> Sistema de Jetski. --> Sistema de gasolina. --> Sistema de compra do seu terreno, onde você mesmo pode construir sua casa in-game. --> Não irei citar todos, apenas alguns que acho importante. ? Faltando fazer? --> Estou criando o mapa do zero, irei criar o mapa todo de San Andreas. ( já comecei a criação, prints no final do post) --> Criar o sistema quests. --> Criar o sistema de estrelas. --> Criar o sistema de profissão ( Trabalho ). --> e outras coisinhas. ? !! Lembrando !! Já temos launcher, criptografia, layout para o client ( Próprio ), vps linux ( Dedicado ), e site. ? VAGAS ? 2 Vaga para Programador. 2 Vagas para Spriter. RESSALTANDO NOVAMENTE Isso não é para crianças, então se seu intuito não está voltado para essas duas vagas ou ajudar, por favor não venham pedir vaga. Discord AlaOGaspar #7077 Não sou muito fã de escrever e ainda mais de organizar kkk, então irei deixar meu discord aqui para caso vocês queiram saber + do servidor.
  6. Salve galera, bom estou tento um problema... recentemente adicionei o campo de visão extendido no OTC do mehah, mas estou com um problema. Sempre que deslogo e logo dnv, o nome do jogador fica no lugar, alguém poderia me ajudar ?
  7. eu instalei o seu, mas vou verificar e com o visual ? vs2019 n funfa e como eu faço pra zerar tudo do dev ? EDIT: consegui compilar, mas agora não loga wtf
  8. mano, estou tento problema na hora de compilar no dev da o seguinte erro , tem alguma ideia do que seja ? ou poderia me mandar o seu dev ? tbm tentei compilar com o visual 19, mas tbm da erro falta o otpch.cpp.
  9. Salve galera beleza ? antes de tudo não sei se é a área correta, se não estiver na área correta peço que alguém mova porfavor... bom recentemente segui um tutorial para aumentar o campo de visão do personagem no meu OT, e funcionou só que fica com umas bordinhas ainda, alguém saberia arrumar ? ou me ajudar... map.cpp otclient /* * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "map.h" #include "game.h" #include "localplayer.h" #include "tile.h" #include "item.h" #include "missile.h" #include "statictext.h" #include "mapview.h" #include "minimap.h" #include <framework/core/eventdispatcher.h> #include <framework/core/application.h> Map g_map; TilePtr Map::m_nulltile; void Map::init() { resetAwareRange(); m_animationFlags |= Animation_Show; } void Map::terminate() { clean(); } void Map::addMapView(const MapViewPtr& mapView) { m_mapViews.push_back(mapView); } void Map::removeMapView(const MapViewPtr& mapView) { auto it = std::find(m_mapViews.begin(), m_mapViews.end(), mapView); if(it != m_mapViews.end()) m_mapViews.erase(it); } void Map::notificateTileUpdate(const Position& pos) { if(!pos.isMapPosition()) return; for(const MapViewPtr& mapView : m_mapViews) mapView->onTileUpdate(pos); g_minimap.updateTile(pos, getTile(pos)); } void Map::clean() { cleanDynamicThings(); for(int i=0;i<=Otc::MAX_Z;++i) m_tileBlocks[i].clear(); m_waypoints.clear(); g_towns.clear(); g_houses.clear(); g_creatures.clearSpawns(); m_tilesRect = Rect(65534, 65534, 0, 0); } void Map::cleanDynamicThings() { for(const auto& pair : m_knownCreatures) { const CreaturePtr& creature = pair.second; removeThing(creature); } m_knownCreatures.clear(); for(int i=0;i<=Otc::MAX_Z;++i) m_floorMissiles[i].clear(); cleanTexts(); } void Map::cleanTexts() { m_animatedTexts.clear(); m_staticTexts.clear(); } void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos) { if(!thing) return; if(thing->isItem() || thing->isCreature() || thing->isEffect()) { const TilePtr& tile = getOrCreateTile(pos); if(tile) tile->addThing(thing, stackPos); } else { if(thing->isMissile()) { m_floorMissiles[pos.z].push_back(thing->static_self_cast<Missile>()); } else if(thing->isAnimatedText()) { // this code will stack animated texts of the same color AnimatedTextPtr animatedText = thing->static_self_cast<AnimatedText>(); AnimatedTextPtr prevAnimatedText; bool merged = false; for(auto other : m_animatedTexts) { if(other->getPosition() == pos) { prevAnimatedText = other; if(other->merge(animatedText)) { merged = true; break; } } } if(!merged) { if(prevAnimatedText) { Point offset = prevAnimatedText->getOffset(); float t = prevAnimatedText->getTimer().ticksElapsed(); if(t < Otc::ANIMATED_TEXT_DURATION / 4.0) { // didnt move 12 pixels int y = 12 - 48 * t / (float)Otc::ANIMATED_TEXT_DURATION; offset += Point(0, y); } offset.y = std::min<int>(offset.y, 12); animatedText->setOffset(offset); } m_animatedTexts.push_back(animatedText); } } else if(thing->isStaticText()) { StaticTextPtr staticText = thing->static_self_cast<StaticText>(); bool mustAdd = true; for(auto other : m_staticTexts) { // try to combine messages if(other->getPosition() == pos && other->addMessage(staticText->getName(), staticText->getMessageMode(), staticText->getFirstMessage())) { mustAdd = false; break; } } if(mustAdd) m_staticTexts.push_back(staticText); else return; } thing->setPosition(pos); thing->onAppear(); } notificateTileUpdate(pos); } ThingPtr Map::getThing(const Position& pos, int stackPos) { if(TilePtr tile = getTile(pos)) return tile->getThing(stackPos); return nullptr; } bool Map::removeThing(const ThingPtr& thing) { if(!thing) return false; bool ret = false; if(thing->isMissile()) { MissilePtr missile = thing->static_self_cast<Missile>(); int z = missile->getPosition().z; auto it = std::find(m_floorMissiles[z].begin(), m_floorMissiles[z].end(), missile); if(it != m_floorMissiles[z].end()) { m_floorMissiles[z].erase(it); ret = true; } } else if(thing->isAnimatedText()) { AnimatedTextPtr animatedText = thing->static_self_cast<AnimatedText>(); auto it = std::find(m_animatedTexts.begin(), m_animatedTexts.end(), animatedText); if(it != m_animatedTexts.end()) { m_animatedTexts.erase(it); ret = true; } } else if(thing->isStaticText()) { StaticTextPtr staticText = thing->static_self_cast<StaticText>(); auto it = std::find(m_staticTexts.begin(), m_staticTexts.end(), staticText); if(it != m_staticTexts.end()) { m_staticTexts.erase(it); ret = true; } } else if(const TilePtr& tile = thing->getTile()) ret = tile->removeThing(thing); notificateTileUpdate(thing->getPosition()); return ret; } bool Map::removeThingByPos(const Position& pos, int stackPos) { if(TilePtr tile = getTile(pos)) return removeThing(tile->getThing(stackPos)); return false; } void Map::colorizeThing(const ThingPtr& thing, const Color& color) { if(!thing) return; if(thing->isItem()) thing->static_self_cast<Item>()->setColor(color); else if(thing->isCreature()) { const TilePtr& tile = thing->getTile(); assert(tile); const ThingPtr& topThing = tile->getTopThing(); assert(topThing); topThing->static_self_cast<Item>()->setColor(color); } } void Map::removeThingColor(const ThingPtr& thing) { if(!thing) return; if(thing->isItem()) thing->static_self_cast<Item>()->setColor(Color::alpha); else if(thing->isCreature()) { const TilePtr& tile = thing->getTile(); assert(tile); const ThingPtr& topThing = tile->getTopThing(); assert(topThing); topThing->static_self_cast<Item>()->setColor(Color::alpha); } } StaticTextPtr Map::getStaticText(const Position& pos) { for(auto staticText : m_staticTexts) { // try to combine messages if(staticText->getPosition() == pos) return staticText; } return nullptr; } const TilePtr& Map::createTile(const Position& pos) { if(!pos.isMapPosition()) return m_nulltile; if(pos.x < m_tilesRect.left()) m_tilesRect.setLeft(pos.x); if(pos.y < m_tilesRect.top()) m_tilesRect.setTop(pos.y); if(pos.x > m_tilesRect.right()) m_tilesRect.setRight(pos.x); if(pos.y > m_tilesRect.bottom()) m_tilesRect.setBottom(pos.y); TileBlock& block = m_tileBlocks[pos.z][getBlockIndex(pos)]; return block.create(pos); } template <typename... Items> const TilePtr& Map::createTileEx(const Position& pos, const Items&... items) { if(!pos.isValid()) return m_nulltile; const TilePtr& tile = getOrCreateTile(pos); auto vec = {items...}; for(auto it : vec) addThing(it, pos); return tile; } const TilePtr& Map::getOrCreateTile(const Position& pos) { if(!pos.isMapPosition()) return m_nulltile; if(pos.x < m_tilesRect.left()) m_tilesRect.setLeft(pos.x); if(pos.y < m_tilesRect.top()) m_tilesRect.setTop(pos.y); if(pos.x > m_tilesRect.right()) m_tilesRect.setRight(pos.x); if(pos.y > m_tilesRect.bottom()) m_tilesRect.setBottom(pos.y); TileBlock& block = m_tileBlocks[pos.z][getBlockIndex(pos)]; return block.getOrCreate(pos); } const TilePtr& Map::getTile(const Position& pos) { if(!pos.isMapPosition()) return m_nulltile; auto it = m_tileBlocks[pos.z].find(getBlockIndex(pos)); if(it != m_tileBlocks[pos.z].end()) return it->second.get(pos); return m_nulltile; } const TileList Map::getTiles(int floor/* = -1*/) { TileList tiles; if(floor > Otc::MAX_Z) { return tiles; } else if(floor < 0) { // Search all floors for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) { for(const auto& pair : m_tileBlocks[z]) { const TileBlock& block = pair.second; for(const TilePtr& tile : block.getTiles()) { if(tile != nullptr) tiles.push_back(tile); } } } } else { for(const auto& pair : m_tileBlocks[floor]) { const TileBlock& block = pair.second; for(const TilePtr& tile : block.getTiles()) { if(tile != nullptr) tiles.push_back(tile); } } } return tiles; } void Map::cleanTile(const Position& pos) { if(!pos.isMapPosition()) return; auto it = m_tileBlocks[pos.z].find(getBlockIndex(pos)); if(it != m_tileBlocks[pos.z].end()) { TileBlock& block = it->second; if(const TilePtr& tile = block.get(pos)) { tile->clean(); if(tile->canErase()) block.remove(pos); notificateTileUpdate(pos); } } for(auto it = m_staticTexts.begin();it != m_staticTexts.end();) { const StaticTextPtr& staticText = *it; if(staticText->getPosition() == pos && staticText->getMessageMode() == Otc::MessageNone) it = m_staticTexts.erase(it); else ++it; } } void Map::setShowZone(tileflags_t zone, bool show) { if(show) m_zoneFlags |= (uint32)zone; else m_zoneFlags &= ~(uint32)zone; } void Map::setShowZones(bool show) { if(!show) m_zoneFlags = 0; else if(m_zoneFlags == 0) m_zoneFlags = TILESTATE_HOUSE | TILESTATE_PROTECTIONZONE; } void Map::setZoneColor(tileflags_t zone, const Color& color) { if((m_zoneFlags & zone) == zone) m_zoneColors[zone] = color; } Color Map::getZoneColor(tileflags_t flag) { auto it = m_zoneColors.find(flag); if(it == m_zoneColors.end()) return Color::alpha; return it->second; } void Map::setForceShowAnimations(bool force) { if(force) { if(!(m_animationFlags & Animation_Force)) m_animationFlags |= Animation_Force; } else m_animationFlags &= ~Animation_Force; } bool Map::isForcingAnimations() { return (m_animationFlags & Animation_Force) == Animation_Force; } bool Map::isShowingAnimations() { return (m_animationFlags & Animation_Show) == Animation_Show; } void Map::setShowAnimations(bool show) { if(show) { if(!(m_animationFlags & Animation_Show)) m_animationFlags |= Animation_Show; } else m_animationFlags &= ~Animation_Show; } void Map::beginGhostMode(float opacity) { g_painter->setOpacity(opacity); } void Map::endGhostMode() { g_painter->resetOpacity(); } std::map<Position, ItemPtr> Map::findItemsById(uint16 clientId, uint32 max) { std::map<Position, ItemPtr> ret; uint32 count = 0; for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) { for(const auto& pair : m_tileBlocks[z]) { const TileBlock& block = pair.second; for(const TilePtr& tile : block.getTiles()) { if(unlikely(!tile || tile->isEmpty())) continue; for(const ItemPtr& item : tile->getItems()) { if(item->getId() == clientId) { ret.insert(std::make_pair(tile->getPosition(), item)); if(++count >= max) break; } } } } } return ret; } void Map::addCreature(const CreaturePtr& creature) { m_knownCreatures[creature->getId()] = creature; } CreaturePtr Map::getCreatureById(uint32 id) { auto it = m_knownCreatures.find(id); if(it == m_knownCreatures.end()) return nullptr; return it->second; } void Map::removeCreatureById(uint32 id) { if(id == 0) return; auto it = m_knownCreatures.find(id); if(it != m_knownCreatures.end()) m_knownCreatures.erase(it); } void Map::removeUnawareThings() { // remove creatures from tiles that we are not aware of anymore for(const auto& pair : m_knownCreatures) { const CreaturePtr& creature = pair.second; if(!isAwareOfPosition(creature->getPosition())) removeThing(creature); } // remove static texts from tiles that we are not aware anymore for(auto it = m_staticTexts.begin(); it != m_staticTexts.end();) { const StaticTextPtr& staticText = *it; if(staticText->getMessageMode() == Otc::MessageNone && !isAwareOfPosition(staticText->getPosition())) it = m_staticTexts.erase(it); else ++it; } if(!g_game.getFeature(Otc::GameKeepUnawareTiles)) { // remove tiles that we are not aware anymore for(int z = 0; z <= Otc::MAX_Z; ++z) { std::unordered_map<uint, TileBlock>& tileBlocks = m_tileBlocks[z]; for(auto it = tileBlocks.begin(); it != tileBlocks.end();) { TileBlock& block = (*it).second; bool blockEmpty = true; for(const TilePtr& tile : block.getTiles()) { if(!tile) continue; const Position& pos = tile->getPosition(); if(!isAwareOfPosition(pos)) block.remove(pos); else blockEmpty = false; } if(blockEmpty) it = tileBlocks.erase(it); else ++it; } } } } void Map::setCentralPosition(const Position& centralPosition) { if(m_centralPosition == centralPosition) return; m_centralPosition = centralPosition; removeUnawareThings(); // this fixes local player position when the local player is removed from the map, // the local player is removed from the map when there are too many creatures on his tile, // so there is no enough stackpos to the server send him g_dispatcher.addEvent([this] { LocalPlayerPtr localPlayer = g_game.getLocalPlayer(); if(!localPlayer || localPlayer->getPosition() == m_centralPosition) return; TilePtr tile = localPlayer->getTile(); if(tile && tile->hasThing(localPlayer)) return; Position oldPos = localPlayer->getPosition(); Position pos = m_centralPosition; if(oldPos != pos) { if(!localPlayer->isRemoved()) localPlayer->onDisappear(); localPlayer->setPosition(pos); localPlayer->onAppear(); g_logger.debug("forced player position update"); } }); for(const MapViewPtr& mapView : m_mapViews) mapView->onMapCenterChange(centralPosition); } std::vector<CreaturePtr> Map::getSightSpectators(const Position& centerPos, bool multiFloor) { return getSpectatorsInRangeEx(centerPos, multiFloor, m_awareRange.left - 1, m_awareRange.right - 2, m_awareRange.top - 1, m_awareRange.bottom - 2); } std::vector<CreaturePtr> Map::getSpectators(const Position& centerPos, bool multiFloor) { return getSpectatorsInRangeEx(centerPos, multiFloor, m_awareRange.left, m_awareRange.right, m_awareRange.top, m_awareRange.bottom); } std::vector<CreaturePtr> Map::getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange) { return getSpectatorsInRangeEx(centerPos, multiFloor, xRange, xRange, yRange, yRange); } std::vector<CreaturePtr> Map::getSpectatorsInRangeEx(const Position& centerPos, bool multiFloor, int minXRange, int maxXRange, int minYRange, int maxYRange) { int minZRange = 0; int maxZRange = 0; std::vector<CreaturePtr> creatures; if(multiFloor) { minZRange = 0; maxZRange = Otc::MAX_Z; } //TODO: optimize //TODO: get creatures from other floors corretly //TODO: delivery creatures in distance order for(int iz=-minZRange; iz<=maxZRange; ++iz) { for(int iy=-minYRange; iy<=maxYRange; ++iy) { for(int ix=-minXRange; ix<=maxXRange; ++ix) { TilePtr tile = getTile(centerPos.translated(ix,iy,iz)); if(!tile) continue; auto tileCreatures = tile->getCreatures(); creatures.insert(creatures.end(), tileCreatures.rbegin(), tileCreatures.rend()); } } } return creatures; } bool Map::isLookPossible(const Position& pos) { TilePtr tile = getTile(pos); return tile && tile->isLookPossible(); } bool Map::isCovered(const Position& pos, int firstFloor) { // check for tiles on top of the postion Position tilePos = pos; while(tilePos.coveredUp() && tilePos.z >= firstFloor) { TilePtr tile = getTile(tilePos); // the below tile is covered when the above tile has a full ground if(tile && tile->isFullGround()) return true; } return false; } bool Map::isCompletelyCovered(const Position& pos, int firstFloor) { const TilePtr& checkTile = getTile(pos); Position tilePos = pos; while(tilePos.coveredUp() && tilePos.z >= firstFloor) { bool covered = true; bool done = false; // check in 2x2 range tiles that has no transparent pixels for(int x=0;x<2 && !done;++x) { for(int y=0;y<2 && !done;++y) { const TilePtr& tile = getTile(tilePos.translated(-x, -y)); if(!tile || !tile->isFullyOpaque()) { covered = false; done = true; } else if(x==0 && y==0 && (!checkTile || checkTile->isSingleDimension())) { done = true; } } } if(covered) return true; } return false; } bool Map::isAwareOfPosition(const Position& pos) { if(pos.z < getFirstAwareFloor() || pos.z > getLastAwareFloor()) return false; Position groundedPos = pos; while(groundedPos.z != m_centralPosition.z) { if(groundedPos.z > m_centralPosition.z) { if(groundedPos.x == 65535 || groundedPos.y == 65535) // When pos == 65535,65535,15 we cant go up to 65536,65536,14 break; groundedPos.coveredUp(); } else { if(groundedPos.x == 0 || groundedPos.y == 0) // When pos == 0,0,0 we cant go down to -1,-1,1 break; groundedPos.coveredDown(); } } return m_centralPosition.isInRange(groundedPos, m_awareRange.left, m_awareRange.right, m_awareRange.top, m_awareRange.bottom); } void Map::setAwareRange(const AwareRange& range) { m_awareRange = range; removeUnawareThings(); } void Map::resetAwareRange() { AwareRange range; range.left = 19; //Change this to = maxClientViewportX range.top = 19; //Change this to = maxClientViewportY range.bottom = range.top+1; range.right = range.left+1; setAwareRange(range); } int Map::getFirstAwareFloor() { if(m_centralPosition.z > Otc::SEA_FLOOR) return m_centralPosition.z-Otc::AWARE_UNDEGROUND_FLOOR_RANGE; else return 0; } int Map::getLastAwareFloor() { if(m_centralPosition.z > Otc::SEA_FLOOR) return std::min<int>(m_centralPosition.z+Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z); else return Otc::SEA_FLOOR; } std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const Position& startPos, const Position& goalPos, int maxComplexity, int flags) { // pathfinding using A* search algorithm // as described in http://en.wikipedia.org/wiki/A*_search_algorithm struct Node { Node(const Position& pos) : cost(0), totalCost(0), pos(pos), prev(nullptr), dir(Otc::InvalidDirection) { } float cost; float totalCost; Position pos; Node *prev; Otc::Direction dir; }; struct LessNode : std::binary_function<std::pair<Node*, float>, std::pair<Node*, float>, bool> { bool operator()(std::pair<Node*, float> a, std::pair<Node*, float> b) const { return b.second < a.second; } }; std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> ret; std::vector<Otc::Direction>& dirs = std::get<0>(ret); Otc::PathFindResult& result = std::get<1>(ret); result = Otc::PathFindResultNoWay; if(startPos == goalPos) { result = Otc::PathFindResultSamePosition; return ret; } if(startPos.z != goalPos.z) { result = Otc::PathFindResultImpossible; return ret; } // check the goal pos is walkable if(g_map.isAwareOfPosition(goalPos)) { const TilePtr goalTile = getTile(goalPos); if(!goalTile || !goalTile->isWalkable()) { return ret; } } else { const MinimapTile& goalTile = g_minimap.getTile(goalPos); if(goalTile.hasFlag(MinimapTileNotWalkable)) { return ret; } } std::unordered_map<Position, Node*, PositionHasher> nodes; std::priority_queue<std::pair<Node*, float>, std::vector<std::pair<Node*, float>>, LessNode> searchList; Node *currentNode = new Node(startPos); currentNode->pos = startPos; nodes[startPos] = currentNode; Node *foundNode = nullptr; while(currentNode) { if((int)nodes.size() > maxComplexity) { result = Otc::PathFindResultTooFar; break; } // path found if(currentNode->pos == goalPos && (!foundNode || currentNode->cost < foundNode->cost)) foundNode = currentNode; // cost too high if(foundNode && currentNode->totalCost >= foundNode->cost) break; for(int i=-1;i<=1;++i) { for(int j=-1;j<=1;++j) { if(i == 0 && j == 0) continue; bool wasSeen = false; bool hasCreature = false; bool isNotWalkable = true; bool isNotPathable = true; int speed = 100; Position neighborPos = currentNode->pos.translated(i, j); if(g_map.isAwareOfPosition(neighborPos)) { wasSeen = true; if(const TilePtr& tile = getTile(neighborPos)) { hasCreature = tile->hasCreature(); isNotWalkable = !tile->isWalkable(); isNotPathable = !tile->isPathable(); speed = tile->getGroundSpeed(); } } else { const MinimapTile& mtile = g_minimap.getTile(neighborPos); wasSeen = mtile.hasFlag(MinimapTileWasSeen); isNotWalkable = mtile.hasFlag(MinimapTileNotWalkable); isNotPathable = mtile.hasFlag(MinimapTileNotPathable); if(isNotWalkable || isNotPathable) wasSeen = true; speed = mtile.getSpeed(); } float walkFactor = 0; if(neighborPos != goalPos) { if(!(flags & Otc::PathFindAllowNotSeenTiles) && !wasSeen) continue; if(wasSeen) { if(!(flags & Otc::PathFindAllowCreatures) && hasCreature) continue; if(!(flags & Otc::PathFindAllowNonPathable) && isNotPathable) continue; if(!(flags & Otc::PathFindAllowNonWalkable) && isNotWalkable) continue; } } else { if(!(flags & Otc::PathFindAllowNotSeenTiles) && !wasSeen) continue; if(wasSeen) { if(!(flags & Otc::PathFindAllowNonWalkable) && isNotWalkable) continue; } } Otc::Direction walkDir = currentNode->pos.getDirectionFromPosition(neighborPos); if(walkDir >= Otc::NorthEast) walkFactor += 3.0f; else walkFactor += 1.0f; float cost = currentNode->cost + (speed * walkFactor) / 100.0f; Node *neighborNode; if(nodes.find(neighborPos) == nodes.end()) { neighborNode = new Node(neighborPos); nodes[neighborPos] = neighborNode; } else { neighborNode = nodes[neighborPos]; if(neighborNode->cost <= cost) continue; } neighborNode->prev = currentNode; neighborNode->cost = cost; neighborNode->totalCost = neighborNode->cost + neighborPos.distance(goalPos); neighborNode->dir = walkDir; searchList.push(std::make_pair(neighborNode, neighborNode->totalCost)); } } if(!searchList.empty()) { currentNode = searchList.top().first; searchList.pop(); } else currentNode = nullptr; } if(foundNode) { currentNode = foundNode; while(currentNode) { dirs.push_back(currentNode->dir); currentNode = currentNode->prev; } dirs.pop_back(); std::reverse(dirs.begin(), dirs.end()); result = Otc::PathFindResultOk; } for(auto it : nodes) delete it.second; return ret; } protocol.cpp /** * The Forgotten Server - a free and open-source MMORPG server emulator * Copyright (C) 2019 Mark Samman <[email protected]> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "otpch.h" #include <boost/range/adaptor/reversed.hpp> #include "protocolgame.h" #include "outputmessage.h" #include "player.h" #include "configmanager.h" #include "actions.h" #include "game.h" #include "iologindata.h" #include "iomarket.h" #include "waitlist.h" #include "ban.h" #include "scheduler.h" extern ConfigManager g_config; extern Actions actions; extern CreatureEvents* g_creatureEvents; extern Chat* g_chat; void ProtocolGame::release() { //dispatcher thread if (player && player->client == shared_from_this()) { player->client.reset(); player->decrementReferenceCounter(); player = nullptr; } OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this()); Protocol::release(); } void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem) { //dispatcher thread Player* foundPlayer = g_game.getPlayerByName(name); if (!foundPlayer || g_config.getBoolean(ConfigManager::ALLOW_CLONES)) { player = new Player(getThis()); player->setName(name); player->incrementReferenceCounter(); player->setID(); if (!IOLoginData::preloadPlayer(player, name)) { disconnectClient("Your character could not be loaded."); return; } if (IOBan::isPlayerNamelocked(player->getGUID())) { disconnectClient("Your character has been namelocked."); return; } if (g_game.getGameState() == GAME_STATE_CLOSING && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) { disconnectClient("The game is just going down.\nPlease try again later."); return; } if (g_game.getGameState() == GAME_STATE_CLOSED && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) { disconnectClient("Server is currently closed.\nPlease try again later."); return; } if (g_config.getBoolean(ConfigManager::ONE_PLAYER_ON_ACCOUNT) && player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER && g_game.getPlayerByAccount(player->getAccount())) { disconnectClient("You may only login with one character\nof your account at the same time."); return; } if (!player->hasFlag(PlayerFlag_CannotBeBanned)) { BanInfo banInfo; if (IOBan::isAccountBanned(accountId, banInfo)) { if (banInfo.reason.empty()) { banInfo.reason = "(none)"; } std::ostringstream ss; if (banInfo.expiresAt > 0) { ss << "Your account has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason; } else { ss << "Your account has been permanently banned by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason; } disconnectClient(ss.str()); return; } } WaitingList& waitingList = WaitingList::getInstance(); if (!waitingList.clientLogin(player)) { uint32_t currentSlot = waitingList.getClientSlot(player); uint32_t retryTime = WaitingList::getTime(currentSlot); std::ostringstream ss; ss << "Too many players online.\nYou are at place " << currentSlot << " on the waiting list."; auto output = OutputMessagePool::getOutputMessage(); output->addByte(0x16); output->addString(ss.str()); output->addByte(retryTime); send(output); disconnect(); return; } if (!IOLoginData::loadPlayerById(player, player->getGUID())) { disconnectClient("Your character could not be loaded."); return; } player->setOperatingSystem(operatingSystem); if (!g_game.placeCreature(player, player->getLoginPosition())) { if (!g_game.placeCreature(player, player->getTemplePosition(), false, true)) { disconnectClient("Temple position is wrong. Contact the administrator."); return; } } if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) { player->registerCreatureEvent("ExtendedOpcode"); } player->lastIP = player->getIP(); player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1); acceptPackets = true; } else { if (eventConnect != 0 || !g_config.getBoolean(ConfigManager::REPLACE_KICK_ON_LOGIN)) { //Already trying to connect disconnectClient("You are already logged in."); return; } if (foundPlayer->client) { foundPlayer->disconnect(); foundPlayer->isConnecting = true; eventConnect = g_scheduler.addEvent(createSchedulerTask(1000, std::bind(&ProtocolGame::connect, getThis(), foundPlayer->getID(), operatingSystem))); } else { connect(foundPlayer->getID(), operatingSystem); } } OutputMessagePool::getInstance().addProtocolToAutosend(shared_from_this()); } void ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem) { eventConnect = 0; Player* foundPlayer = g_game.getPlayerByID(playerId); if (!foundPlayer || foundPlayer->client) { disconnectClient("You are already logged in."); return; } if (isConnectionExpired()) { //ProtocolGame::release() has been called at this point and the Connection object //no longer exists, so we return to prevent leakage of the Player. return; } player = foundPlayer; player->incrementReferenceCounter(); g_chat->removeUserFromAllChannels(*player); player->clearModalWindows(); player->setOperatingSystem(operatingSystem); player->isConnecting = false; player->client = getThis(); sendAddCreature(player, player->getPosition(), 0, false); player->lastIP = player->getIP(); player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1); acceptPackets = true; } void ProtocolGame::logout(bool displayEffect, bool forced) { //dispatcher thread if (!player) { return; } if (!player->isRemoved()) { if (!forced) { if (!player->isAccessPlayer()) { if (player->getTile()->hasFlag(TILESTATE_NOLOGOUT)) { player->sendCancelMessage(RETURNVALUE_YOUCANNOTLOGOUTHERE); return; } if (!player->getTile()->hasFlag(TILESTATE_PROTECTIONZONE) && player->hasCondition(CONDITION_INFIGHT)) { player->sendCancelMessage(RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT); return; } } //scripting event - onLogout if (!g_creatureEvents->playerLogout(player)) { //Let the script handle the error message return; } } if (displayEffect && player->getHealth() > 0) { g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); } } disconnect(); g_game.removeCreature(player); } void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) { if (g_game.getGameState() == GAME_STATE_SHUTDOWN) { disconnect(); return; } OperatingSystem_t operatingSystem = static_cast<OperatingSystem_t>(msg.get<uint16_t>()); version = msg.get<uint16_t>(); msg.skipBytes(7); // U32 client version, U8 client type, U16 dat revision if (!Protocol::RSA_decrypt(msg)) { disconnect(); return; } xtea::key key; key[0] = msg.get<uint32_t>(); key[1] = msg.get<uint32_t>(); key[2] = msg.get<uint32_t>(); key[3] = msg.get<uint32_t>(); enableXTEAEncryption(); setXTEAKey(std::move(key)); if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) { NetworkMessage opcodeMessage; opcodeMessage.addByte(0x32); opcodeMessage.addByte(0x00); opcodeMessage.add<uint16_t>(0x00); writeToOutputBuffer(opcodeMessage); } msg.skipBytes(1); // gamemaster flag std::string sessionKey = msg.getString(); auto sessionArgs = explodeString(sessionKey, "\n", 4); if (sessionArgs.size() != 4) { disconnect(); return; } std::string& accountName = sessionArgs[0]; std::string& password = sessionArgs[1]; std::string& token = sessionArgs[2]; uint32_t tokenTime = 0; try { tokenTime = std::stoul(sessionArgs[3]); } catch (const std::invalid_argument&) { disconnectClient("Malformed token packet."); return; } catch (const std::out_of_range&) { disconnectClient("Token time is too long."); return; } if (accountName.empty()) { disconnectClient("You must enter your account name."); return; } std::string characterName = msg.getString(); uint32_t timeStamp = msg.get<uint32_t>(); uint8_t randNumber = msg.getByte(); if (challengeTimestamp != timeStamp || challengeRandom != randNumber) { disconnect(); return; } if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { std::ostringstream ss; ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!"; disconnectClient(ss.str()); return; } if (g_game.getGameState() == GAME_STATE_STARTUP) { disconnectClient("Gameworld is starting up. Please wait."); return; } if (g_game.getGameState() == GAME_STATE_MAINTAIN) { disconnectClient("Gameworld is under maintenance. Please re-connect in a while."); return; } BanInfo banInfo; if (IOBan::isIpBanned(getIP(), banInfo)) { if (banInfo.reason.empty()) { banInfo.reason = "(none)"; } std::ostringstream ss; ss << "Your IP has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason; disconnectClient(ss.str()); return; } uint32_t accountId = IOLoginData::gameworldAuthentication(accountName, password, characterName, token, tokenTime); if (accountId == 0) { disconnectClient("Account name or password is not correct."); return; } g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem))); } void ProtocolGame::onConnect() { auto output = OutputMessagePool::getOutputMessage(); static std::random_device rd; static std::ranlux24 generator(rd()); static std::uniform_int_distribution<uint16_t> randNumber(0x00, 0xFF); // Skip checksum output->skipBytes(sizeof(uint32_t)); // Packet length & type output->add<uint16_t>(0x0006); output->addByte(0x1F); // Add timestamp & random number challengeTimestamp = static_cast<uint32_t>(time(nullptr)); output->add<uint32_t>(challengeTimestamp); challengeRandom = randNumber(generator); output->addByte(challengeRandom); // Go back and write checksum output->skipBytes(-12); output->add<uint32_t>(adlerChecksum(output->getOutputBuffer() + sizeof(uint32_t), 8)); send(output); } void ProtocolGame::disconnectClient(const std::string& message) const { auto output = OutputMessagePool::getOutputMessage(); output->addByte(0x14); output->addString(message); send(output); disconnect(); } void ProtocolGame::writeToOutputBuffer(const NetworkMessage& msg) { auto out = getOutputBuffer(msg.getLength()); out->append(msg); } void ProtocolGame::parsePacket(NetworkMessage& msg) { if (!acceptPackets || g_game.getGameState() == GAME_STATE_SHUTDOWN || msg.getLength() <= 0) { return; } uint8_t recvbyte = msg.getByte(); if (!player) { if (recvbyte == 0x0F) { disconnect(); } return; } //a dead player can not performs actions if (player->isRemoved() || player->getHealth() <= 0) { if (recvbyte == 0x0F) { disconnect(); return; } if (recvbyte != 0x14) { return; } } switch (recvbyte) { case 0x14: g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::logout, getThis(), true, false))); break; case 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); break; case 0x1E: addGameTask(&Game::playerReceivePing, player->getID()); break; case 0x32: parseExtendedOpcode(msg); break; //otclient extended opcode case 0x64: parseAutoWalk(msg); break; case 0x65: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTH); break; case 0x66: addGameTask(&Game::playerMove, player->getID(), DIRECTION_EAST); break; case 0x67: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTH); break; case 0x68: addGameTask(&Game::playerMove, player->getID(), DIRECTION_WEST); break; case 0x69: addGameTask(&Game::playerStopAutoWalk, player->getID()); break; case 0x6A: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHEAST); break; case 0x6B: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHEAST); break; case 0x6C: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHWEST); break; case 0x6D: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHWEST); break; case 0x6F: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_NORTH); break; case 0x70: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_EAST); break; case 0x71: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_SOUTH); break; case 0x72: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_WEST); break; case 0x77: parseEquipObject(msg); break; case 0x78: parseThrow(msg); break; case 0x79: parseLookInShop(msg); break; case 0x7A: parsePlayerPurchase(msg); break; case 0x7B: parsePlayerSale(msg); break; case 0x7C: addGameTask(&Game::playerCloseShop, player->getID()); break; case 0x7D: parseRequestTrade(msg); break; case 0x7E: parseLookInTrade(msg); break; case 0x7F: addGameTask(&Game::playerAcceptTrade, player->getID()); break; case 0x80: addGameTask(&Game::playerCloseTrade, player->getID()); break; case 0x82: parseUseItem(msg); break; case 0x83: parseUseItemEx(msg); break; case 0x84: parseUseWithCreature(msg); break; case 0x85: parseRotateItem(msg); break; case 0x87: parseCloseContainer(msg); break; case 0x88: parseUpArrowContainer(msg); break; case 0x89: parseTextWindow(msg); break; case 0x8A: parseHouseWindow(msg); break; case 0x8C: parseLookAt(msg); break; case 0x8D: parseLookInBattleList(msg); break; case 0x8E: /* join aggression */ break; case 0x96: parseSay(msg); break; case 0x97: addGameTask(&Game::playerRequestChannels, player->getID()); break; case 0x98: parseOpenChannel(msg); break; case 0x99: parseCloseChannel(msg); break; case 0x9A: parseOpenPrivateChannel(msg); break; case 0x9E: addGameTask(&Game::playerCloseNpcChannel, player->getID()); break; case 0xA0: parseFightModes(msg); break; case 0xA1: parseAttack(msg); break; case 0xA2: parseFollow(msg); break; case 0xA3: parseInviteToParty(msg); break; case 0xA4: parseJoinParty(msg); break; case 0xA5: parseRevokePartyInvite(msg); break; case 0xA6: parsePassPartyLeadership(msg); break; case 0xA7: addGameTask(&Game::playerLeaveParty, player->getID()); break; case 0xA8: parseEnableSharedPartyExperience(msg); break; case 0xAA: addGameTask(&Game::playerCreatePrivateChannel, player->getID()); break; case 0xAB: parseChannelInvite(msg); break; case 0xAC: parseChannelExclude(msg); break; case 0xBE: addGameTask(&Game::playerCancelAttackAndFollow, player->getID()); break; case 0xC9: /* update tile */ break; case 0xCA: parseUpdateContainer(msg); break; case 0xCB: parseBrowseField(msg); break; case 0xCC: parseSeekInContainer(msg); break; case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break; case 0xD3: parseSetOutfit(msg); break; case 0xD4: parseToggleMount(msg); break; case 0xDC: parseAddVip(msg); break; case 0xDD: parseRemoveVip(msg); break; case 0xDE: parseEditVip(msg); break; case 0xE6: parseBugReport(msg); break; case 0xE7: /* thank you */ break; case 0xE8: parseDebugAssert(msg); break; case 0xF0: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerShowQuestLog, player->getID()); break; case 0xF1: parseQuestLine(msg); break; case 0xF2: parseRuleViolationReport(msg); break; case 0xF3: /* get object info */ break; case 0xF4: parseMarketLeave(); break; case 0xF5: parseMarketBrowse(msg); break; case 0xF6: parseMarketCreateOffer(msg); break; case 0xF7: parseMarketCancelOffer(msg); break; case 0xF8: parseMarketAcceptOffer(msg); break; case 0xF9: parseModalWindowAnswer(msg); break; default: // std::cout << "Player: " << player->getName() << " sent an unknown packet header: 0x" << std::hex << static_cast<uint16_t>(recvbyte) << std::dec << "!" << std::endl; break; } if (msg.isOverrun()) { disconnect(); } } void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg) { msg.add<uint16_t>(0x00); //environmental effects int32_t count; Item* ground = tile->getGround(); if (ground) { msg.addItem(ground); count = 1; } else { count = 0; } const TileItemVector* items = tile->getItemList(); if (items) { for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) { msg.addItem(*it); count++; if (count == 9 && tile->getPosition() == player->getPosition()) { break; } else if (count == 10) { return; } } } const CreatureVector* creatures = tile->getCreatures(); if (creatures) { bool playerAdded = false; for (const Creature* creature : boost::adaptors::reverse(*creatures)) { if (!player->canSeeCreature(creature)) { continue; } if (tile->getPosition() == player->getPosition() && count == 9 && !playerAdded) { creature = player; } if (creature->getID() == player->getID()) { playerAdded = true; } bool known; uint32_t removedKnown; checkCreatureAsKnown(creature->getID(), known, removedKnown); AddCreature(msg, creature, known, removedKnown); if (++count == 10) { return; } } } if (items) { for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) { msg.addItem(*it); if (++count == 10) { return; } } } } void ProtocolGame::GetMapDescription(int32_t x, int32_t y, int32_t z, int32_t width, int32_t height, NetworkMessage& msg) { int32_t skip = -1; int32_t startz, endz, zstep; if (z > 7) { startz = z - 2; endz = std::min<int32_t>(MAP_MAX_LAYERS - 1, z + 2); zstep = 1; } else { startz = 7; endz = 0; zstep = -1; } for (int32_t nz = startz; nz != endz + zstep; nz += zstep) { GetFloorDescription(msg, x, y, nz, width, height, z - nz, skip); } if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } } void ProtocolGame::GetFloorDescription(NetworkMessage& msg, int32_t x, int32_t y, int32_t z, int32_t width, int32_t height, int32_t offset, int32_t& skip) { for (int32_t nx = 0; nx < width; nx++) { for (int32_t ny = 0; ny < height; ny++) { Tile* tile = g_game.map.getTile(x + nx + offset, y + ny + offset, z); if (tile) { if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } skip = 0; GetTileDescription(tile, msg); } else if (skip == 0xFE) { msg.addByte(0xFF); msg.addByte(0xFF); skip = -1; } else { ++skip; } } } } void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool& known, uint32_t& removedKnown) { auto result = knownCreatureSet.insert(id); if (!result.second) { known = true; return; } known = false; if (knownCreatureSet.size() > 1300) { // Look for a creature to remove for (auto it = knownCreatureSet.begin(), end = knownCreatureSet.end(); it != end; ++it) { Creature* creature = g_game.getCreatureByID(*it); if (!canSee(creature)) { removedKnown = *it; knownCreatureSet.erase(it); return; } } // Bad situation. Let's just remove anyone. auto it = knownCreatureSet.begin(); if (*it == id) { ++it; } removedKnown = *it; knownCreatureSet.erase(it); } else { removedKnown = 0; } } bool ProtocolGame::canSee(const Creature* c) const { if (!c || !player || c->isRemoved()) { return false; } if (!player->canSeeCreature(c)) { return false; } return canSee(c->getPosition()); } bool ProtocolGame::canSee(const Position& pos) const { return canSee(pos.x, pos.y, pos.z); } bool ProtocolGame::canSee(int32_t x, int32_t y, int32_t z) const { if (!player) { return false; } const Position& myPos = player->getPosition(); if (myPos.z <= 7) { //we are on ground level or above (7 -> 0) //view is from 7 -> 0 if (z > 7) { return false; } } else if (myPos.z >= 8) { //we are underground (8 -> 15) //view is +/- 2 from the floor we stand on if (std::abs(myPos.getZ() - z) > 2) { return false; } } //negative offset means that the action taken place is on a lower floor than ourself int32_t offsetz = myPos.getZ() - z; if ((x >= myPos.getX() - Map::maxClientViewportX + offsetz) && (x <= myPos.getX() + (Map::maxClientViewportX+1) + offsetz) && (y >= myPos.getY() - Map::maxClientViewportY + offsetz) && (y <= myPos.getY() + (Map::maxClientViewportY+1) + offsetz)) { return true; } return false; } // Parse methods void ProtocolGame::parseChannelInvite(NetworkMessage& msg) { const std::string name = msg.getString(); addGameTask(&Game::playerChannelInvite, player->getID(), name); } void ProtocolGame::parseChannelExclude(NetworkMessage& msg) { const std::string name = msg.getString(); addGameTask(&Game::playerChannelExclude, player->getID(), name); } void ProtocolGame::parseOpenChannel(NetworkMessage& msg) { uint16_t channelId = msg.get<uint16_t>(); addGameTask(&Game::playerOpenChannel, player->getID(), channelId); } void ProtocolGame::parseCloseChannel(NetworkMessage& msg) { uint16_t channelId = msg.get<uint16_t>(); addGameTask(&Game::playerCloseChannel, player->getID(), channelId); } void ProtocolGame::parseOpenPrivateChannel(NetworkMessage& msg) { const std::string receiver = msg.getString(); addGameTask(&Game::playerOpenPrivateChannel, player->getID(), receiver); } void ProtocolGame::parseAutoWalk(NetworkMessage& msg) { uint8_t numdirs = msg.getByte(); if (numdirs == 0 || (msg.getBufferPosition() + numdirs) != (msg.getLength() + 8)) { return; } msg.skipBytes(numdirs); std::forward_list<Direction> path; for (uint8_t i = 0; i < numdirs; ++i) { uint8_t rawdir = msg.getPreviousByte(); switch (rawdir) { case 1: path.push_front(DIRECTION_EAST); break; case 2: path.push_front(DIRECTION_NORTHEAST); break; case 3: path.push_front(DIRECTION_NORTH); break; case 4: path.push_front(DIRECTION_NORTHWEST); break; case 5: path.push_front(DIRECTION_WEST); break; case 6: path.push_front(DIRECTION_SOUTHWEST); break; case 7: path.push_front(DIRECTION_SOUTH); break; case 8: path.push_front(DIRECTION_SOUTHEAST); break; default: break; } } if (path.empty()) { return; } addGameTask(&Game::playerAutoWalk, player->getID(), path); } void ProtocolGame::parseSetOutfit(NetworkMessage& msg) { Outfit_t newOutfit; newOutfit.lookType = msg.get<uint16_t>(); newOutfit.lookHead = msg.getByte(); newOutfit.lookBody = msg.getByte(); newOutfit.lookLegs = msg.getByte(); newOutfit.lookFeet = msg.getByte(); newOutfit.lookAddons = msg.getByte(); newOutfit.lookMount = msg.get<uint16_t>(); addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit); } void ProtocolGame::parseToggleMount(NetworkMessage& msg) { bool mount = msg.getByte() != 0; addGameTask(&Game::playerToggleMount, player->getID(), mount); } void ProtocolGame::parseUseItem(NetworkMessage& msg) { Position pos = msg.getPosition(); uint16_t spriteId = msg.get<uint16_t>(); uint8_t stackpos = msg.getByte(); uint8_t index = msg.getByte(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItem, player->getID(), pos, stackpos, index, spriteId); } void ProtocolGame::parseUseItemEx(NetworkMessage& msg) { Position fromPos = msg.getPosition(); uint16_t fromSpriteId = msg.get<uint16_t>(); uint8_t fromStackPos = msg.getByte(); Position toPos = msg.getPosition(); uint16_t toSpriteId = msg.get<uint16_t>(); uint8_t toStackPos = msg.getByte(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItemEx, player->getID(), fromPos, fromStackPos, fromSpriteId, toPos, toStackPos, toSpriteId); } void ProtocolGame::parseUseWithCreature(NetworkMessage& msg) { Position fromPos = msg.getPosition(); uint16_t spriteId = msg.get<uint16_t>(); uint8_t fromStackPos = msg.getByte(); uint32_t creatureId = msg.get<uint32_t>(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseWithCreature, player->getID(), fromPos, fromStackPos, creatureId, spriteId); } void ProtocolGame::parseCloseContainer(NetworkMessage& msg) { uint8_t cid = msg.getByte(); addGameTask(&Game::playerCloseContainer, player->getID(), cid); } void ProtocolGame::parseUpArrowContainer(NetworkMessage& msg) { uint8_t cid = msg.getByte(); addGameTask(&Game::playerMoveUpContainer, player->getID(), cid); } void ProtocolGame::parseUpdateContainer(NetworkMessage& msg) { uint8_t cid = msg.getByte(); addGameTask(&Game::playerUpdateContainer, player->getID(), cid); } void ProtocolGame::parseThrow(NetworkMessage& msg) { Position fromPos = msg.getPosition(); uint16_t spriteId = msg.get<uint16_t>(); uint8_t fromStackpos = msg.getByte(); Position toPos = msg.getPosition(); uint8_t count = msg.getByte(); if (toPos != fromPos) { addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerMoveThing, player->getID(), fromPos, spriteId, fromStackpos, toPos, count); } } void ProtocolGame::parseLookAt(NetworkMessage& msg) { Position pos = msg.getPosition(); msg.skipBytes(2); // spriteId uint8_t stackpos = msg.getByte(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookAt, player->getID(), pos, stackpos); } void ProtocolGame::parseLookInBattleList(NetworkMessage& msg) { uint32_t creatureId = msg.get<uint32_t>(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInBattleList, player->getID(), creatureId); } void ProtocolGame::parseSay(NetworkMessage& msg) { std::string receiver; uint16_t channelId; SpeakClasses type = static_cast<SpeakClasses>(msg.getByte()); switch (type) { case TALKTYPE_PRIVATE_TO: case TALKTYPE_PRIVATE_RED_TO: receiver = msg.getString(); channelId = 0; break; case TALKTYPE_CHANNEL_Y: case TALKTYPE_CHANNEL_R1: channelId = msg.get<uint16_t>(); break; default: channelId = 0; break; } const std::string text = msg.getString(); if (text.length() > 255) { return; } addGameTask(&Game::playerSay, player->getID(), channelId, type, receiver, text); } void ProtocolGame::parseFightModes(NetworkMessage& msg) { uint8_t rawFightMode = msg.getByte(); // 1 - offensive, 2 - balanced, 3 - defensive uint8_t rawChaseMode = msg.getByte(); // 0 - stand while fightning, 1 - chase opponent uint8_t rawSecureMode = msg.getByte(); // 0 - can't attack unmarked, 1 - can attack unmarked // uint8_t rawPvpMode = msg.getByte(); // pvp mode introduced in 10.0 fightMode_t fightMode; if (rawFightMode == 1) { fightMode = FIGHTMODE_ATTACK; } else if (rawFightMode == 2) { fightMode = FIGHTMODE_BALANCED; } else { fightMode = FIGHTMODE_DEFENSE; } addGameTask(&Game::playerSetFightModes, player->getID(), fightMode, rawChaseMode != 0, rawSecureMode != 0); } void ProtocolGame::parseAttack(NetworkMessage& msg) { uint32_t creatureId = msg.get<uint32_t>(); // msg.get<uint32_t>(); creatureId (same as above) addGameTask(&Game::playerSetAttackedCreature, player->getID(), creatureId); } void ProtocolGame::parseFollow(NetworkMessage& msg) { uint32_t creatureId = msg.get<uint32_t>(); // msg.get<uint32_t>(); creatureId (same as above) addGameTask(&Game::playerFollowCreature, player->getID(), creatureId); } void ProtocolGame::parseEquipObject(NetworkMessage& msg) { uint16_t spriteId = msg.get<uint16_t>(); // msg.get<uint8_t>(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerEquipItem, player->getID(), spriteId); } void ProtocolGame::parseTextWindow(NetworkMessage& msg) { uint32_t windowTextId = msg.get<uint32_t>(); const std::string newText = msg.getString(); addGameTask(&Game::playerWriteItem, player->getID(), windowTextId, newText); } void ProtocolGame::parseHouseWindow(NetworkMessage& msg) { uint8_t doorId = msg.getByte(); uint32_t id = msg.get<uint32_t>(); const std::string text = msg.getString(); addGameTask(&Game::playerUpdateHouseWindow, player->getID(), doorId, id, text); } void ProtocolGame::parseLookInShop(NetworkMessage& msg) { uint16_t id = msg.get<uint16_t>(); uint8_t count = msg.getByte(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInShop, player->getID(), id, count); } void ProtocolGame::parsePlayerPurchase(NetworkMessage& msg) { uint16_t id = msg.get<uint16_t>(); uint8_t count = msg.getByte(); uint8_t amount = msg.getByte(); bool ignoreCap = msg.getByte() != 0; bool inBackpacks = msg.getByte() != 0; addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerPurchaseItem, player->getID(), id, count, amount, ignoreCap, inBackpacks); } void ProtocolGame::parsePlayerSale(NetworkMessage& msg) { uint16_t id = msg.get<uint16_t>(); uint8_t count = msg.getByte(); uint8_t amount = msg.getByte(); bool ignoreEquipped = msg.getByte() != 0; addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerSellItem, player->getID(), id, count, amount, ignoreEquipped); } void ProtocolGame::parseRequestTrade(NetworkMessage& msg) { Position pos = msg.getPosition(); uint16_t spriteId = msg.get<uint16_t>(); uint8_t stackpos = msg.getByte(); uint32_t playerId = msg.get<uint32_t>(); addGameTask(&Game::playerRequestTrade, player->getID(), pos, stackpos, playerId, spriteId); } void ProtocolGame::parseLookInTrade(NetworkMessage& msg) { bool counterOffer = (msg.getByte() == 0x01); uint8_t index = msg.getByte(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInTrade, player->getID(), counterOffer, index); } void ProtocolGame::parseAddVip(NetworkMessage& msg) { const std::string name = msg.getString(); addGameTask(&Game::playerRequestAddVip, player->getID(), name); } void ProtocolGame::parseRemoveVip(NetworkMessage& msg) { uint32_t guid = msg.get<uint32_t>(); addGameTask(&Game::playerRequestRemoveVip, player->getID(), guid); } void ProtocolGame::parseEditVip(NetworkMessage& msg) { uint32_t guid = msg.get<uint32_t>(); const std::string description = msg.getString(); uint32_t icon = std::min<uint32_t>(10, msg.get<uint32_t>()); // 10 is max icon in 9.63 bool notify = msg.getByte() != 0; addGameTask(&Game::playerRequestEditVip, player->getID(), guid, description, icon, notify); } void ProtocolGame::parseRotateItem(NetworkMessage& msg) { Position pos = msg.getPosition(); uint16_t spriteId = msg.get<uint16_t>(); uint8_t stackpos = msg.getByte(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerRotateItem, player->getID(), pos, stackpos, spriteId); } void ProtocolGame::parseRuleViolationReport(NetworkMessage& msg) { uint8_t reportType = msg.getByte(); uint8_t reportReason = msg.getByte(); const std::string& targetName = msg.getString(); const std::string& comment = msg.getString(); std::string translation; if (reportType == REPORT_TYPE_NAME) { translation = msg.getString(); } else if (reportType == REPORT_TYPE_STATEMENT) { translation = msg.getString(); msg.get<uint32_t>(); // statement id, used to get whatever player have said, we don't log that. } addGameTask(&Game::playerReportRuleViolation, player->getID(), targetName, reportType, reportReason, comment, translation); } void ProtocolGame::parseBugReport(NetworkMessage& msg) { uint8_t category = msg.getByte(); std::string message = msg.getString(); Position position; if (category == BUG_CATEGORY_MAP) { position = msg.getPosition(); } addGameTask(&Game::playerReportBug, player->getID(), message, position, category); } void ProtocolGame::parseDebugAssert(NetworkMessage& msg) { if (debugAssertSent) { return; } debugAssertSent = true; std::string assertLine = msg.getString(); std::string date = msg.getString(); std::string description = msg.getString(); std::string comment = msg.getString(); addGameTask(&Game::playerDebugAssert, player->getID(), assertLine, date, description, comment); } void ProtocolGame::parseInviteToParty(NetworkMessage& msg) { uint32_t targetId = msg.get<uint32_t>(); addGameTask(&Game::playerInviteToParty, player->getID(), targetId); } void ProtocolGame::parseJoinParty(NetworkMessage& msg) { uint32_t targetId = msg.get<uint32_t>(); addGameTask(&Game::playerJoinParty, player->getID(), targetId); } void ProtocolGame::parseRevokePartyInvite(NetworkMessage& msg) { uint32_t targetId = msg.get<uint32_t>(); addGameTask(&Game::playerRevokePartyInvitation, player->getID(), targetId); } void ProtocolGame::parsePassPartyLeadership(NetworkMessage& msg) { uint32_t targetId = msg.get<uint32_t>(); addGameTask(&Game::playerPassPartyLeadership, player->getID(), targetId); } void ProtocolGame::parseEnableSharedPartyExperience(NetworkMessage& msg) { bool sharedExpActive = msg.getByte() == 1; addGameTask(&Game::playerEnableSharedPartyExperience, player->getID(), sharedExpActive); } void ProtocolGame::parseQuestLine(NetworkMessage& msg) { uint16_t questId = msg.get<uint16_t>(); addGameTask(&Game::playerShowQuestLine, player->getID(), questId); } void ProtocolGame::parseMarketLeave() { addGameTask(&Game::playerLeaveMarket, player->getID()); } void ProtocolGame::parseMarketBrowse(NetworkMessage& msg) { uint16_t browseId = msg.get<uint16_t>(); if (browseId == MARKETREQUEST_OWN_OFFERS) { addGameTask(&Game::playerBrowseMarketOwnOffers, player->getID()); } else if (browseId == MARKETREQUEST_OWN_HISTORY) { addGameTask(&Game::playerBrowseMarketOwnHistory, player->getID()); } else { addGameTask(&Game::playerBrowseMarket, player->getID(), browseId); } } void ProtocolGame::parseMarketCreateOffer(NetworkMessage& msg) { uint8_t type = msg.getByte(); uint16_t spriteId = msg.get<uint16_t>(); uint16_t amount = msg.get<uint16_t>(); uint32_t price = msg.get<uint32_t>(); bool anonymous = (msg.getByte() != 0); addGameTask(&Game::playerCreateMarketOffer, player->getID(), type, spriteId, amount, price, anonymous); } void ProtocolGame::parseMarketCancelOffer(NetworkMessage& msg) { uint32_t timestamp = msg.get<uint32_t>(); uint16_t counter = msg.get<uint16_t>(); addGameTask(&Game::playerCancelMarketOffer, player->getID(), timestamp, counter); } void ProtocolGame::parseMarketAcceptOffer(NetworkMessage& msg) { uint32_t timestamp = msg.get<uint32_t>(); uint16_t counter = msg.get<uint16_t>(); uint16_t amount = msg.get<uint16_t>(); addGameTask(&Game::playerAcceptMarketOffer, player->getID(), timestamp, counter, amount); } void ProtocolGame::parseModalWindowAnswer(NetworkMessage& msg) { uint32_t id = msg.get<uint32_t>(); uint8_t button = msg.getByte(); uint8_t choice = msg.getByte(); addGameTask(&Game::playerAnswerModalWindow, player->getID(), id, button, choice); } void ProtocolGame::parseBrowseField(NetworkMessage& msg) { const Position& pos = msg.getPosition(); addGameTask(&Game::playerBrowseField, player->getID(), pos); } void ProtocolGame::parseSeekInContainer(NetworkMessage& msg) { uint8_t containerId = msg.getByte(); uint16_t index = msg.get<uint16_t>(); addGameTask(&Game::playerSeekInContainer, player->getID(), containerId, index); } // Send methods void ProtocolGame::sendOpenPrivateChannel(const std::string& receiver) { NetworkMessage msg; msg.addByte(0xAD); msg.addString(receiver); writeToOutputBuffer(msg); } void ProtocolGame::sendChannelEvent(uint16_t channelId, const std::string& playerName, ChannelEvent_t channelEvent) { NetworkMessage msg; msg.addByte(0xF3); msg.add<uint16_t>(channelId); msg.addString(playerName); msg.addByte(channelEvent); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureOutfit(const Creature* creature, const Outfit_t& outfit) { if (!canSee(creature)) { return; } NetworkMessage msg; msg.addByte(0x8E); msg.add<uint32_t>(creature->getID()); AddOutfit(msg, outfit); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureLight(const Creature* creature) { if (!canSee(creature)) { return; } NetworkMessage msg; AddCreatureLight(msg, creature); writeToOutputBuffer(msg); } void ProtocolGame::sendWorldLight(LightInfo lightInfo) { NetworkMessage msg; AddWorldLight(msg, lightInfo); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureWalkthrough(const Creature* creature, bool walkthrough) { if (!canSee(creature)) { return; } NetworkMessage msg; msg.addByte(0x92); msg.add<uint32_t>(creature->getID()); msg.addByte(walkthrough ? 0x00 : 0x01); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureShield(const Creature* creature) { if (!canSee(creature)) { return; } NetworkMessage msg; msg.addByte(0x91); msg.add<uint32_t>(creature->getID()); msg.addByte(player->getPartyShield(creature->getPlayer())); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureSkull(const Creature* creature) { if (g_game.getWorldType() != WORLD_TYPE_PVP) { return; } if (!canSee(creature)) { return; } NetworkMessage msg; msg.addByte(0x90); msg.add<uint32_t>(creature->getID()); msg.addByte(player->getSkullClient(creature)); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureType(uint32_t creatureId, uint8_t creatureType) { NetworkMessage msg; msg.addByte(0x95); msg.add<uint32_t>(creatureId); msg.addByte(creatureType); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureHelpers(uint32_t creatureId, uint16_t helpers) { NetworkMessage msg; msg.addByte(0x94); msg.add<uint32_t>(creatureId); msg.add<uint16_t>(helpers); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureSquare(const Creature* creature, SquareColor_t color) { if (!canSee(creature)) { return; } NetworkMessage msg; msg.addByte(0x93); msg.add<uint32_t>(creature->getID()); msg.addByte(0x01); msg.addByte(color); writeToOutputBuffer(msg); } void ProtocolGame::sendTutorial(uint8_t tutorialId) { NetworkMessage msg; msg.addByte(0xDC); msg.addByte(tutorialId); writeToOutputBuffer(msg); } void ProtocolGame::sendAddMarker(const Position& pos, uint8_t markType, const std::string& desc) { NetworkMessage msg; msg.addByte(0xDD); msg.addPosition(pos); msg.addByte(markType); msg.addString(desc); writeToOutputBuffer(msg); } void ProtocolGame::sendReLoginWindow(uint8_t unfairFightReduction) { NetworkMessage msg; msg.addByte(0x28); msg.addByte(0x00); msg.addByte(unfairFightReduction); writeToOutputBuffer(msg); } void ProtocolGame::sendStats() { NetworkMessage msg; AddPlayerStats(msg); writeToOutputBuffer(msg); } void ProtocolGame::sendBasicData() { NetworkMessage msg; msg.addByte(0x9F); if (player->isPremium()) { msg.addByte(1); msg.add<uint32_t>(time(nullptr) + (player->premiumDays * 86400)); } else { msg.addByte(0); msg.add<uint32_t>(0); } msg.addByte(player->getVocation()->getClientId()); msg.add<uint16_t>(0xFF); // number of known spells for (uint8_t spellId = 0x00; spellId < 0xFF; spellId++) { msg.addByte(spellId); } writeToOutputBuffer(msg); } void ProtocolGame::sendTextMessage(const TextMessage& message) { NetworkMessage msg; msg.addByte(0xB4); msg.addByte(message.type); switch (message.type) { case MESSAGE_DAMAGE_DEALT: case MESSAGE_DAMAGE_RECEIVED: case MESSAGE_DAMAGE_OTHERS: { msg.addPosition(message.position); msg.add<uint32_t>(message.primary.value); msg.addByte(message.primary.color); msg.add<uint32_t>(message.secondary.value); msg.addByte(message.secondary.color); break; } case MESSAGE_HEALED: case MESSAGE_HEALED_OTHERS: case MESSAGE_EXPERIENCE: case MESSAGE_EXPERIENCE_OTHERS: { msg.addPosition(message.position); msg.add<uint32_t>(message.primary.value); msg.addByte(message.primary.color); break; } case MESSAGE_GUILD: case MESSAGE_PARTY_MANAGEMENT: case MESSAGE_PARTY: msg.add<uint16_t>(message.channelId); break; default: { break; } } msg.addString(message.text); writeToOutputBuffer(msg); } void ProtocolGame::sendClosePrivate(uint16_t channelId) { NetworkMessage msg; msg.addByte(0xB3); msg.add<uint16_t>(channelId); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatePrivateChannel(uint16_t channelId, const std::string& channelName) { NetworkMessage msg; msg.addByte(0xB2); msg.add<uint16_t>(channelId); msg.addString(channelName); msg.add<uint16_t>(0x01); msg.addString(player->getName()); msg.add<uint16_t>(0x00); writeToOutputBuffer(msg); } void ProtocolGame::sendChannelsDialog() { NetworkMessage msg; msg.addByte(0xAB); const ChannelList& list = g_chat->getChannelList(*player); msg.addByte(list.size()); for (ChatChannel* channel : list) { msg.add<uint16_t>(channel->getId()); msg.addString(channel->getName()); } writeToOutputBuffer(msg); } void ProtocolGame::sendChannel(uint16_t channelId, const std::string& channelName, const UsersMap* channelUsers, const InvitedMap* invitedUsers) { NetworkMessage msg; msg.addByte(0xAC); msg.add<uint16_t>(channelId); msg.addString(channelName); if (channelUsers) { msg.add<uint16_t>(channelUsers->size()); for (const auto& it : *channelUsers) { msg.addString(it.second->getName()); } } else { msg.add<uint16_t>(0x00); } if (invitedUsers) { msg.add<uint16_t>(invitedUsers->size()); for (const auto& it : *invitedUsers) { msg.addString(it.second->getName()); } } else { msg.add<uint16_t>(0x00); } writeToOutputBuffer(msg); } void ProtocolGame::sendChannelMessage(const std::string& author, const std::string& text, SpeakClasses type, uint16_t channel) { NetworkMessage msg; msg.addByte(0xAA); msg.add<uint32_t>(0x00); msg.addString(author); msg.add<uint16_t>(0x00); msg.addByte(type); msg.add<uint16_t>(channel); msg.addString(text); writeToOutputBuffer(msg); } void ProtocolGame::sendIcons(uint16_t icons) { NetworkMessage msg; msg.addByte(0xA2); msg.add<uint16_t>(icons); writeToOutputBuffer(msg); } void ProtocolGame::sendContainer(uint8_t cid, const Container* container, bool hasParent, uint16_t firstIndex) { NetworkMessage msg; msg.addByte(0x6E); msg.addByte(cid); if (container->getID() == ITEM_BROWSEFIELD) { msg.addItem(1987, 1); msg.addString("Browse Field"); } else { msg.addItem(container); msg.addString(container->getName()); } msg.addByte(container->capacity()); msg.addByte(hasParent ? 0x01 : 0x00); msg.addByte(container->isUnlocked() ? 0x01 : 0x00); // Drag and drop msg.addByte(container->hasPagination() ? 0x01 : 0x00); // Pagination uint32_t containerSize = container->size(); msg.add<uint16_t>(containerSize); msg.add<uint16_t>(firstIndex); if (firstIndex < containerSize) { uint8_t itemsToSend = std::min<uint32_t>(std::min<uint32_t>(container->capacity(), containerSize - firstIndex), std::numeric_limits<uint8_t>::max()); msg.addByte(itemsToSend); for (auto it = container->getItemList().begin() + firstIndex, end = it + itemsToSend; it != end; ++it) { msg.addItem(*it); } } else { msg.addByte(0x00); } writeToOutputBuffer(msg); } void ProtocolGame::sendShop(Npc* npc, const ShopInfoList& itemList) { NetworkMessage msg; msg.addByte(0x7A); msg.addString(npc->getName()); uint16_t itemsToSend = std::min<size_t>(itemList.size(), std::numeric_limits<uint16_t>::max()); msg.add<uint16_t>(itemsToSend); uint16_t i = 0; for (auto it = itemList.begin(); i < itemsToSend; ++it, ++i) { AddShopItem(msg, *it); } writeToOutputBuffer(msg); } void ProtocolGame::sendCloseShop() { NetworkMessage msg; msg.addByte(0x7C); writeToOutputBuffer(msg); } void ProtocolGame::sendSaleItemList(const std::list<ShopInfo>& shop) { NetworkMessage msg; msg.addByte(0x7B); msg.add<uint64_t>(player->getMoney() + player->getBankBalance()); std::map<uint16_t, uint32_t> saleMap; if (shop.size() <= 5) { // For very small shops it's not worth it to create the complete map for (const ShopInfo& shopInfo : shop) { if (shopInfo.sellPrice == 0) { continue; } int8_t subtype = -1; const ItemType& itemType = Item::items[shopInfo.itemId]; if (itemType.hasSubType() && !itemType.stackable) { subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType); } uint32_t count = player->getItemTypeCount(shopInfo.itemId, subtype); if (count > 0) { saleMap[shopInfo.itemId] = count; } } } else { // Large shop, it's better to get a cached map of all item counts and use it // We need a temporary map since the finished map should only contain items // available in the shop std::map<uint32_t, uint32_t> tempSaleMap; player->getAllItemTypeCount(tempSaleMap); // We must still check manually for the special items that require subtype matches // (That is, fluids such as potions etc., actually these items are very few since // health potions now use their own ID) for (const ShopInfo& shopInfo : shop) { if (shopInfo.sellPrice == 0) { continue; } int8_t subtype = -1; const ItemType& itemType = Item::items[shopInfo.itemId]; if (itemType.hasSubType() && !itemType.stackable) { subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType); } if (subtype != -1) { uint32_t count; if (!itemType.isFluidContainer() && !itemType.isSplash()) { count = player->getItemTypeCount(shopInfo.itemId, subtype); // This shop item requires extra checks } else { count = subtype; } if (count > 0) { saleMap[shopInfo.itemId] = count; } } else { std::map<uint32_t, uint32_t>::const_iterator findIt = tempSaleMap.find(shopInfo.itemId); if (findIt != tempSaleMap.end() && findIt->second > 0) { saleMap[shopInfo.itemId] = findIt->second; } } } } uint8_t itemsToSend = std::min<size_t>(saleMap.size(), std::numeric_limits<uint8_t>::max()); msg.addByte(itemsToSend); uint8_t i = 0; for (std::map<uint16_t, uint32_t>::const_iterator it = saleMap.begin(); i < itemsToSend; ++it, ++i) { msg.addItemId(it->first); msg.addByte(std::min<uint32_t>(it->second, std::numeric_limits<uint8_t>::max())); } writeToOutputBuffer(msg); } void ProtocolGame::sendMarketEnter(uint32_t depotId) { NetworkMessage msg; msg.addByte(0xF6); msg.add<uint64_t>(player->getBankBalance()); msg.addByte(std::min<uint32_t>(IOMarket::getPlayerOfferCount(player->getGUID()), std::numeric_limits<uint8_t>::max())); DepotChest* depotChest = player->getDepotChest(depotId, false); if (!depotChest) { msg.add<uint16_t>(0x00); writeToOutputBuffer(msg); return; } player->setInMarket(true); std::map<uint16_t, uint32_t> depotItems; std::forward_list<Container*> containerList { depotChest, player->getInbox() }; do { Container* container = containerList.front(); containerList.pop_front(); for (Item* item : container->getItemList()) { Container* c = item->getContainer(); if (c && !c->empty()) { containerList.push_front(c); continue; } const ItemType& itemType = Item::items[item->getID()]; if (itemType.wareId == 0) { continue; } if (c && (!itemType.isContainer() || c->capacity() != itemType.maxItems)) { continue; } if (!item->hasMarketAttributes()) { continue; } depotItems[itemType.wareId] += Item::countByType(item, -1); } } while (!containerList.empty()); uint16_t itemsToSend = std::min<size_t>(depotItems.size(), std::numeric_limits<uint16_t>::max()); msg.add<uint16_t>(itemsToSend); uint16_t i = 0; for (std::map<uint16_t, uint32_t>::const_iterator it = depotItems.begin(); i < itemsToSend; ++it, ++i) { msg.add<uint16_t>(it->first); msg.add<uint16_t>(std::min<uint32_t>(0xFFFF, it->second)); } writeToOutputBuffer(msg); } void ProtocolGame::sendMarketLeave() { NetworkMessage msg; msg.addByte(0xF7); writeToOutputBuffer(msg); } void ProtocolGame::sendMarketBrowseItem(uint16_t itemId, const MarketOfferList& buyOffers, const MarketOfferList& sellOffers) { NetworkMessage msg; msg.addByte(0xF9); msg.addItemId(itemId); msg.add<uint32_t>(buyOffers.size()); for (const MarketOffer& offer : buyOffers) { msg.add<uint32_t>(offer.timestamp); msg.add<uint16_t>(offer.counter); msg.add<uint16_t>(offer.amount); msg.add<uint32_t>(offer.price); msg.addString(offer.playerName); } msg.add<uint32_t>(sellOffers.size()); for (const MarketOffer& offer : sellOffers) { msg.add<uint32_t>(offer.timestamp); msg.add<uint16_t>(offer.counter); msg.add<uint16_t>(offer.amount); msg.add<uint32_t>(offer.price); msg.addString(offer.playerName); } writeToOutputBuffer(msg); } void ProtocolGame::sendMarketAcceptOffer(const MarketOfferEx& offer) { NetworkMessage msg; msg.addByte(0xF9); msg.addItemId(offer.itemId); if (offer.type == MARKETACTION_BUY) { msg.add<uint32_t>(0x01); msg.add<uint32_t>(offer.timestamp); msg.add<uint16_t>(offer.counter); msg.add<uint16_t>(offer.amount); msg.add<uint32_t>(offer.price); msg.addString(offer.playerName); msg.add<uint32_t>(0x00); } else { msg.add<uint32_t>(0x00); msg.add<uint32_t>(0x01); msg.add<uint32_t>(offer.timestamp); msg.add<uint16_t>(offer.counter); msg.add<uint16_t>(offer.amount); msg.add<uint32_t>(offer.price); msg.addString(offer.playerName); } writeToOutputBuffer(msg); } void ProtocolGame::sendMarketBrowseOwnOffers(const MarketOfferList& buyOffers, const MarketOfferList& sellOffers) { NetworkMessage msg; msg.addByte(0xF9); msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS); msg.add<uint32_t>(buyOffers.size()); for (const MarketOffer& offer : buyOffers) { msg.add<uint32_t>(offer.timestamp); msg.add<uint16_t>(offer.counter); msg.addItemId(offer.itemId); msg.add<uint16_t>(offer.amount); msg.add<uint32_t>(offer.price); } msg.add<uint32_t>(sellOffers.size()); for (const MarketOffer& offer : sellOffers) { msg.add<uint32_t>(offer.timestamp); msg.add<uint16_t>(offer.counter); msg.addItemId(offer.itemId); msg.add<uint16_t>(offer.amount); msg.add<uint32_t>(offer.price); } writeToOutputBuffer(msg); } void ProtocolGame::sendMarketCancelOffer(const MarketOfferEx& offer) { NetworkMessage msg; msg.addByte(0xF9); msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS); if (offer.type == MARKETACTION_BUY) { msg.add<uint32_t>(0x01); msg.add<uint32_t>(offer.timestamp); msg.add<uint16_t>(offer.counter); msg.addItemId(offer.itemId); msg.add<uint16_t>(offer.amount); msg.add<uint32_t>(offer.price); msg.add<uint32_t>(0x00); } else { msg.add<uint32_t>(0x00); msg.add<uint32_t>(0x01); msg.add<uint32_t>(offer.timestamp); msg.add<uint16_t>(offer.counter); msg.addItemId(offer.itemId); msg.add<uint16_t>(offer.amount); msg.add<uint32_t>(offer.price); } writeToOutputBuffer(msg); } void ProtocolGame::sendMarketBrowseOwnHistory(const HistoryMarketOfferList& buyOffers, const HistoryMarketOfferList& sellOffers) { uint32_t i = 0; std::map<uint32_t, uint16_t> counterMap; uint32_t buyOffersToSend = std::min<uint32_t>(buyOffers.size(), 810 + std::max<int32_t>(0, 810 - sellOffers.size())); uint32_t sellOffersToSend = std::min<uint32_t>(sellOffers.size(), 810 + std::max<int32_t>(0, 810 - buyOffers.size())); NetworkMessage msg; msg.addByte(0xF9); msg.add<uint16_t>(MARKETREQUEST_OWN_HISTORY); msg.add<uint32_t>(buyOffersToSend); for (auto it = buyOffers.begin(); i < buyOffersToSend; ++it, ++i) { msg.add<uint32_t>(it->timestamp); msg.add<uint16_t>(counterMap[it->timestamp]++); msg.addItemId(it->itemId); msg.add<uint16_t>(it->amount); msg.add<uint32_t>(it->price); msg.addByte(it->state); } counterMap.clear(); i = 0; msg.add<uint32_t>(sellOffersToSend); for (auto it = sellOffers.begin(); i < sellOffersToSend; ++it, ++i) { msg.add<uint32_t>(it->timestamp); msg.add<uint16_t>(counterMap[it->timestamp]++); msg.addItemId(it->itemId); msg.add<uint16_t>(it->amount); msg.add<uint32_t>(it->price); msg.addByte(it->state); } writeToOutputBuffer(msg); } void ProtocolGame::sendMarketDetail(uint16_t itemId) { NetworkMessage msg; msg.addByte(0xF8); msg.addItemId(itemId); const ItemType& it = Item::items[itemId]; if (it.armor != 0) { msg.addString(std::to_string(it.armor)); } else { msg.add<uint16_t>(0x00); } if (it.attack != 0) { // TODO: chance to hit, range // example: // "attack +x, chance to hit +y%, z fields" if (it.abilities && it.abilities->elementType != COMBAT_NONE && it.abilities->elementDamage != 0) { std::ostringstream ss; ss << it.attack << " physical +" << it.abilities->elementDamage << ' ' << getCombatName(it.abilities->elementType); msg.addString(ss.str()); } else { msg.addString(std::to_string(it.attack)); } } else { msg.add<uint16_t>(0x00); } if (it.isContainer()) { msg.addString(std::to_string(it.maxItems)); } else { msg.add<uint16_t>(0x00); } if (it.defense != 0) { if (it.extraDefense != 0) { std::ostringstream ss; ss << it.defense << ' ' << std::showpos << it.extraDefense << std::noshowpos; msg.addString(ss.str()); } else { msg.addString(std::to_string(it.defense)); } } else { msg.add<uint16_t>(0x00); } if (!it.description.empty()) { const std::string& descr = it.description; if (descr.back() == '.') { msg.addString(std::string(descr, 0, descr.length() - 1)); } else { msg.addString(descr); } } else { msg.add<uint16_t>(0x00); } if (it.decayTime != 0) { std::ostringstream ss; ss << it.decayTime << " seconds"; msg.addString(ss.str()); } else { msg.add<uint16_t>(0x00); } if (it.abilities) { std::ostringstream ss; bool separator = false; for (size_t i = 0; i < COMBAT_COUNT; ++i) { if (it.abilities->absorbPercent[i] == 0) { continue; } if (separator) { ss << ", "; } else { separator = true; } ss << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << std::noshowpos << '%'; } msg.addString(ss.str()); } else { msg.add<uint16_t>(0x00); } if (it.minReqLevel != 0) { msg.addString(std::to_string(it.minReqLevel)); } else { msg.add<uint16_t>(0x00); } if (it.minReqMagicLevel != 0) { msg.addString(std::to_string(it.minReqMagicLevel)); } else { msg.add<uint16_t>(0x00); } msg.addString(it.vocationString); msg.addString(it.runeSpellName); if (it.abilities) { std::ostringstream ss; bool separator = false; for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) { if (!it.abilities->skills[i]) { continue; } if (separator) { ss << ", "; } else { separator = true; } ss << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos; } if (it.abilities->stats[STAT_MAGICPOINTS] != 0) { if (separator) { ss << ", "; } else { separator = true; } ss << "magic level " << std::showpos << it.abilities->stats[STAT_MAGICPOINTS] << std::noshowpos; } if (it.abilities->speed != 0) { if (separator) { ss << ", "; } ss << "speed " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos; } msg.addString(ss.str()); } else { msg.add<uint16_t>(0x00); } if (it.charges != 0) { msg.addString(std::to_string(it.charges)); } else { msg.add<uint16_t>(0x00); } std::string weaponName = getWeaponName(it.weaponType); if (it.slotPosition & SLOTP_TWO_HAND) { if (!weaponName.empty()) { weaponName += ", two-handed"; } else { weaponName = "two-handed"; } } msg.addString(weaponName); if (it.weight != 0) { std::ostringstream ss; if (it.weight < 10) { ss << "0.0" << it.weight; } else if (it.weight < 100) { ss << "0." << it.weight; } else { std::string weightString = std::to_string(it.weight); weightString.insert(weightString.end() - 2, '.'); ss << weightString; } ss << " oz"; msg.addString(ss.str()); } else { msg.add<uint16_t>(0x00); } MarketStatistics* statistics = IOMarket::getInstance().getPurchaseStatistics(itemId); if (statistics) { msg.addByte(0x01); msg.add<uint32_t>(statistics->numTransactions); msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice)); msg.add<uint32_t>(statistics->highestPrice); msg.add<uint32_t>(statistics->lowestPrice); } else { msg.addByte(0x00); } statistics = IOMarket::getInstance().getSaleStatistics(itemId); if (statistics) { msg.addByte(0x01); msg.add<uint32_t>(statistics->numTransactions); msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice)); msg.add<uint32_t>(statistics->highestPrice); msg.add<uint32_t>(statistics->lowestPrice); } else { msg.addByte(0x00); } writeToOutputBuffer(msg); } void ProtocolGame::sendQuestLog() { NetworkMessage msg; msg.addByte(0xF0); msg.add<uint16_t>(g_game.quests.getQuestsCount(player)); for (const Quest& quest : g_game.quests.getQuests()) { if (quest.isStarted(player)) { msg.add<uint16_t>(quest.getID()); msg.addString(quest.getName()); msg.addByte(quest.isCompleted(player)); } } writeToOutputBuffer(msg); } void ProtocolGame::sendQuestLine(const Quest* quest) { NetworkMessage msg; msg.addByte(0xF1); msg.add<uint16_t>(quest->getID()); msg.addByte(quest->getMissionsCount(player)); for (const Mission& mission : quest->getMissions()) { if (mission.isStarted(player)) { msg.addString(mission.getName(player)); msg.addString(mission.getDescription(player)); } } writeToOutputBuffer(msg); } void ProtocolGame::sendTradeItemRequest(const std::string& traderName, const Item* item, bool ack) { NetworkMessage msg; if (ack) { msg.addByte(0x7D); } else { msg.addByte(0x7E); } msg.addString(traderName); if (const Container* tradeContainer = item->getContainer()) { std::list<const Container*> listContainer {tradeContainer}; std::list<const Item*> itemList {tradeContainer}; while (!listContainer.empty()) { const Container* container = listContainer.front(); listContainer.pop_front(); for (Item* containerItem : container->getItemList()) { Container* tmpContainer = containerItem->getContainer(); if (tmpContainer) { listContainer.push_back(tmpContainer); } itemList.push_back(containerItem); } } msg.addByte(itemList.size()); for (const Item* listItem : itemList) { msg.addItem(listItem); } } else { msg.addByte(0x01); msg.addItem(item); } writeToOutputBuffer(msg); } void ProtocolGame::sendCloseTrade() { NetworkMessage msg; msg.addByte(0x7F); writeToOutputBuffer(msg); } void ProtocolGame::sendCloseContainer(uint8_t cid) { NetworkMessage msg; msg.addByte(0x6F); msg.addByte(cid); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureTurn(const Creature* creature, uint32_t stackPos) { if (!canSee(creature)) { return; } NetworkMessage msg; msg.addByte(0x6B); msg.addPosition(creature->getPosition()); msg.addByte(stackPos); msg.add<uint16_t>(0x63); msg.add<uint32_t>(creature->getID()); msg.addByte(creature->getDirection()); msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, const Position* pos/* = nullptr*/) { NetworkMessage msg; msg.addByte(0xAA); static uint32_t statementId = 0; msg.add<uint32_t>(++statementId); msg.addString(creature->getName()); //Add level only for players if (const Player* speaker = creature->getPlayer()) { msg.add<uint16_t>(speaker->getLevel()); } else { msg.add<uint16_t>(0x00); } msg.addByte(type); if (pos) { msg.addPosition(*pos); } else { msg.addPosition(creature->getPosition()); } msg.addString(text); writeToOutputBuffer(msg); } void ProtocolGame::sendToChannel(const Creature* creature, SpeakClasses type, const std::string& text, uint16_t channelId) { NetworkMessage msg; msg.addByte(0xAA); static uint32_t statementId = 0; msg.add<uint32_t>(++statementId); if (!creature) { msg.add<uint32_t>(0x00); } else if (type == TALKTYPE_CHANNEL_R2) { msg.add<uint32_t>(0x00); type = TALKTYPE_CHANNEL_R1; } else { msg.addString(creature->getName()); //Add level only for players if (const Player* speaker = creature->getPlayer()) { msg.add<uint16_t>(speaker->getLevel()); } else { msg.add<uint16_t>(0x00); } } msg.addByte(type); msg.add<uint16_t>(channelId); msg.addString(text); writeToOutputBuffer(msg); } void ProtocolGame::sendPrivateMessage(const Player* speaker, SpeakClasses type, const std::string& text) { NetworkMessage msg; msg.addByte(0xAA); static uint32_t statementId = 0; msg.add<uint32_t>(++statementId); if (speaker) { msg.addString(speaker->getName()); msg.add<uint16_t>(speaker->getLevel()); } else { msg.add<uint32_t>(0x00); } msg.addByte(type); msg.addString(text); writeToOutputBuffer(msg); } void ProtocolGame::sendCancelTarget() { NetworkMessage msg; msg.addByte(0xA3); msg.add<uint32_t>(0x00); writeToOutputBuffer(msg); } void ProtocolGame::sendChangeSpeed(const Creature* creature, uint32_t speed) { NetworkMessage msg; msg.addByte(0x8F); msg.add<uint32_t>(creature->getID()); msg.add<uint16_t>(creature->getBaseSpeed() / 2); msg.add<uint16_t>(speed / 2); writeToOutputBuffer(msg); } void ProtocolGame::sendCancelWalk() { NetworkMessage msg; msg.addByte(0xB5); msg.addByte(player->getDirection()); writeToOutputBuffer(msg); } void ProtocolGame::sendSkills() { NetworkMessage msg; AddPlayerSkills(msg); writeToOutputBuffer(msg); } void ProtocolGame::sendPing() { NetworkMessage msg; msg.addByte(0x1D); writeToOutputBuffer(msg); } void ProtocolGame::sendPingBack() { NetworkMessage msg; msg.addByte(0x1E); writeToOutputBuffer(msg); } void ProtocolGame::sendDistanceShoot(const Position& from, const Position& to, uint8_t type) { NetworkMessage msg; msg.addByte(0x85); msg.addPosition(from); msg.addPosition(to); msg.addByte(type); writeToOutputBuffer(msg); } void ProtocolGame::sendMagicEffect(const Position& pos, uint8_t type) { if (!canSee(pos)) { return; } NetworkMessage msg; msg.addByte(0x83); msg.addPosition(pos); msg.addByte(type); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureHealth(const Creature* creature) { NetworkMessage msg; msg.addByte(0x8C); msg.add<uint32_t>(creature->getID()); if (creature->isHealthHidden()) { msg.addByte(0x00); } else { msg.addByte(std::ceil((static_cast<double>(creature->getHealth()) / std::max<int32_t>(creature->getMaxHealth(), 1)) * 100)); } writeToOutputBuffer(msg); } void ProtocolGame::sendFYIBox(const std::string& message) { NetworkMessage msg; msg.addByte(0x15); msg.addString(message); writeToOutputBuffer(msg); } //tile void ProtocolGame::sendMapDescription(const Position& pos) { NetworkMessage msg; msg.addByte(0x64); msg.addPosition(player->getPosition()); GetMapDescription(pos.x - Map::maxClientViewportX, pos.y - Map::maxClientViewportY, pos.z, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, msg); writeToOutputBuffer(msg); } void ProtocolGame::sendAddTileItem(const Position& pos, uint32_t stackpos, const Item* item) { if (!canSee(pos)) { return; } NetworkMessage msg; msg.addByte(0x6A); msg.addPosition(pos); msg.addByte(stackpos); msg.addItem(item); writeToOutputBuffer(msg); } void ProtocolGame::sendUpdateTileItem(const Position& pos, uint32_t stackpos, const Item* item) { if (!canSee(pos)) { return; } NetworkMessage msg; msg.addByte(0x6B); msg.addPosition(pos); msg.addByte(stackpos); msg.addItem(item); writeToOutputBuffer(msg); } void ProtocolGame::sendRemoveTileThing(const Position& pos, uint32_t stackpos) { if (!canSee(pos)) { return; } NetworkMessage msg; RemoveTileThing(msg, pos, stackpos); writeToOutputBuffer(msg); } void ProtocolGame::sendUpdateTile(const Tile* tile, const Position& pos) { if (!canSee(pos)) { return; } NetworkMessage msg; msg.addByte(0x69); msg.addPosition(pos); if (tile) { GetTileDescription(tile, msg); msg.addByte(0x00); msg.addByte(0xFF); } else { msg.addByte(0x01); msg.addByte(0xFF); } writeToOutputBuffer(msg); } void ProtocolGame::sendPendingStateEntered() { NetworkMessage msg; msg.addByte(0x0A); writeToOutputBuffer(msg); } void ProtocolGame::sendEnterWorld() { NetworkMessage msg; msg.addByte(0x0F); writeToOutputBuffer(msg); } void ProtocolGame::sendFightModes() { NetworkMessage msg; msg.addByte(0xA7); msg.addByte(player->fightMode); msg.addByte(player->chaseMode); msg.addByte(player->secureMode); msg.addByte(PVP_MODE_DOVE); writeToOutputBuffer(msg); } void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos, int32_t stackpos, bool isLogin) { if (!canSee(pos)) { return; } if (creature != player) { if (stackpos != -1) { NetworkMessage msg; msg.addByte(0x6A); msg.addPosition(pos); msg.addByte(stackpos); bool known; uint32_t removedKnown; checkCreatureAsKnown(creature->getID(), known, removedKnown); AddCreature(msg, creature, known, removedKnown); writeToOutputBuffer(msg); } if (isLogin) { sendMagicEffect(pos, CONST_ME_TELEPORT); } return; } NetworkMessage msg; msg.addByte(0x17); msg.add<uint32_t>(player->getID()); msg.add<uint16_t>(0x32); // beat duration (50) msg.addDouble(Creature::speedA, 3); msg.addDouble(Creature::speedB, 3); msg.addDouble(Creature::speedC, 3); // can report bugs? if (player->getAccountType() >= ACCOUNT_TYPE_TUTOR) { msg.addByte(0x01); } else { msg.addByte(0x00); } msg.addByte(0x00); // can change pvp framing option msg.addByte(0x00); // expert mode button enabled msg.add<uint16_t>(0x00); // URL (string) to ingame store images msg.add<uint16_t>(25); // premium coin package size writeToOutputBuffer(msg); sendPendingStateEntered(); sendEnterWorld(); sendMapDescription(pos); if (isLogin) { sendMagicEffect(pos, CONST_ME_TELEPORT); } for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) { sendInventoryItem(static_cast<slots_t>(i), player->getInventoryItem(static_cast<slots_t>(i))); } sendStats(); sendSkills(); //gameworld light-settings sendWorldLight(g_game.getWorldLightInfo()); //player light level sendCreatureLight(creature); sendVIPEntries(); sendBasicData(); player->sendIcons(); } void ProtocolGame::sendMoveCreature(const Creature* creature, const Position& newPos, int32_t newStackPos, const Position& oldPos, int32_t oldStackPos, bool teleport) { if (creature == player) { if (oldStackPos >= 10) { sendMapDescription(newPos); } else if (teleport) { NetworkMessage msg; RemoveTileThing(msg, oldPos, oldStackPos); writeToOutputBuffer(msg); sendMapDescription(newPos); } else { NetworkMessage msg; if (oldPos.z == 7 && newPos.z >= 8) { RemoveTileThing(msg, oldPos, oldStackPos); } else { msg.addByte(0x6D); msg.addPosition(oldPos); msg.addByte(oldStackPos); msg.addPosition(newPos); } if (newPos.z > oldPos.z) { MoveDownCreature(msg, creature, newPos, oldPos); } else if (newPos.z < oldPos.z) { MoveUpCreature(msg, creature, newPos, oldPos); } if (oldPos.y > newPos.y) { // north, for old x msg.addByte(0x65); GetMapDescription(oldPos.x - Map::maxClientViewportX, newPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX+1)*2, 1, msg); } else if (oldPos.y < newPos.y) { // south, for old x msg.addByte(0x67); GetMapDescription(oldPos.x - Map::maxClientViewportX, newPos.y + (Map::maxClientViewportY+1), newPos.z, (Map::maxClientViewportX+1)*2, 1, msg); } if (oldPos.x < newPos.x) { // east, [with new y] msg.addByte(0x66); GetMapDescription(newPos.x + (Map::maxClientViewportX+1), newPos.y - Map::maxClientViewportY, newPos.z, 1, (Map::maxClientViewportY+1)*2, msg); } else if (oldPos.x > newPos.x) { // west, [with new y] msg.addByte(0x68); GetMapDescription(newPos.x - Map::maxClientViewportX, newPos.y - Map::maxClientViewportY, newPos.z, 1, (Map::maxClientViewportY+1)*2, msg); } writeToOutputBuffer(msg); } } else if (canSee(oldPos) && canSee(creature->getPosition())) { if (teleport || (oldPos.z == 7 && newPos.z >= 8) || oldStackPos >= 10) { sendRemoveTileThing(oldPos, oldStackPos); sendAddCreature(creature, newPos, newStackPos, false); } else { NetworkMessage msg; msg.addByte(0x6D); msg.addPosition(oldPos); msg.addByte(oldStackPos); msg.addPosition(creature->getPosition()); writeToOutputBuffer(msg); } } else if (canSee(oldPos)) { sendRemoveTileThing(oldPos, oldStackPos); } else if (canSee(creature->getPosition())) { sendAddCreature(creature, newPos, newStackPos, false); } } void ProtocolGame::sendInventoryItem(slots_t slot, const Item* item) { NetworkMessage msg; if (item) { msg.addByte(0x78); msg.addByte(slot); msg.addItem(item); } else { msg.addByte(0x79); msg.addByte(slot); } writeToOutputBuffer(msg); } void ProtocolGame::sendItems() { NetworkMessage msg; msg.addByte(0xF5); const std::vector<uint16_t>& inventory = Item::items.getInventory(); msg.add<uint16_t>(inventory.size() + 11); for (uint16_t i = 1; i <= 11; i++) { msg.add<uint16_t>(i); msg.addByte(0); //always 0 msg.add<uint16_t>(1); // always 1 } for (auto clientId : inventory) { msg.add<uint16_t>(clientId); msg.addByte(0); //always 0 msg.add<uint16_t>(1); } writeToOutputBuffer(msg); } void ProtocolGame::sendAddContainerItem(uint8_t cid, uint16_t slot, const Item* item) { NetworkMessage msg; msg.addByte(0x70); msg.addByte(cid); msg.add<uint16_t>(slot); msg.addItem(item); writeToOutputBuffer(msg); } void ProtocolGame::sendUpdateContainerItem(uint8_t cid, uint16_t slot, const Item* item) { NetworkMessage msg; msg.addByte(0x71); msg.addByte(cid); msg.add<uint16_t>(slot); msg.addItem(item); writeToOutputBuffer(msg); } void ProtocolGame::sendRemoveContainerItem(uint8_t cid, uint16_t slot, const Item* lastItem) { NetworkMessage msg; msg.addByte(0x72); msg.addByte(cid); msg.add<uint16_t>(slot); if (lastItem) { msg.addItem(lastItem); } else { msg.add<uint16_t>(0x00); } writeToOutputBuffer(msg); } void ProtocolGame::sendTextWindow(uint32_t windowTextId, Item* item, uint16_t maxlen, bool canWrite) { NetworkMessage msg; msg.addByte(0x96); msg.add<uint32_t>(windowTextId); msg.addItem(item); if (canWrite) { msg.add<uint16_t>(maxlen); msg.addString(item->getText()); } else { const std::string& text = item->getText(); msg.add<uint16_t>(text.size()); msg.addString(text); } const std::string& writer = item->getWriter(); if (!writer.empty()) { msg.addString(writer); } else { msg.add<uint16_t>(0x00); } time_t writtenDate = item->getDate(); if (writtenDate != 0) { msg.addString(formatDateShort(writtenDate)); } else { msg.add<uint16_t>(0x00); } writeToOutputBuffer(msg); } void ProtocolGame::sendTextWindow(uint32_t windowTextId, uint32_t itemId, const std::string& text) { NetworkMessage msg; msg.addByte(0x96); msg.add<uint32_t>(windowTextId); msg.addItem(itemId, 1); msg.add<uint16_t>(text.size()); msg.addString(text); msg.add<uint16_t>(0x00); msg.add<uint16_t>(0x00); writeToOutputBuffer(msg); } void ProtocolGame::sendHouseWindow(uint32_t windowTextId, const std::string& text) { NetworkMessage msg; msg.addByte(0x97); msg.addByte(0x00); msg.add<uint32_t>(windowTextId); msg.addString(text); writeToOutputBuffer(msg); } void ProtocolGame::sendOutfitWindow() { NetworkMessage msg; msg.addByte(0xC8); Outfit_t currentOutfit = player->getDefaultOutfit(); Mount* currentMount = g_game.mounts.getMountByID(player->getCurrentMount()); if (currentMount) { currentOutfit.lookMount = currentMount->clientId; } AddOutfit(msg, currentOutfit); std::vector<ProtocolOutfit> protocolOutfits; if (player->isAccessPlayer()) { static const std::string gamemasterOutfitName = "Gamemaster"; protocolOutfits.emplace_back(gamemasterOutfitName, 75, 0); } const auto& outfits = Outfits::getInstance().getOutfits(player->getSex()); protocolOutfits.reserve(outfits.size()); for (const Outfit& outfit : outfits) { uint8_t addons; if (!player->getOutfitAddons(outfit, addons)) { continue; } protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons); if (protocolOutfits.size() == 100) { // Game client doesn't allow more than 100 outfits break; } } msg.addByte(protocolOutfits.size()); for (const ProtocolOutfit& outfit : protocolOutfits) { msg.add<uint16_t>(outfit.lookType); msg.addString(outfit.name); msg.addByte(outfit.addons); } std::vector<const Mount*> mounts; for (const Mount& mount : g_game.mounts.getMounts()) { if (player->hasMount(&mount)) { mounts.push_back(&mount); } } msg.addByte(mounts.size()); for (const Mount* mount : mounts) { msg.add<uint16_t>(mount->clientId); msg.addString(mount->name); } writeToOutputBuffer(msg); } void ProtocolGame::sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus) { NetworkMessage msg; msg.addByte(0xD3); msg.add<uint32_t>(guid); msg.addByte(newStatus); writeToOutputBuffer(msg); } void ProtocolGame::sendVIP(uint32_t guid, const std::string& name, const std::string& description, uint32_t icon, bool notify, VipStatus_t status) { NetworkMessage msg; msg.addByte(0xD2); msg.add<uint32_t>(guid); msg.addString(name); msg.addString(description); msg.add<uint32_t>(std::min<uint32_t>(10, icon)); msg.addByte(notify ? 0x01 : 0x00); msg.addByte(status); writeToOutputBuffer(msg); } void ProtocolGame::sendVIPEntries() { const std::forward_list<VIPEntry>& vipEntries = IOLoginData::getVIPEntries(player->getAccount()); for (const VIPEntry& entry : vipEntries) { VipStatus_t vipStatus = VIPSTATUS_ONLINE; Player* vipPlayer = g_game.getPlayerByGUID(entry.guid); if (!vipPlayer || vipPlayer->isInGhostMode() || player->isAccessPlayer()) { vipStatus = VIPSTATUS_OFFLINE; } sendVIP(entry.guid, entry.name, entry.description, entry.icon, entry.notify, vipStatus); } } void ProtocolGame::sendSpellCooldown(uint8_t spellId, uint32_t time) { NetworkMessage msg; msg.addByte(0xA4); msg.addByte(spellId); msg.add<uint32_t>(time); writeToOutputBuffer(msg); } void ProtocolGame::sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time) { NetworkMessage msg; msg.addByte(0xA5); msg.addByte(groupId); msg.add<uint32_t>(time); writeToOutputBuffer(msg); } void ProtocolGame::sendModalWindow(const ModalWindow& modalWindow) { NetworkMessage msg; msg.addByte(0xFA); msg.add<uint32_t>(modalWindow.id); msg.addString(modalWindow.title); msg.addString(modalWindow.message); msg.addByte(modalWindow.buttons.size()); for (const auto& it : modalWindow.buttons) { msg.addString(it.first); msg.addByte(it.second); } msg.addByte(modalWindow.choices.size()); for (const auto& it : modalWindow.choices) { msg.addString(it.first); msg.addByte(it.second); } msg.addByte(modalWindow.defaultEscapeButton); msg.addByte(modalWindow.defaultEnterButton); msg.addByte(modalWindow.priority ? 0x01 : 0x00); writeToOutputBuffer(msg); } ////////////// Add common messages void ProtocolGame::AddCreature(NetworkMessage& msg, const Creature* creature, bool known, uint32_t remove) { CreatureType_t creatureType = creature->getType(); const Player* otherPlayer = creature->getPlayer(); if (known) { msg.add<uint16_t>(0x62); msg.add<uint32_t>(creature->getID()); } else { msg.add<uint16_t>(0x61); msg.add<uint32_t>(remove); msg.add<uint32_t>(creature->getID()); msg.addByte(creatureType); msg.addString(creature->getName()); } if (creature->isHealthHidden()) { msg.addByte(0x00); } else { msg.addByte(std::ceil((static_cast<double>(creature->getHealth()) / std::max<int32_t>(creature->getMaxHealth(), 1)) * 100)); } msg.addByte(creature->getDirection()); if (!creature->isInGhostMode() && !creature->isInvisible()) { AddOutfit(msg, creature->getCurrentOutfit()); } else { static Outfit_t outfit; AddOutfit(msg, outfit); } LightInfo lightInfo = creature->getCreatureLight(); msg.addByte(player->isAccessPlayer() ? 0xFF : lightInfo.level); msg.addByte(lightInfo.color); msg.add<uint16_t>(creature->getStepSpeed() / 2); msg.addByte(player->getSkullClient(creature)); msg.addByte(player->getPartyShield(otherPlayer)); if (!known) { msg.addByte(player->getGuildEmblem(otherPlayer)); } if (creatureType == CREATURETYPE_MONSTER) { const Creature* master = creature->getMaster(); if (master) { const Player* masterPlayer = master->getPlayer(); if (masterPlayer) { if (masterPlayer == player) { creatureType = CREATURETYPE_SUMMON_OWN; } else { creatureType = CREATURETYPE_SUMMON_OTHERS; } } } } msg.addByte(creatureType); // Type (for summons) msg.addByte(creature->getSpeechBubble()); msg.addByte(0xFF); // MARK_UNMARKED if (otherPlayer) { msg.add<uint16_t>(otherPlayer->getHelpers()); } else { msg.add<uint16_t>(0x00); } msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01); } void ProtocolGame::AddPlayerStats(NetworkMessage& msg) { msg.addByte(0xA0); msg.add<uint16_t>(std::min<int32_t>(player->getHealth(), std::numeric_limits<uint16_t>::max())); msg.add<uint16_t>(std::min<int32_t>(player->getMaxHealth(), std::numeric_limits<uint16_t>::max())); msg.add<uint32_t>(player->getFreeCapacity()); msg.add<uint32_t>(player->getCapacity()); msg.add<uint64_t>(player->getExperience()); msg.add<uint16_t>(player->getLevel()); msg.addByte(player->getLevelPercent()); msg.add<uint16_t>(100); // base xp gain rate msg.add<uint16_t>(0); // xp voucher msg.add<uint16_t>(0); // low level bonus msg.add<uint16_t>(0); // xp boost msg.add<uint16_t>(100); // stamina multiplier (100 = x1.0) msg.add<uint16_t>(std::min<int32_t>(player->getMana(), std::numeric_limits<uint16_t>::max())); msg.add<uint16_t>(std::min<int32_t>(player->getMaxMana(), std::numeric_limits<uint16_t>::max())); msg.addByte(std::min<uint32_t>(player->getMagicLevel(), std::numeric_limits<uint8_t>::max())); msg.addByte(std::min<uint32_t>(player->getBaseMagicLevel(), std::numeric_limits<uint8_t>::max())); msg.addByte(player->getMagicLevelPercent()); msg.addByte(player->getSoul()); msg.add<uint16_t>(player->getStaminaMinutes()); msg.add<uint16_t>(player->getBaseSpeed() / 2); Condition* condition = player->getCondition(CONDITION_REGENERATION); msg.add<uint16_t>(condition ? condition->getTicks() / 1000 : 0x00); msg.add<uint16_t>(player->getOfflineTrainingTime() / 60 / 1000); msg.add<uint16_t>(0); // xp boost time (seconds) msg.addByte(0); // enables exp boost in the store } void ProtocolGame::AddPlayerSkills(NetworkMessage& msg) { msg.addByte(0xA1); for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { msg.add<uint16_t>(std::min<int32_t>(player->getSkillLevel(i), std::numeric_limits<uint16_t>::max())); msg.add<uint16_t>(player->getBaseSkill(i)); msg.addByte(player->getSkillPercent(i)); } for (uint8_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) { msg.add<uint16_t>(std::min<int32_t>(100, player->varSpecialSkills[i])); msg.add<uint16_t>(0); } } void ProtocolGame::AddOutfit(NetworkMessage& msg, const Outfit_t& outfit) { msg.add<uint16_t>(outfit.lookType); if (outfit.lookType != 0) { msg.addByte(outfit.lookHead); msg.addByte(outfit.lookBody); msg.addByte(outfit.lookLegs); msg.addByte(outfit.lookFeet); msg.addByte(outfit.lookAddons); } else { msg.addItemId(outfit.lookTypeEx); } msg.add<uint16_t>(outfit.lookMount); } void ProtocolGame::AddWorldLight(NetworkMessage& msg, LightInfo lightInfo) { msg.addByte(0x82); msg.addByte((player->isAccessPlayer() ? 0xFF : lightInfo.level)); msg.addByte(lightInfo.color); } void ProtocolGame::AddCreatureLight(NetworkMessage& msg, const Creature* creature) { LightInfo lightInfo = creature->getCreatureLight(); msg.addByte(0x8D); msg.add<uint32_t>(creature->getID()); msg.addByte((player->isAccessPlayer() ? 0xFF : lightInfo.level)); msg.addByte(lightInfo.color); } //tile void ProtocolGame::RemoveTileThing(NetworkMessage& msg, const Position& pos, uint32_t stackpos) { if (stackpos >= 10) { return; } msg.addByte(0x6C); msg.addPosition(pos); msg.addByte(stackpos); } void ProtocolGame::MoveUpCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos) { if (creature != player) { return; } //floor change up msg.addByte(0xBE); //going to surface if (newPos.z == 7) { int32_t skip = -1; GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 5, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 3, skip); //(floor 7 and 6 already set) GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 4, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 4, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 3, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 5, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 2, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 6, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 1, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 7, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 0, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 8, skip); if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } } //underground, going one floor up (still underground) else if (newPos.z > 7) { int32_t skip = -1; GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, oldPos.getZ() - 3, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 3, skip); if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } } //moving up a floor up makes us out of sync //west msg.addByte(0x68); GetMapDescription(oldPos.x - Map::maxClientViewportX, oldPos.y - (Map::maxClientViewportY-1), newPos.z, 1, (Map::maxClientViewportY+1)*2, msg); //north msg.addByte(0x65); GetMapDescription(oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX+1)*2, 1, msg); } void ProtocolGame::MoveDownCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos) { if (creature != player) { return; } //floor change down msg.addByte(0xBF); //going from surface to underground if (newPos.z == 8) { int32_t skip = -1; GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -1, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + 1, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -2, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + 2, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -3, skip); if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } } //going further down else if (newPos.z > oldPos.z && newPos.z > 8 && newPos.z < 14) { int32_t skip = -1; GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + 2, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -3, skip); if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } } //moving down a floor makes us out of sync //east msg.addByte(0x66); GetMapDescription(oldPos.x + Map::maxClientViewportX+1, oldPos.y - (Map::maxClientViewportY+1), newPos.z, 1, ((Map::maxClientViewportY+1)*2), msg); //south msg.addByte(0x67); GetMapDescription(oldPos.x - Map::maxClientViewportX, oldPos.y + (Map::maxClientViewportY+1), newPos.z, ((Map::maxClientViewportX+1)*2), 1, msg); } void ProtocolGame::AddShopItem(NetworkMessage& msg, const ShopInfo& item) { const ItemType& it = Item::items[item.itemId]; msg.add<uint16_t>(it.clientId); if (it.isSplash() || it.isFluidContainer()) { msg.addByte(serverFluidToClient(item.subType)); } else { msg.addByte(0x00); } msg.addString(item.realName); msg.add<uint32_t>(it.weight); msg.add<uint32_t>(item.buyPrice); msg.add<uint32_t>(item.sellPrice); } void ProtocolGame::parseExtendedOpcode(NetworkMessage& msg) { uint8_t opcode = msg.getByte(); const std::string& buffer = msg.getString(); // process additional opcodes via lua script event addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer); }
  10. já foi resolvido, pode fechar...
  11. Galera estou enfrentando um problema no meu site na hora de criação de conta Já procurei em tudo que é lugar, acontece qnd eu desativo os inicias do site com os iniciais ativos a conta cria 100%, mas qnd eu desativo da isso ai + a conta cria.. quem poder me ajudar urgente eu pago wpp : 85991761786 Discord : AlaOGaspar #7077 Error: Incorrect integer value: '' for column 'key' at row 1
  12. então galera, eu quero remover o sistema de icone do meu servidor, e deixar apenas as balls normais e estou disposto a pagar, me chamem no wpp 85994142350 que passo os arquivos para vocês, segue o vídeo abaixo tmj sz WhatsApp_Video_2020-07-12_at_00_42_26.mp4
  13. eu to com esse mesmo problema :x mas, nao consigo conectar no bit, eu consigo colocar o user e a senha Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully mas não da em nada no bit, ele pede minha senha eu coloco e fala pra colocar denovo e fica nessa
  14. .Salve galera, bem rapidinho qual a melhor base atualmente para por online? não vou dizer sem bugs... mas uma base estável que eu possa por on e ir melhorando e claro open source tmj e rep++
  15. Galera alguém sabe onde encontro esse remake de grama? rep+ pra quem me ajudar
  16. tempo de registro please

Informação Importante

Confirmação de Termo