Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ temp/
cmake-build-debug/
RelWithDebInfo/
docker/configs
valgrind-out.txt

# Third party libraries
thirdparty/mysql/
Expand Down
16 changes: 14 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ project(Darkflame
LANGUAGES C CXX
)

# Sanitizer flags - TODO: Make CMake preset before finalizing PR
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
add_compile_options("-fsanitize=undefined")
add_link_options("-fsanitize=undefined")

if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_link_options("-static-libsan")
else()
add_link_options("-static-libasan")
endif()
endif()

# check if the path to the source directory contains a space
if("${CMAKE_SOURCE_DIR}" MATCHES " ")
message(FATAL_ERROR "The server cannot build in the path (" ${CMAKE_SOURCE_DIR} ") because it contains a space. Please move the server to a path without spaces.")
Expand All @@ -17,7 +29,7 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debugging
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions
set(CMAKE_VISIBILITY_INLINES_HIDDEN OFF) # Set C and C++ symbol visibility to hide inlined functions
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

# Read variables from file
Expand Down Expand Up @@ -262,7 +274,7 @@ if(MSVC)
# add_compile_options("/W4")
# Want to enable warnings eventually, but WAY too much noise right now
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
add_compile_options("-Wuninitialized" "-Wold-style-cast")
add_compile_options("-Wuninitialized" "-Wold-style-cast" "-Wstrict-aliasing=01")
else()
message(WARNING "Unknown compiler: '${CMAKE_CXX_COMPILER_ID}' - No warning flags enabled.")
endif()
Expand Down
1 change: 1 addition & 0 deletions cmake/toolchains/linux-clang.cmake
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Try and find a clang-16 install, falling back to a generic clang install otherwise
find_program(CLANG_C_COMPILER clang-16 | clang REQUIRED)
find_program(CLANG_CXX_COMPILER clang++-16 | clang++ REQUIRED)
find_program(CLANG_CXX_LINKER lld REQUIRED)

# Debug messages
message(DEBUG "CLANG_C_COMPILER = ${CLANG_C_COMPILER}")
Expand Down
56 changes: 25 additions & 31 deletions dCommon/Amf3.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#ifndef __AMF3__H__
#define __AMF3__H__
#ifndef AMF3_H
#define AMF3_H

#include "dCommonVars.h"
#include "Logger.h"
#include "Game.h"
#include "GeneralUtils.h"

#include <unordered_map>
#include <vector>
Expand Down Expand Up @@ -74,28 +75,15 @@ template <> [[nodiscard]] constexpr eAmf AMFValue<double>::GetValueType() const
template <typename ValueType>
[[nodiscard]] constexpr eAmf AMFValue<ValueType>::GetValueType() const noexcept { return eAmf::Undefined; }

// As a string this is much easier to write and read from a BitStream.
template <>
class AMFValue<const char*> : public AMFBaseValue {
public:
AMFValue() = default;
AMFValue(const char* value) { m_Data = value; }
virtual ~AMFValue() override = default;

[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::String; }

[[nodiscard]] const std::string& GetValue() const { return m_Data; }
void SetValue(const std::string& value) { m_Data = value; }
protected:
std::string m_Data;
};

using AMFNullValue = AMFValue<std::nullptr_t>;
using AMFBoolValue = AMFValue<bool>;
using AMFIntValue = AMFValue<int32_t>;
using AMFStringValue = AMFValue<std::string>;
using AMFDoubleValue = AMFValue<double>;

// Template deduction guide to ensure string literals deduce
AMFValue(const char*) -> AMFValue<std::string>; // AMFStringValue

