Compare commits

..

6 Commits

Author SHA1 Message Date
David Markowitz
b8ba79bb7b Fix FetchContent_Declare speed 2025-09-11 22:47:15 -07:00
HailStorm32
b798da8ef8 fix: Update mute expiry from database (#1871)
* Update mute expiry from database

* Address review comments

* Address review comment

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

---------

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2025-09-08 23:07:08 -07:00
Copilot
154112050f feat: Implement Minecraft-style execute command with relative positioning (#1864)
* Initial plan

* Implement Minecraft-style execute command

Co-authored-by: aronwk-aaron <26027722+aronwk-aaron@users.noreply.github.com>

* Add relative positioning support to execute command using ~ syntax

Co-authored-by: aronwk-aaron <26027722+aronwk-aaron@users.noreply.github.com>

* update the parsing and fix chat response

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: aronwk-aaron <26027722+aronwk-aaron@users.noreply.github.com>
Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com>
2025-09-08 22:35:18 -07:00
David Markowitz
6d3bf2fdc3 fix: need to create account twice due to commit latency?? (#1873)
idk fixes the issue
2025-09-08 22:50:22 -05:00
David Markowitz
566a18df38 Show git download progress with FetchContent (#1869)
Some downloads are slower and showing progress is better than sitting there doing nothing
2025-09-07 20:03:00 -05:00
David Markowitz
f6c13d9ee6 Replace Quaternion with glm math (#1868) 2025-09-06 19:18:03 -07:00
88 changed files with 429 additions and 582 deletions

View File

@@ -1,154 +0,0 @@
# Darkflame Universe Server Development Instructions
Darkflame Universe (DLU) is a LEGO Universe server emulator written in C++ with a multi-server architecture (AuthServer, ChatServer, MasterServer, WorldServer). Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
## Working Effectively
### Bootstrap, Build, and Test - REQUIRED STEPS
Execute these commands in order for ANY development work. NEVER CANCEL builds - they take time but work reliably:
```bash
# 1. Install system dependencies (Ubuntu/Debian)
sudo apt update && sudo apt install -y build-essential gcc zlib1g-dev libssl-dev openssl mariadb-server cmake
# 2. Initialize git submodules (CRITICAL - project won't build without this)
git submodule update --init --recursive
# 3. Build using the provided script
./build.sh -j2
```
- **Build time: ~6 minutes. NEVER CANCEL. Set timeout to 720+ seconds (12+ minutes).**
- **Uses CMake 3.25-3.31 (confirmed working with 3.31.6)**
- **Requires g++11+ (confirmed working with g++ 13.3.0)**
### Alternative Build Methods
```bash
# Using CMake presets (CI-style)
cmake --workflow --preset ci-ubuntu-22.04
# Manual CMake (for custom configurations)
mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE="Release" ..
cmake --build . --config Release -j2
```
- **Same timing: ~6 minutes. NEVER CANCEL. Set timeout to 720+ seconds.**
### Run Tests
```bash
cd build
ctest --output-on-failure
```
- **Test time: <4 seconds. Set timeout to 30+ seconds.**
- **91 tests run, all should pass**
- **Tests are built automatically when ENABLE_TESTING=1 in CMakeVariables.txt**
### Database Setup (for runtime testing)
```bash
# Start MariaDB
sudo systemctl start mysql
# Create test database and user
sudo mysql -e "CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'testpass'; GRANT ALL ON *.* TO 'testuser'@'localhost' WITH GRANT OPTION; FLUSH PRIVILEGES; CREATE DATABASE testdarkflame;"
```
## Validation
### Build Validation
- **ALWAYS run the bootstrapping steps first** before making any code changes
- **ALWAYS build and test your changes** before considering them complete
- Build output should include all server binaries: AuthServer, ChatServer, MasterServer, WorldServer
- Build directory contains required files: `*.ini` configs, `navmeshes/`, `migrations/`, `vanity/`, `blocklist.dcf`, `libmariadbcpp.so`
### Runtime Validation
The servers can be started for basic validation:
```bash
cd build
./MasterServer
```
- **Server will start but complain about missing client files (this is expected)**
- **Database connections work with proper configuration in sharedconfig.ini**
- **For full server testing, LEGO Universe client files are required (not available in this repository)**
### Code Validation
**ALWAYS validate your changes by**:
1. Building successfully with no new compilation errors
2. Running the test suite and confirming all tests pass
3. Starting MasterServer to verify basic functionality
4. **Use .editorconfig** - code style uses tabs (width=4), Unix line endings, trailing whitespace removal
## Common Tasks
### Project Structure
```
/home/runner/work/DarkflameServer/DarkflameServer/
├── dAuthServer/ # Authentication server code
├── dChatServer/ # Chat server code
├── dMasterServer/ # Master server (main coordinator)
├── dWorldServer/ # World/game server code
├── dCommon/ # Shared common utilities
├── dDatabase/ # Database abstraction layer
├── dGame/ # Core game logic, components, behaviors
├── dScripts/ # Game scripts (NPCs, quests, etc.)
├── dNet/ # Network utilities
├── dPhysics/ # Physics integration
├── tests/ # Unit tests (GoogleTest)
├── migrations/ # Database schema migrations
├── thirdparty/ # External dependencies
├── build.sh # Main build script
├── CMakeVariables.txt # Build configuration variables
└── CMakePresets.json # CMake preset configurations
```
### Key Files to Know
- **CMakeVariables.txt**: Build configuration (testing enabled, MariaDB jobs, etc.)
- **build/sharedconfig.ini**: Database connection, client location, server settings
- **build/masterconfig.ini**: Master server port and startup configuration
- **CONTRIBUTING.md**: Code style guidelines and commit message format
- **docs/Commands.md**: Complete list of in-game server commands
### Build Configuration
Located in `CMakeVariables.txt`:
- `ENABLE_TESTING=1` - Unit tests enabled (keep enabled)
- `MARIADB_CONNECTOR_COMPILE_JOBS=1` - Parallel compilation jobs for MariaDB connector
- `CDCLIENT_CACHE_ALL=0` - Database caching strategy
### Common Commands Reference
```bash
# Build from clean state
rm -rf build && ./build.sh -j2
# Run specific test
cd build && ctest -R "TestName" --output-on-failure
# Check which servers were built
cd build && ls -la *Server
# View build configuration
cat CMakeVariables.txt
# Check git submodules status
git submodule status
```
### Important Notes
- **Client files are NOT included** - this is only the server emulator
- **Database can use SQLite or MariaDB** - SQLite recommended for development since it's lighter and doesn't require an external service
- **Multi-server architecture** requires all 4 servers to run a complete setup
- **Network ports**: Auth (1001), Chat (2005), Master (2000), World (3000+)
- **Development uses Debug builds**, production uses Release builds
- **GM level 0** = normal player, **GM level 8-9** = admin privileges
## Troubleshooting
- **"Asset bundle not found"**: Expected without LEGO Universe client files
- **"Submodule errors"**: Run `git submodule update --init --recursive`
- **"CMake version errors"**: Requires CMake 3.25-3.31
- **"MariaDB connection errors"**: Check database setup and sharedconfig.ini
- **"Permission denied on port"**: Run `sudo setcap 'cap_net_bind_service=+ep' AuthServer` for ports <1024
### CI Information
- **GitHub Actions** runs builds on Windows, Ubuntu, and macOS
- **Build matrix** tests multiple configurations via CMake presets
- **All tests must pass** for CI to succeed
- **Build artifacts** are automatically generated and uploaded
**Remember: This is a complex game server requiring LEGO Universe client files for full functionality, but the server has the capability to mock everything that's needed to test without the client since cdclient can be mocked and the database can be mocked as well.**

View File

@@ -19,6 +19,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debuggi
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects 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 ON) # Set C and C++ symbol visibility to hide inlined functions
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
set(FETCHCONTENT_QUIET FALSE) # GLM takes a long time to clone, this will at least show _something_ while its downloading
# Read variables from file # Read variables from file
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables) FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
@@ -306,7 +307,7 @@ add_subdirectory(dServer)
add_subdirectory(dWeb) add_subdirectory(dWeb)
# Create a list of common libraries shared between all binaries # Create a list of common libraries shared between all binaries
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "magic_enum") set(COMMON_LIBRARIES glm::glm "dCommon" "dDatabase" "dNet" "raknet" "magic_enum")
# Add platform specific common libraries # Add platform specific common libraries
if(UNIX) if(UNIX)

View File

@@ -6,6 +6,8 @@ FetchContent_Declare(
googletest googletest
GIT_REPOSITORY https://github.com/google/googletest.git GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.12.1 GIT_TAG release-1.12.1
GIT_PROGRESS TRUE
GIT_SHALLOW 1
) )
# For Windows: Prevent overriding the parent project's compiler/linker settings # For Windows: Prevent overriding the parent project's compiler/linker settings

View File

@@ -1,4 +1,4 @@
set(DCHATFILTER_SOURCES "dChatFilter.cpp") set(DCHATFILTER_SOURCES "dChatFilter.cpp")
add_library(dChatFilter STATIC ${DCHATFILTER_SOURCES}) add_library(dChatFilter STATIC ${DCHATFILTER_SOURCES})
target_link_libraries(dChatFilter dDatabase) target_link_libraries(dChatFilter dDatabase glm::glm)

View File

@@ -14,6 +14,6 @@ add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}
add_library(dChatServer ${DCHATSERVER_SOURCES}) add_library(dChatServer ${DCHATSERVER_SOURCES})
target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer" "${PROJECT_SOURCE_DIR}/dChatFilter") target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer" "${PROJECT_SOURCE_DIR}/dChatFilter")
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter) target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter glm::glm)
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer mongoose dWeb) target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer mongoose dWeb)

View File

@@ -54,6 +54,8 @@ elseif (WIN32)
zlib zlib
URL https://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip URL https://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip
URL_HASH MD5=9d6a627693163bbbf3f26403a3a0b0b1 URL_HASH MD5=9d6a627693163bbbf3f26403a3a0b0b1
GIT_PROGRESS TRUE
GIT_SHALLOW 1
) )
# Disable warning about no project version. # Disable warning about no project version.
@@ -74,5 +76,6 @@ else ()
endif () endif ()
target_link_libraries(dCommon target_link_libraries(dCommon
PUBLIC glm::glm
PRIVATE ZLIB::ZLIB bcrypt tinyxml2 PRIVATE ZLIB::ZLIB bcrypt tinyxml2
INTERFACE dDatabase) INTERFACE dDatabase)

View File

@@ -20,6 +20,8 @@
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "Logger.h"
#include <glm/ext/vector_float3.hpp>
enum eInventoryType : uint32_t; enum eInventoryType : uint32_t;
enum class eObjectBits : size_t; enum class eObjectBits : size_t;
enum class eReplicaComponentType : uint32_t; enum class eReplicaComponentType : uint32_t;
@@ -244,7 +246,7 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters * @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
*/ */
template <typename T> template <typename T>
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ) { [[nodiscard]] std::optional<T> TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ) {
const auto x = TryParse<float>(strX); const auto x = TryParse<float>(strX);
if (!x) return std::nullopt; if (!x) return std::nullopt;
@@ -252,7 +254,7 @@ namespace GeneralUtils {
if (!y) return std::nullopt; if (!y) return std::nullopt;
const auto z = TryParse<float>(strZ); const auto z = TryParse<float>(strZ);
return z ? std::make_optional<NiPoint3>(x.value(), y.value(), z.value()) : std::nullopt; return z ? std::make_optional<T>(x.value(), y.value(), z.value()) : std::nullopt;
} }
/** /**
@@ -261,8 +263,8 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters * @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
*/ */
template <typename T> template <typename T>
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::span<const std::string> str) { [[nodiscard]] std::optional<T> TryParse(const std::span<const std::string> str) {
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt; return (str.size() == 3) ? TryParse<T>(str[0], str[1], str[2]) : std::nullopt;
} }
template <typename T> template <typename T>

View File

