860-870 client support drafts

This commit is contained in:
ErikasKontenis
2020-07-26 13:23:05 +03:00
parent 7cdf6c1b3f
commit 9671a5cce7
32 changed files with 80759 additions and 195 deletions

View File

@@ -187,13 +187,32 @@ void Connection::parsePacket(const boost::system::error_code& error)
return;
}
// 870
//Check packet checksum
uint32_t checksum;
int32_t len = msg.getLength() - msg.getBufferPosition() - NetworkMessage::CHECKSUM_LENGTH;
if (len > 0) {
checksum = adlerChecksum(msg.getBuffer() + msg.getBufferPosition() + NetworkMessage::CHECKSUM_LENGTH, len);
}
else {
checksum = 0;
}
uint32_t recvChecksum = msg.get<uint32_t>();
if (recvChecksum != checksum) {
// it might not have been the checksum, step back
msg.skipBytes(-NetworkMessage::CHECKSUM_LENGTH);
}
// 870
if (!receivedFirst) {
// First message received
receivedFirst = true;
if (!protocol) {
// Game protocol has already been created at this point
protocol = service_port->make_protocol(msg, shared_from_this());
//protocol = service_port->make_protocol(msg, shared_from_this()); 792
protocol = service_port->make_protocol(recvChecksum == checksum, msg, shared_from_this()); //870
if (!protocol) {
close(FORCE_CLOSE);
return;

View File

@@ -363,6 +363,7 @@ enum ClientVersion_t : uint16_t {
CLIENT_VERSION_781 = 781,
CLIENT_VERSION_790 = 790,
CLIENT_VERSION_792 = 792,
CLIENT_VERSION_870 = 860,
};
static constexpr int32_t CHANNEL_GUILD = 0x00;

View File

@@ -3747,6 +3747,8 @@ void Game::addDistanceEffect(const Position& fromPos, const Position& toPos, uin
SpectatorVec spectators;
map.getSpectators(spectators, fromPos, false, true);
map.getSpectators(spectators, toPos, false, true);
spectators.addSpectators(spectators);
addDistanceEffect(spectators, fromPos, toPos, effect);
}

View File

@@ -4409,19 +4409,20 @@ int LuaScriptInterface::luaPositionIsSightClear(lua_State* L)
int LuaScriptInterface::luaPositionSendMagicEffect(lua_State* L)
{
// position:sendMagicEffect(magicEffect[, player = nullptr])
SpectatorVec list;
SpectatorVec spectators;
if (lua_gettop(L) >= 3) {
Player* player = getPlayer(L, 3);
if (player) {
list.insert(player);
spectators.emplace_back(player);
}
}
MagicEffectClasses magicEffect = getNumber<MagicEffectClasses>(L, 2);
const Position& position = getPosition(L, 1);
if (!list.empty()) {
Game::addMagicEffect(list, position, magicEffect);
} else {
if (!spectators.empty()) {
Game::addMagicEffect(spectators, position, magicEffect);
}
else {
g_game.addMagicEffect(position, magicEffect);
}
@@ -4432,20 +4433,21 @@ int LuaScriptInterface::luaPositionSendMagicEffect(lua_State* L)
int LuaScriptInterface::luaPositionSendDistanceEffect(lua_State* L)
{
// position:sendDistanceEffect(positionEx, distanceEffect[, player = nullptr])
SpectatorVec list;
SpectatorVec spectators;
if (lua_gettop(L) >= 4) {
Player* player = getPlayer(L, 4);
if (player) {
list.insert(player);
spectators.emplace_back(player);
}
}
ShootType_t distanceEffect = getNumber<ShootType_t>(L, 3);
const Position& positionEx = getPosition(L, 2);
const Position& position = getPosition(L, 1);
if (!list.empty()) {
Game::addDistanceEffect(list, position, positionEx, distanceEffect);
} else {
if (!spectators.empty()) {
Game::addDistanceEffect(spectators, position, positionEx, distanceEffect);
}
else {
g_game.addDistanceEffect(position, positionEx, distanceEffect);
}
@@ -6873,7 +6875,7 @@ int LuaScriptInterface::luaCreatureTeleportTo(lua_State* L)
int LuaScriptInterface::luaCreatureSay(lua_State* L)
{
// creature:say(text, type[, ghost = false[, target = nullptr[, position]]])
// creature:say(text[, type = TALKTYPE_MONSTER_SAY[, ghost = false[, target = nullptr[, position]]]])
int parameters = lua_gettop(L);
Position position;
@@ -6893,7 +6895,7 @@ int LuaScriptInterface::luaCreatureSay(lua_State* L)
bool ghost = getBoolean(L, 4, false);
SpeakClasses type = getNumber<SpeakClasses>(L, 3);
SpeakClasses type = getNumber<SpeakClasses>(L, 3, TALKTYPE_MONSTER_SAY);
const std::string& text = getString(L, 2);
Creature* creature = getUserdata<Creature>(L, 1);
if (!creature) {
@@ -6901,15 +6903,16 @@ int LuaScriptInterface::luaCreatureSay(lua_State* L)
return 1;
}
SpectatorVec list;
SpectatorVec spectators;
if (target) {
list.insert(target);
spectators.emplace_back(target);
}
if (position.x != 0) {
pushBoolean(L, g_game.internalCreatureSay(creature, type, text, ghost, &list, &position));
} else {
pushBoolean(L, g_game.internalCreatureSay(creature, type, text, ghost, &list));
pushBoolean(L, g_game.internalCreatureSay(creature, type, text, ghost, &spectators, &position));
}
else {
pushBoolean(L, g_game.internalCreatureSay(creature, type, text, ghost, &spectators));
}
return 1;
}

View File

@@ -243,16 +243,18 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
bool teleport = forceTeleport || !newTile.getGround() || !Position::areInRange<1, 1, 0>(oldPos, newPos);
SpectatorVec list;
getSpectators(list, oldPos, true);
getSpectators(list, newPos, true);
SpectatorVec spectators, newPosSpectators;
getSpectators(spectators, oldPos, true);
getSpectators(newPosSpectators, newPos, true);
spectators.addSpectators(newPosSpectators);
std::vector<int32_t> oldStackPosVector;
for (Creature* spectator : list) {
for (Creature* spectator : spectators) {
if (Player* tmpPlayer = spectator->getPlayer()) {
if (tmpPlayer->canSeeCreature(&creature)) {
oldStackPosVector.push_back(oldTile.getClientIndexOfCreature(tmpPlayer, &creature));
} else {
}
else {
oldStackPosVector.push_back(-1);
}
}
@@ -276,20 +278,22 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
if (!teleport) {
if (oldPos.y > newPos.y) {
creature.setDirection(DIRECTION_NORTH);
} else if (oldPos.y < newPos.y) {
}
else if (oldPos.y < newPos.y) {
creature.setDirection(DIRECTION_SOUTH);
}
if (oldPos.x < newPos.x) {
creature.setDirection(DIRECTION_EAST);
} else if (oldPos.x > newPos.x) {
}
else if (oldPos.x > newPos.x) {
creature.setDirection(DIRECTION_WEST);
}
}
//send to client
size_t i = 0;
for (Creature* spectator : list) {
for (Creature* spectator : spectators) {
if (Player* tmpPlayer = spectator->getPlayer()) {
//Use the correct stackpos
int32_t stackpos = oldStackPosVector[i++];
@@ -300,7 +304,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
}
//event method
for (Creature* spectator : list) {
for (Creature* spectator : spectators) {
spectator->onCreatureMove(&creature, &newTile, newPos, &oldTile, oldPos, teleport);
}
@@ -308,7 +312,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
newTile.postAddNotification(&creature, &oldTile, 0);
}
void Map::getSpectatorsInternal(SpectatorVec& list, const Position& centerPos, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY, int32_t minRangeZ, int32_t maxRangeZ, bool onlyPlayers) const
void Map::getSpectatorsInternal(SpectatorVec& spectators, const Position& centerPos, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY, int32_t minRangeZ, int32_t maxRangeZ, bool onlyPlayers) const
{
int_fast16_t min_y = centerPos.y + minRangeY;
int_fast16_t min_x = centerPos.x + minRangeX;
@@ -348,23 +352,25 @@ void Map::getSpectatorsInternal(SpectatorVec& list, const Position& centerPos, i
continue;
}
list.insert(creature);
spectators.emplace_back(creature);
}
leafE = leafE->leafE;
} else {
}
else {
leafE = QTreeNode::getLeafStatic<const QTreeLeafNode*, const QTreeNode*>(&root, nx + FLOOR_SIZE, ny);
}
}
if (leafS) {
leafS = leafS->leafS;
} else {
}
else {
leafS = QTreeNode::getLeafStatic<const QTreeLeafNode*, const QTreeNode*>(&root, startx1, ny + FLOOR_SIZE);
}
}
}
void Map::getSpectators(SpectatorVec& list, const Position& centerPos, bool multifloor /*= false*/, bool onlyPlayers /*= false*/, int32_t minRangeX /*= 0*/, int32_t maxRangeX /*= 0*/, int32_t minRangeY /*= 0*/, int32_t maxRangeY /*= 0*/)
void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, bool multifloor /*= false*/, bool onlyPlayers /*= false*/, int32_t minRangeX /*= 0*/, int32_t maxRangeX /*= 0*/, int32_t minRangeY /*= 0*/, int32_t maxRangeY /*= 0*/)
{
if (centerPos.z >= MAP_MAX_LAYERS) {
return;
@@ -382,11 +388,12 @@ void Map::getSpectators(SpectatorVec& list, const Position& centerPos, bool mult
if (onlyPlayers) {
auto it = playersSpectatorCache.find(centerPos);
if (it != playersSpectatorCache.end()) {
if (!list.empty()) {
const SpectatorVec& cachedList = it->second;
list.insert(cachedList.begin(), cachedList.end());
} else {
list = it->second;
if (!spectators.empty()) {
const SpectatorVec& cachedSpectators = it->second;
spectators.insert(spectators.end(), cachedSpectators.begin(), cachedSpectators.end());
}
else {
spectators = it->second;
}
foundCache = true;
@@ -397,23 +404,26 @@ void Map::getSpectators(SpectatorVec& list, const Position& centerPos, bool mult
auto it = spectatorCache.find(centerPos);
if (it != spectatorCache.end()) {
if (!onlyPlayers) {
if (!list.empty()) {
const SpectatorVec& cachedList = it->second;
list.insert(cachedList.begin(), cachedList.end());
} else {
list = it->second;
if (!spectators.empty()) {
const SpectatorVec& cachedSpectators = it->second;
spectators.insert(spectators.end(), cachedSpectators.begin(), cachedSpectators.end());
}
} else {
const SpectatorVec& cachedList = it->second;
for (Creature* spectator : cachedList) {
else {
spectators = it->second;
}
}
else {
const SpectatorVec& cachedSpectators = it->second;
for (Creature* spectator : cachedSpectators) {
if (spectator->getPlayer()) {
list.insert(spectator);
spectators.emplace_back(spectator);
}
}
}
foundCache = true;
} else {
}
else {
cacheResult = true;
}
}
@@ -430,28 +440,33 @@ void Map::getSpectators(SpectatorVec& list, const Position& centerPos, bool mult
//8->15
minRangeZ = std::max<int32_t>(centerPos.getZ() - 2, 0);
maxRangeZ = std::min<int32_t>(centerPos.getZ() + 2, MAP_MAX_LAYERS - 1);
} else if (centerPos.z == 6) {
}
else if (centerPos.z == 6) {
minRangeZ = 0;
maxRangeZ = 8;
} else if (centerPos.z == 7) {
}
else if (centerPos.z == 7) {
minRangeZ = 0;
maxRangeZ = 9;
} else {
}
else {
minRangeZ = 0;
maxRangeZ = 7;
}
} else {
}
else {
minRangeZ = centerPos.z;
maxRangeZ = centerPos.z;
}
getSpectatorsInternal(list, centerPos, minRangeX, maxRangeX, minRangeY, maxRangeY, minRangeZ, maxRangeZ, onlyPlayers);
getSpectatorsInternal(spectators, centerPos, minRangeX, maxRangeX, minRangeY, maxRangeY, minRangeZ, maxRangeZ, onlyPlayers);
if (cacheResult) {
if (onlyPlayers) {
playersSpectatorCache[centerPos] = list;
} else {
spectatorCache[centerPos] = list;
playersSpectatorCache[centerPos] = spectators;
}
else {
spectatorCache[centerPos] = spectators;
}
}
}

View File

@@ -34,11 +34,13 @@ class NetworkMessage
typedef uint16_t MsgSize_t;
// Headers:
// 2 bytes for unencrypted message size
// 4 bytes for checksum
// 2 bytes for encrypted message size
static constexpr MsgSize_t INITIAL_BUFFER_POSITION = 4;
static constexpr MsgSize_t INITIAL_BUFFER_POSITION = 8;
enum { HEADER_LENGTH = 2 };
enum { CHECKSUM_LENGTH = 4 };
enum { XTEA_MULTIPLE = 8 };
enum { MAX_BODY_LENGTH = NETWORKMESSAGE_MAXSIZE - HEADER_LENGTH - XTEA_MULTIPLE };
enum { MAX_BODY_LENGTH = NETWORKMESSAGE_MAXSIZE - HEADER_LENGTH - CHECKSUM_LENGTH - XTEA_MULTIPLE };
enum { MAX_PROTOCOL_BODY_LENGTH = MAX_BODY_LENGTH - 10 };
NetworkMessage() = default;

View File

@@ -185,11 +185,14 @@ void mainLoader(int, char*[], ServiceManager* services)
else if (clientVersion == 792) {
g_game.setClientVersion(CLIENT_VERSION_792);
}
else if (clientVersion == 870) {
g_game.setClientVersion(CLIENT_VERSION_870);
}
else {
std::cout << std::endl;
std::ostringstream ss;
ss << "> ERROR: Unknown client version: " << g_config.getNumber(ConfigManager::CLIENT_VERSION) << ", valid client versions are: 780, 781, 790, 792.";
ss << "> ERROR: Unknown client version: " << g_config.getNumber(ConfigManager::CLIENT_VERSION) << ", valid client versions are: 780, 781, 790, 792, 870.";
startupErrorMessage(ss.str());
return;
}

View File

@@ -1,6 +1,6 @@
/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
*
* 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
@@ -28,76 +28,80 @@ class Protocol;
class OutputMessage : public NetworkMessage
{
public:
OutputMessage() = default;
public:
OutputMessage() = default;
// non-copyable
OutputMessage(const OutputMessage&) = delete;
OutputMessage& operator=(const OutputMessage&) = delete;
// non-copyable
OutputMessage(const OutputMessage&) = delete;
OutputMessage& operator=(const OutputMessage&) = delete;
uint8_t* getOutputBuffer() {
return buffer + outputBufferStart;
}
uint8_t* getOutputBuffer() {
return buffer + outputBufferStart;
}
void writeMessageLength() {
add_header(info.length);
}
void writeMessageLength() {
add_header(info.length);
}
void addCryptoHeader() {
writeMessageLength();
}
void addCryptoHeader(bool addChecksum) {
if (addChecksum) {
add_header(adlerChecksum(buffer + outputBufferStart, info.length));
}
inline void append(const NetworkMessage& msg) {
auto msgLen = msg.getLength();
memcpy(buffer + info.position, msg.getBuffer() + 4, msgLen);
info.length += msgLen;
info.position += msgLen;
}
writeMessageLength();
}
inline void append(const OutputMessage_ptr& msg) {
auto msgLen = msg->getLength();
memcpy(buffer + info.position, msg->getBuffer() + 4, msgLen);
info.length += msgLen;
info.position += msgLen;
}
void append(const NetworkMessage& msg) {
auto msgLen = msg.getLength();
memcpy(buffer + info.position, msg.getBuffer() + 8, msgLen);
info.length += msgLen;
info.position += msgLen;
}
protected:
template <typename T>
inline void add_header(T add) {
assert(outputBufferStart >= sizeof(T));
outputBufferStart -= sizeof(T);
memcpy(buffer + outputBufferStart, &add, sizeof(T));
//added header size to the message size
info.length += sizeof(T);
}
void append(const OutputMessage_ptr& msg) {
auto msgLen = msg->getLength();
memcpy(buffer + info.position, msg->getBuffer() + 8, msgLen);
info.length += msgLen;
info.position += msgLen;
}
MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION;
private:
template <typename T>
void add_header(T add) {
assert(outputBufferStart >= sizeof(T));
outputBufferStart -= sizeof(T);
memcpy(buffer + outputBufferStart, &add, sizeof(T));
//added header size to the message size
info.length += sizeof(T);
}
MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION;
};
class OutputMessagePool
{
public:
// non-copyable
OutputMessagePool(const OutputMessagePool&) = delete;
OutputMessagePool& operator=(const OutputMessagePool&) = delete;
public:
// non-copyable
OutputMessagePool(const OutputMessagePool&) = delete;
OutputMessagePool& operator=(const OutputMessagePool&) = delete;
static OutputMessagePool& getInstance() {
static OutputMessagePool instance;
return instance;
}
static OutputMessagePool& getInstance() {
static OutputMessagePool instance;
return instance;
}
void sendAll();
void scheduleSendAll();
void sendAll();
void scheduleSendAll();
static OutputMessage_ptr getOutputMessage();
static OutputMessage_ptr getOutputMessage();
void addProtocolToAutosend(Protocol_ptr protocol);
void removeProtocolFromAutosend(const Protocol_ptr& protocol);
private:
OutputMessagePool() = default;
//NOTE: A vector is used here because this container is mostly read
//and relatively rarely modified (only when a client connects/disconnects)
std::vector<Protocol_ptr> bufferedProtocols;
void addProtocolToAutosend(Protocol_ptr protocol);
void removeProtocolFromAutosend(const Protocol_ptr& protocol);
private:
OutputMessagePool() = default;
//NOTE: A vector is used here because this container is mostly read
//and relatively rarely modified (only when a client connects/disconnects)
std::vector<Protocol_ptr> bufferedProtocols;
};

View File

@@ -579,6 +579,21 @@ bool Player::canSeeCreature(const Creature* creature) const
return true;
}
bool Player::canWalkthroughEx(const Creature* creature) const
{
if (group->access) {
return true;
}
const Player* player = creature->getPlayer();
if (!player) {
return false;
}
const Tile* playerTile = player->getTile();
return playerTile && (playerTile->hasFlag(TILESTATE_PROTECTIONZONE) || player->getLevel() <= static_cast<uint32_t>(g_config.getNumber(ConfigManager::PROTECTION_LEVEL)));
}
void Player::onReceiveMail(uint32_t townId) const
{
if (isNearDepotBox(townId)) {

View File

@@ -439,6 +439,8 @@ class Player final : public Creature, public Cylinder
bool canSee(const Position& pos) const final;
bool canSeeCreature(const Creature* creature) const final;
bool canWalkthroughEx(const Creature* creature) const;
RaceType_t getRace() const final {
return RACE_BLOOD;
}
@@ -689,6 +691,11 @@ class Player final : public Creature, public Cylinder
client->sendCreatureLight(creature);
}
}
void sendCreatureWalkthrough(const Creature* creature, bool walkthrough) {
if (client) {
client->sendCreatureWalkthrough(creature, walkthrough);
}
}
void sendCreatureShield(const Creature* creature) {
if (client) {
client->sendCreatureShield(creature);

View File

@@ -22,6 +22,7 @@
#include "protocol.h"
#include "outputmessage.h"
#include "rsa.h"
#include "xtea.h"
extern RSA g_RSA;
@@ -32,7 +33,7 @@ void Protocol::onSendMessage(const OutputMessage_ptr& msg) const
if (encryptionEnabled) {
XTEA_encrypt(*msg);
msg->addCryptoHeader();
msg->addCryptoHeader(checksumEnabled);
}
}
}
@@ -61,73 +62,27 @@ OutputMessage_ptr Protocol::getOutputBuffer(int32_t size)
void Protocol::XTEA_encrypt(OutputMessage& msg) const
{
const uint32_t delta = 0x61C88647;
// The message must be a multiple of 8
size_t paddingBytes = msg.getLength() % 8;
size_t paddingBytes = msg.getLength() % 8u;
if (paddingBytes != 0) {
msg.addPaddingBytes(8 - paddingBytes);
}
uint8_t* buffer = msg.getOutputBuffer();
const size_t messageLength = msg.getLength();
size_t readPos = 0;
const uint32_t k[] = { key[0], key[1], key[2], key[3] };
while (readPos < messageLength) {
uint32_t v0;
memcpy(&v0, buffer + readPos, 4);
uint32_t v1;
memcpy(&v1, buffer + readPos + 4, 4);
uint32_t sum = 0;
for (int32_t i = 32; --i >= 0;) {
v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]);
sum -= delta;
v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[(sum >> 11) & 3]);
}
memcpy(buffer + readPos, &v0, 4);
readPos += 4;
memcpy(buffer + readPos, &v1, 4);
readPos += 4;
}
xtea::encrypt(buffer, msg.getLength(), key);
}
bool Protocol::XTEA_decrypt(NetworkMessage& msg) const
{
if (((msg.getLength() - 2) & 7) != 0) {
if (((msg.getLength() - 6) & 7) != 0) {
return false;
}
const uint32_t delta = 0x61C88647;
uint8_t* buffer = msg.getBuffer() + msg.getBufferPosition();
const size_t messageLength = (msg.getLength() - 2);
size_t readPos = 0;
const uint32_t k[] = { key[0], key[1], key[2], key[3] };
while (readPos < messageLength) {
uint32_t v0;
memcpy(&v0, buffer + readPos, 4);
uint32_t v1;
memcpy(&v1, buffer + readPos + 4, 4);
xtea::decrypt(buffer, msg.getLength() - 6, key);
uint32_t sum = 0xC6EF3720;
for (int32_t i = 32; --i >= 0;) {
v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[(sum >> 11) & 3]);
sum += delta;
v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]);
}
memcpy(buffer + readPos, &v0, 4);
readPos += 4;
memcpy(buffer + readPos, &v1, 4);
readPos += 4;
}
int innerLength = msg.get<uint16_t>();
if (innerLength > msg.getLength() - 4) {
uint16_t innerLength = msg.get<uint16_t>();
if (innerLength + 8 > msg.getLength()) {
return false;
}
@@ -137,7 +92,7 @@ bool Protocol::XTEA_decrypt(NetworkMessage& msg) const
bool Protocol::RSA_decrypt(NetworkMessage& msg)
{
if ((msg.getLength() - msg.getBufferPosition()) != 128) {
if ((msg.getLength() - msg.getBufferPosition()) < 128) {
return false;
}

View File

@@ -21,6 +21,7 @@
#define FS_PROTOCOL_H_D71405071ACF4137A4B1203899DE80E1
#include "connection.h"
#include "xtea.h"
class Protocol : public std::enable_shared_from_this<Protocol>
{
@@ -71,8 +72,11 @@ protected:
void enableXTEAEncryption() {
encryptionEnabled = true;
}
void setXTEAKey(const uint32_t* key) {
memcpy(this->key, key, sizeof(*key) * 4);
void setXTEAKey(xtea::key key) {
this->key = std::move(key);
}
void disableChecksum() {
checksumEnabled = false;
}
void XTEA_encrypt(OutputMessage& msg) const;
@@ -89,8 +93,9 @@ protected:
OutputMessage_ptr outputBuffer;
private:
const ConnectionWeak_ptr connection;
uint32_t key[4];
xtea::key key;
bool encryptionEnabled;
bool checksumEnabled = true;
bool rawMessages;
};

View File

@@ -247,13 +247,13 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
return;
}
uint32_t key[4];
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(key);
setXTEAKey(std::move(key));
if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
NetworkMessage opcodeMessage;
@@ -265,7 +265,8 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
msg.skipBytes(1); // gamemaster flag
uint32_t accountNumber = msg.get<uint32_t>();
std::string accountName = msg.getString();
uint32_t accountNumber = 1234567;
std::string characterName = msg.getString();
std::string password = msg.getString();
@@ -318,8 +319,30 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
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
@@ -461,8 +484,7 @@ void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg)
count++;
if (count == 9 && tile->getPosition() == player->getPosition()) {
break;
}
else if (count == 10) {
} else if (count == 10) {
return;
}
}
@@ -565,7 +587,7 @@ void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool& known, uint32_t& remo
known = false;
if (knownCreatureSet.size() > 150) {
if (knownCreatureSet.size() > 250) {
// Look for a creature to remove
for (auto it = knownCreatureSet.begin(), end = knownCreatureSet.end(); it != end; ++it) {
Creature* creature = g_game.getCreatureByID(*it);
@@ -671,7 +693,7 @@ void ProtocolGame::parseOpenPrivateChannel(NetworkMessage& msg)
void ProtocolGame::parseAutoWalk(NetworkMessage& msg)
{
uint8_t numdirs = msg.getByte();
if (numdirs == 0 || (msg.getBufferPosition() + numdirs) != (msg.getLength() + 4)) {
if (numdirs == 0 || (msg.getBufferPosition() + numdirs) != (msg.getLength() + 8)) {
return;
}
@@ -1018,6 +1040,19 @@ void ProtocolGame::sendWorldLight(const LightInfo& 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)) {
@@ -1601,8 +1636,7 @@ void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos
msg.addByte(0x0A);
msg.add<uint32_t>(player->getID());
msg.addByte(0x32); // beat duration (50)
msg.addByte(0x00);
msg.add<uint16_t>(0x32); // beat duration (50)
// can report bugs?
if (player->getAccountType() >= ACCOUNT_TYPE_TUTOR) {
@@ -1924,6 +1958,7 @@ void ProtocolGame::AddCreature(NetworkMessage& msg, const Creature* creature, bo
msg.addByte(player->getSkullClient(creature));
msg.addByte(player->getPartyShield(otherPlayer));
msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01);
}
void ProtocolGame::AddPlayerStats(NetworkMessage& msg)

View File

@@ -52,7 +52,8 @@ class ProtocolGame final : public Protocol
// static protocol information
enum { server_sends_first = true };
enum { protocol_identifier = 0 }; // Not required as we send first
enum { use_checksum = true };
static const char* protocol_name() {
return "gameworld protocol";
}
@@ -169,6 +170,7 @@ class ProtocolGame final : public Protocol
void sendTextMessage(const TextMessage& message);
void sendAnimatedText(const Position& pos, uint8_t color, const std::string& text);
void sendCreatureWalkthrough(const Creature* creature, bool walkthrough);
void sendCreatureShield(const Creature* creature);
void sendCreatureSkull(const Creature* creature);

View File

@@ -128,13 +128,13 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg)
return;
}
uint32_t key[4];
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(key);
setXTEAKey(std::move(key));
if (!isProtocolAllowed(version)) {
std::ostringstream ss;
@@ -170,11 +170,12 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg)
return;
}
uint32_t accountNumber = msg.get<uint32_t>();
if (!accountNumber) {
disconnectClient("Invalid account number.", version);
return;
}
//uint32_t accountNumber = msg.get<uint32_t>();
std::string accountName = msg.getString();
//if (!accountNumber) {
// disconnectClient("Invalid account number.", version);
// return;
// }
std::string password = msg.getString();
if (password.empty()) {
@@ -183,7 +184,7 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg)
}
auto thisPtr = std::static_pointer_cast<ProtocolLogin>(shared_from_this());
g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, version)));
g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, 1234567, password, version)));
}

View File

@@ -32,6 +32,7 @@ class ProtocolLogin : public Protocol
// static protocol information
enum {server_sends_first = false};
enum {protocol_identifier = 0x01};
enum { use_checksum = true };
static const char* protocol_name() {
return "login protocol";
}

View File

@@ -29,6 +29,7 @@ class ProtocolStatus final : public Protocol
// static protocol information
enum {server_sends_first = false};
enum {protocol_identifier = 0xFF};
enum { use_checksum = false };
static const char* protocol_name() {
return "status protocol";
}

View File

@@ -136,7 +136,7 @@ void ServicePort::onAccept(Connection_ptr connection, const boost::system::error
}
}
Protocol_ptr ServicePort::make_protocol(NetworkMessage& msg, const Connection_ptr& connection) const
Protocol_ptr ServicePort::make_protocol(bool checksummed, NetworkMessage& msg, const Connection_ptr& connection) const
{
uint8_t protocolID = msg.getByte();
for (auto& service : services) {
@@ -144,7 +144,9 @@ Protocol_ptr ServicePort::make_protocol(NetworkMessage& msg, const Connection_pt
continue;
}
return service->make_protocol(connection);
if ((checksummed && service->is_checksummed()) || !service->is_checksummed()) {
return service->make_protocol(connection);
}
}
return nullptr;
}

View File

@@ -29,6 +29,7 @@ class ServiceBase
{
public:
virtual bool is_single_socket() const = 0;
virtual bool is_checksummed() const = 0;
virtual uint8_t get_protocol_identifier() const = 0;
virtual const char* get_protocol_name() const = 0;
@@ -42,6 +43,9 @@ public:
bool is_single_socket() const final {
return ProtocolType::server_sends_first;
}
bool is_checksummed() const override {
return ProtocolType::use_checksum;
}
uint8_t get_protocol_identifier() const final {
return ProtocolType::protocol_identifier;
}
@@ -71,7 +75,7 @@ public:
std::string get_protocol_names() const;
bool add_service(const Service_ptr& new_svc);
Protocol_ptr make_protocol(NetworkMessage& msg, const Connection_ptr& connection) const;
Protocol_ptr make_protocol(bool checksummed, NetworkMessage& msg, const Connection_ptr& connection) const;
void onStopServer();
void onAccept(Connection_ptr connection, const boost::system::error_code& error);

83
src/spectators.h Normal file
View File

@@ -0,0 +1,83 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
*
* 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.
*/
#ifndef FS_SPECTATORS_H_D78A7CCB7080406E8CAA6B1D31D3DA71
#define FS_SPECTATORS_H_D78A7CCB7080406E8CAA6B1D31D3DA71
#include <vector>
class Creature;
class SpectatorVec
{
using Vec = std::vector<Creature*>;
using Iterator = Vec::iterator;
using ConstIterator = Vec::const_iterator;
public:
SpectatorVec() {
vec.reserve(32);
}
void addSpectators(const SpectatorVec& spectators) {
const size_t size = vec.size();
for (Creature* spectator : spectators.vec) {
bool duplicate = false;
for (size_t i = 0; i < size; ++i) {
if (vec[i] == spectator) {
duplicate = true;
break;
}
}
if (!duplicate) {
vec.emplace_back(spectator);
}
}
}
void erase(Creature* spectator) {
for (size_t i = 0, len = vec.size(); i < len; i++) {
if (vec[i] == spectator) {
Creature* tmp = vec[len - 1];
vec[len - 1] = vec[i];
vec[i] = tmp;
vec.pop_back();
break;
}
}
}
inline size_t size() const { return vec.size(); }
inline bool empty() const { return vec.empty(); }
inline Iterator begin() { return vec.begin(); }
inline ConstIterator begin() const { return vec.begin(); }
inline ConstIterator cbegin() const { return vec.cbegin(); }
inline Iterator end() { return vec.end(); }
inline ConstIterator end() const { return vec.end(); }
inline ConstIterator cend() const { return vec.cend(); }
inline void emplace_back(Creature* c) { return vec.emplace_back(c); }
template<class InputIterator>
inline void insert(Iterator pos, InputIterator first, InputIterator last) { vec.insert(pos, first, last); }
private:
Vec vec;
};
#endif

View File

@@ -25,6 +25,7 @@
#include "cylinder.h"
#include "item.h"
#include "tools.h"
#include "spectators.h"
class Creature;
class Teleport;
@@ -36,7 +37,6 @@ class BedItem;
typedef std::vector<Creature*> CreatureVector;
typedef std::vector<Item*> ItemVector;
typedef std::unordered_set<Creature*> SpectatorVec;
enum tileflags_t : uint32_t {
TILESTATE_NONE = 0,

View File

@@ -1258,6 +1258,9 @@ std::string getClientVersionString(ClientVersion_t version)
case CLIENT_VERSION_792:
result = "7.92";
break;
case CLIENT_VERSION_870:
result = "8.60";
break;
default:
result = "Unknown";
break;
@@ -1265,3 +1268,29 @@ std::string getClientVersionString(ClientVersion_t version)
return result;
}
uint32_t adlerChecksum(const uint8_t* data, size_t length)
{
if (length > NETWORKMESSAGE_MAXSIZE) {
return 0;
}
const uint16_t adler = 65521;
uint32_t a = 1, b = 0;
while (length > 0) {
size_t tmp = length > 5552 ? 5552 : length;
length -= tmp;
do {
a += *data++;
b += a;
} while (--tmp);
a %= adler;
b %= adler;
}
return (b << 16) | a;
}

View File

@@ -103,6 +103,8 @@ void getFilesInDirectory(const boost::filesystem::path& root, const std::string&
std::string getClientVersionString(uint32_t version);
std::string getClientVersionString(ClientVersion_t version);
uint32_t adlerChecksum(const uint8_t* data, size_t length);
inline int64_t OTSYS_TIME()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();