/**
* The AMFArrayValue object holds 2 types of lists:
* An associative list where a key maps to a value
Expand All @@ -106,7 +94,7 @@ using AMFDoubleValue = AMFValue<double>;
*/
class AMFArrayValue : public AMFBaseValue {
using AMFAssociative =
std::unordered_map<std::string, std::unique_ptr<AMFBaseValue>, GeneralUtils::transparent_string_hash, std::equal_to<>>;
std::unordered_map<std::string, std::unique_ptr<AMFBaseValue>, GeneralUtils::transparent_string_hash, std::equal_to<void>>;

using AMFDense = std::vector<std::unique_ptr<AMFBaseValue>>;

Expand Down Expand Up @@ -137,17 +125,20 @@ class AMFArrayValue : public AMFBaseValue {
* @return The inserted element if the type matched,
* or nullptr if a key existed and was not the same type
*/
template <typename ValueType>
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const std::string_view key, const ValueType value) {
template <typename T>
[[maybe_unused]] auto Insert(const std::string_view key, const T value) -> std::pair<decltype(AMFValue(value))*, bool> {
// This ensures the deduced type matches the AMFValue constructor
using AMFValueType = decltype(AMFValue(value));

const auto element = m_Associative.find(key);
AMFValue<ValueType>* val = nullptr;
AMFValueType* val = nullptr;
bool found = true;
if (element == m_Associative.cend()) {
auto newVal = std::make_unique<AMFValue<ValueType>>(value);
auto newVal = std::make_unique<AMFValueType>(value);
val = newVal.get();
m_Associative.emplace(key, std::move(newVal));
} else {
val = dynamic_cast<AMFValue<ValueType>*>(element->second.get());
val = dynamic_cast<AMFValueType*>(element->second.get());
found = false;
}
return std::make_pair(val, found);
Expand Down Expand Up @@ -190,15 +181,18 @@ class AMFArrayValue : public AMFBaseValue {
* @return The inserted element, or nullptr if the type did not match
* what was at the index.
*/
template <typename ValueType>
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const size_t index, const ValueType value) {
template <typename T>
[[maybe_unused]] auto Insert(const size_t index, const T value) -> std::pair<decltype(AMFValue(value))*, bool> {
// This ensures the deduced type matches the AMFValue constructor
using AMFValueType = decltype(AMFValue(value));

bool inserted = false;
if (index >= m_Dense.size()) {
m_Dense.resize(index + 1);
m_Dense.at(index) = std::make_unique<AMFValue<ValueType>>(value);
m_Dense.at(index) = std::make_unique<AMFValueType>(value);
inserted = true;
}
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(m_Dense.at(index).get()), inserted);
return std::make_pair(dynamic_cast<AMFValueType*>(m_Dense.at(index).get()), inserted);
}

/**
Expand Down Expand Up @@ -245,8 +239,8 @@ class AMFArrayValue : public AMFBaseValue {
*
* @return The inserted pointer, or nullptr should the key already be in use.
*/
template <typename ValueType>
[[maybe_unused]] inline AMFValue<ValueType>* Push(const ValueType value) {
template <typename T>
[[maybe_unused]] inline auto Push(const T value) -> decltype(AMFValue(value))* {
return Insert(m_Dense.size(), value).first;
}

Expand Down Expand Up @@ -356,4 +350,4 @@ class AMFArrayValue : public AMFBaseValue {
AMFDense m_Dense;
};

#endif //!__AMF3__H__
#endif //!AMF3_H
25 changes: 19 additions & 6 deletions dCommon/AmfSerialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,54 @@ void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value) {
this->Write(type);
switch (type) {
case eAmf::Integer: {
this->Write<AMFIntValue&>(*static_cast<AMFIntValue*>(&value));
this->Write<AMFIntValue&>(static_cast<AMFIntValue&>(value));
break;
}

case eAmf::Double: {
this->Write<AMFDoubleValue&>(*static_cast<AMFDoubleValue*>(&value));
this->Write<AMFDoubleValue&>(static_cast<AMFDoubleValue&>(value));
break;
}

case eAmf::String: {
this->Write<AMFStringValue&>(*static_cast<AMFStringValue*>(&value));
this->Write<AMFStringValue&>(static_cast<AMFStringValue&>(value));
break;
}

case eAmf::Array: {
this->Write<AMFArrayValue&>(*static_cast<AMFArrayValue*>(&value));
this->Write<AMFArrayValue&>(static_cast<AMFArrayValue&>(value));
break;
}
default: {
LOG("Encountered unwritable AMFType %i!", type);
[[fallthrough]];
}
case eAmf::Undefined:
[[fallthrough]];
case eAmf::Null:
[[fallthrough]];
case eAmf::False:
[[fallthrough]];
case eAmf::True:
[[fallthrough]];
case eAmf::Date:
[[fallthrough]];
case eAmf::Object:
[[fallthrough]];
case eAmf::XML:
[[fallthrough]];
case eAmf::XMLDoc:
[[fallthrough]];
case eAmf::ByteArray:
[[fallthrough]];
case eAmf::VectorInt:
[[fallthrough]];
case eAmf::VectorUInt:
[[fallthrough]];
case eAmf::VectorDouble:
[[fallthrough]];
case eAmf::VectorObject:
[[fallthrough]];
case eAmf::Dictionary:
break;
}
Expand Down Expand Up @@ -145,8 +159,7 @@ void RakNet::BitStream::Write<AMFIntValue&>(AMFIntValue& value) {
// Writes an AMFDoubleValue to BitStream
template<>
void RakNet::BitStream::Write<AMFDoubleValue&>(AMFDoubleValue& value) {
double d = value.GetValue();
WriteAMFU64(*this, *reinterpret_cast<uint64_t*>(&d));
WriteAMFU64(*this, std::bit_cast<uint64_t>(value.GetValue()));
}

// Writes an AMFStringValue to BitStream
Expand Down
2 changes: 1 addition & 1 deletion dCommon/BrickByBrickFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ void WriteSd0Magic(char* input, uint32_t chunkSize) {
input[2] = '0';
input[3] = 0x01;
input[4] = 0xFF;
*reinterpret_cast<uint32_t*>(input + 5) = chunkSize; // Write the integer to the character array
std::memcpy(&input[5], &chunkSize, sizeof(uint32_t)); // Write the integer to the character array
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one is a bit harder to test, but ill send you a test case for it

}

bool CheckSd0Magic(std::istream& streamToCheck) {
Expand Down
16 changes: 8 additions & 8 deletions dCommon/GeneralUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ bool static _IsSuffixChar(const uint8_t c) {
bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
const size_t rem = slice.length();
if (slice.empty()) return false;
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&slice.front());
const char* const bytes = slice.data();
if (rem > 0) {
const uint8_t first = bytes[0];
const uint8_t first = static_cast<uint8_t>(bytes[0]);
if (first < 0x80) { // 1 byte character
out = static_cast<uint32_t>(first & 0x7F);
slice.remove_prefix(1);
Expand All @@ -64,7 +64,7 @@ bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out
// middle byte, not valid at start, fall through
} else if (first < 0xE0) { // two byte character
if (rem > 1) {
const uint8_t second = bytes[1];
const uint8_t second = static_cast<uint8_t>(bytes[1]);
if (_IsSuffixChar(second)) {
out = (static_cast<uint32_t>(first & 0x1F) << 6)
+ static_cast<uint32_t>(second & 0x3F);
Expand All @@ -74,8 +74,8 @@ bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out
}
} else if (first < 0xF0) { // three byte character
if (rem > 2) {
const uint8_t second = bytes[1];
const uint8_t third = bytes[2];
const uint8_t second = static_cast<uint8_t>(bytes[1]);
const uint8_t third = static_cast<uint8_t>(bytes[2]);
if (_IsSuffixChar(second) && _IsSuffixChar(third)) {
out = (static_cast<uint32_t>(first & 0x0F) << 12)
+ (static_cast<uint32_t>(second & 0x3F) << 6)
Expand All @@ -86,9 +86,9 @@ bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out
}
} else if (first < 0xF8) { // four byte character
if (rem > 3) {
const uint8_t second = bytes[1];
const uint8_t third = bytes[2];
const uint8_t fourth = bytes[3];
const uint8_t second = static_cast<uint8_t>(bytes[1]);
const uint8_t third = static_cast<uint8_t>(bytes[2]);
const uint8_t fourth = static_cast<uint8_t>(bytes[3]);
if (_IsSuffixChar(second) && _IsSuffixChar(third) && _IsSuffixChar(fourth)) {
out = (static_cast<uint32_t>(first & 0x07) << 18)
+ (static_cast<uint32_t>(second & 0x3F) << 12)
Expand Down
23 changes: 22 additions & 1 deletion dCommon/GeneralUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// C++
#include <charconv>
#include <cstdint>
#include <cstring>
#include <ctime>
#include <functional>
#include <optional>
Expand Down Expand Up @@ -145,7 +146,7 @@ namespace GeneralUtils {
template <typename... Bases>
struct overload : Bases... {
using is_transparent = void;
using Bases::operator() ... ;
using Bases::operator()...;
};

struct char_pointer_hash {
Expand All @@ -160,6 +161,26 @@ namespace GeneralUtils {
char_pointer_hash
>;

/**
* A convenience wrapper around std::memcpy that creates a new object
* from the value representation of the provided bytes.Use when
* std::bit_cast is not applicable.
* @warning All restrictions of std::memcpy still apply. Accessing
* outside of the source object is undefined behavior.
* @param from The source of the value representation
* @returns A new object with the given value representation
*/
template <typename To, typename From>
[[nodiscard]]
inline To FromBitsUnchecked(const From* from) noexcept
requires (std::is_trivially_copyable_v<To>
&& std::is_trivially_copyable_v<From>)
{
To to{};
std::memcpy(&to, from, sizeof(To));
return to;
}

// Concept constraining to enum types
template <typename T>
concept Enum = std::is_enum_v<T>;
Expand Down
1 change: 1 addition & 0 deletions dCommon/dClient/AssetManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ struct AssetStream : std::istream {
}

operator bool() {
// NEED TO FIX THIS
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is changed make sure to check files existing and not existing with both packed and unpacked clients

return reinterpret_cast<AssetMemoryBuffer*>(rdbuf())->m_Success;
}
};
Expand Down
3 changes: 2 additions & 1 deletion dCommon/dEnums/MessageType/World.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

namespace MessageType {
enum class World : uint32_t {
VALIDATION = 1, // Session info
INVALID = 0,
VALIDATION, // Session info
CHARACTER_LIST_REQUEST,
CHARACTER_CREATE_REQUEST,
LOGIN_REQUEST, // Character selected
Expand Down
1 change: 1 addition & 0 deletions dScripts/02_server/Map/AG/NpcAgCourseStarter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ void NpcAgCourseStarter::OnFireEventServerSide(Entity* self, Entity* sender, std
LWOOBJID_EMPTY, "", senderSysAddr);
scriptedActivityComponent->RemoveActivityPlayerData(senderId);
} else if (args == "course_finish") {

const auto raceEndTime = Game::server->GetUptime();
const auto fRaceEndTime = std::chrono::duration<float, std::ratio<1>>(raceEndTime).count();
const auto raceTimeElapsed = fRaceEndTime - data->values[1];
Expand Down
4 changes: 3 additions & 1 deletion dWorldServer/WorldServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1386,7 +1386,9 @@ void HandlePacket(Packet* packet) {
}

default:
const auto messageId = *reinterpret_cast<MessageType::World*>(&packet->data[3]);
// Need to use FromBitsUnchecked (aka memcpy) instead of reinterpret_cast to avoid misaligned reads, which are UB
const auto messageId = GeneralUtils::FromBitsUnchecked<MessageType::World>(&packet->data[3]);

const std::string_view messageIdString = StringifiedEnum::ToString(messageId);
LOG("Unknown world packet received: %4i, %s", messageId, messageIdString.data());
}
Expand Down
4 changes: 2 additions & 2 deletions tests/dCommonTests/Amf3Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ TEST(dCommonTests, AMF3InsertionAssociativeTest) {
array.Insert<std::vector<uint32_t>>("Undefined", {});
array.Insert("Null", nullptr);

ASSERT_EQ(array.Get<const char*>("CString")->GetValueType(), eAmf::String);
ASSERT_EQ(array.Get("CString")->GetValueType(), eAmf::String);
ASSERT_EQ(array.Get<std::string>("String")->GetValueType(), eAmf::String);
ASSERT_EQ(array.Get<bool>("False")->GetValueType(), eAmf::False);
ASSERT_EQ(array.Get<bool>("True")->GetValueType(), eAmf::True);
Expand All @@ -95,7 +95,7 @@ TEST(dCommonTests, AMF3InsertionDenseTest) {
array.Push<std::vector<uint32_t>>({});

ASSERT_EQ(array.Get<std::string>(0)->GetValueType(), eAmf::String);
ASSERT_EQ(array.Get<const char*>(1)->GetValueType(), eAmf::String);
ASSERT_EQ(array.Get(1)->GetValueType(), eAmf::String);
ASSERT_EQ(array.Get<bool>(2)->GetValueType(), eAmf::False);
ASSERT_EQ(array.Get<bool>(3)->GetValueType(), eAmf::True);
ASSERT_EQ(array.Get<int32_t>(4)->GetValueType(), eAmf::Integer);
Expand Down
1 change: 1 addition & 0 deletions valgrind.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt build/gnu-debug/MasterServer