@@ -6,10 +6,14 @@
\brief Defines a point in space in XYZ coordinates \brief Defines a point in space in XYZ coordinates
*/ */
class NiPoint3; class NiPoint3;
class NiQuaternion;
typedef NiPoint3 Vector3; //!< The Vector3 class is technically the NiPoint3 class, but typedef'd for clarity in some cases typedef NiPoint3 Vector3; //!< The Vector3 class is technically the NiPoint3 class, but typedef'd for clarity in some cases
#include <glm/ext/vector_float3.hpp>
#include "NiQuaternion.h"
//! A custom class the defines a point in space //! A custom class the defines a point in space
class NiPoint3 { class NiPoint3 {
public: public:
@@ -21,6 +25,12 @@ public:
//! Initializer //! Initializer
constexpr NiPoint3() = default; constexpr NiPoint3() = default;
constexpr NiPoint3(const glm::vec3& vec) noexcept
: x{ vec.x }
, y{ vec.y }
, z{ vec.z } {
}
//! Initializer //! Initializer
/*! /*!
\param x The x coordinate \param x The x coordinate

View File

@@ -4,6 +4,7 @@
#endif #endif
#include "NiQuaternion.h" #include "NiQuaternion.h"
#include <glm/ext/quaternion_float.hpp>
// MARK: Getters / Setters // MARK: Getters / Setters

View File

@@ -3,37 +3,18 @@
// C++ // C++
#include <cmath> #include <cmath>
#include <glm/gtx/quaternion.hpp>
// MARK: Member Functions // MARK: Member Functions
Vector3 NiQuaternion::GetEulerAngles() const { Vector3 QuatUtils::Euler(const NiQuaternion& quat) {
Vector3 angles; return glm::eulerAngles(quat);
// roll (x-axis rotation)
const float sinr_cosp = 2 * (w * x + y * z);
const float cosr_cosp = 1 - 2 * (x * x + y * y);
angles.x = std::atan2(sinr_cosp, cosr_cosp);
// pitch (y-axis rotation)
const float sinp = 2 * (w * y - z * x);
if (std::abs(sinp) >= 1) {
angles.y = std::copysign(3.14 / 2, sinp); // use 90 degrees if out of range
} else {
angles.y = std::asin(sinp);
}
// yaw (z-axis rotation)
const float siny_cosp = 2 * (w * z + x * y);
const float cosy_cosp = 1 - 2 * (y * y + z * z);
angles.z = std::atan2(siny_cosp, cosy_cosp);
return angles;
} }
// MARK: Helper Functions // MARK: Helper Functions
//! Look from a specific point in space to another point in space (Y-locked) //! Look from a specific point in space to another point in space (Y-locked)
NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint) { NiQuaternion QuatUtils::LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
//To make sure we don't orient around the X/Z axis: //To make sure we don't orient around the X/Z axis:
NiPoint3 source = sourcePoint; NiPoint3 source = sourcePoint;
NiPoint3 dest = destPoint; NiPoint3 dest = destPoint;
@@ -51,11 +32,11 @@ NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& d
NiPoint3 vecB = vecA.CrossProduct(posZ); NiPoint3 vecB = vecA.CrossProduct(posZ);
if (vecB.DotProduct(forwardVector) < 0) rotAngle = -rotAngle; if (vecB.DotProduct(forwardVector) < 0) rotAngle = -rotAngle;
return NiQuaternion::CreateFromAxisAngle(vecA, rotAngle); return glm::angleAxis(rotAngle, glm::vec3{vecA.x, vecA.y, vecA.z});
} }
//! Look from a specific point in space to another point in space //! Look from a specific point in space to another point in space
NiQuaternion NiQuaternion::LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint) { NiQuaternion QuatUtils::LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
NiPoint3 forwardVector = NiPoint3(destPoint - sourcePoint).Unitize(); NiPoint3 forwardVector = NiPoint3(destPoint - sourcePoint).Unitize();
NiPoint3 posZ = NiPoint3Constant::UNIT_Z; NiPoint3 posZ = NiPoint3Constant::UNIT_Z;
@@ -67,37 +48,26 @@ NiQuaternion NiQuaternion::LookAtUnlocked(const NiPoint3& sourcePoint, const NiP
NiPoint3 vecB = vecA.CrossProduct(posZ); NiPoint3 vecB = vecA.CrossProduct(posZ);
if (vecB.DotProduct(forwardVector) < 0) rotAngle = -rotAngle; if (vecB.DotProduct(forwardVector) < 0) rotAngle = -rotAngle;
return NiQuaternion::CreateFromAxisAngle(vecA, rotAngle); return glm::angleAxis(rotAngle, glm::vec3{vecA.x, vecA.y, vecA.z});
} }
//! Creates a Quaternion from a specific axis and angle relative to that axis //! Creates a Quaternion from a specific axis and angle relative to that axis
NiQuaternion NiQuaternion::CreateFromAxisAngle(const Vector3& axis, float angle) { NiQuaternion QuatUtils::AxisAngle(const Vector3& axis, float angle) {
float halfAngle = angle * 0.5f; return glm::angleAxis(angle, glm::vec3(axis.x, axis.y, axis.z));
float s = static_cast<float>(sin(halfAngle));
NiQuaternion q;
q.x = axis.GetX() * s;
q.y = axis.GetY() * s;
q.z = axis.GetZ() * s;
q.w = static_cast<float>(cos(halfAngle));
return q;
} }
NiQuaternion NiQuaternion::FromEulerAngles(const NiPoint3& eulerAngles) { NiQuaternion QuatUtils::FromEuler(const NiPoint3& eulerAngles) {
// Abbreviations for the various angular functions return glm::quat(glm::vec3(eulerAngles.x, eulerAngles.y, eulerAngles.z));
float cy = cos(eulerAngles.z * 0.5); }
float sy = sin(eulerAngles.z * 0.5);
float cp = cos(eulerAngles.y * 0.5); Vector3 QuatUtils::Forward(const NiQuaternion& quat) {
float sp = sin(eulerAngles.y * 0.5); return quat * glm::vec3(0, 0, 1);
float cr = cos(eulerAngles.x * 0.5); }
float sr = sin(eulerAngles.x * 0.5);
Vector3 QuatUtils::Up(const NiQuaternion& quat) {
NiQuaternion q; return quat * glm::vec3(0, 1, 0);
q.w = cr * cp * cy + sr * sp * sy; }
q.x = sr * cp * cy - cr * sp * sy;
q.y = cr * sp * cy + sr * cp * sy; Vector3 QuatUtils::Right(const NiQuaternion& quat) {
q.z = cr * cp * sy - sr * sp * cy; return quat * glm::vec3(1, 0, 0);
return q;
} }

View File

@@ -1,158 +1,27 @@
#ifndef __NIQUATERNION_H__ #ifndef NIQUATERNION_H
#define __NIQUATERNION_H__ #define NIQUATERNION_H
// Custom Classes // Custom Classes
#include "NiPoint3.h" #include "NiPoint3.h"
/*! #define GLM_FORCE_QUAT_DATA_WXYZ
\file NiQuaternion.hpp
\brief Defines a quaternion in space in WXYZ coordinates
*/
class NiQuaternion; #include <glm/ext/quaternion_float.hpp>
typedef NiQuaternion Quaternion; //!< A typedef for a shorthand version of NiQuaternion
//! A class that defines a rotation in space using Quaternion = glm::quat;
class NiQuaternion { using NiQuaternion = Quaternion;
public:
float w{ 1 }; //!< The w coordinate
float x{ 0 }; //!< The x coordinate
float y{ 0 }; //!< The y coordinate
float z{ 0 }; //!< The z coordinate
namespace QuatUtils {
//! The initializer constexpr NiQuaternion IDENTITY = glm::identity<NiQuaternion>();
constexpr NiQuaternion() = default; Vector3 Forward(const NiQuaternion& quat);
Vector3 Up(const NiQuaternion& quat);
//! The initializer Vector3 Right(const NiQuaternion& quat);
/*! NiQuaternion LookAt(const NiPoint3& from, const NiPoint3& to);
\param w The w coordinate NiQuaternion LookAtUnlocked(const NiPoint3& from, const NiPoint3& to);
\param x The x coordinate Vector3 Euler(const NiQuaternion& quat);
\param y The y coordinate NiQuaternion AxisAngle(const Vector3& axis, float angle);
\param z The z coordinate NiQuaternion FromEuler(const NiPoint3& eulerAngles);
*/ constexpr float PI_OVER_180 = glm::pi<float>() / 180.0f;
constexpr NiQuaternion(const float w, const float x, const float y, const float z) noexcept
: w{ w }
, x{ x }
, y{ y }
, z{ z } {
}
// MARK: Setters / Getters
//! Gets the W coordinate
/*!
\return The w coordinate
*/
[[nodiscard]] constexpr float GetW() const noexcept;
//! Sets the W coordinate
/*!
\param w The w coordinate
*/
constexpr void SetW(const float w) noexcept;
//! Gets the X coordinate
/*!
\return The x coordinate
*/
[[nodiscard]] constexpr float GetX() const noexcept;
//! Sets the X coordinate
/*!
\param x The x coordinate
*/
constexpr void SetX(const float x) noexcept;
//! Gets the Y coordinate
/*!
\return The y coordinate
*/
[[nodiscard]] constexpr float GetY() const noexcept;
//! Sets the Y coordinate
/*!
\param y The y coordinate
*/
constexpr void SetY(const float y) noexcept;
//! Gets the Z coordinate
/*!
\return The z coordinate
*/
[[nodiscard]] constexpr float GetZ() const noexcept;
//! Sets the Z coordinate
/*!
\param z The z coordinate
*/
constexpr void SetZ(const float z) noexcept;
// MARK: Member Functions
//! Returns the forward vector from the quaternion
/*!
\return The forward vector of the quaternion
*/
[[nodiscard]] constexpr Vector3 GetForwardVector() const noexcept;
//! Returns the up vector from the quaternion
/*!
\return The up vector fo the quaternion
*/
[[nodiscard]] constexpr Vector3 GetUpVector() const noexcept;
//! Returns the right vector from the quaternion
/*!
\return The right vector of the quaternion
*/
[[nodiscard]] constexpr Vector3 GetRightVector() const noexcept;
[[nodiscard]] Vector3 GetEulerAngles() const;
// MARK: Operators
//! Operator to check for equality
constexpr bool operator==(const NiQuaternion& rot) const noexcept;
//! Operator to check for inequality
constexpr bool operator!=(const NiQuaternion& rot) const noexcept;
// MARK: Helper Functions
//! Look from a specific point in space to another point in space (Y-locked)
/*!
\param sourcePoint The source location
\param destPoint The destination location
\return The Quaternion with the rotation towards the destination
*/
[[nodiscard]] static NiQuaternion LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint);
//! Look from a specific point in space to another point in space
/*!
\param sourcePoint The source location
\param destPoint The destination location
\return The Quaternion with the rotation towards the destination
*/
[[nodiscard]] static NiQuaternion LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint);
//! Creates a Quaternion from a specific axis and angle relative to that axis
/*!
\param axis The axis that is used
\param angle The angle relative to this axis
\return A quaternion created from the axis and angle
*/
[[nodiscard]] static NiQuaternion CreateFromAxisAngle(const Vector3& axis, float angle);
[[nodiscard]] static NiQuaternion FromEulerAngles(const NiPoint3& eulerAngles);
}; };
// Static Variables #endif // !NIQUATERNION_H
namespace NiQuaternionConstant {
constexpr NiQuaternion IDENTITY(1, 0, 0, 0);
}
// Include constexpr and inline function definitions in a seperate file for readability
#include "NiQuaternion.inl"
#endif // !__NIQUATERNION_H__

View File

@@ -1,75 +0,0 @@
#pragma once
#ifndef __NIQUATERNION_H__
#error "This should only be included inline in NiQuaternion.h: Do not include directly!"
#endif
// MARK: Setters / Getters
//! Gets the W coordinate
constexpr float NiQuaternion::GetW() const noexcept {
return this->w;
}
//! Sets the W coordinate
constexpr void NiQuaternion::SetW(const float w) noexcept {
this->w = w;
}
//! Gets the X coordinate
constexpr float NiQuaternion::GetX() const noexcept {
return this->x;
}
//! Sets the X coordinate
constexpr void NiQuaternion::SetX(const float x) noexcept {
this->x = x;
}
//! Gets the Y coordinate
constexpr float NiQuaternion::GetY() const noexcept {
return this->y;
}
//! Sets the Y coordinate
constexpr void NiQuaternion::SetY(const float y) noexcept {
this->y = y;
}
//! Gets the Z coordinate
constexpr float NiQuaternion::GetZ() const noexcept {
return this->z;
}
//! Sets the Z coordinate
constexpr void NiQuaternion::SetZ(const float z) noexcept {
this->z = z;
}
// MARK: Member Functions
//! Returns the forward vector from the quaternion
constexpr Vector3 NiQuaternion::GetForwardVector() const noexcept {
return Vector3(2 * (x * z + w * y), 2 * (y * z - w * x), 1 - 2 * (x * x + y * y));
}
//! Returns the up vector from the quaternion
constexpr Vector3 NiQuaternion::GetUpVector() const noexcept {
return Vector3(2 * (x * y - w * z), 1 - 2 * (x * x + z * z), 2 * (y * z + w * x));
}
//! Returns the right vector from the quaternion
constexpr Vector3 NiQuaternion::GetRightVector() const noexcept {
return Vector3(1 - 2 * (y * y + z * z), 2 * (x * y + w * z), 2 * (x * z - w * y));
}
// MARK: Operators
//! Operator to check for equality
constexpr bool NiQuaternion::operator==(const NiQuaternion& rot) const noexcept {
return rot.x == this->x && rot.y == this->y && rot.z == this->z && rot.w == this->w;
}
//! Operator to check for inequality
constexpr bool NiQuaternion::operator!=(const NiQuaternion& rot) const noexcept {
return !(*this == rot);
}

View File

