Merge remote-tracking branch 'refs/remotes/origin/main'

This commit is contained in:
wincent
2024-01-16 21:46:31 +01:00
597 changed files with 12170 additions and 9657 deletions

View File

@@ -1,5 +1,4 @@
#include "AuthPackets.h"
#include "PacketUtils.h"
#include "BitStreamUtils.h"
#include "dNetCommon.h"
@@ -11,13 +10,9 @@
#include "SHA512.h"
#include "GeneralUtils.h"
#ifdef _WIN32
#include <bcrypt/BCrypt.hpp>
#else
#include <bcrypt.h>
#endif
#include <BitStream.h>
#include "BitStream.h"
#include <future>
#include "Game.h"
@@ -27,245 +22,286 @@
#include "eConnectionType.h"
#include "eServerMessageType.h"
#include "eMasterMessageType.h"
#include "eGameMasterLevel.h"
#include "StringifiedEnum.h"
namespace {
std::vector<uint32_t> claimCodes;
}
void Stamp::Serialize(RakNet::BitStream* outBitStream){
outBitStream->Write(type);
outBitStream->Write(value);
outBitStream->Write(timestamp);
};
void AuthPackets::LoadClaimCodes() {
if(!claimCodes.empty()) return;
auto rcstring = Game::config->GetValue("rewardcodes");
auto codestrings = GeneralUtils::SplitString(rcstring, ',');
for(auto const &codestring: codestrings){
uint32_t code = -1;
if(GeneralUtils::TryParse(codestring, code) && code != -1){
claimCodes.push_back(code);
}
}
}
void AuthPackets::HandleHandshake(dServer* server, Packet* packet) {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
CINSTREAM_SKIP_HEADER
uint32_t clientVersion = 0;
inStream.Read(clientVersion);
inStream.IgnoreBytes(4);
ServiceId serviceId;
inStream.Read(serviceId);
if (serviceId != ServiceId::Client) LOG("WARNING: Service ID is not a Client!");
uint32_t processID;
inStream.Read(processID);
uint16_t port;
inStream.Read(port);
if (port != packet->systemAddress.port) LOG("WARNING: Port written in packet does not match the port the client is connecting over!");
inStream.IgnoreBytes(33);
LOG_DEBUG("Client Data [Version: %i, Service: %s, Process: %u, Port: %u, Sysaddr Port: %u]", clientVersion, StringifiedEnum::ToString(serviceId).data(), processID, port, packet->systemAddress.port);
LOG("Received client version: %i", clientVersion);
SendHandshake(server, packet->systemAddress, server->GetIP(), server->GetPort(), server->GetServerType());
}
void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, const std::string& nextServerIP, uint16_t nextServerPort, const ServerType serverType) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::SERVER, eServerMessageType::VERSION_CONFIRM);
uint32_t netVersion;
if (!GeneralUtils::TryParse(Game::config->GetValue("client_net_version"), netVersion)) {
LOG("Failed to parse client_net_version. Cannot authenticate to %s:%i", nextServerIP.c_str(), nextServerPort);
return;
}
bitStream.Write<uint32_t>(netVersion);
bitStream.Write(uint32_t(0x93));
uint32_t clientNetVersion = 171022;
const auto clientNetVersionString = Game::config->GetValue("client_net_version");
if (!clientNetVersionString.empty()) GeneralUtils::TryParse(clientNetVersionString, clientNetVersion);
if (serverType == ServerType::Auth) bitStream.Write(uint32_t(1)); //Conn: auth
else bitStream.Write(uint32_t(4)); //Conn: world
bitStream.Write<uint32_t>(clientNetVersion);
bitStream.Write<uint32_t>(861228100);
bitStream.Write(uint32_t(0)); //Server process ID
bitStream.Write(nextServerPort);
if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth);
else if (serverType == ServerType::World) bitStream.Write(ServiceId::World);
else bitStream.Write(ServiceId::General);
bitStream.Write<uint32_t>(774909490);
server->Send(&bitStream, sysAddr, false);
}
void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) {
std::string username = PacketUtils::ReadString(8, packet, true);
std::string password = PacketUtils::ReadString(0x4A, packet, true);
const char* szUsername = username.c_str();
CINSTREAM_SKIP_HEADER;
std::vector<Stamp> stamps;
stamps.emplace_back(eStamps::PASSPORT_AUTH_START, 0);
LUWString usernameLUString;
inStream.Read(usernameLUString);
const auto username = usernameLUString.GetAsString();
LUWString password(41);
inStream.Read(password);
LanguageCodeID locale_id;
inStream.Read(locale_id);
LOG_DEBUG("Locale ID: %s", StringifiedEnum::ToString(locale_id).data());
ClientOS clientOS;
inStream.Read(clientOS);
LOG_DEBUG("Operating System: %s", StringifiedEnum::ToString(clientOS).data());
stamps.emplace_back(eStamps::PASSPORT_AUTH_CLIENT_OS, 0);
LUWString memoryStats(256);
inStream.Read(memoryStats);
LOG_DEBUG("Memory Stats [%s]", memoryStats.GetAsString().c_str());
LUWString videoCard(128);
inStream.Read(videoCard);
LOG_DEBUG("VideoCard Info: [%s]", videoCard.GetAsString().c_str());
// Processor/CPU info
uint32_t numOfProcessors;
inStream.Read(numOfProcessors);
uint32_t processorType;
inStream.Read(processorType);
uint16_t processorLevel;
inStream.Read(processorLevel);
uint16_t processorRevision;
inStream.Read(processorRevision);
LOG_DEBUG("CPU Info: [#Processors: %i, Processor Type: %i, Processor Level: %i, Processor Revision: %i]", numOfProcessors, processorType, processorLevel, processorRevision);
// OS Info
uint32_t osVersionInfoSize;
inStream.Read(osVersionInfoSize);
uint32_t majorVersion;
inStream.Read(majorVersion);
uint32_t minorVersion;
inStream.Read(minorVersion);
uint32_t buildNumber;
inStream.Read(buildNumber);
uint32_t platformID;
inStream.Read(platformID);
LOG_DEBUG("OS Info: [Size: %i, Major: %i, Minor %i, Buid#: %i, platformID: %i]", osVersionInfoSize, majorVersion, minorVersion, buildNumber, platformID);
// Fetch account details
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT password, banned, locked, play_key_id, gm_level FROM accounts WHERE name=? LIMIT 1;");
stmt->setString(1, szUsername);
auto accountInfo = Database::Get()->GetAccountInfo(username);
sql::ResultSet* res = stmt->executeQuery();
if (res->rowsCount() == 0) {
LOG("No user found!");
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::INVALID_USER, "", "", 2001, username);
if (!accountInfo) {
LOG("No user by name %s found!", username.c_str());
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::INVALID_USER, "", "", 2001, username, stamps);
return;
}
std::string sqlPass = "";
bool sqlBanned = false;
bool sqlLocked = false;
uint32_t sqlPlayKey = 0;
uint32_t sqlGmLevel = 0;
while (res->next()) {
sqlPass = res->getString(1).c_str();
sqlBanned = res->getBoolean(2);
sqlLocked = res->getBoolean(3);
sqlPlayKey = res->getInt(4);
sqlGmLevel = res->getInt(5);
}
delete stmt;
delete res;
//If we aren't running in live mode, then only GMs are allowed to enter:
const auto& closedToNonDevs = Game::config->GetValue("closed_to_non_devs");
if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && sqlGmLevel == 0) {
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username);
if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) {
stamps.emplace_back(eStamps::GM_REQUIRED, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username, stamps);
return;
}
if (Game::config->GetValue("dont_use_keys") != "1") {
if (Game::config->GetValue("dont_use_keys") != "1" && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) {
//Check to see if we have a play key:
if (sqlPlayKey == 0 && sqlGmLevel == 0) {
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username);
if (accountInfo->playKeyId == 0) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username, stamps);
LOG("User %s tried to log in, but they don't have a play key.", username.c_str());
return;
}
//Check if the play key is _valid_:
auto keyCheckStmt = Database::CreatePreppedStmt("SELECT active FROM `play_keys` WHERE id=?");
keyCheckStmt->setInt(1, sqlPlayKey);
auto keyRes = keyCheckStmt->executeQuery();
bool isKeyActive = false;
auto playKeyStatus = Database::Get()->IsPlaykeyActive(accountInfo->playKeyId);
if (keyRes->rowsCount() == 0 && sqlGmLevel == 0) {
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username);
if (!playKeyStatus) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a valid play key associated with it!", "", 2001, username, stamps);
return;
}
while (keyRes->next()) {
isKeyActive = (bool)keyRes->getInt(1);
}
if (!isKeyActive && sqlGmLevel == 0) {
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your play key has been disabled.", "", 2001, username);
if (!playKeyStatus.value()) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your play key has been disabled.", "", 2001, username, stamps);
LOG("User %s tried to log in, but their play key was disabled", username.c_str());
return;
}
} else if (Game::config->GetValue("dont_use_keys") == "1" || accountInfo->maxGmLevel > eGameMasterLevel::CIVILIAN){
stamps.emplace_back(eStamps::PASSPORT_AUTH_BYPASS, 1);
}
if (sqlBanned) {
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, "", "", 2001, username); return;
if (accountInfo->banned) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, "", "", 2001, username, stamps);
return;
}
if (sqlLocked) {
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username); return;
if (accountInfo->locked) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username, stamps);
return;
}
/*
* Updated hashing method:
* First attempt bcrypt.
* If that fails, fallback to old method and setup bcrypt for new login.
*/
bool loginSuccess = true;
int32_t bcryptState = ::bcrypt_checkpw(password.c_str(), sqlPass.c_str());
if (bcryptState != 0) {
// Fallback on old method
std::string oldPassword = sha512(password + username);
if (sqlPass != oldPassword) {
loginSuccess = false;
} else {
// Generate new hash for bcrypt
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
bcryptState = ::bcrypt_gensalt(12, salt);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
sql::PreparedStatement* accountUpdate = Database::CreatePreppedStmt("UPDATE accounts SET password = ? WHERE name = ? LIMIT 1;");
accountUpdate->setString(1, std::string(hash, BCRYPT_HASHSIZE).c_str());
accountUpdate->setString(2, szUsername);
accountUpdate->executeUpdate();
}
} else {
// Login success with bcrypt
}
bool loginSuccess = ::bcrypt_checkpw(password.GetAsString().c_str(), accountInfo->bcryptPassword.c_str()) == 0;
if (!loginSuccess) {
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username);
stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1);
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username, stamps);
LOG("Wrong password used");
} else {
SystemAddress system = packet->systemAddress; //Copy the sysAddr before the Packet gets destroyed from main
if (!server->GetIsConnectedToMaster()) {
AuthPackets::SendLoginResponse(server, system, eLoginResponse::GENERAL_FAILED, "", "", 0, username);
stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_DISCONNECT, 1);
AuthPackets::SendLoginResponse(server, system, eLoginResponse::GENERAL_FAILED, "", "", 0, username, stamps);
return;
}
ZoneInstanceManager::Instance()->RequestZoneTransfer(server, 0, 0, false, [system, server, username](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string zoneIP, uint16_t zonePort) {
AuthPackets::SendLoginResponse(server, system, eLoginResponse::SUCCESS, "", zoneIP, zonePort, username);
stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_SESSION_CONFIRM_TO_AUTH, 1);
ZoneInstanceManager::Instance()->RequestZoneTransfer(server, 0, 0, false, [system, server, username, stamps](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string zoneIP, uint16_t zonePort) mutable {
AuthPackets::SendLoginResponse(server, system, eLoginResponse::SUCCESS, "", zoneIP, zonePort, username, stamps);
});
}
for(auto const code: claimCodes){
Database::Get()->InsertRewardCode(accountInfo->id, code);
}
}
void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username) {
RakNet::BitStream packet;
BitStreamUtils::WriteHeader(packet, eConnectionType::CLIENT, eClientMessageType::LOGIN_RESPONSE);
void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username, std::vector<Stamp>& stamps) {
stamps.emplace_back(eStamps::PASSPORT_AUTH_IM_LOGIN_START, 1);
RakNet::BitStream loginResponse;
BitStreamUtils::WriteHeader(loginResponse, eConnectionType::CLIENT, eClientMessageType::LOGIN_RESPONSE);
packet.Write(static_cast<uint8_t>(responseCode));
loginResponse.Write<uint8_t>(GeneralUtils::CastUnderlyingType(responseCode));
// Event Gating
packet.Write(LUString("Talk_Like_A_Pirate"));
packet.Write(LUString(""));
packet.Write(LUString(""));
packet.Write(LUString(""));
packet.Write(LUString(""));
packet.Write(LUString(""));
packet.Write(LUString(""));
packet.Write(LUString(""));
loginResponse.Write(LUString(Game::config->GetValue("event_1")));
loginResponse.Write(LUString(Game::config->GetValue("event_2")));
loginResponse.Write(LUString(Game::config->GetValue("event_3")));
loginResponse.Write(LUString(Game::config->GetValue("event_4")));
loginResponse.Write(LUString(Game::config->GetValue("event_5")));
loginResponse.Write(LUString(Game::config->GetValue("event_6")));
loginResponse.Write(LUString(Game::config->GetValue("event_7")));
loginResponse.Write(LUString(Game::config->GetValue("event_8")));
packet.Write(static_cast<uint16_t>(1)); // Version Major
packet.Write(static_cast<uint16_t>(10)); // Version Current
packet.Write(static_cast<uint16_t>(64)); // Version Minor
uint16_t version_major = 1;
uint16_t version_current = 10;
uint16_t version_minor = 64;
GeneralUtils::TryParse<uint16_t>(Game::config->GetValue("version_major"), version_major);
GeneralUtils::TryParse<uint16_t>(Game::config->GetValue("version_current"), version_current);
GeneralUtils::TryParse<uint16_t>(Game::config->GetValue("version_minor"), version_minor);
loginResponse.Write(version_major);
loginResponse.Write(version_current);
loginResponse.Write(version_minor);
// Writes the user key
uint32_t sessionKey = GeneralUtils::GenerateRandomNumber<uint32_t>();
std::string userHash = std::to_string(sessionKey);
userHash = md5(userHash);
packet.Write(LUWString(userHash));
loginResponse.Write(LUWString(userHash));
// Write the Character and Chat IPs
packet.Write(LUString(wServerIP));
packet.Write(LUString(""));
// World Server IP
loginResponse.Write(LUString(wServerIP));
// Chat Server IP (unused)
loginResponse.Write(LUString(""));
// Write the Character and Chat Ports
packet.Write(static_cast<uint16_t>(wServerPort));
packet.Write(static_cast<uint16_t>(0));
// World Server Redirect port
loginResponse.Write(wServerPort);
// Char Server Redirect port (unused)
loginResponse.Write(static_cast<uint16_t>(0));
// CDN Key
packet.Write(LUString(""));
loginResponse.Write(LUString(""));
// CDN Ticket
packet.Write(LUString("00000000-0000-0000-0000-000000000000", 37));
loginResponse.Write(LUString("00000000-0000-0000-0000-000000000000", 37));
packet.Write(static_cast<uint32_t>(0)); // Language
// Language
loginResponse.Write(Language::en_US);
// Write the localization
packet.Write(LUString("US", 3));
loginResponse.Write(LUString("US", 3));
packet.Write(static_cast<uint8_t>(false)); // Just upgraded from F2P
packet.Write(static_cast<uint8_t>(false)); // User is F2P
packet.Write(static_cast<uint64_t>(0)); // Time Remaining in F2P
loginResponse.Write<uint8_t>(false); // Just upgraded from F2P
loginResponse.Write<uint8_t>(false); // User is F2P
loginResponse.Write<uint64_t>(0); // Time Remaining in F2P
// Write custom error message
packet.Write(static_cast<uint16_t>(errorMsg.length()));
packet.Write(LUWString(errorMsg, static_cast<uint32_t>(errorMsg.length())));
loginResponse.Write<uint16_t>(errorMsg.length());
loginResponse.Write(LUWString(errorMsg, static_cast<uint32_t>(errorMsg.length())));
// Here write auth logs
packet.Write(static_cast<uint32_t>(20));
for (uint32_t i = 0; i < 20; ++i) {
packet.Write(static_cast<uint32_t>(8));
packet.Write(static_cast<uint32_t>(44));
packet.Write(static_cast<uint32_t>(14000));
packet.Write(static_cast<uint32_t>(0));
}
stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_COMMUNICATION_FINISH, 1);
server->Send(&packet, sysAddr, false);
loginResponse.Write<uint32_t>((sizeof(Stamp) * stamps.size()) + sizeof(uint32_t));
for (auto& stamp : stamps) stamp.Serialize(&loginResponse);
server->Send(&loginResponse, sysAddr, false);
//Inform the master server that we've created a session for this user:
{
if (responseCode == eLoginResponse::SUCCESS) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SET_SESSION_KEY);
bitStream.Write(sessionKey);
bitStream.Write(LUString(username, 66));
bitStream.Write(LUString(username));
server->SendToMaster(&bitStream);
LOG("Set sessionKey: %i for user %s", sessionKey, username.c_str());

