mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-23 04:52:29 +00:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 743fd45b17 | |||
| 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,6 +3,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- tob_patch
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
||||
+4
-4
@@ -20,7 +20,7 @@ endif()
|
||||
|
||||
project(EQEmu
|
||||
VERSION 24.10.3
|
||||
LANGUAGES CXX
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
@@ -42,10 +42,10 @@ option(EQEMU_BUILD_PCH "Build with precompiled headers (Windows)" ON)
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(/bigobj)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS NOMINMAX CRASH_LOGGING _HAS_AUTO_PTR_ETC)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS NOMINMAX WIN32_LEAN_AND_MEAN CRASH_LOGGING _HAS_AUTO_PTR_ETC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
|
||||
option(EQEMU_DISABLE_MSVC_WARNINGS "Disable MSVC compile warnings." ON)
|
||||
option(EQEMU_DISABLE_MSVC_WARNINGS "Disable MSVC compile warnings." OFF)
|
||||
if(EQEMU_DISABLE_MSVC_WARNINGS)
|
||||
add_compile_options(/W0 /wd4005 /wd4996 /nologo /Os)
|
||||
endif()
|
||||
@@ -71,7 +71,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)
|
||||
|
||||
+77
-81
@@ -7,20 +7,20 @@ set(common_sources
|
||||
classes.cpp
|
||||
cli/eqemu_command_handler.cpp
|
||||
compression.cpp
|
||||
condition.cpp
|
||||
content/world_content_service.cpp
|
||||
crash.cpp
|
||||
crc16.cpp
|
||||
crc32.cpp
|
||||
data_bucket.cpp
|
||||
data_bucket.cpp
|
||||
database_instances.cpp
|
||||
database.cpp
|
||||
database/database_dump_service.cpp
|
||||
database/database_update.cpp
|
||||
database_instances.cpp
|
||||
dbcore.cpp
|
||||
deity.cpp
|
||||
discord/discord.cpp
|
||||
discord/discord_manager.cpp
|
||||
discord/discord.cpp
|
||||
dynamic_zone_base.cpp
|
||||
dynamic_zone_lockout.cpp
|
||||
emu_constants.cpp
|
||||
@@ -31,13 +31,16 @@ set(common_sources
|
||||
eq_packet.cpp
|
||||
eq_stream_ident.cpp
|
||||
eq_stream_proxy.cpp
|
||||
eqdb.cpp
|
||||
eqdb_res.cpp
|
||||
eqdb.cpp
|
||||
eqemu_config.cpp
|
||||
eqemu_exception.cpp
|
||||
eqemu_logsys.cpp
|
||||
eqtime.cpp
|
||||
event_sub.cpp
|
||||
event/event_loop.cpp
|
||||
event/task_scheduler.cpp
|
||||
event/timer.cpp
|
||||
events/player_event_discord_formatter.cpp
|
||||
events/player_event_logs.cpp
|
||||
evolving_items.cpp
|
||||
@@ -51,62 +54,59 @@ set(common_sources
|
||||
ipc_mutex.cpp
|
||||
item_data.cpp
|
||||
item_instance.cpp
|
||||
json/json.hpp
|
||||
json/jsoncpp.cpp
|
||||
json_config.cpp
|
||||
json/jsoncpp.cpp
|
||||
light_source.cpp
|
||||
md5.cpp
|
||||
memory/ksm.hpp
|
||||
memory_buffer.cpp
|
||||
memory_mapped_file.cpp
|
||||
misc.cpp
|
||||
memory/ksm.cpp
|
||||
misc_functions.cpp
|
||||
mutex.cpp
|
||||
misc.cpp
|
||||
mysql_request_result.cpp
|
||||
mysql_request_row.cpp
|
||||
mysql_stmt.cpp
|
||||
net/console_server.cpp
|
||||
net/console_server_connection.cpp
|
||||
net/console_server.cpp
|
||||
net/crc32.cpp
|
||||
net/dns.cpp
|
||||
net/eqstream.cpp
|
||||
net/packet.cpp
|
||||
net/reliable_stream_connection.cpp
|
||||
net/servertalk_client_connection.cpp
|
||||
net/servertalk_legacy_client_connection.cpp
|
||||
net/servertalk_server.cpp
|
||||
net/servertalk_server_connection.cpp
|
||||
net/servertalk_server.cpp
|
||||
net/tcp_connection.cpp
|
||||
net/tcp_server.cpp
|
||||
net/websocket_server.cpp
|
||||
net/websocket_server_connection.cpp
|
||||
net/websocket_server.cpp
|
||||
opcode_map.cpp
|
||||
opcodemgr.cpp
|
||||
packet_dump.cpp
|
||||
packet_dump_file.cpp
|
||||
packet_dump.cpp
|
||||
packet_functions.cpp
|
||||
patches/client_version.cpp
|
||||
patches/patches.cpp
|
||||
patches/rof.cpp
|
||||
patches/rof2.cpp
|
||||
patches/rof2_limits.cpp
|
||||
patches/rof_limits.cpp
|
||||
patches/sod.cpp
|
||||
patches/rof.cpp
|
||||
patches/rof2_limits.cpp
|
||||
patches/rof2.cpp
|
||||
patches/sod_limits.cpp
|
||||
patches/sof.cpp
|
||||
patches/sod.cpp
|
||||
patches/sof_limits.cpp
|
||||
patches/titanium.cpp
|
||||
patches/sof.cpp
|
||||
patches/titanium_limits.cpp
|
||||
patches/uf.cpp
|
||||
patches/titanium.cpp
|
||||
patches/tob.cpp
|
||||
patches/tob_limits.cpp
|
||||
patches/uf_limits.cpp
|
||||
patches/uf.cpp
|
||||
path_manager.cpp
|
||||
path_manager.cpp
|
||||
perl_eqdb.cpp
|
||||
perl_eqdb_res.cpp
|
||||
perl_eqdb.cpp
|
||||
platform.cpp
|
||||
platform/inet.h
|
||||
platform/platform.h
|
||||
platform/posix/include_inet.h
|
||||
platform/posix/include_pthreads.h
|
||||
platform/win/include_windows.h
|
||||
platform/win/include_winsock2.h
|
||||
proc_launcher.cpp
|
||||
process.cpp
|
||||
process/process.cpp
|
||||
@@ -123,12 +123,12 @@ set(common_sources
|
||||
shareddb.cpp
|
||||
skill_caps.cpp
|
||||
skills.cpp
|
||||
spdat.cpp
|
||||
spdat_bot.cpp
|
||||
spdat.cpp
|
||||
StackWalker/StackWalker.cpp
|
||||
strings.cpp
|
||||
strings_legacy.cpp
|
||||
strings_misc.cpp
|
||||
strings.cpp
|
||||
struct_strategy.cpp
|
||||
textures.cpp
|
||||
timer.cpp
|
||||
@@ -136,6 +136,7 @@ set(common_sources
|
||||
util/directory.cpp
|
||||
util/uuid.cpp
|
||||
zone_store.cpp
|
||||
links.cpp
|
||||
)
|
||||
|
||||
set(repositories
|
||||
@@ -538,35 +539,32 @@ set(repositories
|
||||
)
|
||||
|
||||
set(common_headers
|
||||
StackWalker/StackWalker.h
|
||||
additive_lagged_fibonacci_engine.h
|
||||
base_packet.h
|
||||
bazaar.h
|
||||
bodytypes.h
|
||||
classes.h
|
||||
cli/argh.h
|
||||
cli/eqemu_command_handler.h
|
||||
cli/terminal_color.hpp
|
||||
classes.h
|
||||
compression.h
|
||||
condition.h
|
||||
content/world_content_service.h
|
||||
crash.h
|
||||
crc16.h
|
||||
crc32.h
|
||||
cron/croncpp.h
|
||||
data_bucket.cpp
|
||||
data_verification.h
|
||||
database_schema.h
|
||||
database.h
|
||||
database/database_dump_service.h
|
||||
database/database_update.h
|
||||
database/database_update_manifest.h
|
||||
database/database_update_manifest_bots.h
|
||||
database/database_update_manifest_custom.h
|
||||
database_schema.h
|
||||
database/database_update_manifest.h
|
||||
database/database_update.h
|
||||
dbcore.h
|
||||
deity.h
|
||||
discord/discord.h
|
||||
discord/discord_manager.h
|
||||
discord/discord.h
|
||||
dynamic_zone_base.h
|
||||
dynamic_zone_lockout.h
|
||||
emu_constants.h
|
||||
@@ -576,24 +574,24 @@ set(common_headers
|
||||
emu_versions.h
|
||||
eq_constants.h
|
||||
eq_limits.h
|
||||
eq_packet.h
|
||||
eq_packet_structs.h
|
||||
eq_packet.h
|
||||
eq_stream_ident.h
|
||||
eq_stream_intf.h
|
||||
eq_stream_locator.h
|
||||
eq_stream_proxy.h
|
||||
eqdb.h
|
||||
eqdb_res.h
|
||||
eqemu_config.h
|
||||
eqdb.h
|
||||
eqemu_config_elements.h
|
||||
eqemu_config.h
|
||||
eqemu_exception.h
|
||||
eqemu_logsys.h
|
||||
eqemu_logsys_log_aliases.h
|
||||
eqemu_logsys.h
|
||||
eqtime.h
|
||||
event_sub.h
|
||||
event/event_loop.h
|
||||
event/task.h
|
||||
event/timer.h
|
||||
event_sub.h
|
||||
events/player_event_discord_formatter.h
|
||||
events/player_event_logs.h
|
||||
events/player_events.h
|
||||
@@ -614,10 +612,11 @@ set(common_headers
|
||||
ipc_mutex.h
|
||||
item_data.h
|
||||
item_instance.h
|
||||
json_config.h
|
||||
json/json_archive_single_line.h
|
||||
json/json-forwards.h
|
||||
json/json.h
|
||||
json/json_archive_single_line.h
|
||||
json_config.h
|
||||
json/json.hpp
|
||||
light_source.h
|
||||
linked_list.h
|
||||
loot.h
|
||||
@@ -625,14 +624,14 @@ set(common_headers
|
||||
md5.h
|
||||
memory_buffer.h
|
||||
memory_mapped_file.h
|
||||
misc.h
|
||||
memory/ksm.h
|
||||
misc_functions.h
|
||||
mutex.h
|
||||
misc.h
|
||||
mysql_request_result.h
|
||||
mysql_request_row.h
|
||||
mysql_stmt.h
|
||||
net/console_server.h
|
||||
net/console_server_connection.h
|
||||
net/console_server.h
|
||||
net/crc32.h
|
||||
net/dns.h
|
||||
net/endian.h
|
||||
@@ -644,49 +643,60 @@ set(common_headers
|
||||
net/servertalk_client_connection.h
|
||||
net/servertalk_common.h
|
||||
net/servertalk_legacy_client_connection.h
|
||||
net/servertalk_server.h
|
||||
net/servertalk_server_connection.h
|
||||
net/tcp_connection.h
|
||||
net/servertalk_server.h
|
||||
net/tcp_connection_pooling.h
|
||||
net/tcp_connection.h
|
||||
net/tcp_server.h
|
||||
net/websocket_server.h
|
||||
net/websocket_server_connection.h
|
||||
net/websocket_server.h
|
||||
op_codes.h
|
||||
opcode_dispatch.h
|
||||
opcodemgr.h
|
||||
packet_dump.h
|
||||
packet_dump_file.h
|
||||
packet_dump.h
|
||||
packet_functions.h
|
||||
patches/IMessage.h
|
||||
patches/client_version.h
|
||||
patches/patches.h
|
||||
patches/rof.h
|
||||
patches/rof2.h
|
||||
patches/rof2_limits.h
|
||||
patches/rof2_ops.h
|
||||
patches/rof2_structs.h
|
||||
patches/rof_limits.h
|
||||
patches/rof_ops.h
|
||||
patches/rof_structs.h
|
||||
patches/sod.h
|
||||
patches/rof.h
|
||||
patches/rof2_limits.h
|
||||
patches/rof2_ops.h
|
||||
patches/rof2_structs.h
|
||||
patches/rof2.h
|
||||
patches/sod_limits.h
|
||||
patches/sod_ops.h
|
||||
patches/sod_structs.h
|
||||
patches/sof.h
|
||||
patches/sod.h
|
||||
patches/sof_limits.h
|
||||
patches/sof_ops.h
|
||||
patches/sof_structs.h
|
||||
patches/sof.h
|
||||
patches/ss_declare.h
|
||||
patches/ss_define.h
|
||||
patches/ss_register.h
|
||||
patches/titanium.h
|
||||
patches/titanium_limits.h
|
||||
patches/titanium_ops.h
|
||||
patches/titanium_structs.h
|
||||
patches/uf.h
|
||||
patches/titanium.h
|
||||
patches/tob.h
|
||||
patches/tob_limits.h
|
||||
patches/tob_ops.h
|
||||
patches/tob_structs.h
|
||||
patches/uf_limits.h
|
||||
patches/uf_ops.h
|
||||
patches/uf_structs.h
|
||||
path_manager.cpp
|
||||
patches/uf.h
|
||||
platform.h
|
||||
platform/inet.h
|
||||
platform/platform.h
|
||||
platform/posix/include_inet.h
|
||||
platform/posix/include_pthreads.h
|
||||
platform/win/include_windows.h
|
||||
platform/win/include_winsock2.h
|
||||
proc_launcher.h
|
||||
process.h
|
||||
process/process.h
|
||||
@@ -713,6 +723,7 @@ set(common_headers
|
||||
skills.h
|
||||
spdat.h
|
||||
stacktrace/backward.hpp
|
||||
StackWalker/StackWalker.h
|
||||
strings.h
|
||||
struct_strategy.h
|
||||
tasks.h
|
||||
@@ -726,28 +737,13 @@ set(common_headers
|
||||
util/memory_stream.h
|
||||
util/uuid.h
|
||||
version.h
|
||||
zone_store.h
|
||||
zone_store.h
|
||||
links.h
|
||||
)
|
||||
|
||||
# Source Groups (Regex based for automatic subdirectory handling)
|
||||
source_group("CLI" REGULAR_EXPRESSION "^cli/")
|
||||
source_group("Content" REGULAR_EXPRESSION "^content/")
|
||||
source_group("Cron" REGULAR_EXPRESSION "^cron/")
|
||||
source_group("Database" REGULAR_EXPRESSION "^database/")
|
||||
source_group("Discord" REGULAR_EXPRESSION "^discord/")
|
||||
source_group("Event" REGULAR_EXPRESSION "^event/")
|
||||
source_group("Events" REGULAR_EXPRESSION "^events/")
|
||||
source_group("Http" REGULAR_EXPRESSION "^http/")
|
||||
source_group("Json" REGULAR_EXPRESSION "^json/")
|
||||
source_group("Memory" REGULAR_EXPRESSION "^memory/")
|
||||
source_group("Net" REGULAR_EXPRESSION "^net/")
|
||||
source_group("Patches" REGULAR_EXPRESSION "^patches/")
|
||||
source_group("Process" REGULAR_EXPRESSION "^process/")
|
||||
source_group("Repositories" REGULAR_EXPRESSION "^repositories/")
|
||||
source_group("StackWalker" REGULAR_EXPRESSION "^StackWalker/")
|
||||
source_group("Stacktrace" REGULAR_EXPRESSION "^stacktrace/")
|
||||
source_group("Termcolor" REGULAR_EXPRESSION "^termcolor/")
|
||||
source_group("Util" REGULAR_EXPRESSION "^util/")
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source Files" FILES ${common_sources})
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Header Files" FILES ${common_headers})
|
||||
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/repositories" PREFIX "Repositories" FILES ${repositories})
|
||||
|
||||
option(EQEMU_ADD_PROFILER "Link with Google perftools profiler" OFF)
|
||||
#PRNG options
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+22
-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); }
|
||||
@@ -73,12 +86,6 @@ public:
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
+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
|
||||
|
||||
+3
-2
@@ -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),
|
||||
@@ -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),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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];
|
||||
@@ -326,6 +333,7 @@ union
|
||||
bool buyer;
|
||||
bool untargetable;
|
||||
uint32 npc_tint_id;
|
||||
EqGuid CharacterGuid;
|
||||
};
|
||||
|
||||
struct PlayerState_Struct {
|
||||
@@ -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,37 +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/mutex.h"
|
||||
#include "common/platform/posix/include_pthreads.h"
|
||||
#include "common/platform/win/include_windows.h"
|
||||
#include "common/event/event_loop.h"
|
||||
#include "uv.h"
|
||||
|
||||
//Sombody, someday needs to figure out how to implement a condition
|
||||
//system on windows...
|
||||
namespace EQ {
|
||||
|
||||
EventLoop& EventLoop::Get()
|
||||
{
|
||||
thread_local EventLoop inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
class Condition {
|
||||
private:
|
||||
#ifdef WIN32
|
||||
enum {
|
||||
SignalEvent = 0,
|
||||
BroadcastEvent,
|
||||
_eventCount
|
||||
};
|
||||
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());
|
||||
}
|
||||
|
||||
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();
|
||||
};
|
||||
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{};
|
||||
|
||||
+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 ||
|
||||
(
|
||||
|
||||
@@ -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, uint32_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, uint32_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,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
|
||||
|
||||
#include "client_version.h"
|
||||
|
||||
// Migration path: replace string_ids.h usage with ID enum values one call site at a time.
|
||||
|
||||
class Client;
|
||||
class Mob;
|
||||
class EQApplicationPacket;
|
||||
|
||||
namespace Message {
|
||||
|
||||
template<typename... Args>
|
||||
concept AllConstChar = (std::is_convertible_v<Args, const char*> && ...);
|
||||
|
||||
class IMessage
|
||||
{
|
||||
public:
|
||||
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 std::array<const char*, 9>& 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,86 @@
|
||||
/* 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/patches/titanium.h"
|
||||
#include "common/patches/sof.h"
|
||||
#include "common/patches/sod.h"
|
||||
#include "common/patches/uf.h"
|
||||
#include "common/patches/rof.h"
|
||||
#include "common/patches/rof2.h"
|
||||
#include "common/patches/tob.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
using Version = EQ::versions::ClientVersion;
|
||||
|
||||
struct ClientComponents
|
||||
{
|
||||
explicit ClientComponents(Version version) : version(version)
|
||||
{
|
||||
switch (version) {
|
||||
case Version::TOB:
|
||||
messageComponent = std::make_unique<Message::TOB>();
|
||||
break;
|
||||
case Version::RoF2:
|
||||
messageComponent = std::make_unique<Message::RoF2>();
|
||||
break;
|
||||
case Version::RoF:
|
||||
messageComponent = std::make_unique<Message::RoF>();
|
||||
break;
|
||||
case Version::UF:
|
||||
messageComponent = std::make_unique<Message::UF>();
|
||||
break;
|
||||
case Version::SoD:
|
||||
messageComponent = std::make_unique<Message::SoD>();
|
||||
break;
|
||||
case Version::SoF:
|
||||
messageComponent = std::make_unique<Message::SoF>();
|
||||
break;
|
||||
case Version::Titanium:
|
||||
messageComponent = std::make_unique<Message::Titanium>();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const Version version;
|
||||
std::unique_ptr<Message::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),
|
||||
}
|
||||
};
|
||||
|
||||
const std::unique_ptr<Message::IMessage>& GetMessageComponent(Version version)
|
||||
{
|
||||
return s_patches.at(static_cast<uint32_t>(version)).messageComponent;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by dannu on 4/21/2026.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/emu_versions.h"
|
||||
#include <memory>
|
||||
|
||||
namespace Message { class IMessage; }
|
||||
|
||||
// store all static functions for the different patches here, this can return nullptr for unsupported patches
|
||||
const std::unique_ptr<Message::IMessage>& GetMessageComponent(EQ::versions::ClientVersion version);
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -3221,7 +3221,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;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "uf.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,14 @@ namespace RoF
|
||||
};
|
||||
|
||||
} /*RoF*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class RoF : public UF
|
||||
{
|
||||
public:
|
||||
RoF() = default;
|
||||
~RoF() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
+15
-12
@@ -689,7 +689,7 @@ namespace RoF2
|
||||
EQApplicationPacket *outapp = nullptr;
|
||||
if (eq->bufffade == 1)
|
||||
{
|
||||
outapp = new EQApplicationPacket(OP_BuffCreate, 29);
|
||||
outapp = new EQApplicationPacket(OP_BuffCreate, 29u);
|
||||
outapp->WriteUInt32(emu->entityid);
|
||||
outapp->WriteUInt32(0); // tic timer
|
||||
outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ?
|
||||
@@ -753,7 +753,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 +1622,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 +2207,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 +2278,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 +2316,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
|
||||
{
|
||||
@@ -3820,7 +3823,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;
|
||||
@@ -4120,8 +4123,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 +4602,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);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "rof.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,14 @@ namespace RoF2
|
||||
};
|
||||
|
||||
}; /*RoF2*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class RoF2 : public RoF
|
||||
{
|
||||
public:
|
||||
RoF2() = default;
|
||||
~RoF2() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@@ -2128,7 +2128,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;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sof.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,14 @@ namespace SoD
|
||||
};
|
||||
|
||||
} /*SoD*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class SoD : public SoF
|
||||
{
|
||||
public:
|
||||
SoD() = default;
|
||||
~SoD() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@@ -1785,7 +1785,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;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "titanium.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,14 @@ namespace SoF
|
||||
};
|
||||
|
||||
} /*SoF*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class SoF : public Titanium
|
||||
{
|
||||
public:
|
||||
SoF() = default;
|
||||
~SoF() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "common/raid.h"
|
||||
#include "common/rulesys.h"
|
||||
#include "common/strings.h"
|
||||
#include "zone/string_ids.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -1991,7 +1992,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;
|
||||
@@ -3919,3 +3920,98 @@ namespace Titanium
|
||||
return index; // as long as we guard against bad slots server side, we should be fine
|
||||
}
|
||||
} /*Titanium*/
|
||||
|
||||
namespace Message {
|
||||
std::unique_ptr<EQApplicationPacket> Titanium::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> Titanium::Formatted(
|
||||
uint32_t color, uint32_t id, const std::array<const char*, 9>& 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> Titanium::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> Titanium::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;
|
||||
fmt::format_to_n(ic->message, strlen(name) + 1, "{}\0", name);
|
||||
return outapp;
|
||||
}
|
||||
|
||||
// A value of 0 means that the string isn't mapped in this client, valid string ids start at 1
|
||||
uint32_t Titanium::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 Titanium::ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const
|
||||
{
|
||||
switch (id) {
|
||||
case SPELL_FIZZLE:
|
||||
case MISS_NOTE:
|
||||
args[0] = nullptr; // drop spell link
|
||||
break;
|
||||
case SPELL_FIZZLE_OTHER:
|
||||
case MISSED_NOTE_OTHER:
|
||||
args[1] = nullptr; // drop spell link
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "IMessage.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,30 @@ namespace Titanium
|
||||
};
|
||||
|
||||
} /*Titanium*/
|
||||
|
||||
// out-going message packets
|
||||
namespace Message {
|
||||
|
||||
class Titanium : public IMessage
|
||||
{
|
||||
public:
|
||||
Titanium() = default;
|
||||
~Titanium() override = default;
|
||||
|
||||
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 std::array<const char*, 9>& 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]] virtual uint32_t ResolveID(uint32_t id) const;
|
||||
virtual void ResolveArguments(uint32_t id, std::array<const char*, 9>& args) const;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "rof2.h"
|
||||
#include "../struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
|
||||
namespace TOB
|
||||
{
|
||||
|
||||
//these are the only public member of this namespace.
|
||||
extern void Register(EQStreamIdentifier& into);
|
||||
extern void Reload();
|
||||
|
||||
|
||||
|
||||
//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 "tob_ops.h"
|
||||
|
||||
};
|
||||
|
||||
}; /*TOB*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class TOB : public RoF2
|
||||
{
|
||||
public:
|
||||
TOB() {}
|
||||
~TOB() override {}
|
||||
|
||||
std::unique_ptr<EQApplicationPacket> Formatted(uint32_t color, uint32_t id,
|
||||
const std::array<const char*, 9>& 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;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
@@ -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 "../types.h"
|
||||
#include "../emu_versions.h"
|
||||
#include "../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*/
|
||||
@@ -0,0 +1,100 @@
|
||||
//list of packets we need to encode on the way out:
|
||||
E(OP_AAExpUpdate)
|
||||
E(OP_Action)
|
||||
E(OP_Animation)
|
||||
E(OP_ApplyPoison)
|
||||
E(OP_AugmentInfo)
|
||||
E(OP_BeginCast)
|
||||
E(OP_BlockedBuffs)
|
||||
E(OP_Buff)
|
||||
E(OP_BuffCreate)
|
||||
E(OP_CancelTrade)
|
||||
E(OP_CastSpell)
|
||||
E(OP_ChannelMessage)
|
||||
E(OP_CharacterCreateRequest)
|
||||
E(OP_CharInventory)
|
||||
E(OP_ClickObjectAction)
|
||||
E(OP_ClientUpdate)
|
||||
E(OP_Consider)
|
||||
E(OP_Damage)
|
||||
E(OP_Death)
|
||||
E(OP_DeleteCharge)
|
||||
E(OP_DeleteItem)
|
||||
E(OP_DeleteSpawn)
|
||||
E(OP_DisciplineUpdate)
|
||||
E(OP_ExpansionInfo)
|
||||
E(OP_ExpUpdate)
|
||||
E(OP_GMTraining)
|
||||
E(OP_GMTrainSkillConfirm)
|
||||
E(OP_GroundSpawn)
|
||||
E(OP_HPUpdate)
|
||||
E(OP_Illusion)
|
||||
E(OP_ItemPacket)
|
||||
E(OP_LogServer)
|
||||
E(OP_ManaChange)
|
||||
E(OP_MemorizeSpell)
|
||||
E(OP_MobHealth)
|
||||
E(OP_MoneyOnCorpse)
|
||||
E(OP_MoveItem)
|
||||
E(OP_NewSpawn)
|
||||
E(OP_NewZone)
|
||||
E(OP_OnLevelMessage)
|
||||
E(OP_PlayerProfile)
|
||||
E(OP_RemoveBlockedBuffs)
|
||||
E(OP_RespondAA)
|
||||
E(OP_RequestClientZoneChange)
|
||||
E(OP_RecipeAutoCombine)
|
||||
E(OP_SendAATable)
|
||||
E(OP_SendCharInfo)
|
||||
E(OP_SendMaxCharacters)
|
||||
E(OP_SendMembership)
|
||||
E(OP_SendMembershipDetails)
|
||||
E(OP_SendZonepoints)
|
||||
E(OP_ShopPlayerBuy)
|
||||
E(OP_ShopPlayerSell)
|
||||
E(OP_ShopRequest)
|
||||
E(OP_SkillUpdate)
|
||||
E(OP_SpecialMesg)
|
||||
E(OP_SpawnAppearance)
|
||||
E(OP_SpawnDoor)
|
||||
E(OP_Stun)
|
||||
E(OP_WearChange)
|
||||
E(OP_ZoneChange)
|
||||
E(OP_ZoneEntry)
|
||||
E(OP_ZonePlayerToBind)
|
||||
E(OP_ZoneSpawns)
|
||||
|
||||
//list of packets we need to decode on the way in:
|
||||
D(OP_Animation)
|
||||
D(OP_ApplyPoison)
|
||||
D(OP_ApproveName)
|
||||
D(OP_AugmentInfo)
|
||||
D(OP_AugmentItem)
|
||||
D(OP_BlockedBuffs)
|
||||
D(OP_CastSpell)
|
||||
D(OP_ChannelMessage)
|
||||
D(OP_CharacterCreate)
|
||||
D(OP_ClientUpdate)
|
||||
D(OP_ClickDoor)
|
||||
D(OP_Consider)
|
||||
D(OP_ConsiderCorpse)
|
||||
D(OP_DeleteItem)
|
||||
D(OP_EnterWorld)
|
||||
D(OP_GMTraining)
|
||||
D(OP_GroupDisband)
|
||||
D(OP_GroupInvite)
|
||||
D(OP_GroupInvite2)
|
||||
D(OP_MemorizeSpell)
|
||||
D(OP_MoveItem)
|
||||
D(OP_RemoveBlockedBuffs)
|
||||
D(OP_SetServerFilter)
|
||||
D(OP_ShopPlayerBuy)
|
||||
D(OP_ShopPlayerSell)
|
||||
D(OP_ShopRequest)
|
||||
D(OP_SpawnAppearance)
|
||||
D(OP_TradeSkillCombine)
|
||||
D(OP_WearChange)
|
||||
D(OP_ZoneEntry)
|
||||
D(OP_ZoneChange)
|
||||
#undef E
|
||||
#undef D
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2711,7 +2711,7 @@ namespace UF
|
||||
|
||||
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;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sod.h"
|
||||
#include "common/struct_strategy.h"
|
||||
|
||||
class EQStreamIdentifier;
|
||||
@@ -48,3 +49,14 @@ namespace UF
|
||||
};
|
||||
|
||||
}; /*UF*/
|
||||
|
||||
namespace Message {
|
||||
|
||||
class UF : public SoD
|
||||
{
|
||||
public:
|
||||
UF() = default;
|
||||
~UF() override = default;
|
||||
};
|
||||
|
||||
} // namespace Message
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#ifdef _WINDOWS
|
||||
|
||||
#include "common/platform/win/include_windows.h"
|
||||
#include <WinSock2.h>
|
||||
|
||||
#endif // _WINDOWS
|
||||
|
||||
+3
-1
@@ -23,11 +23,13 @@
|
||||
|
||||
inline std::string random_string(size_t length)
|
||||
{
|
||||
auto randchar = []() -> char {
|
||||
auto randchar = []() -> char
|
||||
{
|
||||
const char charset[] = "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz";
|
||||
const size_t max_index = (sizeof(charset) - 1);
|
||||
return charset[static_cast<size_t>(std::rand()) % max_index];
|
||||
};
|
||||
|
||||
std::string str(length, 0);
|
||||
std::generate_n(str.begin(), length, randchar);
|
||||
return str;
|
||||
|
||||
+3
-1
@@ -255,6 +255,7 @@ RULE_BOOL(Mercs, AllowMercSuspendInCombat, true, "Allow merc suspend in combat")
|
||||
RULE_BOOL(Mercs, MercsIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.")
|
||||
RULE_INT(Mercs, MercsHasteCap, 100, "Haste cap for non-v3(over haste) haste")
|
||||
RULE_INT(Mercs, MercsHastev3Cap, 25, "Haste cap for v3(over haste) haste")
|
||||
RULE_INT(Mercs, MaxMercSlots, 6, "Maximum number of mercenary slots per character (max = MAXMERCS)")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Guild)
|
||||
@@ -349,11 +350,12 @@ RULE_STRING(World, MOTD, "", "Server MOTD sent on login, change from empty to ha
|
||||
RULE_STRING(World, Rules, "", "Server Rules, change from empty to have this be used instead of variables table 'rules' value, lines are pipe (|) separated, example: A|B|C")
|
||||
RULE_BOOL(World, EnableAutoLogin, false, "Enables or disables auto login of characters, allowing people to log characters in directly from loginserver to ingame")
|
||||
RULE_BOOL(World, EnablePVPRegions, true, "Enables or disables PVP Regions automatically setting your PVP flag")
|
||||
RULE_STRING(World, SupportedClients, "RoF2", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2. Example: Titanium,RoF2")
|
||||
RULE_STRING(World, SupportedClients, "RoF2,TOB", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2 | TOB. Example: Titanium,RoF2,TOB")
|
||||
RULE_STRING(World, CustomFilesKey, "", "Enable if the server requires custom files and sends a key to validate. Empty string to disable. Example: eqcustom_v1")
|
||||
RULE_STRING(World, CustomFilesUrl, "github.com/knervous/eqnexus/releases", "URL to display at character select if client is missing custom files")
|
||||
RULE_INT(World, CustomFilesAdminLevel, 20, "Admin level at which custom file key is not required when CustomFilesKey is specified")
|
||||
RULE_BOOL(World, RealTimeCalculateGuilds, false, "(Temp feature flag) If true, guilds will be calculated in real time instead of at zone boot. This is a performance hit but allows for more dynamic guilds.")
|
||||
RULE_INT(World, Id, 100, "Used by later clients to create GUIDs, expected to be Unique to the world but ultimately not that important")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Zone)
|
||||
|
||||
+24
-20
@@ -27,7 +27,7 @@
|
||||
class SerializeBuffer
|
||||
{
|
||||
public:
|
||||
SerializeBuffer() : m_buffer(nullptr), m_capacity(0), m_pos(0) {}
|
||||
SerializeBuffer() = default;
|
||||
|
||||
explicit SerializeBuffer(size_t size) : m_capacity(size), m_pos(0)
|
||||
{
|
||||
@@ -35,8 +35,10 @@ public:
|
||||
memset(m_buffer, 0, size);
|
||||
}
|
||||
|
||||
SerializeBuffer(const SerializeBuffer &rhs)
|
||||
: m_buffer(new unsigned char[rhs.m_capacity]), m_capacity(rhs.m_capacity), m_pos(rhs.m_pos)
|
||||
SerializeBuffer(const SerializeBuffer& rhs)
|
||||
: m_buffer(new unsigned char[rhs.m_capacity])
|
||||
, m_capacity(rhs.m_capacity)
|
||||
, m_pos(rhs.m_pos)
|
||||
{
|
||||
memcpy(m_buffer, rhs.m_buffer, rhs.m_capacity);
|
||||
}
|
||||
@@ -53,30 +55,31 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
SerializeBuffer(SerializeBuffer &&rhs) : m_buffer(rhs.m_buffer), m_capacity(rhs.m_capacity), m_pos(rhs.m_pos)
|
||||
SerializeBuffer(SerializeBuffer&& rhs)
|
||||
: m_buffer(std::exchange(rhs.m_buffer, nullptr))
|
||||
, m_capacity(std::exchange(rhs.m_capacity, 0))
|
||||
, m_pos(std::exchange(rhs.m_pos, 0))
|
||||
{
|
||||
rhs.m_buffer = nullptr;
|
||||
rhs.m_capacity = 0;
|
||||
rhs.m_pos = 0;
|
||||
}
|
||||
|
||||
SerializeBuffer &operator=(SerializeBuffer &&rhs)
|
||||
SerializeBuffer& operator=(SerializeBuffer&& rhs)
|
||||
{
|
||||
if (this != &rhs) {
|
||||
if (this != &rhs)
|
||||
{
|
||||
delete[] m_buffer;
|
||||
|
||||
m_buffer = rhs.m_buffer;
|
||||
m_capacity = rhs.m_capacity;
|
||||
m_pos = rhs.m_pos;
|
||||
|
||||
rhs.m_buffer = nullptr;
|
||||
rhs.m_capacity = 0;
|
||||
rhs.m_pos = 0;
|
||||
m_buffer = std::exchange(rhs.m_buffer, nullptr);
|
||||
m_capacity = std::exchange(rhs.m_capacity, 0);
|
||||
m_pos = std::exchange(rhs.m_pos, 0);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~SerializeBuffer() { delete[] m_buffer; }
|
||||
~SerializeBuffer()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
void WriteUInt8(uint8_t v)
|
||||
{
|
||||
@@ -209,7 +212,8 @@ public:
|
||||
private:
|
||||
void Grow(size_t new_size);
|
||||
void Reset();
|
||||
unsigned char *m_buffer;
|
||||
size_t m_capacity;
|
||||
size_t m_pos;
|
||||
|
||||
unsigned char* m_buffer = nullptr;
|
||||
size_t m_capacity = 0;
|
||||
size_t m_pos = 0;
|
||||
};
|
||||
|
||||
+28
-31
@@ -389,35 +389,31 @@ enum {
|
||||
class ServerPacket
|
||||
{
|
||||
public:
|
||||
~ServerPacket() { safe_delete_array(pBuffer); }
|
||||
ServerPacket(uint16 in_opcode = 0, uint32 in_size = 0) {
|
||||
this->compressed = false;
|
||||
size = in_size;
|
||||
opcode = in_opcode;
|
||||
if (size == 0) {
|
||||
pBuffer = 0;
|
||||
}
|
||||
else {
|
||||
~ServerPacket()
|
||||
{
|
||||
safe_delete_array(pBuffer);
|
||||
}
|
||||
|
||||
ServerPacket(uint16 in_opcode = 0, size_t in_size = 0)
|
||||
: size(static_cast<uint32>(in_size))
|
||||
, opcode(in_opcode)
|
||||
{
|
||||
if (size != 0)
|
||||
{
|
||||
pBuffer = new uchar[size];
|
||||
memset(pBuffer, 0, size);
|
||||
}
|
||||
_wpos = 0;
|
||||
_rpos = 0;
|
||||
}
|
||||
|
||||
ServerPacket(uint16 in_opcode, const EQ::Net::Packet &p) {
|
||||
this->compressed = false;
|
||||
size = (uint32)p.Length();
|
||||
opcode = in_opcode;
|
||||
if (size == 0) {
|
||||
pBuffer = 0;
|
||||
}
|
||||
else {
|
||||
ServerPacket(uint16 in_opcode, const EQ::Net::Packet& p)
|
||||
: size(static_cast<uint32>(p.Length()))
|
||||
, opcode(in_opcode)
|
||||
{
|
||||
if (size != 0)
|
||||
{
|
||||
pBuffer = new uchar[size];
|
||||
memcpy(pBuffer, p.Data(), size);
|
||||
}
|
||||
_wpos = 0;
|
||||
_rpos = 0;
|
||||
}
|
||||
|
||||
ServerPacket* Copy() {
|
||||
@@ -447,14 +443,14 @@ public:
|
||||
void ReadSkipBytes(uint32 count) { _rpos += count; }
|
||||
void SetReadPosition(uint32 Newrpos) { _rpos = Newrpos; }
|
||||
|
||||
uint32 size;
|
||||
uint16 opcode;
|
||||
uchar* pBuffer;
|
||||
uint32 _wpos;
|
||||
uint32 _rpos;
|
||||
bool compressed;
|
||||
uint32 InflatedSize;
|
||||
uint32 destination;
|
||||
uint32 size = 0;
|
||||
uint16 opcode = 0;
|
||||
uchar* pBuffer = nullptr;
|
||||
uint32 _wpos = 0;
|
||||
uint32 _rpos = 0;
|
||||
bool compressed = false;
|
||||
uint32 InflatedSize = 0;
|
||||
uint32 destination = 0;
|
||||
};
|
||||
|
||||
#pragma pack(push)
|
||||
@@ -989,11 +985,12 @@ struct LauncherConnectInfo {
|
||||
char name[64];
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
enum ZoneRequestCommands {
|
||||
ZR_Start,
|
||||
ZR_Restart,
|
||||
ZR_Stop
|
||||
} ZoneRequestCommands;
|
||||
};
|
||||
|
||||
struct LauncherZoneRequest {
|
||||
uint8 command;
|
||||
char short_name[33];
|
||||
|
||||
+1
-1
@@ -999,7 +999,7 @@ uint8 GetSpellLevel(uint16 spell_id, uint8 class_id)
|
||||
return UINT8_MAX;
|
||||
}
|
||||
|
||||
if (class_id >= Class::PLAYER_CLASS_COUNT) {
|
||||
if (class_id < Class::Warrior || class_id > Class::PLAYER_CLASS_COUNT) {
|
||||
return UINT8_MAX;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -38,10 +38,10 @@
|
||||
#define RELOADTASKS 0
|
||||
#define RELOADTASKSETS 2
|
||||
|
||||
typedef enum {
|
||||
enum TaskMethodType {
|
||||
METHODSINGLEID = 0,
|
||||
METHODQUEST = 2
|
||||
} TaskMethodType;
|
||||
};
|
||||
|
||||
enum class TaskActivityType : int32_t // task element/objective
|
||||
{
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace
|
||||
{
|
||||
m_cache.insert(std::make_pair(
|
||||
key_type(src, target, dynamic_id, object_offset)
|
||||
, cache_entry(offset, distance)
|
||||
, cache_entry(offset, static_cast<int>(distance))
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
+53
-132
@@ -302,20 +302,13 @@ void Client::SendPlayResponse(EQApplicationPacket *outapp)
|
||||
|
||||
void Client::GenerateRandomLoginKey()
|
||||
{
|
||||
m_key.clear();
|
||||
int count = 0;
|
||||
while (count < 10) {
|
||||
static const char key_selection[] =
|
||||
{
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9'
|
||||
};
|
||||
static constexpr std::string_view key_selection = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
constexpr size_t key_length = 10;
|
||||
|
||||
m_key.append((const char *) &key_selection[m_random.Int(0, 35)], 1);
|
||||
count++;
|
||||
m_key.clear();
|
||||
m_key.reserve(key_length);
|
||||
for (size_t i = 0; i < key_length; ++i) {
|
||||
m_key += key_selection[m_random.Int(0, key_selection.size() - 1)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,55 +355,30 @@ void Client::SendFailedLogin()
|
||||
m_stored_username.clear();
|
||||
m_stored_password.clear();
|
||||
|
||||
if (m_client_version == cv_steam_latest) {
|
||||
// unencrypted
|
||||
LoginBaseMessage h{};
|
||||
h.sequence = m_login_base_message.sequence; // login (3)
|
||||
h.encrypt_type = m_login_base_message.encrypt_type;
|
||||
// unencrypted
|
||||
LoginBaseMessage h{};
|
||||
h.sequence = m_login_base_message.sequence;
|
||||
h.encrypt_type = m_login_base_message.encrypt_type;
|
||||
h.unk3 = m_login_base_message.unk3;
|
||||
|
||||
// encrypted
|
||||
PlayerLoginReplySteamLatest r{};
|
||||
r.base_reply.success = false;
|
||||
r.base_reply.error_str_id = 105; // Error - The username and/or password were not valid
|
||||
PlayerLoginReply r = m_client_version == cv_tob ? PlayerLoginReply(PlayerLoginReplyTOB{}) : PlayerLoginReply(PlayerLoginReplyOld{});
|
||||
r.set_error_code(LS::ErrStr::ERROR_INVALID_CREDS);
|
||||
// We don't care what key we send, just that it exists and is 10 characters so that we do not shift
|
||||
r.set_key("InvalidKey");
|
||||
|
||||
char encrypted_buffer[80] = { 0 };
|
||||
auto rc = eqcrypt_block((const char*)&r, sizeof(r), encrypted_buffer, 1);
|
||||
if (rc == nullptr) {
|
||||
LogDebug("Failed to encrypt eqcrypt block for failed login");
|
||||
}
|
||||
|
||||
constexpr int outsize = sizeof(LoginBaseMessage) + sizeof(encrypted_buffer);
|
||||
EQApplicationPacket outapp(OP_LoginAccepted, outsize);
|
||||
outapp.WriteData(&h, sizeof(h));
|
||||
outapp.WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
|
||||
|
||||
m_connection->QueuePacket(&outapp);
|
||||
char encrypted_buffer[80] = { 0 };
|
||||
auto rc = eqcrypt_block(r.data(), r.size(), encrypted_buffer, 1);
|
||||
if (rc == nullptr) {
|
||||
LogDebug("Failed to encrypt eqcrypt block for failed login");
|
||||
}
|
||||
else {
|
||||
// unencrypted
|
||||
LoginBaseMessage h{};
|
||||
h.sequence = m_login_base_message.sequence; // login (3)
|
||||
h.encrypt_type = m_login_base_message.encrypt_type;
|
||||
|
||||
// encrypted
|
||||
PlayerLoginReply r{};
|
||||
r.base_reply.success = false;
|
||||
r.base_reply.error_str_id = 105; // Error - The username and/or password were not valid
|
||||
constexpr int outsize = sizeof(LoginBaseMessage) + sizeof(encrypted_buffer);
|
||||
EQApplicationPacket outapp(OP_LoginAccepted, outsize);
|
||||
outapp.WriteData(&h, sizeof(h));
|
||||
outapp.WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
|
||||
|
||||
char encrypted_buffer[80] = { 0 };
|
||||
auto rc = eqcrypt_block((const char*)&r, sizeof(r), encrypted_buffer, 1);
|
||||
if (rc == nullptr) {
|
||||
LogDebug("Failed to encrypt eqcrypt block for failed login");
|
||||
}
|
||||
m_connection->QueuePacket(&outapp);
|
||||
|
||||
constexpr int outsize = sizeof(LoginBaseMessage) + sizeof(encrypted_buffer);
|
||||
EQApplicationPacket outapp(OP_LoginAccepted, outsize);
|
||||
outapp.WriteData(&h, sizeof(h));
|
||||
outapp.WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
|
||||
|
||||
m_connection->QueuePacket(&outapp);
|
||||
}
|
||||
|
||||
m_client_status = cs_failed_to_login;
|
||||
}
|
||||
|
||||
@@ -496,91 +464,44 @@ void Client::DoSuccessfulLogin(LoginAccountsRepository::LoginAccounts &a)
|
||||
m_account_name = a.account_name;
|
||||
m_loginserver_name = a.source_loginserver;
|
||||
|
||||
if (m_client_version == cv_steam_latest) {
|
||||
// unencrypted
|
||||
LoginBaseMessage h{};
|
||||
h.sequence = m_login_base_message.sequence;
|
||||
h.compressed = false;
|
||||
h.encrypt_type = m_login_base_message.encrypt_type;
|
||||
h.unk3 = m_login_base_message.unk3;
|
||||
// unencrypted
|
||||
LoginBaseMessage h{};
|
||||
h.sequence = m_login_base_message.sequence;
|
||||
h.compressed = false;
|
||||
h.encrypt_type = m_login_base_message.encrypt_type;
|
||||
h.unk3 = m_login_base_message.unk3;
|
||||
|
||||
// not serializing any of the variable length strings so just use struct directly
|
||||
PlayerLoginReplySteamLatest r{};
|
||||
r.base_reply.success = true;
|
||||
r.base_reply.error_str_id = 101; // No Error
|
||||
r.unk1 = 0;
|
||||
r.unk2 = 0;
|
||||
r.lsid = a.id;
|
||||
r.failed_attempts = 0;
|
||||
r.show_player_count = server.options.IsShowPlayerCountEnabled();
|
||||
r.unk3 = 0;
|
||||
r.unk4 = 0;
|
||||
memcpy(r.key, m_key.c_str(), m_key.size());
|
||||
// not serializing any of the variable length strings so just use struct directly
|
||||
PlayerLoginReply r = m_client_version == cv_tob ? PlayerLoginReply(PlayerLoginReplyTOB{}) : PlayerLoginReply(PlayerLoginReplyOld{});
|
||||
|
||||
//todo: needs to be fixed
|
||||
//SendExpansionPacketData(r);
|
||||
r.set_success(true);
|
||||
r.set_error_code(LS::ErrStr::ERROR_NONE);
|
||||
r.set_lsid(a.id);
|
||||
r.set_show_player_count(server.options.IsShowPlayerCountEnabled());
|
||||
r.set_key(m_key);
|
||||
|
||||
char encrypted_buffer[80] = { 0 };
|
||||
|
||||
auto rc = eqcrypt_block((const char*)&r, sizeof(r), encrypted_buffer, 1);
|
||||
if (rc == nullptr) {
|
||||
LogDebug("Failed to encrypt eqcrypt block");
|
||||
}
|
||||
|
||||
constexpr int outsize = sizeof(LoginBaseMessage) + sizeof(encrypted_buffer);
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_LoginAccepted, outsize);
|
||||
outapp->WriteData(&h, sizeof(h));
|
||||
outapp->WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
|
||||
|
||||
m_connection->QueuePacket(outapp.get());
|
||||
if (m_client_version != cv_tob) {
|
||||
SendExpansionPacketData(r.old());
|
||||
}
|
||||
else {
|
||||
// unencrypted
|
||||
LoginBaseMessage h{};
|
||||
h.sequence = m_login_base_message.sequence;
|
||||
h.compressed = false;
|
||||
h.encrypt_type = m_login_base_message.encrypt_type;
|
||||
h.unk3 = m_login_base_message.unk3;
|
||||
|
||||
// not serializing any of the variable length strings so just use struct directly
|
||||
PlayerLoginReply r{};
|
||||
r.base_reply.success = true;
|
||||
r.base_reply.error_str_id = 101; // No Error
|
||||
r.unk1 = 0;
|
||||
r.unk2 = 0;
|
||||
r.lsid = a.id;
|
||||
r.failed_attempts = 0;
|
||||
r.show_player_count = server.options.IsShowPlayerCountEnabled();
|
||||
r.offer_min_days = 99;
|
||||
r.offer_min_views = -1;
|
||||
r.offer_cooldown_minutes = 0;
|
||||
r.web_offer_number = 0;
|
||||
r.web_offer_min_days = 99;
|
||||
r.web_offer_min_views = -1;
|
||||
r.web_offer_cooldown_minutes = 0;
|
||||
memcpy(r.key, m_key.c_str(), m_key.size());
|
||||
char encrypted_buffer[80] = { 0 };
|
||||
|
||||
SendExpansionPacketData(r);
|
||||
|
||||
char encrypted_buffer[80] = { 0 };
|
||||
|
||||
auto rc = eqcrypt_block((const char*)&r, sizeof(r), encrypted_buffer, 1);
|
||||
if (rc == nullptr) {
|
||||
LogDebug("Failed to encrypt eqcrypt block");
|
||||
}
|
||||
|
||||
constexpr int outsize = sizeof(LoginBaseMessage) + sizeof(encrypted_buffer);
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_LoginAccepted, outsize);
|
||||
outapp->WriteData(&h, sizeof(h));
|
||||
outapp->WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
|
||||
|
||||
m_connection->QueuePacket(outapp.get());
|
||||
auto rc = eqcrypt_block(r.data(), r.size(), encrypted_buffer, 1);
|
||||
if (rc == nullptr) {
|
||||
LogDebug("Failed to encrypt eqcrypt block");
|
||||
}
|
||||
|
||||
constexpr int outsize = sizeof(LoginBaseMessage) + sizeof(encrypted_buffer);
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_LoginAccepted, outsize);
|
||||
outapp->WriteData(&h, sizeof(h));
|
||||
outapp->WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
|
||||
|
||||
m_connection->QueuePacket(outapp.get());
|
||||
|
||||
m_client_status = cs_logged_in;
|
||||
}
|
||||
|
||||
void Client::SendExpansionPacketData(PlayerLoginReply &plrs)
|
||||
void Client::SendExpansionPacketData(PlayerLoginReplyOld &plrs)
|
||||
{
|
||||
SerializeBuffer buf;
|
||||
//from eqlsstr_us.txt id of each expansion, excluding 'Everquest'
|
||||
@@ -607,7 +528,7 @@ void Client::SendExpansionPacketData(PlayerLoginReply &plrs)
|
||||
|
||||
//generate expansion data
|
||||
for (int i = 0; i < 19; i++) {
|
||||
buf.WriteInt32(i); //sequenctial number
|
||||
buf.WriteInt32(i); //sequential number
|
||||
buf.WriteInt32((expansion & (1 << i)) == (1 << i) ? 0x01 : 0x00); //1 own 0 not own
|
||||
buf.WriteInt8(0x00);
|
||||
buf.WriteInt32(ExpansionLookup[i]); //from eqlsstr_us.txt
|
||||
@@ -620,7 +541,7 @@ void Client::SendExpansionPacketData(PlayerLoginReply &plrs)
|
||||
buf.WriteInt32(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
auto out = std::make_unique<EQApplicationPacket>(OP_LoginExpansionPacketData, buf);
|
||||
auto out = std::make_unique<EQApplicationPacket>(OP_LoginExpansionPacketData, std::move(buf));
|
||||
m_connection->QueuePacket(out.get());
|
||||
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
// Titanium uses the encrypted data block to contact the expansion (You own xxx:) and the max expansions (of yyy)
|
||||
// Rof uses a separate data packet specifically for the expansion data
|
||||
// Live, as of July 2021 uses a similar but slightly different seperate data packet
|
||||
void SendExpansionPacketData(PlayerLoginReply &plrs);
|
||||
void SendExpansionPacketData(PlayerLoginReplyOld &plrs);
|
||||
void SendPlayToWorld(const char *data);
|
||||
void SendServerListPacket(uint32 seq);
|
||||
void SendPlayResponse(EQApplicationPacket *outapp);
|
||||
|
||||
@@ -74,7 +74,7 @@ void CheckSoDOpcodeFile(const std::string &path)
|
||||
}
|
||||
}
|
||||
|
||||
void CheckSteamLatestOpcodeFile(const std::string &path)
|
||||
void CheckTOBOpcodeFile(const std::string &path)
|
||||
{
|
||||
if (File::Exists(path)) {
|
||||
return;
|
||||
@@ -177,40 +177,40 @@ ClientManager::ClientManager()
|
||||
}
|
||||
);
|
||||
|
||||
int steam_latest_port = server.config.GetVariableInt("client_configuration", "steam_latest_port", 15900);
|
||||
int tob_port = server.config.GetVariableInt("client_configuration", "tob_port", 15900);
|
||||
|
||||
EQStreamManagerInterfaceOptions steam_latest_opts(steam_latest_port, false, false);
|
||||
EQStreamManagerInterfaceOptions tob_opts(tob_port, false, false);
|
||||
|
||||
m_steam_latest_stream = new EQ::Net::EQStreamManager(steam_latest_opts);
|
||||
m_steam_latest_ops = new RegularOpcodeManager;
|
||||
m_tob_stream = new EQ::Net::EQStreamManager(tob_opts);
|
||||
m_tob_ops = new RegularOpcodeManager;
|
||||
|
||||
opcodes_path = fmt::format(
|
||||
"{}/{}",
|
||||
PathManager::Instance()->GetOpcodePath(),
|
||||
"login_opcodes_steam_latest.conf"
|
||||
"login_opcodes_tob.conf"
|
||||
);
|
||||
|
||||
CheckSteamLatestOpcodeFile(opcodes_path);
|
||||
CheckTOBOpcodeFile(opcodes_path);
|
||||
|
||||
if (!m_steam_latest_ops->LoadOpcodes(opcodes_path.c_str())) {
|
||||
if (!m_tob_ops->LoadOpcodes(opcodes_path.c_str())) {
|
||||
LogError(
|
||||
"ClientManager fatal error: couldn't load opcodes for Steam Latest file [{}]",
|
||||
server.config.GetVariableString("client_configuration", "steam_latest_opcodes", "login_opcodes.conf")
|
||||
"ClientManager fatal error: couldn't load opcodes for TOB file [{}]",
|
||||
server.config.GetVariableString("client_configuration", "tob_opcodes", "login_opcodes.conf")
|
||||
);
|
||||
|
||||
run_server = false;
|
||||
}
|
||||
|
||||
m_steam_latest_stream->OnNewConnection(
|
||||
m_tob_stream->OnNewConnection(
|
||||
[this](std::shared_ptr<EQ::Net::EQStream> stream) {
|
||||
LogInfo(
|
||||
"New Steam Latest client connection from [{}:{}]",
|
||||
"New TOB client connection from [{}:{}]",
|
||||
long2ip(stream->GetRemoteIP()),
|
||||
stream->GetRemotePort()
|
||||
);
|
||||
|
||||
stream->SetOpcodeManager(&m_steam_latest_ops);
|
||||
Client *c = new Client(stream, cv_steam_latest);
|
||||
stream->SetOpcodeManager(&m_tob_ops);
|
||||
Client *c = new Client(stream, cv_tob);
|
||||
m_clients.push_back(c);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -37,6 +37,6 @@ private:
|
||||
EQ::Net::EQStreamManager *m_titanium_stream;
|
||||
OpcodeManager *m_sod_ops;
|
||||
EQ::Net::EQStreamManager *m_sod_stream;
|
||||
OpcodeManager *m_steam_latest_ops;
|
||||
EQ::Net::EQStreamManager *m_steam_latest_stream;
|
||||
OpcodeManager *m_tob_ops;
|
||||
EQ::Net::EQStreamManager *m_tob_stream;
|
||||
};
|
||||
|
||||
+85
-16
@@ -20,6 +20,7 @@
|
||||
#include "common/types.h"
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
@@ -45,7 +46,7 @@ struct LoginHandShakeReply {
|
||||
};
|
||||
|
||||
// variable length, can use directly if not serializing strings
|
||||
struct PlayerLoginReply {
|
||||
struct PlayerLoginReplyOld {
|
||||
// base header excluded to make struct data easier to encrypt
|
||||
//LoginBaseMessage base_header;
|
||||
LoginBaseReplyMessage base_reply;
|
||||
@@ -61,25 +62,90 @@ struct PlayerLoginReply {
|
||||
int32_t offer_cooldown_minutes; // guess (default: 0)
|
||||
int32_t web_offer_number; // web order view number, 0 nothing (default: 0)
|
||||
int32_t web_offer_min_days; // number of days to show offer (based on first offer time in client eqls ini) (default: 99)
|
||||
int32_t web_offer_min_views; // mininum views, -1 for no minimum, 0 for never shows (based on client eqls ini) (default: -1)
|
||||
int32_t web_offer_min_views; // minimum views, -1 for no minimum, 0 for never shows (based on client eqls ini) (default: -1)
|
||||
int32_t web_offer_cooldown_minutes; // minimum minutes between offers (based on last offer time in client eqls ini) (default: 0)
|
||||
char username[1]; // variable length, if not empty client attempts to re-login to server select when quitting from char select and sends this in a struct
|
||||
char unknown[1]; // variable length, password unlikely? client doesn't send this on re-login from char select
|
||||
};
|
||||
|
||||
struct PlayerLoginReplySteamLatest
|
||||
{
|
||||
void set_success(bool v) { base_reply.success = v; }
|
||||
void set_error_code(int32_t v) { base_reply.error_str_id = v; }
|
||||
void set_lsid(int32_t v) { lsid = v; }
|
||||
void set_show_player_count(bool v) { show_player_count = v; }
|
||||
void set_hardcoded_success_values() {
|
||||
offer_min_days = 99;
|
||||
offer_min_views = -1;
|
||||
web_offer_min_days = 99;
|
||||
web_offer_min_views = -1;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(PlayerLoginReplyOld) == 58, "PlayerLoginReplyOld struct size does not match expected size");
|
||||
static_assert(std::is_trivially_copyable_v<PlayerLoginReplyOld>);
|
||||
static_assert(std::is_standard_layout_v<PlayerLoginReplyOld>);
|
||||
|
||||
struct PlayerLoginReplyTOB {
|
||||
LoginBaseReplyMessage base_reply;
|
||||
int8_t unk1; // (default: 0)
|
||||
int8_t unk2; // (default: 0)
|
||||
int32_t lsid; // (default: -1)
|
||||
char key[11]; // client reads until null (variable length)
|
||||
int32_t failed_attempts;
|
||||
bool show_player_count; // admin flag, enables admin button and shows server player counts (default: false)
|
||||
int32_t unk3; // guess, needs more investigation (default: 0)
|
||||
int32_t unk4; // guess, needs more investigation (default: 0)
|
||||
char username[1]; // variable length, if not empty client attempts to re-login to server select when quitting from char select and sends this in a struct
|
||||
char unknown[1]; // variable length, password unlikely? client doesn't send this on re-login from char select
|
||||
|
||||
int8_t unk1 = 0;
|
||||
int8_t unk2 = 0;
|
||||
int32_t lsid = -1;
|
||||
char key[11] = {};
|
||||
int32_t failed_attempts = 0;
|
||||
int32_t display_error_str_id = 0;
|
||||
int32_t unk3 = 0;
|
||||
bool show_player_count = false;
|
||||
char username[1] = {};
|
||||
char unk4[1] = {};
|
||||
|
||||
void set_success(bool v) { base_reply.success = v; }
|
||||
void set_error_code(int32_t v) { display_error_str_id = v; base_reply.error_str_id = v; }
|
||||
void set_lsid(int32_t v) { lsid = v; }
|
||||
void set_show_player_count(bool v) { show_player_count = v; }
|
||||
void set_hardcoded_success_values() {}
|
||||
};
|
||||
static_assert(sizeof(PlayerLoginReplyTOB) == 38, "PlayerLoginReplyTOB struct size does not match expected size");
|
||||
static_assert(std::is_trivially_copyable_v<PlayerLoginReplyTOB>);
|
||||
static_assert(std::is_standard_layout_v<PlayerLoginReplyTOB>);
|
||||
|
||||
class PlayerLoginReply {
|
||||
std::variant<PlayerLoginReplyOld, PlayerLoginReplyTOB> v_;
|
||||
static_assert(sizeof(PlayerLoginReplyOld::key) == sizeof(PlayerLoginReplyTOB::key), "Old and TOB key buffers must match in size due to code assumptions");
|
||||
public:
|
||||
PlayerLoginReply(PlayerLoginReplyOld s) : v_(s) {}
|
||||
PlayerLoginReply(PlayerLoginReplyTOB s) : v_(s) {}
|
||||
|
||||
void set_success(bool val) {
|
||||
std::visit([val](auto& s) { s.set_success(val); }, v_);
|
||||
}
|
||||
void set_error_code(int32_t val) {
|
||||
std::visit([val](auto& s) { s.set_error_code(val); }, v_);
|
||||
}
|
||||
void set_lsid(int32_t val) {
|
||||
std::visit([val](auto& s) { s.set_lsid(val); }, v_);
|
||||
}
|
||||
void set_show_player_count(bool val) {
|
||||
std::visit([val](auto& s) { s.set_show_player_count(val); }, v_);
|
||||
}
|
||||
void set_key(std::string_view s) {
|
||||
std::visit([&](auto& st) {
|
||||
const size_t n = s.copy(st.key, sizeof(st.key) - 1);
|
||||
st.key[n] = '\0';
|
||||
}, v_);
|
||||
}
|
||||
template<size_t N>
|
||||
void set_key(const char (&s)[N]) {
|
||||
static_assert(N != (sizeof(PlayerLoginReplyTOB::key) - 1), "Key literal does not match reply struct's key buffer (without null terminator)");
|
||||
set_key(std::string_view{s, N - 1});
|
||||
}
|
||||
|
||||
PlayerLoginReplyOld& old() { return std::get<PlayerLoginReplyOld>(v_); }
|
||||
const PlayerLoginReplyOld& old() const { return std::get<PlayerLoginReplyOld>(v_); }
|
||||
|
||||
char* data() noexcept {
|
||||
return std::visit([](auto& s) { return reinterpret_cast<char*>(&s); }, v_);
|
||||
}
|
||||
size_t size() const noexcept {
|
||||
return std::visit([](auto const& s) { return sizeof(s); }, v_);
|
||||
}
|
||||
};
|
||||
|
||||
// variable length, for reference
|
||||
@@ -126,7 +192,7 @@ struct SystemFingerprint {
|
||||
enum LSClientVersion {
|
||||
cv_titanium,
|
||||
cv_sod,
|
||||
cv_steam_latest
|
||||
cv_tob
|
||||
};
|
||||
|
||||
enum LSClientStatus {
|
||||
@@ -195,11 +261,14 @@ namespace LS {
|
||||
namespace ErrStr {
|
||||
constexpr static int ERROR_NONE = 101; // No Error
|
||||
constexpr static int ERROR_UNKNOWN = 102; // Error - Unknown Error Occurred
|
||||
constexpr static int ERROR_INVALID_CREDS = 105; // Error - Invalid Account Name or Password
|
||||
constexpr static int ERROR_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again.
|
||||
constexpr static int ERROR_PASSWORD_RESET = 112; // Require password reset
|
||||
constexpr static int ERROR_SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later.
|
||||
constexpr static int ERROR_ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information.
|
||||
constexpr static int ERROR_ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information.
|
||||
constexpr static int ERROR_WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later.
|
||||
constexpr static int ERROR_REQUIRE_2FA = 342; // This account requires two-factor authentication.
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -706,7 +706,7 @@ bool WorldServer::ValidateWorldServerAdminLogin(
|
||||
|
||||
void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip, LSClientVersion version) const
|
||||
{
|
||||
if (version == cv_steam_latest) {
|
||||
if (version == cv_tob) {
|
||||
if (use_local_ip) {
|
||||
out.WriteString(GetLocalIP());
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ std::unique_ptr<EQApplicationPacket> WorldServerManager::CreateServerListPacket(
|
||||
s->SerializeForClientServerList(buf, use_local_ip, client->GetClientVersion());
|
||||
}
|
||||
|
||||
return std::make_unique<EQApplicationPacket>(OP_ServerListResponse, buf);
|
||||
return std::make_unique<EQApplicationPacket>(OP_ServerListResponse, std::move(buf));
|
||||
}
|
||||
|
||||
void WorldServerManager::SendUserLoginToWorldRequest(
|
||||
|
||||
+636
@@ -0,0 +1,636 @@
|
||||
### Status
|
||||
Below is a status list for the 450 opcodes we currently use on the server for the TOB client. Currently uses 3 status levels (let me know if we should do more):
|
||||
- 🔴 Not-Set (Opcode not set in the patch file)
|
||||
- 🟡 Unverified (Opcode set but structure hasn't been verified as completely working)
|
||||
- 🟢 Verified (Opcode set and structure is working)
|
||||
|
||||
### World/Zone Opcode Implementation Status
|
||||
|
||||
| Opcode | Status | Notes | Working On |
|
||||
|:----------------------------------|:--------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------|
|
||||
| `OP_AAAction` | 🟡 Unverified | | |
|
||||
| `OP_AAExpUpdate` | 🟢 Verified | | |
|
||||
| `OP_AcceptNewTask` | 🔴 Not-Set | | |
|
||||
| `OP_AckPacket` | 🟢 Verified | | |
|
||||
| `OP_Action` | 🟡 Unverified | | |
|
||||
| `OP_Action2` | 🔴 Not-Set | | |
|
||||
| `OP_AddNimbusEffect` | 🟡 Unverified | | |
|
||||
| `OP_AdventureData` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureDetails` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureFinish` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureInfo` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureInfoRequest` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureLeaderboardReply` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureLeaderboardRequest` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureMerchantPurchase` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureMerchantRequest` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureMerchantResponse` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureMerchantSell` | 🔴 Not-Set | | |
|
||||
| `OP_AdventurePointsUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureRequest` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureStatsReply` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureStatsRequest` | 🔴 Not-Set | | |
|
||||
| `OP_AdventureUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_AggroMeterLockTarget` | 🔴 Not-Set | | |
|
||||
| `OP_AggroMeterTargetInfo` | 🔴 Not-Set | | |
|
||||
| `OP_AggroMeterUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_AltCurrency` | 🔴 Not-Set | | |
|
||||
| `OP_AltCurrencyMerchantReply` | 🔴 Not-Set | | |
|
||||
| `OP_AltCurrencyMerchantRequest` | 🔴 Not-Set | | |
|
||||
| `OP_AltCurrencyPurchase` | 🔴 Not-Set | | |
|
||||
| `OP_AltCurrencyReclaim` | 🔴 Not-Set | | |
|
||||
| `OP_AltCurrencySell` | 🔴 Not-Set | | |
|
||||
| `OP_AltCurrencySellSelection` | 🔴 Not-Set | | |
|
||||
| `OP_Animation` | 🟡 Unverified | | |
|
||||
| `OP_AnnoyingZoneUnknown` | 🔴 Not-Set | | |
|
||||
| `OP_ApplyPoison` | 🟡 Unverified | | |
|
||||
| `OP_ApproveName` | 🟡 Unverified | This takes multiple parameters from the client, and it can take multiple integer values from the server | |
|
||||
| `OP_ApproveWorld` | 🔴 Not-Set | | |
|
||||
| `OP_ApproveZone` | 🔴 Not-Set | | |
|
||||
| `OP_Assist` | 🟡 Unverified | | |
|
||||
| `OP_AssistGroup` | 🟡 Unverified | | |
|
||||
| `OP_AugmentInfo` | 🟡 Unverified | | |
|
||||
| `OP_AugmentItem` | 🟡 Unverified | | |
|
||||
| `OP_AutoAttack` | 🟡 Unverified | | |
|
||||
| `OP_AutoAttack2` | 🟡 Unverified | | |
|
||||
| `OP_AutoFire` | 🟡 Unverified | | |
|
||||
| `OP_Bandolier` | 🔴 Not-Set | | |
|
||||
| `OP_BankerChange` | 🟡 Unverified | | |
|
||||
| `OP_Barter` | 🔴 Not-Set | | |
|
||||
| `OP_Bazaar` | 🔴 Not-Set | | |
|
||||
| `OP_BazaarInspect` | 🔴 Not-Set | | |
|
||||
| `OP_BazaarSearch` | 🔴 Not-Set | | |
|
||||
| `OP_BecomeCorpse` | 🔴 Not-Set | | |
|
||||
| `OP_BecomeTrader` | 🔴 Not-Set | | |
|
||||
| `OP_Begging` | 🟡 Unverified | | |
|
||||
| `OP_BeginCast` | 🟢 Verified | | |
|
||||
| `OP_Bind_Wound` | 🟡 Unverified | | |
|
||||
| `OP_BlockedBuffs` | 🟢 Verified | | |
|
||||
| `OP_BoardBoat` | 🟡 Unverified | | |
|
||||
| `OP_BookButton` | 🟡 Unverified | | |
|
||||
| `OP_Buff` | 🟡 Unverified | | |
|
||||
| `OP_BuffCreate` | 🟡 Unverified | | |
|
||||
| `OP_BuffRemoveRequest` | 🟡 Unverified | | |
|
||||
| `OP_Bug` | 🟡 Unverified | | |
|
||||
| `OP_BuyerItems` | 🔴 Not-Set | | |
|
||||
| `OP_CameraEffect` | 🟡 Unverified | | |
|
||||
| `OP_Camp` | 🟡 Unverified | | |
|
||||
| `OP_CancelSneakHide` | 🟡 Unverified | | |
|
||||
| `OP_CancelTask` | 🔴 Not-Set | | |
|
||||
| `OP_CancelTrade` | 🟡 Unverified | | |
|
||||
| `OP_CashReward` | 🟡 Unverified | | |
|
||||
| `OP_CastSpell` | 🟢 Verified | | |
|
||||
| `OP_ChangeSize` | 🟢 Verified | | |
|
||||
| `OP_ChannelMessage` | 🟢 Verified | | |
|
||||
| `OP_ChangePetName` | 🔴 Not-Set | | |
|
||||
| `OP_CharacterCreate` | 🟢 Verified | Sends heroic type, can be used for something? | |
|
||||
| `OP_CharacterCreateRequest` | 🟢 Verified | | |
|
||||
| `OP_CharInventory` | 🟢 Verified | | |
|
||||
| `OP_Charm` | 🟡 Unverified | | |
|
||||
| `OP_ChatMessage` | 🔴 Not-Set | | |
|
||||
| `OP_ClearAA` | 🟢 Verified | | |
|
||||
| `OP_ClearBlockedBuffs` | 🟢 Verified | | |
|
||||
| `OP_ClearLeadershipAbilities` | 🔴 Not-Set | | |
|
||||
| `OP_ClearNPCMarks` | 🔴 Not-Set | | |
|
||||
| `OP_ClearObject` | 🟡 Unverified | | |
|
||||
| `OP_ClearSurname` | 🔴 Not-Set | | |
|
||||
| `OP_ClickDoor` | 🟡 Unverified | | |
|
||||
| `OP_ClickObject` | 🟡 Unverified | | |
|
||||
| `OP_ClickObjectAction` | 🟡 Unverified | | |
|
||||
| `OP_ClientError` | 🔴 Not-Set | | |
|
||||
| `OP_ClientReady` | 🟢 Verified | | |
|
||||
| `OP_ClientTimeStamp` | 🔴 Not-Set | | |
|
||||
| `OP_ClientUpdate` | 🟢 Verified | | |
|
||||
| `OP_CloseContainer` | 🔴 Not-Set | | |
|
||||
| `OP_CloseTributeMaster` | 🔴 Not-Set | | |
|
||||
| `OP_ColoredText` | 🟢 Verified | | |
|
||||
| `OP_CombatAbility` | 🟡 Unverified | | |
|
||||
| `OP_Command` | 🔴 Not-Set | | |
|
||||
| `OP_CompletedTasks` | 🔴 Not-Set | | |
|
||||
| `OP_ConfirmDelete` | 🟡 Unverified | | |
|
||||
| `OP_Consent` | 🟡 Unverified | | |
|
||||
| `OP_ConsentDeny` | 🟡 Unverified | | |
|
||||
| `OP_ConsentResponse` | 🟢 Verified | | |
|
||||
| `OP_Consider` | 🟢 Verified | | |
|
||||
| `OP_ConsiderCorpse` | 🟡 Unverified | | |
|
||||
| `OP_Consume` | 🟡 Unverified | | |
|
||||
| `OP_ControlBoat` | 🟡 Unverified | | |
|
||||
| `OP_CorpseDrag` | 🟡 Unverified | | |
|
||||
| `OP_CorpseDrop` | 🟡 Unverified | | |
|
||||
| `OP_CrashDump` | 🔴 Not-Set | | |
|
||||
| `OP_CrystalCountUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_CrystalCreate` | 🔴 Not-Set | | |
|
||||
| `OP_CrystalReclaim` | 🔴 Not-Set | | |
|
||||
| `OP_CustomTitles` | 🔴 Not-Set | | |
|
||||
| `OP_Damage` | 🟡 Unverified | | |
|
||||
| `OP_Death` | 🟡 Unverified | | |
|
||||
| `OP_DelegateAbility` | 🔴 Not-Set | | |
|
||||
| `OP_DeleteCharacter` | 🟢 Verified | | |
|
||||
| `OP_DeleteCharge` | 🟡 Unverified | | |
|
||||
| `OP_DeleteItem` | 🟡 Unverified | | |
|
||||
| `OP_DeletePetition` | 🔴 Not-Set | | |
|
||||
| `OP_DeleteSpawn` | 🟡 Unverified | | |
|
||||
| `OP_DeleteSpell` | 🟡 Unverified | | |
|
||||
| `OP_DenyResponse` | 🟡 Unverified | | |
|
||||
| `OP_Disarm` | 🟡 Unverified | | |
|
||||
| `OP_DisarmTraps` | 🟡 Unverified | | |
|
||||
| `OP_DisciplineTimer` | 🟡 Unverified | | |
|
||||
| `OP_DisciplineUpdate` | 🟡 Unverified | | |
|
||||
| `OP_DiscordMerchantInventory` | 🔴 Not-Set | | |
|
||||
| `OP_DoGroupLeadershipAbility` | 🔴 Not-Set | | |
|
||||
| `OP_DuelDecline` | 🔴 Not-Set | | |
|
||||
| `OP_DuelAccept` | 🔴 Not-Set | | |
|
||||
| `OP_DumpName` | 🔴 Not-Set | | |
|
||||
| `OP_Dye` | 🔴 Not-Set | | |
|
||||
| `OP_DynamicWall` | 🔴 Not-Set | | |
|
||||
| `OP_DzAddPlayer` | 🔴 Not-Set | | |
|
||||
| `OP_DzChooseZone` | 🔴 Not-Set | | |
|
||||
| `OP_DzChooseZoneReply` | 🔴 Not-Set | | |
|
||||
| `OP_DzCompass` | 🔴 Not-Set | | |
|
||||
| `OP_DzExpeditionEndsWarning` | 🔴 Not-Set | | |
|
||||
| `OP_DzExpeditionInfo` | 🔴 Not-Set | | |
|
||||
| `OP_DzExpeditionInvite` | 🔴 Not-Set | | |
|
||||
| `OP_DzExpeditionInviteResponse` | 🔴 Not-Set | | |
|
||||
| `OP_DzExpeditionLockoutTimers` | 🔴 Not-Set | | |
|
||||
| `OP_DzListTimers` | 🔴 Not-Set | | |
|
||||
| `OP_DzMakeLeader` | 🔴 Not-Set | | |
|
||||
| `OP_DzMemberList` | 🔴 Not-Set | | |
|
||||
| `OP_DzMemberListName` | 🔴 Not-Set | | |
|
||||
| `OP_DzMemberListStatus` | 🔴 Not-Set | | |
|
||||
| `OP_DzPlayerList` | 🔴 Not-Set | | |
|
||||
| `OP_DzQuit` | 🔴 Not-Set | | |
|
||||
| `OP_DzRemovePlayer` | 🔴 Not-Set | | |
|
||||
| `OP_DzSetLeaderName` | 🔴 Not-Set | | |
|
||||
| `OP_DzSwapPlayer` | 🔴 Not-Set | | |
|
||||
| `OP_Emote` | 🔴 Not-Set | | |
|
||||
| `OP_EndLootRequest` | 🟡 Unverified | | |
|
||||
| `OP_EnduranceUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_EnterChat` | 🔴 Not-Set | | |
|
||||
| `OP_EnterWorld` | 🟢 Verified | | |
|
||||
| `OP_EnvDamage` | 🟡 Unverified | | |
|
||||
| `OP_EvolveItem` | 🔴 Not-Set | | |
|
||||
| `OP_ExpansionInfo` | 🟢 Verified | Updated from u32 to u64 and works now | |
|
||||
| `OP_ExpUpdate` | 🟢 Verified | | |
|
||||
| `OP_FaceChange` | 🔴 Not-Set | | |
|
||||
| `OP_Feedback` | 🔴 Not-Set | | |
|
||||
| `OP_FeignDeath` | 🟡 Unverified | | |
|
||||
| `OP_FellowshipUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_FindPersonReply` | 🔴 Not-Set | | |
|
||||
| `OP_FindPersonRequest` | 🔴 Not-Set | | |
|
||||
| `OP_FinishTrade` | 🟡 Unverified | | |
|
||||
| `OP_FinishWindow` | 🟡 Unverified | | |
|
||||
| `OP_FinishWindow2` | 🟡 Unverified | | |
|
||||
| `OP_Fishing` | 🟡 Unverified | | |
|
||||
| `OP_Fling` | 🟡 Unverified | | |
|
||||
| `OP_FloatListThing` | 🟢 Verified | Movement History. Sent from client, but emu doesn't use it so setting it as verified. Reference is 0x1402FFAD0 | |
|
||||
| `OP_Forage` | 🟡 Unverified | | |
|
||||
| `OP_ForceFindPerson` | 🔴 Not-Set | | |
|
||||
| `OP_FormattedMessage` | 🟢 Verified | Some major work to do here -- the client now expects a spell link in the packet, will need to refactor the client work to decouple the stream from the internal representation | |
|
||||
| `OP_FriendsWho` | 🟡 Unverified | | |
|
||||
| `OP_GetGuildMOTD` | 🔴 Not-Set | | |
|
||||
| `OP_GetGuildMOTDReply` | 🔴 Not-Set | | |
|
||||
| `OP_GetGuildsList` | 🔴 Not-Set | | |
|
||||
| `OP_GiveMoney` | 🔴 Not-Set | | |
|
||||
| `OP_GMApproval` | 🔴 Not-Set | | |
|
||||
| `OP_GMBecomeNPC` | 🔴 Not-Set | | |
|
||||
| `OP_GMDelCorpse` | 🔴 Not-Set | | |
|
||||
| `OP_GMEmoteZone` | 🔴 Not-Set | | |
|
||||
| `OP_GMEndTraining` | 🟡 Unverified | | |
|
||||
| `OP_GMEndTrainingResponse` | 🔴 Not-Set | | |
|
||||
| `OP_GMFind` | 🔴 Not-Set | | |
|
||||
| `OP_GMGoto` | 🔴 Not-Set | | |
|
||||
| `OP_GMHideMe` | 🔴 Not-Set | | |
|
||||
| `OP_GMKick` | 🔴 Not-Set | | |
|
||||
| `OP_GMKill` | 🔴 Not-Set | | |
|
||||
| `OP_GMLastName` | 🔴 Not-Set | | |
|
||||
| `OP_GMNameChange` | 🔴 Not-Set | | |
|
||||
| `OP_GMSearchCorpse` | 🔴 Not-Set | | |
|
||||
| `OP_GMServers` | 🔴 Not-Set | | |
|
||||
| `OP_GMSummon` | 🔴 Not-Set | | |
|
||||
| `OP_GMToggle` | 🔴 Not-Set | | |
|
||||
| `OP_GMTraining` | 🟡 Unverified | | |
|
||||
| `OP_GMTrainSkill` | 🟡 Unverified | | |
|
||||
| `OP_GMTrainSkillConfirm` | 🟡 Unverified | | |
|
||||
| `OP_GMZoneRequest` | 🔴 Not-Set | | |
|
||||
| `OP_GMZoneRequest2` | 🔴 Not-Set | | |
|
||||
| `OP_GroundSpawn` | 🟢 Verified | | |
|
||||
| `OP_GroupAcknowledge` | 🔴 Not-Set | | |
|
||||
| `OP_GroupCancelInvite` | 🔴 Not-Set | | |
|
||||
| `OP_GroupDelete` | 🔴 Not-Set | | |
|
||||
| `OP_GroupDisband` | 🟡 Unverified | | |
|
||||
| `OP_GroupDisbandOther` | 🔴 Not-Set | | |
|
||||
| `OP_GroupDisbandYou` | 🔴 Not-Set | | |
|
||||
| `OP_GroupFollow` | 🔴 Not-Set | | |
|
||||
| `OP_GroupFollow2` | 🔴 Not-Set | | |
|
||||
| `OP_GroupInvite` | 🟡 Unverified | | |
|
||||
| `OP_GroupInvite2` | 🔴 Not-Set | | |
|
||||
| `OP_GroupLeaderChange` | 🔴 Not-Set | | |
|
||||
| `OP_GroupLeadershipAAUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_GroupMakeLeader` | 🔴 Not-Set | | |
|
||||
| `OP_GroupMentor` | 🔴 Not-Set | | |
|
||||
| `OP_GroupRoles` | 🔴 Not-Set | | |
|
||||
| `OP_GroupUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_GroupUpdateB` | 🔴 Not-Set | | |
|
||||
| `OP_GroupUpdateLeaderAA` | 🔴 Not-Set | | |
|
||||
| `OP_GuildBank` | 🔴 Not-Set | | |
|
||||
| `OP_GuildBankItemList` | 🔴 Not-Set | | |
|
||||
| `OP_GuildCreate` | 🔴 Not-Set | | |
|
||||
| `OP_GuildDelete` | 🔴 Not-Set | | |
|
||||
| `OP_GuildDeleteGuild` | 🔴 Not-Set | | |
|
||||
| `OP_GuildDemote` | 🔴 Not-Set | | |
|
||||
| `OP_GuildInvite` | 🔴 Not-Set | | |
|
||||
| `OP_GuildInviteAccept` | 🔴 Not-Set | | |
|
||||
| `OP_GuildLeader` | 🔴 Not-Set | | |
|
||||
| `OP_GuildManageAdd` | 🔴 Not-Set | | |
|
||||
| `OP_GuildManageBanker` | 🔴 Not-Set | | |
|
||||
| `OP_GuildManageRemove` | 🔴 Not-Set | | |
|
||||
| `OP_GuildManageStatus` | 🔴 Not-Set | | |
|
||||
| `OP_GuildMemberLevelUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_GuildMemberList` | 🔴 Not-Set | | |
|
||||
| `OP_GuildMemberUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_GuildMemberLevel` | 🔴 Not-Set | | |
|
||||
| `OP_GuildMemberRankAltBanker` | 🔴 Not-Set | | |
|
||||
| `OP_GuildMemberPublicNote` | 🔴 Not-Set | | |
|
||||
| `OP_GuildMemberAdd` | 🔴 Not-Set | | |
|
||||
| `OP_GuildMemberRename` | 🔴 Not-Set | | |
|
||||
| `OP_GuildMemberDelete` | 🔴 Not-Set | | |
|
||||
| `OP_GuildMemberDetails` | 🔴 Not-Set | | |
|
||||
| `OP_GuildRenameGuild` | 🔴 Not-Set | | |
|
||||
| `OP_GuildMOTD` | 🔴 Not-Set | | |
|
||||
| `OP_GuildPeace` | 🔴 Not-Set | | |
|
||||
| `OP_GuildPromote` | 🔴 Not-Set | | |
|
||||
| `OP_GuildPublicNote` | 🔴 Not-Set | | |
|
||||
| `OP_GuildRemove` | 🔴 Not-Set | | |
|
||||
| `OP_GuildSelectTribute` | 🔴 Not-Set | | |
|
||||
| `OP_GuildModifyBenefits` | 🔴 Not-Set | | |
|
||||
| `OP_GuildTributeToggleReq` | 🔴 Not-Set | | |
|
||||
| `OP_GuildTributeToggleReply` | 🔴 Not-Set | | |
|
||||
| `OP_GuildOptInOut` | 🔴 Not-Set | | |
|
||||
| `OP_GuildSaveActiveTributes` | 🔴 Not-Set | | |
|
||||
| `OP_GuildSendActiveTributes` | 🔴 Not-Set | | |
|
||||
| `OP_GuildTributeFavorAndTimer` | 🔴 Not-Set | | |
|
||||
| `OP_GuildsList` | 🔴 Not-Set | | |
|
||||
| `OP_GuildStatus` | 🔴 Not-Set | | |
|
||||
| `OP_GuildTributeInfo` | 🔴 Not-Set | | |
|
||||
| `OP_GuildUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_GuildTributeDonateItem` | 🔴 Not-Set | | |
|
||||
| `OP_GuildTributeDonatePlat` | 🔴 Not-Set | | |
|
||||
| `OP_GuildWar` | 🔴 Not-Set | | |
|
||||
| `OP_Heartbeat` | 🔴 Not-Set | | |
|
||||
| `OP_Hide` | 🟡 Unverified | | |
|
||||
| `OP_HideCorpse` | 🟡 Unverified | | |
|
||||
| `OP_HPUpdate` | 🟢 Verified | | |
|
||||
| `OP_Illusion` | 🟡 Unverified | | |
|
||||
| `OP_IncreaseStats` | 🟡 Unverified | | |
|
||||
| `OP_InitialHPUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_InitialMobHealth` | 🔴 Not-Set | | |
|
||||
| `OP_InspectAnswer` | 🔴 Not-Set | | |
|
||||
| `OP_InspectBuffs` | 🔴 Not-Set | | |
|
||||
| `OP_InspectMessageUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_InspectRequest` | 🔴 Not-Set | | |
|
||||
| `OP_InstillDoubt` | 🟡 Unverified | | |
|
||||
| `OP_InterruptCast` | 🟢 Verified | Some major work to do here -- the client now expects a spell link in the packet, will need to refactor the client work to decouple the stream from the internal representation | |
|
||||
| `OP_InvokeChangePetName` | 🔴 Not-Set | | |
|
||||
| `OP_InvokeChangePetNameImmediate` | 🔴 Not-Set | | |
|
||||
| `OP_InvokeNameChangeImmediate` | 🔴 Not-Set | | |
|
||||
| `OP_InvokeNameChangeLazy` | 🔴 Not-Set | | |
|
||||
| `OP_ItemLinkClick` | 🔴 Not-Set | | |
|
||||
| `OP_ItemLinkResponse` | 🔴 Not-Set | | |
|
||||
| `OP_ItemLinkText` | 🔴 Not-Set | | |
|
||||
| `OP_ItemName` | 🔴 Not-Set | | |
|
||||
| `OP_ItemPacket` | 🟡 Unverified | | |
|
||||
| `OP_ItemPreview` | 🔴 Not-Set | | |
|
||||
| `OP_ItemPreviewRequest` | 🔴 Not-Set | | |
|
||||
| `OP_ItemRecastDelay` | 🟡 Unverified | | |
|
||||
| `OP_ItemVerifyReply` | 🟡 Unverified | | |
|
||||
| `OP_ItemVerifyRequest` | 🟡 Unverified | | |
|
||||
| `OP_ItemViewUnknown` | 🔴 Not-Set | | |
|
||||
| `OP_Jump` | 🟡 Unverified | | |
|
||||
| `OP_KeyRing` | 🔴 Not-Set | | |
|
||||
| `OP_KickPlayers` | 🟡 Unverified | | |
|
||||
| `OP_KnowledgeBase` | 🔴 Not-Set | | |
|
||||
| `OP_LDoNButton` | 🔴 Not-Set | | |
|
||||
| `OP_LDoNDisarmTraps` | 🔴 Not-Set | | |
|
||||
| `OP_LDoNInspect` | 🔴 Not-Set | | |
|
||||
| `OP_LDoNOpen` | 🟡 Unverified | | |
|
||||
| `OP_LDoNPickLock` | 🟡 Unverified | | |
|
||||
| `OP_LDoNSenseTraps` | 🟡 Unverified | | |
|
||||
| `OP_LeadershipExpToggle` | 🔴 Not-Set | | |
|
||||
| `OP_LeadershipExpUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_LeaveAdventure` | 🔴 Not-Set | | |
|
||||
| `OP_LeaveBoat` | 🟡 Unverified | | |
|
||||
| `OP_LevelAppearance` | 🟡 Unverified | | |
|
||||
| `OP_LevelUpdate` | 🟢 Verified | | |
|
||||
| `OP_LFGAppearance` | 🔴 Not-Set | | |
|
||||
| `OP_LFGCommand` | 🔴 Not-Set | | |
|
||||
| `OP_LFGGetMatchesRequest` | 🔴 Not-Set | | |
|
||||
| `OP_LFGGetMatchesResponse` | 🔴 Not-Set | | |
|
||||
| `OP_LFGResponse` | 🔴 Not-Set | | |
|
||||
| `OP_LFGuild` | 🔴 Not-Set | | |
|
||||
| `OP_LFPCommand` | 🔴 Not-Set | | |
|
||||
| `OP_LFPGetMatchesRequest` | 🔴 Not-Set | | |
|
||||
| `OP_LFPGetMatchesResponse` | 🔴 Not-Set | | |
|
||||
| `OP_LinkedReuse` | 🟢 Verified | | |
|
||||
| `OP_LoadSpellSet` | 🔴 Not-Set | | |
|
||||
| `OP_LocInfo` | 🔴 Not-Set | | |
|
||||
| `OP_LockoutTimerInfo` | 🔴 Not-Set | | |
|
||||
| `OP_Login` | 🔴 Not-Set | | |
|
||||
| `OP_LoginAccepted` | 🔴 Not-Set | | |
|
||||
| `OP_LoginComplete` | 🔴 Not-Set | | |
|
||||
| `OP_LoginExpansionPacketData` | 🔴 Not-Set | | |
|
||||
| `OP_LoginUnknown1` | 🔴 Not-Set | | |
|
||||
| `OP_LoginUnknown2` | 🔴 Not-Set | | |
|
||||
| `OP_Logout` | 🟡 Unverified | | |
|
||||
| `OP_LogoutReply` | 🔴 Not-Set | | |
|
||||
| `OP_LogServer` | 🟢 Verified | Mostly unused values | |
|
||||
| `OP_LootComplete` | 🟡 Unverified | | |
|
||||
| `OP_LootItem` | 🟡 Unverified | | |
|
||||
| `OP_LootRequest` | 🟡 Unverified | | |
|
||||
| `OP_ManaChange` | 🟢 Verified | | |
|
||||
| `OP_ManaUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_MarkNPC` | 🔴 Not-Set | | |
|
||||
| `OP_MarkRaidNPC` | 🔴 Not-Set | | |
|
||||
| `OP_Marquee` | 🟡 Unverified | | |
|
||||
| `OP_MemorizeSpell` | 🟢 Verified | | |
|
||||
| `OP_Mend` | 🟡 Unverified | | |
|
||||
| `OP_MendHPUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryAssign` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryCommand` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryDataRequest` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryDataResponse` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryDataUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryDataUpdateRequest` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryDismiss` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryHire` | 🔴 Not-Set | | |
|
||||
| `OP_MercenarySuspendRequest` | 🔴 Not-Set | | |
|
||||
| `OP_MercenarySuspendResponse` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryTimer` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryTimerRequest` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryUnknown1` | 🔴 Not-Set | | |
|
||||
| `OP_MercenaryUnsuspendResponse` | 🔴 Not-Set | | |
|
||||
| `OP_MerchantBulkItems` | 🔴 Not-Set | | |
|
||||
| `OP_MobEnduranceUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_MobHealth` | 🟡 Unverified | | |
|
||||
| `OP_MobManaUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_MobRename` | 🔴 Not-Set | | |
|
||||
| `OP_MobUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_MoneyOnCorpse` | 🟡 Unverified | | |
|
||||
| `OP_MoneyUpdate` | 🟡 Unverified | | |
|
||||
| `OP_MOTD` | 🟢 Verified | | |
|
||||
| `OP_MoveCoin` | 🟡 Unverified | | |
|
||||
| `OP_MoveDoor` | 🟡 Unverified | | |
|
||||
| `OP_MoveItem` | 🟢 Verified | | |
|
||||
| `OP_MoveMultipleItems` | 🟡 Unverified | | |
|
||||
| `OP_MoveLogDisregard` | 🔴 Not-Set | | |
|
||||
| `OP_MoveLogRequest` | 🔴 Not-Set | | |
|
||||
| `OP_MultiLineMsg` | 🔴 Not-Set | | |
|
||||
| `OP_NewSpawn` | 🟢 Verified | Deprecated in the client, already handled in emu | |
|
||||
| `OP_NewTitlesAvailable` | 🔴 Not-Set | | |
|
||||
| `OP_NewZone` | 🟢 Verified | | |
|
||||
| `OP_NPCMoveUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_OnLevelMessage` | 🟡 Unverified | | |
|
||||
| `OP_OpenContainer` | 🟡 Unverified | | |
|
||||
| `OP_OpenDiscordMerchant` | 🔴 Not-Set | | |
|
||||
| `OP_OpenGuildTributeMaster` | 🔴 Not-Set | | |
|
||||
| `OP_OpenInventory` | 🔴 Not-Set | | |
|
||||
| `OP_OpenTributeMaster` | 🔴 Not-Set | | |
|
||||
| `OP_PDeletePetition` | 🔴 Not-Set | | |
|
||||
| `OP_PetBuffWindow` | 🔴 Not-Set | | |
|
||||
| `OP_PetCommands` | 🔴 Not-Set | | |
|
||||
| `OP_PetCommandState` | 🔴 Not-Set | | |
|
||||
| `OP_PetHoTT` | 🔴 Not-Set | | |
|
||||
| `OP_Petition` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionBug` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionCheckIn` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionCheckout` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionCheckout2` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionDelete` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionQue` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionRefresh` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionResolve` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionSearch` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionSearchResults` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionSearchText` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionUnCheckout` | 🔴 Not-Set | | |
|
||||
| `OP_PetitionUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_PickPocket` | 🟡 Unverified | | |
|
||||
| `OP_PickZone` | 🔴 Not-Set | | |
|
||||
| `OP_PickZoneWindow` | 🔴 Not-Set | | |
|
||||
| `OP_PlayerProfile` | 🟢 Verified | | |
|
||||
| `OP_PlayerStateAdd` | 🟡 Unverified | | |
|
||||
| `OP_PlayerStateRemove` | 🟡 Unverified | | |
|
||||
| `OP_PlayEverquestRequest` | 🔴 Not-Set | | |
|
||||
| `OP_PlayEverquestResponse` | 🔴 Not-Set | | |
|
||||
| `OP_PlayMP3` | 🟡 Unverified | | |
|
||||
| `OP_Poll` | 🔴 Not-Set | | |
|
||||
| `OP_PollResponse` | 🔴 Not-Set | | |
|
||||
| `OP_PopupResponse` | 🟡 Unverified | | |
|
||||
| `OP_PostEnterWorld` | 🟢 Verified | | |
|
||||
| `OP_PotionBelt` | 🔴 Not-Set | | |
|
||||
| `OP_PreLogoutReply` | 🔴 Not-Set | | |
|
||||
| `OP_PurchaseLeadershipAA` | 🔴 Not-Set | | |
|
||||
| `OP_PVPLeaderBoardDetailsReply` | 🔴 Not-Set | | |
|
||||
| `OP_PVPLeaderBoardDetailsRequest` | 🔴 Not-Set | | |
|
||||
| `OP_PVPLeaderBoardReply` | 🔴 Not-Set | | |
|
||||
| `OP_PVPLeaderBoardRequest` | 🔴 Not-Set | | |
|
||||
| `OP_PVPStats` | 🔴 Not-Set | | |
|
||||
| `OP_QueryResponseThing` | 🔴 Not-Set | | |
|
||||
| `OP_QueryUCSServerStatus` | 🟢 Verified | | |
|
||||
| `OP_RaidDelegateAbility` | 🔴 Not-Set | | |
|
||||
| `OP_RaidClearNPCMarks` | 🔴 Not-Set | | |
|
||||
| `OP_RaidInvite` | 🔴 Not-Set | | |
|
||||
| `OP_RaidJoin` | 🔴 Not-Set | | |
|
||||
| `OP_RaidUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_RandomNameGenerator` | 🟢 Verified | The client no longer sends this packet (random name generation is done entirely in the client). The client will still accept this packet to set name (emu doesn't do this, but it's always been supported) | |
|
||||
| `OP_RandomReply` | 🟡 Unverified | | |
|
||||
| `OP_RandomReq` | 🟡 Unverified | | |
|
||||
| `OP_ReadBook` | 🟡 Unverified | | |
|
||||
| `OP_RecipeAutoCombine` | 🟡 Unverified | | |
|
||||
| `OP_RecipeDetails` | 🟡 Unverified | | |
|
||||
| `OP_RecipeReply` | 🟡 Unverified | | |
|
||||
| `OP_RecipesFavorite` | 🟡 Unverified | | |
|
||||
| `OP_RecipesSearch` | 🟡 Unverified | | |
|
||||
| `OP_ReclaimCrystals` | 🔴 Not-Set | | |
|
||||
| `OP_ReloadUI` | 🔴 Not-Set | | |
|
||||
| `OP_RemoveAllDoors` | 🟡 Unverified | | |
|
||||
| `OP_RemoveBlockedBuffs` | 🟢 Verified | | |
|
||||
| `OP_RemoveNimbusEffect` | 🟡 Unverified | | |
|
||||
| `OP_RemoveTrap` | 🔴 Not-Set | | |
|
||||
| `OP_Report` | 🟡 Unverified | | |
|
||||
| `OP_ReqClientSpawn` | 🟢 Verified | | |
|
||||
| `OP_ReqNewZone` | 🟢 Verified | Client does not send this (in LS or TOB), but it does receive it. emu does not send it | |
|
||||
| `OP_RequestClientZoneChange` | 🟢 Verified | parity with RoF2, there's a string that gets passed to teleport at the end that's not known | |
|
||||
| `OP_RequestDuel` | 🔴 Not-Set | | |
|
||||
| `OP_RequestGuildTributes` | 🔴 Not-Set | | |
|
||||
| `OP_RequestKnowledgeBase` | 🔴 Not-Set | | |
|
||||
| `OP_RequestTitles` | 🔴 Not-Set | | |
|
||||
| `OP_RespawnWindow` | 🟡 Unverified | | |
|
||||
| `OP_RespondAA` | 🟢 Verified | | |
|
||||
| `OP_RestState` | 🟡 Unverified | | |
|
||||
| `OP_Rewind` | 🟡 Unverified | | |
|
||||
| `OP_RezzAnswer` | 🔴 Not-Set | | |
|
||||
| `OP_RezzComplete` | 🔴 Not-Set | | |
|
||||
| `OP_RezzRequest` | 🔴 Not-Set | | |
|
||||
| `OP_Sacrifice` | 🟡 Unverified | | |
|
||||
| `OP_SafeFallSuccess` | 🟡 Unverified | | |
|
||||
| `OP_SafePoint` | 🔴 Not-Set | | |
|
||||
| `OP_Save` | 🟡 Unverified | | |
|
||||
| `OP_SaveOnZoneReq` | 🟡 Unverified | | |
|
||||
| `OP_SelectTribute` | 🔴 Not-Set | | |
|
||||
| `OP_SendAAStats` | 🟡 Unverified | | |
|
||||
| `OP_SendAATable` | 🟢 Verified | | |
|
||||
| `OP_SendCharInfo` | 🟢 Verified | | |
|
||||
| `OP_SendExpZonein` | 🟢 Verified | | |
|
||||
| `OP_SendFindableNPCs` | 🔴 Not-Set | | |
|
||||
| `OP_SendGuildTributes` | 🔴 Not-Set | | |
|
||||
| `OP_SendLoginInfo` | 🟢 Verified | | |
|
||||
| `OP_SendMaxCharacters` | 🟢 Verified | | |
|
||||
| `OP_SendMembership` | 🟢 Verified | | |
|
||||
| `OP_SendMembershipDetails` | 🟢 Verified | The struct is correct, will need reversing for actual option keys/values | |
|
||||
| `OP_SendSystemStats` | 🔴 Not-Set | | |
|
||||
| `OP_SendTitleList` | 🔴 Not-Set | | |
|
||||
| `OP_SendTributes` | 🔴 Not-Set | | |
|
||||
| `OP_SendZonepoints` | 🟢 Verified | | |
|
||||
| `OP_SenseHeading` | 🟡 Unverified | | |
|
||||
| `OP_SenseTraps` | 🟡 Unverified | | |
|
||||
| `OP_ServerListRequest` | 🔴 Not-Set | | |
|
||||
| `OP_ServerListResponse` | 🔴 Not-Set | | |
|
||||
| `OP_SessionReady` | 🔴 Not-Set | | |
|
||||
| `OP_SetChatServer` | 🔴 Not-Set | | |
|
||||
| `OP_SetChatServer2` | 🟢 Verified | | |
|
||||
| `OP_SetFace` | 🔴 Not-Set | | |
|
||||
| `OP_SetGroupTarget` | 🔴 Not-Set | | |
|
||||
| `OP_SetGuildMOTD` | 🔴 Not-Set | | |
|
||||
| `OP_SetGuildRank` | 🔴 Not-Set | | |
|
||||
| `OP_SetRunMode` | 🟡 Unverified | | |
|
||||
| `OP_SetServerFilter` | 🟢 Verified | | |
|
||||
| `OP_SetStartCity` | 🔴 Not-Set | | |
|
||||
| `OP_SetTitle` | 🔴 Not-Set | | |
|
||||
| `OP_SetTitleReply` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskMemberList` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskAddPlayer` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskRemovePlayer` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskMakeLeader` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskMemberInvite` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskInvite` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskInviteResponse` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskAcceptNew` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskMemberChange` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskPlayerList` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskSelectWindow` | 🔴 Not-Set | | |
|
||||
| `OP_SharedTaskQuit` | 🔴 Not-Set | | |
|
||||
| `OP_TaskTimers` | 🔴 Not-Set | | |
|
||||
| `OP_Shielding` | 🔴 Not-Set | | |
|
||||
| `OP_ShopDelItem` | 🟡 Unverified | | |
|
||||
| `OP_ShopEnd` | 🟡 Unverified | | |
|
||||
| `OP_ShopEndConfirm` | 🟡 Unverified | | |
|
||||
| `OP_ShopItem` | 🔴 Not-Set | | |
|
||||
| `OP_ShopPlayerBuy` | 🟡 Unverified | | |
|
||||
| `OP_ShopPlayerSell` | 🟡 Unverified | | |
|
||||
| `OP_ShopSendParcel` | 🟡 Unverified | | |
|
||||
| `OP_ShopDeleteParcel` | 🟡 Unverified | | |
|
||||
| `OP_ShopRespondParcel` | 🔴 Not-Set | | |
|
||||
| `OP_ShopRetrieveParcel` | 🟡 Unverified | | |
|
||||
| `OP_ShopParcelIcon` | 🟡 Unverified | | |
|
||||
| `OP_ShopRequest` | 🟡 Unverified | | |
|
||||
| `OP_SimpleMessage` | 🟢 Verified | | |
|
||||
| `OP_SkillUpdate` | 🟡 Unverified | | |
|
||||
| `OP_Sneak` | 🟡 Unverified | | |
|
||||
| `OP_Some3ByteHPUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_Some6ByteHPUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_SomeItemPacketMaybe` | 🔴 Not-Set | | |
|
||||
| `OP_Sound` | 🟡 Unverified | | |
|
||||
| `OP_SpawnAppearance` | 🟢 Verified | | |
|
||||
| `OP_SpawnDoor` | 🟢 Verified | | |
|
||||
| `OP_SpawnPositionUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_SpecialMesg` | 🟢 Verified | | |
|
||||
| `OP_SpellEffect` | 🟡 Unverified | | |
|
||||
| `OP_Split` | 🟡 Unverified | | |
|
||||
| `OP_Stamina` | 🟢 Verified | These values are 0-32k instead of 0-127 | |
|
||||
| `OP_Stun` | 🟡 Unverified | | |
|
||||
| `OP_Surname` | 🔴 Not-Set | | |
|
||||
| `OP_SwapSpell` | 🟢 Verified | | |
|
||||
| `OP_SystemFingerprint` | 🔴 Not-Set | | |
|
||||
| `OP_TargetBuffs` | 🔴 Not-Set | | |
|
||||
| `OP_TargetCommand` | 🟡 Unverified | | |
|
||||
| `OP_TargetHoTT` | 🔴 Not-Set | | |
|
||||
| `OP_TargetMouse` | 🟡 Unverified | | |
|
||||
| `OP_TargetReject` | 🔴 Not-Set | | |
|
||||
| `OP_TaskActivity` | 🔴 Not-Set | | |
|
||||
| `OP_TaskActivityComplete` | 🔴 Not-Set | | |
|
||||
| `OP_TaskDescription` | 🔴 Not-Set | | |
|
||||
| `OP_TaskHistoryReply` | 🔴 Not-Set | | |
|
||||
| `OP_TaskHistoryRequest` | 🔴 Not-Set | | |
|
||||
| `OP_TaskRequestTimer` | 🔴 Not-Set | | |
|
||||
| `OP_TaskSelectWindow` | 🔴 Not-Set | | |
|
||||
| `OP_Taunt` | 🟡 Unverified | | |
|
||||
| `OP_TestBuff` | 🔴 Not-Set | | |
|
||||
| `OP_TGB` | 🔴 Not-Set | | |
|
||||
| `OP_TimeOfDay` | 🟢 Verified | | |
|
||||
| `OP_Track` | 🟡 Unverified | | |
|
||||
| `OP_TrackTarget` | 🟡 Unverified | | |
|
||||
| `OP_TrackUnknown` | 🟡 Unverified | | |
|
||||
| `OP_TradeAcceptClick` | 🟡 Unverified | | |
|
||||
| `OP_TradeBusy` | 🟡 Unverified | | |
|
||||
| `OP_TradeCoins` | 🟡 Unverified | | |
|
||||
| `OP_TradeMoneyUpdate` | 🟡 Unverified | | |
|
||||
| `OP_Trader` | 🔴 Not-Set | | |
|
||||
| `OP_TraderBulkSend` | 🔴 Not-Set | | |
|
||||
| `OP_TraderBuy` | 🔴 Not-Set | | |
|
||||
| `OP_TraderDelItem` | 🔴 Not-Set | | |
|
||||
| `OP_TradeRequest` | 🟡 Unverified | | |
|
||||
| `OP_TradeRequestAck` | 🟡 Unverified | | |
|
||||
| `OP_TraderItemUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_TraderShop` | 🔴 Not-Set | | |
|
||||
| `OP_TradeSkillCombine` | 🟡 Unverified | | |
|
||||
| `OP_TradeSkillRecipeInspect` | 🔴 Not-Set | | |
|
||||
| `OP_Translocate` | 🟡 Unverified | | |
|
||||
| `OP_TributeInfo` | 🔴 Not-Set | | |
|
||||
| `OP_TributeItem` | 🔴 Not-Set | | |
|
||||
| `OP_TributeMoney` | 🔴 Not-Set | | |
|
||||
| `OP_TributeNPC` | 🔴 Not-Set | | |
|
||||
| `OP_TributePointUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_TributeTimer` | 🔴 Not-Set | | |
|
||||
| `OP_TributeToggle` | 🔴 Not-Set | | |
|
||||
| `OP_TributeUpdate` | 🔴 Not-Set | | |
|
||||
| `OP_Untargetable` | 🟡 Unverified | | |
|
||||
| `OP_UpdateAA` | 🟢 Verified | | |
|
||||
| `OP_UpdateAura` | 🔴 Not-Set | | |
|
||||
| `OP_UpdateLeadershipAA` | 🔴 Not-Set | | |
|
||||
| `OP_VetClaimReply` | 🔴 Not-Set | | |
|
||||
| `OP_VetClaimRequest` | 🔴 Not-Set | | |
|
||||
| `OP_VetRewardsAvaliable` | 🔴 Not-Set | | |
|
||||
| `OP_VoiceMacroIn` | 🟡 Unverified | | |
|
||||
| `OP_VoiceMacroOut` | 🟡 Unverified | | |
|
||||
| `OP_WeaponEquip1` | 🔴 Not-Set | | |
|
||||
| `OP_WearChange` | 🟢 Verified | | |
|
||||
| `OP_Weather` | 🟢 Verified | | |
|
||||
| `OP_Weblink` | 🟡 Unverified | | |
|
||||
| `OP_WhoAllRequest` | 🟡 Unverified | | |
|
||||
| `OP_WhoAllResponse` | 🟡 Unverified | | |
|
||||
| `OP_World_Client_CRC1` | 🟢 Verified | | |
|
||||
| `OP_World_Client_CRC2` | 🟢 Verified | | |
|
||||
| `OP_World_Client_CRC3` | 🟢 Verified | | |
|
||||
| `OP_WorldClientReady` | 🟢 Verified | | |
|
||||
| `OP_WorldComplete` | 🟢 Verified | | |
|
||||
| `OP_WorldLogout` | 🔴 Not-Set | | |
|
||||
| `OP_WorldObjectsSent` | 🟢 Verified | | |
|
||||
| `OP_WorldUnknown001` | 🟢 Verified | SetServerTime. emu doesn't currently send it so setting it to verified, but the reference is 0x140292550 | |
|
||||
| `OP_XTargetAutoAddHaters` | 🔴 Not-Set | | |
|
||||
| `OP_XTargetOpen` | 🔴 Not-Set | | |
|
||||
| `OP_XTargetOpenResponse` | 🔴 Not-Set | | |
|
||||
| `OP_XTargetRequest` | 🔴 Not-Set | | |
|
||||
| `OP_XTargetResponse` | 🔴 Not-Set | | |
|
||||
| `OP_YellForHelp` | 🟡 Unverified | | |
|
||||
| `OP_ZoneChange` | 🟢 Verified | | |
|
||||
| `OP_ZoneComplete` | 🔴 Not-Set | | |
|
||||
| `OP_ZoneEntry` | 🟢 Verified | unknown fields in C->S struct are various CRCs, emu doesn't use them | |
|
||||
| `OP_ZoneGuildList` | 🔴 Not-Set | | |
|
||||
| `OP_ZoneInUnknown` | 🔴 Not-Set | | |
|
||||
| `OP_ZonePlayerToBind` | 🟡 Unverified | | |
|
||||
| `OP_ZoneServerInfo` | 🟢 Verified | | |
|
||||
| `OP_ZoneServerReady` | 🔴 Not-Set | | |
|
||||
| `OP_ZoneSpawns` | 🟢 Verified | This is deprecated in the client (and emu never sends it directly) | |
|
||||
| `OP_ZoneUnavail` | 🟢 Verified | The client discards all content of this packet | |
|
||||
| `OP_ResetAA` | 🟡 Unverified | | |
|
||||
| `OP_UnderWorld` | 🟡 Unverified | | |
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user