@@ -24,7 +24,7 @@ struct LocalSpaceInfo {
struct PositionUpdate { struct PositionUpdate {
NiPoint3 position = NiPoint3Constant::ZERO; NiPoint3 position = NiPoint3Constant::ZERO;
NiQuaternion rotation = NiQuaternionConstant::IDENTITY; NiQuaternion rotation = QuatUtils::IDENTITY;
bool onGround = false; bool onGround = false;
bool onRail = false; bool onRail = false;
NiPoint3 velocity = NiPoint3Constant::ZERO; NiPoint3 velocity = NiPoint3Constant::ZERO;

View File

@@ -15,7 +15,7 @@ target_include_directories(dDatabaseCDClient PUBLIC "."
"${PROJECT_SOURCE_DIR}/dCommon" "${PROJECT_SOURCE_DIR}/dCommon"
"${PROJECT_SOURCE_DIR}/dCommon/dEnums" "${PROJECT_SOURCE_DIR}/dCommon/dEnums"
) )
target_link_libraries(dDatabaseCDClient PRIVATE sqlite3) target_link_libraries(dDatabaseCDClient PRIVATE sqlite3 glm::glm)
if (${CDCLIENT_CACHE_ALL}) if (${CDCLIENT_CACHE_ALL})
add_compile_definitions(dDatabaseCDClient PRIVATE CDCLIENT_CACHE_ALL=${CDCLIENT_CACHE_ALL}) add_compile_definitions(dDatabaseCDClient PRIVATE CDCLIENT_CACHE_ALL=${CDCLIENT_CACHE_ALL})

View File

@@ -10,4 +10,5 @@ add_dependencies(dDatabase conncpp_dylib)
target_include_directories(dDatabase PUBLIC ".") target_include_directories(dDatabase PUBLIC ".")
target_link_libraries(dDatabase target_link_libraries(dDatabase
PUBLIC dDatabaseCDClient dDatabaseGame) PUBLIC dDatabaseCDClient dDatabaseGame
PRIVATE glm::glm)

View File

@@ -29,7 +29,7 @@ target_include_directories(dDatabaseGame PUBLIC "."
target_link_libraries(dDatabaseGame target_link_libraries(dDatabaseGame
INTERFACE dCommon INTERFACE dCommon
PRIVATE sqlite3 MariaDB::ConnCpp) PRIVATE sqlite3 MariaDB::ConnCpp glm::glm)
# Glob together all headers that need to be precompiled # Glob together all headers that need to be precompiled
file( file(

View File

@@ -14,6 +14,7 @@ public:
std::string bcryptPassword; std::string bcryptPassword;
uint32_t id{}; uint32_t id{};
uint32_t playKeyId{}; uint32_t playKeyId{};
uint64_t muteExpire{};
bool banned{}; bool banned{};
bool locked{}; bool locked{};
eGameMasterLevel maxGmLevel{}; eGameMasterLevel maxGmLevel{};

View File

@@ -13,7 +13,7 @@ public:
} }
NiPoint3 position; NiPoint3 position;
NiQuaternion rotation; NiQuaternion rotation = QuatUtils::IDENTITY;
LWOOBJID id{}; LWOOBJID id{};
LOT lot{}; LOT lot{};
uint32_t ugcId{}; uint32_t ugcId{};

View File

@@ -3,7 +3,7 @@
#include "eGameMasterLevel.h" #include "eGameMasterLevel.h"
std::optional<IAccounts::Info> MySQLDatabase::GetAccountInfo(const std::string_view username) { std::optional<IAccounts::Info> MySQLDatabase::GetAccountInfo(const std::string_view username) {
auto result = ExecuteSelect("SELECT id, password, banned, locked, play_key_id, gm_level FROM accounts WHERE name = ? LIMIT 1;", username); auto result = ExecuteSelect("SELECT id, password, banned, locked, play_key_id, gm_level, mute_expire FROM accounts WHERE name = ? LIMIT 1;", username);
if (!result->next()) { if (!result->next()) {
return std::nullopt; return std::nullopt;
@@ -16,6 +16,7 @@ std::optional<IAccounts::Info> MySQLDatabase::GetAccountInfo(const std::string_v
toReturn.banned = result->getBoolean("banned"); toReturn.banned = result->getBoolean("banned");
toReturn.locked = result->getBoolean("locked"); toReturn.locked = result->getBoolean("locked");
toReturn.playKeyId = result->getUInt("play_key_id"); toReturn.playKeyId = result->getUInt("play_key_id");
toReturn.muteExpire = result->getUInt64("mute_expire");
return toReturn; return toReturn;
} }

View File

@@ -17,6 +17,7 @@ std::optional<IAccounts::Info> SQLiteDatabase::GetAccountInfo(const std::string_
toReturn.banned = result.getIntField("banned"); toReturn.banned = result.getIntField("banned");
toReturn.locked = result.getIntField("locked"); toReturn.locked = result.getIntField("locked");
toReturn.playKeyId = result.getIntField("play_key_id"); toReturn.playKeyId = result.getIntField("play_key_id");
toReturn.muteExpire = static_cast<uint64_t>(result.getInt64Field("mute_expire"));
return toReturn; return toReturn;
} }

View File

@@ -654,7 +654,7 @@ private:
/** /**
* The spawn rotation of this character when loading in * The spawn rotation of this character when loading in
*/ */
NiQuaternion m_OriginalRotation; NiQuaternion m_OriginalRotation = QuatUtils::IDENTITY;
/** /**
* The respawn points of this character, per world * The respawn points of this character, per world

View File

@@ -304,7 +304,7 @@ void Entity::Initialize() {
//If we came from another zone, put us in the starting loc //If we came from another zone, put us in the starting loc
if (m_Character->GetZoneID() != Game::server->GetZoneID() || mapID == 1603) { // Exception for Moon Base as you tend to spawn on the roof. if (m_Character->GetZoneID() != Game::server->GetZoneID() || mapID == 1603) { // Exception for Moon Base as you tend to spawn on the roof.
NiPoint3 pos; NiPoint3 pos;
NiQuaternion rot; NiQuaternion rot = QuatUtils::IDENTITY;
const auto& targetSceneName = m_Character->GetTargetScene(); const auto& targetSceneName = m_Character->GetTargetScene();
auto* targetScene = Game::entityManager->GetSpawnPointEntity(targetSceneName); auto* targetScene = Game::entityManager->GetSpawnPointEntity(targetSceneName);
@@ -1882,7 +1882,7 @@ const NiQuaternion& Entity::GetRotation() const {
return rigidBodyPhantomPhysicsComponent->GetRotation(); return rigidBodyPhantomPhysicsComponent->GetRotation();
} }
return NiQuaternionConstant::IDENTITY; return QuatUtils::IDENTITY;
} }
void Entity::SetPosition(const NiPoint3& position) { void Entity::SetPosition(const NiPoint3& position) {
@@ -2178,7 +2178,7 @@ const NiPoint3& Entity::GetRespawnPosition() const {
const NiQuaternion& Entity::GetRespawnRotation() const { const NiQuaternion& Entity::GetRespawnRotation() const {
auto* characterComponent = GetComponent<CharacterComponent>(); auto* characterComponent = GetComponent<CharacterComponent>();
return characterComponent ? characterComponent->GetRespawnRotation() : NiQuaternionConstant::IDENTITY; return characterComponent ? characterComponent->GetRespawnRotation() : QuatUtils::IDENTITY;
} }
void Entity::SetRespawnPos(const NiPoint3& position) const { void Entity::SetRespawnPos(const NiPoint3& position) const {

View File

@@ -357,7 +357,7 @@ private:
std::vector<LDFBaseData*> m_NetworkSettings; std::vector<LDFBaseData*> m_NetworkSettings;
NiPoint3 m_DefaultPosition; NiPoint3 m_DefaultPosition;
NiQuaternion m_DefaultRotation; NiQuaternion m_DefaultRotation = QuatUtils::IDENTITY;
float m_Scale; float m_Scale;
Spawner* m_Spawner; Spawner* m_Spawner;

View File

@@ -7,6 +7,10 @@
#include "dZoneManager.h" #include "dZoneManager.h"
#include "eServerDisconnectIdentifiers.h" #include "eServerDisconnectIdentifiers.h"
#include "eGameMasterLevel.h" #include "eGameMasterLevel.h"
#include "BitStreamUtils.h"
#include "MessageType/Chat.h"
#include <chrono>
#include <ctime>
User::User(const SystemAddress& sysAddr, const std::string& username, const std::string& sessionKey) { User::User(const SystemAddress& sysAddr, const std::string& username, const std::string& sessionKey) {
m_AccountID = 0; m_AccountID = 0;
@@ -28,7 +32,7 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std:
if (userInfo) { if (userInfo) {
m_AccountID = userInfo->id; m_AccountID = userInfo->id;
m_MaxGMLevel = userInfo->maxGmLevel; m_MaxGMLevel = userInfo->maxGmLevel;
m_MuteExpire = 0; //res->getUInt64(3); m_MuteExpire = userInfo->muteExpire;
} }
//If we're loading a zone, we'll load the last used (aka current) character: //If we're loading a zone, we'll load the last used (aka current) character:
@@ -91,8 +95,28 @@ Character* User::GetLastUsedChar() {
} }
} }
bool User::GetIsMuted() const { bool User::GetIsMuted() {
return m_MuteExpire == 1 || m_MuteExpire > time(NULL); using namespace std::chrono;
constexpr auto refreshInterval = seconds{ 60 };
const auto now = steady_clock::now();
if (now - m_LastMuteCheck >= refreshInterval) {
m_LastMuteCheck = now;
if (const auto info = Database::Get()->GetAccountInfo(m_Username)) {
const auto expire = static_cast<time_t>(info->muteExpire);
if (expire != m_MuteExpire) {
m_MuteExpire = expire;
if (Game::chatServer && m_LoggedInCharID != 0) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, ServiceType::CHAT, MessageType::Chat::GM_MUTE);
bitStream.Write(m_LoggedInCharID);
bitStream.Write(m_MuteExpire);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
}
}
}
}
return m_MuteExpire == 1 || m_MuteExpire > std::time(nullptr);
} }
time_t User::GetMuteExpire() const { time_t User::GetMuteExpire() const {

View File

@@ -3,6 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <chrono>
#include "RakNetTypes.h" #include "RakNetTypes.h"
#include "dCommonVars.h" #include "dCommonVars.h"
@@ -46,7 +47,7 @@ public:
const std::unordered_map<std::string, bool>& GetIsBestFriendMap() { return m_IsBestFriendMap; } const std::unordered_map<std::string, bool>& GetIsBestFriendMap() { return m_IsBestFriendMap; }
void UpdateBestFriendValue(const std::string_view playerName, const bool newValue); void UpdateBestFriendValue(const std::string_view playerName, const bool newValue);
bool GetIsMuted() const; bool GetIsMuted();
time_t GetMuteExpire() const; time_t GetMuteExpire() const;
void SetMuteExpire(time_t value); void SetMuteExpire(time_t value);
@@ -72,7 +73,8 @@ private:
bool m_LastChatMessageApproved = false; bool m_LastChatMessageApproved = false;
int m_AmountOfTimesOutOfSync = 0; int m_AmountOfTimesOutOfSync = 0;
const int m_MaxDesyncAllowed = 12; const int m_MaxDesyncAllowed = 12;
time_t m_MuteExpire; uint64_t m_MuteExpire;
std::chrono::steady_clock::time_point m_LastMuteCheck{};
}; };
#endif // USER_H #endif // USER_H

View File

@@ -3,6 +3,8 @@
#include "BehaviorContext.h" #include "BehaviorContext.h"
#include "EntityManager.h" #include "EntityManager.h"
#include <glm/gtc/quaternion.hpp>
void ChangeOrientationBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { void ChangeOrientationBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
Entity* sourceEntity; Entity* sourceEntity;
if (this->m_orientCaster) sourceEntity = Game::entityManager->GetEntity(context->originator); if (this->m_orientCaster) sourceEntity = Game::entityManager->GetEntity(context->originator);
@@ -16,12 +18,12 @@ void ChangeOrientationBehavior::Calculate(BehaviorContext* context, RakNet::BitS
if (!destinationEntity) return; if (!destinationEntity) return;
sourceEntity->SetRotation( sourceEntity->SetRotation(
NiQuaternion::LookAt(sourceEntity->GetPosition(), destinationEntity->GetPosition()) QuatUtils::LookAt(sourceEntity->GetPosition(), destinationEntity->GetPosition())
); );
} else if (this->m_toAngle){ } else if (this->m_toAngle){
auto baseAngle = NiPoint3(0, 0, this->m_angle); auto baseAngle = NiPoint3(0, 0, this->m_angle);
if (this->m_relative) baseAngle += sourceEntity->GetRotation().GetForwardVector(); if (this->m_relative) baseAngle += QuatUtils::Forward(sourceEntity->GetRotation());
sourceEntity->SetRotation(NiQuaternion::FromEulerAngles(baseAngle)); sourceEntity->SetRotation(glm::quat(glm::vec3(baseAngle.x, baseAngle.y, baseAngle.z)));
} else return; } else return;
Game::entityManager->SerializeEntity(sourceEntity); Game::entityManager->SerializeEntity(sourceEntity);
return; return;

View File

@@ -48,7 +48,7 @@ void ForceMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStrea
if (controllablePhysicsComponent != nullptr) { if (controllablePhysicsComponent != nullptr) {
if (m_Forward == 1) { if (m_Forward == 1) {
controllablePhysicsComponent->SetVelocity(controllablePhysicsComponent->GetRotation().GetForwardVector() * 25); controllablePhysicsComponent->SetVelocity(QuatUtils::Forward(controllablePhysicsComponent->GetRotation()) * 25);
} }
Game::entityManager->SerializeEntity(casterEntity); Game::entityManager->SerializeEntity(casterEntity);

View File

@@ -92,7 +92,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt
const auto time = distance / this->m_projectileSpeed; const auto time = distance / this->m_projectileSpeed;
const auto rotation = NiQuaternion::LookAtUnlocked(position, other->GetPosition()); const auto rotation = QuatUtils::LookAtUnlocked(position, other->GetPosition());
const auto targetPosition = other->GetPosition(); const auto targetPosition = other->GetPosition();
@@ -112,13 +112,13 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt
bitStream.Write(id); bitStream.Write(id);
auto eulerAngles = rotation.GetEulerAngles(); auto eulerAngles = QuatUtils::Euler(rotation);
eulerAngles.y += angle * (3.14 / 180); eulerAngles.y += angle * (glm::pi<float>() / 180.0f);
const auto angledRotation = NiQuaternion::FromEulerAngles(eulerAngles); const auto angledRotation = QuatUtils::FromEuler(eulerAngles);
const auto direction = angledRotation.GetForwardVector(); const auto direction = QuatUtils::Forward(angledRotation);
const auto destination = position + direction * distance; const auto destination = position + direction * distance;

View File

@@ -36,7 +36,7 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStrea
info.spawner = nullptr; info.spawner = nullptr;
info.spawnerID = context->originator; info.spawnerID = context->originator;
info.spawnerNodeID = 0; info.spawnerNodeID = 0;
info.pos = info.pos + (info.rot.GetForwardVector() * m_Distance); info.pos = info.pos + (QuatUtils::Forward(info.rot) * m_Distance);
auto* entity = Game::entityManager->CreateEntity( auto* entity = Game::entityManager->CreateEntity(
info, info,

View File

@@ -125,7 +125,7 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitS
if (targetPos.y > reference.y && heightDifference > this->m_upperBound || targetPos.y < reference.y && heightDifference > this->m_lowerBound) if (targetPos.y > reference.y && heightDifference > this->m_upperBound || targetPos.y < reference.y && heightDifference > this->m_lowerBound)
continue; continue;
const auto forward = self->GetRotation().GetForwardVector(); const auto forward = QuatUtils::Forward(self->GetRotation());
// forward is a normalized vector of where the caster is facing. // forward is a normalized vector of where the caster is facing.
// targetPos is the position of the target. // targetPos is the position of the target.

View File

@@ -767,7 +767,7 @@ void BaseCombatAIComponent::LookAt(const NiPoint3& point) {
return; return;
} }
m_Parent->SetRotation(NiQuaternion::LookAt(m_Parent->GetPosition(), point)); m_Parent->SetRotation(QuatUtils::LookAt(m_Parent->GetPosition(), point));
} }
void BaseCombatAIComponent::SetDisabled(bool value) { void BaseCombatAIComponent::SetDisabled(bool value) {

View File

@@ -77,4 +77,4 @@ target_include_directories(dComponents PUBLIC "."
) )
target_precompile_headers(dComponents REUSE_FROM dGameBase) target_precompile_headers(dComponents REUSE_FROM dGameBase)
target_link_libraries(dComponents INTERFACE dBehaviors) target_link_libraries(dComponents INTERFACE dBehaviors PRIVATE glm::glm)

View File

@@ -622,7 +622,7 @@ private:
NiPoint3 m_respawnPos; NiPoint3 m_respawnPos;
NiQuaternion m_respawnRot; NiQuaternion m_respawnRot = QuatUtils::IDENTITY;
std::map<LWOOBJID, Loot::Info> m_DroppedLoot; std::map<LWOOBJID, Loot::Info> m_DroppedLoot;

View File

@@ -1279,7 +1279,7 @@ void InventoryComponent::SpawnPet(Item* item) {
EntityInfo info{}; EntityInfo info{};
info.lot = item->GetLot(); info.lot = item->GetLot();
info.pos = m_Parent->GetPosition(); info.pos = m_Parent->GetPosition();
info.rot = NiQuaternionConstant::IDENTITY; info.rot = QuatUtils::IDENTITY;
info.spawnerID = m_Parent->GetObjectID(); info.spawnerID = m_Parent->GetObjectID();
auto* pet = Game::entityManager->CreateEntity(info); auto* pet = Game::entityManager->CreateEntity(info);

View File

@@ -198,7 +198,7 @@ private:
/** /**
* The rotation original of the model * The rotation original of the model
*/ */
NiQuaternion m_OriginalRotation; NiQuaternion m_OriginalRotation = QuatUtils::IDENTITY;
/** /**
* The ID of the user that made the model * The ID of the user that made the model

View File

@@ -83,7 +83,7 @@ void MovementAIComponent::Resume() {
m_Paused = false; m_Paused = false;
SetVelocity(m_SavedVelocity); SetVelocity(m_SavedVelocity);
m_SavedVelocity = NiPoint3Constant::ZERO; m_SavedVelocity = NiPoint3Constant::ZERO;
SetRotation(NiQuaternion::LookAt(m_Parent->GetPosition(), m_NextWaypoint)); SetRotation(QuatUtils::LookAt(m_Parent->GetPosition(), m_NextWaypoint));
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
} }
@@ -154,7 +154,7 @@ void MovementAIComponent::Update(const float deltaTime) {
m_TimeTravelled = 0.0f; m_TimeTravelled = 0.0f;
m_TimeToTravel = length / speed; m_TimeToTravel = length / speed;
SetRotation(NiQuaternion::LookAt(source, m_NextWaypoint)); SetRotation(QuatUtils::LookAt(source, m_NextWaypoint));
} }
} else { } else {
// Check if there are more waypoints in the queue, if so set our next destination to the next waypoint // Check if there are more waypoints in the queue, if so set our next destination to the next waypoint

View File

@@ -168,7 +168,7 @@ void PetComponent::OnUse(Entity* originator) {
const auto originatorPosition = originator->GetPosition(); const auto originatorPosition = originator->GetPosition();
m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition)); m_Parent->SetRotation(QuatUtils::LookAt(petPosition, originatorPosition));
float interactionDistance = m_Parent->GetVar<float>(u"interaction_distance"); float interactionDistance = m_Parent->GetVar<float>(u"interaction_distance");
if (interactionDistance <= 0) { if (interactionDistance <= 0) {
@@ -177,7 +177,7 @@ void PetComponent::OnUse(Entity* originator) {
auto position = originatorPosition; auto position = originatorPosition;
NiPoint3 forward = NiQuaternion::LookAt(m_Parent->GetPosition(), originator->GetPosition()).GetForwardVector(); NiPoint3 forward = QuatUtils::Forward(QuatUtils::LookAt(m_Parent->GetPosition(), originator->GetPosition()));
forward.y = 0; forward.y = 0;
if (dpWorld::IsLoaded()) { if (dpWorld::IsLoaded()) {
@@ -186,7 +186,7 @@ void PetComponent::OnUse(Entity* originator) {
NiPoint3 nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt); NiPoint3 nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt);
while (std::abs(nearestPoint.y - petPosition.y) > 4 && interactionDistance > 10) { while (std::abs(nearestPoint.y - petPosition.y) > 4 && interactionDistance > 10) {
const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector(); const NiPoint3 forward = QuatUtils::Forward(m_Parent->GetRotation());
attempt = originatorPosition + forward * interactionDistance; attempt = originatorPosition + forward * interactionDistance;
@@ -200,7 +200,7 @@ void PetComponent::OnUse(Entity* originator) {
position = petPosition + forward * interactionDistance; position = petPosition + forward * interactionDistance;
} }
auto rotation = NiQuaternion::LookAt(position, petPosition); auto rotation = QuatUtils::LookAt(position, petPosition);
GameMessages::SendNotifyPetTamingMinigame( GameMessages::SendNotifyPetTamingMinigame(
originator->GetObjectID(), originator->GetObjectID(),
@@ -460,7 +460,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
EntityInfo info{}; EntityInfo info{};
info.lot = entry->puzzleModelLot; info.lot = entry->puzzleModelLot;
info.pos = position; info.pos = position;
info.rot = NiQuaternionConstant::IDENTITY; info.rot = QuatUtils::IDENTITY;
info.spawnerID = tamer->GetObjectID(); info.spawnerID = tamer->GetObjectID();
auto* modelEntity = Game::entityManager->CreateEntity(info); auto* modelEntity = Game::entityManager->CreateEntity(info);
@@ -522,7 +522,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
ePetTamingNotifyType::NAMINGPET, ePetTamingNotifyType::NAMINGPET,
NiPoint3Constant::ZERO, NiPoint3Constant::ZERO,
NiPoint3Constant::ZERO, NiPoint3Constant::ZERO,
NiQuaternionConstant::IDENTITY, QuatUtils::IDENTITY,
UNASSIGNED_SYSTEM_ADDRESS UNASSIGNED_SYSTEM_ADDRESS
); );
@@ -601,7 +601,7 @@ void PetComponent::RequestSetPetName(std::u16string name) {
ePetTamingNotifyType::SUCCESS, ePetTamingNotifyType::SUCCESS,
NiPoint3Constant::ZERO, NiPoint3Constant::ZERO,
NiPoint3Constant::ZERO, NiPoint3Constant::ZERO,
NiQuaternionConstant::IDENTITY, QuatUtils::IDENTITY,
UNASSIGNED_SYSTEM_ADDRESS UNASSIGNED_SYSTEM_ADDRESS
); );
@@ -645,7 +645,7 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
ePetTamingNotifyType::QUIT, ePetTamingNotifyType::QUIT,
NiPoint3Constant::ZERO, NiPoint3Constant::ZERO,
NiPoint3Constant::ZERO, NiPoint3Constant::ZERO,
NiQuaternionConstant::IDENTITY, QuatUtils::IDENTITY,
UNASSIGNED_SYSTEM_ADDRESS UNASSIGNED_SYSTEM_ADDRESS
); );
@@ -696,7 +696,7 @@ void PetComponent::ClientFailTamingMinigame() {
ePetTamingNotifyType::FAILED, ePetTamingNotifyType::FAILED,
NiPoint3Constant::ZERO, NiPoint3Constant::ZERO,
NiPoint3Constant::ZERO, NiPoint3Constant::ZERO,
NiQuaternionConstant::IDENTITY, QuatUtils::IDENTITY,
UNASSIGNED_SYSTEM_ADDRESS UNASSIGNED_SYSTEM_ADDRESS
); );

View File

@@ -175,5 +175,5 @@ private:
/** /**
* If this is a respawn volume, the exact rotation an entity will respawn * If this is a respawn volume, the exact rotation an entity will respawn
*/ */
NiQuaternion m_RespawnRot; NiQuaternion m_RespawnRot = QuatUtils::IDENTITY;
}; };

View File

@@ -17,7 +17,7 @@
PhysicsComponent::PhysicsComponent(Entity* parent, int32_t componentId) : Component(parent) { PhysicsComponent::PhysicsComponent(Entity* parent, int32_t componentId) : Component(parent) {
m_Position = NiPoint3Constant::ZERO; m_Position = NiPoint3Constant::ZERO;
m_Rotation = NiQuaternionConstant::IDENTITY; m_Rotation = QuatUtils::IDENTITY;
m_DirtyPosition = false; m_DirtyPosition = false;
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable<CDPhysicsComponentTable>(); CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable<CDPhysicsComponentTable>();
@@ -81,10 +81,10 @@ dpEntity* PhysicsComponent::CreatePhysicsEntity(eReplicaComponentType type) {
toReturn = new dpEntity(m_Parent->GetObjectID(), 1.0f, 12.5f, 20.0f); // Not sure what the real size is toReturn = new dpEntity(m_Parent->GetObjectID(), 1.0f, 12.5f, 20.0f); // Not sure what the real size is
} else if (info->physicsAsset == "env\\NG_NinjaGo\\env_ng_gen_gate_chamber_puzzle_ceiling_tile_falling_phantom.hkx") { } else if (info->physicsAsset == "env\\NG_NinjaGo\\env_ng_gen_gate_chamber_puzzle_ceiling_tile_falling_phantom.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 18.0f, 5.0f, 15.0f); toReturn = new dpEntity(m_Parent->GetObjectID(), 18.0f, 5.0f, 15.0f);
m_Position += m_Rotation.GetForwardVector() * 7.5f; m_Position += QuatUtils::Forward(m_Rotation) * 7.5f;
} else if (info->physicsAsset == "env\\NG_NinjaGo\\ng_flamejet_brick_phantom.HKX") { } else if (info->physicsAsset == "env\\NG_NinjaGo\\ng_flamejet_brick_phantom.HKX") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 1.0f, 1.0f, 12.0f); toReturn = new dpEntity(m_Parent->GetObjectID(), 1.0f, 1.0f, 12.0f);
m_Position += m_Rotation.GetForwardVector() * 6.0f; m_Position += QuatUtils::Forward(m_Rotation) * 6.0f;
} else if (info->physicsAsset == "env\\Ring_Trigger.hkx") { } else if (info->physicsAsset == "env\\Ring_Trigger.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 6.0f, 6.0f, 6.0f); toReturn = new dpEntity(m_Parent->GetObjectID(), 6.0f, 6.0f, 6.0f);
} else if (info->physicsAsset == "env\\vfx_propertyImaginationBall.hkx") { } else if (info->physicsAsset == "env\\vfx_propertyImaginationBall.hkx") {

View File

@@ -45,7 +45,7 @@ protected:
NiPoint3 m_Position; NiPoint3 m_Position;
NiQuaternion m_Rotation; NiQuaternion m_Rotation = QuatUtils::IDENTITY;
bool m_DirtyPosition; bool m_DirtyPosition;

View File

@@ -333,7 +333,7 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N
const auto modelLOT = item->GetLot(); const auto modelLOT = item->GetLot();
if (rotation != NiQuaternionConstant::IDENTITY) { if (rotation != QuatUtils::IDENTITY) {
rotation = { rotation.w, rotation.z, rotation.y, rotation.x }; rotation = { rotation.w, rotation.z, rotation.y, rotation.x };
} }
@@ -516,7 +516,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet
GameMessages::SendGetModelsOnProperty(entity->GetObjectID(), GetModels(), UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendGetModelsOnProperty(entity->GetObjectID(), GetModels(), UNASSIGNED_SYSTEM_ADDRESS);
GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 16, NiQuaternionConstant::IDENTITY); GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 16, QuatUtils::IDENTITY);
if (spawner != nullptr) { if (spawner != nullptr) {
Game::zoneManager->RemoveSpawner(spawner->m_Info.spawnerID); Game::zoneManager->RemoveSpawner(spawner->m_Info.spawnerID);
@@ -569,7 +569,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet
GameMessages::SendGetModelsOnProperty(entity->GetObjectID(), GetModels(), UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendGetModelsOnProperty(entity->GetObjectID(), GetModels(), UNASSIGNED_SYSTEM_ADDRESS);
GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 16, NiQuaternionConstant::IDENTITY); GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 16, QuatUtils::IDENTITY);
if (spawner != nullptr) { if (spawner != nullptr) {
Game::zoneManager->RemoveSpawner(spawner->m_Info.spawnerID); Game::zoneManager->RemoveSpawner(spawner->m_Info.spawnerID);

View File

@@ -123,7 +123,7 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player,
auto spawnPointEntities = Game::entityManager->GetEntitiesByLOT(4843); auto spawnPointEntities = Game::entityManager->GetEntitiesByLOT(4843);
auto startPosition = NiPoint3Constant::ZERO; auto startPosition = NiPoint3Constant::ZERO;
auto startRotation = NiQuaternionConstant::IDENTITY; auto startRotation = QuatUtils::IDENTITY;
const std::string placementAsString = std::to_string(positionNumber); const std::string placementAsString = std::to_string(positionNumber);
for (auto entity : spawnPointEntities) { for (auto entity : spawnPointEntities) {
if (!entity) continue; if (!entity) continue;

View File

@@ -48,7 +48,7 @@ struct RacingPlayerInfo {
/** /**
* Rotation that the player will respawn at if they smash their car * Rotation that the player will respawn at if they smash their car
*/ */
NiQuaternion respawnRotation; NiQuaternion respawnRotation = QuatUtils::IDENTITY;
/** /**
* The index in the respawn point the player is now at * The index in the respawn point the player is now at

View File

@@ -316,7 +316,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(
start.originatorRot = originator->GetRotation(); start.originatorRot = originator->GetRotation();
} }
if (rotationOverride != NiQuaternionConstant::IDENTITY) { if (rotationOverride != QuatUtils::IDENTITY) {
start.originatorRot = rotationOverride; start.originatorRot = rotationOverride;
} }
//start.optionalTargetID = target; //start.optionalTargetID = target;

View File

@@ -129,7 +129,7 @@ public:
* @param optionalOriginatorID change the originator of the skill * @param optionalOriginatorID change the originator of the skill
* @return if the case succeeded * @return if the case succeeded
*/ */
bool CastSkill(const uint32_t skillId, LWOOBJID target = LWOOBJID_EMPTY, const LWOOBJID optionalOriginatorID = LWOOBJID_EMPTY, const int32_t castType = 0, const NiQuaternion rotationOverride = NiQuaternionConstant::IDENTITY); bool CastSkill(const uint32_t skillId, LWOOBJID target = LWOOBJID_EMPTY, const LWOOBJID optionalOriginatorID = LWOOBJID_EMPTY, const int32_t castType = 0, const NiQuaternion rotationOverride = QuatUtils::IDENTITY);
/** /**
* Initializes a server-side skill calculation. * Initializes a server-side skill calculation.
@@ -141,7 +141,7 @@ public:
* @param originatorOverride an override for the originator of the skill calculation * @param originatorOverride an override for the originator of the skill calculation
* @return the result of the skill calculation * @return the result of the skill calculation
*/ */
SkillExecutionResult CalculateBehavior(uint32_t skillId, uint32_t behaviorId, LWOOBJID target, bool ignoreTarget = false, bool clientInitalized = false, LWOOBJID originatorOverride = LWOOBJID_EMPTY, const int32_t castType = 0, const NiQuaternion rotationOverride = NiQuaternionConstant::IDENTITY); SkillExecutionResult CalculateBehavior(uint32_t skillId, uint32_t behaviorId, LWOOBJID target, bool ignoreTarget = false, bool clientInitalized = false, LWOOBJID originatorOverride = LWOOBJID_EMPTY, const int32_t castType = 0, const NiQuaternion rotationOverride = QuatUtils::IDENTITY);
/** /**
* Register a server-side projectile. * Register a server-side projectile.

View File

@@ -17,6 +17,8 @@
#include "EntityManager.h" #include "EntityManager.h"
#include "MovementAIComponent.h" #include "MovementAIComponent.h"
#include <glm/gtc/quaternion.hpp>
TriggerComponent::TriggerComponent(Entity* parent, const std::string triggerInfo) : Component(parent) { TriggerComponent::TriggerComponent(Entity* parent, const std::string triggerInfo) : Component(parent) {
m_Parent = parent; m_Parent = parent;
m_Trigger = nullptr; m_Trigger = nullptr;
@@ -240,10 +242,9 @@ void TriggerComponent::HandleMoveObject(Entity* targetEntity, std::vector<std::s
void TriggerComponent::HandleRotateObject(Entity* targetEntity, std::vector<std::string> argArray) { void TriggerComponent::HandleRotateObject(Entity* targetEntity, std::vector<std::string> argArray) {
if (argArray.size() <= 2) return; if (argArray.size() <= 2) return;
const NiPoint3 vector = GeneralUtils::TryParse<NiPoint3>(argArray).value_or(NiPoint3Constant::ZERO); const auto vector = GeneralUtils::TryParse<glm::vec3>(argArray).value_or(glm::zero<glm::vec3>());
NiQuaternion rotation = NiQuaternion::FromEulerAngles(vector); targetEntity->SetRotation(glm::quat(vector));
targetEntity->SetRotation(rotation);
} }
void TriggerComponent::HandlePushObject(Entity* targetEntity, std::vector<std::string> argArray) { void TriggerComponent::HandlePushObject(Entity* targetEntity, std::vector<std::string> argArray) {

View File

@@ -33,7 +33,7 @@ struct EntityInfo {
LWOOBJID id; LWOOBJID id;
LOT lot; LOT lot;
NiPoint3 pos; NiPoint3 pos;
NiQuaternion rot; NiQuaternion rot = QuatUtils::IDENTITY;
std::vector<LDFBaseData*> settings; std::vector<LDFBaseData*> settings;
std::vector<LDFBaseData*> networkSettings; std::vector<LDFBaseData*> networkSettings;
float scale; float scale;

View File

@@ -15,11 +15,11 @@ public:
iCastType = 0; iCastType = 0;
lastClickedPosit = NiPoint3Constant::ZERO; lastClickedPosit = NiPoint3Constant::ZERO;
optionalTargetID = LWOOBJID_EMPTY; optionalTargetID = LWOOBJID_EMPTY;
originatorRot = NiQuaternionConstant::IDENTITY; originatorRot = QuatUtils::IDENTITY;
uiSkillHandle = 0; uiSkillHandle = 0;
} }
EchoStartSkill(LWOOBJID _optionalOriginatorID, std::string _sBitStream, TSkillID _skillID, bool _bUsedMouse = false, float _fCasterLatency = 0.0f, int32_t _iCastType = 0, NiPoint3 _lastClickedPosit = NiPoint3Constant::ZERO, LWOOBJID _optionalTargetID = LWOOBJID_EMPTY, NiQuaternion _originatorRot = NiQuaternionConstant::IDENTITY, uint32_t _uiSkillHandle = 0) { EchoStartSkill(LWOOBJID _optionalOriginatorID, std::string _sBitStream, TSkillID _skillID, bool _bUsedMouse = false, float _fCasterLatency = 0.0f, int32_t _iCastType = 0, NiPoint3 _lastClickedPosit = NiPoint3Constant::ZERO, LWOOBJID _optionalTargetID = LWOOBJID_EMPTY, NiQuaternion _originatorRot = QuatUtils::IDENTITY, uint32_t _uiSkillHandle = 0) {
bUsedMouse = _bUsedMouse; bUsedMouse = _bUsedMouse;
fCasterLatency = _fCasterLatency; fCasterLatency = _fCasterLatency;
iCastType = _iCastType; iCastType = _iCastType;
@@ -58,8 +58,8 @@ public:
stream.Write(optionalTargetID != LWOOBJID_EMPTY); stream.Write(optionalTargetID != LWOOBJID_EMPTY);
if (optionalTargetID != LWOOBJID_EMPTY) stream.Write(optionalTargetID); if (optionalTargetID != LWOOBJID_EMPTY) stream.Write(optionalTargetID);
stream.Write(originatorRot != NiQuaternionConstant::IDENTITY); stream.Write(originatorRot != QuatUtils::IDENTITY);
if (originatorRot != NiQuaternionConstant::IDENTITY) stream.Write(originatorRot); if (originatorRot != QuatUtils::IDENTITY) stream.Write(originatorRot);
uint32_t sBitStreamLength = sBitStream.length(); uint32_t sBitStreamLength = sBitStream.length();
stream.Write(sBitStreamLength); stream.Write(sBitStreamLength);
@@ -121,7 +121,7 @@ public:
NiPoint3 lastClickedPosit; NiPoint3 lastClickedPosit;
LWOOBJID optionalOriginatorID; LWOOBJID optionalOriginatorID;
LWOOBJID optionalTargetID; LWOOBJID optionalTargetID;
NiQuaternion originatorRot; NiQuaternion originatorRot = QuatUtils::IDENTITY;
std::string sBitStream; std::string sBitStream;
TSkillID skillID; TSkillID skillID;
uint32_t uiSkillHandle; uint32_t uiSkillHandle;

View File

@@ -389,7 +389,7 @@ void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAd
float fMoveTimeElapsed = 0.0f; float fMoveTimeElapsed = 0.0f;
float fPercentBetweenPoints = 0.0f; float fPercentBetweenPoints = 0.0f;
NiPoint3 ptUnexpectedLocation = NiPoint3Constant::ZERO; NiPoint3 ptUnexpectedLocation = NiPoint3Constant::ZERO;
NiQuaternion qUnexpectedRotation = NiQuaternionConstant::IDENTITY; NiQuaternion qUnexpectedRotation = QuatUtils::IDENTITY;
bitStream.Write(bReverse); bitStream.Write(bReverse);
bitStream.Write(bStopAtDesiredWaypoint); bitStream.Write(bStopAtDesiredWaypoint);
@@ -406,8 +406,8 @@ void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAd
bitStream.Write(ptUnexpectedLocation.y); bitStream.Write(ptUnexpectedLocation.y);
bitStream.Write(ptUnexpectedLocation.z); bitStream.Write(ptUnexpectedLocation.z);
bitStream.Write(qUnexpectedRotation != NiQuaternionConstant::IDENTITY); bitStream.Write(qUnexpectedRotation != QuatUtils::IDENTITY);
if (qUnexpectedRotation != NiQuaternionConstant::IDENTITY) { if (qUnexpectedRotation != QuatUtils::IDENTITY) {
bitStream.Write(qUnexpectedRotation.x); bitStream.Write(qUnexpectedRotation.x);
bitStream.Write(qUnexpectedRotation.y); bitStream.Write(qUnexpectedRotation.y);
bitStream.Write(qUnexpectedRotation.z); bitStream.Write(qUnexpectedRotation.z);
@@ -1181,7 +1181,7 @@ void GameMessages::SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPo
bitStream.Write(position.y); bitStream.Write(position.y);
bitStream.Write(position.z); bitStream.Write(position.z);
const bool bIsNotIdentity = rotation != NiQuaternionConstant::IDENTITY; const bool bIsNotIdentity = rotation != QuatUtils::IDENTITY;
bitStream.Write(bIsNotIdentity); bitStream.Write(bIsNotIdentity);
if (bIsNotIdentity) { if (bIsNotIdentity) {
@@ -2129,8 +2129,8 @@ void GameMessages::SendPlaceModelResponse(LWOOBJID objectId, const SystemAddress
bitStream.Write(response); bitStream.Write(response);
} }
bitStream.Write(rotation != NiQuaternionConstant::IDENTITY); bitStream.Write(rotation != QuatUtils::IDENTITY);
if (rotation != NiQuaternionConstant::IDENTITY) { if (rotation != QuatUtils::IDENTITY) {
bitStream.Write(response); bitStream.Write(response);
} }
@@ -2395,13 +2395,13 @@ void GameMessages::HandlePlacePropertyModel(RakNet::BitStream& inStream, Entity*
inStream.Read(model); inStream.Read(model);
PropertyManagementComponent::Instance()->UpdateModelPosition(model, NiPoint3Constant::ZERO, NiQuaternionConstant::IDENTITY); PropertyManagementComponent::Instance()->UpdateModelPosition(model, NiPoint3Constant::ZERO, QuatUtils::IDENTITY);
} }
void GameMessages::HandleUpdatePropertyModel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) { void GameMessages::HandleUpdatePropertyModel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
LWOOBJID model; LWOOBJID model;
NiPoint3 position; NiPoint3 position;
NiQuaternion rotation = NiQuaternionConstant::IDENTITY; NiQuaternion rotation = QuatUtils::IDENTITY;
inStream.Read(model); inStream.Read(model);
inStream.Read(position); inStream.Read(position);
@@ -3400,7 +3400,7 @@ void GameMessages::SendNotifyPetTamingMinigame(LWOOBJID objectId, LWOOBJID petId
bitStream.Write(petsDestPos); bitStream.Write(petsDestPos);
bitStream.Write(telePos); bitStream.Write(telePos);
const bool hasDefault = teleRot != NiQuaternionConstant::IDENTITY; const bool hasDefault = teleRot != QuatUtils::IDENTITY;
bitStream.Write(hasDefault); bitStream.Write(hasDefault);
if (hasDefault) bitStream.Write(teleRot); if (hasDefault) bitStream.Write(teleRot);

View File

@@ -18,7 +18,6 @@ class AMFBaseValue;
class AMFArrayValue; class AMFArrayValue;
class Entity; class Entity;
class Item; class Item;
class NiQuaternion;
class User; class User;
class Leaderboard; class Leaderboard;
class PropertySelectQueryProperty; class PropertySelectQueryProperty;
@@ -765,7 +764,7 @@ namespace GameMessages {
void Handle(Entity& entity, const SystemAddress& sysAddr) override; void Handle(Entity& entity, const SystemAddress& sysAddr) override;
NiPoint3 target{}; NiPoint3 target{};
NiQuaternion rotation{}; NiQuaternion rotation = QuatUtils::IDENTITY;
}; };
struct ChildLoaded : public GameMsg { struct ChildLoaded : public GameMsg {

View File

@@ -18,11 +18,11 @@ public:
iCastType = 0; iCastType = 0;
lastClickedPosit = NiPoint3Constant::ZERO; lastClickedPosit = NiPoint3Constant::ZERO;
optionalTargetID = LWOOBJID_EMPTY; optionalTargetID = LWOOBJID_EMPTY;
originatorRot = NiQuaternionConstant::IDENTITY; originatorRot = QuatUtils::IDENTITY;
uiSkillHandle = 0; uiSkillHandle = 0;
} }
StartSkill(LWOOBJID _optionalOriginatorID, std::string _sBitStream, TSkillID _skillID, bool _bUsedMouse = false, LWOOBJID _consumableItemID = LWOOBJID_EMPTY, float _fCasterLatency = 0.0f, int32_t _iCastType = 0, NiPoint3 _lastClickedPosit = NiPoint3Constant::ZERO, LWOOBJID _optionalTargetID = LWOOBJID_EMPTY, NiQuaternion _originatorRot = NiQuaternionConstant::IDENTITY, uint32_t _uiSkillHandle = 0) { StartSkill(LWOOBJID _optionalOriginatorID, std::string _sBitStream, TSkillID _skillID, bool _bUsedMouse = false, LWOOBJID _consumableItemID = LWOOBJID_EMPTY, float _fCasterLatency = 0.0f, int32_t _iCastType = 0, NiPoint3 _lastClickedPosit = NiPoint3Constant::ZERO, LWOOBJID _optionalTargetID = LWOOBJID_EMPTY, NiQuaternion _originatorRot = QuatUtils::IDENTITY, uint32_t _uiSkillHandle = 0) {
bUsedMouse = _bUsedMouse; bUsedMouse = _bUsedMouse;
consumableItemID = _consumableItemID; consumableItemID = _consumableItemID;
fCasterLatency = _fCasterLatency; fCasterLatency = _fCasterLatency;
@@ -65,8 +65,8 @@ public:
stream.Write(optionalTargetID != LWOOBJID_EMPTY); stream.Write(optionalTargetID != LWOOBJID_EMPTY);
if (optionalTargetID != LWOOBJID_EMPTY) stream.Write(optionalTargetID); if (optionalTargetID != LWOOBJID_EMPTY) stream.Write(optionalTargetID);
stream.Write(originatorRot != NiQuaternionConstant::IDENTITY); stream.Write(originatorRot != QuatUtils::IDENTITY);
if (originatorRot != NiQuaternionConstant::IDENTITY) stream.Write(originatorRot); if (originatorRot != QuatUtils::IDENTITY) stream.Write(originatorRot);
uint32_t sBitStreamLength = sBitStream.length(); uint32_t sBitStreamLength = sBitStream.length();
stream.Write(sBitStreamLength); stream.Write(sBitStreamLength);
@@ -133,7 +133,7 @@ public:
NiPoint3 lastClickedPosit{}; NiPoint3 lastClickedPosit{};
LWOOBJID optionalOriginatorID{}; LWOOBJID optionalOriginatorID{};
LWOOBJID optionalTargetID{}; LWOOBJID optionalTargetID{};
NiQuaternion originatorRot{}; NiQuaternion originatorRot = QuatUtils::IDENTITY;
std::string sBitStream = ""; std::string sBitStream = "";
TSkillID skillID = 0; TSkillID skillID = 0;
uint32_t uiSkillHandle = 0; uint32_t uiSkillHandle = 0;

View File

@@ -138,7 +138,7 @@ void Strip::Spawn(LOT lot, Entity& entity) {
EntityInfo info{}; EntityInfo info{};
info.lot = lot; info.lot = lot;
info.pos = entity.GetPosition(); info.pos = entity.GetPosition();
info.rot = NiQuaternionConstant::IDENTITY; info.rot = QuatUtils::IDENTITY;
info.spawnerID = entity.GetObjectID(); info.spawnerID = entity.GetObjectID();
auto* const spawnedEntity = Game::entityManager->CreateEntity(info, nullptr, &entity); auto* const spawnedEntity = Game::entityManager->CreateEntity(info, nullptr, &entity);
spawnedEntity->AddToGroup("SpawnedPropertyEnemies"); spawnedEntity->AddToGroup("SpawnedPropertyEnemies");

View File

@@ -808,6 +808,15 @@ void SlashCommandHandler::Startup() {
}; };
RegisterCommand(DeleteInvenCommand); RegisterCommand(DeleteInvenCommand);
Command ExecuteCommand{
.help = "Execute commands with modified context (Minecraft-style)",
.info = "Execute commands as different entities or from different positions. Usage: /execute <subcommand> ... run <command>. Subcommands: as <entity>, at <entity>, positioned <x> <y> <z>",
.aliases = { "execute", "exec" },
.handle = DEVGMCommands::Execute,
.requiredLevel = eGameMasterLevel::DEVELOPER
};
RegisterCommand(ExecuteCommand);
// Register Greater Than Zero Commands // Register Greater Than Zero Commands
Command KickCommand{ Command KickCommand{

View File

@@ -559,23 +559,25 @@ namespace DEVGMCommands {
} }
} }
std::optional<float> ParseRelativeAxis(const float sourcePos, const std::string& toParse) { // Parse coordinates with support for relative positioning (~)
if (toParse.empty()) return std::nullopt; std::optional<float> ParseRelativeAxis(const float currentValue, const std::string& rawCoord) {
if (rawCoord.empty()) return std::nullopt;
// relative offset from current position std::string coord = rawCoord;
if (toParse[0] == '~') { // Remove any '+' characters to simplify parsing, since they don't affect the value
if (toParse.size() == 1) return sourcePos; coord.erase(std::remove(coord.begin(), coord.end(), '+'), coord.end());
if (coord[0] == '~') {
if (toParse.size() < 3 || !(toParse[1] != '+' || toParse[1] != '-')) return std::nullopt; if (coord.length() == 1) {
return currentValue;
const auto offset = GeneralUtils::TryParse<float>(toParse.substr(2)); } else {
if (!offset.has_value()) return std::nullopt; auto offsetOpt = GeneralUtils::TryParse<float>(coord.substr(1));
if (!offsetOpt) return std::nullopt;
bool isNegative = toParse[1] == '-'; return currentValue + offsetOpt.value();
return isNegative ? sourcePos - offset.value() : sourcePos + offset.value(); }
} else {
auto absoluteOpt = GeneralUtils::TryParse<float>(coord);
if (!absoluteOpt) return std::nullopt;
return absoluteOpt.value();
} }
return GeneralUtils::TryParse<float>(toParse);
} }
void Teleport(Entity* entity, const SystemAddress& sysAddr, const std::string args) { void Teleport(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
@@ -649,7 +651,7 @@ namespace DEVGMCommands {
if (havokVehiclePhysicsComponent) { if (havokVehiclePhysicsComponent) {
havokVehiclePhysicsComponent->SetPosition(pos); havokVehiclePhysicsComponent->SetPosition(pos);
Game::entityManager->SerializeEntity(possassableEntity); Game::entityManager->SerializeEntity(possassableEntity);
} else GameMessages::SendTeleport(possassableEntity->GetObjectID(), pos, NiQuaternion(), sysAddr); } else GameMessages::SendTeleport(possassableEntity->GetObjectID(), pos, QuatUtils::IDENTITY, sysAddr);
} }
} }
} }
@@ -660,7 +662,7 @@ namespace DEVGMCommands {
const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER); const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER);
for (auto* character : characters) { for (auto* character : characters) {
GameMessages::SendTeleport(character->GetObjectID(), pos, NiQuaternion(), character->GetSystemAddress()); GameMessages::SendTeleport(character->GetObjectID(), pos, QuatUtils::IDENTITY, character->GetSystemAddress());
} }
} }
@@ -815,7 +817,7 @@ namespace DEVGMCommands {
// Set the position to the generated random position plus the player position. This will // Set the position to the generated random position plus the player position. This will
// spawn the entity in a circle around the player. As you get further from the player, the angle chosen will get less accurate. // spawn the entity in a circle around the player. As you get further from the player, the angle chosen will get less accurate.
info.pos = playerPosition + NiPoint3(cos(randomAngle) * randomRadius, 0.0f, sin(randomAngle) * randomRadius); info.pos = playerPosition + NiPoint3(cos(randomAngle) * randomRadius, 0.0f, sin(randomAngle) * randomRadius);
info.rot = NiQuaternion(); info.rot = QuatUtils::IDENTITY;
auto newEntity = Game::entityManager->CreateEntity(info); auto newEntity = Game::entityManager->CreateEntity(info);
if (newEntity == nullptr) { if (newEntity == nullptr) {
@@ -1664,4 +1666,163 @@ namespace DEVGMCommands {
LOG("Despawned entity (%llu)", target->GetObjectID()); LOG("Despawned entity (%llu)", target->GetObjectID());
ChatPackets::SendSystemMessage(sysAddr, u"Despawned entity: " + GeneralUtils::to_u16string(target->GetObjectID())); ChatPackets::SendSystemMessage(sysAddr, u"Despawned entity: " + GeneralUtils::to_u16string(target->GetObjectID()));
} }
void Execute(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
if (args.empty()) {
ChatPackets::SendSystemMessage(sysAddr,
u"Usage: /execute <subcommand> ... run <command>\n"
u"Subcommands:\n"
u" as <playername> - Execute as different player\n"
u" at <playername> - Execute from player's position\n"
u" positioned <x> <y> <z> - Execute from coordinates (absolute or relative with ~)\n"
u"Examples:\n"
u" /execute as Player1 run pos\n"
u" /execute at Player2 positioned 100 200 300 run spawn 1234\n"
u" /execute positioned ~5 ~10 ~ run spawn 1234"
);
return;
}
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
// Prevent execute command recursion by checking if this is already an execute command
for (const auto& arg : splitArgs) {
if (arg == "execute" || arg == "exec") {
ChatPackets::SendSystemMessage(sysAddr, u"Error: Recursive execute commands are not allowed");
return;
}
}
// Context variables for execution
Entity* execEntity = entity; // Entity to execute as
NiPoint3 execPosition = entity->GetPosition(); // Position to execute from
bool positionOverridden = false;
std::string finalCommand;
// Parse subcommands
size_t i = 0;
while (i < splitArgs.size()) {
const std::string& subcommand = splitArgs[i];
if (subcommand == "as") {
if (i + 1 >= splitArgs.size()) {
ChatPackets::SendSystemMessage(sysAddr, u"Error: 'as' requires a player name");
return;
}
const std::string& targetName = splitArgs[i + 1];
auto* targetPlayer = PlayerManager::GetPlayer(targetName);
if (!targetPlayer) {
ChatPackets::SendSystemMessage(sysAddr, u"Error: Player '" + GeneralUtils::ASCIIToUTF16(targetName) + u"' not found");
return;
}
execEntity = targetPlayer;
i += 2;
} else if (subcommand == "at") {
if (i + 1 >= splitArgs.size()) {
ChatPackets::SendSystemMessage(sysAddr, u"Error: 'at' requires a player name");
return;
}
const std::string& targetName = splitArgs[i + 1];
auto* targetPlayer = PlayerManager::GetPlayer(targetName);
if (!targetPlayer) {
ChatPackets::SendSystemMessage(sysAddr, u"Error: Player '" + GeneralUtils::ASCIIToUTF16(targetName) + u"' not found");
return;
}
execPosition = targetPlayer->GetPosition();
positionOverridden = true;
i += 2;
} else if (subcommand == "positioned") {
if (i + 3 >= splitArgs.size()) {
ChatPackets::SendSystemMessage(sysAddr, u"Error: 'positioned' requires x, y, z coordinates");
return;
}
auto xOpt = ParseRelativeAxis(execPosition.x, splitArgs[i + 1]);
auto yOpt = ParseRelativeAxis(execPosition.y, splitArgs[i + 2]);
auto zOpt = ParseRelativeAxis(execPosition.z, splitArgs[i + 3]);
if (!xOpt || !yOpt || !zOpt) {
ChatPackets::SendSystemMessage(sysAddr, u"Error: Invalid coordinates for 'positioned'. Use numeric values or relative coordinates with ~.");
return;
}
execPosition = NiPoint3(xOpt.value(), yOpt.value(), zOpt.value());
positionOverridden = true;
i += 4;
} else if (subcommand == "run") {
// Everything after "run" is the command to execute
if (i + 1 >= splitArgs.size()) {
ChatPackets::SendSystemMessage(sysAddr, u"Error: 'run' requires a command");
return;
}
// Reconstruct the command from remaining args
for (size_t j = i + 1; j < splitArgs.size(); ++j) {
if (!finalCommand.empty()) finalCommand += " ";
finalCommand += splitArgs[j];
}
break;
} else {
ChatPackets::SendSystemMessage(sysAddr, u"Error: Unknown subcommand '" + GeneralUtils::ASCIIToUTF16(subcommand) + u"'");
ChatPackets::SendSystemMessage(sysAddr, u"Valid subcommands: as, at, positioned, run");
return;
}
}
if (finalCommand.empty()) {
ChatPackets::SendSystemMessage(sysAddr, u"Error: No command specified to run. Use 'run <command>' at the end.");
return;
}
// Validate that the command starts with a valid character
if (finalCommand.empty() || finalCommand[0] == '/') {
ChatPackets::SendSystemMessage(sysAddr, u"Error: Command should not start with '/'. Just specify the command name.");
return;
}
// Store original position if we need to restore it
NiPoint3 originalPosition;
bool needToRestore = false;
if (positionOverridden && execEntity == entity) {
// If we're executing as ourselves but from a different position,
// temporarily move the entity
originalPosition = entity->GetPosition();
needToRestore = true;
// Set the position temporarily for the command execution
auto* controllable = entity->GetComponent<ControllablePhysicsComponent>();
if (controllable) {
controllable->SetPosition(execPosition);
}
}
// Provide feedback about what we're executing
std::string execAsName = execEntity->GetCharacter() ? execEntity->GetCharacter()->GetName() : "Unknown";
ChatPackets::SendSystemMessage(sysAddr, u"[Execute] Running as '" + GeneralUtils::ASCIIToUTF16(execAsName) +
u"' from <" + GeneralUtils::to_u16string(execPosition.x) + u", " +
GeneralUtils::to_u16string(execPosition.y) + u", " +
GeneralUtils::to_u16string(execPosition.z) + u">: /" +
GeneralUtils::ASCIIToUTF16(finalCommand));
// Execute the command through the slash command handler
SlashCommandHandler::HandleChatCommand(GeneralUtils::ASCIIToUTF16("/" + finalCommand), execEntity, sysAddr);
// Restore original position if needed
if (needToRestore) {
auto* controllable = entity->GetComponent<ControllablePhysicsComponent>();
if (controllable) {
controllable->SetPosition(originalPosition);
}
}
}
}; };

View File

@@ -76,6 +76,7 @@ namespace DEVGMCommands {
void Shutdown(Entity* entity, const SystemAddress& sysAddr, const std::string args); void Shutdown(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Barfight(Entity* entity, const SystemAddress& sysAddr, const std::string args); void Barfight(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Despawn(Entity* entity, const SystemAddress& sysAddr, const std::string args); void Despawn(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Execute(Entity* entity, const SystemAddress& sysAddr, const std::string args);
} }
#endif //!DEVGMCOMMANDS_H #endif //!DEVGMCOMMANDS_H

View File

@@ -8,7 +8,7 @@
struct VanityObjectLocation { struct VanityObjectLocation {
float m_Chance = 1.0f; float m_Chance = 1.0f;
NiPoint3 m_Position; NiPoint3 m_Position;
NiQuaternion m_Rotation; NiQuaternion m_Rotation = QuatUtils::IDENTITY;
float m_Scale = 1.0f; float m_Scale = 1.0f;
}; };

View File

@@ -159,6 +159,7 @@ int main(int argc, char** argv) {
} }
MigrationRunner::RunMigrations(); MigrationRunner::RunMigrations();
Database::Get()->Commit();
const auto resServerPath = BinaryPathFinder::GetBinaryDir() / "resServer"; const auto resServerPath = BinaryPathFinder::GetBinaryDir() / "resServer";
std::filesystem::create_directories(resServerPath); std::filesystem::create_directories(resServerPath);
const bool cdServerExists = std::filesystem::exists(resServerPath / "CDServer.sqlite"); const bool cdServerExists = std::filesystem::exists(resServerPath / "CDServer.sqlite");

View File

@@ -10,7 +10,7 @@ set(DNET_SOURCES "AuthPackets.cpp"
"ZoneInstanceManager.cpp") "ZoneInstanceManager.cpp")
add_library(dNet STATIC ${DNET_SOURCES}) add_library(dNet STATIC ${DNET_SOURCES})
target_link_libraries(dNet PRIVATE bcrypt MD5) target_link_libraries(dNet PRIVATE bcrypt MD5 glm::glm)
target_include_directories(dNet PRIVATE target_include_directories(dNet PRIVATE
"${PROJECT_SOURCE_DIR}/dCommon" "${PROJECT_SOURCE_DIR}/dCommon"
"${PROJECT_SOURCE_DIR}/dCommon/dEnums" "${PROJECT_SOURCE_DIR}/dCommon/dEnums"

View File

@@ -12,5 +12,5 @@ target_include_directories(dPhysics PUBLIC "."
"${PROJECT_SOURCE_DIR}/dCommon/dEnums" "${PROJECT_SOURCE_DIR}/dCommon/dEnums"
) )
target_link_libraries(dPhysics target_link_libraries(dPhysics
PUBLIC Recast Detour PUBLIC Recast Detour glm::glm
INTERFACE dNavigation dCommon) INTERFACE dNavigation dCommon)

View File

@@ -68,7 +68,7 @@ private:
bool m_IsStatic; bool m_IsStatic;
NiPoint3 m_Position; NiPoint3 m_Position;
NiQuaternion m_Rotation; NiQuaternion m_Rotation = QuatUtils::IDENTITY;
float m_Scale; float m_Scale;
NiPoint3 m_Velocity; NiPoint3 m_Velocity;

View File

@@ -88,7 +88,7 @@ void BossSpiderQueenEnemyServer::WithdrawSpider(Entity* self, const bool withdra
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"SetColGroup", 10, 0, 0, "", UNASSIGNED_SYSTEM_ADDRESS); GameMessages::SendNotifyClientObject(self->GetObjectID(), u"SetColGroup", 10, 0, 0, "", UNASSIGNED_SYSTEM_ADDRESS);
//First rotate for anim //First rotate for anim
NiQuaternion rot = NiQuaternionConstant::IDENTITY; NiQuaternion rot = QuatUtils::IDENTITY;
controllable->SetStatic(false); controllable->SetStatic(false);
@@ -405,7 +405,7 @@ void BossSpiderQueenEnemyServer::OnTimerDone(Entity* self, const std::string tim
const auto withdrawn = self->GetBoolean(u"isWithdrawn"); const auto withdrawn = self->GetBoolean(u"isWithdrawn");
if (!withdrawn) return; if (!withdrawn) return;
NiQuaternion rot = NiQuaternionConstant::IDENTITY; NiQuaternion rot = QuatUtils::IDENTITY;
//First rotate for anim //First rotate for anim
controllable->SetStatic(false); controllable->SetStatic(false);
@@ -600,12 +600,12 @@ void BossSpiderQueenEnemyServer::OnUpdate(Entity* self) {
if (!isWithdrawn) return; if (!isWithdrawn) return;
if (controllable->GetRotation() == NiQuaternionConstant::IDENTITY) { if (controllable->GetRotation() == QuatUtils::IDENTITY) {
return; return;
} }
controllable->SetStatic(false); controllable->SetStatic(false);
controllable->SetRotation(NiQuaternionConstant::IDENTITY); controllable->SetRotation(QuatUtils::IDENTITY);
controllable->SetStatic(true); controllable->SetStatic(true);
Game::entityManager->SerializeEntity(self); Game::entityManager->SerializeEntity(self);

View File

@@ -52,7 +52,7 @@ private:
ControllablePhysicsComponent* controllable = nullptr; ControllablePhysicsComponent* controllable = nullptr;
BaseCombatAIComponent* combat = nullptr; BaseCombatAIComponent* combat = nullptr;
NiQuaternion originRotation; NiQuaternion originRotation = QuatUtils::IDENTITY;
int m_CurrentBossStage = 0; int m_CurrentBossStage = 0;
int m_DeathCounter = 0; int m_DeathCounter = 0;

View File

@@ -76,7 +76,7 @@ void AmDarklingDragon::OnHitOrHealResult(Entity* self, Entity* attacker, int32_t
self->AddTimer("timeToStunLoop", 1.0f); self->AddTimer("timeToStunLoop", 1.0f);
auto position = self->GetPosition(); auto position = self->GetPosition();
auto forward = self->GetRotation().GetForwardVector(); auto forward = QuatUtils::Forward(self->GetRotation());
auto backwards = forward * -1; auto backwards = forward * -1;
forward.x *= 10; forward.x *= 10;

View File

@@ -92,7 +92,7 @@ void FvMaelstromDragon::OnHitOrHealResult(Entity* self, Entity* attacker, int32_
self->AddTimer("timeToStunLoop", 1.0f); self->AddTimer("timeToStunLoop", 1.0f);
auto position = self->GetPosition(); auto position = self->GetPosition();
auto forward = self->GetRotation().GetForwardVector(); auto forward = QuatUtils::Forward(self->GetRotation());
auto backwards = forward * -1; auto backwards = forward * -1;
forward.x *= 10; forward.x *= 10;

View File

@@ -66,7 +66,7 @@ void BaseEnemyApe::OnTimerDone(Entity* self, std::string timerName) {
const auto position = self->GetPosition(); const auto position = self->GetPosition();
const auto rotation = self->GetRotation(); const auto rotation = self->GetRotation();
const auto backwardVector = rotation.GetForwardVector() * -1; const auto backwardVector = QuatUtils::Forward(rotation) * -1;
const auto objectPosition = NiPoint3( const auto objectPosition = NiPoint3(
position.GetX() - (backwardVector.GetX() * 8), position.GetX() - (backwardVector.GetX() * 8),
position.GetY(), position.GetY(),

View File

@@ -93,7 +93,7 @@ void AmDrawBridge::MoveBridgeDown(Entity* self, Entity* bridge, bool down) {
return; return;
} }
auto forwardVect = simplePhysicsComponent->GetRotation().GetForwardVector(); auto forwardVect = QuatUtils::Forward(simplePhysicsComponent->GetRotation());
auto degrees = down ? 90.0f : -90.0f; auto degrees = down ? 90.0f : -90.0f;

View File

@@ -129,7 +129,7 @@ void AmShieldGenerator::EnemyEnteredShield(Entity* self, Entity* intruder) {
return; return;
} }
auto dir = intruder->GetRotation().GetForwardVector() * -1; auto dir = QuatUtils::Forward(intruder->GetRotation()) * -1;
dir.y += 15; dir.y += 15;
dir.x *= 50; dir.x *= 50;
dir.z *= 50; dir.z *= 50;

View File

@@ -187,7 +187,7 @@ void AmShieldGeneratorQuickbuild::EnemyEnteredShield(Entity* self, Entity* intru
return; return;
} }
auto dir = intruder->GetRotation().GetForwardVector() * -1; auto dir = QuatUtils::Forward(intruder->GetRotation()) * -1;
dir.y += 15; dir.y += 15;
dir.x *= 50; dir.x *= 50;
dir.z *= 50; dir.z *= 50;

View File

@@ -46,23 +46,23 @@ void AmSkullkinTower::SpawnLegs(Entity* self, const std::string& loc) {
info.rot = newRot; info.rot = newRot;
if (loc == "Right") { if (loc == "Right") {
const auto dir = rot.GetForwardVector(); const auto dir = QuatUtils::Forward(rot);
pos.x += dir.x * offset; pos.x += dir.x * offset;
pos.z += dir.z * offset; pos.z += dir.z * offset;
info.pos = pos; info.pos = pos;
} else if (loc == "Rear") { } else if (loc == "Rear") {
const auto dir = rot.GetRightVector(); const auto dir = QuatUtils::Right(rot);
pos.x += dir.x * offset; pos.x += dir.x * offset;
pos.z += dir.z * offset; pos.z += dir.z * offset;
info.pos = pos; info.pos = pos;
} else if (loc == "Left") { } else if (loc == "Left") {
const auto dir = rot.GetForwardVector() * -1; const auto dir = QuatUtils::Forward(rot) * -1;
pos.x += dir.x * offset; pos.x += dir.x * offset;
pos.z += dir.z * offset; pos.z += dir.z * offset;
info.pos = pos; info.pos = pos;
} }
info.rot = NiQuaternion::LookAt(info.pos, self->GetPosition()); info.rot = QuatUtils::LookAt(info.pos, self->GetPosition());
auto* entity = Game::entityManager->CreateEntity(info, nullptr, self); auto* entity = Game::entityManager->CreateEntity(info, nullptr, self);

View File

@@ -21,7 +21,7 @@ void GfCaptainsCannon::OnUse(Entity* self, Entity* user) {
); );
auto position = self->GetPosition(); auto position = self->GetPosition();
auto forward = self->GetRotation().GetForwardVector(); auto forward = QuatUtils::Forward(self->GetRotation());
position.x += forward.x * -3; position.x += forward.x * -3;
position.z += forward.z * -3; position.z += forward.z * -3;

View File

@@ -65,7 +65,7 @@ void MastTeleport::OnTimerDone(Entity* self, std::string timerName) {
} else if (timerName == "PlayerAnimDone") { } else if (timerName == "PlayerAnimDone") {
GameMessages::SendStopFXEffect(player, true, "hook"); GameMessages::SendStopFXEffect(player, true, "hook");
auto forward = self->GetRotation().GetForwardVector(); auto forward = QuatUtils::Forward(self->GetRotation());
const auto degrees = -25.0f; const auto degrees = -25.0f;
@@ -81,7 +81,7 @@ void MastTeleport::OnTimerDone(Entity* self, std::string timerName) {
GameMessages::SendOrientToAngle(playerId, true, rads, player->GetSystemAddress()); GameMessages::SendOrientToAngle(playerId, true, rads, player->GetSystemAddress());
GameMessages::SendTeleport(playerId, position, NiQuaternionConstant::IDENTITY, player->GetSystemAddress()); GameMessages::SendTeleport(playerId, position, QuatUtils::IDENTITY, player->GetSystemAddress());
GameMessages::SendSetStunned(playerId, eStateChangeType::POP, player->GetSystemAddress(), GameMessages::SendSetStunned(playerId, eStateChangeType::POP, player->GetSystemAddress(),
LWOOBJID_EMPTY, true, true, true, true, true, true, true LWOOBJID_EMPTY, true, true, true, true, true, true, true

View File

@@ -45,13 +45,13 @@ void QbSpawner::OnTimerDone(Entity* self, std::string timerName) {
if (!gate) return; if (!gate) return;
auto oPos = gate->GetPosition(); auto oPos = gate->GetPosition();
auto oDir = gate->GetRotation().GetForwardVector(); auto oDir = QuatUtils::Forward(gate->GetRotation());
NiPoint3 newPos( NiPoint3 newPos(
oPos.x + (oDir.x * spawnDist), oPos.x + (oDir.x * spawnDist),
oPos.y, oPos.y,
oPos.z + (oDir.z * spawnDist) oPos.z + (oDir.z * spawnDist)
); );
auto newRot = NiQuaternion::LookAt(newPos, oPos); auto newRot = QuatUtils::LookAt(newPos, oPos);
for (int i = 0; i < mobTable.size(); i++) { for (int i = 0; i < mobTable.size(); i++) {
int posOffset = -10; int posOffset = -10;

View File

@@ -108,7 +108,7 @@ void NtCombatChallengeServer::OnChildLoaded(Entity& self, GameMessages::ChildLoa
auto* const child = Game::entityManager->GetEntity(childLoaded.childID); auto* const child = Game::entityManager->GetEntity(childLoaded.childID);
if (child) { if (child) {
child->SetRotation(NiQuaternion::FromEulerAngles(child->GetRotation().GetEulerAngles() += NiPoint3(0, PI, 0))); // rotate 180 degrees child->SetRotation(QuatUtils::FromEuler(QuatUtils::Euler(child->GetRotation()) += NiPoint3(0, PI, 0))); // rotate 180 degrees
} }
self.SetVar(u"currentTargetID", child->GetObjectID()); self.SetVar(u"currentTargetID", child->GetObjectID());

View File

@@ -48,7 +48,7 @@ void NtParadoxPanelServer::OnUse(Entity* self, Entity* user) {
RenderComponent::PlayAnimation(user, shockAnim); RenderComponent::PlayAnimation(user, shockAnim);
const auto dir = self->GetRotation().GetRightVector(); const auto dir = QuatUtils::Right(self->GetRotation());
GameMessages::SendKnockback(user->GetObjectID(), self->GetObjectID(), self->GetObjectID(), 0, { dir.x * 15, 5, dir.z * 15 }); GameMessages::SendKnockback(user->GetObjectID(), self->GetObjectID(), self->GetObjectID(), 0, { dir.x * 15, 5, dir.z * 15 });

View File

@@ -18,7 +18,7 @@ void NtSentinelWalkwayServer::OnStartup(Entity* self) {
force = 115; force = 115;
} }
const auto forward = self->GetRotation().GetRightVector() * -1; const auto forward = QuatUtils::Right(self->GetRotation()) * -1;
phantomPhysicsComponent->SetEffectType(ePhysicsEffectType::PUSH); phantomPhysicsComponent->SetEffectType(ePhysicsEffectType::PUSH);
phantomPhysicsComponent->SetDirectionalMultiplier(force); phantomPhysicsComponent->SetDirectionalMultiplier(force);

View File

@@ -27,7 +27,7 @@ void FlameJetServer::OnCollisionPhantom(Entity* self, Entity* target) {
skillComponent->CalculateBehavior(726, 11723, target->GetObjectID(), true); skillComponent->CalculateBehavior(726, 11723, target->GetObjectID(), true);
auto dir = target->GetRotation().GetForwardVector(); auto dir = QuatUtils::Forward(target->GetRotation());
dir.y = 25; dir.y = 25;
dir.x = -dir.x * 15; dir.x = -dir.x * 15;

View File

@@ -36,7 +36,7 @@ void AgSpiderBossMessage::OnCollisionPhantom(Entity* self, Entity* target) {
auto box = GetBox(self); auto box = GetBox(self);
// knockback the target // knockback the target
auto forward = self->GetRotation().GetForwardVector(); auto forward = QuatUtils::Forward(self->GetRotation());
box.boxTarget = target->GetObjectID(); box.boxTarget = target->GetObjectID();
GameMessages::SendPlayFXEffect(target->GetObjectID(), 1378, u"create", "pushBack"); GameMessages::SendPlayFXEffect(target->GetObjectID(), 1378, u"create", "pushBack");
RenderComponent::PlayAnimation(target, "knockback-recovery"); RenderComponent::PlayAnimation(target, "knockback-recovery");

View File

@@ -10,8 +10,8 @@ void GfBanana::SpawnBanana(Entity* self) {
const auto rotation = self->GetRotation(); const auto rotation = self->GetRotation();
position.y += 12; position.y += 12;
position.x -= rotation.GetRightVector().x * 5; position.x -= QuatUtils::Right(rotation).x * 5;
position.z -= rotation.GetRightVector().z * 5; position.z -= QuatUtils::Right(rotation).z * 5;
EntityInfo info{}; EntityInfo info{};

View File

@@ -22,7 +22,7 @@ struct SGEnemy {
struct SGConstants { struct SGConstants {
Vector3 playerStartPosition; Vector3 playerStartPosition;
Quaternion playerStartRotation; Quaternion playerStartRotation = QuatUtils::IDENTITY;
LOT cannonLot; LOT cannonLot;
uint32_t impactSkillID; uint32_t impactSkillID;
LOT projectileLot; LOT projectileLot;

View File

@@ -273,7 +273,7 @@ void NsConcertInstrument::RepositionPlayer(Entity* self, Entity* player) {
case Keyboard: case Keyboard:
position.SetX(position.GetX() - 0.45f); position.SetX(position.GetX() - 0.45f);
position.SetZ(position.GetZ() + 0.75f); position.SetZ(position.GetZ() + 0.75f);
rotation = NiQuaternion::CreateFromAxisAngle(position, -0.8f); // Slight rotation to make the animation sensible rotation = QuatUtils::AxisAngle(position, -0.8f); // Slight rotation to make the animation sensible
break; break;
case Drum: case Drum:
position.SetZ(position.GetZ() - 0.5f); position.SetZ(position.GetZ() - 0.5f);

View File

@@ -10,7 +10,7 @@ void WblRobotCitizen::OnStartup(Entity* self) {
void WblRobotCitizen::OnUse(Entity* self, Entity* user) { void WblRobotCitizen::OnUse(Entity* self, Entity* user) {
auto movementAIComponent = self->GetComponent<MovementAIComponent>(); auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (movementAIComponent) movementAIComponent->Pause(); if (movementAIComponent) movementAIComponent->Pause();
auto face = NiQuaternion::LookAt(self->GetPosition(), user->GetPosition()); auto face = QuatUtils::LookAt(self->GetPosition(), user->GetPosition());
self->SetRotation(face); self->SetRotation(face);
auto timer = RenderComponent::PlayAnimation(self, "wave", 0.4f); auto timer = RenderComponent::PlayAnimation(self, "wave", 0.4f);
self->AddTimer("animation time", timer); self->AddTimer("animation time", timer);

View File

@@ -1048,7 +1048,7 @@ void HandlePacket(Packet* packet) {
Game::entityManager->ConstructEntity(player, UNASSIGNED_SYSTEM_ADDRESS); Game::entityManager->ConstructEntity(player, UNASSIGNED_SYSTEM_ADDRESS);
if (respawnPoint != NiPoint3Constant::ZERO) { if (respawnPoint != NiPoint3Constant::ZERO) {
GameMessages::SendPlayerReachedRespawnCheckpoint(player, respawnPoint, NiQuaternionConstant::IDENTITY); GameMessages::SendPlayerReachedRespawnCheckpoint(player, respawnPoint, QuatUtils::IDENTITY);
} }
Game::entityManager->ConstructAllEntities(packet->systemAddress); Game::entityManager->ConstructAllEntities(packet->systemAddress);

View File

@@ -13,7 +13,7 @@
struct SpawnerNode { struct SpawnerNode {
NiPoint3 position = NiPoint3Constant::ZERO; NiPoint3 position = NiPoint3Constant::ZERO;
NiQuaternion rotation = NiQuaternionConstant::IDENTITY; NiQuaternion rotation = QuatUtils::IDENTITY;
uint32_t nodeID = 0; uint32_t nodeID = 0;
uint32_t nodeMax = 1; uint32_t nodeMax = 1;
std::vector<LWOOBJID> entities; std::vector<LWOOBJID> entities;

View File

@@ -71,7 +71,7 @@ struct RacingPathWaypoint {
struct PathWaypoint { struct PathWaypoint {
NiPoint3 position; NiPoint3 position;
NiQuaternion rotation; // not included in all, but it's more convenient here NiQuaternion rotation = QuatUtils::IDENTITY; // not included in all, but it's more convenient here
MovingPlatformPathWaypoint movingPlatform; MovingPlatformPathWaypoint movingPlatform;
CameraPathWaypoint camera; CameraPathWaypoint camera;
RacingPathWaypoint racing; RacingPathWaypoint racing;
@@ -237,7 +237,7 @@ private:
uint32_t m_CheckSum; uint32_t m_CheckSum;
uint32_t m_WorldID; //should be equal to the MapID uint32_t m_WorldID; //should be equal to the MapID
NiPoint3 m_Spawnpoint; NiPoint3 m_Spawnpoint;
NiQuaternion m_SpawnpointRotation; NiQuaternion m_SpawnpointRotation = QuatUtils::IDENTITY;
uint32_t m_SceneCount; uint32_t m_SceneCount;
std::string m_ZonePath; //Path to the .luz's folder std::string m_ZonePath; //Path to the .luz's folder

View File

@@ -11,7 +11,7 @@ struct SceneObject {
uint32_t nodeType; uint32_t nodeType;
uint32_t glomId; uint32_t glomId;
NiPoint3 position; NiPoint3 position;
NiQuaternion rotation; NiQuaternion rotation = QuatUtils::IDENTITY;
float scale = 1.0f; float scale = 1.0f;
//std::string settings; //std::string settings;
uint32_t value3; uint32_t value3;

View File

@@ -116,6 +116,7 @@ These commands are primarily for development and testing. The usage of many of t
|setrewardcode|`/setrewardcode <code>`|Sets the rewardcode for the account you are logged into if it's a valid rewardcode, See cdclient table `RewardCodes`|8| |setrewardcode|`/setrewardcode <code>`|Sets the rewardcode for the account you are logged into if it's a valid rewardcode, See cdclient table `RewardCodes`|8|
|barfight|`/barfight start`|Starts a barfight (turns everyones pvp on)|8| |barfight|`/barfight start`|Starts a barfight (turns everyones pvp on)|8|
|despawn|`/despawn <objectID>`|Despawns the entity objectID IF it was spawned in through a slash command.|8| |despawn|`/despawn <objectID>`|Despawns the entity objectID IF it was spawned in through a slash command.|8|
|execute|`/execute <subcommand> ... run <command>`|Execute commands with modified context (Minecraft-style). Subcommands: `as <playername>` (execute as different player), `at <playername>` (execute from player's position), `positioned <x> <y> <z>` (execute from coordinates - supports absolute coordinates like `100 200 300` or relative coordinates like `~5 ~10 ~` where `~` means current position). Example: `/execute as Player1 run pos`, `/execute positioned ~5 ~ ~-3 run spawn 1234`|8|
|crash|`/crash`|Crashes the server.|9| |crash|`/crash`|Crashes the server.|9|
|rollloot|`/rollloot <loot matrix index> <item id> <amount>`|Given a `loot matrix index`, look for `item id` in that matrix `amount` times and print to the chat box statistics of rolling that loot matrix.|9| |rollloot|`/rollloot <loot matrix index> <item id> <amount>`|Given a `loot matrix index`, look for `item id` in that matrix `amount` times and print to the chat box statistics of rolling that loot matrix.|9|
|castskill|`/castskill <skill id>`|Casts the skill as the player|9| |castskill|`/castskill <skill id>`|Casts the skill as the player|9|

View File

@@ -30,7 +30,7 @@ class GameDependenciesTest : public ::testing::Test {
protected: protected:
void SetUpDependencies() { void SetUpDependencies() {
info.pos = NiPoint3Constant::ZERO; info.pos = NiPoint3Constant::ZERO;
info.rot = NiQuaternionConstant::IDENTITY; info.rot = QuatUtils::IDENTITY;
info.scale = 1.0f; info.scale = 1.0f;
info.spawner = nullptr; info.spawner = nullptr;
info.lot = 999; info.lot = 999;

View File

@@ -40,13 +40,16 @@ add_subdirectory(magic_enum)
# Create our third party library objects # Create our third party library objects
add_subdirectory(raknet) add_subdirectory(raknet)
include(FetchContent)
# Download Backtrace if configured # Download Backtrace if configured
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
include(FetchContent)
if (${INCLUDE_BACKTRACE} AND ${COMPILE_BACKTRACE}) if (${INCLUDE_BACKTRACE} AND ${COMPILE_BACKTRACE})
FetchContent_Declare( FetchContent_Declare(
backtrace backtrace
GIT_REPOSITORY https://github.com/ianlancetaylor/libbacktrace.git GIT_REPOSITORY https://github.com/ianlancetaylor/libbacktrace.git
GIT_PROGRESS TRUE
GIT_SHALLOW 1
) )
FetchContent_MakeAvailable(backtrace) FetchContent_MakeAvailable(backtrace)
@@ -64,6 +67,16 @@ if(UNIX AND NOT APPLE)
endif() endif()
endif() endif()
FetchContent_Declare(
glm
GIT_REPOSITORY https://github.com/g-truc/glm.git
GIT_TAG bf71a834948186f4097caa076cd2663c69a10e1e #refs/tags/1.0.1
GIT_PROGRESS TRUE
GIT_SHALLOW 1
)
FetchContent_MakeAvailable(glm)
add_subdirectory(MD5) add_subdirectory(MD5)
add_subdirectory(mongoose) add_subdirectory(mongoose)