View File

@@ -4,17 +4,101 @@
#define _VARIADIC_MAX 10
#include "dCommonVars.h"
#include "dNetCommon.h"
#include "magic_enum.hpp"
enum class ServerType : uint32_t;
enum class eLoginResponse : uint8_t;
class dServer;
enum class eStamps : uint32_t {
PASSPORT_AUTH_START,
PASSPORT_AUTH_BYPASS,
PASSPORT_AUTH_ERROR,
PASSPORT_AUTH_DB_SELECT_START,
PASSPORT_AUTH_DB_SELECT_FINISH,
PASSPORT_AUTH_DB_INSERT_START,
PASSPORT_AUTH_DB_INSERT_FINISH,
PASSPORT_AUTH_LEGOINT_COMMUNICATION_START,
PASSPORT_AUTH_LEGOINT_RECEIVED,
PASSPORT_AUTH_LEGOINT_THREAD_SPAWN,
PASSPORT_AUTH_LEGOINT_WEBSERVICE_START,
PASSPORT_AUTH_LEGOINT_WEBSERVICE_FINISH,
PASSPORT_AUTH_LEGOINT_LEGOCLUB_START,
PASSPORT_AUTH_LEGOINT_LEGOCLUB_FINISH,
PASSPORT_AUTH_LEGOINT_THREAD_FINISH,
PASSPORT_AUTH_LEGOINT_REPLY,
PASSPORT_AUTH_LEGOINT_ERROR,
PASSPORT_AUTH_LEGOINT_COMMUNICATION_END,
PASSPORT_AUTH_LEGOINT_DISCONNECT,
PASSPORT_AUTH_WORLD_COMMUNICATION_START,
PASSPORT_AUTH_CLIENT_OS,
PASSPORT_AUTH_WORLD_PACKET_RECEIVED,
PASSPORT_AUTH_IM_COMMUNICATION_START,
PASSPORT_AUTH_IM_LOGIN_START,
PASSPORT_AUTH_IM_LOGIN_ALREADY_LOGGED_IN,
PASSPORT_AUTH_IM_OTHER_LOGIN_REMOVED,
PASSPORT_AUTH_IM_LOGIN_QUEUED,
PASSPORT_AUTH_IM_LOGIN_RESPONSE,
PASSPORT_AUTH_IM_COMMUNICATION_END,
PASSPORT_AUTH_WORLD_SESSION_CONFIRM_TO_AUTH,
PASSPORT_AUTH_WORLD_COMMUNICATION_FINISH,
PASSPORT_AUTH_WORLD_DISCONNECT,
NO_LEGO_INTERFACE,
DB_ERROR,
GM_REQUIRED,
NO_LEGO_WEBSERVICE_XML,
LEGO_WEBSERVICE_TIMEOUT,
LEGO_WEBSERVICE_ERROR,
NO_WORLD_SERVER
};
struct Stamp {
eStamps type;
uint32_t value;
uint64_t timestamp;
Stamp(eStamps type, uint32_t value, uint64_t timestamp = time(nullptr)){
this->type = type;
this->value = value;
this->timestamp = timestamp;
}
void Serialize(RakNet::BitStream* outBitStream);
};
enum class ClientOS : uint8_t {
UNKNOWN,
WINDOWS,
MACOS
};
enum class LanguageCodeID : uint16_t {
de_DE = 0x0407,
en_US = 0x0409,
en_GB = 0x0809
};
template <>
struct magic_enum::customize::enum_range<LanguageCodeID> {
static constexpr int min = 1031;
static constexpr int max = 2057;
};
enum class Language : uint32_t {
en_US,
pl_US,
de_DE,
en_GB,
};
namespace AuthPackets {
void HandleHandshake(dServer* server, Packet* packet);
void SendHandshake(dServer* server, const SystemAddress& sysAddr, const std::string& nextServerIP, uint16_t nextServerPort, const ServerType serverType);
void HandleLoginRequest(dServer* server, Packet* packet);
void SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username);
void SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username, std::vector<Stamp>& stamps);
void LoadClaimCodes();
}
#endif // AUTHPACKETS_H

