mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-13 15:27:14 +00:00
fix: security vulnerabilities (#1980)
* fix: security vulnerabilities Tested that all functions related to the touched files work will test sqlite on a CI build * fix failing test * ai feedback * add buffer size checking * use c_str * dont log session key * Try this for a mac definition * be quiet apple
This commit is contained in:
@@ -80,7 +80,7 @@ else() # Build from source
|
|||||||
-DWITH_EXTERNAL_ZLIB=ON
|
-DWITH_EXTERNAL_ZLIB=ON
|
||||||
-DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}
|
-DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}
|
||||||
-DCMAKE_C_FLAGS=-w # disable zlib warnings
|
-DCMAKE_C_FLAGS=-w # disable zlib warnings
|
||||||
-DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0\ -include\ cstdint)
|
-DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0\ -Wno-inconsistent-missing-override\ -include\ cstdint)
|
||||||
else()
|
else()
|
||||||
set(MARIADB_EXTRA_CMAKE_ARGS
|
set(MARIADB_EXTRA_CMAKE_ARGS
|
||||||
-DCMAKE_C_FLAGS=-w # disable zlib warnings
|
-DCMAKE_C_FLAGS=-w # disable zlib warnings
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "MessageType/Chat.h"
|
#include "MessageType/Chat.h"
|
||||||
#include "BitStreamUtils.h"
|
#include "BitStreamUtils.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
|
#include "dConfig.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "eObjectBits.h"
|
#include "eObjectBits.h"
|
||||||
|
|
||||||
@@ -72,8 +73,8 @@ void ChatIgnoreList::AddIgnore(Packet* packet) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int32_t MAX_IGNORES = 32;
|
const int32_t MAX_IGNORES = Game::config->GetValue("max_ignores", 32);
|
||||||
if (receiver.ignoredPlayers.size() > MAX_IGNORES) {
|
if (receiver.ignoredPlayers.size() >= MAX_IGNORES) {
|
||||||
LOG_DEBUG("Player %llu has too many ignores", playerId);
|
LOG_DEBUG("Player %llu has too many ignores", playerId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -435,6 +435,11 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
|||||||
inStream.IgnoreBytes(4);
|
inStream.IgnoreBytes(4);
|
||||||
inStream.Read(channel);
|
inStream.Read(channel);
|
||||||
inStream.Read(size);
|
inStream.Read(size);
|
||||||
|
if (size > MAX_MESSAGE_LENGTH) {
|
||||||
|
LOG("Received a probably spoofed chat message, ignoring msg");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
inStream.IgnoreBytes(77);
|
inStream.IgnoreBytes(77);
|
||||||
|
|
||||||
LUWString message(size);
|
LUWString message(size);
|
||||||
@@ -479,6 +484,11 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
|
|||||||
if (channel != eChatChannel::PRIVATE_CHAT) LOG("WARNING: Received Private chat with the wrong channel!");
|
if (channel != eChatChannel::PRIVATE_CHAT) LOG("WARNING: Received Private chat with the wrong channel!");
|
||||||
|
|
||||||
inStream.Read(size);
|
inStream.Read(size);
|
||||||
|
if (size > MAX_MESSAGE_LENGTH) {
|
||||||
|
LOG("Received a probably spoofed chat message, ignoring msg");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
inStream.IgnoreBytes(77);
|
inStream.IgnoreBytes(77);
|
||||||
|
|
||||||
inStream.Read(LUReceiverName);
|
inStream.Read(LUReceiverName);
|
||||||
|
|||||||
@@ -176,6 +176,7 @@ LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
|
|||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Make this a pointer again or do something to make it so you cant edit the LWOOBJID_EMPTY entry? it should be ignored in all cases anyways though...
|
||||||
PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) {
|
PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) {
|
||||||
return m_Players.contains(playerID) ? m_Players[playerID] : m_Players[LWOOBJID_EMPTY];
|
return m_Players.contains(playerID) ? m_Players[playerID] : m_Players[LWOOBJID_EMPTY];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "Amf3.h"
|
#include "Amf3.h"
|
||||||
|
#include "StringifiedEnum.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AMF3 Reference document https://rtmp.veriskope.com/pdf/amf3-file-format-spec.pdf
|
* AMF3 Reference document https://rtmp.veriskope.com/pdf/amf3-file-format-spec.pdf
|
||||||
@@ -53,7 +54,7 @@ std::unique_ptr<AMFBaseValue> AMFDeserialize::Read(RakNet::BitStream& inStream)
|
|||||||
case eAmf::VectorObject:
|
case eAmf::VectorObject:
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case eAmf::Dictionary:
|
case eAmf::Dictionary:
|
||||||
throw marker;
|
throw std::invalid_argument(StringifiedEnum::ToString(marker).data());
|
||||||
default:
|
default:
|
||||||
throw std::invalid_argument("Invalid AMF3 marker" + std::to_string(static_cast<int32_t>(marker)));
|
throw std::invalid_argument("Invalid AMF3 marker" + std::to_string(static_cast<int32_t>(marker)));
|
||||||
}
|
}
|
||||||
@@ -88,6 +89,11 @@ const std::string AMFDeserialize::ReadString(RakNet::BitStream& inStream) {
|
|||||||
// Right shift by 1 bit to get index if reference or size of next string if value
|
// Right shift by 1 bit to get index if reference or size of next string if value
|
||||||
length = length >> 1;
|
length = length >> 1;
|
||||||
if (isReference) {
|
if (isReference) {
|
||||||
|
constexpr int32_t maxStringSize = 1024 * 1024;
|
||||||
|
if (length > maxStringSize) {
|
||||||
|
LOG("1MB string attempted to be allocated in AMF deserialize, possible spoof, aborting deserialize.");
|
||||||
|
throw std::invalid_argument("1MB string attempted to be allocated in AMF deserialize, possible spoof, aborting deserialize.");
|
||||||
|
}
|
||||||
std::string value(length, 0);
|
std::string value(length, 0);
|
||||||
inStream.Read(&value[0], length);
|
inStream.Read(&value[0], length);
|
||||||
// Empty strings are never sent by reference
|
// Empty strings are never sent by reference
|
||||||
@@ -117,6 +123,12 @@ std::unique_ptr<AMFArrayValue> AMFDeserialize::ReadAmfArray(RakNet::BitStream& i
|
|||||||
if (key.size() == 0) break;
|
if (key.size() == 0) break;
|
||||||
arrayValue->Insert(key, Read(inStream));
|
arrayValue->Insert(key, Read(inStream));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr int32_t maxArraySize = 10'000;
|
||||||
|
if (sizeOfDenseArray > maxArraySize) {
|
||||||
|
LOG("Someone sent 10,000 dense array entries, probably a bad packet.");
|
||||||
|
throw std::invalid_argument("Someone sent 10,000 dense array entries, probably a bad packet.");
|
||||||
|
}
|
||||||
// Finally read dense portion
|
// Finally read dense portion
|
||||||
for (uint32_t i = 0; i < sizeOfDenseArray; i++) {
|
for (uint32_t i = 0; i < sizeOfDenseArray; i++) {
|
||||||
arrayValue->Insert(i, Read(inStream));
|
arrayValue->Insert(i, Read(inStream));
|
||||||
|
|||||||
@@ -52,8 +52,7 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
|||||||
|
|
||||||
if (actualUncompressedSize != -1) {
|
if (actualUncompressedSize != -1) {
|
||||||
uint32_t previousSize = completeUncompressedModel.size();
|
uint32_t previousSize = completeUncompressedModel.size();
|
||||||
completeUncompressedModel.append(reinterpret_cast<char*>(uncompressedChunk.get()));
|
completeUncompressedModel.append(reinterpret_cast<char*>(uncompressedChunk.get()), actualUncompressedSize);
|
||||||
completeUncompressedModel.resize(previousSize + actualUncompressedSize);
|
|
||||||
} else {
|
} else {
|
||||||
LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, model.id, err);
|
LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, model.id, err);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -308,8 +308,9 @@ std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::stri
|
|||||||
for (const auto& t : std::filesystem::directory_iterator(folder)) {
|
for (const auto& t : std::filesystem::directory_iterator(folder)) {
|
||||||
if (t.is_directory() || t.is_symlink()) continue;
|
if (t.is_directory() || t.is_symlink()) continue;
|
||||||
auto filename = t.path().filename().string();
|
auto filename = t.path().filename().string();
|
||||||
const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
|
// Ensure the file has a name in the format of xxxxxxxx_anything_goes_here.sql
|
||||||
filenames.emplace(index, std::move(filename));
|
const auto migrationNumber = TryParse<uint32_t>(GeneralUtils::SplitString(filename, '_').at(0));
|
||||||
|
if (migrationNumber.has_value()) filenames.emplace(migrationNumber.value(), std::move(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now sort the map by the oldest migration.
|
// Now sort the map by the oldest migration.
|
||||||
|
|||||||
@@ -205,6 +205,12 @@ namespace GeneralUtils {
|
|||||||
return isParsed ? static_cast<T>(result) : std::optional<T>{};
|
return isParsed ? static_cast<T>(result) : std::optional<T>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A version of TryParse that will return `errorVal` if `str` failed to parse.
|
||||||
|
template <Numeric T>
|
||||||
|
[[nodiscard]] T TryParse(std::string_view str, const T errorVal) {
|
||||||
|
return TryParse<T>(str).value_or(errorVal);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
requires(!Numeric<T>)
|
requires(!Numeric<T>)
|
||||||
[[nodiscard]] std::optional<T> TryParse(std::string_view str);
|
[[nodiscard]] std::optional<T> TryParse(std::string_view str);
|
||||||
@@ -237,6 +243,12 @@ namespace GeneralUtils {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A version of TryParse that will return `errorVal` if `str` failed to parse.
|
||||||
|
template <std::floating_point T>
|
||||||
|
[[nodiscard]] T TryParse(std::string_view str, const T errorVal) {
|
||||||
|
return TryParse<T>(str).value_or(errorVal);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -258,6 +270,11 @@ namespace GeneralUtils {
|
|||||||
return z ? std::make_optional<T>(x.value(), y.value(), z.value()) : std::nullopt;
|
return z ? std::make_optional<T>(x.value(), y.value(), z.value()) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alternative overload of TryParse with a default value
|
||||||
|
[[nodiscard]] inline NiPoint3 TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ, const NiPoint3 errorVal) {
|
||||||
|
return TryParse<NiPoint3>(strX, strY, strZ).value_or(errorVal);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TryParse overload for handling NiPoint3 by passing a span of three strings
|
* The TryParse overload for handling NiPoint3 by passing a span of three strings
|
||||||
* @param str The string vector representing the X, Y, and Z coordinates
|
* @param str The string vector representing the X, Y, and Z coordinates
|
||||||
@@ -268,6 +285,11 @@ namespace GeneralUtils {
|
|||||||
return (str.size() == 3) ? TryParse<T>(str[0], str[1], str[2]) : std::nullopt;
|
return (str.size() == 3) ? TryParse<T>(str[0], str[1], str[2]) : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alternative overload of TryParse with a default value
|
||||||
|
[[nodiscard]] inline NiPoint3 TryParse(const std::span<const std::string> str, const NiPoint3 errorVal) {
|
||||||
|
return TryParse<NiPoint3>(str).value_or(errorVal);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::u16string to_u16string(const T value) {
|
std::u16string to_u16string(const T value) {
|
||||||
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
|
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
|
||||||
|
|||||||
@@ -53,16 +53,21 @@ Lxfml::Result Lxfml::NormalizePositionOnlyFirstPart(const std::string_view data)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto x = GeneralUtils::TryParse<float>(split[9]).value();
|
try {
|
||||||
auto y = GeneralUtils::TryParse<float>(split[10]).value();
|
auto x = GeneralUtils::TryParse<float>(split[9]).value();
|
||||||
auto z = GeneralUtils::TryParse<float>(split[11]).value();
|
auto y = GeneralUtils::TryParse<float>(split[10]).value();
|
||||||
if (x < lowest.x) lowest.x = x;
|
auto z = GeneralUtils::TryParse<float>(split[11]).value();
|
||||||
if (y < lowest.y) lowest.y = y;
|
if (x < lowest.x) lowest.x = x;
|
||||||
if (z < lowest.z) lowest.z = z;
|
if (y < lowest.y) lowest.y = y;
|
||||||
|
if (z < lowest.z) lowest.z = z;
|
||||||
|
|
||||||
if (highest.x < x) highest.x = x;
|
if (highest.x < x) highest.x = x;
|
||||||
if (highest.y < y) highest.y = y;
|
if (highest.y < y) highest.y = y;
|
||||||
if (highest.z < z) highest.z = z;
|
if (highest.z < z) highest.z = z;
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
LOG("Failed to parse a split value of either (%s), (%s), or (%s).", split[9].c_str(), split[10].c_str(), split[11].c_str());
|
||||||
|
return toReturn; // Early return since we failed to parse this lxfml.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto delta = (highest - lowest) / 2.0f;
|
auto delta = (highest - lowest) / 2.0f;
|
||||||
|
|||||||
@@ -45,6 +45,12 @@ Sd0::Sd0(std::istream& buffer) {
|
|||||||
uint32_t bufferSize = buffer.tellg();
|
uint32_t bufferSize = buffer.tellg();
|
||||||
buffer.seekg(0, std::ios::beg);
|
buffer.seekg(0, std::ios::beg);
|
||||||
WriteSize(firstChunk, bufferSize);
|
WriteSize(firstChunk, bufferSize);
|
||||||
|
// its expected that if we got here, we got an old sd0 buffer where we ignored the sd0 part
|
||||||
|
// that means this can be at most the compressed chunk limit.
|
||||||
|
if (bufferSize > MAX_UNCOMPRESSED_CHUNK_SIZE) {
|
||||||
|
LOG("Possible bad chunk size of %i specified, rejecting.", bufferSize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
firstChunk.resize(firstChunk.size() + bufferSize);
|
firstChunk.resize(firstChunk.size() + bufferSize);
|
||||||
auto* dataStart = reinterpret_cast<char*>(firstChunk.data() + GetDataOffset(true));
|
auto* dataStart = reinterpret_cast<char*>(firstChunk.data() + GetDataOffset(true));
|
||||||
if (!buffer.read(dataStart, bufferSize)) {
|
if (!buffer.read(dataStart, bufferSize)) {
|
||||||
@@ -71,6 +77,12 @@ Sd0::Sd0(std::istream& buffer) {
|
|||||||
|
|
||||||
WriteSize(chunk, chunkSize);
|
WriteSize(chunk, chunkSize);
|
||||||
|
|
||||||
|
// Assuming a good buffer that is large enough to take up 2 zlib buffers
|
||||||
|
// any buffer should be compressed enough to take up less size than its uncompressed counterpart
|
||||||
|
if (chunkSize > MAX_UNCOMPRESSED_CHUNK_SIZE) {
|
||||||
|
LOG("Possible bad chunk size of %i specified, rejecting.", chunkSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
chunk.resize(chunkSize + dataOffset);
|
chunk.resize(chunkSize + dataOffset);
|
||||||
auto* dataStart = reinterpret_cast<char*>(chunk.data() + dataOffset);
|
auto* dataStart = reinterpret_cast<char*>(chunk.data() + dataOffset);
|
||||||
if (!buffer.read(dataStart, chunkSize)) {
|
if (!buffer.read(dataStart, chunkSize)) {
|
||||||
@@ -95,6 +107,11 @@ void Sd0::FromData(const uint8_t* data, size_t bufferSize) {
|
|||||||
startOffset, numToCopy,
|
startOffset, numToCopy,
|
||||||
compressedChunk.data(), compressedChunk.size());
|
compressedChunk.data(), compressedChunk.size());
|
||||||
|
|
||||||
|
if (compressedSize == -1) {
|
||||||
|
LOG("Failed to compress chunk, aborting");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
auto& chunk = m_Chunks.emplace_back();
|
auto& chunk = m_Chunks.emplace_back();
|
||||||
bool firstBuffer = m_Chunks.size() == 1;
|
bool firstBuffer = m_Chunks.size() == 1;
|
||||||
auto dataOffset = GetDataOffset(firstBuffer);
|
auto dataOffset = GetDataOffset(firstBuffer);
|
||||||
@@ -119,6 +136,12 @@ std::string Sd0::GetAsStringUncompressed() const {
|
|||||||
auto dataOffset = GetDataOffset(first);
|
auto dataOffset = GetDataOffset(first);
|
||||||
first = false;
|
first = false;
|
||||||
const auto chunkSize = chunk.size();
|
const auto chunkSize = chunk.size();
|
||||||
|
if (chunkSize <= static_cast<size_t>(dataOffset)) {
|
||||||
|
LOG("Bad chunkSize for data, aborting");
|
||||||
|
toReturn = "";
|
||||||
|
totalSize = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
auto oldSize = toReturn.size();
|
auto oldSize = toReturn.size();
|
||||||
toReturn.resize(oldSize + MAX_UNCOMPRESSED_CHUNK_SIZE);
|
toReturn.resize(oldSize + MAX_UNCOMPRESSED_CHUNK_SIZE);
|
||||||
@@ -128,6 +151,13 @@ std::string Sd0::GetAsStringUncompressed() const {
|
|||||||
reinterpret_cast<uint8_t*>(toReturn.data()) + oldSize, MAX_UNCOMPRESSED_CHUNK_SIZE,
|
reinterpret_cast<uint8_t*>(toReturn.data()) + oldSize, MAX_UNCOMPRESSED_CHUNK_SIZE,
|
||||||
error);
|
error);
|
||||||
|
|
||||||
|
if (uncompressedSize == -1) {
|
||||||
|
LOG("Failed to decompress chunk, aborting");
|
||||||
|
toReturn = "";
|
||||||
|
totalSize = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
totalSize += uncompressedSize;
|
totalSize += uncompressedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
#include "zlib.h"
|
#include "zlib.h"
|
||||||
|
|
||||||
namespace ZCompression {
|
namespace ZCompression {
|
||||||
int32_t GetMaxCompressedLength(int32_t nLenSrc) {
|
uint32_t GetMaxCompressedLength(uint32_t nLenSrc) {
|
||||||
int32_t n16kBlocks = (nLenSrc + 16383) / 16384; // round up any fraction of a block
|
uint32_t n16kBlocks = (nLenSrc + 16383) / 16384; // round up any fraction of a block
|
||||||
return (nLenSrc + 6 + (n16kBlocks * 5));
|
return (nLenSrc + 6 + (n16kBlocks * 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t Compress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst) {
|
int32_t Compress(const uint8_t* abSrc, uint32_t nLenSrc, uint8_t* abDst, uint32_t nLenDst) {
|
||||||
z_stream zInfo = { 0 };
|
z_stream zInfo = { 0 };
|
||||||
zInfo.total_in = zInfo.avail_in = nLenSrc;
|
zInfo.total_in = zInfo.avail_in = nLenSrc;
|
||||||
zInfo.total_out = zInfo.avail_out = nLenDst;
|
zInfo.total_out = zInfo.avail_out = nLenDst;
|
||||||
@@ -27,7 +27,7 @@ namespace ZCompression {
|
|||||||
return(nRet);
|
return(nRet);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t Decompress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst, int32_t& nErr) {
|
int32_t Decompress(const uint8_t* abSrc, uint32_t nLenSrc, uint8_t* abDst, uint32_t nLenDst, int32_t& nErr) {
|
||||||
// Get the size of the decompressed data
|
// Get the size of the decompressed data
|
||||||
z_stream zInfo = { 0 };
|
z_stream zInfo = { 0 };
|
||||||
zInfo.total_in = zInfo.avail_in = nLenSrc;
|
zInfo.total_in = zInfo.avail_in = nLenSrc;
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace ZCompression {
|
namespace ZCompression {
|
||||||
int32_t GetMaxCompressedLength(int32_t nLenSrc);
|
uint32_t GetMaxCompressedLength(uint32_t nLenSrc);
|
||||||
|
|
||||||
int32_t Compress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst);
|
int32_t Compress(const uint8_t* abSrc, uint32_t nLenSrc, uint8_t* abDst, uint32_t nLenDst);
|
||||||
|
|
||||||
int32_t Decompress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst, int32_t& nErr);
|
int32_t Decompress(const uint8_t* abSrc, uint32_t nLenSrc, uint8_t* abDst, uint32_t nLenDst, int32_t& nErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include "zlib.h"
|
#include "zlib.h"
|
||||||
|
|
||||||
constexpr uint32_t CRC32_INIT = 0xFFFFFFFF;
|
constexpr uint32_t CRC32_INIT = 0xFFFFFFFF;
|
||||||
constexpr auto NULL_TERMINATOR = std::string_view{"\0\0\0", 4};
|
constexpr auto NULL_TERMINATOR = std::string_view{ "\0\0\0", 4 };
|
||||||
|
|
||||||
AssetManager::AssetManager(const std::filesystem::path& path) {
|
AssetManager::AssetManager(const std::filesystem::path& path) {
|
||||||
if (!std::filesystem::is_directory(path)) {
|
if (!std::filesystem::is_directory(path)) {
|
||||||
@@ -25,7 +25,7 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
|
|||||||
if (!std::filesystem::exists(m_Path / ".." / "versions")) {
|
if (!std::filesystem::exists(m_Path / ".." / "versions")) {
|
||||||
throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded.");
|
throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded.");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_AssetBundleType = eAssetBundleType::Packed;
|
m_AssetBundleType = eAssetBundleType::Packed;
|
||||||
|
|
||||||
m_RootPath = (m_Path / "..");
|
m_RootPath = (m_Path / "..");
|
||||||
@@ -34,7 +34,7 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
|
|||||||
if (!std::filesystem::exists(m_Path / ".." / ".." / "versions")) {
|
if (!std::filesystem::exists(m_Path / ".." / ".." / "versions")) {
|
||||||
throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded.");
|
throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded.");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_AssetBundleType = eAssetBundleType::Packed;
|
m_AssetBundleType = eAssetBundleType::Packed;
|
||||||
|
|
||||||
m_RootPath = (m_Path / ".." / "..");
|
m_RootPath = (m_Path / ".." / "..");
|
||||||
@@ -54,15 +54,15 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (m_AssetBundleType) {
|
switch (m_AssetBundleType) {
|
||||||
case eAssetBundleType::Packed: {
|
case eAssetBundleType::Packed: {
|
||||||
this->LoadPackIndex();
|
this->LoadPackIndex();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case eAssetBundleType::None:
|
case eAssetBundleType::None:
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case eAssetBundleType::Unpacked: {
|
case eAssetBundleType::Unpacked: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ bool AssetManager::HasFile(std::string fixedName) const {
|
|||||||
std::replace(fixedName.begin(), fixedName.end(), '\\', '/');
|
std::replace(fixedName.begin(), fixedName.end(), '\\', '/');
|
||||||
if (std::filesystem::exists(m_ResPath / fixedName)) return true;
|
if (std::filesystem::exists(m_ResPath / fixedName)) return true;
|
||||||
|
|
||||||
if (this->m_AssetBundleType == eAssetBundleType::Unpacked) return false;
|
if (this->m_AssetBundleType == eAssetBundleType::Unpacked || !m_PackIndex) return false;
|
||||||
|
|
||||||
std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
|
std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
|
||||||
if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
|
if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
|
||||||
@@ -145,8 +145,12 @@ bool AssetManager::GetFile(std::string fixedName, char** data, uint32_t* len) co
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto& pack = this->m_PackIndex->GetPacks().at(packIndex);
|
const auto& pack = this->m_PackIndex->GetPacks().at(packIndex);
|
||||||
const bool success = pack.ReadFileFromPack(crc, data, len);
|
bool success = false;
|
||||||
|
try {
|
||||||
|
success = pack.ReadFileFromPack(crc, data, len);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
LOG("Failed to read file %s from pack file", fixedName.c_str());
|
||||||
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ bool Pack::HasFile(const uint32_t crc) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) const {
|
bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) const {
|
||||||
|
const auto pathStr = m_FilePath.string();
|
||||||
// Time for some wacky C file reading for speed reasons
|
// Time for some wacky C file reading for speed reasons
|
||||||
|
|
||||||
PackRecord pkRecord{};
|
PackRecord pkRecord{};
|
||||||
@@ -65,16 +66,21 @@ bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) cons
|
|||||||
bool isCompressed = (pkRecord.m_IsCompressed & 0xff) > 0;
|
bool isCompressed = (pkRecord.m_IsCompressed & 0xff) > 0;
|
||||||
auto inPackSize = isCompressed ? pkRecord.m_CompressedSize : pkRecord.m_UncompressedSize;
|
auto inPackSize = isCompressed ? pkRecord.m_CompressedSize : pkRecord.m_UncompressedSize;
|
||||||
|
|
||||||
FILE* file;
|
FILE* file = nullptr;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
fopen_s(&file, m_FilePath.string().c_str(), "rb");
|
fopen_s(&file, pathStr.c_str(), "rb");
|
||||||
#elif __APPLE__
|
#elif __APPLE__
|
||||||
// macOS has 64bit file IO by default
|
// macOS has 64bit file IO by default
|
||||||
file = fopen(m_FilePath.string().c_str(), "rb");
|
file = fopen(pathStr.c_str(), "rb");
|
||||||
#else
|
#else
|
||||||
file = fopen64(m_FilePath.string().c_str(), "rb");
|
file = fopen64(pathStr.c_str(), "rb");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
LOG("No file found for path %s", pathStr.c_str());
|
||||||
|
throw std::runtime_error("Could not find file " + pathStr);
|
||||||
|
}
|
||||||
|
|
||||||
fseek(file, pos, SEEK_SET);
|
fseek(file, pos, SEEK_SET);
|
||||||
|
|
||||||
if (!isCompressed) {
|
if (!isCompressed) {
|
||||||
@@ -102,14 +108,18 @@ bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) cons
|
|||||||
int32_t readInData = fread(&size, sizeof(uint32_t), 1, file);
|
int32_t readInData = fread(&size, sizeof(uint32_t), 1, file);
|
||||||
pos += 4; // Move pointer position 4 to the right
|
pos += 4; // Move pointer position 4 to the right
|
||||||
|
|
||||||
char* chunk = static_cast<char*>(malloc(size));
|
std::unique_ptr<char[]> chunk(new char[size]);
|
||||||
int32_t readInData2 = fread(chunk, sizeof(int8_t), size, file);
|
int32_t readInData2 = fread(chunk.get(), sizeof(int8_t), size, file);
|
||||||
pos += size; // Move pointer position the amount of bytes read to the right
|
pos += size; // Move pointer position the amount of bytes read to the right
|
||||||
|
|
||||||
int32_t err;
|
int32_t err;
|
||||||
currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE, err);
|
const auto countToRead = ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk.get()), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE, err);
|
||||||
|
if (countToRead == -1) {
|
||||||
|
LOG("Error decompressing zlib data from file %s", pathStr.c_str());
|
||||||
|
throw std::runtime_error("Error decompressing zlib data from file " + pathStr);
|
||||||
|
}
|
||||||
|
currentReadPos += countToRead;
|
||||||
|
|
||||||
free(chunk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*data = decompressedData;
|
*data = decompressedData;
|
||||||
|
|||||||
@@ -84,3 +84,7 @@ void dConfig::ProcessLine(const std::string& line) {
|
|||||||
|
|
||||||
this->m_ConfigValues.insert(std::make_pair(key, value));
|
this->m_ConfigValues.insert(std::make_pair(key, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string dConfig::GetValue(const std::string& key, const char* emptyValue) {
|
||||||
|
return GetValue(key, std::string(emptyValue));
|
||||||
|
};
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "GeneralUtils.h"
|
||||||
|
|
||||||
class dConfig {
|
class dConfig {
|
||||||
public:
|
public:
|
||||||
dConfig(const std::string& filepath);
|
dConfig(const std::string& filepath);
|
||||||
@@ -22,6 +24,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
const std::string& GetValue(std::string key);
|
const std::string& GetValue(std::string key);
|
||||||
|
|
||||||
|
// Gets a value from the config and returns the parsed value, or the default value should parsing have failed.
|
||||||
|
template<typename T>
|
||||||
|
T GetValue(const std::string& key, const T emptyValue = T()) {
|
||||||
|
return GeneralUtils::TryParse<T>(GetValue(key)).value_or(emptyValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetValue(const std::string& key, const char* emptyValue);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the config from a file
|
* Loads the config from a file
|
||||||
*/
|
*/
|
||||||
@@ -43,3 +53,9 @@ private:
|
|||||||
std::vector<std::function<void()>> m_ConfigHandlers;
|
std::vector<std::function<void()>> m_ConfigHandlers;
|
||||||
std::string m_ConfigFilePath;
|
std::string m_ConfigFilePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline std::string dConfig::GetValue(const std::string& key, const std::string emptyValue) {
|
||||||
|
const auto& value = GetValue(key);
|
||||||
|
return value.empty() ? emptyValue : value;
|
||||||
|
};
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
// These are the same define, but they mean two different things in different contexts
|
// These are the same define, but they mean two different things in different contexts
|
||||||
// so a different define to distinguish what calculation is happening will help clarity.
|
// so a different define to distinguish what calculation is happening will help clarity.
|
||||||
#define FRAMES_TO_MS(x) 1000 / x
|
#define FRAMES_TO_MS(x) (1000 / (x))
|
||||||
#define MS_TO_FRAMES(x) 1000 / x
|
#define MS_TO_FRAMES(x) (1000 / (x))
|
||||||
|
|
||||||
//=========== FRAME TIMINGS ===========
|
//=========== FRAME TIMINGS ===========
|
||||||
constexpr uint32_t highFramerate = 60;
|
constexpr uint32_t highFramerate = 60;
|
||||||
|
|||||||
@@ -154,8 +154,8 @@ std::map<LOT, uint32_t> CDItemComponentTable::ParseCraftingCurrencies(const CDIt
|
|||||||
// Checking for 2 here, not sure what to do when there's more stuff than expected
|
// Checking for 2 here, not sure what to do when there's more stuff than expected
|
||||||
if (amountSplit.size() == 2) {
|
if (amountSplit.size() == 2) {
|
||||||
currencies.insert({
|
currencies.insert({
|
||||||
std::stoull(amountSplit[0]),
|
GeneralUtils::TryParse<LOT>(amountSplit[0], LOT_NULL),
|
||||||
std::stoi(amountSplit[1])
|
GeneralUtils::TryParse<uint32_t>(amountSplit[1], 0)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,13 +93,14 @@ std::vector<CDMissions> CDMissionsTable::Query(std::function<bool(CDMissions)> p
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CDMissions* CDMissionsTable::GetPtrByMissionID(uint32_t missionID) const {
|
const CDMissions* CDMissionsTable::GetPtrByMissionID(uint32_t missionID) const {
|
||||||
|
const CDMissions* toReturn = &Default;
|
||||||
for (const auto& entry : GetEntries()) {
|
for (const auto& entry : GetEntries()) {
|
||||||
if (entry.id == missionID) {
|
if (entry.id == missionID) {
|
||||||
return const_cast<CDMissions*>(&entry);
|
toReturn = &entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Default;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CDMissions& CDMissionsTable::GetByMissionID(uint32_t missionID, bool& found) const {
|
const CDMissions& CDMissionsTable::GetByMissionID(uint32_t missionID, bool& found) const {
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ public:
|
|||||||
// Queries the table with a custom "where" clause
|
// Queries the table with a custom "where" clause
|
||||||
std::vector<CDMissions> Query(std::function<bool(CDMissions)> predicate);
|
std::vector<CDMissions> Query(std::function<bool(CDMissions)> predicate);
|
||||||
|
|
||||||
|
// Cannot be null.
|
||||||
const CDMissions* GetPtrByMissionID(uint32_t missionID) const;
|
const CDMissions* GetPtrByMissionID(uint32_t missionID) const;
|
||||||
|
|
||||||
const CDMissions& GetByMissionID(uint32_t missionID, bool& found) const;
|
const CDMissions& GetByMissionID(uint32_t missionID, bool& found) const;
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ const CDObjects& CDObjectsTable::GetByID(const uint32_t lot) {
|
|||||||
entry.name = tableData.getStringField("name", "");
|
entry.name = tableData.getStringField("name", "");
|
||||||
UNUSED(entry.placeable = tableData.getIntField("placeable", -1));
|
UNUSED(entry.placeable = tableData.getIntField("placeable", -1));
|
||||||
entry.type = tableData.getStringField("type", "");
|
entry.type = tableData.getStringField("type", "");
|
||||||
UNUSED(ntry.description = tableData.getStringField(4, ""));
|
UNUSED(entry.description = tableData.getStringField(4, ""));
|
||||||
UNUSED(entry.localize = tableData.getIntField("localize", -1));
|
UNUSED(entry.localize = tableData.getIntField("localize", -1));
|
||||||
UNUSED(entry.npcTemplateID = tableData.getIntField("npcTemplateID", -1));
|
UNUSED(entry.npcTemplateID = tableData.getIntField("npcTemplateID", -1));
|
||||||
UNUSED(entry.displayName = tableData.getStringField("displayName", ""));
|
UNUSED(entry.displayName = tableData.getStringField("displayName", ""));
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ CDRailActivatorComponent CDRailActivatorComponentTable::GetEntryByID(int32_t id)
|
|||||||
std::pair<uint32_t, std::u16string> CDRailActivatorComponentTable::EffectPairFromString(std::string& str) {
|
std::pair<uint32_t, std::u16string> CDRailActivatorComponentTable::EffectPairFromString(std::string& str) {
|
||||||
const auto split = GeneralUtils::SplitString(str, ':');
|
const auto split = GeneralUtils::SplitString(str, ':');
|
||||||
if (split.size() == 2) {
|
if (split.size() == 2) {
|
||||||
return { std::stoi(split.at(0)), GeneralUtils::ASCIIToUTF16(split.at(1)) };
|
return { GeneralUtils::TryParse(split.at(0), 0), GeneralUtils::ASCIIToUTF16(split.at(1)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct PropertyEntranceResult {
|
struct PropertyEntranceResult {
|
||||||
|
// This is the number of entries that are in the query IF it were ran without a limit.
|
||||||
int32_t totalEntriesMatchingQuery{};
|
int32_t totalEntriesMatchingQuery{};
|
||||||
// The entries that match the query. This should only contain up to 12 entries.
|
// The entries that match the query. This should only contain up to 12 entries.
|
||||||
std::vector<IProperty::Info> entries;
|
std::vector<IProperty::Info> entries;
|
||||||
@@ -48,7 +49,7 @@ public:
|
|||||||
// Get the properties for the given property lookup params.
|
// Get the properties for the given property lookup params.
|
||||||
// This is expected to return a result set of up to 12 properties
|
// This is expected to return a result set of up to 12 properties
|
||||||
// so as not to transfer too much data at once.
|
// so as not to transfer too much data at once.
|
||||||
virtual std::optional<IProperty::PropertyEntranceResult> GetProperties(const PropertyLookup& params) = 0;
|
virtual IProperty::PropertyEntranceResult GetProperties(const PropertyLookup& params) = 0;
|
||||||
|
|
||||||
// Update the property moderation info for the given property id.
|
// Update the property moderation info for the given property id.
|
||||||
virtual void UpdatePropertyModerationInfo(const IProperty::Info& info) = 0;
|
virtual void UpdatePropertyModerationInfo(const IProperty::Info& info) = 0;
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ public:
|
|||||||
std::string GetBehavior(const LWOOBJID behaviorId) override;
|
std::string GetBehavior(const LWOOBJID behaviorId) override;
|
||||||
void RemoveBehavior(const LWOOBJID characterId) override;
|
void RemoveBehavior(const LWOOBJID characterId) override;
|
||||||
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
||||||
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
|
IProperty::PropertyEntranceResult GetProperties(const IProperty::PropertyLookup& params) override;
|
||||||
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
|
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
|
||||||
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override;
|
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override;
|
||||||
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override;
|
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ std::vector<MailInfo> MySQLDatabase::GetMailForPlayer(const LWOOBJID characterId
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<MailInfo> MySQLDatabase::GetMail(const uint64_t mailId) {
|
std::optional<MailInfo> MySQLDatabase::GetMail(const uint64_t mailId) {
|
||||||
auto res = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId);
|
auto res = ExecuteSelect("SELECT attachment_lot, attachment_count, receiver_id FROM mail WHERE id=? LIMIT 1;", mailId);
|
||||||
|
|
||||||
if (!res->next()) {
|
if (!res->next()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -57,6 +57,7 @@ std::optional<MailInfo> MySQLDatabase::GetMail(const uint64_t mailId) {
|
|||||||
MailInfo toReturn;
|
MailInfo toReturn;
|
||||||
toReturn.itemLOT = res->getInt("attachment_lot");
|
toReturn.itemLOT = res->getInt("attachment_lot");
|
||||||
toReturn.itemCount = res->getInt("attachment_count");
|
toReturn.itemCount = res->getInt("attachment_count");
|
||||||
|
toReturn.receiverId = res->getUInt64("receiver_id");
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ IProperty::Info ReadPropertyInfo(PreparedStmtResultSet& result) {
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(const IProperty::PropertyLookup& params) {
|
IProperty::PropertyEntranceResult MySQLDatabase::GetProperties(const IProperty::PropertyLookup& params) {
|
||||||
std::optional<IProperty::PropertyEntranceResult> result;
|
IProperty::PropertyEntranceResult result;
|
||||||
std::string query;
|
std::string query;
|
||||||
PreparedStmtResultSet properties;
|
PreparedStmtResultSet properties;
|
||||||
|
|
||||||
@@ -73,8 +73,7 @@ std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(co
|
|||||||
params.playerId
|
params.playerId
|
||||||
);
|
);
|
||||||
if (count->next()) {
|
if (count->next()) {
|
||||||
if (!result) result = IProperty::PropertyEntranceResult();
|
result.totalEntriesMatchingQuery = count->getUInt("count");
|
||||||
result->totalEntriesMatchingQuery = count->getUInt("count");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (params.sortChoice == SORT_TYPE_REPUTATION) {
|
if (params.sortChoice == SORT_TYPE_REPUTATION) {
|
||||||
@@ -127,14 +126,12 @@ std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(co
|
|||||||
params.playerSort
|
params.playerSort
|
||||||
);
|
);
|
||||||
if (count->next()) {
|
if (count->next()) {
|
||||||
if (!result) result = IProperty::PropertyEntranceResult();
|
result.totalEntriesMatchingQuery = count->getUInt("count");
|
||||||
result->totalEntriesMatchingQuery = count->getUInt("count");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (properties->next()) {
|
while (properties->next()) {
|
||||||
if (!result) result = IProperty::PropertyEntranceResult();
|
result.entries.push_back(ReadPropertyInfo(properties));
|
||||||
result->entries.push_back(ReadPropertyInfo(properties));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public:
|
|||||||
std::string GetBehavior(const LWOOBJID behaviorId) override;
|
std::string GetBehavior(const LWOOBJID behaviorId) override;
|
||||||
void RemoveBehavior(const LWOOBJID characterId) override;
|
void RemoveBehavior(const LWOOBJID characterId) override;
|
||||||
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
||||||
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
|
IProperty::PropertyEntranceResult GetProperties(const IProperty::PropertyLookup& params) override;
|
||||||
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
|
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
|
||||||
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override;
|
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override;
|
||||||
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override;
|
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override;
|
||||||
@@ -170,91 +170,91 @@ private:
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const std::string_view param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const std::string_view param) {
|
||||||
LOG("%s", param.data());
|
LOG_DEBUG("%s", param.data());
|
||||||
stmt.bind(index, param.data());
|
stmt.bind(index, param.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const char* param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const char* param) {
|
||||||
LOG("%s", param);
|
LOG_DEBUG("%s", param);
|
||||||
stmt.bind(index, param);
|
stmt.bind(index, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const std::string param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const std::string param) {
|
||||||
LOG("%s", param.c_str());
|
LOG_DEBUG("%s", param.c_str());
|
||||||
stmt.bind(index, param.c_str());
|
stmt.bind(index, param.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const int8_t param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const int8_t param) {
|
||||||
LOG("%u", param);
|
LOG_DEBUG("%u", param);
|
||||||
stmt.bind(index, param);
|
stmt.bind(index, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const uint8_t param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const uint8_t param) {
|
||||||
LOG("%d", param);
|
LOG_DEBUG("%d", param);
|
||||||
stmt.bind(index, param);
|
stmt.bind(index, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const int16_t param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const int16_t param) {
|
||||||
LOG("%u", param);
|
LOG_DEBUG("%u", param);
|
||||||
stmt.bind(index, param);
|
stmt.bind(index, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const uint16_t param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const uint16_t param) {
|
||||||
LOG("%d", param);
|
LOG_DEBUG("%d", param);
|
||||||
stmt.bind(index, param);
|
stmt.bind(index, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const uint32_t param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const uint32_t param) {
|
||||||
LOG("%u", param);
|
LOG_DEBUG("%u", param);
|
||||||
stmt.bind(index, static_cast<int32_t>(param));
|
stmt.bind(index, static_cast<int32_t>(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const int32_t param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const int32_t param) {
|
||||||
LOG("%d", param);
|
LOG_DEBUG("%d", param);
|
||||||
stmt.bind(index, param);
|
stmt.bind(index, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const int64_t param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const int64_t param) {
|
||||||
LOG("%llu", param);
|
LOG_DEBUG("%llu", param);
|
||||||
stmt.bind(index, static_cast<sqlite_int64>(param));
|
stmt.bind(index, static_cast<sqlite_int64>(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const uint64_t param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const uint64_t param) {
|
||||||
LOG("%llu", param);
|
LOG_DEBUG("%llu", param);
|
||||||
stmt.bind(index, static_cast<sqlite_int64>(param));
|
stmt.bind(index, static_cast<sqlite_int64>(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const float param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const float param) {
|
||||||
LOG("%f", param);
|
LOG_DEBUG("%f", param);
|
||||||
stmt.bind(index, param);
|
stmt.bind(index, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const double param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const double param) {
|
||||||
LOG("%f", param);
|
LOG_DEBUG("%f", param);
|
||||||
stmt.bind(index, param);
|
stmt.bind(index, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const bool param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const bool param) {
|
||||||
LOG("%d", param);
|
LOG_DEBUG("%d", param);
|
||||||
stmt.bind(index, param);
|
stmt.bind(index, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const std::istream* param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const std::istream* param) {
|
||||||
LOG("Blob");
|
LOG_DEBUG("Blob");
|
||||||
// This is the one time you will ever see me use const_cast.
|
// This is the one time you will ever see me use const_cast.
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
stream << param->rdbuf();
|
stream << param->rdbuf();
|
||||||
@@ -264,10 +264,10 @@ inline void SetParam(PreppedStmtRef stmt, const int index, const std::istream* p
|
|||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const std::optional<uint32_t> param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const std::optional<uint32_t> param) {
|
||||||
if (param) {
|
if (param) {
|
||||||
LOG("%d", param.value());
|
LOG_DEBUG("%d", param.value());
|
||||||
stmt.bind(index, static_cast<int>(param.value()));
|
stmt.bind(index, static_cast<int>(param.value()));
|
||||||
} else {
|
} else {
|
||||||
LOG("Null");
|
LOG_DEBUG("Null");
|
||||||
stmt.bindNull(index);
|
stmt.bindNull(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,10 +275,10 @@ inline void SetParam(PreppedStmtRef stmt, const int index, const std::optional<u
|
|||||||
template<>
|
template<>
|
||||||
inline void SetParam(PreppedStmtRef stmt, const int index, const std::optional<LWOOBJID> param) {
|
inline void SetParam(PreppedStmtRef stmt, const int index, const std::optional<LWOOBJID> param) {
|
||||||
if (param) {
|
if (param) {
|
||||||
LOG("%d", param.value());
|
LOG_DEBUG("%d", param.value());
|
||||||
stmt.bind(index, static_cast<sqlite_int64>(param.value()));
|
stmt.bind(index, static_cast<sqlite_int64>(param.value()));
|
||||||
} else {
|
} else {
|
||||||
LOG("Null");
|
LOG_DEBUG("Null");
|
||||||
stmt.bindNull(index);
|
stmt.bindNull(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ std::vector<MailInfo> SQLiteDatabase::GetMailForPlayer(const LWOOBJID characterI
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<MailInfo> SQLiteDatabase::GetMail(const uint64_t mailId) {
|
std::optional<MailInfo> SQLiteDatabase::GetMail(const uint64_t mailId) {
|
||||||
auto [_, res] = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId);
|
auto [_, res] = ExecuteSelect("SELECT attachment_lot, attachment_count, receiver_id FROM mail WHERE id=? LIMIT 1;", mailId);
|
||||||
|
|
||||||
if (res.eof()) {
|
if (res.eof()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -56,6 +56,7 @@ std::optional<MailInfo> SQLiteDatabase::GetMail(const uint64_t mailId) {
|
|||||||
MailInfo toReturn;
|
MailInfo toReturn;
|
||||||
toReturn.itemLOT = res.getIntField("attachment_lot");
|
toReturn.itemLOT = res.getIntField("attachment_lot");
|
||||||
toReturn.itemCount = res.getIntField("attachment_count");
|
toReturn.itemCount = res.getIntField("attachment_count");
|
||||||
|
toReturn.receiverId = res.getInt64Field("receiver_id");
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ IProperty::Info ReadPropertyInfo(CppSQLite3Query& propertyEntry) {
|
|||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<IProperty::PropertyEntranceResult> SQLiteDatabase::GetProperties(const IProperty::PropertyLookup& params) {
|
IProperty::PropertyEntranceResult SQLiteDatabase::GetProperties(const IProperty::PropertyLookup& params) {
|
||||||
std::optional<IProperty::PropertyEntranceResult> result;
|
IProperty::PropertyEntranceResult result;
|
||||||
std::string query;
|
std::string query;
|
||||||
std::pair<CppSQLite3Statement, CppSQLite3Query> propertiesRes;
|
std::pair<CppSQLite3Statement, CppSQLite3Query> propertiesRes;
|
||||||
|
|
||||||
@@ -73,8 +73,7 @@ std::optional<IProperty::PropertyEntranceResult> SQLiteDatabase::GetProperties(c
|
|||||||
params.playerId
|
params.playerId
|
||||||
);
|
);
|
||||||
if (!count.eof()) {
|
if (!count.eof()) {
|
||||||
result = IProperty::PropertyEntranceResult();
|
result.totalEntriesMatchingQuery = count.getIntField("count");
|
||||||
result->totalEntriesMatchingQuery = count.getIntField("count");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (params.sortChoice == SORT_TYPE_REPUTATION) {
|
if (params.sortChoice == SORT_TYPE_REPUTATION) {
|
||||||
@@ -127,15 +126,13 @@ std::optional<IProperty::PropertyEntranceResult> SQLiteDatabase::GetProperties(c
|
|||||||
params.playerSort
|
params.playerSort
|
||||||
);
|
);
|
||||||
if (!count.eof()) {
|
if (!count.eof()) {
|
||||||
result = IProperty::PropertyEntranceResult();
|
result.totalEntriesMatchingQuery = count.getIntField("count");
|
||||||
result->totalEntriesMatchingQuery = count.getIntField("count");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& [_, properties] = propertiesRes;
|
auto& [_, properties] = propertiesRes;
|
||||||
if (!properties.eof() && !result.has_value()) result = IProperty::PropertyEntranceResult();
|
|
||||||
while (!properties.eof()) {
|
while (!properties.eof()) {
|
||||||
result->entries.push_back(ReadPropertyInfo(properties));
|
result.entries.push_back(ReadPropertyInfo(properties));
|
||||||
properties.nextRow();
|
properties.nextRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class TestSQLDatabase : public GameDatabase {
|
|||||||
std::string GetBehavior(const LWOOBJID behaviorId) override;
|
std::string GetBehavior(const LWOOBJID behaviorId) override;
|
||||||
void RemoveBehavior(const LWOOBJID behaviorId) override;
|
void RemoveBehavior(const LWOOBJID behaviorId) override;
|
||||||
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
||||||
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override { return {}; };
|
IProperty::PropertyEntranceResult GetProperties(const IProperty::PropertyLookup& params) override { return {}; };
|
||||||
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override { return {}; };
|
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override { return {}; };
|
||||||
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override { return {}; };
|
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override { return {}; };
|
||||||
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override { return {}; };
|
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override { return {}; };
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ public:
|
|||||||
|
|
||||||
uint16_t GetNetworkId() const;
|
uint16_t GetNetworkId() const;
|
||||||
|
|
||||||
|
// Cannot return nullptr.
|
||||||
Entity* GetOwner() const;
|
Entity* GetOwner() const;
|
||||||
|
|
||||||
const NiPoint3& GetDefaultPosition() const;
|
const NiPoint3& GetDefaultPosition() const;
|
||||||
|
|||||||
@@ -30,6 +30,21 @@ void AirMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream& bitS
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// So a player can't send an arbitrary behaviorID in a modified client and cast any behavior on any air behavior
|
||||||
|
Behavior* toSync = nullptr;
|
||||||
|
if (m_GroundAction->GetBehaviorID() == behaviorId) {
|
||||||
|
toSync = m_GroundAction;
|
||||||
|
} else if (m_HitAction->GetBehaviorID() == behaviorId) {
|
||||||
|
toSync = m_HitAction;
|
||||||
|
} else if (m_HitActionEnemy->GetBehaviorID() == behaviorId) {
|
||||||
|
toSync = m_HitActionEnemy;
|
||||||
|
} else if (m_TimeoutAction->GetBehaviorID() == behaviorId) {
|
||||||
|
toSync = m_TimeoutAction;
|
||||||
|
} else {
|
||||||
|
LOG("Invalid Air Movement Behavior sync for behaviorID %i on behavior %i", behaviorId, m_behaviorId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LWOOBJID target{};
|
LWOOBJID target{};
|
||||||
|
|
||||||
if (!bitStream.Read(target)) {
|
if (!bitStream.Read(target)) {
|
||||||
@@ -37,15 +52,17 @@ void AirMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream& bitS
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* behavior = CreateBehavior(behaviorId);
|
|
||||||
|
|
||||||
if (Game::entityManager->GetEntity(target) != nullptr) {
|
if (Game::entityManager->GetEntity(target) != nullptr) {
|
||||||
branch.target = target;
|
branch.target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
behavior->Handle(context, bitStream, branch);
|
toSync->Handle(context, bitStream, branch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AirMovementBehavior::Load() {
|
void AirMovementBehavior::Load() {
|
||||||
this->m_Timeout = (GetFloat("timeout_ms") / 1000.0f);
|
m_Timeout = (GetFloat("timeout_ms") / 1000.0f);
|
||||||
|
m_GroundAction = GetAction("ground_action");
|
||||||
|
m_HitAction = GetAction("hit_action");
|
||||||
|
m_HitActionEnemy = GetAction("hit_action_enemy");
|
||||||
|
m_TimeoutAction = GetAction("timeout_action");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,4 +15,9 @@ public:
|
|||||||
void Load() override;
|
void Load() override;
|
||||||
private:
|
private:
|
||||||
float m_Timeout;
|
float m_Timeout;
|
||||||
|
|
||||||
|
Behavior* m_GroundAction{};
|
||||||
|
Behavior* m_HitAction{};
|
||||||
|
Behavior* m_HitActionEnemy{};
|
||||||
|
Behavior* m_TimeoutAction{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ void AreaOfEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream& b
|
|||||||
LWOOBJID target{};
|
LWOOBJID target{};
|
||||||
if (!bitStream.Read(target)) {
|
if (!bitStream.Read(target)) {
|
||||||
LOG("failed to read in target %i from bitStream, aborting target Handle!", i);
|
LOG("failed to read in target %i from bitStream, aborting target Handle!", i);
|
||||||
|
continue;
|
||||||
};
|
};
|
||||||
targets.push_back(target);
|
targets.push_back(target);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isBlocked) {
|
if (isBlocked) {
|
||||||
destroyableComponent->SetAttacksToBlock(std::min(destroyableComponent->GetAttacksToBlock() - 1, 0U));
|
destroyableComponent->SetAttacksToBlock(std::max<int32_t>(static_cast<int32_t>(destroyableComponent->GetAttacksToBlock() - 1), 0));
|
||||||
Game::entityManager->SerializeEntity(targetEntity);
|
Game::entityManager->SerializeEntity(targetEntity);
|
||||||
this->m_OnFailBlocked->Handle(context, bitStream, branch);
|
this->m_OnFailBlocked->Handle(context, bitStream, branch);
|
||||||
return;
|
return;
|
||||||
@@ -103,9 +103,10 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t totalDamageDealt = armorDamageDealt + healthDamageDealt;
|
uint64_t totalDamageDealt = armorDamageDealt + healthDamageDealt;
|
||||||
|
|
||||||
// A value that's too large may be a cheating attempt, so we set it to MIN
|
// A value that's too large may be a cheating attempt, so we set it to MIN
|
||||||
|
// Can't overflow here either because should we somehow get to a 64 bit number it'll be clamped to a sane value.
|
||||||
if (totalDamageDealt > this->m_MaxDamage) {
|
if (totalDamageDealt > this->m_MaxDamage) {
|
||||||
totalDamageDealt = this->m_MinDamage;
|
totalDamageDealt = this->m_MinDamage;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,15 +48,13 @@ void BlockBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branc
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
auto* const destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||||
|
|
||||||
destroyableComponent->SetAttacksToBlock(this->m_numAttacksCanBlock);
|
if (destroyableComponent) {
|
||||||
|
// ??? what is going on here?
|
||||||
if (destroyableComponent == nullptr) {
|
destroyableComponent->SetAttacksToBlock(this->m_numAttacksCanBlock);
|
||||||
return;
|
destroyableComponent->SetAttacksToBlock(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyableComponent->SetAttacksToBlock(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) {
|
void BlockBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) {
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ void ChainBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStrea
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chainIndex == 0) {
|
||||||
|
LOG("Received invalid chain index of 0 for behavior %i.", m_behaviorId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
chainIndex--;
|
chainIndex--;
|
||||||
|
|
||||||
if (chainIndex < this->m_behaviors.size()) {
|
if (chainIndex < this->m_behaviors.size()) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
void JetPackBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bit_stream, const BehaviorBranchContext branch) {
|
void JetPackBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bit_stream, const BehaviorBranchContext branch) {
|
||||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||||
|
if (!entity) return;
|
||||||
|
|
||||||
GameMessages::SendSetJetPackMode(entity, true, this->m_BypassChecks, this->m_EnableHover, this->m_effectId, this->m_Airspeed, this->m_MaxAirspeed, this->m_VerticalVelocity, this->m_WarningEffectID);
|
GameMessages::SendSetJetPackMode(entity, true, this->m_BypassChecks, this->m_EnableHover, this->m_effectId, this->m_Airspeed, this->m_MaxAirspeed, this->m_VerticalVelocity, this->m_WarningEffectID);
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ void JetPackBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bit_st
|
|||||||
|
|
||||||
void JetPackBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
void JetPackBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
||||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||||
|
if (!entity) return;
|
||||||
|
|
||||||
GameMessages::SendSetJetPackMode(entity, false);
|
GameMessages::SendSetJetPackMode(entity, false);
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ void SwitchMultipleBehavior::Handle(BehaviorContext* context, RakNet::BitStream&
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (m_behaviors.empty()) {
|
||||||
|
LOG("No behaviors were loaded for %i, aborting call.", m_behaviorId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t trigger = 0;
|
uint32_t trigger = 0;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < this->m_behaviors.size(); i++) {
|
for (unsigned int i = 0; i < this->m_behaviors.size(); i++) {
|
||||||
|
|||||||
@@ -478,6 +478,7 @@ std::vector<LWOOBJID> BaseCombatAIComponent::GetTargetWithinAggroRange() const {
|
|||||||
|
|
||||||
for (auto id : m_Parent->GetTargetsInPhantom()) {
|
for (auto id : m_Parent->GetTargetsInPhantom()) {
|
||||||
auto* other = Game::entityManager->GetEntity(id);
|
auto* other = Game::entityManager->GetEntity(id);
|
||||||
|
if (!other) continue;
|
||||||
|
|
||||||
const auto distance = Vector3::DistanceSquared(m_Parent->GetPosition(), other->GetPosition());
|
const auto distance = Vector3::DistanceSquared(m_Parent->GetPosition(), other->GetPosition());
|
||||||
|
|
||||||
|
|||||||
@@ -450,19 +450,10 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
|
|||||||
param.value = result.getFloatField("NumberValue");
|
param.value = result.getFloatField("NumberValue");
|
||||||
param.effectId = result.getIntField("EffectID");
|
param.effectId = result.getIntField("EffectID");
|
||||||
|
|
||||||
if (!result.fieldIsNull("StringValue")) {
|
for (const auto& str : GeneralUtils::SplitString(result.getStringField("StringValue"), ',')) {
|
||||||
std::istringstream stream(result.getStringField("StringValue"));
|
if (str.empty()) continue;
|
||||||
std::string token;
|
const auto value = GeneralUtils::TryParse<float>(str);
|
||||||
|
if (value) param.values.push_back(value.value());
|
||||||
while (std::getline(stream, token, ',')) {
|
|
||||||
try {
|
|
||||||
const auto value = std::stof(token);
|
|
||||||
|
|
||||||
param.values.push_back(value);
|
|
||||||
} catch (std::invalid_argument& exception) {
|
|
||||||
LOG("Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters.push_back(param);
|
parameters.push_back(param);
|
||||||
|
|||||||
@@ -797,8 +797,14 @@ std::string CharacterComponent::StatisticsToString() const {
|
|||||||
return result.str();
|
return result.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t CharacterComponent::GetStatisticFromSplit(std::vector<std::string> split, uint32_t index) {
|
uint64_t CharacterComponent::GetStatisticFromSplit(const std::vector<std::string>& split, const uint32_t index) {
|
||||||
return split.size() > index ? std::stoull(split.at(index)) : 0;
|
uint64_t toReturn = 0;
|
||||||
|
if (index < split.size()) {
|
||||||
|
const auto parsed = GeneralUtils::TryParse<uint64_t>(split[index]);
|
||||||
|
if (parsed) toReturn = *parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
ZoneStatistics& CharacterComponent::GetZoneStatisticsForMap(LWOMAPID mapID) {
|
ZoneStatistics& CharacterComponent::GetZoneStatisticsForMap(LWOMAPID mapID) {
|
||||||
|
|||||||
@@ -450,7 +450,7 @@ private:
|
|||||||
* @param index the statistics ID in the string
|
* @param index the statistics ID in the string
|
||||||
* @return the integer value of this statistic, parsed from the string
|
* @return the integer value of this statistic, parsed from the string
|
||||||
*/
|
*/
|
||||||
static uint64_t GetStatisticFromSplit(std::vector<std::string> split, uint32_t index);
|
static uint64_t GetStatisticFromSplit(const std::vector<std::string>& split, const uint32_t index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all the statistics for a certain map, if it doesn't exist, it creates empty stats
|
* Gets all the statistics for a certain map, if it doesn't exist, it creates empty stats
|
||||||
@@ -526,6 +526,7 @@ private:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Total amount of meters traveled by this character
|
* Total amount of meters traveled by this character
|
||||||
|
* Should be a double and then truncated so decimals can be tracked
|
||||||
*/
|
*/
|
||||||
uint64_t m_MetersTraveled;
|
uint64_t m_MetersTraveled;
|
||||||
|
|
||||||
|
|||||||
@@ -793,7 +793,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
|||||||
|
|
||||||
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
||||||
for (Entity* scriptEntity : scriptedActs) {
|
for (Entity* scriptEntity : scriptedActs) {
|
||||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
if (!zoneControl || scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||||
scriptEntity->GetScript()->OnPlayerDied(scriptEntity, m_Parent);
|
scriptEntity->GetScript()->OnPlayerDied(scriptEntity, m_Parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -964,6 +964,8 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
|||||||
if (m_Parent->IsPlayer()) {
|
if (m_Parent->IsPlayer()) {
|
||||||
//remove hardcore_lose_uscore_on_death_percent from the player's uscore:
|
//remove hardcore_lose_uscore_on_death_percent from the player's uscore:
|
||||||
auto* character = m_Parent->GetComponent<CharacterComponent>();
|
auto* character = m_Parent->GetComponent<CharacterComponent>();
|
||||||
|
if (!character) return;
|
||||||
|
|
||||||
auto uscore = character->GetUScore();
|
auto uscore = character->GetUScore();
|
||||||
|
|
||||||
auto uscoreToLose = static_cast<uint64_t>(uscore * (Game::entityManager->GetHardcoreLoseUscoreOnDeathPercent() / 100.0f));
|
auto uscoreToLose = static_cast<uint64_t>(uscore * (Game::entityManager->GetHardcoreLoseUscoreOnDeathPercent() / 100.0f));
|
||||||
|
|||||||
@@ -966,8 +966,9 @@ void InventoryComponent::EquipScripts(Item* equippedItem) {
|
|||||||
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
|
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
|
||||||
if (!itemScript) {
|
if (!itemScript) {
|
||||||
LOG("null script?");
|
LOG("null script?");
|
||||||
|
} else {
|
||||||
|
itemScript->OnFactionTriggerItemEquipped(m_Parent, equippedItem->GetId());
|
||||||
}
|
}
|
||||||
itemScript->OnFactionTriggerItemEquipped(m_Parent, equippedItem->GetId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -981,8 +982,9 @@ void InventoryComponent::UnequipScripts(Item* unequippedItem) {
|
|||||||
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
|
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
|
||||||
if (!itemScript) {
|
if (!itemScript) {
|
||||||
LOG("null script?");
|
LOG("null script?");
|
||||||
|
} else {
|
||||||
|
itemScript->OnFactionTriggerItemUnequipped(m_Parent, unequippedItem->GetId());
|
||||||
}
|
}
|
||||||
itemScript->OnFactionTriggerItemUnequipped(m_Parent, unequippedItem->GetId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1633,7 +1635,7 @@ void InventoryComponent::LoadPetXml(const tinyxml2::XMLDocument& document) {
|
|||||||
DatabasePet databasePet;
|
DatabasePet databasePet;
|
||||||
databasePet.lot = lot;
|
databasePet.lot = lot;
|
||||||
databasePet.moderationState = moderationStatus;
|
databasePet.moderationState = moderationStatus;
|
||||||
databasePet.name = std::string(name);
|
databasePet.name = name ? name : "";
|
||||||
|
|
||||||
SetDatabasePet(id, databasePet);
|
SetDatabasePet(id, databasePet);
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public:
|
|||||||
void NextLUPExhibit();
|
void NextLUPExhibit();
|
||||||
private:
|
private:
|
||||||
float m_UpdateTimer = 0.0f;
|
float m_UpdateTimer = 0.0f;
|
||||||
std::array<LOT, 4> m_LUPExhibits = { 11121, 11295, 11423, 11979 };
|
const std::array<LOT, 4> m_LUPExhibits = { 11121, 11295, 11423, 11979 };
|
||||||
uint8_t m_LUPExhibitIndex = 0;
|
uint8_t m_LUPExhibitIndex = 0;
|
||||||
bool m_DirtyLUPExhibit = true;
|
bool m_DirtyLUPExhibit = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -449,45 +449,48 @@ void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
|
|||||||
|
|
||||||
if (mis == nullptr) return;
|
if (mis == nullptr) return;
|
||||||
|
|
||||||
auto* cur = mis->FirstChildElement("cur");
|
|
||||||
auto* done = mis->FirstChildElement("done");
|
auto* done = mis->FirstChildElement("done");
|
||||||
|
if (done) {
|
||||||
auto* doneM = done->FirstChildElement();
|
auto* doneM = done->FirstChildElement();
|
||||||
|
|
||||||
while (doneM) {
|
while (doneM) {
|
||||||
int missionId;
|
int missionId;
|
||||||
|
|
||||||
doneM->QueryAttribute("id", &missionId);
|
doneM->QueryAttribute("id", &missionId);
|
||||||
|
|
||||||
auto* mission = new Mission(this, missionId);
|
auto* mission = new Mission(this, missionId);
|
||||||
|
|
||||||
mission->LoadFromXmlDone(*doneM);
|
mission->LoadFromXmlDone(*doneM);
|
||||||
|
|
||||||
doneM = doneM->NextSiblingElement();
|
doneM = doneM->NextSiblingElement();
|
||||||
|
|
||||||
m_Missions.insert_or_assign(missionId, mission);
|
m_Missions.insert_or_assign(missionId, mission);
|
||||||
}
|
|
||||||
|
|
||||||
auto* currentM = cur->FirstChildElement();
|
|
||||||
|
|
||||||
uint32_t missionOrder{};
|
|
||||||
while (currentM) {
|
|
||||||
int missionId;
|
|
||||||
|
|
||||||
currentM->QueryAttribute("id", &missionId);
|
|
||||||
|
|
||||||
auto* mission = m_Missions.contains(missionId) ? m_Missions[missionId] : new Mission(this, missionId);
|
|
||||||
|
|
||||||
mission->LoadFromXmlCur(*currentM);
|
|
||||||
|
|
||||||
if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
|
|
||||||
mission->SetUniqueMissionOrderID(missionOrder);
|
|
||||||
if (missionOrder > m_LastUsedMissionOrderUID) m_LastUsedMissionOrderUID = missionOrder;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* cur = mis->FirstChildElement("cur");
|
||||||
|
if (cur) {
|
||||||
|
auto* currentM = cur->FirstChildElement();
|
||||||
|
|
||||||
currentM = currentM->NextSiblingElement();
|
uint32_t missionOrder{};
|
||||||
|
while (currentM) {
|
||||||
|
int missionId;
|
||||||
|
|
||||||
m_Missions.insert_or_assign(missionId, mission);
|
currentM->QueryAttribute("id", &missionId);
|
||||||
|
|
||||||
|
auto* mission = m_Missions.contains(missionId) ? m_Missions[missionId] : new Mission(this, missionId);
|
||||||
|
|
||||||
|
mission->LoadFromXmlCur(*currentM);
|
||||||
|
|
||||||
|
if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
|
||||||
|
mission->SetUniqueMissionOrderID(missionOrder);
|
||||||
|
if (missionOrder > m_LastUsedMissionOrderUID) m_LastUsedMissionOrderUID = missionOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentM = currentM->NextSiblingElement();
|
||||||
|
|
||||||
|
m_Missions.insert_or_assign(missionId, mission);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ MultiZoneEntranceComponent::MultiZoneEntranceComponent(Entity* parent, const int
|
|||||||
MultiZoneEntranceComponent::~MultiZoneEntranceComponent() {}
|
MultiZoneEntranceComponent::~MultiZoneEntranceComponent() {}
|
||||||
|
|
||||||
void MultiZoneEntranceComponent::OnUse(Entity* originator) {
|
void MultiZoneEntranceComponent::OnUse(Entity* originator) {
|
||||||
auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator);
|
auto* const characterComponent = originator->GetComponent<CharacterComponent>();
|
||||||
|
if (!characterComponent) return;
|
||||||
|
|
||||||
|
auto* rocket = characterComponent->RocketEquip(originator);
|
||||||
if (!rocket) return;
|
if (!rocket) return;
|
||||||
|
|
||||||
// the LUP world menu is just the property menu, the client knows how to handle it
|
// the LUP world menu is just the property menu, the client knows how to handle it
|
||||||
@@ -26,7 +29,7 @@ void MultiZoneEntranceComponent::OnUse(Entity* originator) {
|
|||||||
|
|
||||||
void MultiZoneEntranceComponent::OnSelectWorld(Entity* originator, uint32_t index) {
|
void MultiZoneEntranceComponent::OnSelectWorld(Entity* originator, uint32_t index) {
|
||||||
auto* rocketLaunchpadControlComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>();
|
auto* rocketLaunchpadControlComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>();
|
||||||
if (!rocketLaunchpadControlComponent) return;
|
if (!rocketLaunchpadControlComponent || index >= m_LUPWorlds.size()) return;
|
||||||
|
|
||||||
rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0);
|
rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -922,7 +922,9 @@ void PetComponent::Deactivate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::Release() {
|
void PetComponent::Release() {
|
||||||
auto* inventoryComponent = GetOwner()->GetComponent<InventoryComponent>();
|
auto* const owner = GetOwner();
|
||||||
|
if (!owner) return;
|
||||||
|
auto* const inventoryComponent = owner->GetComponent<InventoryComponent>();
|
||||||
|
|
||||||
if (inventoryComponent == nullptr) {
|
if (inventoryComponent == nullptr) {
|
||||||
return;
|
return;
|
||||||
@@ -932,9 +934,9 @@ void PetComponent::Release() {
|
|||||||
|
|
||||||
inventoryComponent->RemoveDatabasePet(m_DatabaseId);
|
inventoryComponent->RemoveDatabasePet(m_DatabaseId);
|
||||||
|
|
||||||
auto* item = inventoryComponent->FindItemBySubKey(m_DatabaseId);
|
auto* const item = inventoryComponent->FindItemBySubKey(m_DatabaseId);
|
||||||
|
|
||||||
item->SetCount(0, false, false);
|
if (item) item->SetCount(0, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, const int32_t commandType, const int32_t typeId, const bool overrideObey) {
|
void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, const int32_t commandType, const int32_t typeId, const bool overrideObey) {
|
||||||
|
|||||||
@@ -58,31 +58,16 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent, const int32_t c
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_IsRespawnVolume) {
|
if (m_IsRespawnVolume) {
|
||||||
{
|
const auto respawnPos = GeneralUtils::SplitString(m_Parent->GetVarAsString(u"rspPos"), '\x1f');
|
||||||
auto respawnString = std::stringstream(m_Parent->GetVarAsString(u"rspPos"));
|
m_RespawnPos = GeneralUtils::TryParse(respawnPos, NiPoint3Constant::ZERO);
|
||||||
|
|
||||||
std::string segment;
|
const auto respawnRot = GeneralUtils::SplitString(m_Parent->GetVarAsString(u"rspRot"), '\x1f');
|
||||||
std::vector<std::string> seglist;
|
m_RespawnRot = respawnRot.size() >= 4 ? NiQuaternion(
|
||||||
|
GeneralUtils::TryParse(respawnRot[0], 1.0f),
|
||||||
while (std::getline(respawnString, segment, '\x1f')) {
|
GeneralUtils::TryParse(respawnRot[1], 0.0f),
|
||||||
seglist.push_back(segment);
|
GeneralUtils::TryParse(respawnRot[2], 0.0f),
|
||||||
}
|
GeneralUtils::TryParse(respawnRot[3], 0.0f))
|
||||||
|
: QuatUtils::IDENTITY;
|
||||||
m_RespawnPos = NiPoint3(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto respawnString = std::stringstream(m_Parent->GetVarAsString(u"rspRot"));
|
|
||||||
|
|
||||||
std::string segment;
|
|
||||||
std::vector<std::string> seglist;
|
|
||||||
|
|
||||||
while (std::getline(respawnString, segment, '\x1f')) {
|
|
||||||
seglist.push_back(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_RespawnRot = NiQuaternion(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]), std::stof(seglist[3]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HF - RespawnPoints. Legacy respawn entity.
|
// HF - RespawnPoints. Legacy respawn entity.
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
|||||||
|
|
||||||
const auto lookupResult = Database::Get()->GetProperties(propertyLookup);
|
const auto lookupResult = Database::Get()->GetProperties(propertyLookup);
|
||||||
|
|
||||||
for (const auto& propertyEntry : lookupResult->entries) {
|
for (const auto& propertyEntry : lookupResult.entries) {
|
||||||
const auto owner = propertyEntry.ownerId;
|
const auto owner = propertyEntry.ownerId;
|
||||||
const auto otherCharacter = Database::Get()->GetCharacterInfo(owner);
|
const auto otherCharacter = Database::Get()->GetCharacterInfo(owner);
|
||||||
if (!otherCharacter.has_value()) {
|
if (!otherCharacter.has_value()) {
|
||||||
@@ -174,5 +174,5 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Query here is to figure out whether or not to display the button to go to the next page or not.
|
// Query here is to figure out whether or not to display the button to go to the next page or not.
|
||||||
GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, lookupResult->totalEntriesMatchingQuery - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr);
|
GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, lookupResult.totalEntriesMatchingQuery - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,20 +107,12 @@ std::vector<NiPoint3> PropertyManagementComponent::GetPaths() const {
|
|||||||
|
|
||||||
std::vector<float> points;
|
std::vector<float> points;
|
||||||
|
|
||||||
std::istringstream stream(result.getStringField("path"));
|
for (const auto& str : GeneralUtils::SplitString(result.getStringField("path"), ' ')) {
|
||||||
std::string token;
|
const auto value = GeneralUtils::TryParse<float>(str);
|
||||||
|
if (value) points.push_back(value.value());
|
||||||
while (std::getline(stream, token, ' ')) {
|
|
||||||
try {
|
|
||||||
auto value = std::stof(token);
|
|
||||||
|
|
||||||
points.push_back(value);
|
|
||||||
} catch (std::invalid_argument& exception) {
|
|
||||||
LOG("Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto i = 0u; i < points.size(); i += 3) {
|
for (auto i = 0u; i + 2 < points.size(); i += 3) {
|
||||||
paths.emplace_back(points[i], points[i + 1], points[i + 2]);
|
paths.emplace_back(points[i], points[i + 1], points[i + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -780,15 +772,17 @@ void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const
|
|||||||
privacy = static_cast<char>(this->privacyOption);
|
privacy = static_cast<char>(this->privacyOption);
|
||||||
if (moderatorRequested) {
|
if (moderatorRequested) {
|
||||||
auto moderationInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId);
|
auto moderationInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId);
|
||||||
if (moderationInfo->rejectionReason != "") {
|
if (moderationInfo) {
|
||||||
moderatorRequested = false;
|
if (moderationInfo->rejectionReason != "") {
|
||||||
rejectionReason = moderationInfo->rejectionReason;
|
moderatorRequested = false;
|
||||||
} else if (moderationInfo->rejectionReason == "" && moderationInfo->modApproved == 1) {
|
rejectionReason = moderationInfo->rejectionReason;
|
||||||
moderatorRequested = false;
|
} else if (moderationInfo->rejectionReason == "" && moderationInfo->modApproved == 1) {
|
||||||
rejectionReason = "";
|
moderatorRequested = false;
|
||||||
} else {
|
rejectionReason = "";
|
||||||
moderatorRequested = true;
|
} else {
|
||||||
rejectionReason = "";
|
moderatorRequested = true;
|
||||||
|
rejectionReason = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ void QuickBuildComponent::StartQuickBuild(Entity* const user) {
|
|||||||
m_Builder = user->GetObjectID();
|
m_Builder = user->GetObjectID();
|
||||||
|
|
||||||
auto* character = user->GetComponent<CharacterComponent>();
|
auto* character = user->GetComponent<CharacterComponent>();
|
||||||
character->SetCurrentActivity(eGameActivity::QUICKBUILDING);
|
if (character) character->SetCurrentActivity(eGameActivity::QUICKBUILDING);
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(user);
|
Game::entityManager->SerializeEntity(user);
|
||||||
|
|
||||||
|
|||||||
@@ -306,7 +306,7 @@ void RacingControlComponent::OnRequestDie(Entity* player, const std::u16string&
|
|||||||
eKillType::VIOLENT, deathType, 0, 0, 90.0f, false, true, 0);
|
eKillType::VIOLENT, deathType, 0, 0, 90.0f, false, true, 0);
|
||||||
|
|
||||||
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
|
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
|
||||||
uint32_t respawnImagination = 0;
|
int32_t respawnImagination = 0;
|
||||||
// Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live.
|
// Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live.
|
||||||
// Do not actually change the value yet. Do that on respawn.
|
// Do not actually change the value yet. Do that on respawn.
|
||||||
if (destroyableComponent) {
|
if (destroyableComponent) {
|
||||||
|
|||||||
@@ -385,26 +385,30 @@ void TriggerComponent::HandleSetPhysicsVolumeEffect(Entity* targetEntity, std::v
|
|||||||
}
|
}
|
||||||
phantomPhysicsComponent->SetPhysicsEffectActive(true);
|
phantomPhysicsComponent->SetPhysicsEffectActive(true);
|
||||||
ePhysicsEffectType effectType = ePhysicsEffectType::PUSH;
|
ePhysicsEffectType effectType = ePhysicsEffectType::PUSH;
|
||||||
std::transform(argArray.at(0).begin(), argArray.at(0).end(), argArray.at(0).begin(), ::tolower); //Transform to lowercase
|
if (!argArray.empty()) {
|
||||||
if (argArray.at(0) == "push") effectType = ePhysicsEffectType::PUSH;
|
std::transform(argArray.at(0).begin(), argArray.at(0).end(), argArray.at(0).begin(), ::tolower); //Transform to lowercase
|
||||||
else if (argArray.at(0) == "attract") effectType = ePhysicsEffectType::ATTRACT;
|
if (argArray.at(0) == "push") effectType = ePhysicsEffectType::PUSH;
|
||||||
else if (argArray.at(0) == "repulse") effectType = ePhysicsEffectType::REPULSE;
|
else if (argArray.at(0) == "attract") effectType = ePhysicsEffectType::ATTRACT;
|
||||||
else if (argArray.at(0) == "gravity") effectType = ePhysicsEffectType::GRAVITY_SCALE;
|
else if (argArray.at(0) == "repulse") effectType = ePhysicsEffectType::REPULSE;
|
||||||
else if (argArray.at(0) == "friction") effectType = ePhysicsEffectType::FRICTION;
|
else if (argArray.at(0) == "gravity") effectType = ePhysicsEffectType::GRAVITY_SCALE;
|
||||||
|
else if (argArray.at(0) == "friction") effectType = ePhysicsEffectType::FRICTION;
|
||||||
|
}
|
||||||
|
|
||||||
phantomPhysicsComponent->SetEffectType(effectType);
|
phantomPhysicsComponent->SetEffectType(effectType);
|
||||||
phantomPhysicsComponent->SetDirectionalMultiplier(std::stof(argArray.at(1)));
|
if (argArray.size() > 1) {
|
||||||
|
phantomPhysicsComponent->SetDirectionalMultiplier(GeneralUtils::TryParse(argArray.at(1), 0.0f));
|
||||||
|
}
|
||||||
if (argArray.size() > 4) {
|
if (argArray.size() > 4) {
|
||||||
const NiPoint3 direction =
|
const NiPoint3 direction =
|
||||||
GeneralUtils::TryParse<NiPoint3>(argArray.at(2), argArray.at(3), argArray.at(4)).value_or(NiPoint3Constant::ZERO);
|
GeneralUtils::TryParse(argArray.at(2), argArray.at(3), argArray.at(4), NiPoint3Constant::ZERO);
|
||||||
|
|
||||||
phantomPhysicsComponent->SetDirection(direction);
|
phantomPhysicsComponent->SetDirection(direction);
|
||||||
}
|
}
|
||||||
if (argArray.size() > 5) {
|
if (argArray.size() > 6) {
|
||||||
const uint32_t min = GeneralUtils::TryParse<uint32_t>(argArray.at(6)).value_or(0);
|
const uint32_t min = GeneralUtils::TryParse(argArray.at(6), 0);
|
||||||
phantomPhysicsComponent->SetMin(min);
|
phantomPhysicsComponent->SetMin(min);
|
||||||
|
|
||||||
const uint32_t max = GeneralUtils::TryParse<uint32_t>(argArray.at(7)).value_or(0);
|
const uint32_t max = GeneralUtils::TryParse(argArray.at(7), 0);
|
||||||
phantomPhysicsComponent->SetMax(max);
|
phantomPhysicsComponent->SetMax(max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,11 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
|||||||
|
|
||||||
User* usr = UserManager::Instance()->GetUser(sysAddr);
|
User* usr = UserManager::Instance()->GetUser(sysAddr);
|
||||||
|
|
||||||
|
if (!usr) {
|
||||||
|
LOG("Failed to find a logged in user for (%llu), aborting GM: %4i, %s!", objectID, messageID, StringifiedEnum::ToString(messageID).data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
LOG("Failed to find associated entity (%llu), aborting GM: %4i, %s!", objectID, messageID, StringifiedEnum::ToString(messageID).data());
|
LOG("Failed to find associated entity (%llu), aborting GM: %4i, %s!", objectID, messageID, StringifiedEnum::ToString(messageID).data());
|
||||||
return;
|
return;
|
||||||
@@ -76,7 +81,8 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
|||||||
if (msg->requiredGmLevel > eGameMasterLevel::CIVILIAN) {
|
if (msg->requiredGmLevel > eGameMasterLevel::CIVILIAN) {
|
||||||
auto* usingEntity = Game::entityManager->GetEntity(usr->GetLoggedInChar());
|
auto* usingEntity = Game::entityManager->GetEntity(usr->GetLoggedInChar());
|
||||||
if (!usingEntity || usingEntity->GetGMLevel() < msg->requiredGmLevel) {
|
if (!usingEntity || usingEntity->GetGMLevel() < msg->requiredGmLevel) {
|
||||||
LOG("User %s (%llu) does not have the required GM level to execute this command.", usingEntity->GetCharacter()->GetName().c_str(), usingEntity->GetObjectID());
|
if (usingEntity) LOG("User %s (%llu) does not have the required GM level to execute this command.", usingEntity->GetCharacter()->GetName().c_str(), usingEntity->GetObjectID());
|
||||||
|
else LOG("ObjectID %llu tried to use a gm required message.", usr->GetLoggedInChar());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,8 +173,8 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
|||||||
|
|
||||||
GameMessages::SendRestoreToPostLoadStats(entity, sysAddr);
|
GameMessages::SendRestoreToPostLoadStats(entity, sysAddr);
|
||||||
|
|
||||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
auto* const destroyable = entity->GetComponent<DestroyableComponent>();
|
||||||
destroyable->SetImagination(destroyable->GetImagination());
|
if (destroyable) destroyable->SetImagination(destroyable->GetImagination());
|
||||||
Game::entityManager->SerializeEntity(entity);
|
Game::entityManager->SerializeEntity(entity);
|
||||||
|
|
||||||
std::vector<Entity*> racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
|
std::vector<Entity*> racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
|
||||||
@@ -186,7 +192,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
|||||||
|
|
||||||
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT);
|
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT);
|
||||||
for (Entity* scriptEntity : scriptedActs) {
|
for (Entity* scriptEntity : scriptedActs) {
|
||||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
if (!zoneControl || scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||||
scriptEntity->GetScript()->OnPlayerLoaded(scriptEntity, entity);
|
scriptEntity->GetScript()->OnPlayerLoaded(scriptEntity, entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,9 +338,9 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
|||||||
if (behaviorId > 0) {
|
if (behaviorId > 0) {
|
||||||
auto bs = RakNet::BitStream(reinterpret_cast<unsigned char*>(&startSkill.sBitStream[0]), startSkill.sBitStream.size(), false);
|
auto bs = RakNet::BitStream(reinterpret_cast<unsigned char*>(&startSkill.sBitStream[0]), startSkill.sBitStream.size(), false);
|
||||||
|
|
||||||
auto* skillComponent = entity->GetComponent<SkillComponent>();
|
auto* const skillComponent = entity->GetComponent<SkillComponent>();
|
||||||
|
|
||||||
success = skillComponent->CastPlayerSkill(behaviorId, startSkill.uiSkillHandle, bs, startSkill.optionalTargetID, startSkill.skillID);
|
if (skillComponent) success = skillComponent->CastPlayerSkill(behaviorId, startSkill.uiSkillHandle, bs, startSkill.optionalTargetID, startSkill.skillID);
|
||||||
|
|
||||||
if (success && entity->GetCharacter()) {
|
if (success && entity->GetCharacter()) {
|
||||||
DestroyableComponent* destComp = entity->GetComponent<DestroyableComponent>();
|
DestroyableComponent* destComp = entity->GetComponent<DestroyableComponent>();
|
||||||
@@ -387,9 +393,9 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
|||||||
if (usr != nullptr) {
|
if (usr != nullptr) {
|
||||||
auto bs = RakNet::BitStream(reinterpret_cast<unsigned char*>(&sync.sBitStream[0]), sync.sBitStream.size(), false);
|
auto bs = RakNet::BitStream(reinterpret_cast<unsigned char*>(&sync.sBitStream[0]), sync.sBitStream.size(), false);
|
||||||
|
|
||||||
auto* skillComponent = entity->GetComponent<SkillComponent>();
|
auto* const skillComponent = entity->GetComponent<SkillComponent>();
|
||||||
|
|
||||||
skillComponent->SyncPlayerSkill(sync.uiSkillHandle, sync.uiBehaviorHandle, bs);
|
if (skillComponent) skillComponent->SyncPlayerSkill(sync.uiSkillHandle, sync.uiBehaviorHandle, bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
EchoSyncSkill echo = EchoSyncSkill();
|
EchoSyncSkill echo = EchoSyncSkill();
|
||||||
|
|||||||
@@ -2137,6 +2137,7 @@ void GameMessages::HandleUpdatePropertyOrModelForFilterCheck(RakNet::BitStream&
|
|||||||
inStream.Read(worldId);
|
inStream.Read(worldId);
|
||||||
|
|
||||||
inStream.Read(descriptionLength);
|
inStream.Read(descriptionLength);
|
||||||
|
if (descriptionLength > MAX_MESSAGE_LENGTH) return;
|
||||||
for (uint32_t i = 0; i < descriptionLength; ++i) {
|
for (uint32_t i = 0; i < descriptionLength; ++i) {
|
||||||
uint16_t character;
|
uint16_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
@@ -2144,6 +2145,7 @@ void GameMessages::HandleUpdatePropertyOrModelForFilterCheck(RakNet::BitStream&
|
|||||||
}
|
}
|
||||||
|
|
||||||
inStream.Read(nameLength);
|
inStream.Read(nameLength);
|
||||||
|
if (nameLength > MAX_MESSAGE_LENGTH) return;
|
||||||
for (uint32_t i = 0; i < nameLength; ++i) {
|
for (uint32_t i = 0; i < nameLength; ++i) {
|
||||||
uint16_t character;
|
uint16_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
@@ -2474,9 +2476,15 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent
|
|||||||
|
|
||||||
uint32_t sd0Size;
|
uint32_t sd0Size;
|
||||||
inStream.Read(sd0Size);
|
inStream.Read(sd0Size);
|
||||||
std::unique_ptr<char[]> sd0Data(new char[sd0Size]);
|
|
||||||
|
|
||||||
if (sd0Data == nullptr) return;
|
// For the sake of letting players make models as big as they want, only reject if we cant allocate the required memory.
|
||||||
|
std::unique_ptr<char[]> sd0Data;
|
||||||
|
try {
|
||||||
|
sd0Data.reset(new char[sd0Size]);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
LOG("Failed to allocate sd0 of size %u", sd0Size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(sd0Data.get()), sd0Size);
|
inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(sd0Data.get()), sd0Size);
|
||||||
|
|
||||||
@@ -2652,6 +2660,7 @@ void GameMessages::HandlePropertyEntranceSync(RakNet::BitStream& inStream, Entit
|
|||||||
inStream.Read(startIndex);
|
inStream.Read(startIndex);
|
||||||
inStream.Read(filterTextLength);
|
inStream.Read(filterTextLength);
|
||||||
|
|
||||||
|
if (filterTextLength > MAX_MESSAGE_LENGTH) return;
|
||||||
for (auto i = 0u; i < filterTextLength; i++) {
|
for (auto i = 0u; i < filterTextLength; i++) {
|
||||||
char c;
|
char c;
|
||||||
inStream.Read(c);
|
inStream.Read(c);
|
||||||
@@ -3040,6 +3049,7 @@ void GameMessages::HandleVerifyAck(RakNet::BitStream& inStream, Entity* entity,
|
|||||||
|
|
||||||
uint32_t sBitStreamLength = 0;
|
uint32_t sBitStreamLength = 0;
|
||||||
inStream.Read(sBitStreamLength);
|
inStream.Read(sBitStreamLength);
|
||||||
|
if (sBitStreamLength > MAX_MESSAGE_LENGTH) return;
|
||||||
for (uint64_t k = 0; k < sBitStreamLength; k++) {
|
for (uint64_t k = 0; k < sBitStreamLength; k++) {
|
||||||
uint8_t character;
|
uint8_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
@@ -3261,6 +3271,7 @@ void GameMessages::HandleClientTradeUpdate(RakNet::BitStream& inStream, Entity*
|
|||||||
|
|
||||||
inStream.Read(currency);
|
inStream.Read(currency);
|
||||||
inStream.Read(itemCount);
|
inStream.Read(itemCount);
|
||||||
|
if (itemCount > MAX_MESSAGE_LENGTH) return;
|
||||||
|
|
||||||
LOG("Trade update from (%llu) -> (%llu), (%i)", entity->GetObjectID(), currency, itemCount);
|
LOG("Trade update from (%llu) -> (%llu), (%i)", entity->GetObjectID(), currency, itemCount);
|
||||||
|
|
||||||
@@ -3673,6 +3684,7 @@ void GameMessages::HandleRequestSetPetName(RakNet::BitStream& inStream, Entity*
|
|||||||
|
|
||||||
inStream.Read(nameLength);
|
inStream.Read(nameLength);
|
||||||
|
|
||||||
|
if (nameLength > MAX_MESSAGE_LENGTH) return;
|
||||||
for (size_t i = 0; i < nameLength; i++) {
|
for (size_t i = 0; i < nameLength; i++) {
|
||||||
char16_t character;
|
char16_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
@@ -3742,6 +3754,7 @@ void GameMessages::HandleMessageBoxResponse(RakNet::BitStream& inStream, Entity*
|
|||||||
inStream.Read(iButton);
|
inStream.Read(iButton);
|
||||||
|
|
||||||
inStream.Read(identifierLength);
|
inStream.Read(identifierLength);
|
||||||
|
if (identifierLength > MAX_MESSAGE_LENGTH) return;
|
||||||
for (size_t i = 0; i < identifierLength; i++) {
|
for (size_t i = 0; i < identifierLength; i++) {
|
||||||
char16_t character;
|
char16_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
@@ -3749,6 +3762,7 @@ void GameMessages::HandleMessageBoxResponse(RakNet::BitStream& inStream, Entity*
|
|||||||
}
|
}
|
||||||
|
|
||||||
inStream.Read(userDataLength);
|
inStream.Read(userDataLength);
|
||||||
|
if (userDataLength > MAX_MESSAGE_LENGTH) return;
|
||||||
for (size_t i = 0; i < userDataLength; i++) {
|
for (size_t i = 0; i < userDataLength; i++) {
|
||||||
char16_t character;
|
char16_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
@@ -3796,6 +3810,7 @@ void GameMessages::HandleChoiceBoxRespond(RakNet::BitStream& inStream, Entity* e
|
|||||||
std::u16string identifier;
|
std::u16string identifier;
|
||||||
|
|
||||||
inStream.Read(buttonIdentifierLength);
|
inStream.Read(buttonIdentifierLength);
|
||||||
|
if (buttonIdentifierLength > MAX_MESSAGE_LENGTH) return;
|
||||||
for (size_t i = 0; i < buttonIdentifierLength; i++) {
|
for (size_t i = 0; i < buttonIdentifierLength; i++) {
|
||||||
char16_t character;
|
char16_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
@@ -3805,6 +3820,7 @@ void GameMessages::HandleChoiceBoxRespond(RakNet::BitStream& inStream, Entity* e
|
|||||||
inStream.Read(iButton);
|
inStream.Read(iButton);
|
||||||
|
|
||||||
inStream.Read(identifierLength);
|
inStream.Read(identifierLength);
|
||||||
|
if (identifierLength > MAX_MESSAGE_LENGTH) return;
|
||||||
for (size_t i = 0; i < identifierLength; i++) {
|
for (size_t i = 0; i < identifierLength; i++) {
|
||||||
char16_t character;
|
char16_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
@@ -4158,7 +4174,13 @@ void GameMessages::HandleUpdatePropertyPerformanceCost(RakNet::BitStream& inStre
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Database::Get()->UpdatePerformanceCost(zone->GetZoneID(), performanceCost);
|
const auto* const propertyManagementComponent = entity->GetComponent<PropertyManagementComponent>();
|
||||||
|
const auto* const ownerEntity = propertyManagementComponent ? propertyManagementComponent->GetOwner() : nullptr;
|
||||||
|
const auto* const character = ownerEntity ? ownerEntity->GetCharacter() : nullptr;
|
||||||
|
const auto& zoneID = zone->GetZoneID();
|
||||||
|
if (character && character->GetPropertyCloneID() == zoneID.GetCloneID()) {
|
||||||
|
Database::Get()->UpdatePerformanceCost(zoneID, performanceCost);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameMessages::HandleVehicleNotifyHitImaginationServer(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
void GameMessages::HandleVehicleNotifyHitImaginationServer(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
||||||
@@ -4783,13 +4805,19 @@ void GameMessages::HandleParseChatMessage(RakNet::BitStream& inStream, Entity* e
|
|||||||
|
|
||||||
uint32_t wsStringLength;
|
uint32_t wsStringLength;
|
||||||
inStream.Read(wsStringLength);
|
inStream.Read(wsStringLength);
|
||||||
|
|
||||||
|
if (wsStringLength > MAX_MESSAGE_LENGTH) {
|
||||||
|
LOG("Max message length reached, capping message.");
|
||||||
|
wsStringLength = MAX_MESSAGE_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < wsStringLength; ++i) {
|
for (uint32_t i = 0; i < wsStringLength; ++i) {
|
||||||
uint16_t character;
|
uint16_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
wsString.push_back(character);
|
wsString.push_back(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wsString[0] == L'/') {
|
if (!wsString.empty() && wsString[0] == L'/') {
|
||||||
SlashCommandHandler::HandleChatCommand(wsString, entity, sysAddr);
|
SlashCommandHandler::HandleChatCommand(wsString, entity, sysAddr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4806,6 +4834,7 @@ void GameMessages::HandleFireEventServerSide(RakNet::BitStream& inStream, Entity
|
|||||||
LWOOBJID senderID{};
|
LWOOBJID senderID{};
|
||||||
|
|
||||||
inStream.Read(argsLength);
|
inStream.Read(argsLength);
|
||||||
|
if (argsLength > MAX_MESSAGE_LENGTH) return;
|
||||||
for (uint32_t i = 0; i < argsLength; ++i) {
|
for (uint32_t i = 0; i < argsLength; ++i) {
|
||||||
uint16_t character;
|
uint16_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
@@ -5436,7 +5465,7 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream& inStream, Entity*
|
|||||||
std::vector<LOT> modList;
|
std::vector<LOT> modList;
|
||||||
auto& oldPartList = character->GetVar<std::string>(u"currentModifiedBuild");
|
auto& oldPartList = character->GetVar<std::string>(u"currentModifiedBuild");
|
||||||
bool everyPieceSwapped = !oldPartList.empty(); // If the player didn't put a build in initially, then they should not get this achievement.
|
bool everyPieceSwapped = !oldPartList.empty(); // If the player didn't put a build in initially, then they should not get this achievement.
|
||||||
if (count >= 3) {
|
if (count >= 3 && count < 8) {
|
||||||
std::u16string modules;
|
std::u16string modules;
|
||||||
|
|
||||||
for (uint32_t k = 0; k < count; k++) {
|
for (uint32_t k = 0; k < count; k++) {
|
||||||
@@ -5733,6 +5762,7 @@ void GameMessages::HandleMatchRequest(RakNet::BitStream& inStream, Entity* entit
|
|||||||
|
|
||||||
inStream.Read(activator);
|
inStream.Read(activator);
|
||||||
inStream.Read(playerChoicesLen);
|
inStream.Read(playerChoicesLen);
|
||||||
|
if (playerChoicesLen > MAX_MESSAGE_LENGTH) return;
|
||||||
for (uint32_t i = 0; i < playerChoicesLen; ++i) {
|
for (uint32_t i = 0; i < playerChoicesLen; ++i) {
|
||||||
uint16_t character;
|
uint16_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
@@ -5890,7 +5920,7 @@ void GameMessages::HandlePlayerRailArrivedNotification(RakNet::BitStream& inStre
|
|||||||
const SystemAddress& sysAddr) {
|
const SystemAddress& sysAddr) {
|
||||||
uint32_t pathNameLength;
|
uint32_t pathNameLength;
|
||||||
inStream.Read(pathNameLength);
|
inStream.Read(pathNameLength);
|
||||||
|
if (pathNameLength > MAX_MESSAGE_LENGTH) return;
|
||||||
std::u16string pathName;
|
std::u16string pathName;
|
||||||
for (auto k = 0; k < pathNameLength; k++) {
|
for (auto k = 0; k < pathNameLength; k++) {
|
||||||
uint16_t c;
|
uint16_t c;
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ uint32_t Inventory::GetLotCount(const LOT lot) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Inventory::SetSize(const uint32_t value) {
|
void Inventory::SetSize(const uint32_t value) {
|
||||||
free += static_cast<int32_t>(value) - static_cast<int32_t>(size);
|
const auto delta = static_cast<int32_t>(value) - static_cast<int32_t>(size);
|
||||||
|
free = static_cast<uint32_t>(std::max(0, static_cast<int32_t>(free) + delta));
|
||||||
|
|
||||||
size = value;
|
size = value;
|
||||||
|
|
||||||
|
|||||||
@@ -401,7 +401,8 @@ void Item::Disassemble(const eInventoryType inventoryType) {
|
|||||||
const auto deliminator = '+';
|
const auto deliminator = '+';
|
||||||
|
|
||||||
while (std::getline(ssData, token, deliminator)) {
|
while (std::getline(ssData, token, deliminator)) {
|
||||||
const auto modLot = std::stoi(token.substr(2, token.size() - 1));
|
if (token.size() <= 2) continue; // invalid token, must have size of at least 3.
|
||||||
|
const auto modLot = GeneralUtils::TryParse(token.substr(2, token.size() - 1), LOT_NULL);
|
||||||
|
|
||||||
modArray.push_back(modLot);
|
modArray.push_back(modLot);
|
||||||
}
|
}
|
||||||
@@ -440,7 +441,10 @@ void Item::DisassembleModel(uint32_t numToDismantle) {
|
|||||||
std::vector<std::string> renderAssetSplit = GeneralUtils::SplitString(renderAsset, '/');
|
std::vector<std::string> renderAssetSplit = GeneralUtils::SplitString(renderAsset, '/');
|
||||||
if (renderAssetSplit.empty()) return;
|
if (renderAssetSplit.empty()) return;
|
||||||
|
|
||||||
std::string lxfmlPath = "BrickModels" + lxfmlFolderName + "/" + GeneralUtils::SplitString(renderAssetSplit.back(), '.').at(0) + ".lxfml";
|
const auto renderAssetSplitSplit = GeneralUtils::SplitString(renderAssetSplit.back(), '.');
|
||||||
|
if (renderAssetSplitSplit.empty()) return;
|
||||||
|
|
||||||
|
std::string lxfmlPath = "BrickModels" + lxfmlFolderName + "/" + renderAssetSplitSplit[0] + ".lxfml";
|
||||||
auto file = Game::assetManager->GetFile(lxfmlPath.c_str());
|
auto file = Game::assetManager->GetFile(lxfmlPath.c_str());
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
|
|||||||
@@ -128,8 +128,8 @@ void ItemSet::OnEquip(const LOT lot) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* skillComponent = m_InventoryComponent->GetParent()->GetComponent<SkillComponent>();
|
auto [skillComponent, missionComponent] = m_InventoryComponent->GetParent()->GetComponentsMut<SkillComponent, MissionComponent>();
|
||||||
auto* missionComponent = m_InventoryComponent->GetParent()->GetComponent<MissionComponent>();
|
if (!skillComponent || !missionComponent) return; // Nothing to do here if these are null
|
||||||
|
|
||||||
for (const auto skill : skillSet) {
|
for (const auto skill : skillSet) {
|
||||||
auto* skillTable = CDClientManager::GetTable<CDSkillBehaviorTable>();
|
auto* skillTable = CDClientManager::GetTable<CDSkillBehaviorTable>();
|
||||||
|
|||||||
@@ -74,23 +74,19 @@ Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) {
|
|||||||
|
|
||||||
void Mission::LoadFromXmlDone(const tinyxml2::XMLElement& element) {
|
void Mission::LoadFromXmlDone(const tinyxml2::XMLElement& element) {
|
||||||
// Start custom XML
|
// Start custom XML
|
||||||
if (element.Attribute("state") != nullptr) {
|
m_State = static_cast<eMissionState>(element.UnsignedAttribute("state"));
|
||||||
m_State = static_cast<eMissionState>(std::stoul(element.Attribute("state")));
|
|
||||||
}
|
|
||||||
// End custom XML
|
// End custom XML
|
||||||
|
|
||||||
if (element.Attribute("cct") != nullptr) {
|
m_Completions = element.UnsignedAttribute("cct");
|
||||||
m_Completions = std::stoul(element.Attribute("cct"));
|
|
||||||
|
|
||||||
m_Timestamp = std::stoul(element.Attribute("cts"));
|
m_Timestamp = element.UnsignedAttribute("cts");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mission::LoadFromXmlCur(const tinyxml2::XMLElement& element) {
|
void Mission::LoadFromXmlCur(const tinyxml2::XMLElement& element) {
|
||||||
const auto* const character = GetCharacter();
|
const auto* const character = GetCharacter();
|
||||||
// Start custom XML
|
// Start custom XML
|
||||||
if (element.Attribute("state") != nullptr) {
|
if (element.Attribute("state") != nullptr) {
|
||||||
m_State = static_cast<eMissionState>(std::stoul(element.Attribute("state")));
|
m_State = static_cast<eMissionState>(element.IntAttribute("state", -1));
|
||||||
}
|
}
|
||||||
// End custom XML
|
// End custom XML
|
||||||
|
|
||||||
@@ -106,7 +102,7 @@ void Mission::LoadFromXmlCur(const tinyxml2::XMLElement& element) {
|
|||||||
|
|
||||||
const auto type = curTask->GetType();
|
const auto type = curTask->GetType();
|
||||||
|
|
||||||
auto value = std::stoul(task->Attribute("v"));
|
auto value = task->UnsignedAttribute("v");
|
||||||
curTask->SetProgress(value, false);
|
curTask->SetProgress(value, false);
|
||||||
task = task->NextSiblingElement();
|
task = task->NextSiblingElement();
|
||||||
|
|
||||||
@@ -114,7 +110,7 @@ void Mission::LoadFromXmlCur(const tinyxml2::XMLElement& element) {
|
|||||||
if (type == eMissionTaskType::COLLECTION || type == eMissionTaskType::VISIT_PROPERTY) {
|
if (type == eMissionTaskType::COLLECTION || type == eMissionTaskType::VISIT_PROPERTY) {
|
||||||
std::vector<uint32_t> uniques;
|
std::vector<uint32_t> uniques;
|
||||||
while (task != nullptr && value > 0) {
|
while (task != nullptr && value > 0) {
|
||||||
const auto unique = std::stoul(task->Attribute("v"));
|
const auto unique = task->UnsignedAttribute("v");
|
||||||
|
|
||||||
uniques.push_back(unique);
|
uniques.push_back(unique);
|
||||||
|
|
||||||
|
|||||||
@@ -17,50 +17,63 @@ PropertyBehavior::PropertyBehavior(bool _isTemplated) {
|
|||||||
isTemplated = _isTemplated;
|
isTemplated = _isTemplated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CheckStateRange(const BehaviorState state) {
|
||||||
|
return state >= BehaviorState::HOME_STATE && state <= BehaviorState::STAR_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void PropertyBehavior::HandleMsg(AddStripMessage& msg) {
|
void PropertyBehavior::HandleMsg(AddStripMessage& msg) {
|
||||||
|
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void PropertyBehavior::HandleMsg(AddActionMessage& msg) {
|
void PropertyBehavior::HandleMsg(AddActionMessage& msg) {
|
||||||
|
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void PropertyBehavior::HandleMsg(RearrangeStripMessage& msg) {
|
void PropertyBehavior::HandleMsg(RearrangeStripMessage& msg) {
|
||||||
|
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void PropertyBehavior::HandleMsg(UpdateActionMessage& msg) {
|
void PropertyBehavior::HandleMsg(UpdateActionMessage& msg) {
|
||||||
|
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void PropertyBehavior::HandleMsg(UpdateStripUiMessage& msg) {
|
void PropertyBehavior::HandleMsg(UpdateStripUiMessage& msg) {
|
||||||
|
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void PropertyBehavior::HandleMsg(RemoveStripMessage& msg) {
|
void PropertyBehavior::HandleMsg(RemoveStripMessage& msg) {
|
||||||
|
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void PropertyBehavior::HandleMsg(RemoveActionsMessage& msg) {
|
void PropertyBehavior::HandleMsg(RemoveActionsMessage& msg) {
|
||||||
|
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void PropertyBehavior::HandleMsg(SplitStripMessage& msg) {
|
void PropertyBehavior::HandleMsg(SplitStripMessage& msg) {
|
||||||
|
if (!CheckStateRange(msg.GetSourceActionContext().GetStateId())) return;
|
||||||
|
if (!CheckStateRange(msg.GetDestinationActionContext().GetStateId())) return;
|
||||||
m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_LastEditedState = msg.GetDestinationActionContext().GetStateId();
|
m_LastEditedState = msg.GetDestinationActionContext().GetStateId();
|
||||||
@@ -68,6 +81,8 @@ void PropertyBehavior::HandleMsg(SplitStripMessage& msg) {
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
void PropertyBehavior::HandleMsg(MigrateActionsMessage& msg) {
|
void PropertyBehavior::HandleMsg(MigrateActionsMessage& msg) {
|
||||||
|
if (!CheckStateRange(msg.GetSourceActionContext().GetStateId())) return;
|
||||||
|
if (!CheckStateRange(msg.GetDestinationActionContext().GetStateId())) return;
|
||||||
m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_LastEditedState = msg.GetDestinationActionContext().GetStateId();
|
m_LastEditedState = msg.GetDestinationActionContext().GetStateId();
|
||||||
@@ -75,6 +90,8 @@ void PropertyBehavior::HandleMsg(MigrateActionsMessage& msg) {
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
void PropertyBehavior::HandleMsg(MergeStripsMessage& msg) {
|
void PropertyBehavior::HandleMsg(MergeStripsMessage& msg) {
|
||||||
|
if (!CheckStateRange(msg.GetSourceActionContext().GetStateId())) return;
|
||||||
|
if (!CheckStateRange(msg.GetDestinationActionContext().GetStateId())) return;
|
||||||
m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg);
|
m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg);
|
||||||
m_LastEditedState = msg.GetDestinationActionContext().GetStateId();
|
m_LastEditedState = msg.GetDestinationActionContext().GetStateId();
|
||||||
|
|||||||
@@ -66,13 +66,9 @@ const BrickList& BrickDatabase::GetBricks(const LxfmlPath& lxfmlPath) {
|
|||||||
std::string materialString(materialList);
|
std::string materialString(materialList);
|
||||||
const auto materials = GeneralUtils::SplitString(materialString, ',');
|
const auto materials = GeneralUtils::SplitString(materialString, ',');
|
||||||
|
|
||||||
if (!materials.empty()) {
|
brick.materialID = GeneralUtils::TryParse(materials[0], 0);
|
||||||
brick.materialID = std::stoi(materials[0]);
|
|
||||||
} else {
|
|
||||||
brick.materialID = 0;
|
|
||||||
}
|
|
||||||
} else if (materialID != nullptr) {
|
} else if (materialID != nullptr) {
|
||||||
brick.materialID = std::stoi(materialID);
|
brick.materialID = GeneralUtils::TryParse(materialID, 0);
|
||||||
} else {
|
} else {
|
||||||
brick.materialID = 0; // This is bad, makes it so the minigame can't be played
|
brick.materialID = 0; // This is bad, makes it so the minigame can't be played
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,17 +54,23 @@ void LogAndSaveFailedAntiCheatCheck(const LWOOBJID& id, const SystemAddress& sys
|
|||||||
|
|
||||||
// If player exists and entity exists in world, use both for logging info.
|
// If player exists and entity exists in world, use both for logging info.
|
||||||
if (entity && player) {
|
if (entity && player) {
|
||||||
|
const auto* const playerChar = player->GetCharacter();
|
||||||
|
const auto& playerName = playerChar ? playerChar->GetName() : "(null player character)";
|
||||||
|
const auto* const entityChar = entity->GetCharacter();
|
||||||
|
const auto& entityName = entityChar ? entityChar->GetName() : "(null entity character)";
|
||||||
LOG("Player (%s) (%llu) at system address (%s) with sending player (%s) (%llu) does not match their own.",
|
LOG("Player (%s) (%llu) at system address (%s) with sending player (%s) (%llu) does not match their own.",
|
||||||
player->GetCharacter()->GetName().c_str(), player->GetObjectID(),
|
playerName.c_str(), player->GetObjectID(),
|
||||||
sysAddr.ToString(),
|
sysAddr.ToString(),
|
||||||
entity->GetCharacter()->GetName().c_str(), entity->GetObjectID());
|
entityName.c_str(), entity->GetObjectID());
|
||||||
if (player->GetCharacter()) toReport = player->GetCharacter()->GetParentUser();
|
if (playerChar) toReport = playerChar->GetParentUser();
|
||||||
// In the case that the target entity id did not exist, just log the player info.
|
// In the case that the target entity id did not exist, just log the player info.
|
||||||
} else if (player) {
|
} else if (player) {
|
||||||
|
const auto* const playerChar = player->GetCharacter();
|
||||||
|
const auto& playerName = playerChar ? playerChar->GetName() : "(null player character)";
|
||||||
LOG("Player (%s) (%llu) at system address (%s) with sending player (%llu) does not match their own.",
|
LOG("Player (%s) (%llu) at system address (%s) with sending player (%llu) does not match their own.",
|
||||||
player->GetCharacter()->GetName().c_str(), player->GetObjectID(),
|
playerName.c_str(), player->GetObjectID(),
|
||||||
sysAddr.ToString(), id);
|
sysAddr.ToString(), id);
|
||||||
if (player->GetCharacter()) toReport = player->GetCharacter()->GetParentUser();
|
if (playerChar) toReport = playerChar->GetParentUser();
|
||||||
// In the rare case that the player does not exist, just log the system address and who the target id was.
|
// In the rare case that the player does not exist, just log the system address and who the target id was.
|
||||||
} else {
|
} else {
|
||||||
LOG("Player at system address (%s) with sending player (%llu) does not match their own.",
|
LOG("Player at system address (%s) with sending player (%llu) does not match their own.",
|
||||||
@@ -76,8 +82,11 @@ void LogAndSaveFailedAntiCheatCheck(const LWOOBJID& id, const SystemAddress& sys
|
|||||||
auto* user = UserManager::Instance()->GetUser(sysAddr);
|
auto* user = UserManager::Instance()->GetUser(sysAddr);
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
|
const auto* const lastChar = user->GetLastUsedChar();
|
||||||
|
const auto& lastName = lastChar ? lastChar->GetName() : "(null last char)";
|
||||||
|
const auto lastObjID = lastChar ? lastChar->GetObjectID() : LWOOBJID_EMPTY;
|
||||||
LOG("User at system address (%s) (%s) (%llu) sent a packet as (%llu) which is not an id they own.",
|
LOG("User at system address (%s) (%s) (%llu) sent a packet as (%llu) which is not an id they own.",
|
||||||
sysAddr.ToString(), user->GetLastUsedChar()->GetName().c_str(), user->GetLastUsedChar()->GetObjectID(), id);
|
sysAddr.ToString(), lastName.c_str(), lastObjID, id);
|
||||||
// Can't know sending player. Just log system address for IP banning.
|
// Can't know sending player. Just log system address for IP banning.
|
||||||
} else {
|
} else {
|
||||||
LOG("No user found for system address (%s).", sysAddr.ToString());
|
LOG("No user found for system address (%s).", sysAddr.ToString());
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ void DropRegularLoot(Team& team, GameMessages::DropClientLoot& lootMsg, const bo
|
|||||||
void DropLoot(Entity* player, const LWOOBJID source, const std::map<LOT, LootDropInfo>& rolledItems, uint32_t minCoins, uint32_t maxCoins, const bool noTeamLootOnDeath) {
|
void DropLoot(Entity* player, const LWOOBJID source, const std::map<LOT, LootDropInfo>& rolledItems, uint32_t minCoins, uint32_t maxCoins, const bool noTeamLootOnDeath) {
|
||||||
player = player->GetOwner(); // if the owner is overwritten, we collect that here
|
player = player->GetOwner(); // if the owner is overwritten, we collect that here
|
||||||
const auto playerID = player->GetObjectID();
|
const auto playerID = player->GetObjectID();
|
||||||
if (!player || !player->IsPlayer()) {
|
if (!player->IsPlayer()) {
|
||||||
LOG("Trying to drop loot for non-player %llu:%i", playerID, player->GetLOT());
|
LOG("Trying to drop loot for non-player %llu:%i", playerID, player->GetLOT());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,69 +74,74 @@ namespace Mail {
|
|||||||
void SendRequest::Handle() {
|
void SendRequest::Handle() {
|
||||||
SendResponse response;
|
SendResponse response;
|
||||||
auto* character = player->GetCharacter();
|
auto* character = player->GetCharacter();
|
||||||
const bool restrictMailOnMute = UserManager::Instance()->GetMuteRestrictMail() && character->GetParentUser()->GetIsMuted();
|
if (!character) {
|
||||||
const bool restrictedMailAccess = character->HasPermission(ePermissionMap::RestrictedMailAccess);
|
response.status = eSendResponse::UnknownError;
|
||||||
|
|
||||||
if (character && !(restrictedMailAccess || restrictMailOnMute)) {
|
|
||||||
mailInfo.recipient = std::regex_replace(mailInfo.recipient, std::regex("[^0-9a-zA-Z]+"), "");
|
|
||||||
auto receiverID = Database::Get()->GetCharacterInfo(mailInfo.recipient);
|
|
||||||
|
|
||||||
if (!receiverID) {
|
|
||||||
response.status = eSendResponse::RecipientNotFound;
|
|
||||||
} else if (GeneralUtils::CaseInsensitiveStringCompare(mailInfo.recipient, character->GetName()) || receiverID->id == character->GetID()) {
|
|
||||||
response.status = eSendResponse::CannotMailSelf;
|
|
||||||
} else {
|
|
||||||
uint32_t mailCost = Game::zoneManager->GetWorldConfig().mailBaseFee;
|
|
||||||
uint32_t stackSize = 0;
|
|
||||||
|
|
||||||
auto inventoryComponent = player->GetComponent<InventoryComponent>();
|
|
||||||
Item* item = nullptr;
|
|
||||||
|
|
||||||
bool hasAttachment = mailInfo.itemID != 0 && mailInfo.itemCount > 0;
|
|
||||||
|
|
||||||
if (hasAttachment) {
|
|
||||||
item = inventoryComponent->FindItemById(mailInfo.itemID);
|
|
||||||
if (item) {
|
|
||||||
mailCost += (item->GetInfo().baseValue * Game::zoneManager->GetWorldConfig().mailPercentAttachmentFee);
|
|
||||||
mailInfo.itemLOT = item->GetLot();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasAttachment && !item) {
|
|
||||||
response.status = eSendResponse::AttachmentNotFound;
|
|
||||||
} else if (player->GetCharacter()->GetCoins() - mailCost < 0) {
|
|
||||||
response.status = eSendResponse::NotEnoughCoins;
|
|
||||||
} else {
|
|
||||||
bool removeSuccess = true;
|
|
||||||
// Remove coins and items from the sender
|
|
||||||
player->GetCharacter()->SetCoins(player->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL);
|
|
||||||
if (inventoryComponent && hasAttachment && item) {
|
|
||||||
removeSuccess = inventoryComponent->RemoveItem(mailInfo.itemLOT, mailInfo.itemCount, ALL, true);
|
|
||||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
|
||||||
if (missionComponent && removeSuccess) missionComponent->Progress(eMissionTaskType::GATHER, mailInfo.itemLOT, LWOOBJID_EMPTY, "", -mailInfo.itemCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we passed all the checks, now we can actully send the mail
|
|
||||||
if (removeSuccess) {
|
|
||||||
mailInfo.senderId = character->GetID();
|
|
||||||
mailInfo.senderUsername = character->GetName();
|
|
||||||
mailInfo.receiverId = receiverID->id;
|
|
||||||
mailInfo.itemSubkey = LWOOBJID_EMPTY;
|
|
||||||
|
|
||||||
//clear out the attachementID
|
|
||||||
mailInfo.itemID = 0;
|
|
||||||
|
|
||||||
Database::Get()->InsertNewMail(mailInfo);
|
|
||||||
response.status = eSendResponse::Success;
|
|
||||||
character->SaveXMLToDatabase();
|
|
||||||
} else {
|
|
||||||
response.status = eSendResponse::AttachmentNotFound;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
response.status = eSendResponse::SenderAccountIsMuted;
|
const bool restrictMailOnMute = UserManager::Instance()->GetMuteRestrictMail() && character->GetParentUser()->GetIsMuted();
|
||||||
|
const bool restrictedMailAccess = character->HasPermission(ePermissionMap::RestrictedMailAccess);
|
||||||
|
|
||||||
|
if (character && !(restrictedMailAccess || restrictMailOnMute)) {
|
||||||
|
mailInfo.recipient = std::regex_replace(mailInfo.recipient, std::regex("[^0-9a-zA-Z]+"), "");
|
||||||
|
auto receiverID = Database::Get()->GetCharacterInfo(mailInfo.recipient);
|
||||||
|
|
||||||
|
if (!receiverID) {
|
||||||
|
response.status = eSendResponse::RecipientNotFound;
|
||||||
|
} else if (GeneralUtils::CaseInsensitiveStringCompare(mailInfo.recipient, character->GetName()) || receiverID->id == character->GetID()) {
|
||||||
|
response.status = eSendResponse::CannotMailSelf;
|
||||||
|
} else {
|
||||||
|
uint32_t mailCost = Game::zoneManager->GetWorldConfig().mailBaseFee;
|
||||||
|
uint32_t stackSize = 0;
|
||||||
|
|
||||||
|
auto inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||||
|
Item* item = nullptr;
|
||||||
|
|
||||||
|
bool hasAttachment = mailInfo.itemID != 0 && mailInfo.itemCount > 0;
|
||||||
|
|
||||||
|
if (hasAttachment) {
|
||||||
|
item = inventoryComponent->FindItemById(mailInfo.itemID);
|
||||||
|
if (item) {
|
||||||
|
mailCost += (item->GetInfo().baseValue * Game::zoneManager->GetWorldConfig().mailPercentAttachmentFee);
|
||||||
|
mailInfo.itemLOT = item->GetLot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasAttachment && !item) {
|
||||||
|
response.status = eSendResponse::AttachmentNotFound;
|
||||||
|
} else if (player->GetCharacter()->GetCoins() - mailCost < 0) {
|
||||||
|
response.status = eSendResponse::NotEnoughCoins;
|
||||||
|
} else {
|
||||||
|
bool removeSuccess = true;
|
||||||
|
// Remove coins and items from the sender
|
||||||
|
player->GetCharacter()->SetCoins(player->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL);
|
||||||
|
if (inventoryComponent && hasAttachment && item) {
|
||||||
|
removeSuccess = inventoryComponent->RemoveItem(mailInfo.itemLOT, mailInfo.itemCount, ALL, true);
|
||||||
|
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||||
|
if (missionComponent && removeSuccess) missionComponent->Progress(eMissionTaskType::GATHER, mailInfo.itemLOT, LWOOBJID_EMPTY, "", -mailInfo.itemCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we passed all the checks, now we can actully send the mail
|
||||||
|
if (removeSuccess) {
|
||||||
|
mailInfo.senderId = character->GetID();
|
||||||
|
mailInfo.senderUsername = character->GetName();
|
||||||
|
mailInfo.receiverId = receiverID->id;
|
||||||
|
mailInfo.itemSubkey = LWOOBJID_EMPTY;
|
||||||
|
|
||||||
|
//clear out the attachementID
|
||||||
|
mailInfo.itemID = 0;
|
||||||
|
|
||||||
|
Database::Get()->InsertNewMail(mailInfo);
|
||||||
|
response.status = eSendResponse::Success;
|
||||||
|
character->SaveXMLToDatabase();
|
||||||
|
} else {
|
||||||
|
response.status = eSendResponse::AttachmentNotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response.status = eSendResponse::SenderAccountIsMuted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("Finished send with status %s", StringifiedEnum::ToString(response.status).data());
|
LOG("Finished send with status %s", StringifiedEnum::ToString(response.status).data());
|
||||||
response.Send(sysAddr);
|
response.Send(sysAddr);
|
||||||
}
|
}
|
||||||
@@ -193,7 +198,7 @@ namespace Mail {
|
|||||||
|
|
||||||
if (mailID > 0 && playerID == player->GetObjectID() && inv) {
|
if (mailID > 0 && playerID == player->GetObjectID() && inv) {
|
||||||
auto playerMail = Database::Get()->GetMail(mailID);
|
auto playerMail = Database::Get()->GetMail(mailID);
|
||||||
if (!playerMail) {
|
if (!playerMail || playerMail->receiverId != player->GetObjectID()) {
|
||||||
response.status = eAttachmentCollectResponse::MailNotFound;
|
response.status = eAttachmentCollectResponse::MailNotFound;
|
||||||
} else if (!inv->HasSpaceForLoot({ {playerMail->itemLOT, playerMail->itemCount} })) {
|
} else if (!inv->HasSpaceForLoot({ {playerMail->itemLOT, playerMail->itemCount} })) {
|
||||||
response.status = eAttachmentCollectResponse::NoSpaceInInventory;
|
response.status = eAttachmentCollectResponse::NoSpaceInInventory;
|
||||||
@@ -225,15 +230,21 @@ namespace Mail {
|
|||||||
DeleteResponse response;
|
DeleteResponse response;
|
||||||
response.mailID = mailID;
|
response.mailID = mailID;
|
||||||
|
|
||||||
auto mailData = Database::Get()->GetMail(mailID);
|
const auto mailData = Database::Get()->GetMail(mailID);
|
||||||
if (mailData && !(mailData->itemLOT > 0 && mailData->itemCount > 0)) {
|
response.status = eDeleteResponse::NotFound;
|
||||||
Database::Get()->DeleteMail(mailID);
|
if (mailData) {
|
||||||
response.status = eDeleteResponse::Success;
|
if (mailData->receiverId != playerID) {
|
||||||
} else if (mailData && mailData->itemLOT > 0 && mailData->itemCount > 0) {
|
LOG("Player %llu attempted to delete mail owned by %llu. Possible spoof?", playerID, mailData->receiverId);
|
||||||
response.status = eDeleteResponse::HasAttachments;
|
} else {
|
||||||
} else {
|
if (!(mailData->itemLOT > 0 && mailData->itemCount > 0)) {
|
||||||
response.status = eDeleteResponse::NotFound;
|
Database::Get()->DeleteMail(mailID);
|
||||||
|
response.status = eDeleteResponse::Success;
|
||||||
|
} else if (mailData->itemLOT > 0 && mailData->itemCount > 0) {
|
||||||
|
response.status = eDeleteResponse::HasAttachments;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("DeleteRequest status %s", StringifiedEnum::ToString(response.status).data());
|
LOG("DeleteRequest status %s", StringifiedEnum::ToString(response.status).data());
|
||||||
response.Send(sysAddr);
|
response.Send(sysAddr);
|
||||||
}
|
}
|
||||||
@@ -253,11 +264,19 @@ namespace Mail {
|
|||||||
|
|
||||||
void ReadRequest::Handle() {
|
void ReadRequest::Handle() {
|
||||||
ReadResponse response;
|
ReadResponse response;
|
||||||
|
response.status = eReadResponse::UnknownError;
|
||||||
response.mailID = mailID;
|
response.mailID = mailID;
|
||||||
|
|
||||||
if (Database::Get()->GetMail(mailID)) {
|
const auto mail = Database::Get()->GetMail(mailID);
|
||||||
response.status = eReadResponse::Success;
|
if (mail) {
|
||||||
Database::Get()->MarkMailRead(mailID);
|
if (mail->receiverId == player->GetObjectID()) {
|
||||||
|
response.status = eReadResponse::Success;
|
||||||
|
Database::Get()->MarkMailRead(mailID);
|
||||||
|
} else {
|
||||||
|
LOG("Player %llu tried to mark mail read for player %llu", mail->receiverId, player->GetObjectID());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG("No mail by ID %llu found to mark as read.", mailID);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("ReadRequest %s", StringifiedEnum::ToString(response.status).data());
|
LOG("ReadRequest %s", StringifiedEnum::ToString(response.status).data());
|
||||||
|
|||||||
@@ -114,11 +114,13 @@ bool Precondition::Check(Entity* player, bool evaluateCosts) const {
|
|||||||
|
|
||||||
|
|
||||||
bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluateCosts) const {
|
bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluateCosts) const {
|
||||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
|
||||||
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
|
||||||
auto* destroyableComponent = player->GetComponent<DestroyableComponent>();
|
|
||||||
auto* levelComponent = player->GetComponent<LevelProgressionComponent>();
|
|
||||||
auto* character = player->GetCharacter();
|
auto* character = player->GetCharacter();
|
||||||
|
auto [missionComponent, inventoryComponent, destroyableComponent, levelComponent] =
|
||||||
|
player->GetComponentsMut<const MissionComponent, /* not const */ InventoryComponent, const DestroyableComponent, const LevelProgressionComponent>();
|
||||||
|
|
||||||
|
if (!missionComponent || !inventoryComponent || !destroyableComponent || !levelComponent || !character) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Mission* mission;
|
Mission* mission;
|
||||||
|
|
||||||
|
|||||||
@@ -825,7 +825,7 @@ namespace DEVGMCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto numberToSpawnOptional = GeneralUtils::TryParse<uint32_t>(splitArgs[1]);
|
const auto numberToSpawnOptional = GeneralUtils::TryParse<uint32_t>(splitArgs[1]);
|
||||||
if (!numberToSpawnOptional && numberToSpawnOptional.value() > 0) {
|
if (!numberToSpawnOptional) {
|
||||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid number of enemies to spawn.");
|
ChatPackets::SendSystemMessage(sysAddr, u"Invalid number of enemies to spawn.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -833,7 +833,7 @@ namespace DEVGMCommands {
|
|||||||
|
|
||||||
// Must spawn within a radius of at least 0.0f
|
// Must spawn within a radius of at least 0.0f
|
||||||
const auto radiusToSpawnWithinOptional = GeneralUtils::TryParse<float>(splitArgs[2]);
|
const auto radiusToSpawnWithinOptional = GeneralUtils::TryParse<float>(splitArgs[2]);
|
||||||
if (!radiusToSpawnWithinOptional && radiusToSpawnWithinOptional.value() < 0.0f) {
|
if (!radiusToSpawnWithinOptional || radiusToSpawnWithinOptional.value() < 0.0f) {
|
||||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid radius to spawn within.");
|
ChatPackets::SendSystemMessage(sysAddr, u"Invalid radius to spawn within.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1133,6 +1133,10 @@ namespace DEVGMCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto& password = splitArgs[2];
|
const auto& password = splitArgs[2];
|
||||||
|
if (password.length() >= 50) {
|
||||||
|
ChatPackets::SendSystemMessage(sysAddr, u"Password is too long.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ZoneInstanceManager::Instance()->CreatePrivateZone(Game::server, zone.value(), clone.value(), password);
|
ZoneInstanceManager::Instance()->CreatePrivateZone(Game::server, zone.value(), clone.value(), password);
|
||||||
|
|
||||||
|
|||||||
@@ -187,8 +187,13 @@ namespace GMZeroCommands {
|
|||||||
auto splitArgs = GeneralUtils::SplitString(args, ' ');
|
auto splitArgs = GeneralUtils::SplitString(args, ' ');
|
||||||
if (splitArgs.empty()) return;
|
if (splitArgs.empty()) return;
|
||||||
|
|
||||||
ChatPackets::SendSystemMessage(sysAddr, u"Requesting private map...");
|
|
||||||
const auto& password = splitArgs[0];
|
const auto& password = splitArgs[0];
|
||||||
|
if (password.length() >= 50) {
|
||||||
|
ChatPackets::SendSystemMessage(sysAddr, u"Password is too long.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatPackets::SendSystemMessage(sysAddr, u"Requesting private map...");
|
||||||
|
|
||||||
ZoneInstanceManager::Instance()->RequestPrivateZone(Game::server, false, password, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
ZoneInstanceManager::Instance()->RequestPrivateZone(Game::server, false, password, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||||
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||||
|
|||||||
@@ -328,20 +328,14 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Game::randomEngine = std::mt19937(time(0));
|
Game::randomEngine = std::mt19937(time(0));
|
||||||
uint32_t maxClients = 999;
|
uint32_t maxClients = Game::config->GetValue("max_clients", 999);
|
||||||
uint32_t ourPort = 2000;
|
uint32_t ourPort = Game::config->GetValue("master_server_port", 2000);
|
||||||
std::string ourIP = "localhost";
|
std::string ourIP = Game::config->GetValue("external_ip", "localhost");
|
||||||
const auto maxClientsString = Game::config->GetValue("max_clients");
|
|
||||||
if (!maxClientsString.empty()) maxClients = std::stoi(maxClientsString);
|
|
||||||
const auto masterServerPortString = Game::config->GetValue("master_server_port");
|
|
||||||
if (!masterServerPortString.empty()) ourPort = std::atoi(masterServerPortString.c_str());
|
|
||||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
|
||||||
if (!externalIPString.empty()) ourIP = externalIPString;
|
|
||||||
|
|
||||||
char salt[BCRYPT_HASHSIZE];
|
char salt[BCRYPT_HASHSIZE];
|
||||||
char hash[BCRYPT_HASHSIZE];
|
char hash[BCRYPT_HASHSIZE];
|
||||||
const auto& cfgPassword = Game::config->GetValue("master_password");
|
const auto& cfgPassword = Game::config->GetValue<std::string>("master_password", "3.25DARKFLAME1");
|
||||||
int res = GenerateBCryptPassword(!cfgPassword.empty() ? cfgPassword : "3.25DARKFLAME1", 13, salt, hash);
|
int res = GenerateBCryptPassword(cfgPassword, 13, salt, hash);
|
||||||
assert(res == 0);
|
assert(res == 0);
|
||||||
|
|
||||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServiceType::MASTER, Game::config, &Game::lastSignal, hash);
|
Game::server = new dServer(ourIP, ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServiceType::MASTER, Game::config, &Game::lastSignal, hash);
|
||||||
@@ -535,8 +529,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
|
|
||||||
case MessageType::Master::REQUEST_ZONE_TRANSFER: {
|
case MessageType::Master::REQUEST_ZONE_TRANSFER: {
|
||||||
LOG("Received zone transfer req");
|
LOG("Received zone transfer req");
|
||||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
CINSTREAM_SKIP_HEADER;
|
||||||
uint64_t header = inStream.Read(header);
|
|
||||||
uint64_t requestID = 0;
|
uint64_t requestID = 0;
|
||||||
uint8_t mythranShift = false;
|
uint8_t mythranShift = false;
|
||||||
uint32_t zoneID = 0;
|
uint32_t zoneID = 0;
|
||||||
@@ -575,8 +568,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
//This is here because otherwise we'd have to include IM in
|
//This is here because otherwise we'd have to include IM in
|
||||||
//non-master servers. This packet allows us to add World
|
//non-master servers. This packet allows us to add World
|
||||||
//servers back if master crashed
|
//servers back if master crashed
|
||||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
CINSTREAM_SKIP_HEADER;
|
||||||
uint64_t header = inStream.Read(header);
|
|
||||||
|
|
||||||
uint32_t theirPort = 0;
|
uint32_t theirPort = 0;
|
||||||
uint32_t theirZoneID = 0;
|
uint32_t theirZoneID = 0;
|
||||||
@@ -602,13 +594,13 @@ void HandlePacket(Packet* packet) {
|
|||||||
instance->SetSysAddr(packet->systemAddress);
|
instance->SetSysAddr(packet->systemAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ServiceType::CHAT:
|
case ServiceType::CHAT:
|
||||||
chatServerMasterPeerSysAddr = packet->systemAddress;
|
chatServerMasterPeerSysAddr = packet->systemAddress;
|
||||||
break;
|
break;
|
||||||
case ServiceType::AUTH:
|
case ServiceType::AUTH:
|
||||||
authServerMasterPeerSysAddr = packet->systemAddress;
|
authServerMasterPeerSysAddr = packet->systemAddress;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// We just ignore any other server type
|
// We just ignore any other server type
|
||||||
break;
|
break;
|
||||||
@@ -664,8 +656,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MessageType::Master::PLAYER_ADDED: {
|
case MessageType::Master::PLAYER_ADDED: {
|
||||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
CINSTREAM_SKIP_HEADER;
|
||||||
uint64_t header = inStream.Read(header);
|
|
||||||
|
|
||||||
LWOMAPID theirZoneID = 0;
|
LWOMAPID theirZoneID = 0;
|
||||||
LWOINSTANCEID theirInstanceID = 0;
|
LWOINSTANCEID theirInstanceID = 0;
|
||||||
@@ -684,8 +675,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MessageType::Master::PLAYER_REMOVED: {
|
case MessageType::Master::PLAYER_REMOVED: {
|
||||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
CINSTREAM_SKIP_HEADER;
|
||||||
uint64_t header = inStream.Read(header);
|
|
||||||
|
|
||||||
LWOMAPID theirZoneID = 0;
|
LWOMAPID theirZoneID = 0;
|
||||||
LWOINSTANCEID theirInstanceID = 0;
|
LWOINSTANCEID theirInstanceID = 0;
|
||||||
@@ -702,8 +692,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MessageType::Master::CREATE_PRIVATE_ZONE: {
|
case MessageType::Master::CREATE_PRIVATE_ZONE: {
|
||||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
CINSTREAM_SKIP_HEADER;
|
||||||
uint64_t header = inStream.Read(header);
|
|
||||||
|
|
||||||
uint32_t mapId;
|
uint32_t mapId;
|
||||||
LWOCLONEID cloneId;
|
LWOCLONEID cloneId;
|
||||||
@@ -714,6 +703,8 @@ void HandlePacket(Packet* packet) {
|
|||||||
|
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
inStream.Read<uint32_t>(len);
|
inStream.Read<uint32_t>(len);
|
||||||
|
len = std::min<uint32_t>(len, 50); // cap the master password at 50 characters
|
||||||
|
|
||||||
for (uint32_t i = 0; len > i; i++) {
|
for (uint32_t i = 0; len > i; i++) {
|
||||||
char character;
|
char character;
|
||||||
inStream.Read<char>(character);
|
inStream.Read<char>(character);
|
||||||
@@ -726,8 +717,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MessageType::Master::REQUEST_PRIVATE_ZONE: {
|
case MessageType::Master::REQUEST_PRIVATE_ZONE: {
|
||||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
CINSTREAM_SKIP_HEADER;
|
||||||
uint64_t header = inStream.Read(header);
|
|
||||||
|
|
||||||
uint64_t requestID = 0;
|
uint64_t requestID = 0;
|
||||||
uint8_t mythranShift = false;
|
uint8_t mythranShift = false;
|
||||||
@@ -739,6 +729,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
|
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
inStream.Read<uint32_t>(len);
|
inStream.Read<uint32_t>(len);
|
||||||
|
len = std::min<uint32_t>(len, 50);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < len; i++) {
|
for (uint32_t i = 0; i < len; i++) {
|
||||||
char character; inStream.Read<char>(character);
|
char character; inStream.Read<char>(character);
|
||||||
@@ -761,8 +752,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MessageType::Master::WORLD_READY: {
|
case MessageType::Master::WORLD_READY: {
|
||||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
CINSTREAM_SKIP_HEADER;
|
||||||
uint64_t header = inStream.Read(header);
|
|
||||||
|
|
||||||
LWOMAPID zoneID;
|
LWOMAPID zoneID;
|
||||||
LWOINSTANCEID instanceID;
|
LWOINSTANCEID instanceID;
|
||||||
@@ -785,8 +775,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MessageType::Master::PREP_ZONE: {
|
case MessageType::Master::PREP_ZONE: {
|
||||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
CINSTREAM_SKIP_HEADER;
|
||||||
uint64_t header = inStream.Read(header);
|
|
||||||
|
|
||||||
int32_t zoneID;
|
int32_t zoneID;
|
||||||
inStream.Read(zoneID);
|
inStream.Read(zoneID);
|
||||||
@@ -801,8 +790,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MessageType::Master::AFFIRM_TRANSFER_RESPONSE: {
|
case MessageType::Master::AFFIRM_TRANSFER_RESPONSE: {
|
||||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
CINSTREAM_SKIP_HEADER;
|
||||||
uint64_t header = inStream.Read(header);
|
|
||||||
|
|
||||||
uint64_t requestID;
|
uint64_t requestID;
|
||||||
|
|
||||||
@@ -876,7 +864,7 @@ int ShutdownSequence(int32_t signal) {
|
|||||||
if (!instance->GetIsReady()) {
|
if (!instance->GetIsReady()) {
|
||||||
Game::im->RemoveInstance(instance);
|
Game::im->RemoveInstance(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
instance->SetIsShuttingDown(true);
|
instance->SetIsShuttingDown(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ dNavMesh::~dNavMesh() {
|
|||||||
|
|
||||||
|
|
||||||
void dNavMesh::LoadNavmesh() {
|
void dNavMesh::LoadNavmesh() {
|
||||||
|
struct FPClose {
|
||||||
|
FILE* fp{};
|
||||||
|
~FPClose() { if (fp) fclose(fp); }
|
||||||
|
} fp;
|
||||||
|
|
||||||
std::string path = (BinaryPathFinder::GetBinaryDir() / "navmeshes/" / (std::to_string(m_ZoneId) + ".bin")).string();
|
std::string path = (BinaryPathFinder::GetBinaryDir() / "navmeshes/" / (std::to_string(m_ZoneId) + ".bin")).string();
|
||||||
|
|
||||||
@@ -44,55 +48,48 @@ void dNavMesh::LoadNavmesh() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE* fp;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
fopen_s(&fp, path.c_str(), "rb");
|
fopen_s(&fp.fp, path.c_str(), "rb");
|
||||||
#elif __APPLE__
|
#elif __APPLE__
|
||||||
// macOS has 64bit file IO by default
|
// macOS has 64bit file IO by default
|
||||||
fp = fopen(path.c_str(), "rb");
|
fp.fp = fopen(path.c_str(), "rb");
|
||||||
#else
|
#else
|
||||||
fp = fopen64(path.c_str(), "rb");
|
fp.fp = fopen64(path.c_str(), "rb");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!fp) {
|
if (!fp.fp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read header.
|
// Read header.
|
||||||
NavMeshSetHeader header;
|
NavMeshSetHeader header;
|
||||||
size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp);
|
size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp.fp);
|
||||||
if (readLen != 1) {
|
if (readLen != 1) {
|
||||||
fclose(fp);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.magic != NAVMESHSET_MAGIC) {
|
if (header.magic != NAVMESHSET_MAGIC) {
|
||||||
fclose(fp);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.version != NAVMESHSET_VERSION) {
|
if (header.version != NAVMESHSET_VERSION) {
|
||||||
fclose(fp);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dtNavMesh* mesh = dtAllocNavMesh();
|
dtNavMesh* mesh = dtAllocNavMesh();
|
||||||
if (!mesh) {
|
if (!mesh) {
|
||||||
fclose(fp);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dtStatus status = mesh->init(&header.params);
|
dtStatus status = mesh->init(&header.params);
|
||||||
if (dtStatusFailed(status)) {
|
if (dtStatusFailed(status)) {
|
||||||
fclose(fp);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read tiles.
|
// Read tiles.
|
||||||
for (int i = 0; i < header.numTiles; ++i) {
|
for (int i = 0; i < header.numTiles; ++i) {
|
||||||
NavMeshTileHeader tileHeader;
|
NavMeshTileHeader tileHeader;
|
||||||
readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
|
readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp.fp);
|
||||||
if (readLen != 1) return;
|
if (readLen != 1) return;
|
||||||
|
|
||||||
if (!tileHeader.tileRef || !tileHeader.dataSize)
|
if (!tileHeader.tileRef || !tileHeader.dataSize)
|
||||||
@@ -101,14 +98,15 @@ void dNavMesh::LoadNavmesh() {
|
|||||||
unsigned char* data = static_cast<unsigned char*>(dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM));
|
unsigned char* data = static_cast<unsigned char*>(dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM));
|
||||||
if (!data) break;
|
if (!data) break;
|
||||||
memset(data, 0, tileHeader.dataSize);
|
memset(data, 0, tileHeader.dataSize);
|
||||||
readLen = fread(data, tileHeader.dataSize, 1, fp);
|
readLen = fread(data, tileHeader.dataSize, 1, fp.fp);
|
||||||
if (readLen != 1) return;
|
if (readLen != 1) {
|
||||||
|
dtFree(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
|
mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
m_NavMesh = mesh;
|
m_NavMesh = mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,13 +185,18 @@ std::vector<NiPoint3> dNavMesh::GetPath(const NiPoint3& startPos, const NiPoint3
|
|||||||
path.push_back(startPos); //insert the start pos
|
path.push_back(startPos); //insert the start pos
|
||||||
|
|
||||||
// Linearly interpolate between these two points:
|
// Linearly interpolate between these two points:
|
||||||
for (int i = 0; i < numPoints; i++) {
|
const auto yDist = endPos.y - startPos.y;
|
||||||
NiPoint3 newPoint{ startPos };
|
// Ensure we cannot divide by zero
|
||||||
|
const auto xDist = endPos.x - startPos.x;
|
||||||
|
if (xDist != 0.0f) {
|
||||||
|
for (int i = 0; i < numPoints; i++) {
|
||||||
|
NiPoint3 newPoint{ startPos };
|
||||||
|
|
||||||
newPoint.x += speed;
|
newPoint.x += speed;
|
||||||
newPoint.y = newPoint.y + (((endPos.y - startPos.y) / (endPos.x - startPos.x)) * (newPoint.x - startPos.x));
|
newPoint.y = newPoint.y + ((yDist / xDist) * (newPoint.x - startPos.x));
|
||||||
|
|
||||||
path.push_back(newPoint);
|
path.push_back(newPoint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
path.push_back(endPos); //finally insert our end pos
|
path.push_back(endPos); //finally insert our end pos
|
||||||
@@ -212,16 +215,16 @@ std::vector<NiPoint3> dNavMesh::GetPath(const NiPoint3& startPos, const NiPoint3
|
|||||||
ePos[2] = endPos.z;
|
ePos[2] = endPos.z;
|
||||||
|
|
||||||
dtStatus pathFindStatus;
|
dtStatus pathFindStatus;
|
||||||
dtPolyRef startRef;
|
dtPolyRef startRef{};
|
||||||
dtPolyRef endRef;
|
dtPolyRef endRef{};
|
||||||
float polyPickExt[3] = { 32.0f, 32.0f, 32.0f };
|
float polyPickExt[3] = { 32.0f, 32.0f, 32.0f };
|
||||||
dtQueryFilter filter{};
|
dtQueryFilter filter{};
|
||||||
|
|
||||||
//Find our start poly
|
//Find our start poly
|
||||||
m_NavQuery->findNearestPoly(sPos, polyPickExt, &filter, &startRef, 0);
|
const auto startResult = m_NavQuery->findNearestPoly(sPos, polyPickExt, &filter, &startRef, 0);
|
||||||
|
|
||||||
//Find our end poly
|
//Find our end poly
|
||||||
m_NavQuery->findNearestPoly(ePos, polyPickExt, &filter, &endRef, 0);
|
const auto endResult = m_NavQuery->findNearestPoly(ePos, polyPickExt, &filter, &endRef, 0);
|
||||||
|
|
||||||
pathFindStatus = DT_FAILURE;
|
pathFindStatus = DT_FAILURE;
|
||||||
int m_nstraightPath = 0;
|
int m_nstraightPath = 0;
|
||||||
@@ -232,7 +235,7 @@ std::vector<NiPoint3> dNavMesh::GetPath(const NiPoint3& startPos, const NiPoint3
|
|||||||
dtPolyRef m_straightPathPolys[MAX_POLYS];
|
dtPolyRef m_straightPathPolys[MAX_POLYS];
|
||||||
int m_straightPathOptions = 0;
|
int m_straightPathOptions = 0;
|
||||||
|
|
||||||
if (startRef && endRef) {
|
if (dtStatusSucceed(startResult) && dtStatusSucceed(endResult)) {
|
||||||
m_NavQuery->findPath(startRef, endRef, sPos, ePos, &filter, m_polys, &m_npolys, MAX_POLYS);
|
m_NavQuery->findPath(startRef, endRef, sPos, ePos, &filter, m_polys, &m_npolys, MAX_POLYS);
|
||||||
|
|
||||||
if (m_npolys) {
|
if (m_npolys) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ RawChunk::RawChunk(std::ifstream& stream) {
|
|||||||
|
|
||||||
// We can just skip the rest of the data so we can read the next chunks, we don't need anymore data
|
// We can just skip the rest of the data so we can read the next chunks, we don't need anymore data
|
||||||
|
|
||||||
|
// Possible overflow here? TODO make reasonable upper bound or confirm big numbers arent necessary to have
|
||||||
uint32_t colorMapSize;
|
uint32_t colorMapSize;
|
||||||
BinaryIO::BinaryRead(stream, colorMapSize);
|
BinaryIO::BinaryRead(stream, colorMapSize);
|
||||||
stream.seekg(static_cast<uint32_t>(stream.tellg()) + (colorMapSize * colorMapSize * 4));
|
stream.seekg(static_cast<uint32_t>(stream.tellg()) + (colorMapSize * colorMapSize * 4));
|
||||||
|
|||||||
@@ -157,8 +157,7 @@ void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//If we aren't running in live mode, then only GMs are allowed to enter:
|
//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 (Game::config->GetValue<bool>("closed_to_non_devs", false) && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) {
|
||||||
if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) {
|
|
||||||
stamps.emplace_back(eStamps::GM_REQUIRED, 1);
|
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);
|
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username, stamps);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ ChatModerationRequest ClientPackets::HandleChatModerationRequest(Packet* packet)
|
|||||||
|
|
||||||
uint16_t messageLength;
|
uint16_t messageLength;
|
||||||
inStream.Read(messageLength);
|
inStream.Read(messageLength);
|
||||||
|
if (messageLength > MAX_MESSAGE_LENGTH) return request;
|
||||||
for (uint32_t i = 0; i < messageLength; ++i) {
|
for (uint32_t i = 0; i < messageLength; ++i) {
|
||||||
uint16_t character;
|
uint16_t character;
|
||||||
inStream.Read(character);
|
inStream.Read(character);
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ void MasterPackets::SendZoneTransferResponse(dServer* server, const SystemAddres
|
|||||||
|
|
||||||
void MasterPackets::HandleServerInfo(Packet* packet) {
|
void MasterPackets::HandleServerInfo(Packet* packet) {
|
||||||
RakNet::BitStream inStream(packet->data, packet->length, false);
|
RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||||
uint64_t header = inStream.Read(header);
|
uint64_t header{};
|
||||||
|
inStream.Read(header);
|
||||||
|
|
||||||
uint32_t theirPort = 0;
|
uint32_t theirPort = 0;
|
||||||
uint32_t theirZoneID = 0;
|
uint32_t theirZoneID = 0;
|
||||||
|
|||||||
@@ -103,12 +103,9 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t rep
|
|||||||
|
|
||||||
//Compress the data before sending:
|
//Compress the data before sending:
|
||||||
const uint32_t reservedSize = ZCompression::GetMaxCompressedLength(data.GetNumberOfBytesUsed());
|
const uint32_t reservedSize = ZCompression::GetMaxCompressedLength(data.GetNumberOfBytesUsed());
|
||||||
uint8_t* compressedData = new uint8_t[reservedSize];
|
auto compressedData = std::make_unique<uint8_t[]>(reservedSize);
|
||||||
|
|
||||||
// TODO There should be better handling here for not enough memory...
|
size_t size = ZCompression::Compress(data.GetData(), data.GetNumberOfBytesUsed(), compressedData.get(), reservedSize);
|
||||||
if (!compressedData) return;
|
|
||||||
|
|
||||||
size_t size = ZCompression::Compress(data.GetData(), data.GetNumberOfBytesUsed(), compressedData, reservedSize);
|
|
||||||
|
|
||||||
assert(size <= reservedSize);
|
assert(size <= reservedSize);
|
||||||
|
|
||||||
@@ -123,11 +120,10 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t rep
|
|||||||
* an assertion is done to prevent bad data from being saved or sent.
|
* an assertion is done to prevent bad data from being saved or sent.
|
||||||
*/
|
*/
|
||||||
#pragma warning(disable:6385) // C6385 Reading invalid data from 'compressedData'.
|
#pragma warning(disable:6385) // C6385 Reading invalid data from 'compressedData'.
|
||||||
bitStream.WriteAlignedBytes(compressedData, size);
|
bitStream.WriteAlignedBytes(compressedData.get(), size);
|
||||||
#pragma warning(default:6385)
|
#pragma warning(default:6385)
|
||||||
|
|
||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
delete[] compressedData;
|
|
||||||
LOG("Sent CreateCharacter for ID: %llu", player);
|
LOG("Sent CreateCharacter for ID: %llu", player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -233,13 +233,11 @@ bool dServer::Startup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dServer::UpdateMaximumMtuSize() {
|
void dServer::UpdateMaximumMtuSize() {
|
||||||
auto maxMtuSize = mConfig->GetValue("maximum_mtu_size");
|
mPeer->SetMTUSize(mConfig->GetValue<int32_t>("maximum_mtu_size", 1228));
|
||||||
mPeer->SetMTUSize(maxMtuSize.empty() ? 1228 : std::stoi(maxMtuSize));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dServer::UpdateBandwidthLimit() {
|
void dServer::UpdateBandwidthLimit() {
|
||||||
auto newBandwidth = mConfig->GetValue("maximum_outgoing_bandwidth");
|
mPeer->SetPerConnectionOutgoingBandwidthLimit(mConfig->GetValue<int32_t>("maximum_outgoing_bandwidth", 0));
|
||||||
mPeer->SetPerConnectionOutgoingBandwidthLimit(!newBandwidth.empty() ? std::stoi(newBandwidth) : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dServer::Shutdown() {
|
void dServer::Shutdown() {
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ void dpEntity::SetPosition(const NiPoint3& newPos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dpEntity::SetRotation(const NiQuaternion& newRot) {
|
void dpEntity::SetRotation(const NiQuaternion& newRot) {
|
||||||
|
if (!m_CollisionShape) return;
|
||||||
m_Rotation = newRot;
|
m_Rotation = newRot;
|
||||||
|
|
||||||
if (m_CollisionShape->GetShapeType() == dpShapeType::Box) {
|
if (m_CollisionShape->GetShapeType() == dpShapeType::Box) {
|
||||||
@@ -118,6 +119,7 @@ void dpEntity::SetRotation(const NiQuaternion& newRot) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dpEntity::SetScale(float newScale) {
|
void dpEntity::SetScale(float newScale) {
|
||||||
|
if (!m_CollisionShape) return;
|
||||||
m_Scale = newScale;
|
m_Scale = newScale;
|
||||||
|
|
||||||
if (m_CollisionShape->GetShapeType() == dpShapeType::Box) {
|
if (m_CollisionShape->GetShapeType() == dpShapeType::Box) {
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ void dpWorld::Shutdown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool dpWorld::IsLoaded() {
|
bool dpWorld::IsLoaded() {
|
||||||
return m_NavMesh->IsNavmeshLoaded();
|
return m_NavMesh && m_NavMesh->IsNavmeshLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void dpWorld::StepWorld(float deltaTime) {
|
void dpWorld::StepWorld(float deltaTime) {
|
||||||
|
|||||||
@@ -36,13 +36,14 @@ void AmSkullkinDrill::OnStartup(Entity* self) {
|
|||||||
Entity* AmSkullkinDrill::GetStandObj(Entity* self) {
|
Entity* AmSkullkinDrill::GetStandObj(Entity* self) {
|
||||||
const auto& myGroup = self->GetGroups();
|
const auto& myGroup = self->GetGroups();
|
||||||
|
|
||||||
if (myGroup.empty()) {
|
if (myGroup.empty() || myGroup[0].empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
const auto& group = myGroup[0];
|
||||||
|
|
||||||
std::string groupName = "Drill_Stand_";
|
std::string groupName = "Drill_Stand_";
|
||||||
|
|
||||||
groupName.push_back(myGroup[0][myGroup[0].size() - 1]);
|
groupName.push_back(group.back());
|
||||||
|
|
||||||
const auto standObjs = Game::entityManager->GetEntitiesInGroup(groupName);
|
const auto standObjs = Game::entityManager->GetEntitiesInGroup(groupName);
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,14 @@
|
|||||||
void Binoculars::OnUse(Entity* self, Entity* user) {
|
void Binoculars::OnUse(Entity* self, Entity* user) {
|
||||||
const auto number = self->GetVarAsString(u"number");
|
const auto number = self->GetVarAsString(u"number");
|
||||||
|
|
||||||
int32_t flag = std::stoi(std::to_string(Game::server->GetZoneID()).substr(0, 2) + number);
|
const int32_t flag = GeneralUtils::TryParse(std::to_string(Game::server->GetZoneID()).substr(0, 2) + number, 0);
|
||||||
if (user->GetCharacter()->GetPlayerFlag(flag) == false) {
|
GameMessages::GetFlag flagMsg;
|
||||||
user->GetCharacter()->SetPlayerFlag(flag, true);
|
flagMsg.target = user->GetObjectID();
|
||||||
|
flagMsg.flagID = flag;
|
||||||
|
flagMsg.Send();
|
||||||
|
if (!flagMsg.flag) {
|
||||||
|
auto* const character = user->GetCharacter();
|
||||||
|
if (character) character->SetPlayerFlag(flag, true);
|
||||||
GameMessages::SendFireEventClientSide(self->GetObjectID(), user->GetSystemAddress(), u"achieve", LWOOBJID_EMPTY, 0, -1, LWOOBJID_EMPTY);
|
GameMessages::SendFireEventClientSide(self->GetObjectID(), user->GetSystemAddress(), u"achieve", LWOOBJID_EMPTY, 0, -1, LWOOBJID_EMPTY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,14 +33,18 @@ void StoryBoxInteractServer::OnUse(Entity* self, Entity* user) {
|
|||||||
const auto storyText = self->GetVarAsString(u"storyText");
|
const auto storyText = self->GetVarAsString(u"storyText");
|
||||||
if (storyText.length() > 2) {
|
if (storyText.length() > 2) {
|
||||||
auto storyValue = GeneralUtils::TryParse<uint32_t>(storyText.substr(storyText.length() - 2));
|
auto storyValue = GeneralUtils::TryParse<uint32_t>(storyText.substr(storyText.length() - 2));
|
||||||
if(!storyValue) return;
|
if (!storyValue) return;
|
||||||
int32_t boxFlag = self->GetVar<int32_t>(u"altFlagID");
|
int32_t boxFlag = self->GetVar<int32_t>(u"altFlagID");
|
||||||
if (boxFlag <= 0) {
|
if (boxFlag <= 0) {
|
||||||
boxFlag = (10000 + Game::server->GetZoneID() + storyValue.value());
|
boxFlag = (10000 + Game::server->GetZoneID() + storyValue.value());
|
||||||
}
|
}
|
||||||
|
GameMessages::GetFlag flagMsg;
|
||||||
if (user->GetCharacter()->GetPlayerFlag(boxFlag) == false) {
|
flagMsg.target = user->GetObjectID();
|
||||||
user->GetCharacter()->SetPlayerFlag(boxFlag, true);
|
flagMsg.flagID = boxFlag;
|
||||||
|
flagMsg.Send();
|
||||||
|
if (!flagMsg.flag) {
|
||||||
|
auto* const character = user->GetCharacter();
|
||||||
|
if (character) character->SetPlayerFlag(boxFlag, true);
|
||||||
GameMessages::SendFireEventClientSide(self->GetObjectID(), user->GetSystemAddress(), u"achieve", LWOOBJID_EMPTY, 0, -1, LWOOBJID_EMPTY);
|
GameMessages::SendFireEventClientSide(self->GetObjectID(), user->GetSystemAddress(), u"achieve", LWOOBJID_EMPTY, 0, -1, LWOOBJID_EMPTY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ void NsConcertChoiceBuildManager::SpawnCrate(Entity* self) {
|
|||||||
const auto splitGroup = GeneralUtils::SplitString(group, '_');
|
const auto splitGroup = GeneralUtils::SplitString(group, '_');
|
||||||
if (splitGroup.size() < 2)
|
if (splitGroup.size() < 2)
|
||||||
return;
|
return;
|
||||||
const auto groupNumber = std::stoi(splitGroup.at(1));
|
const auto groupNumber = GeneralUtils::TryParse(splitGroup.at(1), -1);
|
||||||
|
if (groupNumber == -1) return;
|
||||||
|
|
||||||
EntityInfo info{};
|
EntityInfo info{};
|
||||||
info.lot = crate.lot;
|
info.lot = crate.lot;
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ void NtParadoxPanelServer::OnUse(Entity* self, Entity* user) {
|
|||||||
|
|
||||||
const auto flag = self->GetVar<int32_t>(u"flag");
|
const auto flag = self->GetVar<int32_t>(u"flag");
|
||||||
|
|
||||||
player->GetCharacter()->SetPlayerFlag(flag, true);
|
auto* const character = player->GetCharacter();
|
||||||
|
if (character) character->SetPlayerFlag(flag, true);
|
||||||
|
|
||||||
RenderComponent::PlayAnimation(player, u"rebuild-celebrate");
|
RenderComponent::PlayAnimation(player, u"rebuild-celebrate");
|
||||||
|
|
||||||
|
|||||||
@@ -413,7 +413,8 @@ void ZoneAgProperty::BaseOnFireEventServerSide(Entity* self, Entity* sender, std
|
|||||||
if (player == nullptr)
|
if (player == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
player->GetCharacter()->SetPlayerFlag(self->GetVar<int32_t>(defeatedProperyFlag), true);
|
auto* const character = player->GetCharacter();
|
||||||
|
if (character) character->SetPlayerFlag(self->GetVar<int32_t>(defeatedProperyFlag), true);
|
||||||
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlayCinematic", 0, 0,
|
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"PlayCinematic", 0, 0,
|
||||||
LWOOBJID_EMPTY, destroyedCinematic, UNASSIGNED_SYSTEM_ADDRESS);
|
LWOOBJID_EMPTY, destroyedCinematic, UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ void VeMissionConsole::OnUse(Entity* self, Entity* user) {
|
|||||||
|
|
||||||
// The flag to set is 101<number>
|
// The flag to set is 101<number>
|
||||||
const auto flagNumber = self->GetVar<std::u16string>(m_NumberVariable);
|
const auto flagNumber = self->GetVar<std::u16string>(m_NumberVariable);
|
||||||
const int32_t flag = std::stoi("101" + GeneralUtils::UTF16ToWTF8(flagNumber));
|
const int32_t flag = GeneralUtils::TryParse("101" + GeneralUtils::UTF16ToWTF8(flagNumber), 0);
|
||||||
|
|
||||||
auto* character = user->GetCharacter();
|
auto* character = user->GetCharacter();
|
||||||
if (character != nullptr) {
|
if (character != nullptr) {
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ void CavePrisonCage::OnStartup(Entity* self) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* spawner = Game::zoneManager->GetSpawnersByName("PrisonCounterweight_0" + GeneralUtils::UTF16ToWTF8(myNum))[0];
|
const auto spawners = Game::zoneManager->GetSpawnersByName("PrisonCounterweight_0" + GeneralUtils::UTF16ToWTF8(myNum));
|
||||||
|
if (spawners.empty()) return;
|
||||||
|
|
||||||
|
auto* spawner = spawners[0];
|
||||||
|
|
||||||
self->SetVar<Spawner*>(u"CWSpawner", spawner);
|
self->SetVar<Spawner*>(u"CWSpawner", spawner);
|
||||||
|
|
||||||
@@ -21,6 +24,7 @@ void CavePrisonCage::OnStartup(Entity* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CavePrisonCage::Setup(Entity* self, Spawner* spawner) {
|
void CavePrisonCage::Setup(Entity* self, Spawner* spawner) {
|
||||||
|
if (!spawner) return;
|
||||||
SpawnCounterweight(self, spawner);
|
SpawnCounterweight(self, spawner);
|
||||||
|
|
||||||
NiPoint3 mypos = self->GetPosition();
|
NiPoint3 mypos = self->GetPosition();
|
||||||
@@ -62,6 +66,8 @@ void CavePrisonCage::OnQuickBuildNotifyState(Entity* self, eQuickBuildState stat
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CavePrisonCage::SpawnCounterweight(Entity* self, Spawner* spawner) {
|
void CavePrisonCage::SpawnCounterweight(Entity* self, Spawner* spawner) {
|
||||||
|
if (!spawner) return;
|
||||||
|
|
||||||
spawner->Reset();
|
spawner->Reset();
|
||||||
|
|
||||||
auto* counterweight = spawner->Spawn();
|
auto* counterweight = spawner->Spawn();
|
||||||
@@ -164,7 +170,8 @@ void CavePrisonCage::OnTimerDone(Entity* self, std::string timerName) {
|
|||||||
const auto flagNum = 2020 + self->GetVarAs<int32_t>(u"myNumber");
|
const auto flagNum = 2020 + self->GetVarAs<int32_t>(u"myNumber");
|
||||||
|
|
||||||
// Set the flag on the builder character
|
// Set the flag on the builder character
|
||||||
builder->GetCharacter()->SetPlayerFlag(flagNum, true);
|
auto* const character = builder->GetCharacter();
|
||||||
|
if (character) character->SetPlayerFlag(flagNum, true);
|
||||||
|
|
||||||
// Setup a timer named 'VillagerEscape' to be triggered in 5 seconds
|
// Setup a timer named 'VillagerEscape' to be triggered in 5 seconds
|
||||||
self->AddTimer("VillagerEscape", 5.0f);
|
self->AddTimer("VillagerEscape", 5.0f);
|
||||||
|
|||||||
@@ -408,7 +408,7 @@ void NjMonastryBossInstance::SummonWave(Entity* self, Entity* frakjaw) {
|
|||||||
|
|
||||||
// Stop the music for the first, fourth and fifth wave
|
// Stop the music for the first, fourth and fifth wave
|
||||||
const auto wave = self->GetVar<uint32_t>(WaveNumberVariable);
|
const auto wave = self->GetVar<uint32_t>(WaveNumberVariable);
|
||||||
if (wave >= 1 || wave < (m_Waves.size() - 1)) {
|
if (wave >= 1 && wave < (m_Waves.size() - 1)) {
|
||||||
GameMessages::SendNotifyClientObject(self->GetObjectID(), StopMusicNotification, 0, 0,
|
GameMessages::SendNotifyClientObject(self->GetObjectID(), StopMusicNotification, 0, 0,
|
||||||
LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(wave - 1),
|
LWOOBJID_EMPTY, AudioWaveAudio + std::to_string(wave - 1),
|
||||||
UNASSIGNED_SYSTEM_ADDRESS);
|
UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ void BaseConsoleTeleportServer::TransferPlayer(Entity* self, Entity* player, int
|
|||||||
|
|
||||||
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
||||||
|
|
||||||
if (characterComponent) characterComponent->SendToZone(std::stoi(GeneralUtils::UTF16ToWTF8(teleportZone)));
|
if (characterComponent) characterComponent->SendToZone(GeneralUtils::TryParse(GeneralUtils::UTF16ToWTF8(teleportZone), 0));
|
||||||
|
|
||||||
UpdatePlayerTable(self, player, false);
|
UpdatePlayerTable(self, player, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,12 +127,15 @@ void BasePropertyServer::BasePlayerLoaded(Entity* self, Entity* player) {
|
|||||||
if (player->GetObjectID() != propertyOwner)
|
if (player->GetObjectID() != propertyOwner)
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
const auto defeatedFlag = player->GetCharacter()->GetPlayerFlag(self->GetVar<int32_t>(defeatedProperyFlag));
|
GameMessages::GetFlag flagMsg;
|
||||||
|
flagMsg.target = player->GetObjectID();
|
||||||
|
flagMsg.flagID = self->GetVar<int32_t>(defeatedProperyFlag);
|
||||||
|
flagMsg.Send();
|
||||||
|
|
||||||
self->SetNetworkVar(UnclaimedVariable, true);
|
self->SetNetworkVar(UnclaimedVariable, true);
|
||||||
self->SetVar<LWOOBJID>(PlayerIDVariable, player->GetObjectID());
|
self->SetVar<LWOOBJID>(PlayerIDVariable, player->GetObjectID());
|
||||||
|
|
||||||
if (!defeatedFlag) {
|
if (!flagMsg.flag) {
|
||||||
StartMaelstrom(self, player);
|
StartMaelstrom(self, player);
|
||||||
SpawnSpots(self);
|
SpawnSpots(self);
|
||||||
GameMessages::SendPlay2DAmbientSound(player, GUIDMaelstrom);
|
GameMessages::SendPlay2DAmbientSound(player, GUIDMaelstrom);
|
||||||
|
|||||||
@@ -124,21 +124,23 @@ void BaseRandomServer::NotifySpawnerOfDeath(Entity* self, Spawner* spawner) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& sectionName = spawnerName.substr(0, spawnerName.size() - 7);
|
if (spawnerName.size() >= 7) {
|
||||||
|
const auto& sectionName = spawnerName.substr(0, spawnerName.size() - 7);
|
||||||
|
|
||||||
const auto variableName = u"mobsDead" + GeneralUtils::ASCIIToUTF16(sectionName);
|
const auto variableName = u"mobsDead" + GeneralUtils::ASCIIToUTF16(sectionName);
|
||||||
|
|
||||||
auto mobDeathCount = self->GetVar<int32_t>(variableName);
|
auto mobDeathCount = self->GetVar<int32_t>(variableName);
|
||||||
|
|
||||||
mobDeathCount++;
|
mobDeathCount++;
|
||||||
|
|
||||||
if (mobDeathCount >= mobDeathResetNumber) {
|
if (mobDeathCount >= mobDeathResetNumber) {
|
||||||
const auto& zoneInfo = GeneralUtils::SplitString(sectionName, '_');
|
const auto& zoneInfo = GeneralUtils::SplitString(sectionName, '_');
|
||||||
|
|
||||||
SpawnSection(self, sectionName, sectionMultipliers[zoneInfo[sectionIDConst - 1]]);
|
SpawnSection(self, sectionName, sectionMultipliers[zoneInfo[sectionIDConst - 1]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->SetVar(variableName, mobDeathCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
self->SetVar(variableName, mobDeathCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseRandomServer::NamedEnemyDeath(Entity* self, Spawner* spawner) {
|
void BaseRandomServer::NamedEnemyDeath(Entity* self, Spawner* spawner) {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public:
|
|||||||
void NamedTimerDone(Entity* self, const std::string& timerName);
|
void NamedTimerDone(Entity* self, const std::string& timerName);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<int32_t> namedMobs = {
|
const std::vector<int32_t> namedMobs = {
|
||||||
11988, // Ronin
|
11988, // Ronin
|
||||||
11984, // Spiderling
|
11984, // Spiderling
|
||||||
12654, // Horsemen
|
12654, // Horsemen
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ void BaseSurvivalServer::BasePlayerLoaded(Entity* self, Entity* player) {
|
|||||||
|
|
||||||
if (waitingIter != state.waitingPlayers.end() || playersIter != state.players.end()) {
|
if (waitingIter != state.waitingPlayers.end() || playersIter != state.players.end()) {
|
||||||
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
||||||
if (characterComponent) characterComponent->SendToZone(player->GetCharacter()->GetLastNonInstanceZoneID());
|
const auto* const character = player->GetCharacter();
|
||||||
|
if (characterComponent && character) characterComponent->SendToZone(character->GetLastNonInstanceZoneID());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ void NtFactionSpyServer::OnCinematicUpdate(Entity* self, Entity* sender, eCinema
|
|||||||
// Make sure we have a path of type <root>_<index>
|
// Make sure we have a path of type <root>_<index>
|
||||||
if (pathSplit.size() >= 2) {
|
if (pathSplit.size() >= 2) {
|
||||||
auto pathRoot = pathSplit.at(0);
|
auto pathRoot = pathSplit.at(0);
|
||||||
auto pathIndex = std::stoi(GeneralUtils::UTF16ToWTF8(pathSplit.at(1))) - 1;
|
auto pathIndex = GeneralUtils::TryParse(GeneralUtils::UTF16ToWTF8(pathSplit.at(1)), -1) - 1;
|
||||||
const auto& dialogueTable = self->GetVar<std::vector<SpyDialogue>>(m_SpyDialogueTableVariable);
|
const auto& dialogueTable = self->GetVar<std::vector<SpyDialogue>>(m_SpyDialogueTableVariable);
|
||||||
|
|
||||||
// Make sure we're listening to the root we're interested in
|
// Make sure we're listening to the root we're interested in
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ bool SpawnPetBaseServer::CheckNumberOfPets(Entity* self, Entity* user) {
|
|||||||
if (petID.empty())
|
if (petID.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto* spawnedPet = Game::entityManager->GetEntity(std::stoull(petID));
|
const auto* spawnedPet = Game::entityManager->GetEntity(GeneralUtils::TryParse(petID, LWOOBJID_EMPTY));
|
||||||
if (spawnedPet == nullptr)
|
if (spawnedPet == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,11 @@ void AgFans::OnStartup(Entity* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AgFans::ToggleFX(Entity* self, bool hit) {
|
void AgFans::ToggleFX(Entity* self, bool hit) {
|
||||||
std::string fanGroup = self->GetGroups()[0];
|
const auto groups = self->GetGroups();
|
||||||
std::vector<Entity*> fanVolumes = Game::entityManager->GetEntitiesInGroup(fanGroup);
|
std::string fanGroup = groups.empty() ? "" : groups[0];
|
||||||
|
const auto fanVolumes = Game::entityManager->GetEntitiesInGroup(fanGroup);
|
||||||
|
const auto fxObjs = Game::entityManager->GetEntitiesInGroup(fanGroup + "fx");
|
||||||
|
auto* const fxObj = fxObjs.empty() ? nullptr : fxObjs[0];
|
||||||
|
|
||||||
auto* renderComponent = static_cast<RenderComponent*>(self->GetComponent(eReplicaComponentType::RENDER));
|
auto* renderComponent = static_cast<RenderComponent*>(self->GetComponent(eReplicaComponentType::RENDER));
|
||||||
|
|
||||||
@@ -47,8 +50,7 @@ void AgFans::ToggleFX(Entity* self, bool hit) {
|
|||||||
volumePhys->SetPhysicsEffectActive(false);
|
volumePhys->SetPhysicsEffectActive(false);
|
||||||
Game::entityManager->SerializeEntity(volume);
|
Game::entityManager->SerializeEntity(volume);
|
||||||
if (!hit) {
|
if (!hit) {
|
||||||
Entity* fxObj = Game::entityManager->GetEntitiesInGroup(fanGroup + "fx")[0];
|
if (fxObj) RenderComponent::PlayAnimation(fxObj, u"trigger");
|
||||||
RenderComponent::PlayAnimation(fxObj, u"trigger");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!self->GetVar<bool>(u"on") && self->GetVar<bool>(u"alive")) {
|
} else if (!self->GetVar<bool>(u"on") && self->GetVar<bool>(u"alive")) {
|
||||||
@@ -63,8 +65,7 @@ void AgFans::ToggleFX(Entity* self, bool hit) {
|
|||||||
volumePhys->SetPhysicsEffectActive(true);
|
volumePhys->SetPhysicsEffectActive(true);
|
||||||
Game::entityManager->SerializeEntity(volume);
|
Game::entityManager->SerializeEntity(volume);
|
||||||
if (!hit) {
|
if (!hit) {
|
||||||
Entity* fxObj = Game::entityManager->GetEntitiesInGroup(fanGroup + "fx")[0];
|
if (fxObj) RenderComponent::PlayAnimation(fxObj, u"idle");
|
||||||
RenderComponent::PlayAnimation(fxObj, u"idle");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
void FvBrickPuzzleServer::OnStartup(Entity* self) {
|
void FvBrickPuzzleServer::OnStartup(Entity* self) {
|
||||||
const auto myGroup = GeneralUtils::UTF16ToWTF8(self->GetVar<std::u16string>(u"spawner_name"));
|
const auto myGroup = GeneralUtils::UTF16ToWTF8(self->GetVar<std::u16string>(u"spawner_name"));
|
||||||
|
if (myGroup.size() <= 10) return;
|
||||||
|
|
||||||
const auto pipeNum = GeneralUtils::TryParse<int32_t>(myGroup.substr(10, 1));
|
const auto pipeNum = GeneralUtils::TryParse<int32_t>(myGroup.substr(10, 1));
|
||||||
if (!pipeNum) return;
|
if (!pipeNum) return;
|
||||||
@@ -17,6 +18,7 @@ void FvBrickPuzzleServer::OnStartup(Entity* self) {
|
|||||||
|
|
||||||
void FvBrickPuzzleServer::OnDie(Entity* self, Entity* killer) {
|
void FvBrickPuzzleServer::OnDie(Entity* self, Entity* killer) {
|
||||||
const auto myGroup = GeneralUtils::UTF16ToWTF8(self->GetVar<std::u16string>(u"spawner_name"));
|
const auto myGroup = GeneralUtils::UTF16ToWTF8(self->GetVar<std::u16string>(u"spawner_name"));
|
||||||
|
if (myGroup.size() <= 10) return;
|
||||||
|
|
||||||
const auto pipeNum = GeneralUtils::TryParse<int32_t>(myGroup.substr(10, 1));
|
const auto pipeNum = GeneralUtils::TryParse<int32_t>(myGroup.substr(10, 1));
|
||||||
if (!pipeNum) return;
|
if (!pipeNum) return;
|
||||||
|
|||||||
@@ -9,9 +9,12 @@ void FvFacilityBrick::OnStartup(Entity* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FvFacilityBrick::OnNotifyObject(Entity* self, Entity* sender, const std::string& name, int32_t param1, int32_t param2) {
|
void FvFacilityBrick::OnNotifyObject(Entity* self, Entity* sender, const std::string& name, int32_t param1, int32_t param2) {
|
||||||
auto* brickSpawner = Game::zoneManager->GetSpawnersByName("ImaginationBrick")[0];
|
const auto brickObjs = Game::zoneManager->GetSpawnersByName("ImaginationBrick");
|
||||||
auto* bugSpawner = Game::zoneManager->GetSpawnersByName("MaelstromBug")[0];
|
auto* const brickSpawner = brickObjs.empty() ? nullptr : brickObjs[0];
|
||||||
auto* canisterSpawner = Game::zoneManager->GetSpawnersByName("BrickCanister")[0];
|
const auto bugObjs = Game::zoneManager->GetSpawnersByName("MaelstromBug");
|
||||||
|
auto* const bugSpawner = bugObjs.empty() ? nullptr : bugObjs[0];
|
||||||
|
const auto canisterObjs = Game::zoneManager->GetSpawnersByName("BrickCanister");
|
||||||
|
auto* const canisterSpawner = canisterObjs.empty() ? nullptr : canisterObjs[0];
|
||||||
|
|
||||||
if (name == "ConsoleLeftUp") {
|
if (name == "ConsoleLeftUp") {
|
||||||
GameMessages::SendStopFXEffect(self, true, "LeftPipeOff");
|
GameMessages::SendStopFXEffect(self, true, "LeftPipeOff");
|
||||||
@@ -62,7 +65,7 @@ void FvFacilityBrick::OnNotifyObject(Entity* self, Entity* sender, const std::st
|
|||||||
canisterSpawner->Reset();
|
canisterSpawner->Reset();
|
||||||
canisterSpawner->Deactivate();
|
canisterSpawner->Deactivate();
|
||||||
} else if (self->GetVar<bool>(u"ConsoleLEFTActive") || self->GetVar<bool>(u"ConsoleRIGHTActive")) {
|
} else if (self->GetVar<bool>(u"ConsoleLEFTActive") || self->GetVar<bool>(u"ConsoleRIGHTActive")) {
|
||||||
brickSpawner->Activate();
|
if (brickSpawner) brickSpawner->Activate();
|
||||||
|
|
||||||
auto* object = Game::entityManager->GetEntitiesInGroup("Brick")[0];
|
auto* object = Game::entityManager->GetEntitiesInGroup("Brick")[0];
|
||||||
|
|
||||||
@@ -70,17 +73,25 @@ void FvFacilityBrick::OnNotifyObject(Entity* self, Entity* sender, const std::st
|
|||||||
GameMessages::SendStopFXEffect(object, true, "bluebrick");
|
GameMessages::SendStopFXEffect(object, true, "bluebrick");
|
||||||
}
|
}
|
||||||
|
|
||||||
bugSpawner->Reset();
|
if (bugSpawner) {
|
||||||
bugSpawner->Deactivate();
|
bugSpawner->Reset();
|
||||||
|
bugSpawner->Deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
canisterSpawner->Reset();
|
if (canisterSpawner) {
|
||||||
canisterSpawner->Activate();
|
canisterSpawner->Reset();
|
||||||
|
canisterSpawner->Activate();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
brickSpawner->Reset();
|
if (brickSpawner) {
|
||||||
brickSpawner->Deactivate();
|
brickSpawner->Reset();
|
||||||
|
brickSpawner->Deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
bugSpawner->Reset();
|
if (bugSpawner) {
|
||||||
bugSpawner->Activate();
|
bugSpawner->Reset();
|
||||||
|
bugSpawner->Activate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ void SGCannon::GameOverTimerFunc(Entity* self) {
|
|||||||
|
|
||||||
void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) {
|
void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) {
|
||||||
if (self->GetVar<bool>(GameStartedVariable)) {
|
if (self->GetVar<bool>(GameStartedVariable)) {
|
||||||
const auto spawnNumber = static_cast<uint32_t>(std::stoi(name.substr(7)));
|
const auto spawnNumber = GeneralUtils::TryParse(name.substr(7), 0);
|
||||||
const auto& activeSpawns = self->GetVar<std::vector<SGEnemy>>(ActiveSpawnsVariable);
|
const auto& activeSpawns = self->GetVar<std::vector<SGEnemy>>(ActiveSpawnsVariable);
|
||||||
if (activeSpawns.size() <= spawnNumber) {
|
if (activeSpawns.size() <= spawnNumber) {
|
||||||
LOG_DEBUG("Trying to spawn %i when spawns size is only %i", spawnNumber, activeSpawns.size());
|
LOG_DEBUG("Trying to spawn %i when spawns size is only %i", spawnNumber, activeSpawns.size());
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ void NsConcertQuickBuild::OnStartup(Entity* self) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Get the manager of the crate of this quick build
|
// Get the manager of the crate of this quick build
|
||||||
const auto groupNumber = std::stoi(splitGroup.at(3));
|
const auto groupNumber = GeneralUtils::TryParse(splitGroup.at(3), -1);
|
||||||
|
if (groupNumber == -1) return;
|
||||||
|
|
||||||
const auto managerObjects = Game::entityManager->GetEntitiesInGroup("CB_" + std::to_string(groupNumber));
|
const auto managerObjects = Game::entityManager->GetEntitiesInGroup("CB_" + std::to_string(groupNumber));
|
||||||
if (managerObjects.empty())
|
if (managerObjects.empty())
|
||||||
return;
|
return;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user