mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-23 00:42:27 +00:00
Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 11a8e46ee8 | |||
| 7d98b10c18 | |||
| 65330af50e | |||
| ef6dfe0469 | |||
| d7e010a3ec | |||
| 99f99c8b8e | |||
| e5ce882b9d | |||
| 18df055f16 | |||
| c253734c57 | |||
| 85c3255568 | |||
| 9cc3d492ce | |||
| c6ddf300a4 | |||
| 42d6004467 | |||
| cabe06ea92 | |||
| 4abd9c1b40 | |||
| accda849ac | |||
| f72bbab0e9 | |||
| 9647a5b82c | |||
| 84a90cef23 | |||
| a8db057440 | |||
| 743fd45b17 | |||
| 6d6cc8ca95 | |||
| 6694281f22 | |||
| 0ada77f340 | |||
| 9c8107ce96 | |||
| 4581059e78 | |||
| dceec36fad | |||
| 3035e906fe | |||
| 5dc093fe5e | |||
| 758774b0bf | |||
| 1958a12bc7 | |||
| 348094b881 | |||
| 0e0162edc0 | |||
| af06fb703c | |||
| c5d089de68 | |||
| b8ee811ac6 | |||
| 08cdd8234d | |||
| 3bb7f94713 | |||
| ec5a9d0bd4 | |||
| 2da6d3f37c | |||
| 6a7baf8f1c | |||
| a8e3ab41e1 | |||
| fe4146050f | |||
| 36ea946255 | |||
| f29d87aced | |||
| 767f04731b | |||
| 8e7964b835 | |||
| a9333fb51b | |||
| 27ad857ee5 | |||
| 5549daedb1 | |||
| b5cc8dfab1 | |||
| 865f619e21 | |||
| a4785d30e0 | |||
| a54711817d | |||
| 139575661d | |||
| 8dd24f4a70 | |||
| 517d9419a7 | |||
| a789b22fc7 | |||
| 6e1fe45090 | |||
| 492d848f6a | |||
| ce5e216be9 | |||
| 48e0847f21 | |||
| 7f42add39b | |||
| 49161a618f | |||
| aac7bbf48a | |||
| 2c4d82f1b9 | |||
| dea5031d83 | |||
| 30c9c6317f | |||
| c7eea72997 | |||
| ba2ca5eada | |||
| 28e6ef29d4 | |||
| a6f4438c0d | |||
| e5a111d8d8 | |||
| 491b1edd12 | |||
| abbaf6f9a1 | |||
| ccdc9f2e43 | |||
| a9effc7bac | |||
| a2b3b36cf1 |
@@ -20,3 +20,4 @@
|
||||
*.css text
|
||||
*.js text
|
||||
*.types text
|
||||
*.pdf binary
|
||||
|
||||
@@ -3,8 +3,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
name: Linux
|
||||
@@ -25,10 +25,21 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential ninja-build ccache uuid-dev
|
||||
|
||||
- name: Restore vcpkg Cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
build/vcpkg_installed
|
||||
submodules/vcpkg/downloads
|
||||
key: ${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }}-
|
||||
${{ runner.os }}-vcpkg-
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DEQEMU_BUILD_TESTS=ON \
|
||||
@@ -47,6 +58,9 @@ jobs:
|
||||
windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
VCPKG_DOWNLOADS: ${{ github.workspace }}\submodules\vcpkg\downloads
|
||||
VCPKG_BINARY_SOURCES: 'clear;files,${{ github.workspace }}\vcpkg_archives,readwrite'
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v5
|
||||
@@ -57,15 +71,25 @@ jobs:
|
||||
run: git config --global core.longpaths true
|
||||
|
||||
- name: Setup MSVC environment
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
uses: TheMrMilchmann/setup-msvc-dev@v4
|
||||
with:
|
||||
arch: x64
|
||||
|
||||
- name: Restore vcpkg Cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
${{ env.VCPKG_DOWNLOADS }}
|
||||
${{ github.workspace }}/vcpkg_archives
|
||||
key: ${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-vcpkg-
|
||||
|
||||
- name: Configure
|
||||
shell: pwsh
|
||||
run: |
|
||||
cmake -S . -B build -G "Visual Studio 17 2022" -A x64 `
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DEQEMU_BUILD_TESTS=ON `
|
||||
-DEQEMU_BUILD_LOGIN=ON `
|
||||
-DEQEMU_BUILD_LUA=ON `
|
||||
@@ -74,8 +98,8 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
shell: pwsh
|
||||
run: cmake --build build --config RelWithDebInfo --target ALL_BUILD -- /m
|
||||
run: cmake --build build --config Release --target ALL_BUILD -- /m
|
||||
|
||||
- name: Test
|
||||
working-directory: build
|
||||
run: ./bin/RelWithDebInfo/tests.exe
|
||||
run: ./bin/Release/tests.exe
|
||||
|
||||
+23
-18
@@ -18,43 +18,48 @@ if(NOT CMAKE_TOOLCHAIN_FILE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Uncomment to generate just the standard Debug and Release configurations
|
||||
#set(CMAKE_BUILD_TYPE "Debug")
|
||||
#set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
set(dummy "${CMAKE_C_COMPILER}")
|
||||
|
||||
project(EQEmu
|
||||
VERSION 24.10.3
|
||||
LANGUAGES CXX
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
#explicitly set CMP0167 for Find Boost
|
||||
if(POLICY CMP0167)
|
||||
cmake_policy(SET CMP0167 NEW)
|
||||
cmake_policy(SET CMP0167 NEW)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Build type")
|
||||
endif()
|
||||
option(EQEMU_BUILD_PCH "Build with precompiled headers" ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
option(EQEMU_BUILD_PCH "Build with precompiled headers (Windows)" ON)
|
||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(/bigobj)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS NOMINMAX CRASH_LOGGING _HAS_AUTO_PTR_ETC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
# Always build with debug symbols
|
||||
add_compile_options("$<$<CONFIG:RELEASE>:/Zi>")
|
||||
add_link_options("$<$<CONFIG:RELEASE>:/DEBUG /OPT:REF /OPT:ICF>")
|
||||
|
||||
option(EQEMU_DISABLE_MSVC_WARNINGS "Disable MSVC compile warnings." ON)
|
||||
if(EQEMU_DISABLE_MSVC_WARNINGS)
|
||||
add_compile_options(/W0 /wd4005 /wd4996 /nologo /Os)
|
||||
endif()
|
||||
add_compile_options(/bigobj /MP)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS NOMINMAX WIN32_LEAN_AND_MEAN CRASH_LOGGING)
|
||||
|
||||
option(EQEMU_MSVC_DEBUG_CRT "Use the debug CRT on MSVC debug builds" OFF)
|
||||
if(EQEMU_MSVC_DEBUG_CRT)
|
||||
add_compile_definitions(_CRT_DBG_MAP_ALLOC)
|
||||
endif()
|
||||
else()
|
||||
add_compile_options("$<$<CONFIG:RELEASE>:-g>")
|
||||
|
||||
add_compile_definitions(HAS_UNION_SEMUN)
|
||||
endif()
|
||||
|
||||
@@ -71,7 +76,7 @@ if(UNIX)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Boost REQUIRED COMPONENTS dynamic_bitset foreach tuple)
|
||||
find_package(Boost REQUIRED COMPONENTS dynamic_bitset foreach tuple CONFIG REQUIRED)
|
||||
find_package(cereal CONFIG REQUIRED)
|
||||
find_package(fmt CONFIG REQUIRED)
|
||||
find_package(glm CONFIG REQUIRED)
|
||||
|
||||
+73
-48
@@ -1,49 +1,74 @@
|
||||
{
|
||||
"version": 3,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 19,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "linux-debug",
|
||||
"displayName": "Linux Debug",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
|
||||
"CMAKE_C_COMPILER_LAUNCHER": "ccache",
|
||||
"CMAKE_CXX_COMPILER_LAUNCHER": "ccache",
|
||||
"EQEMU_BUILD_LOGIN": "ON",
|
||||
"EQEMU_BUILD_TESTS": "ON",
|
||||
"EQEMU_ADD_PROFILER": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-release",
|
||||
"displayName": "Linux Release",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build/release",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"CMAKE_C_COMPILER_LAUNCHER": "ccache",
|
||||
"CMAKE_CXX_COMPILER_LAUNCHER": "ccache",
|
||||
"EQEMU_BUILD_LOGIN": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "win-msvc",
|
||||
"displayName": "Windows MSVC (VS 2022)",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"architecture": { "value": "x64" },
|
||||
"cacheVariables": {
|
||||
"CMAKE_CONFIGURATION_TYPES": "Debug;Release",
|
||||
"EQEMU_BUILD_LOGIN": "ON",
|
||||
"EQEMU_BUILD_TESTS": "ON"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"version": 3,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 20,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "linux",
|
||||
"displayName": "Linux-Ninja",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"cacheVariables": {
|
||||
"EQEMU_BUILD_TESTS": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-clang",
|
||||
"displayName": "Linux-Ninja (Clang)",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"cacheVariables": {
|
||||
"EQEMU_BUILD_TESTS": "ON",
|
||||
"CMAKE_C_COMPILER": "clang",
|
||||
"CMAKE_CXX_COMPILER": "clang++"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-ccache",
|
||||
"displayName": "Linux-Ninja (Ccache)",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"cacheVariables": {
|
||||
"CMAKE_C_COMPILER_LAUNCHER": "ccache",
|
||||
"CMAKE_CXX_COMPILER_LAUNCHER": "ccache",
|
||||
"EQEMU_BUILD_TESTS": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-ccache",
|
||||
"displayName": "Linux-Ninja (Clang, Ccache)",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"cacheVariables": {
|
||||
"CMAKE_C_COMPILER_LAUNCHER": "ccache",
|
||||
"CMAKE_CXX_COMPILER_LAUNCHER": "ccache",
|
||||
"EQEMU_BUILD_TESTS": "ON",
|
||||
"CMAKE_C_COMPILER": "clang",
|
||||
"CMAKE_CXX_COMPILER": "clang++"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "win-msvc-2022",
|
||||
"displayName": "Windows MSVC (VS 2022)",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"architecture": { "value": "x64" },
|
||||
"cacheVariables": {
|
||||
"EQEMU_BUILD_TESTS": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "win-msvc-2026",
|
||||
"displayName": "Windows MSVC (VS 2026)",
|
||||
"generator": "Visual Studio 18 2026",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"architecture": { "value": "x64" },
|
||||
"cacheVariables": {
|
||||
"EQEMU_BUILD_TESTS": "ON"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
add_subdirectory(import)
|
||||
add_subdirectory(export)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
set(export_sources
|
||||
main.cpp
|
||||
)
|
||||
@@ -12,7 +10,6 @@ add_executable(export_client_files ${export_sources} ${export_headers})
|
||||
install(TARGETS export_client_files RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
target_link_libraries(export_client_files common)
|
||||
target_include_directories(export_client_files PRIVATE ../..)
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
set_property(TARGET export_client_files PROPERTY FOLDER executables/tools)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
set(import_sources
|
||||
main.cpp
|
||||
)
|
||||
@@ -12,7 +10,6 @@ add_executable(import_client_files ${import_sources} ${import_headers})
|
||||
install(TARGETS import_client_files RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
target_link_libraries(import_client_files common)
|
||||
target_include_directories(import_client_files PRIVATE ../..)
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
set_property(TARGET import_client_files PROPERTY FOLDER executables/tools)
|
||||
|
||||
+757
-768
File diff suppressed because it is too large
Load Diff
@@ -359,7 +359,7 @@ public:
|
||||
BOOL Publics; // contains public symbols
|
||||
};
|
||||
*/
|
||||
typedef struct IMAGEHLP_MODULE64_V2 {
|
||||
struct IMAGEHLP_MODULE64_V2 {
|
||||
DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
|
||||
DWORD64 BaseOfImage; // base load address of module
|
||||
DWORD ImageSize; // virtual size of the loaded module
|
||||
|
||||
@@ -134,7 +134,7 @@ protected:
|
||||
CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
|
||||
} CallstackEntry;
|
||||
|
||||
typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
|
||||
enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
|
||||
|
||||
virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
|
||||
virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
|
||||
|
||||
+14
-24
@@ -19,44 +19,34 @@
|
||||
#include "common/misc.h"
|
||||
#include "common/packet_dump.h"
|
||||
|
||||
BasePacket::BasePacket(const unsigned char *buf, uint32 len)
|
||||
BasePacket::BasePacket(const unsigned char* buf, size_t len)
|
||||
{
|
||||
pBuffer=nullptr;
|
||||
size=0;
|
||||
_wpos = 0;
|
||||
_rpos = 0;
|
||||
timestamp.tv_sec = 0;
|
||||
if (len>0) {
|
||||
size=len;
|
||||
pBuffer= new unsigned char[len];
|
||||
if (len > 0) {
|
||||
size = static_cast<uint32>(len);
|
||||
pBuffer = new unsigned char[len];
|
||||
if (buf) {
|
||||
memcpy(pBuffer,buf,len);
|
||||
} else {
|
||||
memset(pBuffer,0,len);
|
||||
memcpy(pBuffer, buf, len);
|
||||
}
|
||||
else {
|
||||
memset(pBuffer, 0, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasePacket::BasePacket(SerializeBuffer &buf)
|
||||
BasePacket::BasePacket(SerializeBuffer&& buf)
|
||||
: pBuffer(std::exchange(buf.m_buffer, nullptr))
|
||||
{
|
||||
pBuffer = buf.m_buffer;
|
||||
buf.m_buffer = nullptr;
|
||||
size = buf.m_pos;
|
||||
buf.m_pos = 0;
|
||||
// We are essentially taking ownership of this serialize buffer.
|
||||
size = static_cast<uint32>(std::exchange(buf.m_pos, 0));
|
||||
buf.m_capacity = 0;
|
||||
_wpos = 0;
|
||||
_rpos = 0;
|
||||
timestamp.tv_sec = 0;
|
||||
}
|
||||
|
||||
BasePacket::~BasePacket()
|
||||
{
|
||||
if (pBuffer)
|
||||
delete[] pBuffer;
|
||||
pBuffer=nullptr;
|
||||
delete[] pBuffer;
|
||||
pBuffer = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void BasePacket::build_raw_header_dump(char *buffer, uint16 seq) const
|
||||
{
|
||||
if (timestamp.tv_sec) {
|
||||
|
||||
+23
-15
@@ -23,14 +23,26 @@
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
class BasePacket {
|
||||
class BasePacket
|
||||
{
|
||||
protected:
|
||||
BasePacket() = default;
|
||||
BasePacket(const unsigned char* buf, size_t len);
|
||||
BasePacket(SerializeBuffer&& buf);
|
||||
|
||||
virtual ~BasePacket();
|
||||
|
||||
public:
|
||||
unsigned char *pBuffer;
|
||||
uint32 size, _wpos, _rpos;
|
||||
uint32 src_ip,dst_ip;
|
||||
uint16 src_port,dst_port;
|
||||
uint32 priority;
|
||||
timeval timestamp;
|
||||
unsigned char* pBuffer = nullptr;
|
||||
uint32 size = 0;
|
||||
uint32 _wpos = 0;
|
||||
uint32 _rpos = 0;
|
||||
uint32 src_ip = 0;
|
||||
uint32 dst_ip = 0;
|
||||
uint16 src_port = 0;
|
||||
uint16 dst_port = 0;
|
||||
uint32 priority = 0;
|
||||
timeval timestamp{};
|
||||
|
||||
virtual void build_raw_header_dump(char *buffer, uint16 seq=0xffff) const;
|
||||
virtual void build_header_dump(char *buffer) const;
|
||||
@@ -40,16 +52,17 @@ public:
|
||||
|
||||
void setSrcInfo(uint32 sip, uint16 sport) { src_ip=sip; src_port=sport; }
|
||||
void setDstInfo(uint32 dip, uint16 dport) { dst_ip=dip; dst_port=dport; }
|
||||
void setTimeInfo(uint32 ts_sec, uint32 ts_usec) { timestamp.tv_sec=ts_sec; timestamp.tv_usec=ts_usec; }
|
||||
void setTimeInfo(uint32 ts_sec, uint32 ts_usec) { timestamp.tv_sec = ts_sec; timestamp.tv_usec = ts_usec; }
|
||||
void copyInfo(const BasePacket *p) { src_ip=p->src_ip; src_port=p->src_port; dst_ip=p->dst_ip; dst_port=p->dst_port; timestamp.tv_sec=p->timestamp.tv_sec; timestamp.tv_usec=p->timestamp.tv_usec; }
|
||||
|
||||
inline bool operator<(const BasePacket &rhs) {
|
||||
return (timestamp.tv_sec < rhs.timestamp.tv_sec || (timestamp.tv_sec==rhs.timestamp.tv_sec && timestamp.tv_usec < rhs.timestamp.tv_usec));
|
||||
return (timestamp.tv_sec < rhs.timestamp.tv_sec || (timestamp.tv_sec == rhs.timestamp.tv_sec && timestamp.tv_usec < rhs.timestamp.tv_usec));
|
||||
}
|
||||
|
||||
void WriteUInt8(uint8 value) { *(uint8 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint8); }
|
||||
void WriteUInt32(uint32 value) { *(uint32 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint32); }
|
||||
void WriteUInt64(uint64 value) { *(uint64 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint64); }
|
||||
void WriteSInt16(int32 value) { *(int16*)(pBuffer + _wpos) = value; _wpos += sizeof(int16); }
|
||||
void WriteUInt16(uint32 value) { *(uint16 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint16); }
|
||||
void WriteSInt32(int32 value) { *(int32 *)(pBuffer + _wpos) = value; _wpos += sizeof(int32); }
|
||||
void WriteFloat(float value) { *(float *)(pBuffer + _wpos) = value; _wpos += sizeof(float); }
|
||||
@@ -68,17 +81,12 @@ public:
|
||||
void ReadString(char *str) { uint32 len = static_cast<uint32>(strlen((char *)(pBuffer + _rpos))) + 1; memcpy(str, pBuffer + _rpos, len); _rpos += len; }
|
||||
void ReadString(std::string &str) { str = reinterpret_cast<char *>(pBuffer + _rpos); _rpos += str.length() + 1; }
|
||||
void ReadString(char *str, uint32 Offset, uint32 MaxLength) const;
|
||||
void ReadLengthString(std::string& str) { uint32 len = *(uint32*)(pBuffer + _rpos); _rpos += sizeof(uint32); str.resize(len); memcpy(&str[0], pBuffer + _rpos, len); _rpos += len; }
|
||||
|
||||
uint32 GetWritePosition() { return _wpos; }
|
||||
uint32 GetReadPosition() { return _rpos; }
|
||||
void SetWritePosition(uint32 Newwpos) { _wpos = Newwpos; }
|
||||
void SetReadPosition(uint32 Newrpos) { _rpos = Newrpos; }
|
||||
|
||||
protected:
|
||||
virtual ~BasePacket();
|
||||
BasePacket() { pBuffer=nullptr; size=0; _wpos = 0; _rpos = 0; }
|
||||
BasePacket(const unsigned char *buf, const uint32 len);
|
||||
BasePacket(SerializeBuffer &buf);
|
||||
};
|
||||
|
||||
extern void DumpPacketHex(const BasePacket* app);
|
||||
|
||||
@@ -71,6 +71,9 @@ namespace Class {
|
||||
constexpr uint8 FellowshipMaster = 69;
|
||||
constexpr uint8 AlternateCurrencyMerchant = 70;
|
||||
constexpr uint8 MercenaryLiaison = 71;
|
||||
constexpr uint8 RealEstateMerchant = 72;
|
||||
constexpr uint8 LoyaltyMerchant = 73;
|
||||
constexpr uint8 TributeMaster2 = 74;
|
||||
|
||||
constexpr uint8 PLAYER_CLASS_COUNT = 16;
|
||||
constexpr uint16 ALL_CLASSES_BITMASK = 65535;
|
||||
|
||||
@@ -23,12 +23,9 @@
|
||||
|
||||
namespace EQEmuCommand {
|
||||
|
||||
std::map<std::string, void (*)(
|
||||
int argc,
|
||||
char **argv,
|
||||
argh::parser &cmd,
|
||||
std::string &description
|
||||
)> function_map;
|
||||
using CommandFunction = void(*)(int argc, char** argv, argh::parser& cmd, std::string& description);
|
||||
|
||||
std::map<std::string, CommandFunction> function_map;
|
||||
|
||||
/**
|
||||
* @param cmd
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
# include <unistd.h>
|
||||
#elif defined(TERMCOLOR_OS_WINDOWS)
|
||||
# include <io.h>
|
||||
# include <windows.h>
|
||||
# include "common/platform/win/include_windows.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define PUSH_DISABLE_DEPRECATED_WARNINGS() __pragma(warning(push)) \
|
||||
__pragma(warning(disable:4996))
|
||||
#define POP_DISABLE_DEPRECATED_WARNINGS() __pragma(warning(pop))
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define PUSH_DISABLE_DEPRECATED_WARNINGS() _Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
|
||||
#define POP_DISABLE_DEPRECATED_WARNINGS() _Pragma("GCC diagnostic pop")
|
||||
#else
|
||||
#define PUSH_DISABLE_DEPRECATED_WARNINGS()
|
||||
#define POP_DISABLE_DEPRECATED_WARNINGS()
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#define UNREACHABLE() __assume(0)
|
||||
#else
|
||||
#define UNREACHABLE() __builtin_unreachable()
|
||||
#endif
|
||||
@@ -1,146 +0,0 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "condition.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
|
||||
Condition::Condition()
|
||||
{
|
||||
m_events[SignalEvent] = CreateEvent (nullptr, // security
|
||||
FALSE, // is auto-reset event?
|
||||
FALSE, // is signaled initially?
|
||||
nullptr); // name
|
||||
m_events[BroadcastEvent] = CreateEvent (nullptr, // security
|
||||
TRUE, // is auto-reset event?
|
||||
FALSE, // is signaled initially?
|
||||
nullptr); // name
|
||||
m_waiters = 0;
|
||||
InitializeCriticalSection(&CSMutex);
|
||||
}
|
||||
|
||||
Condition::~Condition()
|
||||
{
|
||||
DeleteCriticalSection(&CSMutex);
|
||||
CloseHandle(m_events[SignalEvent]);
|
||||
CloseHandle(m_events[BroadcastEvent]);
|
||||
}
|
||||
|
||||
void Condition::Signal()
|
||||
{
|
||||
EnterCriticalSection(&CSMutex);
|
||||
if(m_waiters > 0)
|
||||
SetEvent(m_events[SignalEvent]);
|
||||
LeaveCriticalSection(&CSMutex);
|
||||
}
|
||||
|
||||
void Condition::SignalAll()
|
||||
{
|
||||
EnterCriticalSection(&CSMutex);
|
||||
if(m_waiters > 0)
|
||||
SetEvent(m_events[BroadcastEvent]);
|
||||
LeaveCriticalSection(&CSMutex);
|
||||
}
|
||||
|
||||
void Condition::Wait()
|
||||
{
|
||||
EnterCriticalSection(&CSMutex);
|
||||
|
||||
m_waiters++;
|
||||
|
||||
|
||||
LeaveCriticalSection(&CSMutex);
|
||||
int result = WaitForMultipleObjects (_eventCount, m_events, FALSE, INFINITE);
|
||||
EnterCriticalSection(&CSMutex);
|
||||
|
||||
m_waiters--;
|
||||
|
||||
//see if we are the last person waiting on the condition, and there was a broadcast
|
||||
//if so, we need to reset the broadcast event.
|
||||
if(m_waiters == 0 && result == (WAIT_OBJECT_0+BroadcastEvent))
|
||||
ResetEvent(m_events[BroadcastEvent]);
|
||||
|
||||
LeaveCriticalSection(&CSMutex);
|
||||
}
|
||||
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
|
||||
Condition::Condition()
|
||||
{
|
||||
pthread_cond_init(&cond,nullptr);
|
||||
pthread_mutex_init(&mutex,nullptr);
|
||||
}
|
||||
|
||||
void Condition::Signal()
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
pthread_cond_signal(&cond);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
void Condition::SignalAll()
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
pthread_cond_broadcast(&cond);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
void Condition::Wait()
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
pthread_cond_wait(&cond,&mutex);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
I commented this specifically because I think it might be very
|
||||
difficult to write a windows counterpart to it, so I would like
|
||||
to discourage its use until we can confirm that it can be reasonably
|
||||
implemented on windows.
|
||||
|
||||
bool Condition::TimedWait(unsigned long usec)
|
||||
{
|
||||
struct timeval now;
|
||||
struct timespec timeout;
|
||||
int retcode=0;
|
||||
pthread_mutex_lock(&mutex);
|
||||
gettimeofday(&now,nullptr);
|
||||
now.tv_usec+=usec;
|
||||
timeout.tv_sec = now.tv_sec + (now.tv_usec/1000000);
|
||||
timeout.tv_nsec = (now.tv_usec%1000000) *1000;
|
||||
//cout << "now=" << now.tv_sec << "."<<now.tv_usec << endl;
|
||||
//cout << "timeout=" << timeout.tv_sec << "."<<timeout.tv_nsec << endl;
|
||||
retcode=pthread_cond_timedwait(&cond,&mutex,&timeout);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
return retcode!=ETIMEDOUT;
|
||||
}
|
||||
*/
|
||||
|
||||
Condition::~Condition()
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
pthread_cond_destroy(&cond);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -132,7 +132,7 @@ void WorldContentService::SetContentFlags(const std::vector<ContentFlagsReposito
|
||||
bool WorldContentService::IsContentFlagEnabled(const std::string &content_flag)
|
||||
{
|
||||
for (auto &f: GetContentFlags()) {
|
||||
if (f.flag_name == content_flag && f.enabled == true) {
|
||||
if (f.flag_name == content_flag && f.enabled == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -147,7 +147,7 @@ bool WorldContentService::IsContentFlagEnabled(const std::string &content_flag)
|
||||
bool WorldContentService::IsContentFlagDisabled(const std::string &content_flag)
|
||||
{
|
||||
for (auto &f: GetContentFlags()) {
|
||||
if (f.flag_name == content_flag && f.enabled == false) {
|
||||
if (f.flag_name == content_flag && f.enabled == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+19
-1
@@ -99,6 +99,24 @@ uint32 CRC32::GenerateNoFlip(const uint8* buf, uint32 bufsize) {
|
||||
return Update(buf, bufsize);
|
||||
}
|
||||
|
||||
unsigned long CRC32::GetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at)
|
||||
{
|
||||
unsigned long data;
|
||||
unsigned long check = 0xffffffff;
|
||||
|
||||
for (uint32 i = start_at; i < in_length; i++)
|
||||
{
|
||||
data = in_data[i];
|
||||
data = data ^ (check);
|
||||
data = data & 0x000000ff;
|
||||
check = check >> 8;
|
||||
data = CRC32Table[data];
|
||||
check = check ^ data;
|
||||
}
|
||||
|
||||
return check;
|
||||
}
|
||||
|
||||
void CRC32::SetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at)
|
||||
{
|
||||
unsigned long data;
|
||||
@@ -125,6 +143,6 @@ uint32 CRC32::Update(const uint8* buf, uint32 bufsize, uint32 crc32var) {
|
||||
return crc32var;
|
||||
}
|
||||
|
||||
inline void CRC32::Calc(const uint8 byte, uint32& crc32var) {
|
||||
void CRC32::Calc(const uint8 byte, uint32& crc32var) {
|
||||
crc32var = ((crc32var) >> 8) ^ CRC32Table[(byte) ^ ((crc32var) & 0x000000FF)];
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
static uint32 Generate(const uint8* buf, uint32 bufsize);
|
||||
static uint32 GenerateNoFlip(const uint8* buf, uint32 bufsize); // Same as Generate(), but without the ~
|
||||
static void SetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at=4);
|
||||
static unsigned long GetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at = 4);
|
||||
|
||||
// Multiple buffer CRC32
|
||||
static uint32 Update(const uint8* buf, uint32 bufsize, uint32 crc32 = 0xFFFFFFFF);
|
||||
|
||||
+2
-2
@@ -728,7 +728,7 @@ bool Database::LoadVariables()
|
||||
return true;
|
||||
}
|
||||
|
||||
LockMutex lock(&Mvarcache);
|
||||
std::scoped_lock lock(Mvarcache);
|
||||
|
||||
for (const auto& e : l) {
|
||||
varcache.last_update = std::time(nullptr);
|
||||
@@ -747,7 +747,7 @@ bool Database::LoadVariables()
|
||||
|
||||
bool Database::GetVariable(const std::string& name, std::string& value)
|
||||
{
|
||||
LockMutex lock(&Mvarcache);
|
||||
std::scoped_lock lock(Mvarcache);
|
||||
|
||||
if (name.empty()) {
|
||||
return false;
|
||||
|
||||
+3
-4
@@ -20,13 +20,12 @@
|
||||
#include "common/dbcore.h"
|
||||
#include "common/eq_packet_structs.h"
|
||||
#include "common/eqemu_logsys.h"
|
||||
#include "common/linked_list.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#define AUTHENTICATION_TIMEOUT 60
|
||||
#define INVALID_ID 0xFFFFFFFF
|
||||
@@ -265,7 +264,7 @@ public:
|
||||
uint64_t GetNextTableId(const std::string& table_name);
|
||||
|
||||
private:
|
||||
Mutex Mvarcache;
|
||||
std::mutex Mvarcache;
|
||||
VarCache_Struct varcache;
|
||||
|
||||
/* Groups, utility methods. */
|
||||
|
||||
+102
-146
@@ -28,6 +28,7 @@
|
||||
#include "common/strings.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <ranges>
|
||||
|
||||
|
||||
constexpr int BREAK_LENGTH = 70;
|
||||
@@ -73,35 +74,38 @@ void DatabaseUpdate::CheckDbUpdates()
|
||||
return;
|
||||
}
|
||||
|
||||
if (UpdateManifest(manifest_entries, v.server_database_version, b.server_database_version)) {
|
||||
int server_database_version = UpdateManifest(manifest_entries, v.server_database_version, b.server_database_version);
|
||||
if (server_database_version > v.server_database_version) {
|
||||
LogInfo(
|
||||
"Updates ran successfully, setting database version to [{}] from [{}]",
|
||||
b.server_database_version,
|
||||
server_database_version,
|
||||
v.server_database_version
|
||||
);
|
||||
m_database->QueryDatabase(fmt::format("UPDATE `db_version` SET `version` = {}", b.server_database_version));
|
||||
m_database->QueryDatabase(fmt::format("UPDATE `db_version` SET `version` = {}", server_database_version));
|
||||
}
|
||||
|
||||
if (UpdateManifest(manifest_entries_custom, v.custom_database_version, b.custom_database_version)) {
|
||||
int custom_database_version = UpdateManifest(manifest_entries_custom, v.custom_database_version, b.custom_database_version);
|
||||
if (custom_database_version > v.server_database_version) {
|
||||
LogInfo(
|
||||
"Updates ran successfully, setting database version to [{}] from [{}]",
|
||||
b.custom_database_version,
|
||||
custom_database_version,
|
||||
v.custom_database_version
|
||||
);
|
||||
m_database->QueryDatabase(fmt::format("UPDATE `db_version` SET `custom_version` = {}", b.custom_database_version));
|
||||
m_database->QueryDatabase(fmt::format("UPDATE `db_version` SET `custom_version` = {}", custom_database_version));
|
||||
}
|
||||
|
||||
if (b.bots_database_version > 0) {
|
||||
if (UpdateManifest(bot_manifest_entries, v.bots_database_version, b.bots_database_version)) {
|
||||
int bots_database_version = UpdateManifest(bot_manifest_entries, v.bots_database_version, b.bots_database_version);
|
||||
if (bots_database_version > v.bots_database_version) {
|
||||
LogInfo(
|
||||
"Updates ran successfully, setting database version to [{}] from [{}]",
|
||||
b.bots_database_version,
|
||||
bots_database_version,
|
||||
v.bots_database_version
|
||||
);
|
||||
m_database->QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE `db_version` SET `bots_version` = {}",
|
||||
b.bots_database_version
|
||||
bots_database_version
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -131,7 +135,7 @@ std::string DatabaseUpdate::GetQueryResult(const ManifestEntry& e)
|
||||
return Strings::Join(result_lines, "\n");
|
||||
}
|
||||
|
||||
bool DatabaseUpdate::ShouldRunMigration(ManifestEntry &e, std::string query_result)
|
||||
bool DatabaseUpdate::ShouldRunMigration(const ManifestEntry& e, std::string& query_result)
|
||||
{
|
||||
std::string r = Strings::Trim(query_result);
|
||||
if (e.condition == "contains") {
|
||||
@@ -163,53 +167,52 @@ bool is_atty()
|
||||
#endif
|
||||
}
|
||||
|
||||
// return true if we ran updates
|
||||
bool DatabaseUpdate::UpdateManifest(
|
||||
std::vector<ManifestEntry> entries,
|
||||
std::string DisplayPrompt(const std::string& prompt)
|
||||
{
|
||||
std::string input;
|
||||
if (is_atty()) {
|
||||
LogInfo("{} (Timeout 60s)", prompt);
|
||||
|
||||
// user input
|
||||
bool gave_input = false;
|
||||
time_t start_time = time(nullptr);
|
||||
time_t wait_time_seconds = 60;
|
||||
|
||||
// spawn a concurrent thread that waits for input from std::cin
|
||||
std::thread t1(
|
||||
[&]() {
|
||||
std::cin >> input;
|
||||
gave_input = true;
|
||||
}
|
||||
);
|
||||
t1.detach();
|
||||
|
||||
// check the inputReceived flag once every 50ms for 10 seconds
|
||||
while (time(nullptr) < start_time + wait_time_seconds && !gave_input) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
}
|
||||
|
||||
return Strings::Trim(input);
|
||||
}
|
||||
|
||||
// return last successful version updated
|
||||
int DatabaseUpdate::UpdateManifest(
|
||||
std::vector<ManifestEntry>& entries,
|
||||
int version_low,
|
||||
int version_high
|
||||
)
|
||||
{
|
||||
std::vector<int> missing_migrations = {};
|
||||
int latest_version = version_low;
|
||||
if (version_low != version_high) {
|
||||
// assume at this point that we have a migration to do because there is a version number difference. If a
|
||||
// migration for a specific manifest entry does not happen because of a missing test, then log it and
|
||||
// continue (the assumption here is that the user has manually fixed the database at this point). If a force
|
||||
// interactive flag is set, then stop for each query. Fail the migration if the user says no or it times out
|
||||
// because it means the database isn't going to have a correct state to continue. Start with backing up the
|
||||
// database as per user options.
|
||||
|
||||
EQEmuLogSys::Instance()->DisableMySQLErrorLogs();
|
||||
bool force_interactive = false;
|
||||
for (int version = version_low + 1; version <= version_high; ++version) {
|
||||
for (auto &e: entries) {
|
||||
if (e.version == version) {
|
||||
bool has_migration = true;
|
||||
std::string r = GetQueryResult(e);
|
||||
if (ShouldRunMigration(e, r)) {
|
||||
has_migration = false;
|
||||
missing_migrations.emplace_back(e.version);
|
||||
}
|
||||
|
||||
std::string prefix = fmt::format(
|
||||
"[{}]",
|
||||
has_migration ? "ok" : "missing"
|
||||
);
|
||||
|
||||
LogInfo(
|
||||
"[{}] {:>10} | [{}]",
|
||||
e.version,
|
||||
prefix,
|
||||
e.description
|
||||
);
|
||||
|
||||
if (!has_migration && e.force_interactive) {
|
||||
force_interactive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EQEmuLogSys::Instance()->EnableMySQLErrorLogs();
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
|
||||
if (!missing_migrations.empty() && m_skip_backup) {
|
||||
LogInfo("Skipping database backup");
|
||||
}
|
||||
else if (!missing_migrations.empty()) {
|
||||
if (!m_skip_backup) {
|
||||
LogInfo("Automatically backing up database before applying updates");
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
auto s = DatabaseDumpService();
|
||||
@@ -217,120 +220,73 @@ bool DatabaseUpdate::UpdateManifest(
|
||||
s.SetDumpWithCompression(true);
|
||||
s.DatabaseDump();
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
} else {
|
||||
LogInfo("Skipping database backup");
|
||||
}
|
||||
|
||||
if (!missing_migrations.empty()) {
|
||||
LogInfo("Running database migrations. Please wait...");
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
}
|
||||
LogInfo("Running database migrations. Please wait...");
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
|
||||
if (force_interactive && !std::getenv("FORCE_INTERACTIVE")) {
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
LogInfo("Some migrations require user input. Running interactively");
|
||||
LogInfo("This is usually due to a major change that could cause data loss");
|
||||
LogInfo("Your server is automatically backed up before these updates are applied");
|
||||
LogInfo("but you should also make sure you take a backup prior to running this update");
|
||||
LogInfo("Would you like to run this update? [y/n] (Timeout 60s)");
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
auto filtered_entries = entries | std::views::filter(
|
||||
[version_low, version_high](const ManifestEntry& entry)
|
||||
{ return entry.version > version_low && entry.version <= version_high; });
|
||||
|
||||
// user input
|
||||
std::string input;
|
||||
bool gave_input = false;
|
||||
time_t start_time = time(nullptr);
|
||||
time_t wait_time_seconds = 60;
|
||||
std::vector<ManifestEntry> sorted_entries{filtered_entries.begin(), filtered_entries.end()};
|
||||
std::ranges::sort(sorted_entries, {}, &ManifestEntry::version);
|
||||
|
||||
// spawn a concurrent thread that waits for input from std::cin
|
||||
std::thread t1(
|
||||
[&]() {
|
||||
std::cin >> input;
|
||||
gave_input = true;
|
||||
}
|
||||
);
|
||||
t1.detach();
|
||||
for (const auto& entry : sorted_entries) {
|
||||
// this is the test to run this individual migration. If the test fails, then it is safe to assume
|
||||
// that this migration has already happened manually or otherwise and it's safe to skip
|
||||
// suppress error messages here, it's all tested in the following function
|
||||
EQEmuLogSys::Instance()->DisableMySQLErrorLogs();
|
||||
std::string result = GetQueryResult(entry);
|
||||
EQEmuLogSys::Instance()->EnableMySQLErrorLogs();
|
||||
|
||||
// check the inputReceived flag once every 50ms for 10 seconds
|
||||
while (time(nullptr) < start_time + wait_time_seconds && !gave_input) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
if (ShouldRunMigration(entry, result)) {
|
||||
if (entry.force_interactive && !std::getenv("FORCE_INTERACTIVE")) {
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
LogInfo("This migration requires user input. Running interactively");
|
||||
LogInfo("This is usually due to a major change that could cause data loss");
|
||||
LogInfo("Your server is automatically backed up before these updates are applied");
|
||||
LogInfo("but you should also make sure you take a backup prior to running this update");
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
|
||||
// prompt for user skip
|
||||
if (Strings::Trim(input) != "y") {
|
||||
LogInfo("Exiting due to user input");
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &m: missing_migrations) {
|
||||
for (auto &e: entries) {
|
||||
if (e.version == m) {
|
||||
bool errored_migration = false;
|
||||
|
||||
auto r = (e.content_schema_update ? m_content_database : m_database)->QueryDatabaseMulti(e.sql);
|
||||
|
||||
// ignore empty query result "errors"
|
||||
if (r.ErrorNumber() != 1065 && !r.ErrorMessage().empty()) {
|
||||
LogError("(#{}) [{}]", r.ErrorNumber(), r.ErrorMessage());
|
||||
errored_migration = true;
|
||||
|
||||
LogInfo("Required database update failed. This could be a problem");
|
||||
|
||||
// if terminal attached then prompt for skip
|
||||
if (is_atty()) {
|
||||
LogInfo("Would you like to skip this update? [y/n] (Timeout 60s)");
|
||||
|
||||
// user input
|
||||
std::string input;
|
||||
bool gave_input = false;
|
||||
time_t start_time = time(nullptr);
|
||||
time_t wait_time_seconds = 60;
|
||||
|
||||
// spawn a concurrent thread that waits for input from std::cin
|
||||
std::thread t1(
|
||||
[&]() {
|
||||
std::cin >> input;
|
||||
gave_input = true;
|
||||
}
|
||||
);
|
||||
t1.detach();
|
||||
|
||||
// check the inputReceived flag once every 50ms for 10 seconds
|
||||
while (time(nullptr) < start_time + wait_time_seconds && !gave_input) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
|
||||
// prompt for user skip
|
||||
if (Strings::Trim(input) == "y") {
|
||||
errored_migration = false;
|
||||
LogInfo("Skipping update [{}] [{}]", e.version, e.description);
|
||||
}
|
||||
} else {
|
||||
errored_migration = true;
|
||||
LogInfo("Skipping update [{}] [{}]", e.version, e.description);
|
||||
}
|
||||
// prompt for user skip
|
||||
if (DisplayPrompt("Would you like to run this update? [y/n]") != "y") {
|
||||
LogInfo("Exiting due to user input");
|
||||
return latest_version;
|
||||
}
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"[{}] [{}] [{}]",
|
||||
e.version,
|
||||
e.description,
|
||||
(errored_migration ? "error" : "ok")
|
||||
);
|
||||
auto r = (entry.content_schema_update ? m_content_database : m_database)->QueryDatabaseMulti(entry.sql);
|
||||
|
||||
if (errored_migration) {
|
||||
LogError("Fatal | Database migration [{}] failed to run", e.description);
|
||||
// ignore empty query result "errors"
|
||||
if (r.ErrorNumber() != 1065 && !r.ErrorMessage().empty()) {
|
||||
LogError("(#{}) [{}]", r.ErrorNumber(), r.ErrorMessage());
|
||||
LogInfo("Required database update failed.");
|
||||
|
||||
// if terminal attached then prompt for skip
|
||||
if (DisplayPrompt("Would you like to skip this update? [y/n]") == "y") {
|
||||
LogInfo("Skipping update [{}] [{}]", entry.version, entry.description);
|
||||
} else {
|
||||
LogError("Fatal | Database migration [{}] failed to run", entry.description);
|
||||
LogError("Fatal | Shutting down");
|
||||
std::exit(1);
|
||||
return latest_version;
|
||||
}
|
||||
|
||||
LogInfo("[{}] [{}] [error]", entry.version, entry.description);
|
||||
} else {
|
||||
LogInfo("[{}] [{}] [ok]", entry.version, entry.description);
|
||||
}
|
||||
|
||||
latest_version = entry.version;
|
||||
}
|
||||
}
|
||||
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return latest_version;
|
||||
}
|
||||
|
||||
DatabaseUpdate *DatabaseUpdate::SetDatabase(Database *db)
|
||||
|
||||
@@ -42,8 +42,8 @@ public:
|
||||
DatabaseVersion GetBinaryDatabaseVersions();
|
||||
void CheckDbUpdates();
|
||||
std::string GetQueryResult(const ManifestEntry& e);
|
||||
static bool ShouldRunMigration(ManifestEntry &e, std::string query_result);
|
||||
bool UpdateManifest(std::vector<ManifestEntry> entries, int version_low, int version_high);
|
||||
static bool ShouldRunMigration(const ManifestEntry& e, std::string& query_result);
|
||||
int UpdateManifest(std::vector<ManifestEntry>& entries, int version_low, int version_high);
|
||||
|
||||
DatabaseUpdate *SetDatabase(Database *db);
|
||||
DatabaseUpdate *SetContentDatabase(Database *db);
|
||||
|
||||
@@ -7207,6 +7207,64 @@ ALTER TABLE `character_parcels_containers`
|
||||
)",
|
||||
.content_schema_update = false
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9329,
|
||||
.description = "2026_04_27_spell_id_type_correction.sql",
|
||||
.check = "SHOW COLUMNS FROM `character_spells` WHERE `Field` LIKE 'spell_id' AND `Type` LIKE 'int(%)'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `blocked_spells` MODIFY COLUMN `spellid` INTEGER NULL;
|
||||
ALTER TABLE `character_buffs` MODIFY COLUMN `spell_id` INTEGER NOT NULL;
|
||||
ALTER TABLE `character_memmed_spells` MODIFY COLUMN `spell_id` INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `character_spells` MODIFY COLUMN `spell_id` INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `damageshieldtypes` MODIFY COLUMN `spellid` INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `ldon_trap_templates` MODIFY COLUMN `spell_id` INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE IF EXISTS `merc_buffs` MODIFY COLUMN `SpellId` INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE IF EXISTS `merc_spell_list_entries` MODIFY COLUMN `spell_id` INTEGER NOT NULL;
|
||||
ALTER TABLE `npc_spells_entries` MODIFY COLUMN `spellid` INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `spell_buckets` MODIFY COLUMN `spell_id` INTEGER NOT NULL;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9330,
|
||||
.description = "2026_04_30_buffdurations.sql",
|
||||
.check = "SHOW COLUMNS FROM `character_buffs` LIKE 'initialduration'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `character_buffs`
|
||||
ADD COLUMN `initialduration` INT(11) SIGNED NOT NULL DEFAULT 0 AFTER `ticsremaining`,
|
||||
CHANGE COLUMN `dot_rune` `dot_rune` INT(10) UNSIGNED NOT NULL DEFAULT 0;
|
||||
UPDATE `character_buffs` SET `initialduration` = `ticsremaining` WHERE TRUE;
|
||||
|
||||
ALTER TABLE IF EXISTS `merc_buffs`
|
||||
CHANGE COLUMN `dot_rune` `dot_rune` INT(10) UNSIGNED NOT NULL DEFAULT 0,
|
||||
ADD COLUMN `InitialDuration` INT(11) SIGNED NOT NULL DEFAULT 0 AFTER `TicsRemaining`,
|
||||
ADD COLUMN `instrument_mod` INT(10) UNSIGNED NOT NULL DEFAULT 10 AFTER `ExtraDIChance`;
|
||||
IF EXISTS(
|
||||
SELECT 1 FROM `information_schema`.`TABLES` WHERE `table_schema` = DATABASE() AND `table_name` = 'merc_buffs'
|
||||
) THEN UPDATE `merc_buffs` SET `InitialDuration` = `TicsRemaining` WHERE TRUE; END IF;
|
||||
|
||||
ALTER TABLE `character_pet_buffs`
|
||||
CHANGE COLUMN `char_id` `character_id` INT(11) UNSIGNED NOT NULL,
|
||||
CHANGE COLUMN `slot` `slot_id` TINYINT(3) UNSIGNED NOT NULL,
|
||||
CHANGE COLUMN `caster_level` `caster_level` TINYINT(3) UNSIGNED NOT NULL,
|
||||
RENAME COLUMN `castername` TO `caster_name`,
|
||||
ADD COLUMN `initialduration` INT(11) SIGNED NOT NULL DEFAULT 0 AFTER `ticsremaining`,
|
||||
CHANGE COLUMN `counters` `counters` INT(10) UNSIGNED NOT NULL DEFAULT 0,
|
||||
CHANGE COLUMN `numhits` `numhits` INT(10) UNSIGNED NOT NULL DEFAULT 0,
|
||||
CHANGE COLUMN `rune` `melee_rune` INT(10) UNSIGNED NOT NULL DEFAULT 0,
|
||||
ADD COLUMN `magic_rune` INT(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `melee_rune`,
|
||||
ADD COLUMN `persistent` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `magic_rune`,
|
||||
ADD COLUMN `dot_rune` INT(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `persistent`,
|
||||
ADD COLUMN `caston_x` INT(10) NOT NULL DEFAULT 0 AFTER `dot_rune`,
|
||||
ADD COLUMN `caston_y` INT(10) NOT NULL DEFAULT 0 AFTER `caston_x`,
|
||||
ADD COLUMN `caston_z` INT(10) NOT NULL DEFAULT 0 AFTER `caston_y`,
|
||||
ADD COLUMN `ExtraDIChance` INT(10) NOT NULL DEFAULT 0 AFTER `caston_z`,
|
||||
CHANGE COLUMN `instrument_mod` `instrument_mod` INT(10) UNSIGNED NOT NULL DEFAULT 10 AFTER `ExtraDIChance`;
|
||||
)",
|
||||
},
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
// ManifestEntry{
|
||||
// .version = 9228,
|
||||
|
||||
@@ -2174,7 +2174,53 @@ FROM bot_settings
|
||||
WHERE `setting_id` = 0
|
||||
AND `setting_type` = 0;
|
||||
)"
|
||||
}
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9056,
|
||||
.description = "some_new_migration.sql",
|
||||
.check = "SHOW COLUMNS FROM `table_name` LIKE 'column_name'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `bot_blocked_buffs` MODIFY COLUMN `spell_id` INTEGER NOT NULL;
|
||||
ALTER TABLE `bot_buffs` MODIFY COLUMN `spell_id` INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `bot_pet_buffs` MODIFY COLUMN `spell_id` INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `bot_pets` MODIFY COLUMN `spell_id` INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `bot_spell_settings` MODIFY COLUMN `spell_id` INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `bot_spells_entries` MODIFY COLUMN `spell_id` INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE `bot_timers` MODIFY COLUMN `spell_id` INTEGER NOT NULL DEFAULT 0;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9057,
|
||||
.description = "2026_04_30_buffdurations.sql",
|
||||
.check = "SHOW COLUMNS FROM `bot_buffs` LIKE 'initial_duration'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `bot_buffs`
|
||||
ADD COLUMN `caster_name` VARCHAR(64) NOT NULL DEFAULT '' AFTER `caster_level`,
|
||||
ADD COLUMN `initial_duration` INT(11) SIGNED NOT NULL DEFAULT 0 AFTER `tics_remaining`;
|
||||
UPDATE `bot_buffs` SET `initial_duration` = `tics_remaining` WHERE TRUE;
|
||||
|
||||
ALTER TABLE `bot_pet_buffs`
|
||||
ADD COLUMN `caster_name` VARCHAR(64) NOT NULL DEFAULT '' AFTER `caster_level`,
|
||||
CHANGE COLUMN `duration` `tics_remaining` INT(11) SIGNED NOT NULL DEFAULT 0 AFTER `caster_name`,
|
||||
ADD COLUMN `initial_duration` INT(11) SIGNED NOT NULL DEFAULT 0 AFTER `tics_remaining`,
|
||||
ADD COLUMN `counters` INT(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `initial_duration`,
|
||||
ADD COLUMN `numhits` INT(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `counters`,
|
||||
ADD COLUMN `melee_rune` INT(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `numhits`,
|
||||
ADD COLUMN `magic_rune` INT(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `melee_rune`,
|
||||
ADD COLUMN `dot_rune` INT(10) UNSIGNED NOT NULL DEFAULT 0 AFTER `magic_rune`,
|
||||
ADD COLUMN `persistent` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `dot_rune`,
|
||||
ADD COLUMN `caston_x` INT(10) NOT NULL DEFAULT 0 AFTER `persistent`,
|
||||
ADD COLUMN `caston_y` INT(10) NOT NULL DEFAULT 0 AFTER `caston_x`,
|
||||
ADD COLUMN `caston_z` INT(10) NOT NULL DEFAULT 0 AFTER `caston_y`,
|
||||
ADD COLUMN `extra_di_chance` INT(10) NOT NULL DEFAULT 0 AFTER `caston_z`,
|
||||
ADD COLUMN `instrument_mod` INT(10) UNSIGNED NOT NULL DEFAULT 10 AFTER `extra_di_chance`;
|
||||
UPDATE `bot_pet_buffs` SET `initial_duration` = `tics_remaining` WHERE TRUE;
|
||||
)",
|
||||
},
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
// ManifestEntry{
|
||||
// .version = 9228,
|
||||
|
||||
+23
-40
@@ -33,17 +33,9 @@
|
||||
#endif
|
||||
|
||||
DBcore::DBcore()
|
||||
: mysql(mysql_init(nullptr))
|
||||
, m_mutex(std::make_shared<Mutex>())
|
||||
{
|
||||
mysql = mysql_init(nullptr);
|
||||
mysqlOwner = true;
|
||||
pHost = nullptr;
|
||||
pUser = nullptr;
|
||||
pPassword = nullptr;
|
||||
pDatabase = nullptr;
|
||||
pCompress = false;
|
||||
pSSL = false;
|
||||
pStatus = Closed;
|
||||
m_mutex = new Mutex;
|
||||
}
|
||||
|
||||
DBcore::~DBcore()
|
||||
@@ -56,20 +48,17 @@ DBcore::~DBcore()
|
||||
if (mysqlOwner) {
|
||||
mysql_close(mysql);
|
||||
}
|
||||
|
||||
safe_delete_array(pHost);
|
||||
safe_delete_array(pUser);
|
||||
safe_delete_array(pPassword);
|
||||
safe_delete_array(pDatabase);
|
||||
}
|
||||
|
||||
// Sends the MySQL server a keepalive
|
||||
void DBcore::ping()
|
||||
{
|
||||
if (!m_mutex->trylock()) {
|
||||
if (!m_mutex->try_lock())
|
||||
{
|
||||
// well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive
|
||||
return;
|
||||
}
|
||||
|
||||
mysql_ping(mysql);
|
||||
m_mutex->unlock();
|
||||
}
|
||||
@@ -92,7 +81,7 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
|
||||
BenchTimer timer;
|
||||
timer.reset();
|
||||
|
||||
LockMutex lock(m_mutex);
|
||||
std::scoped_lock lock(*m_mutex);
|
||||
|
||||
// Reconnect if we are not connected before hand.
|
||||
if (pStatus != Connected) {
|
||||
@@ -217,15 +206,12 @@ bool DBcore::Open(
|
||||
bool iSSL
|
||||
)
|
||||
{
|
||||
LockMutex lock(m_mutex);
|
||||
safe_delete_array(pHost);
|
||||
safe_delete_array(pUser);
|
||||
safe_delete_array(pPassword);
|
||||
safe_delete_array(pDatabase);
|
||||
pHost = strcpy(new char[strlen(iHost) + 1], iHost);
|
||||
pUser = strcpy(new char[strlen(iUser) + 1], iUser);
|
||||
pPassword = strcpy(new char[strlen(iPassword) + 1], iPassword);
|
||||
pDatabase = strcpy(new char[strlen(iDatabase) + 1], iDatabase);
|
||||
std::scoped_lock lock(*m_mutex);
|
||||
|
||||
m_host = iHost;
|
||||
m_user = iUser;
|
||||
m_password = iPassword;
|
||||
m_database = iDatabase;
|
||||
pCompress = iCompress;
|
||||
pPort = iPort;
|
||||
pSSL = iSSL;
|
||||
@@ -234,10 +220,12 @@ bool DBcore::Open(
|
||||
|
||||
bool DBcore::Open(uint32 *errnum, char *errbuf)
|
||||
{
|
||||
// Expects m_mutex to already be locked.
|
||||
|
||||
if (errbuf) {
|
||||
errbuf[0] = 0;
|
||||
}
|
||||
LockMutex lock(m_mutex);
|
||||
|
||||
if (GetStatus() == Connected) {
|
||||
return true;
|
||||
}
|
||||
@@ -245,7 +233,7 @@ bool DBcore::Open(uint32 *errnum, char *errbuf)
|
||||
mysql_close(mysql);
|
||||
mysql_init(mysql); // Initialize structure again
|
||||
}
|
||||
if (!pHost) {
|
||||
if (m_host.empty()) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
@@ -268,11 +256,10 @@ bool DBcore::Open(uint32 *errnum, char *errbuf)
|
||||
mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &off);
|
||||
mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &off);
|
||||
}
|
||||
if (mysql_real_connect(mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) {
|
||||
if (mysql_real_connect(mysql, m_host.c_str(), m_user.c_str(), m_password.c_str(), m_database.c_str(), pPort, nullptr, flags)) {
|
||||
pStatus = Connected;
|
||||
|
||||
std::string connected_origin_host = pHost;
|
||||
SetOriginHost(connected_origin_host);
|
||||
SetOriginHost(m_host);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -293,9 +280,9 @@ const std::string &DBcore::GetOriginHost() const
|
||||
return origin_host;
|
||||
}
|
||||
|
||||
void DBcore::SetOriginHost(const std::string &origin_host)
|
||||
void DBcore::SetOriginHost(const std::string& originHost)
|
||||
{
|
||||
DBcore::origin_host = origin_host;
|
||||
DBcore::origin_host = originHost;
|
||||
}
|
||||
|
||||
std::string DBcore::Escape(const std::string& s)
|
||||
@@ -307,12 +294,8 @@ std::string DBcore::Escape(const std::string& s)
|
||||
return temp.data();
|
||||
}
|
||||
|
||||
void DBcore::SetMutex(Mutex *mutex)
|
||||
void DBcore::SetMutex(const std::shared_ptr<Mutex>& mutex)
|
||||
{
|
||||
if (m_mutex && m_mutex != mutex) {
|
||||
safe_delete(m_mutex);
|
||||
}
|
||||
|
||||
DBcore::m_mutex = mutex;
|
||||
}
|
||||
|
||||
@@ -326,7 +309,7 @@ MySQLRequestResult DBcore::QueryDatabaseMulti(const std::string &query)
|
||||
BenchTimer timer;
|
||||
timer.reset();
|
||||
|
||||
LockMutex lock(m_mutex);
|
||||
std::scoped_lock lock(*m_mutex);
|
||||
|
||||
// Reconnect if we are not connected before hand.
|
||||
if (pStatus != Connected) {
|
||||
@@ -449,5 +432,5 @@ MySQLRequestResult DBcore::QueryDatabaseMulti(const std::string &query)
|
||||
|
||||
mysql::PreparedStmt DBcore::Prepare(std::string query)
|
||||
{
|
||||
return mysql::PreparedStmt(*mysql, std::move(query), m_mutex);
|
||||
return mysql::PreparedStmt(*mysql, std::move(query), *m_mutex);
|
||||
}
|
||||
|
||||
+20
-18
@@ -17,11 +17,11 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/mutex.h"
|
||||
#include "common/mysql_request_result.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#include "mysql.h"
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#define CR_SERVER_GONE_ERROR 2006
|
||||
@@ -29,12 +29,15 @@
|
||||
|
||||
namespace mysql { class PreparedStmt; }
|
||||
|
||||
class DBcore {
|
||||
class DBcore
|
||||
{
|
||||
public:
|
||||
enum eStatus {
|
||||
Closed, Connected, Error
|
||||
};
|
||||
|
||||
using Mutex = std::recursive_mutex;
|
||||
|
||||
DBcore();
|
||||
~DBcore();
|
||||
eStatus GetStatus() { return pStatus; }
|
||||
@@ -48,17 +51,17 @@ public:
|
||||
uint32 DoEscapeString(char *tobuf, const char *frombuf, uint32 fromlen);
|
||||
void ping();
|
||||
|
||||
const std::string &GetOriginHost() const;
|
||||
void SetOriginHost(const std::string &origin_host);
|
||||
const std::string& GetOriginHost() const;
|
||||
void SetOriginHost(const std::string& origin_host);
|
||||
|
||||
bool DoesTableExist(const std::string& table_name);
|
||||
|
||||
void SetMySQL(const DBcore &o)
|
||||
void SetMySQL(const DBcore& o)
|
||||
{
|
||||
mysql = o.mysql;
|
||||
mysqlOwner = false;
|
||||
}
|
||||
void SetMutex(Mutex *mutex);
|
||||
void SetMutex(const std::shared_ptr<Mutex>& mutex);
|
||||
|
||||
// only safe on connections shared with other threads if results buffered
|
||||
// unsafe to use off main thread due to internal server logging
|
||||
@@ -81,22 +84,21 @@ protected:
|
||||
private:
|
||||
bool Open(uint32 *errnum = nullptr, char *errbuf = nullptr);
|
||||
|
||||
MYSQL* mysql;
|
||||
bool mysqlOwner;
|
||||
Mutex *m_mutex;
|
||||
eStatus pStatus;
|
||||
MYSQL* mysql = nullptr;
|
||||
bool mysqlOwner = true;
|
||||
eStatus pStatus = Closed;
|
||||
|
||||
std::mutex m_query_lock{};
|
||||
std::shared_ptr<Mutex> m_mutex;
|
||||
|
||||
std::string origin_host;
|
||||
|
||||
char *pHost;
|
||||
char *pUser;
|
||||
char *pPassword;
|
||||
char *pDatabase;
|
||||
bool pCompress;
|
||||
uint32 pPort;
|
||||
bool pSSL;
|
||||
std::string m_host;
|
||||
std::string m_user;
|
||||
std::string m_password;
|
||||
std::string m_database;
|
||||
bool pCompress = false;
|
||||
uint32 pPort = 0;
|
||||
bool pSSL = false;
|
||||
|
||||
// allows multiple queries to be executed within the same query
|
||||
// do not use this under normal operation
|
||||
|
||||
+7
-6
@@ -18,8 +18,6 @@
|
||||
// system use
|
||||
N(OP_ExploreUnknown),
|
||||
// start (please add new opcodes in descending order and re-order any name changes where applicable)
|
||||
N(OP_0x0193),
|
||||
N(OP_0x0347),
|
||||
N(OP_AAAction),
|
||||
N(OP_AAExpUpdate),
|
||||
N(OP_AcceptNewTask),
|
||||
@@ -80,8 +78,8 @@ N(OP_Bind_Wound),
|
||||
N(OP_BlockedBuffs),
|
||||
N(OP_BoardBoat),
|
||||
N(OP_BookButton),
|
||||
N(OP_Buff),
|
||||
N(OP_BuffCreate),
|
||||
N(OP_BuffDefinition),
|
||||
N(OP_RefreshBuffs),
|
||||
N(OP_BuffRemoveRequest),
|
||||
N(OP_Bug),
|
||||
N(OP_BuyerItems),
|
||||
@@ -376,10 +374,12 @@ N(OP_MercenaryDismiss),
|
||||
N(OP_MercenaryHire),
|
||||
N(OP_MercenarySuspendRequest),
|
||||
N(OP_MercenarySuspendResponse),
|
||||
N(OP_MercenarySwitch),
|
||||
N(OP_MercenaryTimer),
|
||||
N(OP_MercenaryTimerRequest),
|
||||
N(OP_MercenaryUnknown1),
|
||||
N(OP_MercenaryUnsuspendResponse),
|
||||
N(OP_MerchantBulkItems),
|
||||
N(OP_MobEnduranceUpdate),
|
||||
N(OP_MobHealth),
|
||||
N(OP_MobManaUpdate),
|
||||
@@ -398,6 +398,7 @@ N(OP_MultiLineMsg),
|
||||
N(OP_NewSpawn),
|
||||
N(OP_NewTitlesAvailable),
|
||||
N(OP_NewZone),
|
||||
N(OP_NPCMoveUpdate),
|
||||
N(OP_OnLevelMessage),
|
||||
N(OP_OpenContainer),
|
||||
N(OP_OpenDiscordMerchant),
|
||||
@@ -405,7 +406,7 @@ N(OP_OpenGuildTributeMaster),
|
||||
N(OP_OpenInventory),
|
||||
N(OP_OpenTributeMaster),
|
||||
N(OP_PDeletePetition),
|
||||
N(OP_PetBuffWindow),
|
||||
N(OP_RefreshPetBuffs),
|
||||
N(OP_PetCommands),
|
||||
N(OP_PetCommandState),
|
||||
N(OP_PetHoTT),
|
||||
@@ -561,7 +562,7 @@ N(OP_Stun),
|
||||
N(OP_Surname),
|
||||
N(OP_SwapSpell),
|
||||
N(OP_SystemFingerprint),
|
||||
N(OP_TargetBuffs),
|
||||
N(OP_RefreshTargetBuffs),
|
||||
N(OP_TargetCommand),
|
||||
N(OP_TargetHoTT),
|
||||
N(OP_TargetMouse),
|
||||
|
||||
@@ -54,6 +54,8 @@ const char* EQ::versions::ClientVersionName(ClientVersion client_version)
|
||||
return "RoF";
|
||||
case ClientVersion::RoF2:
|
||||
return "RoF2";
|
||||
case ClientVersion::TOB:
|
||||
return "TOB";
|
||||
default:
|
||||
return "Invalid Version";
|
||||
};
|
||||
@@ -74,6 +76,8 @@ uint32 EQ::versions::ConvertClientVersionToClientVersionBit(ClientVersion client
|
||||
return bitRoF;
|
||||
case ClientVersion::RoF2:
|
||||
return bitRoF2;
|
||||
case ClientVersion::TOB:
|
||||
return bitTOB;
|
||||
default:
|
||||
return bitUnknown;
|
||||
}
|
||||
@@ -94,6 +98,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertClientVersionBitToClientVersion
|
||||
return ClientVersion::RoF;
|
||||
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::RoF2) - 1)) :
|
||||
return ClientVersion::RoF2;
|
||||
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::TOB) - 1)) :
|
||||
return ClientVersion::TOB;
|
||||
default:
|
||||
return ClientVersion::Unknown;
|
||||
}
|
||||
@@ -182,6 +188,8 @@ const char* EQ::versions::MobVersionName(MobVersion mob_version)
|
||||
return "RoF";
|
||||
case MobVersion::RoF2:
|
||||
return "RoF2";
|
||||
case MobVersion::TOB:
|
||||
return "TOB";
|
||||
case MobVersion::NPC:
|
||||
return "NPC";
|
||||
case MobVersion::NPCMerchant:
|
||||
@@ -210,6 +218,8 @@ const char* EQ::versions::MobVersionName(MobVersion mob_version)
|
||||
return "Offline RoF";
|
||||
case MobVersion::OfflineRoF2:
|
||||
return "Offline RoF2";
|
||||
case MobVersion::OfflineTOB:
|
||||
return "Offline TOB";
|
||||
default:
|
||||
return "Invalid Version";
|
||||
};
|
||||
@@ -233,6 +243,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertMobVersionToClientVersion(MobVe
|
||||
return ClientVersion::RoF;
|
||||
case MobVersion::RoF2:
|
||||
return ClientVersion::RoF2;
|
||||
case MobVersion::TOB:
|
||||
return ClientVersion::TOB;
|
||||
default:
|
||||
return ClientVersion::Unknown;
|
||||
}
|
||||
@@ -256,6 +268,8 @@ EQ::versions::MobVersion EQ::versions::ConvertClientVersionToMobVersion(ClientVe
|
||||
return MobVersion::RoF;
|
||||
case ClientVersion::RoF2:
|
||||
return MobVersion::RoF2;
|
||||
case ClientVersion::TOB:
|
||||
return MobVersion::TOB;
|
||||
default:
|
||||
return MobVersion::Unknown;
|
||||
}
|
||||
@@ -276,6 +290,8 @@ EQ::versions::MobVersion EQ::versions::ConvertPCMobVersionToOfflinePCMobVersion(
|
||||
return MobVersion::OfflineRoF;
|
||||
case MobVersion::RoF2:
|
||||
return MobVersion::OfflineRoF2;
|
||||
case MobVersion::TOB:
|
||||
return MobVersion::OfflineTOB;
|
||||
default:
|
||||
return MobVersion::Unknown;
|
||||
}
|
||||
@@ -296,6 +312,8 @@ EQ::versions::MobVersion EQ::versions::ConvertOfflinePCMobVersionToPCMobVersion(
|
||||
return MobVersion::RoF;
|
||||
case MobVersion::OfflineRoF2:
|
||||
return MobVersion::RoF2;
|
||||
case MobVersion::OfflineTOB:
|
||||
return MobVersion::TOB;
|
||||
default:
|
||||
return MobVersion::Unknown;
|
||||
}
|
||||
@@ -316,6 +334,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertOfflinePCMobVersionToClientVers
|
||||
return ClientVersion::RoF;
|
||||
case MobVersion::OfflineRoF2:
|
||||
return ClientVersion::RoF2;
|
||||
case MobVersion::OfflineTOB:
|
||||
return ClientVersion::TOB;
|
||||
default:
|
||||
return ClientVersion::Unknown;
|
||||
}
|
||||
@@ -336,6 +356,8 @@ EQ::versions::MobVersion EQ::versions::ConvertClientVersionToOfflinePCMobVersion
|
||||
return MobVersion::OfflineRoF;
|
||||
case ClientVersion::RoF2:
|
||||
return MobVersion::OfflineRoF2;
|
||||
case ClientVersion::TOB:
|
||||
return MobVersion::OfflineTOB;
|
||||
default:
|
||||
return MobVersion::Unknown;
|
||||
}
|
||||
@@ -386,6 +408,28 @@ const char* EQ::expansions::ExpansionName(Expansion expansion)
|
||||
return "Rain of Fear";
|
||||
case Expansion::CotF:
|
||||
return "Call of the Forsaken";
|
||||
case Expansion::TDS:
|
||||
return "The Darkened Sea";
|
||||
case Expansion::TBM:
|
||||
return "The Broken Mirror";
|
||||
case Expansion::EoK:
|
||||
return "Empires of Kunark";
|
||||
case Expansion::RoS:
|
||||
return "Ring of Scale";
|
||||
case Expansion::TBL:
|
||||
return "The Burning Lands";
|
||||
case Expansion::ToV:
|
||||
return "Torment of Velious";
|
||||
case Expansion::CoV:
|
||||
return "Claws of Veeshan";
|
||||
case Expansion::ToL:
|
||||
return "Terror of Luclin";
|
||||
case Expansion::NoS:
|
||||
return "Night of Shadows";
|
||||
case Expansion::LS:
|
||||
return "Laurion's Song";
|
||||
case Expansion::TOB:
|
||||
return "The Outer Brood";
|
||||
default:
|
||||
return "Invalid Expansion";
|
||||
}
|
||||
@@ -439,6 +483,29 @@ uint32 EQ::expansions::ConvertExpansionToExpansionBit(Expansion expansion)
|
||||
return bitRoF;
|
||||
case Expansion::CotF:
|
||||
return bitCotF;
|
||||
case Expansion::TDS:
|
||||
return bitTDS;
|
||||
case Expansion::TBM:
|
||||
return bitTBM;
|
||||
case Expansion::EoK:
|
||||
return bitEoK;
|
||||
case Expansion::RoS:
|
||||
return bitRoS;
|
||||
case Expansion::TBL:
|
||||
return bitTBL;
|
||||
case Expansion::ToV:
|
||||
return bitToV;
|
||||
case Expansion::CoV:
|
||||
return bitCoV;
|
||||
case Expansion::ToL:
|
||||
return bitToL;
|
||||
case Expansion::NoS:
|
||||
return bitNoS;
|
||||
case Expansion::LS:
|
||||
return bitLS;
|
||||
case Expansion::TOB:
|
||||
return bitTOB;
|
||||
|
||||
default:
|
||||
return bitEverQuest;
|
||||
}
|
||||
@@ -487,6 +554,28 @@ EQ::expansions::Expansion EQ::expansions::ConvertExpansionBitToExpansion(uint32
|
||||
return Expansion::RoF;
|
||||
case bitCotF:
|
||||
return Expansion::CotF;
|
||||
case bitTDS:
|
||||
return Expansion::TDS;
|
||||
case bitTBM:
|
||||
return Expansion::TBM;
|
||||
case bitEoK:
|
||||
return Expansion::EoK;
|
||||
case bitRoS:
|
||||
return Expansion::RoS;
|
||||
case bitTBL:
|
||||
return Expansion::TBL;
|
||||
case bitToV:
|
||||
return Expansion::ToV;
|
||||
case bitCoV:
|
||||
return Expansion::CoV;
|
||||
case bitToL:
|
||||
return Expansion::ToL;
|
||||
case bitNoS:
|
||||
return Expansion::NoS;
|
||||
case bitLS:
|
||||
return Expansion::LS;
|
||||
case bitTOB:
|
||||
return Expansion::TOB;
|
||||
default:
|
||||
return Expansion::EverQuest;
|
||||
}
|
||||
@@ -535,6 +624,28 @@ uint32 EQ::expansions::ConvertExpansionToExpansionsMask(Expansion expansion)
|
||||
return maskRoF;
|
||||
case Expansion::CotF:
|
||||
return maskCotF;
|
||||
case Expansion::TDS:
|
||||
return maskTDS;
|
||||
case Expansion::TBM:
|
||||
return maskTBM;
|
||||
case Expansion::EoK:
|
||||
return maskEoK;
|
||||
case Expansion::RoS:
|
||||
return maskRoS;
|
||||
case Expansion::TBL:
|
||||
return maskTBL;
|
||||
case Expansion::ToV:
|
||||
return maskToV;
|
||||
case Expansion::CoV:
|
||||
return maskCoV;
|
||||
case Expansion::ToL:
|
||||
return maskToL;
|
||||
case Expansion::NoS:
|
||||
return maskNoS;
|
||||
case Expansion::LS:
|
||||
return maskLS;
|
||||
case Expansion::TOB:
|
||||
return maskTOB;
|
||||
default:
|
||||
return maskEverQuest;
|
||||
}
|
||||
|
||||
+49
-10
@@ -32,7 +32,8 @@ namespace EQ
|
||||
SoD, // Build: 'Dec 19 2008 15:22:49'
|
||||
UF, // Build: 'Jun 8 2010 16:44:32'
|
||||
RoF, // Build: 'Dec 10 2012 17:35:44'
|
||||
RoF2 // Build: 'May 10 2013 23:30:08'
|
||||
RoF2, // Build: 'May 10 2013 23:30:08'
|
||||
TOB // Build: 'Sep 11 2025 11:54:10'
|
||||
};
|
||||
|
||||
enum ClientVersionBitmask : uint32 {
|
||||
@@ -44,6 +45,7 @@ namespace EQ
|
||||
bitUF = 0x00000010,
|
||||
bitRoF = 0x00000020,
|
||||
bitRoF2 = 0x00000040,
|
||||
bitTOB = 0x00000080,
|
||||
maskUnknown = 0x00000000,
|
||||
maskTitaniumAndEarlier = 0x00000003,
|
||||
maskSoFAndEarlier = 0x00000007,
|
||||
@@ -55,11 +57,12 @@ namespace EQ
|
||||
maskUFAndLater = 0xFFFFFFF0,
|
||||
maskRoFAndLater = 0xFFFFFFE0,
|
||||
maskRoF2AndLater = 0xFFFFFFC0,
|
||||
maskTOBAndLater = 0xFFFFFF80,
|
||||
maskAllClients = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
const ClientVersion LastClientVersion = ClientVersion::RoF2;
|
||||
const size_t ClientVersionCount = (static_cast<size_t>(LastClientVersion) + 1);
|
||||
inline constexpr ClientVersion LastClientVersion = ClientVersion::TOB;
|
||||
inline constexpr size_t ClientVersionCount = (static_cast<size_t>(LastClientVersion) + 1);
|
||||
|
||||
bool IsValidClientVersion(ClientVersion client_version);
|
||||
ClientVersion ValidateClientVersion(ClientVersion client_version);
|
||||
@@ -76,6 +79,7 @@ namespace EQ
|
||||
UF,
|
||||
RoF,
|
||||
RoF2,
|
||||
TOB,
|
||||
NPC,
|
||||
NPCMerchant,
|
||||
Merc,
|
||||
@@ -89,13 +93,14 @@ namespace EQ
|
||||
OfflineSoD,
|
||||
OfflineUF,
|
||||
OfflineRoF,
|
||||
OfflineRoF2
|
||||
OfflineRoF2,
|
||||
OfflineTOB
|
||||
};
|
||||
|
||||
const MobVersion LastMobVersion = MobVersion::OfflineRoF2;
|
||||
const MobVersion LastPCMobVersion = MobVersion::RoF2;
|
||||
const MobVersion LastMobVersion = MobVersion::OfflineTOB;
|
||||
const MobVersion LastPCMobVersion = MobVersion::TOB;
|
||||
const MobVersion LastNonPCMobVersion = MobVersion::BotPet;
|
||||
const MobVersion LastOfflinePCMobVersion = MobVersion::OfflineRoF2;
|
||||
const MobVersion LastOfflinePCMobVersion = MobVersion::OfflineTOB;
|
||||
const size_t MobVersionCount = (static_cast<size_t>(LastMobVersion) + 1);
|
||||
|
||||
bool IsValidMobVersion(MobVersion mob_version);
|
||||
@@ -127,7 +132,8 @@ namespace EQ
|
||||
ucsSoDCombined = 'D',
|
||||
ucsUFCombined = 'E',
|
||||
ucsRoFCombined = 'F',
|
||||
ucsRoF2Combined = 'G'
|
||||
ucsRoF2Combined = 'G',
|
||||
ucsTOBCombined = 'H'
|
||||
};
|
||||
|
||||
} /*versions*/
|
||||
@@ -154,7 +160,18 @@ namespace EQ
|
||||
HoT,
|
||||
VoA,
|
||||
RoF,
|
||||
CotF
|
||||
CotF,
|
||||
TDS,
|
||||
TBM,
|
||||
EoK,
|
||||
RoS,
|
||||
TBL,
|
||||
ToV,
|
||||
CoV,
|
||||
ToL,
|
||||
NoS,
|
||||
LS,
|
||||
TOB
|
||||
};
|
||||
|
||||
enum ExpansionBitmask : uint32 {
|
||||
@@ -179,6 +196,17 @@ namespace EQ
|
||||
bitVoA = 0x00020000,
|
||||
bitRoF = 0x00040000,
|
||||
bitCotF = 0x00080000,
|
||||
bitTDS = 0x00100000,
|
||||
bitTBM = 0x00200000,
|
||||
bitEoK = 0x00400000,
|
||||
bitRoS = 0x00800000,
|
||||
bitTBL = 0x01000000,
|
||||
bitToV = 0x02000000,
|
||||
bitCoV = 0x04000000,
|
||||
bitToL = 0x08000000,
|
||||
bitNoS = 0x10000000,
|
||||
bitLS = 0x20000000,
|
||||
bitTOB = 0x40000000,
|
||||
maskEverQuest = 0x00000000,
|
||||
maskRoK = 0x00000001,
|
||||
maskSoV = 0x00000003,
|
||||
@@ -199,7 +227,18 @@ namespace EQ
|
||||
maskHoT = 0x0001FFFF,
|
||||
maskVoA = 0x0003FFFF,
|
||||
maskRoF = 0x0007FFFF,
|
||||
maskCotF = 0x000FFFFF
|
||||
maskCotF = 0x000FFFFF,
|
||||
maskTDS = 0x001FFFFF,
|
||||
maskTBM = 0x003FFFFF,
|
||||
maskEoK = 0x007FFFFF,
|
||||
maskRoS = 0x00FFFFFF,
|
||||
maskTBL = 0x01FFFFFF,
|
||||
maskToV = 0x03FFFFFF,
|
||||
maskCoV = 0x07FFFFFF,
|
||||
maskToL = 0x0FFFFFFF,
|
||||
maskNoS = 0x1FFFFFFF,
|
||||
maskLS = 0x3FFFFFFF,
|
||||
maskTOB = 0x7FFFFFFF,
|
||||
};
|
||||
|
||||
const char* ExpansionName(Expansion expansion);
|
||||
|
||||
@@ -759,6 +759,46 @@ typedef enum {
|
||||
FilterStrikethrough = 26, //0=show, 1=hide // RoF2 Confirmed
|
||||
FilterStuns = 27, //0=show, 1=hide // RoF2 Confirmed
|
||||
FilterBardSongsOnPets = 28, //0=show, 1=hide // RoF2 Confirmed
|
||||
FilterSwarmPetDeath = 29,
|
||||
FilterFellowshipChat = 30,
|
||||
FilterMercenaryMessages = 31,
|
||||
FilterSpam = 32,
|
||||
FilterAchievements = 33,
|
||||
FilterPvPMessages = 34,
|
||||
FilterSpellNameInCast = 35,
|
||||
FilterRandomMine = 36,
|
||||
FilterRandomGroupRaid = 37,
|
||||
FilterRandomOthers = 38,
|
||||
FilterEnvironmentalDamage = 39,
|
||||
FilterMessages = 40,
|
||||
FilterOverwriteDetrimental = 41,
|
||||
FilterOverwriteBeneficial = 42,
|
||||
FilterCantUseCommand = 43,
|
||||
FilterCombatAbilityReuse = 44,
|
||||
FilterAAAbilityReuse = 45,
|
||||
FilterProcBeginCasting = 46,
|
||||
FilterDestroyedItems = 47,
|
||||
FilterYourAuras = 48,
|
||||
FilterOtherAuras = 49,
|
||||
FilterYourHeals = 50,
|
||||
FilterOtherHeals = 51,
|
||||
FilterYourDoTs = 52,
|
||||
FilterOtherDoTs = 53,
|
||||
FilterOtherDirectDamage = 54,
|
||||
FilterSpellEmotes = 55,
|
||||
FilterFactionMessages = 56,
|
||||
FilterTauntMessages = 57,
|
||||
FilterYourDisciplines = 58,
|
||||
FilterOtherDisplines = 59,
|
||||
FilterAchievementsOthers = 60,
|
||||
FilterRaidVictory = 61,
|
||||
FilterOtherDirectDamageCrits = 62,
|
||||
FilterDoTYoursCritical = 63,
|
||||
FilterDoTOthersCritical = 64,
|
||||
FilterDoTDamageTaken = 65,
|
||||
FilterHealsReceived = 66,
|
||||
FilterHealsYoursCritical = 67,
|
||||
FilterHealsOthersCritical = 68,
|
||||
_FilterCount
|
||||
} eqFilterType;
|
||||
|
||||
|
||||
@@ -110,6 +110,15 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
|
||||
RoF2::constants::CHARACTER_CREATION_LIMIT,
|
||||
RoF2::constants::SAY_LINK_BODY_SIZE,
|
||||
RoF2::constants::MAX_BAZAAR_TRADERS
|
||||
),
|
||||
/*[ClientVersion::TOB] =*/
|
||||
EQ::constants::LookupEntry(
|
||||
TOB::constants::EXPANSION,
|
||||
TOB::constants::EXPANSION_BIT,
|
||||
TOB::constants::EXPANSIONS_MASK,
|
||||
TOB::constants::CHARACTER_CREATION_LIMIT,
|
||||
TOB::constants::SAY_LINK_BODY_SIZE,
|
||||
TOB::constants::MAX_BAZAAR_TRADERS
|
||||
)
|
||||
};
|
||||
|
||||
@@ -376,6 +385,34 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
|
||||
RoF2::inventory::ConcatenateInvTypeLimbo,
|
||||
RoF2::inventory::AllowOverLevelEquipment
|
||||
),
|
||||
/*[MobVersion::TOB] =*/
|
||||
//TOBTodo: These need to be set to the latest values not just use RoF2
|
||||
EQ::inventory::LookupEntry(
|
||||
EQ::inventory::LookupEntry::InventoryTypeSize_Struct(
|
||||
EQ::invtype::POSSESSIONS_SIZE, RoF2::invtype::BANK_SIZE, RoF2::invtype::SHARED_BANK_SIZE,
|
||||
RoF2::invtype::TRADE_SIZE, RoF2::invtype::WORLD_SIZE, RoF2::invtype::LIMBO_SIZE,
|
||||
RoF2::invtype::TRIBUTE_SIZE, RoF2::invtype::TROPHY_TRIBUTE_SIZE, RoF2::invtype::GUILD_TRIBUTE_SIZE,
|
||||
RoF2::invtype::MERCHANT_SIZE, RoF2::invtype::DELETED_SIZE, RoF2::invtype::CORPSE_SIZE,
|
||||
RoF2::invtype::BAZAAR_SIZE, RoF2::invtype::INSPECT_SIZE, RoF2::invtype::REAL_ESTATE_SIZE,
|
||||
RoF2::invtype::VIEW_MOD_PC_SIZE, RoF2::invtype::VIEW_MOD_BANK_SIZE, RoF2::invtype::VIEW_MOD_SHARED_BANK_SIZE,
|
||||
RoF2::invtype::VIEW_MOD_LIMBO_SIZE, RoF2::invtype::ALT_STORAGE_SIZE, RoF2::invtype::ARCHIVED_SIZE,
|
||||
RoF2::invtype::MAIL_SIZE, RoF2::invtype::GUILD_TROPHY_TRIBUTE_SIZE, RoF2::invtype::KRONO_SIZE,
|
||||
RoF2::invtype::GUILD_BANK_MAIN_SIZE, RoF2::invtype::GUILD_BANK_DEPOSIT_SIZE, RoF2::invtype::OTHER_SIZE
|
||||
),
|
||||
|
||||
RoF2::invslot::EQUIPMENT_BITMASK,
|
||||
RoF2::invslot::GENERAL_BITMASK,
|
||||
RoF2::invslot::CURSOR_BITMASK,
|
||||
RoF2::invslot::POSSESSIONS_BITMASK,
|
||||
RoF2::invslot::CORPSE_BITMASK,
|
||||
RoF2::invbag::SLOT_COUNT,
|
||||
RoF2::invaug::SOCKET_COUNT,
|
||||
|
||||
RoF2::inventory::AllowEmptyBagInBag,
|
||||
RoF2::inventory::AllowClickCastFromBag,
|
||||
RoF2::inventory::ConcatenateInvTypeLimbo,
|
||||
RoF2::inventory::AllowOverLevelEquipment
|
||||
),
|
||||
/*[MobVersion::NPC] =*/
|
||||
EQ::inventory::LookupEntry(
|
||||
EQ::inventory::LookupEntry::InventoryTypeSize_Struct(
|
||||
@@ -748,6 +785,35 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
|
||||
RoF2::INULL,
|
||||
RoF2::invbag::SLOT_COUNT,
|
||||
RoF2::invaug::SOCKET_COUNT,
|
||||
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
),
|
||||
/*[MobVersion::OfflineTOB] =*/
|
||||
//TOBTodo: Need to use their own values instead of RoF2
|
||||
EQ::inventory::LookupEntry(
|
||||
EQ::inventory::LookupEntry::InventoryTypeSize_Struct(
|
||||
RoF2::INULL, RoF2::INULL, RoF2::INULL,
|
||||
RoF2::invtype::TRADE_SIZE, RoF2::INULL, RoF2::INULL,
|
||||
RoF2::INULL, RoF2::INULL, RoF2::INULL,
|
||||
RoF2::invtype::MERCHANT_SIZE, RoF2::INULL, RoF2::INULL,
|
||||
RoF2::invtype::BAZAAR_SIZE, RoF2::invtype::INSPECT_SIZE, RoF2::INULL,
|
||||
RoF2::invtype::VIEW_MOD_PC_SIZE, RoF2::invtype::VIEW_MOD_BANK_SIZE, RoF2::invtype::VIEW_MOD_SHARED_BANK_SIZE,
|
||||
RoF2::invtype::VIEW_MOD_LIMBO_SIZE, RoF2::INULL, RoF2::INULL,
|
||||
RoF2::INULL, RoF2::INULL, RoF2::INULL,
|
||||
RoF2::INULL, RoF2::INULL, RoF2::INULL
|
||||
),
|
||||
|
||||
RoF2::INULL,
|
||||
RoF2::INULL,
|
||||
RoF2::INULL,
|
||||
RoF2::INULL,
|
||||
RoF2::INULL,
|
||||
RoF2::invbag::SLOT_COUNT,
|
||||
RoF2::invaug::SOCKET_COUNT,
|
||||
|
||||
|
||||
false,
|
||||
false,
|
||||
@@ -1000,6 +1066,11 @@ static const EQ::behavior::LookupEntry behavior_static_lookup_entries[EQ::versio
|
||||
EQ::behavior::LookupEntry(
|
||||
RoF2::behavior::CoinHasWeight
|
||||
),
|
||||
/*[MobVersion::TOB] =*/
|
||||
//TOBTodo: We need this value set properly
|
||||
EQ::behavior::LookupEntry(
|
||||
RoF2::behavior::CoinHasWeight
|
||||
),
|
||||
/*[MobVersion::NPC] =*/
|
||||
EQ::behavior::LookupEntry(
|
||||
EQ::behavior::CoinHasWeight
|
||||
@@ -1053,6 +1124,11 @@ static const EQ::behavior::LookupEntry behavior_static_lookup_entries[EQ::versio
|
||||
RoF::behavior::CoinHasWeight
|
||||
),
|
||||
/*[MobVersion::OfflineRoF2] =*/
|
||||
EQ::behavior::LookupEntry(
|
||||
RoF2::behavior::CoinHasWeight
|
||||
),
|
||||
/*[MobVersion::OfflineTOB] =*/
|
||||
//TOBTodo: We need this value set properly
|
||||
EQ::behavior::LookupEntry(
|
||||
RoF2::behavior::CoinHasWeight
|
||||
)
|
||||
@@ -1208,6 +1284,19 @@ static const EQ::spells::LookupEntry spells_static_lookup_entries[EQ::versions::
|
||||
RoF2::spells::NPC_BUFFS,
|
||||
RoF2::spells::PET_BUFFS,
|
||||
RoF2::spells::MERC_BUFFS
|
||||
),
|
||||
/*[ClientVersion::TOB] =*/
|
||||
EQ::spells::LookupEntry(
|
||||
TOB::spells::SPELL_ID_MAX,
|
||||
TOB::spells::SPELLBOOK_SIZE,
|
||||
UF::spells::SPELL_GEM_COUNT, // client translators are setup to allow the max value a client supports..however, the top 4 indices are not valid in this case
|
||||
TOB::spells::LONG_BUFFS,
|
||||
TOB::spells::SHORT_BUFFS,
|
||||
TOB::spells::DISC_BUFFS,
|
||||
TOB::spells::TOTAL_BUFFS,
|
||||
TOB::spells::NPC_BUFFS,
|
||||
TOB::spells::PET_BUFFS,
|
||||
TOB::spells::MERC_BUFFS
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "common/patches/rof2_limits.h"
|
||||
#include "common/patches/sod_limits.h"
|
||||
#include "common/patches/sof_limits.h"
|
||||
#include "common/patches/tob_limits.h"
|
||||
#include "common/patches/titanium_limits.h"
|
||||
#include "common/patches/uf_limits.h"
|
||||
#include "common/types.h"
|
||||
|
||||
+20
-11
@@ -31,9 +31,19 @@
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
EQPacket::EQPacket(EmuOpcode op, const unsigned char *buf, uint32 len)
|
||||
: BasePacket(buf, len),
|
||||
emu_opcode(op)
|
||||
EQPacket::EQPacket()
|
||||
{
|
||||
}
|
||||
|
||||
EQPacket::EQPacket(EmuOpcode op, const unsigned char *buf, size_t len)
|
||||
: BasePacket(buf, len)
|
||||
, emu_opcode(op)
|
||||
{
|
||||
}
|
||||
|
||||
EQPacket::EQPacket(EmuOpcode opcode, SerializeBuffer&& buf)
|
||||
: BasePacket(std::move(buf))
|
||||
, emu_opcode(opcode)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -360,17 +370,16 @@ EQRawApplicationPacket::EQRawApplicationPacket(const unsigned char *buf, const u
|
||||
}
|
||||
}
|
||||
|
||||
void DumpPacket(const EQApplicationPacket* app, bool iShowInfo) {
|
||||
void DumpPacket(const EQApplicationPacket* app, bool iShowInfo)
|
||||
{
|
||||
if (iShowInfo) {
|
||||
std::cout << "Dumping Applayer: 0x" << std::hex << std::setfill('0') << std::setw(4) << app->GetOpcode() << std::dec;
|
||||
std::cout << " size:" << app->size << std::endl;
|
||||
printf("Dumping Applayer: 0x%04x size: %u", app->GetOpcode(), app->size);
|
||||
}
|
||||
|
||||
DumpPacketHex(app->pBuffer, app->size);
|
||||
// DumpPacketAscii(app->pBuffer, app->size);
|
||||
}
|
||||
|
||||
std::string DumpPacketToString(const EQApplicationPacket* app){
|
||||
std::ostringstream out;
|
||||
out << DumpPacketHexToString(app->pBuffer, app->size);
|
||||
return out.str();
|
||||
std::string DumpPacketToString(const EQApplicationPacket* app)
|
||||
{
|
||||
return DumpPacketHexToString(app->pBuffer, app->size);
|
||||
}
|
||||
|
||||
+49
-29
@@ -28,8 +28,15 @@
|
||||
#include "common/emu_opcodes.h"
|
||||
#endif
|
||||
|
||||
class EQPacket : public BasePacket {
|
||||
class EQPacket : public BasePacket
|
||||
{
|
||||
friend class EQStream;
|
||||
|
||||
protected:
|
||||
EQPacket();
|
||||
EQPacket(EmuOpcode opcode, const unsigned char* buf, size_t len);
|
||||
EQPacket(EmuOpcode opcode, SerializeBuffer&& buf);
|
||||
|
||||
public:
|
||||
virtual ~EQPacket() {}
|
||||
|
||||
@@ -41,19 +48,12 @@ public:
|
||||
virtual void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const;
|
||||
|
||||
void SetOpcode(EmuOpcode op) { emu_opcode = op; }
|
||||
const EmuOpcode GetOpcode() const { return(emu_opcode); }
|
||||
// const char *GetOpcodeName() const;
|
||||
EmuOpcode GetOpcode() const { return(emu_opcode); }
|
||||
|
||||
protected:
|
||||
//this is just a cache so we dont look it up several times on Get()
|
||||
//and it is mutable so we can store the cached copy even on a const object
|
||||
EmuOpcode emu_opcode;
|
||||
|
||||
EQPacket(EmuOpcode opcode, const unsigned char *buf, const uint32 len);
|
||||
EQPacket(EmuOpcode opcode, SerializeBuffer &buf) : BasePacket(buf), emu_opcode(opcode) { };
|
||||
// EQPacket(const EQPacket &p) { }
|
||||
EQPacket() { emu_opcode=OP_Unknown; pBuffer=nullptr; size=0; }
|
||||
|
||||
EmuOpcode emu_opcode = OP_Unknown;
|
||||
};
|
||||
|
||||
class EQRawApplicationPacket;
|
||||
@@ -90,19 +90,43 @@ protected:
|
||||
uint16 opcode;
|
||||
};
|
||||
|
||||
class EQApplicationPacket : public EQPacket {
|
||||
class EQApplicationPacket : public EQPacket
|
||||
{
|
||||
friend class EQStream;
|
||||
|
||||
public:
|
||||
EQApplicationPacket()
|
||||
{
|
||||
}
|
||||
|
||||
EQApplicationPacket(EmuOpcode op)
|
||||
: EQPacket(op, nullptr, 0)
|
||||
{
|
||||
}
|
||||
|
||||
EQApplicationPacket(EmuOpcode op, size_t len)
|
||||
: EQPacket(op, nullptr, len)
|
||||
{
|
||||
}
|
||||
EQApplicationPacket(EmuOpcode op, const unsigned char* buf, size_t len)
|
||||
: EQPacket(op, buf, len)
|
||||
{
|
||||
}
|
||||
|
||||
EQApplicationPacket(EmuOpcode op, SerializeBuffer&& buf)
|
||||
: EQPacket(op, std::move(buf))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
EQApplicationPacket(const EQApplicationPacket& p)
|
||||
: EQPacket(p.emu_opcode, p.pBuffer, p.size)
|
||||
, app_opcode_size(p.app_opcode_size)
|
||||
, opcode_bypass(p.opcode_bypass)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
EQApplicationPacket() : EQPacket(OP_Unknown, nullptr, 0), opcode_bypass(0)
|
||||
{ app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; }
|
||||
EQApplicationPacket(const EmuOpcode op) : EQPacket(op, nullptr, 0), opcode_bypass(0)
|
||||
{ app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; }
|
||||
EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(op, nullptr, len), opcode_bypass(0)
|
||||
{ app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; }
|
||||
EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(op, buf, len), opcode_bypass(0)
|
||||
{ app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; }
|
||||
EQApplicationPacket(const EmuOpcode op, SerializeBuffer &buf) : EQPacket(op, buf), opcode_bypass(0)
|
||||
{ app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; }
|
||||
bool combine(const EQApplicationPacket *rhs);
|
||||
uint32 serialize (uint16 opcode, unsigned char *dest) const;
|
||||
uint32 Size() const { return size+app_opcode_size; }
|
||||
@@ -119,15 +143,11 @@ public:
|
||||
|
||||
uint16 GetProtocolOpcode() const { return protocol_opcode; }
|
||||
void SetProtocolOpcode(uint16 v) { protocol_opcode = v; }
|
||||
|
||||
protected:
|
||||
|
||||
uint16 protocol_opcode;
|
||||
uint8 app_opcode_size;
|
||||
uint16 opcode_bypass;
|
||||
private:
|
||||
|
||||
EQApplicationPacket(const EQApplicationPacket &p) : EQPacket(p.emu_opcode, p.pBuffer, p.size), opcode_bypass(p.opcode_bypass) { app_opcode_size = p.app_opcode_size; }
|
||||
|
||||
uint16 protocol_opcode = 0;
|
||||
uint8 app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2;
|
||||
uint16 opcode_bypass = 0;
|
||||
};
|
||||
|
||||
class EQRawApplicationPacket : public EQApplicationPacket {
|
||||
|
||||
+32
-18
@@ -47,6 +47,13 @@ static const uint32 ADVANCED_LORE_LENGTH = 8192;
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
struct EqGuid
|
||||
{
|
||||
uint32_t Id;
|
||||
uint16_t WorldId;
|
||||
uint16_t Reserved;
|
||||
};
|
||||
|
||||
struct LoginInfo {
|
||||
/*000*/ char login_info[64];
|
||||
/*064*/ uint8 unknown064[124];
|
||||
@@ -121,7 +128,7 @@ struct LDoNTrapTemplate
|
||||
{
|
||||
uint32 id;
|
||||
LDoNChestTypes type;
|
||||
uint32 spell_id;
|
||||
int32 spell_id;
|
||||
uint16 skill;
|
||||
uint8 locked;
|
||||
};
|
||||
@@ -326,6 +333,7 @@ union
|
||||
bool buyer;
|
||||
bool untargetable;
|
||||
uint32 npc_tint_id;
|
||||
EqGuid CharacterGuid;
|
||||
};
|
||||
|
||||
struct PlayerState_Struct {
|
||||
@@ -414,7 +422,7 @@ struct NewZone_Struct {
|
||||
*/
|
||||
struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
uint32 reduction; // lower reuse
|
||||
};
|
||||
@@ -463,7 +471,7 @@ struct ManaChange_Struct
|
||||
{
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*08*/ int32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
/*16*/ int32 slot; // -1 normal, otherwise clear ETA and GCD
|
||||
@@ -481,14 +489,14 @@ struct BeginCast_Struct
|
||||
{
|
||||
// len = 8
|
||||
/*000*/ uint16 caster_id;
|
||||
/*002*/ uint16 spell_id;
|
||||
/*002*/ int16 spell_id;
|
||||
/*004*/ uint32 cast_time; // in miliseconds
|
||||
};
|
||||
|
||||
struct CastSpell_Struct
|
||||
{
|
||||
uint32 slot;
|
||||
uint32 spell_id;
|
||||
int32 spell_id;
|
||||
uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast
|
||||
uint32 target_id;
|
||||
uint32 cs_unknown1;
|
||||
@@ -536,7 +544,7 @@ struct SpellBuff_Struct
|
||||
/*001*/ uint8 level;
|
||||
/*002*/ uint8 bard_modifier;
|
||||
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking? -- OSX client calls this "activated"
|
||||
/*004*/ uint32 spellid;
|
||||
/*004*/ int32 spellid;
|
||||
/*008*/ int32 duration;
|
||||
/*012*/ uint32 counters; // single book keeping value (counters, rune/vie)
|
||||
/*016*/ uint32 player_id; // caster ID, pretty sure just zone ID
|
||||
@@ -566,7 +574,7 @@ struct BuffRemoveRequest_Struct
|
||||
|
||||
struct PetBuff_Struct {
|
||||
/*000*/ uint32 petid;
|
||||
/*004*/ uint32 spellid[PET_BUFF_COUNT];
|
||||
/*004*/ int32 spellid[PET_BUFF_COUNT];
|
||||
/*124*/ int32 ticsremaining[PET_BUFF_COUNT];
|
||||
/*244*/ uint32 buffcount;
|
||||
};
|
||||
@@ -867,7 +875,7 @@ struct BindStruct {
|
||||
|
||||
struct SuspendedMinion_Struct
|
||||
{
|
||||
/*000*/ uint16 SpellID;
|
||||
/*000*/ int16 SpellID;
|
||||
/*002*/ uint32 HP;
|
||||
/*006*/ uint32 Mana;
|
||||
/*010*/ SpellBuff_Struct Buffs[BUFF_COUNT];
|
||||
@@ -1002,7 +1010,7 @@ struct PlayerProfile_Struct
|
||||
/*2505*/ uint8 unknown2541[47]; // ?
|
||||
/*2552*/ uint8 languages[MAX_PP_LANGUAGE];
|
||||
/*2580*/ uint8 unknown2616[4];
|
||||
/*2584*/ uint32 spell_book[EQ::spells::SPELLBOOK_SIZE];
|
||||
/*2584*/ int32 spell_book[EQ::spells::SPELLBOOK_SIZE];
|
||||
/*4504*/ uint8 unknown4540[128]; // Was [428] all 0xff
|
||||
/*4632*/ uint32 mem_spells[EQ::spells::SPELL_GEM_COUNT];
|
||||
/*4668*/ uint8 unknown4704[32]; //
|
||||
@@ -1336,7 +1344,7 @@ struct CombatDamage_Struct
|
||||
/* 00 */ uint16 target;
|
||||
/* 02 */ uint16 source;
|
||||
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells, skill
|
||||
/* 05 */ uint16 spellid;
|
||||
/* 05 */ int16 spellid;
|
||||
/* 07 */ uint32 damage;
|
||||
/* 11 */ float force;
|
||||
/* 15 */ float hit_heading; // see above notes in Action_Struct
|
||||
@@ -1369,7 +1377,7 @@ struct Death_Struct
|
||||
/*004*/ uint32 killer_id;
|
||||
/*008*/ uint32 corpseid; // was corpseid
|
||||
/*012*/ uint32 bindzoneid;
|
||||
/*016*/ uint32 spell_id;
|
||||
/*016*/ int32 spell_id;
|
||||
/*020*/ uint32 attack_skill;
|
||||
/*024*/ uint32 damage;
|
||||
/*028*/ uint32 unknown028;
|
||||
@@ -2975,7 +2983,7 @@ struct Resurrect_Struct {
|
||||
/*024*/ char your_name[64];
|
||||
/*088*/ uint32 unknown088;
|
||||
/*092*/ char rezzer_name[64];
|
||||
/*156*/ uint32 spellid;
|
||||
/*156*/ int32 spellid;
|
||||
/*160*/ char corpse_name[64];
|
||||
/*224*/ uint32 action;
|
||||
/* 228 */
|
||||
@@ -2983,7 +2991,7 @@ struct Resurrect_Struct {
|
||||
|
||||
struct Translocate_Struct {
|
||||
/*000*/ uint32 ZoneID;
|
||||
/*004*/ uint32 SpellID;
|
||||
/*004*/ int32 SpellID;
|
||||
/*008*/ uint32 unknown008; //Heading ?
|
||||
/*012*/ char Caster[64];
|
||||
/*076*/ float y;
|
||||
@@ -3000,7 +3008,7 @@ struct PendingTranslocate_Struct
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
uint32 spell_id;
|
||||
int32 spell_id;
|
||||
};
|
||||
|
||||
struct Sacrifice_Struct {
|
||||
@@ -4867,7 +4875,7 @@ struct MarkNPC_Struct
|
||||
};
|
||||
|
||||
struct InspectBuffs_Struct {
|
||||
/*000*/ uint32 spell_id[BUFF_COUNT];
|
||||
/*000*/ int32 spell_id[BUFF_COUNT];
|
||||
/*100*/ int32 tics_remaining[BUFF_COUNT];
|
||||
};
|
||||
|
||||
@@ -5136,7 +5144,7 @@ struct SendAA_Struct {
|
||||
/*0040*/ uint32 prereq_skill; //is < 0, abs() is category #
|
||||
/*0044*/ uint32 prereq_minpoints; //min points in the prereq
|
||||
/*0048*/ uint32 type;
|
||||
/*0052*/ uint32 spellid;
|
||||
/*0052*/ int32 spellid;
|
||||
/*0056*/ uint32 spell_type;
|
||||
/*0060*/ uint32 spell_refresh;
|
||||
/*0064*/ uint16 classes;
|
||||
@@ -5637,7 +5645,7 @@ struct HideCorpse_Struct
|
||||
struct BuffIconEntry_Struct
|
||||
{
|
||||
uint32 buff_slot;
|
||||
uint32 spell_id;
|
||||
int32 spell_id;
|
||||
int32 tics_remaining;
|
||||
uint32 num_hits;
|
||||
char caster[64];
|
||||
@@ -5648,7 +5656,7 @@ struct BuffIcon_Struct
|
||||
uint32 entity_id;
|
||||
uint8 all_buffs;
|
||||
uint16 count;
|
||||
uint8 type; // 0 = self buff window, 1 = self target window, 4 = group, 5 = PC, 7 = NPC
|
||||
uint8 type; // 0 = self buff window, 1 = self target window, 2 = pet buff or target window, 4 = group, 5 = PC, 7 = NPC
|
||||
int32 tic_timer;
|
||||
int32 name_lengths; // so ahh we kind of do these packets hacky, this is the total length of all the names to make creating the real packets in the translators easier
|
||||
BuffIconEntry_Struct entries[0];
|
||||
@@ -6236,6 +6244,12 @@ struct SuspendMercenary_Struct {
|
||||
/*0001*/
|
||||
};
|
||||
|
||||
// [OPCode: 0x1b37 (RoF2)] [Client->Server] [Size: 4]
|
||||
struct SwitchMercenary_Struct {
|
||||
/*0000*/ uint32 MercIndex; // 0-based UI index into owned merc list
|
||||
/*0004*/
|
||||
};
|
||||
|
||||
// [OPCode: 0x2528] On Live as of April 2 2012 [Server->Client] [Size: 4]
|
||||
// Response to suspend merc with timestamp
|
||||
struct SuspendMercenaryResponse_Struct {
|
||||
|
||||
@@ -26,13 +26,13 @@
|
||||
//this is the only part of an EQStream that is seen by the application.
|
||||
|
||||
|
||||
typedef enum {
|
||||
enum EQStreamState {
|
||||
ESTABLISHED,
|
||||
CLOSING, //waiting for pending data to flush.
|
||||
DISCONNECTING, //have sent disconnect, waiting for their disconnect reply.
|
||||
CLOSED, //received a disconnect from remote side.
|
||||
UNESTABLISHED
|
||||
} EQStreamState;
|
||||
};
|
||||
|
||||
class EQApplicationPacket;
|
||||
class OpcodeManager;
|
||||
|
||||
@@ -33,10 +33,8 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#include <conio.h>
|
||||
#include "common/platform/platform.h"
|
||||
#include <direct.h>
|
||||
#include <process.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <thread>
|
||||
|
||||
@@ -15,36 +15,43 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "common/platform/posix/include_pthreads.h"
|
||||
#include "common/platform/win/include_windows.h"
|
||||
#include "common/event/event_loop.h"
|
||||
#include "uv.h"
|
||||
|
||||
class Mutex {
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
namespace EQ {
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
bool trylock();
|
||||
protected:
|
||||
private:
|
||||
#if defined _WINDOWS
|
||||
CRITICAL_SECTION CSMutex;
|
||||
#else
|
||||
pthread_mutex_t CSMutex;
|
||||
#endif
|
||||
};
|
||||
EventLoop& EventLoop::Get()
|
||||
{
|
||||
thread_local EventLoop inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
class LockMutex {
|
||||
public:
|
||||
LockMutex(Mutex* in_mut, bool iLock = true);
|
||||
~LockMutex();
|
||||
void unlock();
|
||||
void lock();
|
||||
private:
|
||||
bool locked;
|
||||
Mutex* mut;
|
||||
};
|
||||
EventLoop::EventLoop()
|
||||
: m_loop(std::make_unique<uv_loop_t>())
|
||||
{
|
||||
memset(m_loop.get(), 0, sizeof(uv_loop_t));
|
||||
uv_loop_init(m_loop.get());
|
||||
}
|
||||
|
||||
EventLoop::~EventLoop()
|
||||
{
|
||||
uv_loop_close(m_loop.get());
|
||||
}
|
||||
|
||||
void EventLoop::Process()
|
||||
{
|
||||
uv_run(m_loop.get(), UV_RUN_NOWAIT);
|
||||
}
|
||||
|
||||
void EventLoop::Run()
|
||||
{
|
||||
uv_run(m_loop.get(), UV_RUN_DEFAULT);
|
||||
}
|
||||
|
||||
void EventLoop::Shutdown()
|
||||
{
|
||||
uv_stop(m_loop.get());
|
||||
}
|
||||
|
||||
} // namespace EQ
|
||||
+19
-36
@@ -17,48 +17,31 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/platform/win/include_windows.h" // uv.h is going to include it so let's do it first.
|
||||
#include "uv.h" // FIXME: hide this
|
||||
#include <memory>
|
||||
|
||||
#include <cstring>
|
||||
typedef struct uv_loop_s uv_loop_t;
|
||||
|
||||
namespace EQ
|
||||
namespace EQ {
|
||||
|
||||
class EventLoop
|
||||
{
|
||||
class EventLoop
|
||||
{
|
||||
public:
|
||||
static EventLoop &Get() {
|
||||
static thread_local EventLoop inst;
|
||||
return inst;
|
||||
}
|
||||
public:
|
||||
static EventLoop& Get();
|
||||
|
||||
~EventLoop() {
|
||||
uv_loop_close(&m_loop);
|
||||
}
|
||||
~EventLoop();
|
||||
EventLoop(const EventLoop&) = delete;
|
||||
EventLoop& operator=(const EventLoop&) = delete;
|
||||
|
||||
void Process() {
|
||||
uv_run(&m_loop, UV_RUN_NOWAIT);
|
||||
}
|
||||
void Process();
|
||||
void Run();
|
||||
void Shutdown();
|
||||
|
||||
void Run() {
|
||||
uv_run(&m_loop, UV_RUN_DEFAULT);
|
||||
}
|
||||
uv_loop_t* Handle() { return m_loop.get(); }
|
||||
|
||||
void Shutdown() {
|
||||
uv_stop(&m_loop);
|
||||
}
|
||||
private:
|
||||
EventLoop();
|
||||
|
||||
uv_loop_t* Handle() { return &m_loop; }
|
||||
std::unique_ptr<uv_loop_t> m_loop;
|
||||
};
|
||||
|
||||
private:
|
||||
EventLoop() {
|
||||
memset(&m_loop, 0, sizeof(uv_loop_t));
|
||||
uv_loop_init(&m_loop);
|
||||
}
|
||||
|
||||
EventLoop(const EventLoop&);
|
||||
EventLoop& operator=(const EventLoop&);
|
||||
|
||||
uv_loop_t m_loop;
|
||||
};
|
||||
}
|
||||
} // namespace EQ
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "task_scheduler.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace EQ::Event {
|
||||
|
||||
static constexpr int DefaultThreadCount = 4;
|
||||
|
||||
struct TaskScheduler::SchedulerData
|
||||
{
|
||||
std::atomic_bool running{ false };
|
||||
std::vector<std::thread> threads;
|
||||
std::mutex lock;
|
||||
std::condition_variable cv;
|
||||
std::queue<std::function<void()>> tasks;
|
||||
};
|
||||
|
||||
TaskScheduler::TaskScheduler()
|
||||
: m_data(std::make_unique<SchedulerData>())
|
||||
{
|
||||
Start(DefaultThreadCount);
|
||||
}
|
||||
|
||||
TaskScheduler::TaskScheduler(size_t threads)
|
||||
{
|
||||
Start(threads);
|
||||
}
|
||||
|
||||
TaskScheduler::~TaskScheduler()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void TaskScheduler::Start(size_t threads)
|
||||
{
|
||||
if (m_data->running.exchange(true))
|
||||
return;
|
||||
|
||||
m_data->threads.reserve(threads);
|
||||
|
||||
for (size_t i = 0; i < threads; ++i)
|
||||
{
|
||||
m_data->threads.emplace_back(
|
||||
[this]{ ProcessWork(); });
|
||||
}
|
||||
}
|
||||
|
||||
void TaskScheduler::Stop()
|
||||
{
|
||||
if (!m_data->running.exchange(false))
|
||||
return;
|
||||
|
||||
m_data->cv.notify_all();
|
||||
|
||||
for (auto& t : m_data->threads)
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
|
||||
m_data->threads.clear();
|
||||
}
|
||||
|
||||
void TaskScheduler::ProcessWork()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
std::function<void()> work;
|
||||
|
||||
{
|
||||
std::unique_lock lock(m_data->lock);
|
||||
|
||||
m_data->cv.wait(lock,
|
||||
[this]
|
||||
{
|
||||
return !m_data->running || !m_data->tasks.empty();
|
||||
});
|
||||
|
||||
if (!m_data->running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
work = std::move(m_data->tasks.front());
|
||||
m_data->tasks.pop();
|
||||
}
|
||||
|
||||
work();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskScheduler::AddTask(std::function<void()>&& task)
|
||||
{
|
||||
if (!m_data->running)
|
||||
{
|
||||
throw std::runtime_error("Enqueue on stopped scheduler.");
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock lock(m_data->lock);
|
||||
m_data->tasks.push(std::move(task));
|
||||
}
|
||||
m_data->cv.notify_one();
|
||||
}
|
||||
|
||||
} // namespace EQ::Event
|
||||
+33
-104
@@ -17,116 +17,45 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace EQ
|
||||
namespace EQ::Event {
|
||||
|
||||
class TaskScheduler
|
||||
{
|
||||
namespace Event
|
||||
public:
|
||||
TaskScheduler();
|
||||
TaskScheduler(size_t threads);
|
||||
|
||||
~TaskScheduler();
|
||||
|
||||
void Start(size_t threads);
|
||||
void Stop();
|
||||
|
||||
template <typename Fn, typename... Args>
|
||||
auto Enqueue(Fn&& fn, Args&&... args) -> std::future<typename std::invoke_result<Fn, Args...>::type>
|
||||
{
|
||||
class TaskScheduler
|
||||
{
|
||||
public:
|
||||
static const int DefaultThreadCount = 4;
|
||||
|
||||
TaskScheduler() : _running(false)
|
||||
using return_type = typename std::invoke_result<Fn, Args...>::type;
|
||||
|
||||
auto task = std::make_shared<std::packaged_task<return_type()>>(
|
||||
[fn = std::forward<Fn>(fn), ...args = std::forward<Args>(args)]() mutable
|
||||
{
|
||||
Start(DefaultThreadCount);
|
||||
}
|
||||
|
||||
TaskScheduler(size_t threads) : _running(false)
|
||||
{
|
||||
Start(threads);
|
||||
return fn(std::forward<Args>(args)...);
|
||||
}
|
||||
);
|
||||
|
||||
~TaskScheduler() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void Start(size_t threads) {
|
||||
if (true == _running) {
|
||||
return;
|
||||
}
|
||||
|
||||
_running = true;
|
||||
|
||||
for (size_t i = 0; i < threads; ++i) {
|
||||
_threads.emplace_back(std::thread(std::bind(&TaskScheduler::ProcessWork, this)));
|
||||
}
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
if (false == _running) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_lock);
|
||||
_running = false;
|
||||
}
|
||||
|
||||
_cv.notify_all();
|
||||
|
||||
for (auto &t : _threads) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Fn, typename... Args>
|
||||
auto Enqueue(Fn&& fn, Args&&... args) -> std::future<typename std::invoke_result<Fn, Args...>::type> {
|
||||
using return_type = typename std::invoke_result<Fn, Args...>::type;
|
||||
|
||||
auto task = std::make_shared<std::packaged_task<return_type()>>(
|
||||
std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...)
|
||||
);
|
||||
|
||||
std::future<return_type> res = task->get_future();
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_lock);
|
||||
|
||||
if (false == _running) {
|
||||
throw std::runtime_error("Enqueue on stopped scheduler.");
|
||||
}
|
||||
|
||||
_tasks.emplace([task]() { (*task)(); });
|
||||
}
|
||||
|
||||
_cv.notify_one();
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
void ProcessWork() {
|
||||
for (;;) {
|
||||
std::function<void()> work;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_lock);
|
||||
_cv.wait(lock, [this] { return !_running || !_tasks.empty(); });
|
||||
|
||||
if (false == _running) {
|
||||
return;
|
||||
}
|
||||
|
||||
work = std::move(_tasks.front());
|
||||
_tasks.pop();
|
||||
}
|
||||
|
||||
work();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool _running = true;
|
||||
std::vector<std::thread> _threads;
|
||||
std::mutex _lock;
|
||||
std::condition_variable _cv;
|
||||
std::queue<std::function<void()>> _tasks;
|
||||
};
|
||||
AddTask([task] { (*task)(); });
|
||||
return task->get_future();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void AddTask(std::function<void()>&& task);
|
||||
void ProcessWork();
|
||||
|
||||
struct SchedulerData;
|
||||
std::unique_ptr<SchedulerData> m_data;
|
||||
};
|
||||
|
||||
} // namespace EQ::Event
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/event/timer.h"
|
||||
#include "uv.h"
|
||||
|
||||
namespace EQ {
|
||||
|
||||
Timer::Timer(callback_t cb)
|
||||
: m_cb(std::move(cb))
|
||||
{
|
||||
}
|
||||
|
||||
Timer::Timer(uint64_t duration_ms, bool repeats, callback_t cb)
|
||||
: m_cb(std::move(cb))
|
||||
{
|
||||
Start(duration_ms, repeats);
|
||||
}
|
||||
|
||||
Timer::~Timer()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void Timer::Start(uint64_t duration_ms, bool repeats)
|
||||
{
|
||||
if (!m_timer)
|
||||
{
|
||||
uv_loop_t* loop = EventLoop::Get().Handle();
|
||||
|
||||
m_timer = std::make_unique<uv_timer_t>();
|
||||
memset(m_timer.get(), 0, sizeof(uv_timer_t));
|
||||
|
||||
uv_timer_init(loop, m_timer.get());
|
||||
m_timer->data = this;
|
||||
|
||||
if (repeats)
|
||||
{
|
||||
uv_timer_start(m_timer.get(), [](uv_timer_t* handle)
|
||||
{
|
||||
Timer* t = static_cast<Timer*>(handle->data);
|
||||
t->Execute();
|
||||
}, duration_ms, duration_ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
uv_timer_start(m_timer.get(),
|
||||
[](uv_timer_t* handle)
|
||||
{
|
||||
Timer* t = static_cast<Timer*>(handle->data);
|
||||
t->Stop();
|
||||
t->Execute();
|
||||
}, duration_ms, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::Stop()
|
||||
{
|
||||
if (m_timer)
|
||||
{
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(m_timer.release()),
|
||||
[](uv_handle_t* handle)
|
||||
{
|
||||
delete reinterpret_cast<uv_timer_t*>(handle);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::Execute()
|
||||
{
|
||||
m_cb(this);
|
||||
}
|
||||
|
||||
} // namespace EQ
|
||||
+20
-58
@@ -19,69 +19,31 @@
|
||||
|
||||
#include "event_loop.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
typedef struct uv_timer_s uv_timer_t;
|
||||
|
||||
namespace EQ {
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
Timer(std::function<void(Timer *)> cb)
|
||||
{
|
||||
m_timer = nullptr;
|
||||
m_cb = cb;
|
||||
}
|
||||
|
||||
Timer(uint64_t duration_ms, bool repeats, std::function<void(Timer *)> cb)
|
||||
{
|
||||
m_timer = nullptr;
|
||||
m_cb = cb;
|
||||
Start(duration_ms, repeats);
|
||||
}
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
using callback_t = std::function<void(Timer*)>;
|
||||
|
||||
Timer(callback_t cb);
|
||||
Timer(uint64_t duration_ms, bool repeats, callback_t cb);
|
||||
|
||||
~Timer()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
~Timer();
|
||||
|
||||
void Start(uint64_t duration_ms, bool repeats) {
|
||||
auto loop = EventLoop::Get().Handle();
|
||||
if (!m_timer) {
|
||||
m_timer = new uv_timer_t;
|
||||
memset(m_timer, 0, sizeof(uv_timer_t));
|
||||
uv_timer_init(loop, m_timer);
|
||||
m_timer->data = this;
|
||||
void Start(uint64_t duration_ms, bool repeats);
|
||||
void Stop();
|
||||
|
||||
if (repeats) {
|
||||
uv_timer_start(m_timer, [](uv_timer_t *handle) {
|
||||
Timer *t = (Timer*)handle->data;
|
||||
t->Execute();
|
||||
}, duration_ms, duration_ms);
|
||||
}
|
||||
else {
|
||||
uv_timer_start(m_timer, [](uv_timer_t *handle) {
|
||||
Timer *t = (Timer*)handle->data;
|
||||
t->Stop();
|
||||
t->Execute();
|
||||
}, duration_ms, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
if (m_timer) {
|
||||
uv_close((uv_handle_t*)m_timer, [](uv_handle_t* handle) {
|
||||
delete (uv_timer_t *)handle;
|
||||
});
|
||||
m_timer = nullptr;
|
||||
}
|
||||
}
|
||||
private:
|
||||
void Execute() {
|
||||
m_cb(this);
|
||||
}
|
||||
private:
|
||||
void Execute();
|
||||
|
||||
uv_timer_t *m_timer;
|
||||
std::function<void(Timer*)> m_cb;
|
||||
};
|
||||
}
|
||||
std::unique_ptr<uv_timer_t> m_timer;
|
||||
callback_t m_cb;
|
||||
};
|
||||
|
||||
} // namespace EQ
|
||||
|
||||
@@ -56,12 +56,12 @@ void PlayerEventLogs::Init()
|
||||
auto s = PlayerEventLogSettingsRepository::All(*m_database);
|
||||
std::vector<int> db{};
|
||||
db.reserve(s.size());
|
||||
for (auto &e: s) {
|
||||
for (auto& e: s) {
|
||||
if (e.id >= PlayerEvent::MAX) {
|
||||
continue;
|
||||
}
|
||||
m_settings[e.id] = e;
|
||||
db.emplace_back(e.id);
|
||||
db.emplace_back(static_cast<int>(e.id));
|
||||
}
|
||||
|
||||
std::vector<PlayerEventLogSettingsRepository::PlayerEventLogSettings> settings_to_insert{};
|
||||
|
||||
@@ -960,7 +960,7 @@ namespace PlayerEvent {
|
||||
struct ResurrectAcceptEvent {
|
||||
std::string resurrecter_name;
|
||||
std::string spell_name;
|
||||
uint32 spell_id;
|
||||
int32 spell_id;
|
||||
|
||||
// cereal
|
||||
template <class Archive>
|
||||
@@ -1058,7 +1058,7 @@ namespace PlayerEvent {
|
||||
uint32 killer_id;
|
||||
std::string killer_name;
|
||||
int64 damage;
|
||||
uint32 spell_id;
|
||||
int32 spell_id;
|
||||
std::string spell_name;
|
||||
int skill_id;
|
||||
std::string skill_name;
|
||||
|
||||
+2
-2
@@ -46,7 +46,7 @@
|
||||
#define GUILD_INITIATE 7
|
||||
#define GUILD_RECRUIT 8
|
||||
|
||||
typedef enum {
|
||||
enum GuildAction {
|
||||
GUILD_ACTION_BANNER_CHANGE = 1,
|
||||
GUILD_ACTION_BANNER_PLANT = 2,
|
||||
GUILD_ACTION_BANNER_REMOVE = 3,
|
||||
@@ -77,6 +77,6 @@ typedef enum {
|
||||
GUILD_ACTION_REAL_ESTATE_GUILD_PLOT_SELL = 28,
|
||||
GUILD_ACTION_REAL_ESTATE_MODIFY_TROPHIES = 29,
|
||||
GUILD_ACTION_MEMBERS_DEMOTE_SELF = 30,
|
||||
} GuildAction;
|
||||
};
|
||||
|
||||
constexpr int format_as(GuildAction action) { return static_cast<int>(action); }
|
||||
|
||||
@@ -349,7 +349,7 @@ bool EQ::InventoryProfile::SwapItem(
|
||||
fail_state = swapLevel;
|
||||
return false;
|
||||
}
|
||||
if (source_item_instance->IsEvolving() > 0) {
|
||||
if (source_item_instance->IsEvolving()) {
|
||||
source_item_instance->SetEvolveEquipped(true);
|
||||
}
|
||||
}
|
||||
|
||||
+34
-125
@@ -25,14 +25,24 @@
|
||||
#include "common/net/dns.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include <cstring>
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @param ip
|
||||
* @return
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
uint32_t IpUtil::IPToUInt(const std::string &ip)
|
||||
{
|
||||
int a, b, c, d;
|
||||
@@ -49,12 +59,6 @@ uint32_t IpUtil::IPToUInt(const std::string &ip)
|
||||
return addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ip
|
||||
* @param network
|
||||
* @param mask
|
||||
* @return
|
||||
*/
|
||||
bool IpUtil::IsIpInRange(const std::string &ip, const std::string &network, const std::string &mask)
|
||||
{
|
||||
uint32_t ip_addr = IpUtil::IPToUInt(ip);
|
||||
@@ -67,10 +71,6 @@ bool IpUtil::IsIpInRange(const std::string &ip, const std::string &network, cons
|
||||
return ip_addr >= net_lower && ip_addr <= net_upper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ip
|
||||
* @return
|
||||
*/
|
||||
bool IpUtil::IsIpInPrivateRfc1918(const std::string &ip)
|
||||
{
|
||||
return (
|
||||
@@ -80,30 +80,13 @@ bool IpUtil::IsIpInPrivateRfc1918(const std::string &ip)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
std::string IpUtil::GetLocalIPAddress()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
return "";
|
||||
}
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
char my_ip_address[INET_ADDRSTRLEN];
|
||||
@@ -114,10 +97,10 @@ std::string IpUtil::GetLocalIPAddress()
|
||||
// Create a UDP socket
|
||||
#ifdef _WIN32
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd == INVALID_SOCKET) {
|
||||
WSACleanup();
|
||||
return "";
|
||||
}
|
||||
if (sockfd == INVALID_SOCKET) {
|
||||
WSACleanup();
|
||||
return "";
|
||||
}
|
||||
#else
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd < 0) {
|
||||
@@ -200,98 +183,24 @@ std::string IpUtil::GetPublicIPAddress()
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string IpUtil::DNSLookupSync(const std::string &addr, int port)
|
||||
{
|
||||
auto task_runner = new EQ::Event::TaskScheduler();
|
||||
auto res = task_runner->Enqueue(
|
||||
[&]() -> std::string {
|
||||
bool running = true;
|
||||
std::string ret;
|
||||
|
||||
EQ::Net::DNSLookup(
|
||||
addr, port, false, [&](const std::string &addr) {
|
||||
ret = addr;
|
||||
if (addr.empty()) {
|
||||
ret = "";
|
||||
running = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
);
|
||||
|
||||
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
|
||||
|
||||
auto &loop = EQ::EventLoop::Get();
|
||||
while (running) {
|
||||
if (!ret.empty()) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() > 1500) {
|
||||
LogInfo(
|
||||
"Deadline exceeded [{}]",
|
||||
1500
|
||||
);
|
||||
running = false;
|
||||
}
|
||||
|
||||
loop.Process();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
);
|
||||
|
||||
std::string result = res.get();
|
||||
safe_delete(task_runner);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IpUtil::IsIPAddress(const std::string &ip_address)
|
||||
{
|
||||
struct sockaddr_in sa{};
|
||||
int result = inet_pton(AF_INET, ip_address.c_str(), &(sa.sin_addr));
|
||||
sockaddr_in sa{};
|
||||
int result = inet_pton(AF_INET, ip_address.c_str(), &(sa.sin_addr));
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h> // For inet_pton
|
||||
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h> // For inet_pton
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
bool IpUtil::IsPortInUse(const std::string& ip, int port) {
|
||||
bool IpUtil::IsPortInUse(const std::string& ip, int port)
|
||||
{
|
||||
bool in_use = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
std::cerr << "WSAStartup failed\n";
|
||||
return true; // Assume in use on failure
|
||||
}
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
std::cerr << "WSAStartup failed\n";
|
||||
return true; // Assume in use on failure
|
||||
}
|
||||
#endif
|
||||
|
||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
@@ -319,20 +228,20 @@ bool IpUtil::IsPortInUse(const std::string& ip, int port) {
|
||||
std::cerr << "Invalid IP address format: " << ip << std::endl;
|
||||
#ifdef _WIN32
|
||||
closesocket(sock);
|
||||
WSACleanup();
|
||||
WSACleanup();
|
||||
#else
|
||||
close(sock);
|
||||
#endif
|
||||
return true; // Assume in use on failure
|
||||
}
|
||||
|
||||
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
if (bind(sock, (sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
in_use = true; // Bind failed, port is in use
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
closesocket(sock);
|
||||
WSACleanup();
|
||||
WSACleanup();
|
||||
#else
|
||||
close(sock);
|
||||
#endif
|
||||
|
||||
+1
-4
@@ -29,10 +29,7 @@ public:
|
||||
static bool IsIpInPrivateRfc1918(const std::string &ip);
|
||||
static std::string GetLocalIPAddress();
|
||||
static std::string GetPublicIPAddress();
|
||||
static std::string DNSLookupSync(
|
||||
const std::string &addr,
|
||||
int port
|
||||
);
|
||||
|
||||
static bool IsIPAddress(const std::string &ip_address);
|
||||
static bool IsPortInUse(const std::string& ip, int port);
|
||||
|
||||
|
||||
@@ -320,6 +320,7 @@ bool EQ::ItemInstance::IsAugmentSlotAvailable(int32 augment_type, uint8 slot) co
|
||||
}
|
||||
|
||||
return (
|
||||
slot < invaug::SOCKET_COUNT &&
|
||||
(
|
||||
augment_type == -1 ||
|
||||
(
|
||||
|
||||
@@ -271,11 +271,7 @@ static size_t const stackLimit_g = JSONCPP_DEPRECATED_STACK_LIMIT; // see readVa
|
||||
|
||||
namespace Json {
|
||||
|
||||
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
|
||||
typedef std::unique_ptr<CharReader> CharReaderPtr;
|
||||
#else
|
||||
typedef std::auto_ptr<CharReader> CharReaderPtr;
|
||||
#endif
|
||||
|
||||
// Implementation of class Features
|
||||
// ////////////////////////////////
|
||||
@@ -4153,11 +4149,7 @@ Value& Path::make(Value& root) const {
|
||||
|
||||
namespace Json {
|
||||
|
||||
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
|
||||
typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
|
||||
#else
|
||||
typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
|
||||
#endif
|
||||
|
||||
static bool containsControlCharacter(const char* str) {
|
||||
while (*str) {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// Created by dannu on 4/18/2026.
|
||||
//
|
||||
|
||||
#include "links.h"
|
||||
|
||||
#include "spdat.h"
|
||||
|
||||
void Links::FormatItemLink(char* Buffer, size_t BufferSize, const EQ::ItemInstance* item)
|
||||
{
|
||||
// TODO: Reverse 0x14064B220 to get definition of this function
|
||||
}
|
||||
|
||||
void Links::FormatSpellLink(char* Buffer, size_t BufferSize, int32_t SpellID,
|
||||
const char* spellNameOverride)
|
||||
{
|
||||
snprintf(Buffer, BufferSize, "%c%d3^%d^0^'%s%c", ITEM_TAG_CHAR, ETAG_SPELL, SpellID,
|
||||
spellNameOverride && spellNameOverride[0] ? spellNameOverride : GetSpellName(SpellID), ITEM_TAG_CHAR);
|
||||
}
|
||||
|
||||
void Links::FormatDialogLink(char* Buffer, size_t BufferSize, std::string_view keyword, std::string_view text)
|
||||
{
|
||||
if (text.empty()) {
|
||||
snprintf(Buffer, BufferSize, "%c%d%.*s%c", ITEM_TAG_CHAR, ETAG_DIALOG_RESPONSE,
|
||||
static_cast<int>(keyword.length()), keyword.data(), ITEM_TAG_CHAR);
|
||||
} else {
|
||||
snprintf(Buffer, BufferSize, "%c%d%.*s:%.*s%c", ITEM_TAG_CHAR, ETAG_DIALOG_RESPONSE,
|
||||
static_cast<int>(keyword.length()), keyword.data(),
|
||||
static_cast<int>(text.length()), text.data(), ITEM_TAG_CHAR);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Created by dannu on 4/18/2026.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include "item_instance.h"
|
||||
|
||||
namespace EQ { class ItemInstance; }
|
||||
|
||||
namespace Links
|
||||
{
|
||||
// Max Link Size in bytes
|
||||
constexpr size_t MAX_LINK_SIZE = 512;
|
||||
|
||||
// Universal link tag character
|
||||
constexpr char ITEM_TAG_CHAR = '\x12';
|
||||
|
||||
// Enumeration of different types of item tags
|
||||
enum ETagCodes
|
||||
{
|
||||
ETAG_ITEM = 0,
|
||||
ETAG_PLAYER,
|
||||
ETAG_SPAM,
|
||||
ETAG_ACHIEVEMENT,
|
||||
ETAG_DIALOG_RESPONSE,
|
||||
ETAG_COMMAND,
|
||||
ETAG_SPELL,
|
||||
ETAG_FACTION,
|
||||
ETAG_COMMAND2,
|
||||
ETAG_UNKNOWN9,
|
||||
|
||||
ETAG_COUNT,
|
||||
ETAG_FIRST = ETAG_ITEM,
|
||||
ETAG_LAST = ETAG_UNKNOWN9,
|
||||
|
||||
ETAG_INVALID = -1,
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Link Formatting -- Pulled from MQ code
|
||||
|
||||
// Create an achievement link for the given achievement.
|
||||
// TODO: implement this when achievements are added, leave the signature here for reference. Code in eqlib's ItemLinks.cpp
|
||||
// void FormatAchievementLink(char* Buffer, size_t BufferSize, const Achievement* achievement,
|
||||
// std::string_view playerName);
|
||||
|
||||
// Create an item link from the given item.
|
||||
void FormatItemLink(char* Buffer, size_t BufferSize, const EQ::ItemInstance* item);
|
||||
|
||||
// Create a spell link for the given spell, with optional spell name override. Spells on items often have
|
||||
// spell name overrides that changes the display name of the spell.
|
||||
void FormatSpellLink(char* Buffer, size_t BufferSize, int32_t SpellID,
|
||||
const char* spellNameOverride = nullptr);
|
||||
|
||||
// Format text into a clickable dialog link. The keyword is the text that will be displayed in the chat window,
|
||||
// and the text is the text that will be sent to the server when the link is clicked. If no text is provided,
|
||||
// then the keyword will be used as the text.
|
||||
void FormatDialogLink(char* Buffer, size_t BufferSize, std::string_view keyword,
|
||||
std::string_view text = {});
|
||||
|
||||
}
|
||||
+1
-2
@@ -45,5 +45,4 @@ struct LootItem {
|
||||
uint32 lootdrop_id; // required for zone state referencing
|
||||
};
|
||||
|
||||
typedef std::list<LootItem*> LootItems;
|
||||
|
||||
using LootItems = std::list<LootItem*>;
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "ksm.h"
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h> // For madvise
|
||||
#include <unistd.h> // For sysconf, sbrk
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows-specific functionality
|
||||
|
||||
void* PageAlignedAllocatorBase::allocateInternal(size_t size, size_t alignment)
|
||||
{
|
||||
void* ptr = malloc(size);
|
||||
|
||||
if (!ptr)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
size_t PageAlignedAllocatorBase::getPageSize() const
|
||||
{
|
||||
SYSTEM_INFO sysInfo;
|
||||
GetSystemInfo(&sysInfo);
|
||||
return sysInfo.dwPageSize; // Page size in bytes
|
||||
}
|
||||
|
||||
namespace KSM {
|
||||
|
||||
// Windows-specific placeholder functions (no-op)
|
||||
void CheckPageAlignment(void* ptr)
|
||||
{
|
||||
}
|
||||
|
||||
void* AllocatePageAligned(size_t size)
|
||||
{
|
||||
return memset(malloc(size), 0, size);
|
||||
}
|
||||
|
||||
void MarkMemoryForKSM(void* start, size_t size)
|
||||
{
|
||||
}
|
||||
|
||||
void AlignHeapToPageBoundary()
|
||||
{
|
||||
}
|
||||
|
||||
void* MarkHeapStart()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t MeasureHeapUsage(void* start)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace KSM
|
||||
|
||||
#else
|
||||
|
||||
// Linux-specific functionality
|
||||
|
||||
void* PageAlignedAllocatorBase::allocateInternal(size_t size, size_t alignment)
|
||||
{
|
||||
void* ptr = nullptr;
|
||||
|
||||
if (posix_memalign(&ptr, alignment, size) != 0)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
size_t PageAlignedAllocatorBase::getPageSize() const
|
||||
{
|
||||
return static_cast<size_t>(sysconf(_SC_PAGESIZE));
|
||||
}
|
||||
|
||||
namespace KSM {
|
||||
|
||||
void CheckPageAlignment(void* ptr)
|
||||
{
|
||||
size_t page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
if (reinterpret_cast<uintptr_t>(ptr) % page_size == 0)
|
||||
{
|
||||
LogKSMDetail("Memory is page-aligned [{}]", ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogKSMDetail("Memory is NOT page-aligned [{}]", ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void* AllocatePageAligned(size_t size)
|
||||
{
|
||||
size_t page_size = sysconf(_SC_PAGESIZE);
|
||||
void* aligned_ptr = nullptr;
|
||||
|
||||
if (posix_memalign(&aligned_ptr, page_size, size) != 0)
|
||||
{
|
||||
LogKSM("Failed to allocate page-aligned memory on Linux. page_size [{}] size [{}] bytes", page_size, size);
|
||||
}
|
||||
|
||||
std::memset(aligned_ptr, 0, size);
|
||||
return aligned_ptr;
|
||||
}
|
||||
|
||||
void MarkMemoryForKSM(void* start, size_t size)
|
||||
{
|
||||
if (madvise(start, size, MADV_MERGEABLE) == 0)
|
||||
{
|
||||
LogKSM("Marked memory for KSM | start [{}] size [{}] bytes", start, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("madvise failed");
|
||||
}
|
||||
}
|
||||
|
||||
void AlignHeapToPageBoundary()
|
||||
{
|
||||
size_t page_size = sysconf(_SC_PAGESIZE);
|
||||
if (page_size == 0)
|
||||
{
|
||||
LogKSM("Failed to retrieve page size SC_PAGESIZE [{}]", page_size);
|
||||
return;
|
||||
}
|
||||
|
||||
void* current_break = sbrk(0);
|
||||
if (current_break == (void*)-1)
|
||||
{
|
||||
LogKSM("Failed to retrieve the current program break");
|
||||
return;
|
||||
}
|
||||
|
||||
uintptr_t current_address = reinterpret_cast<uintptr_t>(current_break);
|
||||
size_t misalignment = current_address % page_size;
|
||||
|
||||
if (misalignment != 0)
|
||||
{
|
||||
size_t adjustment = page_size - misalignment;
|
||||
if (sbrk(adjustment) == (void*)-1)
|
||||
{
|
||||
LogKSM("Failed to align heap to page boundary. adjustment [{}] bytes", adjustment);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LogKSMDetail("Heap aligned to next page boundary. Current break [{}]", sbrk(0));
|
||||
}
|
||||
|
||||
void* MarkHeapStart()
|
||||
{
|
||||
void* current_pos = sbrk(0);
|
||||
AlignHeapToPageBoundary();
|
||||
return current_pos;
|
||||
}
|
||||
|
||||
size_t MeasureHeapUsage(void* start)
|
||||
{
|
||||
void* current_break = sbrk(0);
|
||||
return static_cast<char*>(current_break) - static_cast<char*>(start);
|
||||
}
|
||||
|
||||
} // namespace KSM
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/eqemu_logsys.h"
|
||||
|
||||
class PageAlignedAllocatorBase
|
||||
{
|
||||
protected:
|
||||
void* allocateInternal(size_t amount, size_t alignment);
|
||||
size_t getPageSize() const;
|
||||
};
|
||||
|
||||
// Page-aligned allocator for std::vector
|
||||
template <typename T>
|
||||
class PageAlignedAllocator : public PageAlignedAllocatorBase
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
PageAlignedAllocator() noexcept = default;
|
||||
|
||||
template <typename U>
|
||||
PageAlignedAllocator(const PageAlignedAllocator<U>&) noexcept {}
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
size_t size = n * sizeof(T);
|
||||
|
||||
return static_cast<T*>(allocateInternal(size, getPageSize()));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) noexcept
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(const PageAlignedAllocator<T>&, const PageAlignedAllocator<U>&) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const PageAlignedAllocator<T>&, const PageAlignedAllocator<U>&) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Kernel Samepage Merging (KSM) functionality
|
||||
namespace KSM {
|
||||
|
||||
void CheckPageAlignment(void* ptr);
|
||||
void* AllocatePageAligned(size_t size);
|
||||
void MarkMemoryForKSM(void* start, size_t size);
|
||||
void AlignHeapToPageBoundary();
|
||||
void* MarkHeapStart();
|
||||
size_t MeasureHeapUsage(void* start);
|
||||
|
||||
} // namespace KSM
|
||||
@@ -1,234 +0,0 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/eqemu_logsys.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#ifdef _WIN32
|
||||
#include <malloc.h> // For _aligned_malloc, _aligned_free
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h> // For madvise
|
||||
#include <unistd.h> // For sysconf, sbrk
|
||||
#endif
|
||||
|
||||
|
||||
// Page-aligned allocator for std::vector
|
||||
template <typename T>
|
||||
class PageAlignedAllocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
PageAlignedAllocator() noexcept = default;
|
||||
template <typename U>
|
||||
PageAlignedAllocator(const PageAlignedAllocator<U>&) noexcept {}
|
||||
|
||||
T* allocate(std::size_t n) {
|
||||
void* ptr = nullptr;
|
||||
size_t size = n * sizeof(T);
|
||||
|
||||
#ifdef _WIN32
|
||||
// Simply allocate memory without alignment
|
||||
ptr = malloc(size);
|
||||
if (!ptr) throw std::bad_alloc();
|
||||
#else
|
||||
size_t alignment = getPageSize(); // Get the system's page size
|
||||
if (posix_memalign(&ptr, alignment, size) != 0) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
#endif
|
||||
return static_cast<T*>(ptr);
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) noexcept {
|
||||
free(p);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t getPageSize() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO sysInfo;
|
||||
GetSystemInfo(&sysInfo);
|
||||
return sysInfo.dwPageSize; // Page size in bytes
|
||||
#else
|
||||
return static_cast<size_t>(sysconf(_SC_PAGESIZE));
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(const PageAlignedAllocator<T>&, const PageAlignedAllocator<U>&) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const PageAlignedAllocator<T>&, const PageAlignedAllocator<U>&) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Kernel Samepage Merging (KSM) functionality
|
||||
namespace KSM {
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows-specific placeholder functions (no-op)
|
||||
inline void CheckPageAlignment(void* ptr) {
|
||||
}
|
||||
|
||||
inline void* AllocatePageAligned(size_t size) {
|
||||
return memset(malloc(size), 0, size);
|
||||
}
|
||||
|
||||
inline void MarkMemoryForKSM(void* start, size_t size) {
|
||||
}
|
||||
|
||||
inline void AlignHeapToPageBoundary() {
|
||||
}
|
||||
|
||||
inline void* MarkHeapStart() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline size_t MeasureHeapUsage(void* start) {
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
// Linux-specific functionality
|
||||
inline void CheckPageAlignment(void* ptr) {
|
||||
size_t page_size = sysconf(_SC_PAGESIZE);
|
||||
if (reinterpret_cast<uintptr_t>(ptr) % page_size == 0) {
|
||||
LogKSMDetail("Memory is page-aligned [{}]", ptr);
|
||||
} else {
|
||||
LogKSMDetail("Memory is NOT page-aligned [{}]", ptr);
|
||||
}
|
||||
}
|
||||
|
||||
inline void* AllocatePageAligned(size_t size) {
|
||||
size_t page_size = sysconf(_SC_PAGESIZE);
|
||||
void* aligned_ptr = nullptr;
|
||||
if (posix_memalign(&aligned_ptr, page_size, size) != 0) {
|
||||
LogKSM("Failed to allocate page-aligned memory on Linux. page_size [{}] size [{}] bytes", page_size, size);
|
||||
}
|
||||
std::memset(aligned_ptr, 0, size);
|
||||
return aligned_ptr;
|
||||
}
|
||||
|
||||
inline void MarkMemoryForKSM(void* start, size_t size) {
|
||||
if (madvise(start, size, MADV_MERGEABLE) == 0) {
|
||||
LogKSM("Marked memory for KSM | start [{}] size [{}] bytes", start, size);
|
||||
} else {
|
||||
perror("madvise failed");
|
||||
}
|
||||
}
|
||||
|
||||
inline void AlignHeapToPageBoundary() {
|
||||
size_t page_size = sysconf(_SC_PAGESIZE);
|
||||
if (page_size == 0) {
|
||||
LogKSM("Failed to retrieve page size SC_PAGESIZE [{}]", page_size);
|
||||
return;
|
||||
}
|
||||
|
||||
void* current_break = sbrk(0);
|
||||
if (current_break == (void*)-1) {
|
||||
LogKSM("Failed to retrieve the current program break");
|
||||
return;
|
||||
}
|
||||
|
||||
uintptr_t current_address = reinterpret_cast<uintptr_t>(current_break);
|
||||
size_t misalignment = current_address % page_size;
|
||||
|
||||
if (misalignment != 0) {
|
||||
size_t adjustment = page_size - misalignment;
|
||||
if (sbrk(adjustment) == (void*)-1) {
|
||||
LogKSM("Failed to align heap to page boundary. adjustment [{}] bytes", adjustment);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LogKSMDetail("Heap aligned to next page boundary. Current break [{}]", sbrk(0));
|
||||
}
|
||||
|
||||
inline void* MarkHeapStart() {
|
||||
void* current_pos = sbrk(0);
|
||||
AlignHeapToPageBoundary();
|
||||
return current_pos;
|
||||
}
|
||||
|
||||
inline size_t MeasureHeapUsage(void* start) {
|
||||
void* current_break = sbrk(0);
|
||||
return static_cast<char*>(current_break) - static_cast<char*>(start);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline size_t getPageSize()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO sysInfo;
|
||||
GetSystemInfo(&sysInfo);
|
||||
return sysInfo.dwPageSize; // Page size in bytes
|
||||
#else
|
||||
return static_cast<size_t>(sysconf(_SC_PAGESIZE)); // POSIX page size
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline void PageAlignVectorAligned(std::vector<T, PageAlignedAllocator<T>>& vec) {
|
||||
if (vec.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t page_size = getPageSize();
|
||||
void* start = vec.data();
|
||||
size_t size = vec.size() * sizeof(T);
|
||||
|
||||
// Check if the memory is page-aligned
|
||||
if (reinterpret_cast<std::uintptr_t>(start) % page_size != 0) {
|
||||
// Allocate a new aligned vector
|
||||
std::vector<T, PageAlignedAllocator<T>> aligned_vec(vec.get_allocator());
|
||||
aligned_vec.reserve(vec.capacity()); // Match capacity to avoid reallocation during copy
|
||||
|
||||
// Copy elements from the original vector
|
||||
aligned_vec.insert(aligned_vec.end(), vec.begin(), vec.end());
|
||||
|
||||
// Swap the aligned vector with the original vector
|
||||
vec.swap(aligned_vec);
|
||||
|
||||
// Clear the temporary aligned vector to free its memory
|
||||
aligned_vec.clear();
|
||||
|
||||
// Verify the new alignment
|
||||
start = vec.data();
|
||||
if (reinterpret_cast<std::uintptr_t>(start) % page_size != 0) {
|
||||
throw std::runtime_error("Failed to align vector memory to page boundaries.");
|
||||
}
|
||||
|
||||
LogKSMDetail("Vector reallocated to ensure page alignment. start [{}] size [{}] bytes", start, size);
|
||||
} else {
|
||||
LogKSMDetail("Vector is already page-aligned. start [{}] size [{}] bytes", start, size);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
// Mark memory for KSM (only on non-Windows systems)
|
||||
MarkMemoryForKSM(start, size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "mutex.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define DEBUG_MUTEX_CLASS 0
|
||||
#if DEBUG_MUTEX_CLASS >= 1
|
||||
#endif
|
||||
|
||||
#ifdef _WINDOWS
|
||||
bool IsTryLockSupported();
|
||||
bool TrylockSupported = IsTryLockSupported();
|
||||
|
||||
bool IsTryLockSupported() {
|
||||
OSVERSIONINFOEX osvi;
|
||||
BOOL bOsVersionInfoEx;
|
||||
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||||
|
||||
if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
|
||||
{
|
||||
// If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
|
||||
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
|
||||
if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) {
|
||||
#if DEBUG_MUTEX_CLASS >= 1
|
||||
std::cout << "Mutex::trylock() NOT supported" << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for Windows NT product family.
|
||||
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion >= 4) {
|
||||
#if DEBUG_MUTEX_CLASS >= 1
|
||||
std::cout << "Mutex::trylock() SUPPORTED" << std::endl;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
#if DEBUG_MUTEX_CLASS >= 1
|
||||
std::cout << "Mutex::trylock() NOT supported" << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Mutex::Mutex() {
|
||||
|
||||
#if DEBUG_MUTEX_CLASS >= 7
|
||||
std::cout << "Constructing Mutex" << std::endl;
|
||||
#endif
|
||||
#ifdef _WINDOWS
|
||||
InitializeCriticalSection(&CSMutex);
|
||||
#else
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
#if defined(__CYGWIN__) || defined(__APPLE__) || defined(FREEBSD)
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
#else
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
|
||||
#endif
|
||||
pthread_mutex_init(&CSMutex, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
#endif
|
||||
}
|
||||
|
||||
Mutex::~Mutex() {
|
||||
#ifdef _WINDOWS
|
||||
DeleteCriticalSection(&CSMutex);
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::lock() {
|
||||
#if DEBUG_MUTEX_CLASS >= 5
|
||||
if (!trylock()) {
|
||||
std::cout << "Locking Mutex: Having to wait" << std::endl;
|
||||
#ifdef _WINDOWS
|
||||
EnterCriticalSection(&CSMutex);
|
||||
#else
|
||||
pthread_mutex_lock(&CSMutex);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
#ifdef _WINDOWS
|
||||
EnterCriticalSection(&CSMutex);
|
||||
#else
|
||||
pthread_mutex_lock(&CSMutex);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Mutex::trylock() {
|
||||
#ifdef _WINDOWS
|
||||
#if(_WIN32_WINNT >= 0x0400)
|
||||
if (TrylockSupported)
|
||||
return TryEnterCriticalSection(&CSMutex);
|
||||
else {
|
||||
EnterCriticalSection(&CSMutex);
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
EnterCriticalSection(&CSMutex);
|
||||
return true;
|
||||
#endif
|
||||
#else
|
||||
return (pthread_mutex_trylock(&CSMutex) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::unlock() {
|
||||
#ifdef _WINDOWS
|
||||
LeaveCriticalSection(&CSMutex);
|
||||
#else
|
||||
pthread_mutex_unlock(&CSMutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
LockMutex::LockMutex(Mutex* in_mut, bool iLock) {
|
||||
mut = in_mut;
|
||||
locked = iLock;
|
||||
if (locked) {
|
||||
mut->lock();
|
||||
}
|
||||
}
|
||||
|
||||
LockMutex::~LockMutex() {
|
||||
if (locked) {
|
||||
mut->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void LockMutex::unlock() {
|
||||
if (locked)
|
||||
mut->unlock();
|
||||
locked = false;
|
||||
}
|
||||
|
||||
void LockMutex::lock() {
|
||||
if (!locked)
|
||||
mut->lock();
|
||||
locked = true;
|
||||
}
|
||||
+10
-6
@@ -18,7 +18,6 @@
|
||||
#include "mysql_stmt.h"
|
||||
|
||||
#include "common/eqemu_logsys.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/timer.h"
|
||||
|
||||
#include <charconv>
|
||||
@@ -31,14 +30,19 @@ void PreparedStmt::StmtDeleter::operator()(MYSQL_STMT* stmt) noexcept
|
||||
// The connection must be locked when closing the stmt to avoid mysql errors
|
||||
// in case another thread tries to use it during the close. If the mutex is
|
||||
// changed to one that throws then exceptions need to be caught here.
|
||||
LockMutex lock(mutex);
|
||||
std::scoped_lock lock(mutex);
|
||||
|
||||
mysql_stmt_close(stmt);
|
||||
}
|
||||
|
||||
PreparedStmt::PreparedStmt(MYSQL& mysql, std::string query, Mutex* mutex, StmtOptions opts)
|
||||
: m_stmt(mysql_stmt_init(&mysql), { mutex }), m_query(std::move(query)), m_mutex(mutex), m_options(opts)
|
||||
PreparedStmt::PreparedStmt(MYSQL& mysql, std::string query, DBcore::Mutex& mutex, StmtOptions opts)
|
||||
: m_stmt(mysql_stmt_init(&mysql), { mutex })
|
||||
, m_query(std::move(query))
|
||||
, m_options(opts)
|
||||
, m_mutex(mutex)
|
||||
{
|
||||
LockMutex lock(m_mutex);
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
if (mysql_stmt_prepare(m_stmt.get(), m_query.c_str(), static_cast<unsigned long>(m_query.size())) != 0)
|
||||
{
|
||||
ThrowError(fmt::format("Prepare error: {}", GetStmtError()));
|
||||
@@ -186,7 +190,7 @@ void PreparedStmt::CheckArgs(size_t argc)
|
||||
StmtResult PreparedStmt::DoExecute()
|
||||
{
|
||||
BenchTimer timer;
|
||||
LockMutex lock(m_mutex);
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
if (m_need_bind && mysql_stmt_bind_param(m_stmt.get(), m_params.data()) != 0)
|
||||
{
|
||||
|
||||
+7
-4
@@ -17,10 +17,12 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "mysql.h"
|
||||
#include "common/dbcore.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
@@ -183,7 +185,7 @@ public:
|
||||
int64_t, uint64_t, float, double, bool, std::string_view, std::nullptr_t>;
|
||||
|
||||
PreparedStmt() = delete;
|
||||
PreparedStmt(MYSQL& mysql, std::string query, Mutex* mutex, StmtOptions opts = {});
|
||||
PreparedStmt(MYSQL& mysql, std::string query, DBcore::Mutex& mutex, StmtOptions opts = {});
|
||||
|
||||
const std::string& GetQuery() const { return m_query; }
|
||||
StmtOptions GetOptions() const { return m_options; }
|
||||
@@ -219,7 +221,8 @@ private:
|
||||
|
||||
struct StmtDeleter
|
||||
{
|
||||
Mutex* mutex = nullptr;
|
||||
DBcore::Mutex& mutex;
|
||||
|
||||
void operator()(MYSQL_STMT* stmt) noexcept;
|
||||
};
|
||||
|
||||
@@ -232,7 +235,7 @@ private:
|
||||
std::string m_query;
|
||||
StmtOptions m_options = {};
|
||||
bool m_need_bind = true;
|
||||
Mutex* m_mutex = nullptr; // connection mutex
|
||||
DBcore::Mutex& m_mutex; // connection mutex
|
||||
};
|
||||
|
||||
} // namespace mysql
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dns.h"
|
||||
|
||||
#include "common/eqemu_logsys.h"
|
||||
#include "common/event/event_loop.h"
|
||||
#include "common/event/task_scheduler.h"
|
||||
|
||||
#include "uv.h"
|
||||
|
||||
namespace EQ::Net {
|
||||
|
||||
struct DNSBaton
|
||||
{
|
||||
dns_callback_t cb;
|
||||
bool ipv6;
|
||||
};
|
||||
|
||||
void DNSLookup(const std::string& addr, int port, bool ipv6, dns_callback_t cb)
|
||||
{
|
||||
addrinfo hints = {};
|
||||
hints.ai_family = PF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
uv_loop_t* loop = EQ::EventLoop::Get().Handle();
|
||||
|
||||
uv_getaddrinfo_t* resolver = new uv_getaddrinfo_t();
|
||||
memset(resolver, 0, sizeof(uv_getaddrinfo_t));
|
||||
std::string port_str = std::to_string(port);
|
||||
DNSBaton* baton = new DNSBaton();
|
||||
baton->cb = std::move(cb);
|
||||
baton->ipv6 = ipv6;
|
||||
resolver->data = baton;
|
||||
|
||||
uv_getaddrinfo(loop, resolver, [](uv_getaddrinfo_t* req, int status, addrinfo* result)
|
||||
{
|
||||
DNSBaton* baton = static_cast<DNSBaton*>(req->data);
|
||||
|
||||
dns_callback_t dns_callback = std::move(baton->cb);
|
||||
bool ipv6 = baton->ipv6;
|
||||
|
||||
delete baton;
|
||||
delete req;
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
dns_callback({});
|
||||
return;
|
||||
}
|
||||
|
||||
char addr[40] = {};
|
||||
|
||||
if (ipv6)
|
||||
{
|
||||
uv_ip6_name(reinterpret_cast<sockaddr_in6*>(result->ai_addr), addr, 40);
|
||||
}
|
||||
else
|
||||
{
|
||||
uv_ip4_name(reinterpret_cast<sockaddr_in*>(result->ai_addr), addr, 40);
|
||||
}
|
||||
|
||||
uv_freeaddrinfo(result);
|
||||
|
||||
dns_callback(addr);
|
||||
}, addr.c_str(), port_str.c_str(), &hints);
|
||||
}
|
||||
|
||||
std::string DNSLookupSync(const std::string& addr, int port, bool ipv6 /* = false */)
|
||||
{
|
||||
EQ::Event::TaskScheduler task_runner;
|
||||
|
||||
auto res = task_runner.Enqueue(
|
||||
[addr, port, ipv6]() -> std::string
|
||||
{
|
||||
bool running = true;
|
||||
std::string ret;
|
||||
|
||||
EQ::Net::DNSLookup(
|
||||
addr, port, ipv6, [&](const std::string& addr) {
|
||||
ret = addr;
|
||||
running = !addr.empty();
|
||||
|
||||
return ret;
|
||||
}
|
||||
);
|
||||
|
||||
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
|
||||
|
||||
auto& loop = EQ::EventLoop::Get();
|
||||
while (running) {
|
||||
if (!ret.empty()) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() > 1500) {
|
||||
LogInfo(
|
||||
"Deadline exceeded [{}]",
|
||||
1500
|
||||
);
|
||||
running = false;
|
||||
}
|
||||
|
||||
loop.Process();
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
|
||||
return res.get();
|
||||
}
|
||||
|
||||
|
||||
} // namespace EQ::Net
|
||||
+5
-53
@@ -17,63 +17,15 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/event/event_loop.h"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
static void DNSLookup(const std::string &addr, int port, bool ipv6, std::function<void(const std::string&)> cb) {
|
||||
struct DNSBaton
|
||||
{
|
||||
std::function<void(const std::string&)> cb;
|
||||
bool ipv6;
|
||||
};
|
||||
namespace EQ::Net {
|
||||
|
||||
addrinfo hints;
|
||||
memset(&hints, 0, sizeof(addrinfo));
|
||||
hints.ai_family = PF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
using dns_callback_t = std::function<void(const std::string&)>;
|
||||
|
||||
auto loop = EQ::EventLoop::Get().Handle();
|
||||
uv_getaddrinfo_t *resolver = new uv_getaddrinfo_t();
|
||||
memset(resolver, 0, sizeof(uv_getaddrinfo_t));
|
||||
auto port_str = std::to_string(port);
|
||||
DNSBaton *baton = new DNSBaton();
|
||||
baton->cb = cb;
|
||||
baton->ipv6 = ipv6;
|
||||
resolver->data = baton;
|
||||
void DNSLookup(const std::string& addr, int port, bool ipv6, dns_callback_t cb);
|
||||
|
||||
uv_getaddrinfo(loop, resolver, [](uv_getaddrinfo_t* req, int status, addrinfo* res) {
|
||||
DNSBaton *baton = (DNSBaton*)req->data;
|
||||
if (status < 0) {
|
||||
auto cb = baton->cb;
|
||||
delete baton;
|
||||
delete req;
|
||||
cb("");
|
||||
return;
|
||||
}
|
||||
std::string DNSLookupSync(const std::string& addr, int port, bool ipv6 = false);
|
||||
|
||||
char addr[40] = { 0 };
|
||||
|
||||
if (baton->ipv6) {
|
||||
uv_ip6_name((struct sockaddr_in6*)res->ai_addr, addr, 40);
|
||||
}
|
||||
else {
|
||||
uv_ip4_name((struct sockaddr_in*)res->ai_addr, addr, 40);
|
||||
}
|
||||
|
||||
auto cb = baton->cb;
|
||||
delete baton;
|
||||
delete req;
|
||||
uv_freeaddrinfo(res);
|
||||
|
||||
cb(addr);
|
||||
}, addr.c_str(), port_str.c_str(), &hints);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace EQ::Net
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
|
||||
#include "common/net/tcp_connection_pooling.h"
|
||||
|
||||
#include "common/platform/platform.h"
|
||||
#include "uv.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -49,9 +49,9 @@ namespace EQ
|
||||
WebsocketException(const std::string &msg)
|
||||
: _msg(msg.empty() ? "Unknown Error" : msg) { }
|
||||
|
||||
~WebsocketException() throw() {}
|
||||
~WebsocketException() noexcept {}
|
||||
|
||||
virtual char const *what() const throw() {
|
||||
virtual char const *what() const noexcept override {
|
||||
return _msg.c_str();
|
||||
}
|
||||
private:
|
||||
|
||||
+13
-14
@@ -17,17 +17,16 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
static const char OP_SessionRequest = 0x01;
|
||||
static const char OP_SessionResponse = 0x02;
|
||||
static const char OP_Combined = 0x03;
|
||||
static const char OP_SessionDisconnect = 0x05;
|
||||
static const char OP_KeepAlive = 0x06;
|
||||
static const char OP_SessionStatRequest = 0x07;
|
||||
static const char OP_SessionStatResponse= 0x08;
|
||||
static const char OP_Packet = 0x09;
|
||||
static const char OP_Fragment = 0x0d;
|
||||
static const char OP_OutOfOrderAck = 0x11;
|
||||
static const char OP_Ack = 0x15;
|
||||
static const char OP_AppCombined = 0x19;
|
||||
static const char OP_OutOfSession = 0x1d;
|
||||
static constexpr char OP_SessionRequest = 0x01;
|
||||
static constexpr char OP_SessionResponse = 0x02;
|
||||
static constexpr char OP_Combined = 0x03;
|
||||
static constexpr char OP_SessionDisconnect = 0x05;
|
||||
static constexpr char OP_KeepAlive = 0x06;
|
||||
static constexpr char OP_SessionStatRequest = 0x07;
|
||||
static constexpr char OP_SessionStatResponse = 0x08;
|
||||
static constexpr char OP_Packet = 0x09;
|
||||
static constexpr char OP_Fragment = 0x0d;
|
||||
static constexpr char OP_OutOfOrderAck = 0x11;
|
||||
static constexpr char OP_Ack = 0x15;
|
||||
static constexpr char OP_AppCombined = 0x19;
|
||||
static constexpr char OP_OutOfSession = 0x1d;
|
||||
|
||||
+26
-23
@@ -142,10 +142,12 @@ RegularOpcodeManager::~RegularOpcodeManager() {
|
||||
safe_delete_array(eq_to_emu);
|
||||
}
|
||||
|
||||
bool RegularOpcodeManager::LoadOpcodes(const char *filename, bool report_errors) {
|
||||
bool RegularOpcodeManager::LoadOpcodes(const char *filename, bool report_errors)
|
||||
{
|
||||
std::scoped_lock lock(MOpcodes);
|
||||
|
||||
NormalMemStrategy s;
|
||||
s.it = this;
|
||||
MOpcodes.lock();
|
||||
|
||||
loaded = true;
|
||||
eq_to_emu = new EmuOpcode[MAX_EQ_OPCODE];
|
||||
@@ -158,32 +160,30 @@ bool RegularOpcodeManager::LoadOpcodes(const char *filename, bool report_errors)
|
||||
memset(emu_to_eq, 0, sizeof(uint16)*_maxEmuOpcode);
|
||||
|
||||
bool ret = LoadOpcodesFile(filename, &s, report_errors);
|
||||
MOpcodes.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool RegularOpcodeManager::ReloadOpcodes(const char *filename, bool report_errors) {
|
||||
if(!loaded)
|
||||
return(LoadOpcodes(filename));
|
||||
bool RegularOpcodeManager::ReloadOpcodes(const char* filename, bool report_errors)
|
||||
{
|
||||
if (!loaded)
|
||||
return LoadOpcodes(filename);
|
||||
|
||||
std::scoped_lock lock(MOpcodes);
|
||||
|
||||
NormalMemStrategy s;
|
||||
s.it = this;
|
||||
MOpcodes.lock();
|
||||
|
||||
memset(eq_to_emu, 0, sizeof(uint16)*MAX_EQ_OPCODE);
|
||||
|
||||
bool ret = LoadOpcodesFile(filename, &s, report_errors);
|
||||
|
||||
MOpcodes.unlock();
|
||||
return(ret);
|
||||
memset(eq_to_emu, 0, sizeof(uint16) * MAX_EQ_OPCODE);
|
||||
return LoadOpcodesFile(filename, &s, report_errors);
|
||||
}
|
||||
|
||||
uint16 RegularOpcodeManager::EmuToEQ(const EmuOpcode emu_op) {
|
||||
//opcode is checked for validity in GetEQOpcode
|
||||
uint16 res;
|
||||
MOpcodes.lock();
|
||||
res = emu_to_eq[emu_op];
|
||||
MOpcodes.unlock();
|
||||
{
|
||||
std::scoped_lock lock(MOpcodes);
|
||||
res = emu_to_eq[emu_op];
|
||||
}
|
||||
|
||||
LogNetcodeDetail("[Opcode Manager] Translate emu [{}] ({:#06x}) eq [{:#06x}]", OpcodeNames[emu_op], emu_op, res);
|
||||
|
||||
@@ -193,15 +193,18 @@ uint16 RegularOpcodeManager::EmuToEQ(const EmuOpcode emu_op) {
|
||||
return(res);
|
||||
}
|
||||
|
||||
EmuOpcode RegularOpcodeManager::EQToEmu(const uint16 eq_op) {
|
||||
EmuOpcode RegularOpcodeManager::EQToEmu(const uint16 eq_op)
|
||||
{
|
||||
//opcode is checked for validity in GetEmuOpcode
|
||||
//Disabled since current live EQ uses the entire uint16 bitspace for opcodes
|
||||
// if(eq_op > MAX_EQ_OPCODE)
|
||||
// return(OP_Unknown);
|
||||
//Disabled since current live EQ uses the entire uint16 bitspace for opcodes
|
||||
// if(eq_op > MAX_EQ_OPCODE)
|
||||
// return(OP_Unknown);
|
||||
EmuOpcode res;
|
||||
MOpcodes.lock();
|
||||
res = eq_to_emu[eq_op];
|
||||
MOpcodes.unlock();
|
||||
{
|
||||
std::scoped_lock lock(MOpcodes);
|
||||
res = eq_to_emu[eq_op];
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TRANSLATE
|
||||
fprintf(stderr, "M Translate EQ 0x%.4x to Emu %s (%d)\n", eq_op, OpcodeNames[res], res);
|
||||
#endif
|
||||
|
||||
+20
-16
@@ -18,10 +18,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/emu_opcodes.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
//enable the use of shared mem opcodes for world and zone only
|
||||
#ifdef ZONE
|
||||
@@ -31,7 +31,8 @@
|
||||
#define SHARED_OPCODES
|
||||
#endif
|
||||
|
||||
class OpcodeManager {
|
||||
class OpcodeManager
|
||||
{
|
||||
public:
|
||||
OpcodeManager();
|
||||
virtual ~OpcodeManager() {}
|
||||
@@ -48,24 +49,27 @@ public:
|
||||
EmuOpcode NameSearch(const char *name);
|
||||
|
||||
//This has to be public for stupid visual studio
|
||||
class OpcodeSetStrategy {
|
||||
class OpcodeSetStrategy
|
||||
{
|
||||
public:
|
||||
virtual ~OpcodeSetStrategy() {} //shut up compiler!
|
||||
virtual ~OpcodeSetStrategy() = default;
|
||||
virtual void Set(EmuOpcode emu_op, uint16 eq_op) = 0;
|
||||
};
|
||||
|
||||
protected:
|
||||
bool loaded; //true if all opcodes loaded
|
||||
Mutex MOpcodes; //this only protects the local machine
|
||||
std::mutex MOpcodes; //this only protects the local machine
|
||||
//in a shared manager, this dosent protect others
|
||||
|
||||
static bool LoadOpcodesFile(const char *filename, OpcodeSetStrategy *s, bool report_errors);
|
||||
};
|
||||
|
||||
class MutableOpcodeManager : public OpcodeManager {
|
||||
class MutableOpcodeManager : public OpcodeManager
|
||||
{
|
||||
public:
|
||||
MutableOpcodeManager() : OpcodeManager() {}
|
||||
virtual bool Mutable() { return(true); }
|
||||
MutableOpcodeManager() = default;
|
||||
|
||||
virtual bool Mutable() override { return true; }
|
||||
virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op) = 0;
|
||||
};
|
||||
|
||||
@@ -108,16 +112,16 @@ public:
|
||||
virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op);
|
||||
|
||||
protected:
|
||||
class NormalMemStrategy : public OpcodeManager::OpcodeSetStrategy {
|
||||
class NormalMemStrategy : public OpcodeManager::OpcodeSetStrategy
|
||||
{
|
||||
public:
|
||||
virtual ~NormalMemStrategy() {} //shut up compiler!
|
||||
RegularOpcodeManager *it;
|
||||
void Set(EmuOpcode emu_op, uint16 eq_op);
|
||||
};
|
||||
friend class NormalMemStrategy;
|
||||
RegularOpcodeManager* it;
|
||||
|
||||
uint16 *emu_to_eq;
|
||||
EmuOpcode *eq_to_emu;
|
||||
virtual void Set(EmuOpcode emu_op, uint16 eq_op) override;
|
||||
};
|
||||
|
||||
uint16* emu_to_eq;
|
||||
EmuOpcode* eq_to_emu;
|
||||
uint32 EQOpcodeCount;
|
||||
uint32 EmuOpcodeCount;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/emu_opcodes.h"
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
class Client;
|
||||
class Mob;
|
||||
class EQApplicationPacket;
|
||||
class Buffs_Struct;
|
||||
|
||||
namespace ClientPatch {
|
||||
|
||||
class IBuff
|
||||
{
|
||||
public:
|
||||
using BuffSequenceFunc = std::function<std::unique_ptr<EQApplicationPacket>(const Client*)>;
|
||||
|
||||
IBuff(uint32_t maxLongBuffs, uint32_t maxShortBuffs)
|
||||
: m_maxLongBuffs(maxLongBuffs)
|
||||
, m_maxShortBuffs(maxShortBuffs)
|
||||
{}
|
||||
|
||||
IBuff() = delete;
|
||||
virtual ~IBuff() = default;
|
||||
|
||||
virtual std::unique_ptr<EQApplicationPacket> BuffDefinition(Mob* mob, const Buffs_Struct& buff, uint32_t slot,
|
||||
bool fade) const = 0;
|
||||
virtual std::unique_ptr<EQApplicationPacket> RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
|
||||
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const = 0;
|
||||
virtual void SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const = 0;
|
||||
virtual bool NeedsWearMessage() const = 0;
|
||||
|
||||
uint32_t ServerToPatchBuffSlot(uint32_t slot) const;
|
||||
|
||||
protected:
|
||||
uint32_t m_maxLongBuffs;
|
||||
uint32_t m_maxShortBuffs;
|
||||
};
|
||||
|
||||
} // namespace Buff
|
||||
@@ -0,0 +1,51 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Migration path: replace string_ids.h usage with ID enum values one call site at a time.
|
||||
|
||||
class Client;
|
||||
class Mob;
|
||||
class EQApplicationPacket;
|
||||
|
||||
namespace ClientPatch {
|
||||
|
||||
template<typename... Args>
|
||||
concept AllConstChar = (std::is_convertible_v<Args, const char*> && ...);
|
||||
|
||||
class IMessage
|
||||
{
|
||||
public:
|
||||
using FormattedArgs = std::array<const char*, 9>;
|
||||
IMessage() = default;
|
||||
virtual ~IMessage() = default;
|
||||
|
||||
// these two are the basic string message packets
|
||||
virtual std::unique_ptr<EQApplicationPacket> Simple(uint32_t color, uint32_t id) const = 0;
|
||||
virtual std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
|
||||
const FormattedArgs& args) const = 0;
|
||||
|
||||
// These aren't technically messages, but they use the same format and are similar enough to include here
|
||||
virtual std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const = 0;
|
||||
virtual std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name, const char* spell_link) const = 0;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
@@ -0,0 +1,123 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "client_version.h"
|
||||
|
||||
#include "common/emu_constants.h"
|
||||
|
||||
#include "common/patches/titanium.h"
|
||||
#include "common/patches/titanium_limits.h"
|
||||
#include "common/patches/sof.h"
|
||||
#include "common/patches/sof_limits.h"
|
||||
#include "common/patches/sod.h"
|
||||
#include "common/patches/sod_limits.h"
|
||||
#include "common/patches/uf.h"
|
||||
#include "common/patches/uf_limits.h"
|
||||
#include "common/patches/rof.h"
|
||||
#include "common/patches/rof_limits.h"
|
||||
#include "common/patches/rof2.h"
|
||||
#include "common/patches/rof2_limits.h"
|
||||
#include "common/patches/tob.h"
|
||||
#include "common/patches/tob_limits.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
using Version = EQ::versions::ClientVersion;
|
||||
|
||||
struct ClientComponents
|
||||
{
|
||||
explicit ClientComponents(Version version) : version(version)
|
||||
{
|
||||
switch (version) {
|
||||
case Version::TOB:
|
||||
buffComponent = std::make_unique<TOB::BuffComponent>(TOB::spells::LONG_BUFFS, TOB::spells::SHORT_BUFFS);
|
||||
messageComponent = std::make_unique<TOB::MessageComponent>();
|
||||
break;
|
||||
case Version::RoF2:
|
||||
buffComponent = std::make_unique<UF::BuffComponent>(RoF2::spells::LONG_BUFFS, RoF2::spells::SHORT_BUFFS);
|
||||
messageComponent = std::make_unique<Titanium::MessageComponent>();
|
||||
break;
|
||||
case Version::RoF:
|
||||
buffComponent = std::make_unique<UF::BuffComponent>(RoF::spells::LONG_BUFFS, RoF::spells::SHORT_BUFFS);
|
||||
messageComponent = std::make_unique<Titanium::MessageComponent>();
|
||||
break;
|
||||
case Version::UF:
|
||||
buffComponent = std::make_unique<UF::BuffComponent>(UF::spells::LONG_BUFFS, UF::spells::SHORT_BUFFS);
|
||||
messageComponent = std::make_unique<Titanium::MessageComponent>();
|
||||
break;
|
||||
case Version::SoD:
|
||||
buffComponent = std::make_unique<SoD::BuffComponent>(SoD::spells::LONG_BUFFS, SoD::spells::SHORT_BUFFS);
|
||||
messageComponent = std::make_unique<Titanium::MessageComponent>();
|
||||
break;
|
||||
case Version::SoF:
|
||||
buffComponent = std::make_unique<Titanium::BuffComponent>(SoF::spells::LONG_BUFFS, SoF::spells::SHORT_BUFFS);
|
||||
messageComponent = std::make_unique<Titanium::MessageComponent>();
|
||||
break;
|
||||
case Version::Titanium:
|
||||
buffComponent = std::make_unique<Titanium::BuffComponent>(Titanium::spells::LONG_BUFFS, Titanium::spells::SHORT_BUFFS);
|
||||
messageComponent = std::make_unique<Titanium::MessageComponent>();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const Version version;
|
||||
std::unique_ptr<ClientPatch::IBuff> buffComponent;
|
||||
std::unique_ptr<ClientPatch::IMessage> messageComponent;
|
||||
};
|
||||
|
||||
// this array must be in the same order as the Version enum because it converts Version to index directly
|
||||
static const std::array<ClientComponents, EQ::versions::ClientVersionCount> s_patches = {
|
||||
{
|
||||
ClientComponents(Version::Unknown), // empty
|
||||
ClientComponents(Version::Client62), // empty
|
||||
ClientComponents(Version::Titanium),
|
||||
ClientComponents(Version::SoF),
|
||||
ClientComponents(Version::SoD),
|
||||
ClientComponents(Version::UF),
|
||||
ClientComponents(Version::RoF),
|
||||
ClientComponents(Version::RoF2),
|
||||
ClientComponents(Version::TOB),
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
const std::unique_ptr<ClientPatch::IBuff>& GetComponent(Version version)
|
||||
{
|
||||
return s_patches.at(static_cast<uint32_t>(version)).buffComponent;
|
||||
}
|
||||
|
||||
template<>
|
||||
const std::unique_ptr<ClientPatch::IMessage>& GetComponent(Version version)
|
||||
{
|
||||
return s_patches.at(static_cast<uint32_t>(version)).messageComponent;
|
||||
}
|
||||
|
||||
uint32_t ClientPatch::IBuff::ServerToPatchBuffSlot(uint32_t slot) const
|
||||
{
|
||||
// we're a disc
|
||||
if (slot >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
|
||||
return slot - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
|
||||
m_maxLongBuffs + m_maxShortBuffs;
|
||||
// we're a song
|
||||
if (slot >= EQ::spells::LONG_BUFFS)
|
||||
return slot - EQ::spells::LONG_BUFFS + m_maxLongBuffs;
|
||||
// we're a normal buff
|
||||
return slot; // as long as we guard against bad slots server side, we should be fine
|
||||
}
|
||||
@@ -15,37 +15,24 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/mutex.h"
|
||||
#include "common/platform/posix/include_pthreads.h"
|
||||
#include "common/platform/win/include_windows.h"
|
||||
#include "common/emu_versions.h"
|
||||
#include <memory>
|
||||
|
||||
//Sombody, someday needs to figure out how to implement a condition
|
||||
//system on windows...
|
||||
namespace ClientPatch {
|
||||
class IBuff;
|
||||
class IMessage;
|
||||
}
|
||||
|
||||
// store all static functions for the different patches here
|
||||
// store all static functions for the different patches here, this can return nullptr for unsupported patches
|
||||
template <typename Component>
|
||||
const std::unique_ptr<Component>& GetComponent(EQ::versions::ClientVersion version);
|
||||
|
||||
class Condition {
|
||||
private:
|
||||
#ifdef WIN32
|
||||
enum {
|
||||
SignalEvent = 0,
|
||||
BroadcastEvent,
|
||||
_eventCount
|
||||
};
|
||||
template <>
|
||||
const std::unique_ptr<ClientPatch::IBuff>& GetComponent(EQ::versions::ClientVersion version);
|
||||
|
||||
HANDLE m_events[_eventCount];
|
||||
uint32 m_waiters;
|
||||
CRITICAL_SECTION CSMutex;
|
||||
#else
|
||||
pthread_cond_t cond;
|
||||
pthread_mutex_t mutex;
|
||||
#endif
|
||||
public:
|
||||
Condition();
|
||||
void Signal();
|
||||
void SignalAll();
|
||||
void Wait();
|
||||
// bool TimedWait(unsigned long usec);
|
||||
~Condition();
|
||||
};
|
||||
template <>
|
||||
const std::unique_ptr<ClientPatch::IMessage>& GetComponent(EQ::versions::ClientVersion version);
|
||||
@@ -353,7 +353,7 @@
|
||||
0x05ea,
|
||||
0x1b6f,
|
||||
0x198e,
|
||||
0x7bd6, OP_Buff
|
||||
0x7bd6, OP_BuffDefinition
|
||||
0x3501,
|
||||
0x47ab,
|
||||
0x7a9e, OP_World_Client_CRC1
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "common/patches/rof2.h"
|
||||
#include "common/patches/sod.h"
|
||||
#include "common/patches/sof.h"
|
||||
#include "common/patches/tob.h"
|
||||
#include "common/patches/titanium.h"
|
||||
#include "common/patches/uf.h"
|
||||
|
||||
@@ -33,6 +34,7 @@ void RegisterAllPatches(EQStreamIdentifier &into)
|
||||
UF::Register(into);
|
||||
RoF::Register(into);
|
||||
RoF2::Register(into);
|
||||
TOB::Register(into);
|
||||
}
|
||||
|
||||
void ReloadAllPatches()
|
||||
@@ -43,4 +45,5 @@ void ReloadAllPatches()
|
||||
UF::Reload();
|
||||
RoF::Reload();
|
||||
RoF2::Reload();
|
||||
TOB::Reload();
|
||||
}
|
||||
|
||||
+6
-103
@@ -67,7 +67,6 @@ namespace RoF
|
||||
static inline spells::CastingSlot ServerToRoFCastingSlot(EQ::spells::CastingSlot slot);
|
||||
static inline EQ::spells::CastingSlot RoFToServerCastingSlot(spells::CastingSlot slot);
|
||||
|
||||
static inline int ServerToRoFBuffSlot(int index);
|
||||
static inline int RoFToServerBuffSlot(int index);
|
||||
|
||||
void Register(EQStreamIdentifier &into)
|
||||
@@ -402,7 +401,7 @@ namespace RoF
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_Buff)
|
||||
ENCODE(OP_BuffDefinition)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
|
||||
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
||||
@@ -410,8 +409,7 @@ namespace RoF
|
||||
OUT(entityid);
|
||||
OUT(buff.effect_type);
|
||||
OUT(buff.level);
|
||||
// just so we're 100% sure we get a 1.0f ...
|
||||
eq->buff.bard_modifier = emu->buff.bard_modifier == 10 ? 1.0f : emu->buff.bard_modifier / 10.0f;
|
||||
OUT(buff.bard_modifier);
|
||||
OUT(buff.spellid);
|
||||
OUT(buff.duration);
|
||||
OUT(buff.player_id);
|
||||
@@ -420,59 +418,13 @@ namespace RoF
|
||||
OUT(buff.x);
|
||||
OUT(buff.z);
|
||||
// TODO: implement slot_data stuff
|
||||
eq->slotid = ServerToRoFBuffSlot(emu->slotid);
|
||||
OUT(slotid);
|
||||
|
||||
if (emu->bufffade == 1)
|
||||
eq->bufffade = 1;
|
||||
else
|
||||
eq->bufffade = 2;
|
||||
|
||||
// Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon
|
||||
EQApplicationPacket *outapp = nullptr;
|
||||
if (eq->bufffade == 1)
|
||||
{
|
||||
outapp = new EQApplicationPacket(OP_BuffCreate, 29);
|
||||
outapp->WriteUInt32(emu->entityid);
|
||||
outapp->WriteUInt32(0); // tic timer
|
||||
outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ?
|
||||
outapp->WriteUInt16(1); // 1 buff in this packet
|
||||
outapp->WriteUInt32(eq->slotid);
|
||||
outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove)
|
||||
outapp->WriteUInt32(0); // Duration
|
||||
outapp->WriteUInt32(0); // numhits
|
||||
outapp->WriteUInt8(0); // Caster name
|
||||
outapp->WriteUInt8(0); // Type
|
||||
}
|
||||
FINISH_ENCODE();
|
||||
|
||||
if (outapp)
|
||||
dest->FastQueuePacket(&outapp); // Send the OP_BuffCreate to remove the buff
|
||||
}
|
||||
|
||||
ENCODE(OP_BuffCreate)
|
||||
{
|
||||
SETUP_VAR_ENCODE(BuffIcon_Struct);
|
||||
|
||||
uint32 sz = 12 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm
|
||||
__packet->size = sz;
|
||||
__packet->pBuffer = new unsigned char[sz];
|
||||
memset(__packet->pBuffer, 0, sz);
|
||||
|
||||
__packet->WriteUInt32(emu->entity_id);
|
||||
__packet->WriteUInt32(emu->tic_timer);
|
||||
__packet->WriteUInt8(emu->all_buffs); // 1 indicates all buffs on the player (0 to add or remove a single buff)
|
||||
__packet->WriteUInt16(emu->count);
|
||||
|
||||
for (int i = 0; i < emu->count; ++i)
|
||||
{
|
||||
__packet->WriteUInt32(emu->type == 0 ? ServerToRoFBuffSlot(emu->entries[i].buff_slot) : emu->entries[i].buff_slot);
|
||||
__packet->WriteUInt32(emu->entries[i].spell_id);
|
||||
__packet->WriteUInt32(emu->entries[i].tics_remaining);
|
||||
__packet->WriteUInt32(emu->entries[i].num_hits); // Unknown
|
||||
__packet->WriteString(emu->entries[i].caster);
|
||||
}
|
||||
__packet->WriteUInt8(emu->type); // Unknown
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
@@ -1858,38 +1810,6 @@ namespace RoF
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_PetBuffWindow)
|
||||
{
|
||||
// The format of the RoF packet is identical to the OP_BuffCreate packet.
|
||||
|
||||
SETUP_VAR_ENCODE(PetBuff_Struct);
|
||||
|
||||
uint32 sz = 12 + (17 * emu->buffcount);
|
||||
__packet->size = sz;
|
||||
__packet->pBuffer = new unsigned char[sz];
|
||||
memset(__packet->pBuffer, 0, sz);
|
||||
|
||||
__packet->WriteUInt32(emu->petid);
|
||||
__packet->WriteUInt32(0); // PlayerID ?
|
||||
__packet->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff)
|
||||
__packet->WriteUInt16(emu->buffcount);
|
||||
|
||||
for (uint16 i = 0; i < PET_BUFF_COUNT; ++i)
|
||||
{
|
||||
if (emu->spellid[i])
|
||||
{
|
||||
__packet->WriteUInt32(i);
|
||||
__packet->WriteUInt32(emu->spellid[i]);
|
||||
__packet->WriteUInt32(emu->ticsremaining[i]);
|
||||
__packet->WriteUInt32(0); // numhits
|
||||
__packet->WriteString("");
|
||||
}
|
||||
}
|
||||
__packet->WriteUInt8(0); // some sort of type
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_PlayerProfile)
|
||||
{
|
||||
EQApplicationPacket *in = *p;
|
||||
@@ -2179,7 +2099,7 @@ namespace RoF
|
||||
outapp->WriteUInt32(emu->buffs[r].counters);
|
||||
outapp->WriteUInt32(emu->buffs[r].duration);
|
||||
outapp->WriteUInt8(emu->buffs[r].level);
|
||||
outapp->WriteUInt32(emu->buffs[r].spellid);
|
||||
outapp->WriteSInt32 (emu->buffs[r].spellid);
|
||||
outapp->WriteUInt8(effect_type); // Only ever seen 2
|
||||
outapp->WriteUInt32(emu->buffs[r].num_hits);
|
||||
outapp->WriteUInt32(0);
|
||||
@@ -3221,7 +3141,7 @@ namespace RoF
|
||||
|
||||
buf.WriteString(new_message);
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, std::move(buf));
|
||||
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
delete in;
|
||||
@@ -3239,8 +3159,6 @@ namespace RoF
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); }
|
||||
|
||||
ENCODE(OP_TaskDescription)
|
||||
{
|
||||
EQApplicationPacket *in = *p;
|
||||
@@ -4196,7 +4114,7 @@ namespace RoF
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_Buff)
|
||||
DECODE(OP_BuffDefinition)
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
|
||||
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
||||
@@ -6263,21 +6181,6 @@ namespace RoF
|
||||
}
|
||||
}
|
||||
|
||||
// these should be optimized out for RoF since they should all boil down to return index :P
|
||||
// but lets leave it here for future proofing
|
||||
static inline int ServerToRoFBuffSlot(int index)
|
||||
{
|
||||
// we're a disc
|
||||
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
|
||||
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
|
||||
spells::LONG_BUFFS + spells::SHORT_BUFFS;
|
||||
// we're a song
|
||||
if (index >= EQ::spells::LONG_BUFFS)
|
||||
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
|
||||
// we're a normal buff
|
||||
return index; // as long as we guard against bad slots server side, we should be fine
|
||||
}
|
||||
|
||||
static inline int RoFToServerBuffSlot(int index)
|
||||
{
|
||||
// we're a disc
|
||||
|
||||
+16
-22
@@ -18,33 +18,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/struct_strategy.h"
|
||||
#include "common/patches/uf.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
|
||||
namespace RoF
|
||||
namespace RoF {
|
||||
|
||||
extern void Register(EQStreamIdentifier& into);
|
||||
extern void Reload();
|
||||
|
||||
class Strategy : public StructStrategy
|
||||
{
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
//these are the only public member of this namespace.
|
||||
extern void Register(EQStreamIdentifier &into);
|
||||
extern void Reload();
|
||||
protected:
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQ::versions::ClientVersion ClientVersion() const;
|
||||
|
||||
|
||||
|
||||
//you should not directly access anything below..
|
||||
//I just dont feel like making a seperate header for it.
|
||||
|
||||
class Strategy : public StructStrategy {
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
protected:
|
||||
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQ::versions::ClientVersion ClientVersion() const;
|
||||
|
||||
//magic macro to declare our opcode processors
|
||||
#include "ss_declare.h"
|
||||
#include "rof_ops.h"
|
||||
};
|
||||
//magic macro to declare our opcode processors
|
||||
#include "ss_declare.h"
|
||||
#include "rof_ops.h"
|
||||
};
|
||||
|
||||
} /*RoF*/
|
||||
|
||||
+18
-111
@@ -69,7 +69,6 @@ namespace RoF2
|
||||
static inline spells::CastingSlot ServerToRoF2CastingSlot(EQ::spells::CastingSlot slot);
|
||||
static inline EQ::spells::CastingSlot RoF2ToServerCastingSlot(spells::CastingSlot slot);
|
||||
|
||||
static inline int ServerToRoF2BuffSlot(int index);
|
||||
static inline int RoF2ToServerBuffSlot(int index);
|
||||
|
||||
void Register(EQStreamIdentifier &into)
|
||||
@@ -661,7 +660,7 @@ namespace RoF2
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_Buff)
|
||||
ENCODE(OP_BuffDefinition)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
|
||||
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
||||
@@ -678,59 +677,13 @@ namespace RoF2
|
||||
OUT(buff.y);
|
||||
OUT(buff.x);
|
||||
OUT(buff.z);
|
||||
eq->slotid = ServerToRoF2BuffSlot(emu->slotid);
|
||||
OUT(slotid);
|
||||
// TODO: implement slot_data stuff
|
||||
if (emu->bufffade == 1)
|
||||
eq->bufffade = 1;
|
||||
else
|
||||
eq->bufffade = 2;
|
||||
|
||||
// Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon
|
||||
EQApplicationPacket *outapp = nullptr;
|
||||
if (eq->bufffade == 1)
|
||||
{
|
||||
outapp = new EQApplicationPacket(OP_BuffCreate, 29);
|
||||
outapp->WriteUInt32(emu->entityid);
|
||||
outapp->WriteUInt32(0); // tic timer
|
||||
outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ?
|
||||
outapp->WriteUInt16(1); // 1 buff in this packet
|
||||
outapp->WriteUInt32(eq->slotid);
|
||||
outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove)
|
||||
outapp->WriteUInt32(0); // Duration
|
||||
outapp->WriteUInt32(0); // numhits
|
||||
outapp->WriteUInt8(0); // Caster name
|
||||
outapp->WriteUInt8(0); // Type
|
||||
}
|
||||
FINISH_ENCODE();
|
||||
|
||||
if (outapp)
|
||||
dest->FastQueuePacket(&outapp); // Send the OP_BuffCreate to remove the buff
|
||||
}
|
||||
|
||||
ENCODE(OP_BuffCreate)
|
||||
{
|
||||
SETUP_VAR_ENCODE(BuffIcon_Struct);
|
||||
|
||||
uint32 sz = 12 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm
|
||||
__packet->size = sz;
|
||||
__packet->pBuffer = new unsigned char[sz];
|
||||
memset(__packet->pBuffer, 0, sz);
|
||||
|
||||
__packet->WriteUInt32(emu->entity_id);
|
||||
__packet->WriteUInt32(emu->tic_timer);
|
||||
__packet->WriteUInt8(emu->all_buffs); // 1 indicates all buffs on the player (0 to add or remove a single buff)
|
||||
__packet->WriteUInt16(emu->count);
|
||||
|
||||
for (int i = 0; i < emu->count; ++i)
|
||||
{
|
||||
__packet->WriteUInt32(emu->type == 0 ? ServerToRoF2BuffSlot(emu->entries[i].buff_slot) : emu->entries[i].buff_slot);
|
||||
__packet->WriteUInt32(emu->entries[i].spell_id);
|
||||
__packet->WriteUInt32(emu->entries[i].tics_remaining);
|
||||
__packet->WriteUInt32(emu->entries[i].num_hits); // Unknown
|
||||
__packet->WriteString(emu->entries[i].caster);
|
||||
}
|
||||
__packet->WriteUInt8(emu->type); // Unknown
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
@@ -753,7 +706,7 @@ namespace RoF2
|
||||
ar(bl);
|
||||
|
||||
//packet size
|
||||
auto packet_size = bl.item_name.length() + 1 + 34;
|
||||
size_t packet_size = bl.item_name.length() + 1 + 34;
|
||||
for (auto const &b: bl.trade_items) {
|
||||
packet_size += b.item_name.length() + 1;
|
||||
packet_size += 12;
|
||||
@@ -1622,7 +1575,7 @@ namespace RoF2
|
||||
//Log.LogDebugType(Logs::General, Logs::Netcode, "[ERROR] Yourname is %s", gu2->yourname);
|
||||
|
||||
int MemberCount = 1;
|
||||
int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1;
|
||||
uint32 PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1;
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
@@ -2207,7 +2160,7 @@ namespace RoF2
|
||||
|
||||
char *Buffer = (char *)in->pBuffer;
|
||||
|
||||
int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4;
|
||||
uint32 PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4;
|
||||
PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount;
|
||||
|
||||
uint32 r;
|
||||
@@ -2278,15 +2231,19 @@ namespace RoF2
|
||||
// There are 2 different sized versions of this packet depending if a merc is hired or not
|
||||
if (emu->MercStatus >= 0)
|
||||
{
|
||||
PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount;
|
||||
|
||||
// Per-merc size: base struct minus Stances[1] and MercUnk05,
|
||||
// then add back actual stances and name length per merc.
|
||||
// MercUnk05 is a single trailing field after all mercs.
|
||||
PacketSize += sizeof(structs::MercenaryDataUpdate_Struct);
|
||||
uint32 r;
|
||||
uint32 k;
|
||||
for (r = 0; r < emu->MercCount; r++)
|
||||
{
|
||||
PacketSize += sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct) - sizeof(uint32); // subtract Stances[1] and MercUnk05
|
||||
PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount;
|
||||
PacketSize += strlen(emu->MercData[r].MercName); // Null Terminator size already accounted for in the struct
|
||||
}
|
||||
PacketSize += sizeof(uint32); // MercUnk05 - trailing field after all mercs
|
||||
|
||||
outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize);
|
||||
Buffer = (char *)outapp->pBuffer;
|
||||
@@ -2312,15 +2269,14 @@ namespace RoF2
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount);
|
||||
VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].MercUnk03);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk04);
|
||||
//VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // MercName
|
||||
VARSTRUCT_ENCODE_STRING(Buffer, emu->MercData[r].MercName);
|
||||
for (k = 0; k < emu->MercData[r].StanceCount; k++)
|
||||
{
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance);
|
||||
}
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); // MercUnk05
|
||||
}
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[0].MercUnk05); // MercUnk05 - trailing field (unlocked slot count)
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2461,38 +2417,6 @@ namespace RoF2
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_PetBuffWindow)
|
||||
{
|
||||
// The format of the RoF2 packet is identical to the OP_BuffCreate packet.
|
||||
|
||||
SETUP_VAR_ENCODE(PetBuff_Struct);
|
||||
|
||||
uint32 sz = 12 + (17 * emu->buffcount);
|
||||
__packet->size = sz;
|
||||
__packet->pBuffer = new unsigned char[sz];
|
||||
memset(__packet->pBuffer, 0, sz);
|
||||
|
||||
__packet->WriteUInt32(emu->petid);
|
||||
__packet->WriteUInt32(0); // PlayerID ?
|
||||
__packet->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff)
|
||||
__packet->WriteUInt16(emu->buffcount);
|
||||
|
||||
for (uint16 i = 0; i < PET_BUFF_COUNT; ++i)
|
||||
{
|
||||
if (emu->spellid[i])
|
||||
{
|
||||
__packet->WriteUInt32(i);
|
||||
__packet->WriteUInt32(emu->spellid[i]);
|
||||
__packet->WriteUInt32(emu->ticsremaining[i]);
|
||||
__packet->WriteUInt32(0); // num hits
|
||||
__packet->WriteString("");
|
||||
}
|
||||
}
|
||||
__packet->WriteUInt8(0); // some sort of type
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_PlayerProfile)
|
||||
{
|
||||
EQApplicationPacket *in = *p;
|
||||
@@ -2783,7 +2707,7 @@ namespace RoF2
|
||||
outapp->WriteUInt32(emu->buffs[r].counters);
|
||||
outapp->WriteUInt32(emu->buffs[r].duration);
|
||||
outapp->WriteUInt8(emu->buffs[r].level);
|
||||
outapp->WriteUInt32(emu->buffs[r].spellid);
|
||||
outapp->WriteSInt32 (emu->buffs[r].spellid);
|
||||
outapp->WriteUInt8(effect_type); // Only ever seen 2
|
||||
outapp->WriteUInt32(emu->buffs[r].num_hits);
|
||||
outapp->WriteUInt32(0);
|
||||
@@ -3820,7 +3744,7 @@ namespace RoF2
|
||||
|
||||
buf.WriteString(new_message);
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, std::move(buf));
|
||||
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
delete in;
|
||||
@@ -3838,8 +3762,6 @@ namespace RoF2
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); }
|
||||
|
||||
ENCODE(OP_TaskDescription)
|
||||
{
|
||||
EQApplicationPacket *in = *p;
|
||||
@@ -4120,8 +4042,8 @@ namespace RoF2
|
||||
std::begin(emu->items),
|
||||
std::end(emu->items),
|
||||
std::begin(eq->items),
|
||||
[&](const uint32 x) {
|
||||
return x;
|
||||
[&](uint64 x) {
|
||||
return static_cast<uint32>(x);
|
||||
}
|
||||
);
|
||||
std::copy_n(
|
||||
@@ -4599,7 +4521,7 @@ namespace RoF2
|
||||
int k;
|
||||
for (r = 0; r < entrycount; r++, emu++) {
|
||||
|
||||
int PacketSize = 206;
|
||||
uint32 PacketSize = 206;
|
||||
|
||||
PacketSize += strlen(emu->name);
|
||||
PacketSize += strlen(emu->lastName);
|
||||
@@ -5142,7 +5064,7 @@ namespace RoF2
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_Buff)
|
||||
DECODE(OP_BuffDefinition)
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
|
||||
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
||||
@@ -7492,21 +7414,6 @@ namespace RoF2
|
||||
}
|
||||
}
|
||||
|
||||
// these should be optimized out for RoF2 since they should all boil down to return index :P
|
||||
// but lets leave it here for future proofing
|
||||
static inline int ServerToRoF2BuffSlot(int index)
|
||||
{
|
||||
// we're a disc
|
||||
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
|
||||
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
|
||||
spells::LONG_BUFFS + spells::SHORT_BUFFS;
|
||||
// we're a song
|
||||
if (index >= EQ::spells::LONG_BUFFS)
|
||||
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
|
||||
// we're a normal buff
|
||||
return index; // as long as we guard against bad slots server side, we should be fine
|
||||
}
|
||||
|
||||
static inline int RoF2ToServerBuffSlot(int index)
|
||||
{
|
||||
// we're a disc
|
||||
|
||||
+16
-22
@@ -18,33 +18,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/struct_strategy.h"
|
||||
#include "common/patches/rof.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
|
||||
namespace RoF2
|
||||
namespace RoF2 {
|
||||
|
||||
extern void Register(EQStreamIdentifier& into);
|
||||
extern void Reload();
|
||||
|
||||
class Strategy : public StructStrategy
|
||||
{
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
//these are the only public member of this namespace.
|
||||
extern void Register(EQStreamIdentifier &into);
|
||||
extern void Reload();
|
||||
protected:
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQ::versions::ClientVersion ClientVersion() const;
|
||||
|
||||
|
||||
|
||||
//you should not directly access anything below..
|
||||
//I just dont feel like making a seperate header for it.
|
||||
|
||||
class Strategy : public StructStrategy {
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
protected:
|
||||
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQ::versions::ClientVersion ClientVersion() const;
|
||||
|
||||
//magic macro to declare our opcode processors
|
||||
#include "ss_declare.h"
|
||||
#include "rof2_ops.h"
|
||||
};
|
||||
//magic macro to declare our opcode processors
|
||||
#include "ss_declare.h"
|
||||
#include "rof2_ops.h"
|
||||
};
|
||||
|
||||
}; /*RoF2*/
|
||||
|
||||
@@ -43,8 +43,7 @@ E(OP_BazaarSearch)
|
||||
E(OP_BecomeTrader)
|
||||
E(OP_BeginCast)
|
||||
E(OP_BlockedBuffs)
|
||||
E(OP_Buff)
|
||||
E(OP_BuffCreate)
|
||||
E(OP_BuffDefinition)
|
||||
E(OP_BuyerItems)
|
||||
E(OP_CancelTrade)
|
||||
E(OP_CastSpell)
|
||||
@@ -100,7 +99,6 @@ E(OP_MoveItem)
|
||||
E(OP_NewSpawn)
|
||||
E(OP_NewZone)
|
||||
E(OP_OnLevelMessage)
|
||||
E(OP_PetBuffWindow)
|
||||
E(OP_PlayerProfile)
|
||||
E(OP_RaidJoin)
|
||||
E(OP_RaidUpdate)
|
||||
@@ -123,7 +121,6 @@ E(OP_SpawnAppearance)
|
||||
E(OP_SpawnDoor)
|
||||
E(OP_SpecialMesg)
|
||||
E(OP_Stun)
|
||||
E(OP_TargetBuffs)
|
||||
E(OP_TaskDescription)
|
||||
E(OP_TaskHistoryReply)
|
||||
E(OP_Track)
|
||||
@@ -153,7 +150,7 @@ D(OP_Barter)
|
||||
D(OP_BazaarSearch)
|
||||
D(OP_BlockedBuffs)
|
||||
D(OP_BookButton)
|
||||
D(OP_Buff)
|
||||
D(OP_BuffDefinition)
|
||||
D(OP_BuffRemoveRequest)
|
||||
D(OP_BuyerItems)
|
||||
D(OP_CastSpell)
|
||||
|
||||
@@ -668,7 +668,7 @@ struct NewZone_Struct {
|
||||
*/
|
||||
struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
uint32 reduction; // lowers reuse
|
||||
};
|
||||
@@ -705,7 +705,7 @@ struct ManaChange_Struct
|
||||
{
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*08*/ int32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
|
||||
@@ -719,7 +719,7 @@ struct SwapSpell_Struct
|
||||
|
||||
struct BeginCast_Struct
|
||||
{
|
||||
/*000*/ uint32 spell_id;
|
||||
/*000*/ int32 spell_id;
|
||||
/*004*/ uint16 caster_id;
|
||||
/*006*/ uint32 cast_time; // in miliseconds
|
||||
/*010*/
|
||||
@@ -728,7 +728,7 @@ struct BeginCast_Struct
|
||||
struct CastSpell_Struct
|
||||
{
|
||||
/*00*/ uint32 slot;
|
||||
/*04*/ uint32 spell_id;
|
||||
/*04*/ int32 spell_id;
|
||||
/*08*/ InventorySlot_Struct inventory_slot; // slot for clicky item, Seen unknown of 131 = normal cast
|
||||
/*20*/ uint32 target_id;
|
||||
/*24*/ uint32 cs_unknown[2];
|
||||
@@ -760,7 +760,7 @@ struct SpellBuff_Struct
|
||||
/*002*/ uint8 unknown002; //pretty sure padding now
|
||||
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking?
|
||||
/*004*/ float bard_modifier;
|
||||
/*008*/ uint32 spellid;
|
||||
/*008*/ int32 spellid;
|
||||
/*012*/ uint32 duration;
|
||||
/*016*/ uint32 player_id; // caster ID, pretty sure just zone ID
|
||||
/*020*/ uint32 num_hits;
|
||||
@@ -791,7 +791,7 @@ struct BuffRemoveRequest_Struct
|
||||
// not in use
|
||||
struct BuffIconEntry_Struct {
|
||||
/*000*/ uint32 buff_slot;
|
||||
/*004*/ uint32 spell_id;
|
||||
/*004*/ int32 spell_id;
|
||||
/*008*/ uint32 tics_remaining;
|
||||
/*012*/ uint32 num_hits;
|
||||
// char name[0]; caster name is also here sometimes
|
||||
@@ -1513,7 +1513,7 @@ struct CombatDamage_Struct
|
||||
/* 00 */ uint16 target;
|
||||
/* 02 */ uint16 source;
|
||||
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
|
||||
/* 05 */ uint32 spellid;
|
||||
/* 05 */ int32 spellid;
|
||||
/* 09 */ int32 damage;
|
||||
/* 13 */ float force; // cd cc cc 3d
|
||||
/* 17 */ float hit_heading; // see above notes in Action_Struct
|
||||
@@ -1549,7 +1549,7 @@ struct Death_Struct
|
||||
/*004*/ uint32 killer_id;
|
||||
/*008*/ uint32 corpseid; // was corpseid
|
||||
/*012*/ uint32 attack_skill; // was type
|
||||
/*016*/ uint32 spell_id;
|
||||
/*016*/ int32 spell_id;
|
||||
/*020*/ uint32 bindzoneid; //bindzoneid?
|
||||
/*024*/ uint32 damage;
|
||||
/*028*/ uint32 unknown028;
|
||||
@@ -2658,7 +2658,7 @@ struct GroupFollow_Struct { // Live Follow Struct
|
||||
};
|
||||
|
||||
struct InspectBuffs_Struct {
|
||||
/*000*/ uint32 spell_id[BUFF_COUNT];
|
||||
/*000*/ int32 spell_id[BUFF_COUNT];
|
||||
/*168*/ int32 tics_remaining[BUFF_COUNT];
|
||||
};
|
||||
|
||||
@@ -3077,7 +3077,7 @@ struct Resurrect_Struct
|
||||
/*024*/ char your_name[64];
|
||||
/*088*/ uint32 unknown088;
|
||||
/*092*/ char rezzer_name[64];
|
||||
/*156*/ uint32 spellid;
|
||||
/*156*/ int32 spellid;
|
||||
/*160*/ char corpse_name[64];
|
||||
/*224*/ uint32 action;
|
||||
/*228*/ uint32 unknown228;
|
||||
@@ -4581,7 +4581,7 @@ struct SendAA_Struct {
|
||||
/*0045*/ uint32 prereq_minpoints_count; // mutliple prereqs at least 1, even no prereqs
|
||||
/*0049*/ uint32 prereq_minpoints; //min points in the prereq
|
||||
/*0053*/ uint32 type;
|
||||
/*0057*/ uint32 spellid;
|
||||
/*0057*/ int32 spellid;
|
||||
/*0061*/ uint32 unknown057; // Introduced during HoT - Seen 1 - Maybe account status or enable/disable AA?
|
||||
/*0065*/ uint32 spell_type;
|
||||
/*0069*/ uint32 spell_refresh;
|
||||
|
||||
@@ -27,8 +27,7 @@ E(OP_Barter)
|
||||
E(OP_BazaarSearch)
|
||||
E(OP_BeginCast)
|
||||
E(OP_BlockedBuffs)
|
||||
E(OP_Buff)
|
||||
E(OP_BuffCreate)
|
||||
E(OP_BuffDefinition)
|
||||
E(OP_CancelTrade)
|
||||
E(OP_CastSpell)
|
||||
E(OP_ChannelMessage)
|
||||
@@ -81,7 +80,6 @@ E(OP_MoveItem)
|
||||
E(OP_NewSpawn)
|
||||
E(OP_NewZone)
|
||||
E(OP_OnLevelMessage)
|
||||
E(OP_PetBuffWindow)
|
||||
E(OP_PlayerProfile)
|
||||
E(OP_RaidJoin)
|
||||
E(OP_RaidUpdate)
|
||||
@@ -104,7 +102,6 @@ E(OP_SpawnAppearance)
|
||||
E(OP_SpawnDoor)
|
||||
E(OP_SpecialMesg)
|
||||
E(OP_Stun)
|
||||
E(OP_TargetBuffs)
|
||||
E(OP_TaskDescription)
|
||||
E(OP_TaskHistoryReply)
|
||||
E(OP_Track)
|
||||
@@ -131,7 +128,7 @@ D(OP_AugmentInfo)
|
||||
D(OP_AugmentItem)
|
||||
D(OP_BazaarSearch)
|
||||
D(OP_BlockedBuffs)
|
||||
D(OP_Buff)
|
||||
D(OP_BuffDefinition)
|
||||
D(OP_BuffRemoveRequest)
|
||||
D(OP_CastSpell)
|
||||
D(OP_ChannelMessage)
|
||||
|
||||
@@ -614,7 +614,7 @@ struct NewZone_Struct {
|
||||
*/
|
||||
struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
uint32 reduction; // lowers reuse
|
||||
};
|
||||
@@ -651,7 +651,7 @@ struct ManaChange_Struct
|
||||
{
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*08*/ int32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
|
||||
@@ -665,7 +665,7 @@ struct SwapSpell_Struct
|
||||
|
||||
struct BeginCast_Struct
|
||||
{
|
||||
/*000*/ uint32 spell_id;
|
||||
/*000*/ int32 spell_id;
|
||||
/*004*/ uint16 caster_id;
|
||||
/*006*/ uint32 cast_time; // in miliseconds
|
||||
/*010*/
|
||||
@@ -674,7 +674,7 @@ struct BeginCast_Struct
|
||||
struct CastSpell_Struct
|
||||
{
|
||||
/*00*/ uint32 slot;
|
||||
/*04*/ uint32 spell_id;
|
||||
/*04*/ int32 spell_id;
|
||||
/*08*/ InventorySlot_Struct inventory_slot; // slot for clicky item, Seen unknown of 131 = normal cast
|
||||
/*20*/ uint32 target_id;
|
||||
/*24*/ uint32 cs_unknown[2];
|
||||
@@ -706,7 +706,7 @@ struct SpellBuff_Struct
|
||||
/*002*/ uint8 unknown002; //pretty sure padding now
|
||||
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking?
|
||||
/*004*/ float bard_modifier;
|
||||
/*008*/ uint32 spellid;
|
||||
/*008*/ int32 spellid;
|
||||
/*012*/ uint32 duration;
|
||||
/*016*/ uint32 player_id; // caster ID, pretty sure just zone ID
|
||||
/*020*/ uint32 num_hits;
|
||||
@@ -737,7 +737,7 @@ struct BuffRemoveRequest_Struct
|
||||
// not in use
|
||||
struct BuffIconEntry_Struct {
|
||||
/*000*/ uint32 buff_slot;
|
||||
/*004*/ uint32 spell_id;
|
||||
/*004*/ int32 spell_id;
|
||||
/*008*/ uint32 tics_remaining;
|
||||
/*012*/ uint32 num_hits;
|
||||
// char name[0]; caster name is also here sometimes
|
||||
@@ -1500,7 +1500,7 @@ struct CombatDamage_Struct
|
||||
/* 00 */ uint16 target;
|
||||
/* 02 */ uint16 source;
|
||||
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
|
||||
/* 05 */ uint32 spellid;
|
||||
/* 05 */ int32 spellid;
|
||||
/* 09 */ int32 damage;
|
||||
/* 13 */ float force; // cd cc cc 3d
|
||||
/* 17 */ float hit_heading; // see above notes in Action_Struct
|
||||
@@ -1536,7 +1536,7 @@ struct Death_Struct
|
||||
/*004*/ uint32 killer_id;
|
||||
/*008*/ uint32 corpseid; // was corpseid
|
||||
/*012*/ uint32 attack_skill; // was type
|
||||
/*016*/ uint32 spell_id;
|
||||
/*016*/ int32 spell_id;
|
||||
/*020*/ uint32 bindzoneid; //bindzoneid?
|
||||
/*024*/ uint32 damage;
|
||||
/*028*/ uint32 unknown028;
|
||||
@@ -2603,7 +2603,7 @@ struct GroupFollow_Struct { // Live Follow Struct
|
||||
};
|
||||
|
||||
struct InspectBuffs_Struct {
|
||||
/*000*/ uint32 spell_id[BUFF_COUNT];
|
||||
/*000*/ int32 spell_id[BUFF_COUNT];
|
||||
/*168*/ int32 tics_remaining[BUFF_COUNT];
|
||||
};
|
||||
|
||||
@@ -3013,7 +3013,7 @@ struct Resurrect_Struct
|
||||
/*024*/ char your_name[64];
|
||||
/*088*/ uint32 unknown088;
|
||||
/*092*/ char rezzer_name[64];
|
||||
/*156*/ uint32 spellid;
|
||||
/*156*/ int32 spellid;
|
||||
/*160*/ char corpse_name[64];
|
||||
/*224*/ uint32 action;
|
||||
/*228*/ uint32 unknown228;
|
||||
@@ -4333,7 +4333,7 @@ struct SendAA_Struct {
|
||||
/*0045*/ uint32 prereq_minpoints_count; // mutliple prereqs at least 1, even no prereqs
|
||||
/*0049*/ uint32 prereq_minpoints; //min points in the prereq
|
||||
/*0053*/ uint32 type;
|
||||
/*0057*/ uint32 spellid;
|
||||
/*0057*/ int32 spellid;
|
||||
/*0061*/ uint32 unknown057; // Introduced during HoT - Seen 1 - Maybe account status or enable/disable AA?
|
||||
/*0065*/ uint32 spell_type;
|
||||
/*0069*/ uint32 spell_refresh;
|
||||
|
||||
+57
-79
@@ -31,6 +31,8 @@
|
||||
#include "common/raid.h"
|
||||
#include "common/rulesys.h"
|
||||
#include "common/strings.h"
|
||||
#include "zone/client.h"
|
||||
#include "zone/mob.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
@@ -61,7 +63,6 @@ namespace SoD
|
||||
static inline spells::CastingSlot ServerToSoDCastingSlot(EQ::spells::CastingSlot slot);
|
||||
static inline EQ::spells::CastingSlot SoDToServerCastingSlot(spells::CastingSlot slot);
|
||||
|
||||
static inline int ServerToSoDBuffSlot(int index);
|
||||
static inline int SoDToServerBuffSlot(int index);
|
||||
|
||||
void Register(EQStreamIdentifier &into)
|
||||
@@ -293,7 +294,7 @@ namespace SoD
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
}
|
||||
|
||||
ENCODE(OP_Buff)
|
||||
ENCODE(OP_BuffDefinition)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
|
||||
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
||||
@@ -306,7 +307,7 @@ namespace SoD
|
||||
OUT(buff.duration);
|
||||
OUT(buff.counters);
|
||||
OUT(buff.player_id);
|
||||
eq->slotid = ServerToSoDBuffSlot(emu->slotid);
|
||||
OUT(slotid);
|
||||
OUT(bufffade);
|
||||
|
||||
FINISH_ENCODE();
|
||||
@@ -1375,38 +1376,6 @@ namespace SoD
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_PetBuffWindow)
|
||||
{
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
PetBuff_Struct *emu = (PetBuff_Struct *)__emu_buffer;
|
||||
int PacketSize = 7 + (emu->buffcount * 13);
|
||||
in->size = PacketSize;
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
char *Buffer = (char *)in->pBuffer;
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid);
|
||||
VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount);
|
||||
|
||||
for (unsigned int i = 0; i < PET_BUFF_COUNT; ++i)
|
||||
{
|
||||
if (emu->spellid[i])
|
||||
{
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff.
|
||||
}
|
||||
}
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); // I think this is actually some sort of type
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
}
|
||||
|
||||
ENCODE(OP_PlayerProfile)
|
||||
{
|
||||
SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct);
|
||||
@@ -2128,7 +2097,7 @@ namespace SoD
|
||||
|
||||
buf.WriteString(new_message);
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, std::move(buf));
|
||||
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
delete in;
|
||||
@@ -2145,35 +2114,6 @@ namespace SoD
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_TargetBuffs)
|
||||
{
|
||||
SETUP_VAR_ENCODE(BuffIcon_Struct);
|
||||
|
||||
uint32 sz = 7 + (13 * emu->count);
|
||||
__packet->size = sz;
|
||||
__packet->pBuffer = new unsigned char[sz];
|
||||
memset(__packet->pBuffer, 0, sz);
|
||||
|
||||
uchar *ptr = __packet->pBuffer;
|
||||
*((uint32*)ptr) = emu->entity_id;
|
||||
ptr += sizeof(uint32);
|
||||
|
||||
*((uint16*)ptr) = emu->count;
|
||||
ptr += sizeof(uint16);
|
||||
|
||||
for (uint16 i = 0; i < emu->count; ++i)
|
||||
{
|
||||
*((uint32*)ptr) = emu->entries[i].buff_slot;
|
||||
ptr += sizeof(uint32);
|
||||
*((uint32*)ptr) = emu->entries[i].spell_id;
|
||||
ptr += sizeof(uint32);
|
||||
*((uint32*)ptr) = emu->entries[i].tics_remaining;
|
||||
ptr += sizeof(uint32);
|
||||
ptr += 1;
|
||||
}
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_TaskDescription)
|
||||
{
|
||||
EQApplicationPacket *in = *p;
|
||||
@@ -2877,7 +2817,7 @@ namespace SoD
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_Buff)
|
||||
DECODE(OP_BuffDefinition)
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
|
||||
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
||||
@@ -4259,19 +4199,6 @@ namespace SoD
|
||||
}
|
||||
}
|
||||
|
||||
static inline int ServerToSoDBuffSlot(int index)
|
||||
{
|
||||
// we're a disc
|
||||
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
|
||||
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
|
||||
spells::LONG_BUFFS + spells::SHORT_BUFFS;
|
||||
// we're a song
|
||||
if (index >= EQ::spells::LONG_BUFFS)
|
||||
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
|
||||
// we're a normal buff
|
||||
return index; // as long as we guard against bad slots server side, we should be fine
|
||||
}
|
||||
|
||||
static inline int SoDToServerBuffSlot(int index)
|
||||
{
|
||||
// we're a disc
|
||||
@@ -4284,4 +4211,55 @@ namespace SoD
|
||||
// we're a normal buff
|
||||
return index; // as long as we guard against bad slots server side, we should be fine
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> BuffComponent::RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
|
||||
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const
|
||||
{
|
||||
if (opcode == OP_RefreshPetBuffs || opcode == OP_RefreshTargetBuffs) {
|
||||
Buffs_Struct* buffs = mob->GetBuffs();
|
||||
|
||||
size_t buffer_size = 7; // 7 bytes outside the list
|
||||
std::vector<uint32_t> send_slots;
|
||||
if (slots.empty()) {
|
||||
for (uint32_t slot = 0; slot < mob->GetMaxTotalSlots(); ++slot)
|
||||
if (buffs[slot].spellid > 1) {
|
||||
buffer_size += 13 + strlen(buffs[slot].caster_name); // 13 includes the null terminator
|
||||
send_slots.push_back(slot);
|
||||
}
|
||||
} else {
|
||||
for (uint32_t slot : slots)
|
||||
if (slot < mob->GetMaxTotalSlots() && buffs[slot].spellid > 1) {
|
||||
buffer_size += 13 + strlen(buffs[slot].caster_name);
|
||||
send_slots.push_back(slot);
|
||||
}
|
||||
}
|
||||
|
||||
// SoD only supports target and pet refresh, not self refresh packets
|
||||
SerializeBuffer buffer(buffer_size);
|
||||
|
||||
buffer.WriteUInt32(mob->GetID());
|
||||
buffer.WriteUInt16(send_slots.size());
|
||||
|
||||
for (uint32_t slot : send_slots) {
|
||||
buffer.WriteUInt32(ServerToPatchBuffSlot(slot));
|
||||
buffer.WriteInt32(remove ? -1 : buffs[slot].spellid);
|
||||
buffer.WriteInt32(buffs[slot].ticsremaining);
|
||||
buffer.WriteString(buffs[slot].caster_name);
|
||||
}
|
||||
|
||||
buffer.WriteUInt8(opcode == OP_RefreshPetBuffs ? 2 : 0);
|
||||
|
||||
return std::make_unique<EQApplicationPacket>(opcode, std::move(buffer));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 0 = self buff window, 1 = self target window, 2 = pet buff or target window, 4 = group, 5 = PC, 7 = NPC
|
||||
void BuffComponent::SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const
|
||||
{
|
||||
if (packet)
|
||||
packet->pBuffer[packet->size - 1] = refresh_type;
|
||||
}
|
||||
|
||||
} /*SoD*/
|
||||
|
||||
+26
-20
@@ -18,33 +18,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/struct_strategy.h"
|
||||
#include "common/patches/sof.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
|
||||
namespace SoD
|
||||
namespace SoD {
|
||||
|
||||
extern void Register(EQStreamIdentifier& into);
|
||||
extern void Reload();
|
||||
|
||||
class Strategy : public StructStrategy
|
||||
{
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
//these are the only public member of this namespace.
|
||||
extern void Register(EQStreamIdentifier &into);
|
||||
extern void Reload();
|
||||
protected:
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQ::versions::ClientVersion ClientVersion() const;
|
||||
|
||||
//magic macro to declare our opcode processors
|
||||
#include "ss_declare.h"
|
||||
#include "sod_ops.h"
|
||||
};
|
||||
|
||||
class BuffComponent : public Titanium::BuffComponent
|
||||
{
|
||||
public:
|
||||
BuffComponent(uint32_t maxLongBuffs, uint32_t maxShortBuffs) : Titanium::BuffComponent(maxLongBuffs, maxShortBuffs) {}
|
||||
BuffComponent() = delete;
|
||||
~BuffComponent() override = default;
|
||||
|
||||
//you should not directly access anything below..
|
||||
//I just dont feel like making a seperate header for it.
|
||||
|
||||
class Strategy : public StructStrategy {
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
protected:
|
||||
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQ::versions::ClientVersion ClientVersion() const;
|
||||
|
||||
//magic macro to declare our opcode processors
|
||||
#include "ss_declare.h"
|
||||
#include "sod_ops.h"
|
||||
};
|
||||
std::unique_ptr<EQApplicationPacket> RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
|
||||
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const override;
|
||||
void SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const override;
|
||||
};
|
||||
|
||||
} /*SoD*/
|
||||
|
||||
@@ -23,7 +23,7 @@ E(OP_ApplyPoison)
|
||||
E(OP_AugmentInfo)
|
||||
E(OP_Barter)
|
||||
E(OP_BazaarSearch)
|
||||
E(OP_Buff)
|
||||
E(OP_BuffDefinition)
|
||||
E(OP_CancelTrade)
|
||||
E(OP_ChannelMessage)
|
||||
E(OP_CharInventory)
|
||||
@@ -65,7 +65,6 @@ E(OP_MoveItem)
|
||||
E(OP_NewSpawn)
|
||||
E(OP_NewZone)
|
||||
E(OP_OnLevelMessage)
|
||||
E(OP_PetBuffWindow)
|
||||
E(OP_PlayerProfile)
|
||||
E(OP_RaidJoin)
|
||||
E(OP_RaidUpdate)
|
||||
@@ -80,7 +79,6 @@ E(OP_SomeItemPacketMaybe)
|
||||
E(OP_SpawnDoor)
|
||||
E(OP_SpecialMesg)
|
||||
E(OP_Stun)
|
||||
E(OP_TargetBuffs)
|
||||
E(OP_TaskDescription)
|
||||
E(OP_Track)
|
||||
E(OP_Trader)
|
||||
@@ -102,7 +100,7 @@ D(OP_AugmentInfo)
|
||||
D(OP_AugmentItem)
|
||||
D(OP_BazaarSearch)
|
||||
D(OP_BookButton)
|
||||
D(OP_Buff)
|
||||
D(OP_BuffDefinition)
|
||||
D(OP_CastSpell)
|
||||
D(OP_ChannelMessage)
|
||||
D(OP_CharacterCreate)
|
||||
|
||||
@@ -480,7 +480,7 @@ struct NewZone_Struct {
|
||||
*/
|
||||
struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
uint32 reduction; // lowers reuse
|
||||
};
|
||||
@@ -517,7 +517,7 @@ struct ManaChange_Struct
|
||||
{
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*08*/ int32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
|
||||
@@ -535,14 +535,14 @@ struct BeginCast_Struct
|
||||
{
|
||||
// len = 8
|
||||
/*004*/ uint16 caster_id;
|
||||
/*006*/ uint16 spell_id;
|
||||
/*006*/ int16 spell_id;
|
||||
/*016*/ uint32 cast_time; // in miliseconds
|
||||
};
|
||||
|
||||
struct CastSpell_Struct
|
||||
{
|
||||
uint32 slot;
|
||||
uint32 spell_id;
|
||||
int32 spell_id;
|
||||
uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast
|
||||
uint32 target_id;
|
||||
uint8 cs_unknown[4];
|
||||
@@ -571,7 +571,7 @@ struct SpellBuff_Struct
|
||||
/*001*/ uint8 level; // Seen 1 for no buff
|
||||
/*002*/ uint8 bard_modifier;
|
||||
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking?
|
||||
/*004*/ uint32 spellid;
|
||||
/*004*/ int32 spellid;
|
||||
/*008*/ uint32 duration;
|
||||
/*012*/ uint32 counters;
|
||||
/*016*/ uint32 unknown016;
|
||||
@@ -1266,7 +1266,7 @@ struct CombatDamage_Struct
|
||||
/* 00 */ uint16 target;
|
||||
/* 02 */ uint16 source;
|
||||
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
|
||||
/* 05 */ uint16 spellid;
|
||||
/* 05 */ int16 spellid;
|
||||
/* 07 */ int32 damage;
|
||||
/* 11 */ float force; // cd cc cc 3d
|
||||
/* 15 */ float hit_heading; // see above notes in Action_Struct
|
||||
@@ -1301,7 +1301,7 @@ struct Death_Struct
|
||||
/*004*/ uint32 killer_id;
|
||||
/*008*/ uint32 corpseid; // was corpseid
|
||||
/*012*/ uint32 attack_skill; // was type
|
||||
/*016*/ uint32 spell_id;
|
||||
/*016*/ int32 spell_id;
|
||||
/*020*/ uint32 bindzoneid; //bindzoneid?
|
||||
/*024*/ uint32 damage;
|
||||
/*028*/ uint32 unknown028;
|
||||
@@ -2534,7 +2534,7 @@ struct Resurrect_Struct {
|
||||
char your_name[64];
|
||||
uint32 unknown88;
|
||||
char rezzer_name[64];
|
||||
uint32 spellid;
|
||||
int32 spellid;
|
||||
char corpse_name[64];
|
||||
uint32 action;
|
||||
/* 228 */
|
||||
@@ -3787,7 +3787,7 @@ struct SendAA_Struct {
|
||||
/*0037*/ uint32 prereq_skill; //is < 0, abs() is category #
|
||||
/*0041*/ uint32 prereq_minpoints; //min points in the prereq
|
||||
/*0045*/ uint32 type;
|
||||
/*0049*/ uint32 spellid;
|
||||
/*0049*/ int32 spellid;
|
||||
/*0053*/ uint32 spell_type;
|
||||
/*0057*/ uint32 spell_refresh;
|
||||
/*0061*/ uint32 classes;
|
||||
|
||||
+5
-40
@@ -60,7 +60,6 @@ namespace SoF
|
||||
static inline spells::CastingSlot ServerToSoFCastingSlot(EQ::spells::CastingSlot slot);
|
||||
static inline EQ::spells::CastingSlot SoFToServerCastingSlot(spells::CastingSlot slot, uint32 item_location);
|
||||
|
||||
static inline int ServerToSoFBuffSlot(int index);
|
||||
static inline int SoFToServerBuffSlot(int index);
|
||||
|
||||
void Register(EQStreamIdentifier &into)
|
||||
@@ -272,7 +271,7 @@ namespace SoF
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_Buff)
|
||||
ENCODE(OP_BuffDefinition)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
|
||||
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
||||
@@ -285,7 +284,7 @@ namespace SoF
|
||||
OUT(buff.duration);
|
||||
OUT(buff.counters);
|
||||
OUT(buff.player_id);
|
||||
eq->slotid = ServerToSoFBuffSlot(emu->slotid);
|
||||
eq->slotid = SoFToServerBuffSlot(emu->slotid);
|
||||
OUT(bufffade);
|
||||
|
||||
FINISH_ENCODE();
|
||||
@@ -1049,28 +1048,6 @@ namespace SoF
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_PetBuffWindow)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(PetBuff_Struct);
|
||||
SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct);
|
||||
|
||||
OUT(petid);
|
||||
OUT(buffcount);
|
||||
|
||||
int EQBuffSlot = 0; // do we really want to shuffle them around like this?
|
||||
|
||||
for (uint32 EmuBuffSlot = 0; EmuBuffSlot < PET_BUFF_COUNT; ++EmuBuffSlot)
|
||||
{
|
||||
if (emu->spellid[EmuBuffSlot])
|
||||
{
|
||||
eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot];
|
||||
eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot];
|
||||
}
|
||||
}
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_PlayerProfile)
|
||||
{
|
||||
SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct);
|
||||
@@ -1785,7 +1762,7 @@ namespace SoF
|
||||
|
||||
buf.WriteString(new_message);
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, std::move(buf));
|
||||
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
delete in;
|
||||
@@ -2321,7 +2298,7 @@ namespace SoF
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_Buff)
|
||||
DECODE(OP_BuffDefinition)
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
|
||||
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
||||
@@ -2334,7 +2311,7 @@ namespace SoF
|
||||
IN(buff.duration);
|
||||
IN(buff.counters);
|
||||
IN(buff.player_id);
|
||||
emu->slotid = SoFToServerBuffSlot(eq->slotid);
|
||||
IN(slotid);
|
||||
IN(bufffade);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
@@ -3657,18 +3634,6 @@ namespace SoF
|
||||
}
|
||||
}
|
||||
|
||||
static inline int ServerToSoFBuffSlot(int index) {
|
||||
// we're a disc
|
||||
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
|
||||
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
|
||||
spells::LONG_BUFFS + spells::SHORT_BUFFS;
|
||||
// we're a song
|
||||
if (index >= EQ::spells::LONG_BUFFS)
|
||||
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
|
||||
// we're a normal buff
|
||||
return index; // as long as we guard against bad slots server side, we should be fine
|
||||
}
|
||||
|
||||
static inline int SoFToServerBuffSlot(int index)
|
||||
{
|
||||
// we're a disc
|
||||
|
||||
+16
-22
@@ -18,33 +18,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/struct_strategy.h"
|
||||
#include "common/patches/titanium.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
|
||||
namespace SoF
|
||||
namespace SoF {
|
||||
|
||||
extern void Register(EQStreamIdentifier& into);
|
||||
extern void Reload();
|
||||
|
||||
class Strategy : public StructStrategy
|
||||
{
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
//these are the only public member of this namespace.
|
||||
extern void Register(EQStreamIdentifier &into);
|
||||
extern void Reload();
|
||||
protected:
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQ::versions::ClientVersion ClientVersion() const;
|
||||
|
||||
|
||||
|
||||
//you should not directly access anything below..
|
||||
//I just dont feel like making a seperate header for it.
|
||||
|
||||
class Strategy : public StructStrategy {
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
protected:
|
||||
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQ::versions::ClientVersion ClientVersion() const;
|
||||
|
||||
//magic macro to declare our opcode processors
|
||||
#include "ss_declare.h"
|
||||
#include "sof_ops.h"
|
||||
};
|
||||
//magic macro to declare our opcode processors
|
||||
#include "ss_declare.h"
|
||||
#include "sof_ops.h"
|
||||
};
|
||||
|
||||
} /*SoF*/
|
||||
|
||||
@@ -23,7 +23,7 @@ E(OP_ApplyPoison)
|
||||
E(OP_AugmentInfo)
|
||||
E(OP_BazaarSearch)
|
||||
E(OP_BecomeTrader)
|
||||
E(OP_Buff)
|
||||
E(OP_BuffDefinition)
|
||||
E(OP_CancelTrade)
|
||||
E(OP_ChannelMessage)
|
||||
E(OP_CharInventory)
|
||||
@@ -60,7 +60,6 @@ E(OP_MoveItem)
|
||||
E(OP_NewSpawn)
|
||||
E(OP_NewZone)
|
||||
E(OP_OnLevelMessage)
|
||||
E(OP_PetBuffWindow)
|
||||
E(OP_PlayerProfile)
|
||||
E(OP_RaidJoin)
|
||||
E(OP_RaidUpdate)
|
||||
@@ -93,7 +92,7 @@ D(OP_ApplyPoison)
|
||||
D(OP_AugmentInfo)
|
||||
D(OP_AugmentItem)
|
||||
D(OP_BookButton)
|
||||
D(OP_Buff)
|
||||
D(OP_BuffDefinition)
|
||||
D(OP_Bug)
|
||||
D(OP_CastSpell)
|
||||
D(OP_ChannelMessage)
|
||||
|
||||
@@ -480,7 +480,7 @@ struct NewZone_Struct {
|
||||
*/
|
||||
struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
uint32 reduction; // lowers reuse
|
||||
};
|
||||
@@ -517,7 +517,7 @@ struct ManaChange_Struct
|
||||
{
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*08*/ int32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
/*16*/ int32 slot; // -1 for normal usage slot for when we want silent interrupt? I think it does timer stuff or something. Linked Spell Reuse interrupt uses it
|
||||
@@ -535,14 +535,14 @@ struct BeginCast_Struct
|
||||
{
|
||||
// len = 8
|
||||
/*004*/ uint16 caster_id;
|
||||
/*006*/ uint16 spell_id;
|
||||
/*006*/ int16 spell_id;
|
||||
/*016*/ uint32 cast_time; // in miliseconds
|
||||
};
|
||||
|
||||
struct CastSpell_Struct
|
||||
{
|
||||
uint32 slot;
|
||||
uint32 spell_id;
|
||||
int32 spell_id;
|
||||
uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast
|
||||
uint32 target_id;
|
||||
uint8 cs_unknown[4];
|
||||
@@ -571,7 +571,7 @@ struct SpellBuff_Struct
|
||||
/*001*/ uint8 level; // Seen 1 for no buff
|
||||
/*002*/ uint8 bard_modifier;
|
||||
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking?
|
||||
/*004*/ uint32 spellid;
|
||||
/*004*/ int32 spellid;
|
||||
/*008*/ uint32 duration;
|
||||
/*012*/ uint32 counters;
|
||||
/*016*/ uint32 unknown016;
|
||||
@@ -1266,7 +1266,7 @@ struct CombatDamage_Struct
|
||||
/* 00 */ uint16 target;
|
||||
/* 02 */ uint16 source;
|
||||
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
|
||||
/* 05 */ uint16 spellid;
|
||||
/* 05 */ int16 spellid;
|
||||
/* 07 */ int32 damage;
|
||||
/* 11 */ float force; // cd cc cc 3d
|
||||
/* 15 */ float hit_heading; // see above notes in Action_Struct
|
||||
@@ -1301,7 +1301,7 @@ struct Death_Struct
|
||||
/*004*/ uint32 killer_id;
|
||||
/*008*/ uint32 corpseid; // was corpseid
|
||||
/*012*/ uint32 attack_skill; // was type
|
||||
/*016*/ uint32 spell_id;
|
||||
/*016*/ int32 spell_id;
|
||||
/*020*/ uint32 bindzoneid; //bindzoneid?
|
||||
/*024*/ uint32 damage;
|
||||
/*028*/ uint32 unknown028;
|
||||
@@ -2504,7 +2504,7 @@ struct Resurrect_Struct {
|
||||
char your_name[64];
|
||||
uint32 unknown88;
|
||||
char rezzer_name[64];
|
||||
uint32 spellid;
|
||||
int32 spellid;
|
||||
char corpse_name[64];
|
||||
uint32 action;
|
||||
/* 228 */
|
||||
@@ -3711,7 +3711,7 @@ struct SendAA_Struct {
|
||||
/*0037*/ uint32 prereq_skill; //is < 0, abs() is category #
|
||||
/*0041*/ uint32 prereq_minpoints; //min points in the prereq
|
||||
/*0045*/ uint32 type;
|
||||
/*0049*/ uint32 spellid;
|
||||
/*0049*/ int32 spellid;
|
||||
/*0053*/ uint32 spell_type;
|
||||
/*0057*/ uint32 spell_refresh;
|
||||
/*0061*/ uint32 classes;
|
||||
|
||||
+13
-20
@@ -23,27 +23,20 @@ class EQStreamIdentifier;
|
||||
|
||||
namespace TEMPLATE {
|
||||
|
||||
//these are the only public member of this namespace.
|
||||
extern void Register(EQStreamIdentifier &into);
|
||||
extern void Reload();
|
||||
extern void Register(EQStreamIdentifier& into);
|
||||
extern void Reload();
|
||||
|
||||
class Strategy : public StructStrategy
|
||||
{
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
|
||||
//you should not directly access anything below..
|
||||
//I just dont feel like making a seperate header for it.
|
||||
|
||||
class Strategy : public StructStrategy {
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
protected:
|
||||
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQClientVersion ClientVersion() const;
|
||||
//magic macro to declare our opcodes
|
||||
#include "ss_declare.h"
|
||||
#include "TEMPLATE_ops.h"
|
||||
|
||||
};
|
||||
protected:
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQClientVersion ClientVersion() const;
|
||||
//magic macro to declare our opcodes
|
||||
#include "ss_declare.h"
|
||||
#include "TEMPLATE_ops.h"
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
+180
-40
@@ -32,6 +32,8 @@
|
||||
#include "common/raid.h"
|
||||
#include "common/rulesys.h"
|
||||
#include "common/strings.h"
|
||||
#include "zone/mob.h"
|
||||
#include "zone/string_ids.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -61,7 +63,6 @@ namespace Titanium
|
||||
static inline spells::CastingSlot ServerToTitaniumCastingSlot(EQ::spells::CastingSlot slot);
|
||||
static inline EQ::spells::CastingSlot TitaniumToServerCastingSlot(spells::CastingSlot slot, uint32 item_location);
|
||||
|
||||
static inline int ServerToTitaniumBuffSlot(int index);
|
||||
static inline int TitaniumToServerBuffSlot(int index);
|
||||
|
||||
void Register(EQStreamIdentifier &into)
|
||||
@@ -325,7 +326,7 @@ namespace Titanium
|
||||
}
|
||||
}
|
||||
|
||||
ENCODE(OP_Buff)
|
||||
ENCODE(OP_BuffDefinition)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct);
|
||||
SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
||||
@@ -338,7 +339,7 @@ namespace Titanium
|
||||
OUT(buff.duration);
|
||||
OUT(buff.counters);
|
||||
OUT(buff.player_id);
|
||||
eq->slotid = ServerToTitaniumBuffSlot(emu->slotid);
|
||||
OUT(slotid);
|
||||
OUT(bufffade);
|
||||
|
||||
FINISH_ENCODE();
|
||||
@@ -1306,28 +1307,6 @@ namespace Titanium
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_PetBuffWindow)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(PetBuff_Struct);
|
||||
SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct);
|
||||
|
||||
OUT(petid);
|
||||
OUT(buffcount);
|
||||
|
||||
int EQBuffSlot = 0; // do we really want to shuffle them around like this?
|
||||
|
||||
for (uint32 EmuBuffSlot = 0; EmuBuffSlot < PET_BUFF_COUNT; ++EmuBuffSlot)
|
||||
{
|
||||
if (emu->spellid[EmuBuffSlot])
|
||||
{
|
||||
eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot];
|
||||
eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot];
|
||||
}
|
||||
}
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_PlayerProfile)
|
||||
{
|
||||
SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct);
|
||||
@@ -1991,7 +1970,7 @@ namespace Titanium
|
||||
|
||||
buf.WriteString(new_message);
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
|
||||
auto outapp = new EQApplicationPacket(OP_SpecialMesg, std::move(buf));
|
||||
|
||||
dest->FastQueuePacket(&outapp, ack_req);
|
||||
delete in;
|
||||
@@ -2539,7 +2518,7 @@ namespace Titanium
|
||||
}
|
||||
}
|
||||
|
||||
DECODE(OP_Buff)
|
||||
DECODE(OP_BuffDefinition)
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
|
||||
SETUP_DIRECT_DECODE(SpellBuffPacket_Struct, structs::SpellBuffPacket_Struct);
|
||||
@@ -3893,19 +3872,6 @@ namespace Titanium
|
||||
}
|
||||
}
|
||||
|
||||
static inline int ServerToTitaniumBuffSlot(int index)
|
||||
{
|
||||
// we're a disc
|
||||
if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS)
|
||||
return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS +
|
||||
spells::LONG_BUFFS + spells::SHORT_BUFFS;
|
||||
// we're a song
|
||||
if (index >= EQ::spells::LONG_BUFFS)
|
||||
return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS;
|
||||
// we're a normal buff
|
||||
return index; // as long as we guard against bad slots server side, we should be fine
|
||||
}
|
||||
|
||||
static inline int TitaniumToServerBuffSlot(int index)
|
||||
{
|
||||
// we're a disc
|
||||
@@ -3918,4 +3884,178 @@ namespace Titanium
|
||||
// we're a normal buff
|
||||
return index; // as long as we guard against bad slots server side, we should be fine
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> MessageComponent::Simple(uint32_t color, uint32_t id) const
|
||||
{
|
||||
uint32_t string_id = ResolveID(id);
|
||||
if (string_id > 0) {
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_SimpleMessage, sizeof(SimpleMessage_Struct));
|
||||
auto* sms = reinterpret_cast<SimpleMessage_Struct*>(outapp->pBuffer);
|
||||
sms->string_id = string_id;
|
||||
sms->color = color;
|
||||
sms->unknown8 = 0;
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> MessageComponent::Formatted(uint32_t color, uint32_t id,
|
||||
const FormattedArgs& args) const
|
||||
{
|
||||
uint32_t string_id = ResolveID(id);
|
||||
if (string_id > 0) {
|
||||
std::array<const char*, 9> resolved_args = args;
|
||||
ResolveArguments(id, resolved_args);
|
||||
if (!resolved_args[0])
|
||||
return Simple(color, id);
|
||||
|
||||
SerializeBuffer buf(20);
|
||||
buf.WriteUInt32(0);
|
||||
buf.WriteUInt32(string_id);
|
||||
buf.WriteUInt32(color);
|
||||
|
||||
for (const auto* a : resolved_args) {
|
||||
if (a != nullptr)
|
||||
buf.WriteString(a);
|
||||
}
|
||||
|
||||
buf.WriteUInt8(0);
|
||||
|
||||
return std::make_unique<EQApplicationPacket>(OP_FormattedMessage, std::move(buf));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> MessageComponent::InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const
|
||||
{
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast, sizeof(InterruptCast_Struct));
|
||||
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
|
||||
ic->messageid = ResolveID(message);
|
||||
ic->spawnid = spawn_id;
|
||||
outapp->priority = 5;
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> MessageComponent::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name, const char* spell_link) const
|
||||
{
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(name) + 1);
|
||||
auto ic = reinterpret_cast<InterruptCast_Struct*>(outapp->pBuffer);
|
||||
ic->messageid = ResolveID(message);
|
||||
ic->spawnid = spawn_id;
|
||||
strcpy(ic->message, spell_link);
|
||||
return outapp;
|
||||
}
|
||||
|
||||
// A value of 0 means that the string isn't mapped in this client, valid string ids start at 1
|
||||
uint32_t MessageComponent::ResolveID(uint32_t id) const
|
||||
{
|
||||
// passthrough — string IDs are defined at the base client level;
|
||||
// override in patches where IDs need remapping
|
||||
return id;
|
||||
}
|
||||
|
||||
void MessageComponent::ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const
|
||||
{
|
||||
switch (id) {
|
||||
case SPELL_FIZZLE:
|
||||
case MISS_NOTE:
|
||||
args[0] = nullptr; // the 0th (and only) argument here is the spell link, not supported before TOB
|
||||
break;
|
||||
case SPELL_FIZZLE_OTHER:
|
||||
case MISSED_NOTE_OTHER:
|
||||
args[1] = nullptr; // the 1st argument here is the spell link, not supported before TOB
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> BuffComponent::BuffDefinition(Mob* mob, const Buffs_Struct& buff, uint32_t slot,
|
||||
bool fade) const
|
||||
{
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_BuffDefinition, sizeof(SpellBuffPacket_Struct));
|
||||
auto sbf = reinterpret_cast<SpellBuffPacket_Struct*>(outapp->pBuffer);
|
||||
|
||||
sbf->entityid = mob->GetID();
|
||||
|
||||
sbf->buff.effect_type = 2;
|
||||
|
||||
sbf->buff.level = buff.casterlevel > 0 ? buff.casterlevel : mob->GetLevel();
|
||||
sbf->buff.bard_modifier = buff.instrument_mod;
|
||||
sbf->buff.spellid = buff.spellid;
|
||||
sbf->buff.duration = buff.ticsremaining;
|
||||
if (buff.dot_rune)
|
||||
sbf->buff.counters = buff.dot_rune;
|
||||
else if (buff.magic_rune)
|
||||
sbf->buff.counters = buff.magic_rune;
|
||||
else if (buff.melee_rune)
|
||||
sbf->buff.counters = buff.melee_rune;
|
||||
else if (buff.counters)
|
||||
sbf->buff.counters = buff.counters;
|
||||
sbf->buff.player_id = buff.casterid;
|
||||
sbf->buff.num_hits = buff.hit_number;
|
||||
sbf->buff.y = buff.caston_y;
|
||||
sbf->buff.x = buff.caston_x;
|
||||
sbf->buff.z = buff.caston_z;
|
||||
|
||||
sbf->slotid = ServerToPatchBuffSlot(slot);
|
||||
sbf->bufffade = fade;
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> BuffComponent::RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
|
||||
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const
|
||||
{
|
||||
// titanium only sends refresh for pet buffs
|
||||
if (opcode == OP_RefreshPetBuffs) {
|
||||
Buffs_Struct* buffs = mob->GetBuffs();
|
||||
|
||||
std::vector<uint32_t> send_slots;
|
||||
if (slots.empty()) {
|
||||
for (uint32_t slot = 0; slot < mob->GetMaxTotalSlots(); ++slot)
|
||||
if (buffs[slot].spellid > 1)
|
||||
send_slots.push_back(slot);
|
||||
} else {
|
||||
for (uint32_t slot : slots)
|
||||
if (slot < mob->GetMaxTotalSlots() && buffs[slot].spellid > 1)
|
||||
send_slots.push_back(slot);
|
||||
}
|
||||
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_RefreshPetBuffs, sizeof(PetBuff_Struct));
|
||||
auto pbs = reinterpret_cast<PetBuff_Struct*>(outapp->pBuffer);
|
||||
memset(outapp->pBuffer, 0, outapp->size);
|
||||
|
||||
pbs->petid = mob->GetID();
|
||||
int MaxSlots = mob->GetMaxTotalSlots();
|
||||
if (MaxSlots > PET_BUFF_COUNT)
|
||||
MaxSlots = PET_BUFF_COUNT;
|
||||
|
||||
int count = 0;
|
||||
for (uint32_t slot : send_slots) {
|
||||
if (slot < MaxSlots) {
|
||||
pbs->spellid[slot] = buffs[slot].spellid;
|
||||
pbs->ticsremaining[slot] = buffs[slot].ticsremaining;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
pbs->buffcount = count;
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool BuffComponent::NeedsWearMessage() const { return true; }
|
||||
|
||||
void BuffComponent::SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const {}
|
||||
|
||||
} /*Titanium*/
|
||||
|
||||
+48
-16
@@ -18,33 +18,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/struct_strategy.h"
|
||||
#include "common/patches/IBuff.h"
|
||||
#include "common/patches/IMessage.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
|
||||
namespace Titanium
|
||||
namespace Titanium {
|
||||
|
||||
extern void Register(EQStreamIdentifier& into);
|
||||
extern void Reload();
|
||||
|
||||
class Strategy : public StructStrategy
|
||||
{
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
//these are the only public member of this namespace.
|
||||
extern void Register(EQStreamIdentifier &into);
|
||||
extern void Reload();
|
||||
protected:
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQ::versions::ClientVersion ClientVersion() const;
|
||||
|
||||
//magic macro to declare our opcode processors
|
||||
#include "ss_declare.h"
|
||||
#include "titanium_ops.h"
|
||||
};
|
||||
|
||||
class MessageComponent : public ClientPatch::IMessage
|
||||
{
|
||||
public:
|
||||
MessageComponent() = default;
|
||||
~MessageComponent() override = default;
|
||||
|
||||
//you should not directly access anything below..
|
||||
//I just dont feel like making a seperate header for it.
|
||||
std::unique_ptr<EQApplicationPacket> Simple(uint32_t color, uint32_t id) const override;
|
||||
std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
|
||||
const FormattedArgs& args) const override;
|
||||
|
||||
class Strategy : public StructStrategy {
|
||||
public:
|
||||
Strategy();
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const override;
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name,
|
||||
const char* spell_link) const override;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
[[nodiscard]] virtual uint32_t ResolveID(uint32_t id) const;
|
||||
virtual void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const;
|
||||
};
|
||||
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQ::versions::ClientVersion ClientVersion() const;
|
||||
class BuffComponent : public ClientPatch::IBuff
|
||||
{
|
||||
public:
|
||||
BuffComponent(uint32_t maxLongBuffs, uint32_t maxShortBuffs) : IBuff(maxLongBuffs, maxShortBuffs) {}
|
||||
BuffComponent() = delete;
|
||||
~BuffComponent() override = default;
|
||||
|
||||
//magic macro to declare our opcode processors
|
||||
#include "ss_declare.h"
|
||||
#include "titanium_ops.h"
|
||||
};
|
||||
std::unique_ptr<EQApplicationPacket> BuffDefinition(Mob* mob, const Buffs_Struct& buff, uint32_t slot,
|
||||
bool fade) const override;
|
||||
std::unique_ptr<EQApplicationPacket> RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
|
||||
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const override;
|
||||
bool NeedsWearMessage() const override;
|
||||
void SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const override;
|
||||
};
|
||||
|
||||
} /*Titanium*/
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ E(OP_AdventureMerchantSell)
|
||||
E(OP_ApplyPoison)
|
||||
E(OP_BazaarSearch)
|
||||
E(OP_BecomeTrader)
|
||||
E(OP_Buff)
|
||||
E(OP_BuffDefinition)
|
||||
E(OP_ChannelMessage)
|
||||
E(OP_CharInventory)
|
||||
E(OP_ClientUpdate)
|
||||
@@ -61,7 +61,6 @@ E(OP_ManaChange)
|
||||
E(OP_MemorizeSpell)
|
||||
E(OP_MoveItem)
|
||||
E(OP_OnLevelMessage)
|
||||
E(OP_PetBuffWindow)
|
||||
E(OP_PlayerProfile)
|
||||
E(OP_NewSpawn)
|
||||
E(OP_MarkRaidNPC)
|
||||
@@ -91,7 +90,7 @@ D(OP_AdventureMerchantSell)
|
||||
D(OP_ApplyPoison)
|
||||
D(OP_AugmentItem)
|
||||
D(OP_BazaarSearch)
|
||||
D(OP_Buff)
|
||||
D(OP_BuffDefinition)
|
||||
D(OP_Bug)
|
||||
D(OP_CastSpell)
|
||||
D(OP_ChannelMessage)
|
||||
|
||||
@@ -406,7 +406,7 @@ struct NewZone_Struct {
|
||||
*/
|
||||
struct MemorizeSpell_Struct {
|
||||
uint32 slot; // Spot in the spell book/memorized slot
|
||||
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
int32 spell_id; // Spell id (200 or c8 is minor healing, etc)
|
||||
uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming
|
||||
uint32 reduction; // lowers reuse
|
||||
};
|
||||
@@ -442,7 +442,7 @@ struct ManaChange_Struct
|
||||
{
|
||||
/*00*/ uint32 new_mana; // New Mana AMount
|
||||
/*04*/ uint32 stamina;
|
||||
/*08*/ uint32 spell_id;
|
||||
/*08*/ int32 spell_id;
|
||||
/*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting?
|
||||
/*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like
|
||||
};
|
||||
@@ -459,14 +459,14 @@ struct BeginCast_Struct
|
||||
{
|
||||
// len = 8
|
||||
/*000*/ uint16 caster_id;
|
||||
/*002*/ uint16 spell_id;
|
||||
/*002*/ int16 spell_id;
|
||||
/*004*/ uint32 cast_time; // in miliseconds
|
||||
};
|
||||
|
||||
struct CastSpell_Struct
|
||||
{
|
||||
uint32 slot;
|
||||
uint32 spell_id;
|
||||
int32 spell_id;
|
||||
uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast
|
||||
uint32 target_id;
|
||||
uint8 cs_unknown[4];
|
||||
@@ -495,7 +495,7 @@ struct SpellBuff_Struct
|
||||
/*001*/ uint8 level;
|
||||
/*002*/ uint8 bard_modifier;
|
||||
/*003*/ uint8 unknown003; // MQ2 used to call this "damage shield" -- don't see client referencing it, so maybe server side DS type tracking?
|
||||
/*004*/ uint32 spellid;
|
||||
/*004*/ int32 spellid;
|
||||
/*008*/ int32 duration;
|
||||
/*012*/ uint32 counters; // single book keeping value (counters, rune/vie)
|
||||
/*016*/ uint32 player_id; // caster ID, pretty sure just zone ID
|
||||
@@ -1142,7 +1142,7 @@ struct CombatDamage_Struct
|
||||
/* 00 */ uint16 target;
|
||||
/* 02 */ uint16 source;
|
||||
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells, skill
|
||||
/* 05 */ uint16 spellid;
|
||||
/* 05 */ int16 spellid;
|
||||
/* 07 */ uint32 damage;
|
||||
/* 11 */ float force;
|
||||
/* 15 */ float hit_heading; // see above notes in Action_Struct
|
||||
@@ -1177,7 +1177,7 @@ struct Death_Struct
|
||||
/*004*/ uint32 killer_id;
|
||||
/*008*/ uint32 corpseid; // was corpseid
|
||||
/*012*/ uint32 attack_skill; // was type
|
||||
/*016*/ uint32 spell_id;
|
||||
/*016*/ int32 spell_id;
|
||||
/*020*/ uint32 bindzoneid; //bindzoneid?
|
||||
/*024*/ uint32 damage;
|
||||
/*028*/ uint32 unknown028;
|
||||
@@ -2233,7 +2233,7 @@ struct Resurrect_Struct {
|
||||
char your_name[64];
|
||||
uint32 unknown88;
|
||||
char rezzer_name[64];
|
||||
uint32 spellid;
|
||||
int32 spellid;
|
||||
char corpse_name[64];
|
||||
uint32 action;
|
||||
/* 228 */
|
||||
@@ -3247,7 +3247,7 @@ struct SendAA_Struct {
|
||||
/*0040*/ uint32 prereq_skill; //is < 0, abs() is category #
|
||||
/*0044*/ uint32 prereq_minpoints; //min points in the prereq
|
||||
/*0048*/ uint32 type;
|
||||
/*0052*/ uint32 spellid;
|
||||
/*0052*/ int32 spellid;
|
||||
/*0056*/ uint32 spell_type;
|
||||
/*0060*/ uint32 spell_refresh;
|
||||
/*0064*/ uint32 classes;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,79 @@
|
||||
/* EQEmu: EQEmulator
|
||||
|
||||
Copyright (C) 2001-2026 EQEmu Development Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/struct_strategy.h"
|
||||
#include "common/patches/rof2.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
|
||||
namespace TOB {
|
||||
|
||||
extern void Register(EQStreamIdentifier& into);
|
||||
extern void Reload();
|
||||
|
||||
class Strategy : public StructStrategy
|
||||
{
|
||||
public:
|
||||
Strategy();
|
||||
|
||||
protected:
|
||||
virtual std::string Describe() const;
|
||||
virtual const EQ::versions::ClientVersion ClientVersion() const;
|
||||
|
||||
//magic macro to declare our opcode processors
|
||||
#include "ss_declare.h"
|
||||
#include "tob_ops.h"
|
||||
};
|
||||
|
||||
class MessageComponent : public Titanium::MessageComponent
|
||||
{
|
||||
public:
|
||||
MessageComponent() = default;
|
||||
~MessageComponent() override = default;
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
|
||||
const FormattedArgs& args) const override;
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpell(uint32_t message, uint32_t spawn_id,
|
||||
const char* spell_link) const override;
|
||||
std::unique_ptr<EQApplicationPacket> InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id,
|
||||
const char* name, const char* spell_link) const override;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] uint32_t ResolveID(uint32_t id) const override;
|
||||
void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const override;
|
||||
};
|
||||
|
||||
class BuffComponent : public UF::BuffComponent
|
||||
{
|
||||
public:
|
||||
BuffComponent(uint32_t maxLongBuffs, uint32_t maxShortBuffs) : UF::BuffComponent(maxLongBuffs, maxShortBuffs) {}
|
||||
BuffComponent() = delete;
|
||||
~BuffComponent() override = default;
|
||||
|
||||
std::unique_ptr<EQApplicationPacket>
|
||||
BuffDefinition(Mob* mob, const Buffs_Struct& buff, uint32_t slot, bool fade) const override;
|
||||
std::unique_ptr<EQApplicationPacket> RefreshBuffs(EmuOpcode opcode, Mob* mob, bool remove,
|
||||
bool buff_timers_suspended, const std::vector<uint32_t>& slots) const override;
|
||||
bool NeedsWearMessage() const override;
|
||||
void SetRefreshType(std::unique_ptr<EQApplicationPacket>& packet, uint8_t refresh_type) const override;
|
||||
};
|
||||
|
||||
}; /*TOB*/
|
||||
@@ -0,0 +1,265 @@
|
||||
#include "tob_limits.h"
|
||||
|
||||
#include "../strings.h"
|
||||
|
||||
|
||||
int16 TOB::invtype::GetInvTypeSize(int16 inv_type)
|
||||
{
|
||||
switch (inv_type) {
|
||||
case invtype::typePossessions:
|
||||
return invtype::POSSESSIONS_SIZE;
|
||||
case invtype::typeBank:
|
||||
return invtype::BANK_SIZE;
|
||||
case invtype::typeSharedBank:
|
||||
return invtype::SHARED_BANK_SIZE;
|
||||
case invtype::typeTrade:
|
||||
return invtype::TRADE_SIZE;
|
||||
case invtype::typeWorld:
|
||||
return invtype::WORLD_SIZE;
|
||||
case invtype::typeLimbo:
|
||||
return invtype::LIMBO_SIZE;
|
||||
case invtype::typeTribute:
|
||||
return invtype::TRIBUTE_SIZE;
|
||||
case invtype::typeTrophyTribute:
|
||||
return invtype::TROPHY_TRIBUTE_SIZE;
|
||||
case invtype::typeGuildTribute:
|
||||
return invtype::GUILD_TRIBUTE_SIZE;
|
||||
case invtype::typeMerchant:
|
||||
return invtype::MERCHANT_SIZE;
|
||||
case invtype::typeDeleted:
|
||||
return invtype::DELETED_SIZE;
|
||||
case invtype::typeCorpse:
|
||||
return invtype::CORPSE_SIZE;
|
||||
case invtype::typeBazaar:
|
||||
return invtype::BAZAAR_SIZE;
|
||||
case invtype::typeInspect:
|
||||
return invtype::INSPECT_SIZE;
|
||||
case invtype::typeRealEstate:
|
||||
return invtype::REAL_ESTATE_SIZE;
|
||||
case invtype::typeViewMODPC:
|
||||
return invtype::VIEW_MOD_PC_SIZE;
|
||||
case invtype::typeViewMODBank:
|
||||
return invtype::VIEW_MOD_BANK_SIZE;
|
||||
case invtype::typeViewMODSharedBank:
|
||||
return invtype::VIEW_MOD_SHARED_BANK_SIZE;
|
||||
case invtype::typeViewMODLimbo:
|
||||
return invtype::VIEW_MOD_LIMBO_SIZE;
|
||||
case invtype::typeAltStorage:
|
||||
return invtype::ALT_STORAGE_SIZE;
|
||||
case invtype::typeArchived:
|
||||
return invtype::ARCHIVED_SIZE;
|
||||
case invtype::typeMail:
|
||||
return invtype::MAIL_SIZE;
|
||||
case invtype::typeGuildTrophyTribute:
|
||||
return invtype::GUILD_TROPHY_TRIBUTE_SIZE;
|
||||
case invtype::typeKrono:
|
||||
return invtype::KRONO_SIZE;
|
||||
case invtype::typeOther:
|
||||
return invtype::OTHER_SIZE;
|
||||
default:
|
||||
return INULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char* TOB::invtype::GetInvTypeName(int16 inv_type)
|
||||
{
|
||||
switch (inv_type) {
|
||||
case invtype::TYPE_INVALID:
|
||||
return "Invalid Type";
|
||||
case invtype::typePossessions:
|
||||
return "Possessions";
|
||||
case invtype::typeBank:
|
||||
return "Bank";
|
||||
case invtype::typeSharedBank:
|
||||
return "Shared Bank";
|
||||
case invtype::typeTrade:
|
||||
return "Trade";
|
||||
case invtype::typeWorld:
|
||||
return "World";
|
||||
case invtype::typeLimbo:
|
||||
return "Limbo";
|
||||
case invtype::typeTribute:
|
||||
return "Tribute";
|
||||
case invtype::typeTrophyTribute:
|
||||
return "Trophy Tribute";
|
||||
case invtype::typeGuildTribute:
|
||||
return "Guild Tribute";
|
||||
case invtype::typeMerchant:
|
||||
return "Merchant";
|
||||
case invtype::typeDeleted:
|
||||
return "Deleted";
|
||||
case invtype::typeCorpse:
|
||||
return "Corpse";
|
||||
case invtype::typeBazaar:
|
||||
return "Bazaar";
|
||||
case invtype::typeInspect:
|
||||
return "Inspect";
|
||||
case invtype::typeRealEstate:
|
||||
return "Real Estate";
|
||||
case invtype::typeViewMODPC:
|
||||
return "View MOD PC";
|
||||
case invtype::typeViewMODBank:
|
||||
return "View MOD Bank";
|
||||
case invtype::typeViewMODSharedBank:
|
||||
return "View MOD Shared Bank";
|
||||
case invtype::typeViewMODLimbo:
|
||||
return "View MOD Limbo";
|
||||
case invtype::typeAltStorage:
|
||||
return "Alt Storage";
|
||||
case invtype::typeArchived:
|
||||
return "Archived";
|
||||
case invtype::typeMail:
|
||||
return "Mail";
|
||||
case invtype::typeGuildTrophyTribute:
|
||||
return "Guild Trophy Tribute";
|
||||
case invtype::typeKrono:
|
||||
return "Krono";
|
||||
case invtype::typeOther:
|
||||
return "Other";
|
||||
default:
|
||||
return "Unknown Type";
|
||||
}
|
||||
}
|
||||
|
||||
bool TOB::invtype::IsInvTypePersistent(int16 inv_type)
|
||||
{
|
||||
switch (inv_type) {
|
||||
case invtype::typePossessions:
|
||||
case invtype::typeBank:
|
||||
case invtype::typeSharedBank:
|
||||
case invtype::typeTrade:
|
||||
case invtype::typeWorld:
|
||||
case invtype::typeLimbo:
|
||||
case invtype::typeTribute:
|
||||
case invtype::typeTrophyTribute:
|
||||
case invtype::typeGuildTribute:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char* TOB::invslot::GetInvPossessionsSlotName(int16 inv_slot)
|
||||
{
|
||||
switch (inv_slot) {
|
||||
case invslot::SLOT_INVALID:
|
||||
return "Invalid Slot";
|
||||
case invslot::slotCharm:
|
||||
return "Charm";
|
||||
case invslot::slotEar1:
|
||||
return "Ear 1";
|
||||
case invslot::slotHead:
|
||||
return "Head";
|
||||
case invslot::slotFace:
|
||||
return "Face";
|
||||
case invslot::slotEar2:
|
||||
return "Ear 2";
|
||||
case invslot::slotNeck:
|
||||
return "Neck";
|
||||
case invslot::slotShoulders:
|
||||
return "Shoulders";
|
||||
case invslot::slotArms:
|
||||
return "Arms";
|
||||
case invslot::slotBack:
|
||||
return "Back";
|
||||
case invslot::slotWrist1:
|
||||
return "Wrist 1";
|
||||
case invslot::slotWrist2:
|
||||
return "Wrist 2";
|
||||
case invslot::slotRange:
|
||||
return "Range";
|
||||
case invslot::slotHands:
|
||||
return "Hands";
|
||||
case invslot::slotPrimary:
|
||||
return "Primary";
|
||||
case invslot::slotSecondary:
|
||||
return "Secondary";
|
||||
case invslot::slotFinger1:
|
||||
return "Finger 1";
|
||||
case invslot::slotFinger2:
|
||||
return "Finger 2";
|
||||
case invslot::slotChest:
|
||||
return "Chest";
|
||||
case invslot::slotLegs:
|
||||
return "Legs";
|
||||
case invslot::slotFeet:
|
||||
return "Feet";
|
||||
case invslot::slotWaist:
|
||||
return "Waist";
|
||||
case invslot::slotPowerSource:
|
||||
return "Power Source";
|
||||
case invslot::slotAmmo:
|
||||
return "Ammo";
|
||||
case invslot::slotGeneral1:
|
||||
return "General 1";
|
||||
case invslot::slotGeneral2:
|
||||
return "General 2";
|
||||
case invslot::slotGeneral3:
|
||||
return "General 3";
|
||||
case invslot::slotGeneral4:
|
||||
return "General 4";
|
||||
case invslot::slotGeneral5:
|
||||
return "General 5";
|
||||
case invslot::slotGeneral6:
|
||||
return "General 6";
|
||||
case invslot::slotGeneral7:
|
||||
return "General 7";
|
||||
case invslot::slotGeneral8:
|
||||
return "General 8";
|
||||
case invslot::slotGeneral9:
|
||||
return "General 9";
|
||||
case invslot::slotGeneral10:
|
||||
return "General 10";
|
||||
case invslot::slotCursor:
|
||||
return "Cursor";
|
||||
default:
|
||||
return "Unknown Slot";
|
||||
}
|
||||
}
|
||||
|
||||
const char* TOB::invslot::GetInvSlotName(int16 inv_type, int16 inv_slot)
|
||||
{
|
||||
if (inv_type == invtype::typePossessions)
|
||||
return invslot::GetInvPossessionsSlotName(inv_slot);
|
||||
|
||||
int16 type_size = invtype::GetInvTypeSize(inv_type);
|
||||
|
||||
if (!type_size || inv_slot == invslot::SLOT_INVALID)
|
||||
return "Invalid Slot";
|
||||
|
||||
if ((inv_slot + 1) >= type_size)
|
||||
return "Unknown Slot";
|
||||
|
||||
static std::string ret_str;
|
||||
ret_str = StringFormat("Slot %i", (inv_slot + 1));
|
||||
|
||||
return ret_str.c_str();
|
||||
}
|
||||
|
||||
const char* TOB::invbag::GetInvBagIndexName(int16 bag_index)
|
||||
{
|
||||
if (bag_index == invbag::SLOT_INVALID)
|
||||
return "Invalid Bag";
|
||||
|
||||
if (bag_index >= invbag::SLOT_COUNT)
|
||||
return "Unknown Bag";
|
||||
|
||||
static std::string ret_str;
|
||||
ret_str = StringFormat("Bag %i", (bag_index + 1));
|
||||
|
||||
return ret_str.c_str();
|
||||
}
|
||||
|
||||
const char* TOB::invaug::GetInvAugIndexName(int16 aug_index)
|
||||
{
|
||||
if (aug_index == invaug::SOCKET_INVALID)
|
||||
return "Invalid Augment";
|
||||
|
||||
if (aug_index >= invaug::SOCKET_COUNT)
|
||||
return "Unknown Augment";
|
||||
|
||||
static std::string ret_str;
|
||||
ret_str = StringFormat("Augment %i", (aug_index + 1));
|
||||
|
||||
return ret_str.c_str();
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
#ifndef COMMON_LAURION_LIMITS_H
|
||||
#define COMMON_LAURION_LIMITS_H
|
||||
|
||||
#include "common/types.h"
|
||||
#include "common/emu_versions.h"
|
||||
#include "common/skills.h"
|
||||
|
||||
namespace TOB
|
||||
{
|
||||
const int16 IINVALID = -1;
|
||||
const int16 INULL = 0;
|
||||
|
||||
namespace inventory {
|
||||
inline EQ::versions::ClientVersion GetInventoryRef() { return EQ::versions::ClientVersion::TOB; }
|
||||
|
||||
const bool ConcatenateInvTypeLimbo = false;
|
||||
|
||||
const bool AllowOverLevelEquipment = true;
|
||||
|
||||
const bool AllowEmptyBagInBag = true;
|
||||
const bool AllowClickCastFromBag = true;
|
||||
|
||||
} /*inventory*/
|
||||
|
||||
namespace invtype {
|
||||
inline EQ::versions::ClientVersion GetInvTypeRef() { return EQ::versions::ClientVersion::TOB; }
|
||||
|
||||
namespace enum_ {
|
||||
enum InventoryTypes : int16 {
|
||||
typePossessions = INULL,
|
||||
typeBank,
|
||||
typeSharedBank,
|
||||
typeTrade,
|
||||
typeWorld,
|
||||
typeLimbo,
|
||||
typeTribute,
|
||||
typeTrophyTribute,
|
||||
typeGuildTribute,
|
||||
typeMerchant,
|
||||
typeDeleted,
|
||||
typeCorpse,
|
||||
typeBazaar,
|
||||
typeInspect,
|
||||
typeRealEstate,
|
||||
typeViewMODPC,
|
||||
typeViewMODBank,
|
||||
typeViewMODSharedBank,
|
||||
typeViewMODLimbo,
|
||||
typeAltStorage,
|
||||
typeArchived,
|
||||
typeMail,
|
||||
typeGuildTrophyTribute,
|
||||
typeKrono,
|
||||
typeOther,
|
||||
typeMercenaryItems,
|
||||
typeViewModMercenaryItems,
|
||||
typeMountKeyRingItems,
|
||||
typeViewModMountKeyRingItems,
|
||||
typeIllusionKeyRingItems,
|
||||
typeViewModIllusionKeyRingItems,
|
||||
typeFamiliarKeyRingItems,
|
||||
typeViewModFamiliarKeyRingItems,
|
||||
typeHeroForgeKeyRingItems,
|
||||
typeViewModHeroForgeKeyRingItems,
|
||||
typeTeleportationKeyRingItems,
|
||||
typeViewModTeleportationKeyRingItems,
|
||||
typeOverflow,
|
||||
typeDragonHoard,
|
||||
typeTradeskillDepot,
|
||||
typeGuildTradeskillDepot
|
||||
};
|
||||
|
||||
} // namespace enum_
|
||||
using namespace enum_;
|
||||
|
||||
const int16 POSSESSIONS_SIZE = 34;
|
||||
const int16 BANK_SIZE = 24;
|
||||
const int16 SHARED_BANK_SIZE = 2;
|
||||
const int16 TRADE_SIZE = 8;
|
||||
const int16 WORLD_SIZE = 10;
|
||||
const int16 LIMBO_SIZE = 36;
|
||||
const int16 TRIBUTE_SIZE = 5;
|
||||
const int16 TROPHY_TRIBUTE_SIZE = 0;//unknown
|
||||
const int16 GUILD_TRIBUTE_SIZE = 2;//unverified
|
||||
const int16 MERCHANT_SIZE = 200;
|
||||
const int16 DELETED_SIZE = 0;//unknown - "Recovery Tab"
|
||||
const int16 CORPSE_SIZE = POSSESSIONS_SIZE;
|
||||
const int16 BAZAAR_SIZE = 200;
|
||||
const int16 INSPECT_SIZE = 23;
|
||||
const int16 REAL_ESTATE_SIZE = 0;//unknown
|
||||
const int16 VIEW_MOD_PC_SIZE = POSSESSIONS_SIZE;
|
||||
const int16 VIEW_MOD_BANK_SIZE = BANK_SIZE;
|
||||
const int16 VIEW_MOD_SHARED_BANK_SIZE = SHARED_BANK_SIZE;
|
||||
const int16 VIEW_MOD_LIMBO_SIZE = LIMBO_SIZE;
|
||||
const int16 ALT_STORAGE_SIZE = 0;//unknown - "Shroud Bank"
|
||||
const int16 ARCHIVED_SIZE = 0;//unknown
|
||||
const int16 MAIL_SIZE = 0;//unknown
|
||||
const int16 GUILD_TROPHY_TRIBUTE_SIZE = 0;//unknown
|
||||
const int16 KRONO_SIZE = 0;//unknown
|
||||
const int16 OTHER_SIZE = 0;//unknown
|
||||
|
||||
const int16 TRADE_NPC_SIZE = 4; // defined by implication
|
||||
|
||||
const int16 TYPE_INVALID = IINVALID;
|
||||
const int16 TYPE_BEGIN = typePossessions;
|
||||
const int16 TYPE_END = typeOther;
|
||||
const int16 TYPE_COUNT = (TYPE_END - TYPE_BEGIN) + 1;
|
||||
|
||||
int16 GetInvTypeSize(int16 inv_type);
|
||||
const char* GetInvTypeName(int16 inv_type);
|
||||
|
||||
bool IsInvTypePersistent(int16 inv_type);
|
||||
|
||||
} /*invtype*/
|
||||
|
||||
namespace invslot {
|
||||
inline EQ::versions::ClientVersion GetInvSlotRef() { return EQ::versions::ClientVersion::TOB; }
|
||||
|
||||
namespace enum_ {
|
||||
enum InventorySlots : int16 {
|
||||
slotCharm = INULL,
|
||||
slotEar1,
|
||||
slotHead,
|
||||
slotFace,
|
||||
slotEar2,
|
||||
slotNeck,
|
||||
slotShoulders,
|
||||
slotArms,
|
||||
slotBack,
|
||||
slotWrist1,
|
||||
slotWrist2,
|
||||
slotRange,
|
||||
slotHands,
|
||||
slotPrimary,
|
||||
slotSecondary,
|
||||
slotFinger1,
|
||||
slotFinger2,
|
||||
slotChest,
|
||||
slotLegs,
|
||||
slotFeet,
|
||||
slotWaist,
|
||||
slotPowerSource,
|
||||
slotAmmo,
|
||||
slotGeneral1,
|
||||
slotGeneral2,
|
||||
slotGeneral3,
|
||||
slotGeneral4,
|
||||
slotGeneral5,
|
||||
slotGeneral6,
|
||||
slotGeneral7,
|
||||
slotGeneral8,
|
||||
slotGeneral9,
|
||||
slotGeneral10,
|
||||
slotGeneral11,
|
||||
slotGeneral12,
|
||||
slotCursor
|
||||
};
|
||||
|
||||
constexpr int16 format_as(InventorySlots slot) { return static_cast<int16>(slot); }
|
||||
} // namespace enum_
|
||||
using namespace enum_;
|
||||
|
||||
const int16 SLOT_INVALID = IINVALID;
|
||||
const int16 SLOT_BEGIN = INULL;
|
||||
|
||||
const int16 POSSESSIONS_BEGIN = slotCharm;
|
||||
const int16 POSSESSIONS_END = slotCursor;
|
||||
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
|
||||
|
||||
const int16 EQUIPMENT_BEGIN = slotCharm;
|
||||
const int16 EQUIPMENT_END = slotAmmo;
|
||||
const int16 EQUIPMENT_COUNT = (EQUIPMENT_END - EQUIPMENT_BEGIN) + 1;
|
||||
|
||||
//We support more if enabled but for now lets leave it at the 10 slots
|
||||
const int16 GENERAL_BEGIN = slotGeneral1;
|
||||
const int16 GENERAL_END = slotGeneral10;
|
||||
const int16 GENERAL_COUNT = (GENERAL_END - GENERAL_BEGIN) + 1;
|
||||
|
||||
const int16 BONUS_BEGIN = invslot::slotCharm;
|
||||
const int16 BONUS_STAT_END = invslot::slotPowerSource;
|
||||
const int16 BONUS_SKILL_END = invslot::slotAmmo;
|
||||
|
||||
const int16 CORPSE_BEGIN = invslot::slotGeneral1;
|
||||
const int16 CORPSE_END = invslot::slotGeneral1 + invslot::slotCursor;
|
||||
|
||||
const uint64 EQUIPMENT_BITMASK = 0x00000000007FFFFF;
|
||||
const uint64 GENERAL_BITMASK = 0x00000007FF800000;
|
||||
const uint64 CURSOR_BITMASK = 0x0000000800000000;
|
||||
const uint64 POSSESSIONS_BITMASK = (EQUIPMENT_BITMASK | GENERAL_BITMASK | CURSOR_BITMASK); // based on 36-slot count (TOB+)
|
||||
const uint64 CORPSE_BITMASK = (GENERAL_BITMASK | CURSOR_BITMASK | (EQUIPMENT_BITMASK << 36)); // based on 36-slot count (TOB+)
|
||||
|
||||
|
||||
const char* GetInvPossessionsSlotName(int16 inv_slot);
|
||||
const char* GetInvSlotName(int16 inv_type, int16 inv_slot);
|
||||
|
||||
} /*invslot*/
|
||||
|
||||
namespace invbag {
|
||||
inline EQ::versions::ClientVersion GetInvBagRef() { return EQ::versions::ClientVersion::TOB; }
|
||||
|
||||
const int16 SLOT_INVALID = IINVALID;
|
||||
const int16 SLOT_BEGIN = INULL;
|
||||
const int16 SLOT_END = 199;
|
||||
const int16 SLOT_COUNT = 200; // server Size will be 200..unsure what actual client is (test)
|
||||
|
||||
const char* GetInvBagIndexName(int16 bag_index);
|
||||
|
||||
} /*invbag*/
|
||||
|
||||
namespace invaug {
|
||||
inline EQ::versions::ClientVersion GetInvAugRef() { return EQ::versions::ClientVersion::TOB; }
|
||||
|
||||
const int16 SOCKET_INVALID = IINVALID;
|
||||
const int16 SOCKET_BEGIN = INULL;
|
||||
const int16 SOCKET_END = 5;
|
||||
const int16 SOCKET_COUNT = 6;
|
||||
|
||||
const char* GetInvAugIndexName(int16 aug_index);
|
||||
|
||||
} /*invaug*/
|
||||
|
||||
namespace item {
|
||||
inline EQ::versions::ClientVersion GetItemRef() { return EQ::versions::ClientVersion::TOB; }
|
||||
|
||||
//enum Unknown : int { // looks like item class..but, RoF has it too - nothing in UF-
|
||||
// Unknown1 = 0,
|
||||
// Unknown2 = 1,
|
||||
// Unknown3 = 2,
|
||||
// Unknown4 = 5 // krono?
|
||||
//};
|
||||
|
||||
enum ItemPacketType : int {
|
||||
ItemPacketMerchant = 0x64,
|
||||
ItemPacketTradeView = 0x65,
|
||||
ItemPacketLoot = 0x66,
|
||||
ItemPacketTrade = 0x67,
|
||||
//looks like they added something at 0x68 that didn't exist before and shifted everything after it up by 1
|
||||
ItemPacketUnknown068 = 0x68, //Not sure but it seems to deal with the cursor somehow.
|
||||
ItemPacketCharInventory = 0x6A, //Rof 0x69 -> Larion 0x6a (requires translation)
|
||||
ItemPacketLimbo = 0x6B, //0x6A -> 0x6B
|
||||
ItemPacketWorldContainer = 0x6C,
|
||||
ItemPacketTributeItem = 0x6D,
|
||||
ItemPacketGuildTribute = 0x6E,
|
||||
ItemPacketCharmUpdate = 0x6f,
|
||||
ItemPacketRecovery = 0x72,
|
||||
ItemPacketParcel = 0x74,
|
||||
ItemPacketUnknown075 = 0x75, //Not sure but uses a lot of the same logic as the trade and char inventory types
|
||||
ItemPacketOverflow = 0x76,
|
||||
ItemPacketDragonHoard = 0x77,
|
||||
ItemPacketTradeskill = 0x78,
|
||||
ItemPacketTradeskillDepot = 0x79,
|
||||
ItemPacketInvalid = 0xFF
|
||||
};
|
||||
|
||||
} /*item*/
|
||||
|
||||
namespace profile {
|
||||
inline EQ::versions::ClientVersion GetProfileRef() { return EQ::versions::ClientVersion::TOB; }
|
||||
|
||||
const int16 BANDOLIERS_SIZE = 20; // number of bandolier instances
|
||||
const int16 BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
|
||||
|
||||
const int16 POTION_BELT_SIZE = 5;
|
||||
|
||||
const int16 SKILL_ARRAY_SIZE = 100;
|
||||
|
||||
} /*profile*/
|
||||
|
||||
namespace constants {
|
||||
inline EQ::versions::ClientVersion GetConstantsRef() { return EQ::versions::ClientVersion::TOB; }
|
||||
|
||||
const EQ::expansions::Expansion EXPANSION = EQ::expansions::Expansion::LS;
|
||||
const uint32 EXPANSION_BIT = EQ::expansions::bitLS;
|
||||
const uint32 EXPANSIONS_MASK = EQ::expansions::maskLS;
|
||||
|
||||
const size_t CHARACTER_CREATION_LIMIT = 12;
|
||||
|
||||
const size_t SAY_LINK_BODY_SIZE = 56;
|
||||
const uint32 MAX_GUILD_ID = 50000;
|
||||
const uint32 MAX_BAZAAR_TRADERS = 600;
|
||||
|
||||
} /*constants*/
|
||||
|
||||
namespace behavior {
|
||||
inline EQ::versions::ClientVersion GetBehaviorRef() { return EQ::versions::ClientVersion::TOB; }
|
||||
|
||||
const bool CoinHasWeight = false;
|
||||
|
||||
} /*behavior*/
|
||||
|
||||
namespace skills {
|
||||
inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::TOB; }
|
||||
|
||||
const size_t LastUsableSkill = EQ::skills::Skill2HPiercing;
|
||||
|
||||
} /*skills*/
|
||||
|
||||
namespace spells {
|
||||
inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::TOB; }
|
||||
|
||||
enum class CastingSlot : uint32 {
|
||||
Gem1 = 0,
|
||||
Gem2 = 1,
|
||||
Gem3 = 2,
|
||||
Gem4 = 3,
|
||||
Gem5 = 4,
|
||||
Gem6 = 5,
|
||||
Gem7 = 6,
|
||||
Gem8 = 7,
|
||||
Gem9 = 8,
|
||||
Gem10 = 9,
|
||||
Gem11 = 10,
|
||||
Gem12 = 11,
|
||||
MaxGems = 18, // fallacy..only 12 slot are useable...
|
||||
Item = 12,
|
||||
Discipline = 13,
|
||||
AltAbility = 0xFF
|
||||
};
|
||||
|
||||
const int SPELL_ID_MAX = 71999;
|
||||
const int SPELLBOOK_SIZE = 1120;
|
||||
const int SPELL_GEM_COUNT = static_cast<uint32>(CastingSlot::MaxGems);
|
||||
const int SPELL_GEM_RECAST_TIMER = 15;
|
||||
|
||||
const int LONG_BUFFS = 42;
|
||||
const int SHORT_BUFFS = 30;
|
||||
const int DISC_BUFFS = 1;
|
||||
const int TOTAL_BUFFS = LONG_BUFFS + SHORT_BUFFS + DISC_BUFFS;
|
||||
const int NPC_BUFFS = 400;
|
||||
const int PET_BUFFS = NPC_BUFFS;
|
||||
const int MERC_BUFFS = LONG_BUFFS;
|
||||
|
||||
} /*spells*/
|
||||
|
||||
}; /* TOB */
|
||||
|
||||
#endif /*COMMON_LAURION_LIMITS_H*/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user