View File

@@ -12,7 +12,7 @@ struct LUString {
std::string string;
uint32_t size;
LUString(uint32_t size) {
LUString(uint32_t size = 33) {
this->size = size;
};
LUString(std::string string, uint32_t size = 33) {
@@ -28,7 +28,7 @@ struct LUWString {
std::u16string string;
uint32_t size;
LUWString(uint32_t size) {
LUWString(uint32_t size = 33) {
this->size = size;
};
LUWString(std::u16string string, uint32_t size = 33) {
@@ -47,9 +47,9 @@ struct LUWString {
namespace BitStreamUtils {
template<typename T>
void WriteHeader(RakNet::BitStream& bitStream, eConnectionType connectionType, T internalPacketID) {
bitStream.Write<uint8_t>(MessageID(ID_USER_PACKET_ENUM));
bitStream.Write<MessageID>(ID_USER_PACKET_ENUM);
bitStream.Write<eConnectionType>(connectionType);
bitStream.Write<uint32_t>(static_cast<uint32_t>(internalPacketID));
bitStream.Write(static_cast<uint32_t>(internalPacketID));
bitStream.Write<uint8_t>(0);
}

View File

@@ -8,4 +8,5 @@ set(DNET_SOURCES "AuthPackets.cpp"
"ZoneInstanceManager.cpp")
add_library(dNet STATIC ${DNET_SOURCES})
target_link_libraries(dNet dCommon dDatabase)
target_link_libraries(dNet PUBLIC dCommon)

View File

@@ -7,7 +7,6 @@
#include "RakNetTypes.h"
#include "BitStream.h"
#include "Game.h"
#include "PacketUtils.h"
#include "BitStreamUtils.h"
#include "dServer.h"
#include "eConnectionType.h"
@@ -17,20 +16,20 @@ void ChatPackets::SendChatMessage(const SystemAddress& sysAddr, char chatChannel
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GENERAL_CHAT_MESSAGE);
bitStream.Write(static_cast<uint64_t>(0));
bitStream.Write<uint64_t>(0);
bitStream.Write(chatChannel);
bitStream.Write(static_cast<uint32_t>(message.size()));
bitStream.Write<uint32_t>(message.size());
bitStream.Write(LUWString(senderName));
bitStream.Write(playerObjectID);
bitStream.Write(static_cast<uint16_t>(0));
bitStream.Write(static_cast<char>(0));
bitStream.Write<uint16_t>(0);
bitStream.Write<char>(0);
for (uint32_t i = 0; i < message.size(); ++i) {
bitStream.Write(static_cast<uint16_t>(message[i]));
bitStream.Write<uint16_t>(message[i]);
}
bitStream.Write(static_cast<uint16_t>(0));
bitStream.Write<uint16_t>(0);
SEND_PACKET_BROADCAST;
}
@@ -39,21 +38,21 @@ void ChatPackets::SendSystemMessage(const SystemAddress& sysAddr, const std::u16
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GENERAL_CHAT_MESSAGE);
bitStream.Write(static_cast<uint64_t>(0));
bitStream.Write(static_cast<char>(4));
bitStream.Write<uint64_t>(0);
bitStream.Write<char>(4);
bitStream.Write(static_cast<uint32_t>(message.size()));
bitStream.Write<uint32_t>(message.size());
bitStream.Write(LUWString("", 33));
bitStream.Write(static_cast<uint64_t>(0));
bitStream.Write(static_cast<uint16_t>(0));
bitStream.Write(static_cast<char>(0));
bitStream.Write<uint64_t>(0);
bitStream.Write<uint16_t>(0);
bitStream.Write<char>(0);
for (uint32_t i = 0; i < message.size(); ++i) {
bitStream.Write(static_cast<uint16_t>(message[i]));
bitStream.Write<uint16_t>(message[i]);
}
bitStream.Write(static_cast<uint16_t>(0));
bitStream.Write<uint16_t>(0);
//This is so Wincent's announcement works:
if (sysAddr != UNASSIGNED_SYSTEM_ADDRESS) {

View File

@@ -4,17 +4,6 @@
*/
#include "ClientPackets.h"
#include "UserManager.h"
#include "User.h"
#include "Character.h"
#include "EntityManager.h"
#include "Entity.h"
#include "ControllablePhysicsComponent.h"
#include "Game.h"
#include "Logger.h"
#include "WorldPackets.h"
#include "NiPoint3.h"
#include "NiQuaternion.h"
#include "dCommonVars.h"
#include "BitStream.h"
#include "dChatFilter.h"
@@ -35,51 +24,24 @@
#include "eReplicaComponentType.h"
#include "CheatDetection.h"
#include "Recorder.h"
#include "PositionUpdate.h"
void ClientPackets::HandleChatMessage(const SystemAddress& sysAddr, Packet* packet) {
User* user = UserManager::Instance()->GetUser(sysAddr);
if (!user) {
LOG("Unable to get user to parse chat message");
return;
}
if (user->GetIsMuted()) {
user->GetLastUsedChar()->SendMuteNotice();
return;
}
ChatMessage ClientPackets::HandleChatMessage(Packet* packet) {
CINSTREAM_SKIP_HEADER;
char chatChannel;
uint16_t unknown;
ChatMessage message;
uint32_t messageLength;
std::u16string message;
inStream.Read(chatChannel);
inStream.Read(unknown);
inStream.Read(message.chatChannel);
inStream.Read(message.unknown);
inStream.Read(messageLength);
for (uint32_t i = 0; i < (messageLength - 1); ++i) {
uint16_t character;
inStream.Read(character);
message.push_back(character);
message.message.push_back(character);
}
std::string playerName = user->GetLastUsedChar()->GetName();
bool isMythran = user->GetLastUsedChar()->GetGMLevel() > eGameMasterLevel::CIVILIAN;
bool isOk = Game::chatFilter->IsSentenceOkay(GeneralUtils::UTF16ToWTF8(message), user->GetLastUsedChar()->GetGMLevel()).empty();
LOG_DEBUG("Msg: %s was approved previously? %i", GeneralUtils::UTF16ToWTF8(message).c_str(), user->GetLastChatMessageApproved());
if (!isOk) {
// Add a limit to the string converted by general utils because it is a user received string and may be a bad actor.
CheatDetection::ReportCheat(
user,
sysAddr,
"Player %s attempted to bypass chat filter with message: %s",
playerName.c_str(),
GeneralUtils::UTF16ToWTF8(message, 512).c_str());
}
if (!isOk && !isMythran) return;
std::string sMessage = GeneralUtils::UTF16ToWTF8(message);
LOG("%s: %s", playerName.c_str(), sMessage.c_str());
ChatPackets::SendChatMessage(sysAddr, chatChannel, playerName, user->GetLoggedInChar(), isMythran, message);
@@ -89,96 +51,63 @@ void ClientPackets::HandleChatMessage(const SystemAddress& sysAddr, Packet* pack
if (recorder != nullptr) {
recorder->AddRecord(new Cinema::Recording::SpeakRecord(sMessage));
}
return message;
}
void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Packet* packet) {
User* user = UserManager::Instance()->GetUser(sysAddr);
if (!user) {
LOG("Unable to get user to parse position update");
return;
}
PositionUpdate ClientPackets::HandleClientPositionUpdate(Packet* packet) {
PositionUpdate update;
CINSTREAM_SKIP_HEADER;
Entity* entity = Game::entityManager->GetEntity(user->GetLastUsedChar()->GetObjectID());
if (!entity) return;
inStream.Read(update.position.x);
inStream.Read(update.position.y);
inStream.Read(update.position.z);
ControllablePhysicsComponent* comp = static_cast<ControllablePhysicsComponent*>(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS));
if (!comp) return;
inStream.Read(update.rotation.x);
inStream.Read(update.rotation.y);
inStream.Read(update.rotation.z);
inStream.Read(update.rotation.w);
/*
//If we didn't move, this will match and stop our velocity
if (packet->length == 37) {
NiPoint3 zeroVel(0.0f, 0.0f, 0.0f);
comp->SetVelocity(zeroVel);
comp->SetAngularVelocity(zeroVel);
comp->SetIsOnGround(true); //probably8
Game::entityManager->SerializeEntity(entity);
return;
}
*/
auto* possessorComponent = entity->GetComponent<PossessorComponent>();
NiPoint3 position;
inStream.Read(position.x);
inStream.Read(position.y);
inStream.Read(position.z);
NiQuaternion rotation;
inStream.Read(rotation.x);
inStream.Read(rotation.y);
inStream.Read(rotation.z);
inStream.Read(rotation.w);
bool onGround = false;
bool onRail = false;
inStream.Read(onGround);
inStream.Read(onRail);
inStream.Read(update.onGround);
inStream.Read(update.onRail);
bool velocityFlag = false;
inStream.Read(velocityFlag);
NiPoint3 velocity{};
if (velocityFlag) {
inStream.Read(velocity.x);
inStream.Read(velocity.y);
inStream.Read(velocity.z);
inStream.Read(update.velocity.x);
inStream.Read(update.velocity.y);
inStream.Read(update.velocity.z);
}
bool angVelocityFlag = false;
inStream.Read(angVelocityFlag);
NiPoint3 angVelocity{};
if (angVelocityFlag) {
inStream.Read(angVelocity.x);
inStream.Read(angVelocity.y);
inStream.Read(angVelocity.z);
inStream.Read(update.angularVelocity.x);
inStream.Read(update.angularVelocity.y);
inStream.Read(update.angularVelocity.z);
}
// TODO figure out how to use these. Ignoring for now, but reading in if they exist.
bool hasLocalSpaceInfo{};
LWOOBJID objectId{};
NiPoint3 localSpacePosition{};
bool hasLinearVelocity{};
NiPoint3 linearVelocity{};
if (inStream.Read(hasLocalSpaceInfo) && hasLocalSpaceInfo) {
inStream.Read(objectId);
inStream.Read(localSpacePosition.x);
inStream.Read(localSpacePosition.y);
inStream.Read(localSpacePosition.z);
inStream.Read(update.localSpaceInfo.objectId);
inStream.Read(update.localSpaceInfo.position.x);
inStream.Read(update.localSpaceInfo.position.y);
inStream.Read(update.localSpaceInfo.position.z);
bool hasLinearVelocity = false;
if (inStream.Read(hasLinearVelocity) && hasLinearVelocity) {
inStream.Read(linearVelocity.x);
inStream.Read(linearVelocity.y);
inStream.Read(linearVelocity.z);
inStream.Read(update.localSpaceInfo.linearVelocity.x);
inStream.Read(update.localSpaceInfo.linearVelocity.y);
inStream.Read(update.localSpaceInfo.linearVelocity.z);
}
}
bool hasRemoteInputInfo{};
RemoteInputInfo remoteInput{};
bool hasRemoteInputInfo{};
if (inStream.Read(hasRemoteInputInfo) && hasRemoteInputInfo) {
inStream.Read(remoteInput.m_RemoteInputX);
inStream.Read(remoteInput.m_RemoteInputY);
inStream.Read(remoteInput.m_IsPowersliding);
inStream.Read(remoteInput.m_IsModified);
inStream.Read(update.remoteInputInfo.m_RemoteInputX);
inStream.Read(update.remoteInputInfo.m_RemoteInputY);
inStream.Read(update.remoteInputInfo.m_IsPowersliding);
inStream.Read(update.remoteInputInfo.m_IsModified);
}
bool updateChar = true;
@@ -302,124 +231,43 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac
Game::entityManager->SerializeEntity(entity, player);
}
*/
return update;
}
void ClientPackets::HandleChatModerationRequest(const SystemAddress& sysAddr, Packet* packet) {
User* user = UserManager::Instance()->GetUser(sysAddr);
if (!user) {
LOG("Unable to get user to parse chat moderation request");
return;
}
ChatModerationRequest ClientPackets::HandleChatModerationRequest(Packet* packet) {
CINSTREAM_SKIP_HEADER;
ChatModerationRequest request;
auto* entity = Player::GetPlayer(sysAddr);
if (entity == nullptr) {
LOG("Unable to get player to parse chat moderation request");
return;
}
// Check if the player has restricted chat access
auto* character = entity->GetCharacter();
if (character->HasPermission(ePermissionMap::RestrictedChatAccess)) {
// Send a message to the player
ChatPackets::SendSystemMessage(
sysAddr,
u"This character has restricted chat access."
);
return;
}
RakNet::BitStream stream(packet->data, packet->length, false);
uint64_t header;
stream.Read(header);
// Data
uint8_t chatLevel;
uint8_t requestID;
uint16_t messageLength;
std::string receiver = "";
std::string message = "";
stream.Read(chatLevel);
stream.Read(requestID);
inStream.Read(request.chatLevel);
inStream.Read(request.requestID);
for (uint32_t i = 0; i < 42; ++i) {
uint16_t character;
stream.Read(character);
receiver.push_back(static_cast<uint8_t>(character));
inStream.Read(character);
request.receiver.push_back(static_cast<uint8_t>(character));
}
if (!receiver.empty()) {
if (std::string(receiver.c_str(), 4) == "[GM]") { // Shift the string forward if we are speaking to a GM as the client appends "[GM]" if they are
receiver = std::string(receiver.c_str() + 4, receiver.size() - 4);
if (!request.receiver.empty()) {
if (std::string(request.receiver.c_str(), 4) == "[GM]") { // Shift the string forward if we are speaking to a GM as the client appends "[GM]" if they are
request.receiver = std::string(request.receiver.c_str() + 4, request.receiver.size() - 4);
}
}
stream.Read(messageLength);
uint16_t messageLength;
inStream.Read(messageLength);
for (uint32_t i = 0; i < messageLength; ++i) {
uint16_t character;
stream.Read(character);
message.push_back(static_cast<uint8_t>(character));
inStream.Read(character);
request.message.push_back(static_cast<uint8_t>(character));
}
bool isBestFriend = false;
if (chatLevel == 1) {
// Private chat
LWOOBJID idOfReceiver = LWOOBJID_EMPTY;
{
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE name = ?");
stmt->setString(1, receiver);
sql::ResultSet* res = stmt->executeQuery();
if (res->next()) {
idOfReceiver = res->getInt("id");
}
delete stmt;
delete res;
}
if (user->GetIsBestFriendMap().find(receiver) == user->GetIsBestFriendMap().end() && idOfReceiver != LWOOBJID_EMPTY) {
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;");
stmt->setInt(1, entity->GetObjectID());
stmt->setInt(2, idOfReceiver);
stmt->setInt(3, idOfReceiver);
stmt->setInt(4, entity->GetObjectID());
sql::ResultSet* res = stmt->executeQuery();
if (res->next()) {
isBestFriend = res->getInt("best_friend") == 3;
}
if (isBestFriend) {
auto tmpBestFriendMap = user->GetIsBestFriendMap();
tmpBestFriendMap[receiver] = true;
user->SetIsBestFriendMap(tmpBestFriendMap);
}
delete res;
delete stmt;
} else if (user->GetIsBestFriendMap().find(receiver) != user->GetIsBestFriendMap().end()) {
isBestFriend = true;
}
}
std::vector<std::pair<uint8_t, uint8_t>> segments = Game::chatFilter->IsSentenceOkay(message, entity->GetGMLevel(), !(isBestFriend && chatLevel == 1));
bool bAllClean = segments.empty();
if (user->GetIsMuted()) {
bAllClean = false;
}
user->SetLastChatMessageApproved(bAllClean);
WorldPackets::SendChatModerationResponse(sysAddr, bAllClean, requestID, receiver, segments);
return request;
}
int32_t ClientPackets::SendTop5HelpIssues(Packet* packet) {
CINSTREAM_SKIP_HEADER;
int32_t language = 0;
inStream.Read(language);
return language;
}

View File

@@ -6,12 +6,31 @@
#ifndef CLIENTPACKETS_H
#define CLIENTPACKETS_H
#include "RakNetTypes.h"
#include <cstdint>
#include <string>
class PositionUpdate;
struct Packet;
struct ChatMessage {
uint8_t chatChannel = 0;
uint16_t unknown = 0;
std::u16string message;
};
struct ChatModerationRequest {
uint8_t chatLevel = 0;
uint8_t requestID = 0;
std::string receiver;
std::string message;
};
namespace ClientPackets {
void HandleChatMessage(const SystemAddress& sysAddr, Packet* packet);
void HandleClientPositionUpdate(const SystemAddress& sysAddr, Packet* packet);
void HandleChatModerationRequest(const SystemAddress& sysAddr, Packet* packet);
ChatMessage HandleChatMessage(Packet* packet);
PositionUpdate HandleClientPositionUpdate(Packet* packet);
ChatModerationRequest HandleChatModerationRequest(Packet* packet);
int32_t SendTop5HelpIssues(Packet* packet);
};
#endif // CLIENTPACKETS_H

View File

@@ -1,6 +1,5 @@
#include "MasterPackets.h"
#include "BitStream.h"
#include "PacketUtils.h"
#include "dCommonVars.h"
#include "dServer.h"
#include "eConnectionType.h"
@@ -31,7 +30,7 @@ void MasterPackets::SendZoneTransferRequest(dServer* server, uint64_t requestID,
BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::REQUEST_ZONE_TRANSFER);
bitStream.Write(requestID);
bitStream.Write(static_cast<uint8_t>(mythranShift));
bitStream.Write<uint8_t>(mythranShift);
bitStream.Write(zoneID);
bitStream.Write(cloneID);
@@ -58,7 +57,7 @@ void MasterPackets::SendZoneRequestPrivate(dServer* server, uint64_t requestID,
BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::REQUEST_PRIVATE_ZONE);
bitStream.Write(requestID);
bitStream.Write(static_cast<uint8_t>(mythranShift));
bitStream.Write<uint8_t>(mythranShift);
bitStream.Write<uint32_t>(password.size());
for (auto character : password) {
@@ -83,12 +82,12 @@ void MasterPackets::SendZoneTransferResponse(dServer* server, const SystemAddres
BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::REQUEST_ZONE_TRANSFER_RESPONSE);
bitStream.Write(requestID);
bitStream.Write(static_cast<uint8_t>(mythranShift));
bitStream.Write<uint8_t>(mythranShift);
bitStream.Write(zoneID);
bitStream.Write(zoneInstance);
bitStream.Write(zoneClone);
bitStream.Write(static_cast<uint16_t>(serverPort));
bitStream.Write(LUString(serverIP, static_cast<uint32_t>(serverIP.size() + 1)));
bitStream.Write<uint16_t>(serverPort);
bitStream.Write(LUString(serverIP, 255));
server->Send(&bitStream, sysAddr, false);
}
@@ -100,12 +99,12 @@ void MasterPackets::HandleServerInfo(Packet* packet) {
uint32_t theirPort = 0;
uint32_t theirZoneID = 0;
uint32_t theirInstanceID = 0;
std::string theirIP = "";
LUString theirIP;
inStream.Read(theirPort);
inStream.Read(theirZoneID);
inStream.Read(theirInstanceID);
theirIP = PacketUtils::ReadString(inStream.GetReadOffset(), packet, false); //20 is the current offset
inStream.Read(theirIP);
//TODO: Actually mark this server as an available server in the manager
}
@@ -118,7 +117,7 @@ void MasterPackets::SendServerInfo(dServer* server, Packet* packet) {
bitStream.Write(server->GetZoneID());
bitStream.Write(server->GetInstanceID());
bitStream.Write(server->GetServerType());
bitStream.Write(LUString(server->GetIP(), server->GetIP().size()));
bitStream.Write(LUString(server->GetIP()));
server->SendToMaster(&bitStream);
}

View File

@@ -1,64 +1,8 @@
#include "PacketUtils.h"
#include <vector>
#include <fstream>
#include "Logger.h"
#include "Game.h"
uint16_t PacketUtils::ReadU16(uint32_t startLoc, Packet* packet) {
if (startLoc + 2 > packet->length) return 0;
std::vector<unsigned char> t;
for (uint32_t i = startLoc; i < startLoc + 2; i++) t.push_back(packet->data[i]);
return *(uint16_t*)t.data();
}
uint32_t PacketUtils::ReadU32(uint32_t startLoc, Packet* packet) {
if (startLoc + 4 > packet->length) return 0;
std::vector<unsigned char> t;
for (uint32_t i = startLoc; i < startLoc + 4; i++) {
t.push_back(packet->data[i]);
}
return *(uint32_t*)t.data();
}
uint64_t PacketUtils::ReadU64(uint32_t startLoc, Packet* packet) {
if (startLoc + 8 > packet->length) return 0;
std::vector<unsigned char> t;
for (uint32_t i = startLoc; i < startLoc + 8; i++) t.push_back(packet->data[i]);
return *(uint64_t*)t.data();
}
int64_t PacketUtils::ReadS64(uint32_t startLoc, Packet* packet) {
if (startLoc + 8 > packet->length) return 0;
std::vector<unsigned char> t;
for (size_t i = startLoc; i < startLoc + 8; i++) t.push_back(packet->data[i]);
return *(int64_t*)t.data();
}
std::string PacketUtils::ReadString(uint32_t startLoc, Packet* packet, bool wide, uint32_t maxLen) {
std::string readString = "";
if (wide) maxLen *= 2;
if (packet->length > startLoc) {
uint32_t i = 0;
while (packet->data[startLoc + i] != '\0' && packet->length > (uint32_t)(startLoc + i) && maxLen > i) {
readString.push_back(packet->data[startLoc + i]);
if (wide) {
i += 2; // Wide-char string
} else {
i++; // Regular string
}
}
}
return readString;
}
//! Saves a packet to the filesystem
void PacketUtils::SavePacket(const std::string& filename, const char* data, size_t length) {
//If we don't log to the console, don't save the bin files either. This takes up a lot of time.

View File

@@ -1,18 +1,13 @@
#ifndef PACKETUTILS_H
#define PACKETUTILS_H
#include <MessageIdentifiers.h>
#include <BitStream.h>
#include "MessageIdentifiers.h"
#include "BitStream.h"
#include <string>
enum class eConnectionType : uint16_t;
namespace PacketUtils {
uint16_t ReadU16(uint32_t startLoc, Packet* packet);
uint32_t ReadU32(uint32_t startLoc, Packet* packet);
uint64_t ReadU64(uint32_t startLoc, Packet* packet);
int64_t ReadS64(uint32_t startLoc, Packet* packet);
std::string ReadString(uint32_t startLoc, Packet* packet, bool wide, uint32_t maxLen = 33);
void SavePacket(const std::string& filename, const char* data, size_t length);
};

View File

@@ -1,90 +1,34 @@
#include "dCommonVars.h"
#include "WorldPackets.h"
#include "dCommonVars.h"
#include "BitStream.h"
#include "PacketUtils.h"
#include "GeneralUtils.h"
#include "User.h"
#include "Character.h"
#include "Logger.h"
#include <iostream>
#include "Game.h"
#include "LDFFormat.h"
#include "dServer.h"
#include "dZoneManager.h"
#include "CharacterComponent.h"
#include "ZCompression.h"
#include "eConnectionType.h"
#include "BitStreamUtils.h"
void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum) {
#include <iostream>
void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::LOAD_STATIC_ZONE);
auto zone = Game::zoneManager->GetZone()->GetZoneID();
bitStream.Write(static_cast<uint16_t>(zone.GetMapID()));
bitStream.Write(static_cast<uint16_t>(zone.GetInstanceID()));
//bitStream.Write(static_cast<uint32_t>(zone.GetCloneID()));
bitStream.Write<uint16_t>(zone.GetMapID());
bitStream.Write<uint16_t>(zone.GetInstanceID());
//bitStream.Write<uint32_t>(zone.GetCloneID());
bitStream.Write(0);
bitStream.Write(checksum);
bitStream.Write(static_cast<uint16_t>(0)); // ??
bitStream.Write<uint16_t>(0); // ??
bitStream.Write(x);
bitStream.Write(y);
bitStream.Write(z);
bitStream.Write(static_cast<uint32_t>(0)); // Change this to eventually use 4 on activity worlds
SEND_PACKET;
}
void WorldPackets::SendCharacterList(const SystemAddress& sysAddr, User* user) {
if (!user) return;
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CHARACTER_LIST_RESPONSE);
std::vector<Character*> characters = user->GetCharacters();
bitStream.Write(static_cast<uint8_t>(characters.size()));
bitStream.Write(static_cast<uint8_t>(0)); //character index in front, just picking 0
for (uint32_t i = 0; i < characters.size(); ++i) {
bitStream.Write(characters[i]->GetObjectID());
bitStream.Write(static_cast<uint32_t>(0));
bitStream.Write(LUWString(characters[i]->GetName()));
bitStream.Write(LUWString(characters[i]->GetUnapprovedName()));
bitStream.Write(static_cast<uint8_t>(characters[i]->GetNameRejected()));
bitStream.Write(static_cast<uint8_t>(false));
bitStream.Write(LUString("", 10));
bitStream.Write(characters[i]->GetShirtColor());
bitStream.Write(characters[i]->GetShirtStyle());
bitStream.Write(characters[i]->GetPantsColor());
bitStream.Write(characters[i]->GetHairStyle());
bitStream.Write(characters[i]->GetHairColor());
bitStream.Write(characters[i]->GetLeftHand());
bitStream.Write(characters[i]->GetRightHand());
bitStream.Write(characters[i]->GetEyebrows());
bitStream.Write(characters[i]->GetEyes());
bitStream.Write(characters[i]->GetMouth());
bitStream.Write(static_cast<uint32_t>(0));
bitStream.Write(static_cast<uint16_t>(characters[i]->GetZoneID()));
bitStream.Write(static_cast<uint16_t>(characters[i]->GetZoneInstance()));
bitStream.Write(characters[i]->GetZoneClone());
bitStream.Write(characters[i]->GetLastLogin());
const auto& equippedItems = characters[i]->GetEquippedItems();
bitStream.Write(static_cast<uint16_t>(equippedItems.size()));
for (uint32_t j = 0; j < equippedItems.size(); ++j) {
bitStream.Write(equippedItems[j]);
}
}
bitStream.Write<uint32_t>(0); // Change this to eventually use 4 on activity worlds
SEND_PACKET;
}
@@ -106,7 +50,7 @@ void WorldPackets::SendCharacterRenameResponse(const SystemAddress& sysAddr, eRe
void WorldPackets::SendCharacterDeleteResponse(const SystemAddress& sysAddr, bool response) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::DELETE_CHARACTER_RESPONSE);
bitStream.Write(static_cast<uint8_t>(response));
bitStream.Write<uint8_t>(response);
SEND_PACKET;
}
@@ -115,8 +59,8 @@ void WorldPackets::SendTransferToWorld(const SystemAddress& sysAddr, const std::
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TRANSFER_TO_WORLD);
bitStream.Write(LUString(serverIP));
bitStream.Write(static_cast<uint16_t>(serverPort));
bitStream.Write(static_cast<uint8_t>(mythranShift));
bitStream.Write<uint16_t>(serverPort);
bitStream.Write<uint8_t>(mythranShift);
SEND_PACKET;
}
@@ -124,30 +68,24 @@ void WorldPackets::SendTransferToWorld(const SystemAddress& sysAddr, const std::
void WorldPackets::SendServerState(const SystemAddress& sysAddr) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::SERVER_STATES);
bitStream.Write(static_cast<uint8_t>(1)); //If the server is receiving this request, it probably is ready anyway.
bitStream.Write<uint8_t>(1); //If the server is receiving this request, it probably is ready anyway.
SEND_PACKET;
}
void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, Entity* entity, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm) {
void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CREATE_CHARACTER);
RakNet::BitStream data;
data.Write<uint32_t>(7); //LDF key count
auto character = entity->GetComponent<CharacterComponent>();
if (!character) {
LOG("Entity is not a character?? what??");
return;
}
LDFData<LWOOBJID>* objid = new LDFData<LWOOBJID>(u"objid", entity->GetObjectID());
LDFData<LOT>* lot = new LDFData<LOT>(u"template", 1);
LDFData<std::string>* xmlConfigData = new LDFData<std::string>(u"xmlData", xmlData);
LDFData<std::u16string>* name = new LDFData<std::u16string>(u"name", username);
LDFData<int32_t>* gmlevel = new LDFData<int32_t>(u"gmlevel", static_cast<int32_t>(gm));
LDFData<int32_t>* chatmode = new LDFData<int32_t>(u"chatmode", static_cast<int32_t>(gm));
LDFData<int64_t>* reputation = new LDFData<int64_t>(u"reputation", character->GetReputation());
std::unique_ptr<LDFData<LWOOBJID>> objid(new LDFData<LWOOBJID>(u"objid", player));
std::unique_ptr<LDFData<LOT>> lot(new LDFData<LOT>(u"template", 1));
std::unique_ptr<LDFData<std::string>> xmlConfigData(new LDFData<std::string>(u"xmlData", xmlData));
std::unique_ptr<LDFData<std::u16string>> name(new LDFData<std::u16string>(u"name", username));
std::unique_ptr<LDFData<int32_t>> gmlevel(new LDFData<int32_t>(u"gmlevel", static_cast<int32_t>(gm)));
std::unique_ptr<LDFData<int32_t>> chatmode(new LDFData<int32_t>(u"chatmode", static_cast<int32_t>(gm)));
std::unique_ptr<LDFData<int64_t>> reputationLdf(new LDFData<int64_t>(u"reputation", reputation));
objid->WriteToPacket(&data);
lot->WriteToPacket(&data);
@@ -155,15 +93,7 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, Entity* ent
gmlevel->WriteToPacket(&data);
chatmode->WriteToPacket(&data);
xmlConfigData->WriteToPacket(&data);
reputation->WriteToPacket(&data);
delete objid;
delete lot;
delete xmlConfigData;
delete gmlevel;
delete chatmode;
delete name;
delete reputation;
reputationLdf->WriteToPacket(&data);
//Compress the data before sending:
const uint32_t reservedSize = ZCompression::GetMaxCompressedLength(data.GetNumberOfBytesUsed());
@@ -187,14 +117,12 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, Entity* ent
* an assertion is done to prevent bad data from being saved or sent.
*/
#pragma warning(disable:6385) // C6385 Reading invalid data from 'compressedData'.
for (size_t i = 0; i < size; i++)
bitStream.Write(compressedData[i]);
bitStream.WriteAlignedBytes(compressedData, size);
#pragma warning(default:6385)
// PacketUtils::SavePacket("chardata.bin", (const char*)bitStream.GetData(), static_cast<uint32_t>(bitStream.GetNumberOfBytesUsed()));
SEND_PACKET;
delete[] compressedData;
LOG("Sent CreateCharacter for ID: %llu", entity->GetObjectID());
LOG("Sent CreateCharacter for ID: %llu", player);
}
void WorldPackets::SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector<std::pair<uint8_t, uint8_t>> unacceptedItems) {
@@ -204,8 +132,8 @@ void WorldPackets::SendChatModerationResponse(const SystemAddress& sysAddr, bool
bitStream.Write<uint8_t>(unacceptedItems.empty()); // Is sentence ok?
bitStream.Write<uint16_t>(0x16); // Source ID, unknown
bitStream.Write(static_cast<uint8_t>(requestID)); // request ID
bitStream.Write(static_cast<char>(0)); // chat mode
bitStream.Write<uint8_t>(requestID); // request ID
bitStream.Write<char>(0); // chat mode
bitStream.Write(LUWString(receiver, 42)); // receiver name

View File

@@ -2,9 +2,8 @@
#define WORLDPACKETS_H
#include "dCommonVars.h"
#include <vector>
#include <string>
#include <unordered_map>
#include "Entity.h"
class User;
struct SystemAddress;
@@ -13,14 +12,13 @@ enum class eCharacterCreationResponse : uint8_t;
enum class eRenameResponse : uint8_t;
namespace WorldPackets {
void SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum);
void SendCharacterList(const SystemAddress& sysAddr, User* user);
void SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone);
void SendCharacterCreationResponse(const SystemAddress& sysAddr, eCharacterCreationResponse response);
void SendCharacterRenameResponse(const SystemAddress& sysAddr, eRenameResponse response);
void SendCharacterDeleteResponse(const SystemAddress& sysAddr, bool response);
void SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift);
void SendServerState(const SystemAddress& sysAddr);
void SendCreateCharacter(const SystemAddress& sysAddr, Entity* entity, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm);
void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm);
void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector<std::pair<uint8_t, uint8_t>> unacceptedItems);
void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel);
}

View File

@@ -3,7 +3,6 @@
// Custom Classes
#include "MasterPackets.h"
#include "PacketUtils.h"
#include "dServer.h"
// C++
@@ -25,20 +24,30 @@ void ZoneInstanceManager::RequestZoneTransfer(dServer* server, uint32_t zoneID,
}
//! Handles a zone transfer response
void ZoneInstanceManager::HandleRequestZoneTransferResponse(uint64_t requestID, Packet* packet) {
bool mythranShift = static_cast<bool>(packet->data[16]);
uint32_t zoneID = PacketUtils::ReadU32(17, packet);
uint32_t zoneInstance = PacketUtils::ReadU32(21, packet);
uint32_t zoneClone = PacketUtils::ReadU32(25, packet);
uint16_t serverPort = PacketUtils::ReadU16(29, packet);
std::string serverIP = PacketUtils::ReadString(31, packet, false);
void ZoneInstanceManager::HandleRequestZoneTransferResponse(Packet* packet) {
CINSTREAM_SKIP_HEADER;
uint64_t requestID;
inStream.Read(requestID);
bool mythranShift;
uint8_t tmp;
inStream.Read(tmp);
mythranShift = tmp > 0;
uint32_t zoneID;
inStream.Read(zoneID);
uint32_t zoneInstance;
inStream.Read(zoneInstance);
uint32_t zoneClone;
inStream.Read(zoneClone);
uint16_t serverPort;
inStream.Read(serverPort);
LUString serverIP(255);
inStream.Read(serverIP);
for (uint32_t i = 0; i < this->requests.size(); ++i) {
if (this->requests[i]->requestID == requestID) {
// Call the request callback
this->requests[i]->callback(mythranShift, zoneID, zoneInstance, zoneClone, serverIP, serverPort);
this->requests[i]->callback(mythranShift, zoneID, zoneInstance, zoneClone, serverIP.string, serverPort);
delete this->requests[i];
this->requests.erase(this->requests.begin() + i);

View File

@@ -1,13 +1,11 @@
#pragma once
// C++
#define _VARIADIC_MAX 10
#include <functional>
#include <vector>
#include <string>
// RakNet
#include <RakNetTypes.h>
#include "RakNetTypes.h"
class dServer;
@@ -56,7 +54,7 @@ public:
\param requestID The request ID
\param packet The packet
*/
void HandleRequestZoneTransferResponse(uint64_t requestID, Packet* packet);
void HandleRequestZoneTransferResponse(Packet* packet);
void CreatePrivateZone(dServer* server, uint32_t zoneID, uint32_t zoneClone, const std::string& password);

View File

@@ -10,10 +10,10 @@
#include "eServerMessageType.h"
#include "eMasterMessageType.h"
#include "PacketUtils.h"
#include "BitStreamUtils.h"
#include "MasterPackets.h"
#include "ZoneInstanceManager.h"
#include "StringifiedEnum.h"
//! Replica Constructor class
class ReplicaConstructor : public ReceiveConstructionInterface {
@@ -39,7 +39,7 @@ public:
}
} ReceiveDownloadCompleteCB;
dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, bool* shouldShutdown, unsigned int zoneID) {
dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, Game::signal_t* lastSignal, unsigned int zoneID) {
mIP = ip;
mPort = port;
mZoneID = zoneID;
@@ -55,7 +55,7 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
mReplicaManager = nullptr;
mServerType = serverType;
mConfig = config;
mShouldShutdown = shouldShutdown;
mShouldShutdown = lastSignal;
//Attempt to start our server here:
mIsOkay = Startup();
@@ -65,9 +65,9 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
if (mIsOkay) {
if (zoneID == 0)
LOG("Server is listening on %s:%i with encryption: %i", ip.c_str(), port, int(useEncryption));
LOG("%s Server is listening on %s:%i with encryption: %i", StringifiedEnum::ToString(serverType).data(), ip.c_str(), port, int(useEncryption));
else
LOG("Server is listening on %s:%i with encryption: %i, running zone %i / %i", ip.c_str(), port, int(useEncryption), zoneID, instanceID);
LOG("%s Server is listening on %s:%i with encryption: %i, running zone %i / %i", StringifiedEnum::ToString(serverType).data(), ip.c_str(), port, int(useEncryption), zoneID, instanceID);
} else { LOG("FAILED TO START SERVER ON IP/PORT: %s:%i", ip.c_str(), port); return; }
mLogger->SetLogToConsole(prevLogSetting);
@@ -75,7 +75,9 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
//Connect to master if we are not master:
if (serverType != ServerType::Master) {
SetupForMasterConnection();
ConnectToMaster();
if (!ConnectToMaster()) {
LOG("Failed ConnectToMaster!");
}
}
//Set up Replica if we're a world server:
@@ -124,12 +126,11 @@ Packet* dServer::ReceiveFromMaster() {
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::MASTER) {
switch (static_cast<eMasterMessageType>(packet->data[3])) {
case eMasterMessageType::REQUEST_ZONE_TRANSFER_RESPONSE: {
uint64_t requestID = PacketUtils::ReadU64(8, packet);
ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(requestID, packet);
ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(packet);
break;
}
case eMasterMessageType::SHUTDOWN:
*mShouldShutdown = true;
*mShouldShutdown = -2;
break;
//When we handle these packets in World instead dServer, we just return the packet's pointer.
@@ -236,10 +237,12 @@ void dServer::Shutdown() {
void dServer::SetupForMasterConnection() {
mMasterSocketDescriptor = SocketDescriptor(uint16_t(mPort + 1), 0);
mMasterPeer = RakNetworkFactory::GetRakPeerInterface();
mMasterPeer->Startup(1, 30, &mMasterSocketDescriptor, 1);
bool ret = mMasterPeer->Startup(1, 30, &mMasterSocketDescriptor, 1);
if (!ret) LOG("Failed MasterPeer Startup!");
}
bool dServer::ConnectToMaster() {
//LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort);
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, "3.25 DARKFLAME1", 15);
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include <string>
#include <csignal>
#include "RakPeerInterface.h"
#include "ReplicaManager.h"
#include "NetworkIDManager.h"
@@ -15,6 +16,18 @@ enum class ServerType : uint32_t {
World
};
enum class ServiceId : uint32_t{
General = 0,
Auth = 1,
Chat = 2,
World = 4,
Client = 5,
};
namespace Game {
using signal_t = volatile std::sig_atomic_t;
}
class dServer {
public:
// Default constructor should only used for testing!
@@ -31,7 +44,7 @@ public:
int masterPort,
ServerType serverType,
dConfig* config,
bool* shouldShutdown,
Game::signal_t* shouldShutdown,
unsigned int zoneID = 0);
~dServer();
@@ -81,9 +94,9 @@ private:
NetworkIDManager* mNetIDManager = nullptr;
/**
* Whether or not to shut down the server. Pointer to Game::shouldShutdown.
* Whether or not to shut down the server. Pointer to Game::lastSignal.
*/
bool* mShouldShutdown = nullptr;
Game::signal_t* mShouldShutdown = nullptr;
SocketDescriptor mSocketDescriptor;
std::string mIP;
int mPort;